Actual source code: ex3.c

  1: static char help[] = "Solve a toy 3D problem on a staggered grid\n\n";
  2: /*

  4:   To demonstrate the basic functionality of DMStag, solves an isoviscous
  5:   incompressible Stokes problem on a rectangular 3D domain.

  7:   u_{xx} + u_{yy} + u_{zz} - p_x = f^x
  8:   v_{xx} + v_{yy} + u_{zz} - p_y = f^y
  9:   w_{xx} + w_{yy} + w_{zz} - p_y = f^z
 10:   u_x    + v_y    + w_z          = g

 12:   g = 0 for the physical case.

 14:   Boundary conditions give prescribed flow perpendicular to the boundaries,
 15:   and zero derivative perpendicular to them (free slip). This involves
 16:   using a modified stencil at the boundaries. Another option would be to
 17:   use DM_BOUNDARY_GHOSTED in DMStagCreate3d() and a matrix-free operator (MATSHELL)
 18:   making use of the uniformly-available ghost layer.

 20:   Use the -pinpressure option to fix a pressure node, instead of providing
 21:   a constant-pressure nullspace. This allows use of direct solvers, e.g. to
 22:   use UMFPACK,

 24:      ./ex3 -pinpressure 1 -pc_type lu -pc_factor_mat_solver_type umfpack

 26: */
 27: #include <petscdm.h>
 28: #include <petscksp.h>
 29: #include <petscdmstag.h>

 31: /* Shorter, more convenient names for DMStagStencilLocation entries */
 32: #define BACK_DOWN_LEFT   DMSTAG_BACK_DOWN_LEFT
 33: #define BACK_DOWN        DMSTAG_BACK_DOWN
 34: #define BACK_DOWN_RIGHT  DMSTAG_BACK_DOWN_RIGHT
 35: #define BACK_LEFT        DMSTAG_BACK_LEFT
 36: #define BACK             DMSTAG_BACK
 37: #define BACK_RIGHT       DMSTAG_BACK_RIGHT
 38: #define BACK_UP_LEFT     DMSTAG_BACK_UP_LEFT
 39: #define BACK_UP          DMSTAG_BACK_UP
 40: #define BACK_UP_RIGHT    DMSTAG_BACK_UP_RIGHT
 41: #define DOWN_LEFT        DMSTAG_DOWN_LEFT
 42: #define DOWN             DMSTAG_DOWN
 43: #define DOWN_RIGHT       DMSTAG_DOWN_RIGHT
 44: #define LEFT             DMSTAG_LEFT
 45: #define ELEMENT          DMSTAG_ELEMENT
 46: #define RIGHT            DMSTAG_RIGHT
 47: #define UP_LEFT          DMSTAG_UP_LEFT
 48: #define UP               DMSTAG_UP
 49: #define UP_RIGHT         DMSTAG_UP_RIGHT
 50: #define FRONT_DOWN_LEFT  DMSTAG_FRONT_DOWN_LEFT
 51: #define FRONT_DOWN       DMSTAG_FRONT_DOWN
 52: #define FRONT_DOWN_RIGHT DMSTAG_FRONT_DOWN_RIGHT
 53: #define FRONT_LEFT       DMSTAG_FRONT_LEFT
 54: #define FRONT            DMSTAG_FRONT
 55: #define FRONT_RIGHT      DMSTAG_FRONT_RIGHT
 56: #define FRONT_UP_LEFT    DMSTAG_FRONT_UP_LEFT
 57: #define FRONT_UP         DMSTAG_FRONT_UP
 58: #define FRONT_UP_RIGHT   DMSTAG_FRONT_UP_RIGHT

 60: static PetscErrorCode CreateReferenceSolution(DM, Vec *);
 61: static PetscErrorCode CreateSystem(DM, Mat *, Vec *, PetscBool);
 62: static PetscErrorCode AttachNullspace(DM, Mat);
 63: static PetscErrorCode CheckSolution(Vec, Vec);

 65: /* Manufactured solution. Chosen to be higher order than can be solved exactly,
 66: and to have a zero derivative for flow parallel to the boundaries. That is,
 67: d(ux)/dy = 0 at the top and bottom boundaries, and d(uy)/dx = 0 at the right
 68: and left boundaries.
 69: These expressions could be made more interesting with more z terms,
 70: and convergence could be confirmed.  */

 72: static PetscScalar uxRef(PetscScalar x, PetscScalar y, PetscScalar z)
 73: {
 74:   return 0.0 * x + y * y - 2.0 * y * y * y + y * y * y * y + 0.0 * z;
 75: }
 76: static PetscScalar uyRef(PetscScalar x, PetscScalar y, PetscScalar z)
 77: {
 78:   return x * x - 2.0 * x * x * x + x * x * x * x + 0.0 * y + 0.0 * z;
 79: }
 80: static PetscScalar uzRef(PetscScalar x, PetscScalar y, PetscScalar z)
 81: {
 82:   return 0.0 * x + 0.0 * y + 0.0 * z + 1.0;
 83: }
 84: static PetscScalar pRef(PetscScalar x, PetscScalar y, PetscScalar z)
 85: {
 86:   return -1.0 * (x - 0.5) + -3.0 / 2.0 * y * y + 0.5 - 2.0 * (z - 1.0);
 87: } /* zero integral */
 88: static PetscScalar fx(PetscScalar x, PetscScalar y, PetscScalar z)
 89: {
 90:   return 0.0 * x + 2.0 - 12.0 * y + 12.0 * y * y + 0.0 * z + 1.0;
 91: }
 92: static PetscScalar fy(PetscScalar x, PetscScalar y, PetscScalar z)
 93: {
 94:   return 2.0 - 12.0 * x + 12.0 * x * x + 3.0 * y + 0.0 * z;
 95: }
 96: static PetscScalar fz(PetscScalar x, PetscScalar y, PetscScalar z)
 97: {
 98:   return 0.0 * x + 0.0 * y + 0.0 * z + 2.0;
 99: }
100: static PetscScalar g(PetscScalar x, PetscScalar y, PetscScalar z)
101: {
102:   return 0.0 * x + 0.0 * y + 0.0 * z + 0.0;
103: }

105: int main(int argc, char **argv)
106: {
107:   DM        dmSol;
108:   Vec       sol, solRef, rhs;
109:   Mat       A;
110:   KSP       ksp;
111:   PC        pc;
112:   PetscBool pinPressure;

114:   /* Initialize PETSc and process command line arguments */
115:   PetscFunctionBeginUser;
116:   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
117:   pinPressure = PETSC_TRUE;
118:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-pinpressure", &pinPressure, NULL));

120:   /* Create 3D DMStag for the solution, and set up. */
121:   {
122:     const PetscInt dof0 = 0, dof1 = 0, dof2 = 1, dof3 = 1; /* 1 dof on each face and element center */
123:     const PetscInt stencilWidth = 1;
124:     PetscCall(DMStagCreate3d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, 4, 5, 6, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE, dof0, dof1, dof2, dof3, DMSTAG_STENCIL_BOX, stencilWidth, NULL, NULL, NULL, &dmSol));
125:     PetscCall(DMSetFromOptions(dmSol));
126:     PetscCall(DMSetUp(dmSol));
127:     PetscCall(DMStagSetUniformCoordinatesExplicit(dmSol, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0));
128:     /* Note: also see ex2.c, where another, more efficient option is demonstrated,
129:        using DMStagSetUniformCoordinatesProduct() */
130:   }

132:   /* Compute (manufactured) reference solution */
133:   PetscCall(CreateReferenceSolution(dmSol, &solRef));

135:   /* Assemble system */
136:   PetscCall(CreateSystem(dmSol, &A, &rhs, pinPressure));

138:   /* Attach a constant-pressure nullspace to the operator */
139:   if (!pinPressure) PetscCall(AttachNullspace(dmSol, A));

141:   /* Solve */
142:   PetscCall(DMCreateGlobalVector(dmSol, &sol));
143:   PetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
144:   PetscCall(KSPSetType(ksp, KSPFGMRES));
145:   PetscCall(KSPSetOperators(ksp, A, A));
146:   PetscCall(KSPGetPC(ksp, &pc));
147:   PetscCall(PCSetType(pc, PCFIELDSPLIT));
148:   PetscCall(PCFieldSplitSetDetectSaddlePoint(pc, PETSC_TRUE));
149:   PetscCall(KSPSetFromOptions(ksp));
150:   PetscCall(KSPSolve(ksp, rhs, sol));

152:   /* Check Solution */
153:   PetscCall(CheckSolution(sol, solRef));

155:   /* Clean up and finalize PETSc */
156:   PetscCall(KSPDestroy(&ksp));
157:   PetscCall(VecDestroy(&sol));
158:   PetscCall(VecDestroy(&solRef));
159:   PetscCall(VecDestroy(&rhs));
160:   PetscCall(MatDestroy(&A));
161:   PetscCall(DMDestroy(&dmSol));
162:   PetscCall(PetscFinalize());
163:   return 0;
164: }

166: static PetscErrorCode CreateSystem(DM dmSol, Mat *pA, Vec *pRhs, PetscBool pinPressure)
167: {
168:   Vec             rhs, coordLocal;
169:   Mat             A;
170:   PetscInt        startx, starty, startz, N[3], nx, ny, nz, ex, ey, ez, d;
171:   PetscInt        icp[3], icux[3], icuy[3], icuz[3], icux_right[3], icuy_up[3], icuz_front[3];
172:   PetscReal       hx, hy, hz;
173:   DM              dmCoord;
174:   PetscScalar ****arrCoord;

176:   PetscFunctionBeginUser;
177:   PetscCall(DMCreateMatrix(dmSol, pA));
178:   A = *pA;
179:   PetscCall(DMCreateGlobalVector(dmSol, pRhs));
180:   rhs = *pRhs;

182:   PetscCall(DMStagGetCorners(dmSol, &startx, &starty, &startz, &nx, &ny, &nz, NULL, NULL, NULL));
183:   PetscCall(DMStagGetGlobalSizes(dmSol, &N[0], &N[1], &N[2]));
184:   PetscCheck(N[0] >= 2 && N[1] >= 2 && N[2] >= 2, PetscObjectComm((PetscObject)dmSol), PETSC_ERR_ARG_SIZ, "This example requires at least two elements in each dimensions");
185:   hx = 1.0 / N[0];
186:   hy = 1.0 / N[1];
187:   hz = 1.0 / N[2];
188:   PetscCall(DMGetCoordinateDM(dmSol, &dmCoord));
189:   PetscCall(DMGetCoordinatesLocal(dmSol, &coordLocal));
190:   PetscCall(DMStagVecGetArrayRead(dmCoord, coordLocal, &arrCoord));
191:   for (d = 0; d < 3; ++d) {
192:     PetscCall(DMStagGetLocationSlot(dmCoord, ELEMENT, d, &icp[d]));
193:     PetscCall(DMStagGetLocationSlot(dmCoord, LEFT, d, &icux[d]));
194:     PetscCall(DMStagGetLocationSlot(dmCoord, DOWN, d, &icuy[d]));
195:     PetscCall(DMStagGetLocationSlot(dmCoord, BACK, d, &icuz[d]));
196:     PetscCall(DMStagGetLocationSlot(dmCoord, RIGHT, d, &icux_right[d]));
197:     PetscCall(DMStagGetLocationSlot(dmCoord, UP, d, &icuy_up[d]));
198:     PetscCall(DMStagGetLocationSlot(dmCoord, FRONT, d, &icuz_front[d]));
199:   }

201:   /* Loop over all local elements. Note that it may be more efficient in real
202:      applications to loop over each boundary separately */
203:   for (ez = startz; ez < startz + nz; ++ez) { /* With DMStag, always iterate x fastest, y second fastest, z slowest */
204:     for (ey = starty; ey < starty + ny; ++ey) {
205:       for (ex = startx; ex < startx + nx; ++ex) {
206:         if (ex == N[0] - 1) {
207:           /* Right Boundary velocity Dirichlet */
208:           DMStagStencil     row;
209:           PetscScalar       valRhs;
210:           const PetscScalar valA = 1.0;
211:           row.i                  = ex;
212:           row.j                  = ey;
213:           row.k                  = ez;
214:           row.loc                = RIGHT;
215:           row.c                  = 0;
216:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, 1, &row, &valA, INSERT_VALUES));
217:           valRhs = uxRef(arrCoord[ez][ey][ex][icux_right[0]], arrCoord[ez][ey][ex][icux_right[1]], arrCoord[ez][ey][ex][icux_right[2]]);
218:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
219:         }
220:         if (ey == N[1] - 1) {
221:           /* Top boundary velocity Dirichlet */
222:           DMStagStencil     row;
223:           PetscScalar       valRhs;
224:           const PetscScalar valA = 1.0;
225:           row.i                  = ex;
226:           row.j                  = ey;
227:           row.k                  = ez;
228:           row.loc                = UP;
229:           row.c                  = 0;
230:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, 1, &row, &valA, INSERT_VALUES));
231:           valRhs = uyRef(arrCoord[ez][ey][ex][icuy_up[0]], arrCoord[ez][ey][ex][icuy_up[1]], arrCoord[ez][ey][ex][icuy_up[2]]);
232:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
233:         }
234:         if (ez == N[2] - 1) {
235:           /* Front boundary velocity Dirichlet */
236:           DMStagStencil     row;
237:           PetscScalar       valRhs;
238:           const PetscScalar valA = 1.0;
239:           row.i                  = ex;
240:           row.j                  = ey;
241:           row.k                  = ez;
242:           row.loc                = FRONT;
243:           row.c                  = 0;
244:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, 1, &row, &valA, INSERT_VALUES));
245:           valRhs = uzRef(arrCoord[ez][ey][ex][icuz_front[0]], arrCoord[ez][ey][ex][icuz_front[1]], arrCoord[ez][ey][ex][icuz_front[2]]);
246:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
247:         }

249:         /* Equation on left face of this element */
250:         if (ex == 0) {
251:           /* Left velocity Dirichlet */
252:           DMStagStencil     row;
253:           PetscScalar       valRhs;
254:           const PetscScalar valA = 1.0;
255:           row.i                  = ex;
256:           row.j                  = ey;
257:           row.k                  = ez;
258:           row.loc                = LEFT;
259:           row.c                  = 0;
260:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, 1, &row, &valA, INSERT_VALUES));
261:           valRhs = uxRef(arrCoord[ez][ey][ex][icux[0]], arrCoord[ez][ey][ex][icux[1]], arrCoord[ez][ey][ex][icux[2]]);
262:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
263:         } else {
264:           /* X-momentum interior equation : (u_xx + u_yy + u_zz) - p_x = f^x */
265:           DMStagStencil row, col[9];
266:           PetscScalar   valA[9], valRhs;
267:           PetscInt      nEntries;

269:           row.i   = ex;
270:           row.j   = ey;
271:           row.k   = ez;
272:           row.loc = LEFT;
273:           row.c   = 0;
274:           if (ey == 0) {
275:             if (ez == 0) {
276:               nEntries   = 7;
277:               col[0].i   = ex;
278:               col[0].j   = ey;
279:               col[0].k   = ez;
280:               col[0].loc = LEFT;
281:               col[0].c   = 0;
282:               valA[0]    = -2.0 / (hx * hx) + -1.0 / (hy * hy) - 1.0 / (hz * hz);
283:               /* Missing down term */
284:               col[1].i   = ex;
285:               col[1].j   = ey + 1;
286:               col[1].k   = ez;
287:               col[1].loc = LEFT;
288:               col[1].c   = 0;
289:               valA[1]    = 1.0 / (hy * hy);
290:               col[2].i   = ex - 1;
291:               col[2].j   = ey;
292:               col[2].k   = ez;
293:               col[2].loc = LEFT;
294:               col[2].c   = 0;
295:               valA[2]    = 1.0 / (hx * hx);
296:               col[3].i   = ex + 1;
297:               col[3].j   = ey;
298:               col[3].k   = ez;
299:               col[3].loc = LEFT;
300:               col[3].c   = 0;
301:               valA[3]    = 1.0 / (hx * hx);
302:               /* Missing back term */
303:               col[4].i   = ex;
304:               col[4].j   = ey;
305:               col[4].k   = ez + 1;
306:               col[4].loc = LEFT;
307:               col[4].c   = 0;
308:               valA[4]    = 1.0 / (hz * hz);
309:               col[5].i   = ex - 1;
310:               col[5].j   = ey;
311:               col[5].k   = ez;
312:               col[5].loc = ELEMENT;
313:               col[5].c   = 0;
314:               valA[5]    = 1.0 / hx;
315:               col[6].i   = ex;
316:               col[6].j   = ey;
317:               col[6].k   = ez;
318:               col[6].loc = ELEMENT;
319:               col[6].c   = 0;
320:               valA[6]    = -1.0 / hx;
321:             } else if (ez == N[2] - 1) {
322:               nEntries   = 7;
323:               col[0].i   = ex;
324:               col[0].j   = ey;
325:               col[0].k   = ez;
326:               col[0].loc = LEFT;
327:               col[0].c   = 0;
328:               valA[0]    = -2.0 / (hx * hx) + -1.0 / (hy * hy) - 1.0 / (hz * hz);
329:               /* Missing down term */
330:               col[1].i   = ex;
331:               col[1].j   = ey + 1;
332:               col[1].k   = ez;
333:               col[1].loc = LEFT;
334:               col[1].c   = 0;
335:               valA[1]    = 1.0 / (hy * hy);
336:               col[2].i   = ex - 1;
337:               col[2].j   = ey;
338:               col[2].k   = ez;
339:               col[2].loc = LEFT;
340:               col[2].c   = 0;
341:               valA[2]    = 1.0 / (hx * hx);
342:               col[3].i   = ex + 1;
343:               col[3].j   = ey;
344:               col[3].k   = ez;
345:               col[3].loc = LEFT;
346:               col[3].c   = 0;
347:               valA[3]    = 1.0 / (hx * hx);
348:               col[4].i   = ex;
349:               col[4].j   = ey;
350:               col[4].k   = ez - 1;
351:               col[4].loc = LEFT;
352:               col[4].c   = 0;
353:               valA[4]    = 1.0 / (hz * hz);
354:               /* Missing front term */
355:               col[5].i   = ex - 1;
356:               col[5].j   = ey;
357:               col[5].k   = ez;
358:               col[5].loc = ELEMENT;
359:               col[5].c   = 0;
360:               valA[5]    = 1.0 / hx;
361:               col[6].i   = ex;
362:               col[6].j   = ey;
363:               col[6].k   = ez;
364:               col[6].loc = ELEMENT;
365:               col[6].c   = 0;
366:               valA[6]    = -1.0 / hx;
367:             } else {
368:               nEntries   = 8;
369:               col[0].i   = ex;
370:               col[0].j   = ey;
371:               col[0].k   = ez;
372:               col[0].loc = LEFT;
373:               col[0].c   = 0;
374:               valA[0]    = -2.0 / (hx * hx) + -1.0 / (hy * hy) - 2.0 / (hz * hz);
375:               /* Missing down term */
376:               col[1].i   = ex;
377:               col[1].j   = ey + 1;
378:               col[1].k   = ez;
379:               col[1].loc = LEFT;
380:               col[1].c   = 0;
381:               valA[1]    = 1.0 / (hy * hy);
382:               col[2].i   = ex - 1;
383:               col[2].j   = ey;
384:               col[2].k   = ez;
385:               col[2].loc = LEFT;
386:               col[2].c   = 0;
387:               valA[2]    = 1.0 / (hx * hx);
388:               col[3].i   = ex + 1;
389:               col[3].j   = ey;
390:               col[3].k   = ez;
391:               col[3].loc = LEFT;
392:               col[3].c   = 0;
393:               valA[3]    = 1.0 / (hx * hx);
394:               col[4].i   = ex;
395:               col[4].j   = ey;
396:               col[4].k   = ez - 1;
397:               col[4].loc = LEFT;
398:               col[4].c   = 0;
399:               valA[4]    = 1.0 / (hz * hz);
400:               col[5].i   = ex;
401:               col[5].j   = ey;
402:               col[5].k   = ez + 1;
403:               col[5].loc = LEFT;
404:               col[5].c   = 0;
405:               valA[5]    = 1.0 / (hz * hz);
406:               col[6].i   = ex - 1;
407:               col[6].j   = ey;
408:               col[6].k   = ez;
409:               col[6].loc = ELEMENT;
410:               col[6].c   = 0;
411:               valA[6]    = 1.0 / hx;
412:               col[7].i   = ex;
413:               col[7].j   = ey;
414:               col[7].k   = ez;
415:               col[7].loc = ELEMENT;
416:               col[7].c   = 0;
417:               valA[7]    = -1.0 / hx;
418:             }
419:           } else if (ey == N[1] - 1) {
420:             if (ez == 0) {
421:               nEntries   = 7;
422:               col[0].i   = ex;
423:               col[0].j   = ey;
424:               col[0].k   = ez;
425:               col[0].loc = LEFT;
426:               col[0].c   = 0;
427:               valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
428:               col[1].i   = ex;
429:               col[1].j   = ey - 1;
430:               col[1].k   = ez;
431:               col[1].loc = LEFT;
432:               col[1].c   = 0;
433:               valA[1]    = 1.0 / (hy * hy);
434:               /* Missing up term */
435:               col[2].i   = ex - 1;
436:               col[2].j   = ey;
437:               col[2].k   = ez;
438:               col[2].loc = LEFT;
439:               col[2].c   = 0;
440:               valA[2]    = 1.0 / (hx * hx);
441:               col[3].i   = ex + 1;
442:               col[3].j   = ey;
443:               col[3].k   = ez;
444:               col[3].loc = LEFT;
445:               col[3].c   = 0;
446:               valA[3]    = 1.0 / (hx * hx);
447:               /* Missing back entry */
448:               col[4].i   = ex;
449:               col[4].j   = ey;
450:               col[4].k   = ez + 1;
451:               col[4].loc = LEFT;
452:               col[4].c   = 0;
453:               valA[4]    = 1.0 / (hz * hz);
454:               col[5].i   = ex - 1;
455:               col[5].j   = ey;
456:               col[5].k   = ez;
457:               col[5].loc = ELEMENT;
458:               col[5].c   = 0;
459:               valA[5]    = 1.0 / hx;
460:               col[6].i   = ex;
461:               col[6].j   = ey;
462:               col[6].k   = ez;
463:               col[6].loc = ELEMENT;
464:               col[6].c   = 0;
465:               valA[6]    = -1.0 / hx;
466:             } else if (ez == N[2] - 1) {
467:               nEntries   = 7;
468:               col[0].i   = ex;
469:               col[0].j   = ey;
470:               col[0].k   = ez;
471:               col[0].loc = LEFT;
472:               col[0].c   = 0;
473:               valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
474:               col[1].i   = ex;
475:               col[1].j   = ey - 1;
476:               col[1].k   = ez;
477:               col[1].loc = LEFT;
478:               col[1].c   = 0;
479:               valA[1]    = 1.0 / (hy * hy);
480:               /* Missing up term */
481:               col[2].i   = ex - 1;
482:               col[2].j   = ey;
483:               col[2].k   = ez;
484:               col[2].loc = LEFT;
485:               col[2].c   = 0;
486:               valA[2]    = 1.0 / (hx * hx);
487:               col[3].i   = ex + 1;
488:               col[3].j   = ey;
489:               col[3].k   = ez;
490:               col[3].loc = LEFT;
491:               col[3].c   = 0;
492:               valA[3]    = 1.0 / (hx * hx);
493:               col[4].i   = ex;
494:               col[4].j   = ey;
495:               col[4].k   = ez - 1;
496:               col[4].loc = LEFT;
497:               col[4].c   = 0;
498:               valA[4]    = 1.0 / (hz * hz);
499:               /* Missing front term */
500:               col[5].i   = ex - 1;
501:               col[5].j   = ey;
502:               col[5].k   = ez;
503:               col[5].loc = ELEMENT;
504:               col[5].c   = 0;
505:               valA[5]    = 1.0 / hx;
506:               col[6].i   = ex;
507:               col[6].j   = ey;
508:               col[6].k   = ez;
509:               col[6].loc = ELEMENT;
510:               col[6].c   = 0;
511:               valA[6]    = -1.0 / hx;
512:             } else {
513:               nEntries   = 8;
514:               col[0].i   = ex;
515:               col[0].j   = ey;
516:               col[0].k   = ez;
517:               col[0].loc = LEFT;
518:               col[0].c   = 0;
519:               valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 2.0 / (hz * hz);
520:               col[1].i   = ex;
521:               col[1].j   = ey - 1;
522:               col[1].k   = ez;
523:               col[1].loc = LEFT;
524:               col[1].c   = 0;
525:               valA[1]    = 1.0 / (hy * hy);
526:               /* Missing up term */
527:               col[2].i   = ex - 1;
528:               col[2].j   = ey;
529:               col[2].k   = ez;
530:               col[2].loc = LEFT;
531:               col[2].c   = 0;
532:               valA[2]    = 1.0 / (hx * hx);
533:               col[3].i   = ex + 1;
534:               col[3].j   = ey;
535:               col[3].k   = ez;
536:               col[3].loc = LEFT;
537:               col[3].c   = 0;
538:               valA[3]    = 1.0 / (hx * hx);
539:               col[4].i   = ex;
540:               col[4].j   = ey;
541:               col[4].k   = ez - 1;
542:               col[4].loc = LEFT;
543:               col[4].c   = 0;
544:               valA[4]    = 1.0 / (hz * hz);
545:               col[5].i   = ex;
546:               col[5].j   = ey;
547:               col[5].k   = ez + 1;
548:               col[5].loc = LEFT;
549:               col[5].c   = 0;
550:               valA[5]    = 1.0 / (hz * hz);
551:               col[6].i   = ex - 1;
552:               col[6].j   = ey;
553:               col[6].k   = ez;
554:               col[6].loc = ELEMENT;
555:               col[6].c   = 0;
556:               valA[6]    = 1.0 / hx;
557:               col[7].i   = ex;
558:               col[7].j   = ey;
559:               col[7].k   = ez;
560:               col[7].loc = ELEMENT;
561:               col[7].c   = 0;
562:               valA[7]    = -1.0 / hx;
563:             }
564:           } else if (ez == 0) {
565:             nEntries   = 8;
566:             col[0].i   = ex;
567:             col[0].j   = ey;
568:             col[0].k   = ez;
569:             col[0].loc = LEFT;
570:             col[0].c   = 0;
571:             valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
572:             col[1].i   = ex;
573:             col[1].j   = ey - 1;
574:             col[1].k   = ez;
575:             col[1].loc = LEFT;
576:             col[1].c   = 0;
577:             valA[1]    = 1.0 / (hy * hy);
578:             col[2].i   = ex;
579:             col[2].j   = ey + 1;
580:             col[2].k   = ez;
581:             col[2].loc = LEFT;
582:             col[2].c   = 0;
583:             valA[2]    = 1.0 / (hy * hy);
584:             col[3].i   = ex - 1;
585:             col[3].j   = ey;
586:             col[3].k   = ez;
587:             col[3].loc = LEFT;
588:             col[3].c   = 0;
589:             valA[3]    = 1.0 / (hx * hx);
590:             col[4].i   = ex + 1;
591:             col[4].j   = ey;
592:             col[4].k   = ez;
593:             col[4].loc = LEFT;
594:             col[4].c   = 0;
595:             valA[4]    = 1.0 / (hx * hx);
596:             /* Missing back term */
597:             col[5].i   = ex;
598:             col[5].j   = ey;
599:             col[5].k   = ez + 1;
600:             col[5].loc = LEFT;
601:             col[5].c   = 0;
602:             valA[5]    = 1.0 / (hz * hz);
603:             col[6].i   = ex - 1;
604:             col[6].j   = ey;
605:             col[6].k   = ez;
606:             col[6].loc = ELEMENT;
607:             col[6].c   = 0;
608:             valA[6]    = 1.0 / hx;
609:             col[7].i   = ex;
610:             col[7].j   = ey;
611:             col[7].k   = ez;
612:             col[7].loc = ELEMENT;
613:             col[7].c   = 0;
614:             valA[7]    = -1.0 / hx;
615:           } else if (ez == N[2] - 1) {
616:             nEntries   = 8;
617:             col[0].i   = ex;
618:             col[0].j   = ey;
619:             col[0].k   = ez;
620:             col[0].loc = LEFT;
621:             col[0].c   = 0;
622:             valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
623:             col[1].i   = ex;
624:             col[1].j   = ey - 1;
625:             col[1].k   = ez;
626:             col[1].loc = LEFT;
627:             col[1].c   = 0;
628:             valA[1]    = 1.0 / (hy * hy);
629:             col[2].i   = ex;
630:             col[2].j   = ey + 1;
631:             col[2].k   = ez;
632:             col[2].loc = LEFT;
633:             col[2].c   = 0;
634:             valA[2]    = 1.0 / (hy * hy);
635:             col[3].i   = ex - 1;
636:             col[3].j   = ey;
637:             col[3].k   = ez;
638:             col[3].loc = LEFT;
639:             col[3].c   = 0;
640:             valA[3]    = 1.0 / (hx * hx);
641:             col[4].i   = ex + 1;
642:             col[4].j   = ey;
643:             col[4].k   = ez;
644:             col[4].loc = LEFT;
645:             col[4].c   = 0;
646:             valA[4]    = 1.0 / (hx * hx);
647:             col[5].i   = ex;
648:             col[5].j   = ey;
649:             col[5].k   = ez - 1;
650:             col[5].loc = LEFT;
651:             col[5].c   = 0;
652:             valA[5]    = 1.0 / (hz * hz);
653:             /* Missing front term */
654:             col[6].i   = ex - 1;
655:             col[6].j   = ey;
656:             col[6].k   = ez;
657:             col[6].loc = ELEMENT;
658:             col[6].c   = 0;
659:             valA[6]    = 1.0 / hx;
660:             col[7].i   = ex;
661:             col[7].j   = ey;
662:             col[7].k   = ez;
663:             col[7].loc = ELEMENT;
664:             col[7].c   = 0;
665:             valA[7]    = -1.0 / hx;
666:           } else {
667:             nEntries   = 9;
668:             col[0].i   = ex;
669:             col[0].j   = ey;
670:             col[0].k   = ez;
671:             col[0].loc = LEFT;
672:             col[0].c   = 0;
673:             valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 2.0 / (hz * hz);
674:             col[1].i   = ex;
675:             col[1].j   = ey - 1;
676:             col[1].k   = ez;
677:             col[1].loc = LEFT;
678:             col[1].c   = 0;
679:             valA[1]    = 1.0 / (hy * hy);
680:             col[2].i   = ex;
681:             col[2].j   = ey + 1;
682:             col[2].k   = ez;
683:             col[2].loc = LEFT;
684:             col[2].c   = 0;
685:             valA[2]    = 1.0 / (hy * hy);
686:             col[3].i   = ex - 1;
687:             col[3].j   = ey;
688:             col[3].k   = ez;
689:             col[3].loc = LEFT;
690:             col[3].c   = 0;
691:             valA[3]    = 1.0 / (hx * hx);
692:             col[4].i   = ex + 1;
693:             col[4].j   = ey;
694:             col[4].k   = ez;
695:             col[4].loc = LEFT;
696:             col[4].c   = 0;
697:             valA[4]    = 1.0 / (hx * hx);
698:             col[5].i   = ex;
699:             col[5].j   = ey;
700:             col[5].k   = ez - 1;
701:             col[5].loc = LEFT;
702:             col[5].c   = 0;
703:             valA[5]    = 1.0 / (hz * hz);
704:             col[6].i   = ex;
705:             col[6].j   = ey;
706:             col[6].k   = ez + 1;
707:             col[6].loc = LEFT;
708:             col[6].c   = 0;
709:             valA[6]    = 1.0 / (hz * hz);
710:             col[7].i   = ex - 1;
711:             col[7].j   = ey;
712:             col[7].k   = ez;
713:             col[7].loc = ELEMENT;
714:             col[7].c   = 0;
715:             valA[7]    = 1.0 / hx;
716:             col[8].i   = ex;
717:             col[8].j   = ey;
718:             col[8].k   = ez;
719:             col[8].loc = ELEMENT;
720:             col[8].c   = 0;
721:             valA[8]    = -1.0 / hx;
722:           }
723:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, nEntries, col, valA, INSERT_VALUES));
724:           valRhs = fx(arrCoord[ez][ey][ex][icux[0]], arrCoord[ez][ey][ex][icux[1]], arrCoord[ez][ey][ex][icux[2]]);
725:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
726:         }

728:         /* Equation on bottom face of this element */
729:         if (ey == 0) {
730:           /* Bottom boundary velocity Dirichlet */
731:           DMStagStencil     row;
732:           PetscScalar       valRhs;
733:           const PetscScalar valA = 1.0;
734:           row.i                  = ex;
735:           row.j                  = ey;
736:           row.k                  = ez;
737:           row.loc                = DOWN;
738:           row.c                  = 0;
739:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, 1, &row, &valA, INSERT_VALUES));
740:           valRhs = uyRef(arrCoord[ez][ey][ex][icuy[0]], arrCoord[ez][ey][ex][icuy[1]], arrCoord[ez][ey][ex][icuy[2]]);
741:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
742:         } else {
743:           /* Y-momentum equation, (v_xx + v_yy + v_zz) - p_y = f^y */
744:           DMStagStencil row, col[9];
745:           PetscScalar   valA[9], valRhs;
746:           PetscInt      nEntries;

748:           row.i   = ex;
749:           row.j   = ey;
750:           row.k   = ez;
751:           row.loc = DOWN;
752:           row.c   = 0;
753:           if (ex == 0) {
754:             if (ez == 0) {
755:               nEntries   = 7;
756:               col[0].i   = ex;
757:               col[0].j   = ey;
758:               col[0].k   = ez;
759:               col[0].loc = DOWN;
760:               col[0].c   = 0;
761:               valA[0]    = -1.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
762:               col[1].i   = ex;
763:               col[1].j   = ey - 1;
764:               col[1].k   = ez;
765:               col[1].loc = DOWN;
766:               col[1].c   = 0;
767:               valA[1]    = 1.0 / (hy * hy);
768:               col[2].i   = ex;
769:               col[2].j   = ey + 1;
770:               col[2].k   = ez;
771:               col[2].loc = DOWN;
772:               col[2].c   = 0;
773:               valA[2]    = 1.0 / (hy * hy);
774:               /* Left term missing */
775:               col[3].i   = ex + 1;
776:               col[3].j   = ey;
777:               col[3].k   = ez;
778:               col[3].loc = DOWN;
779:               col[3].c   = 0;
780:               valA[3]    = 1.0 / (hx * hx);
781:               /* Back term missing */
782:               col[4].i   = ex;
783:               col[4].j   = ey;
784:               col[4].k   = ez + 1;
785:               col[4].loc = DOWN;
786:               col[4].c   = 0;
787:               valA[4]    = 1.0 / (hz * hz);
788:               col[5].i   = ex;
789:               col[5].j   = ey - 1;
790:               col[5].k   = ez;
791:               col[5].loc = ELEMENT;
792:               col[5].c   = 0;
793:               valA[5]    = 1.0 / hy;
794:               col[6].i   = ex;
795:               col[6].j   = ey;
796:               col[6].k   = ez;
797:               col[6].loc = ELEMENT;
798:               col[6].c   = 0;
799:               valA[6]    = -1.0 / hy;
800:             } else if (ez == N[2] - 1) {
801:               nEntries   = 7;
802:               col[0].i   = ex;
803:               col[0].j   = ey;
804:               col[0].k   = ez;
805:               col[0].loc = DOWN;
806:               col[0].c   = 0;
807:               valA[0]    = -1.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
808:               col[1].i   = ex;
809:               col[1].j   = ey - 1;
810:               col[1].k   = ez;
811:               col[1].loc = DOWN;
812:               col[1].c   = 0;
813:               valA[1]    = 1.0 / (hy * hy);
814:               col[2].i   = ex;
815:               col[2].j   = ey + 1;
816:               col[2].k   = ez;
817:               col[2].loc = DOWN;
818:               col[2].c   = 0;
819:               valA[2]    = 1.0 / (hy * hy);
820:               /* Left term missing */
821:               col[3].i   = ex + 1;
822:               col[3].j   = ey;
823:               col[3].k   = ez;
824:               col[3].loc = DOWN;
825:               col[3].c   = 0;
826:               valA[3]    = 1.0 / (hx * hx);
827:               col[4].i   = ex;
828:               col[4].j   = ey;
829:               col[4].k   = ez - 1;
830:               col[4].loc = DOWN;
831:               col[4].c   = 0;
832:               valA[4]    = 1.0 / (hz * hz);
833:               /* Front term missing */
834:               col[5].i   = ex;
835:               col[5].j   = ey - 1;
836:               col[5].k   = ez;
837:               col[5].loc = ELEMENT;
838:               col[5].c   = 0;
839:               valA[5]    = 1.0 / hy;
840:               col[6].i   = ex;
841:               col[6].j   = ey;
842:               col[6].k   = ez;
843:               col[6].loc = ELEMENT;
844:               col[6].c   = 0;
845:               valA[6]    = -1.0 / hy;
846:             } else {
847:               nEntries   = 8;
848:               col[0].i   = ex;
849:               col[0].j   = ey;
850:               col[0].k   = ez;
851:               col[0].loc = DOWN;
852:               col[0].c   = 0;
853:               valA[0]    = -1.0 / (hx * hx) + -2.0 / (hy * hy) - 2.0 / (hz * hz);
854:               col[1].i   = ex;
855:               col[1].j   = ey - 1;
856:               col[1].k   = ez;
857:               col[1].loc = DOWN;
858:               col[1].c   = 0;
859:               valA[1]    = 1.0 / (hy * hy);
860:               col[2].i   = ex;
861:               col[2].j   = ey + 1;
862:               col[2].k   = ez;
863:               col[2].loc = DOWN;
864:               col[2].c   = 0;
865:               valA[2]    = 1.0 / (hy * hy);
866:               /* Left term missing */
867:               col[3].i   = ex + 1;
868:               col[3].j   = ey;
869:               col[3].k   = ez;
870:               col[3].loc = DOWN;
871:               col[3].c   = 0;
872:               valA[3]    = 1.0 / (hx * hx);
873:               col[4].i   = ex;
874:               col[4].j   = ey;
875:               col[4].k   = ez - 1;
876:               col[4].loc = DOWN;
877:               col[4].c   = 0;
878:               valA[4]    = 1.0 / (hz * hz);
879:               col[5].i   = ex;
880:               col[5].j   = ey;
881:               col[5].k   = ez + 1;
882:               col[5].loc = DOWN;
883:               col[5].c   = 0;
884:               valA[5]    = 1.0 / (hz * hz);
885:               col[6].i   = ex;
886:               col[6].j   = ey - 1;
887:               col[6].k   = ez;
888:               col[6].loc = ELEMENT;
889:               col[6].c   = 0;
890:               valA[6]    = 1.0 / hy;
891:               col[7].i   = ex;
892:               col[7].j   = ey;
893:               col[7].k   = ez;
894:               col[7].loc = ELEMENT;
895:               col[7].c   = 0;
896:               valA[7]    = -1.0 / hy;
897:             }
898:           } else if (ex == N[0] - 1) {
899:             if (ez == 0) {
900:               nEntries   = 7;
901:               col[0].i   = ex;
902:               col[0].j   = ey;
903:               col[0].k   = ez;
904:               col[0].loc = DOWN;
905:               col[0].c   = 0;
906:               valA[0]    = -1.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
907:               col[1].i   = ex;
908:               col[1].j   = ey - 1;
909:               col[1].k   = ez;
910:               col[1].loc = DOWN;
911:               col[1].c   = 0;
912:               valA[1]    = 1.0 / (hy * hy);
913:               col[2].i   = ex;
914:               col[2].j   = ey + 1;
915:               col[2].k   = ez;
916:               col[2].loc = DOWN;
917:               col[2].c   = 0;
918:               valA[2]    = 1.0 / (hy * hy);
919:               col[3].i   = ex - 1;
920:               col[3].j   = ey;
921:               col[3].k   = ez;
922:               col[3].loc = DOWN;
923:               col[3].c   = 0;
924:               valA[3]    = 1.0 / (hx * hx);
925:               /* Right term missing */
926:               /* Back term missing */
927:               col[4].i   = ex;
928:               col[4].j   = ey;
929:               col[4].k   = ez + 1;
930:               col[4].loc = DOWN;
931:               col[4].c   = 0;
932:               valA[4]    = 1.0 / (hz * hz);
933:               col[5].i   = ex;
934:               col[5].j   = ey - 1;
935:               col[5].k   = ez;
936:               col[5].loc = ELEMENT;
937:               col[5].c   = 0;
938:               valA[5]    = 1.0 / hy;
939:               col[6].i   = ex;
940:               col[6].j   = ey;
941:               col[6].k   = ez;
942:               col[6].loc = ELEMENT;
943:               col[6].c   = 0;
944:               valA[6]    = -1.0 / hy;
945:             } else if (ez == N[2] - 1) {
946:               nEntries   = 7;
947:               col[0].i   = ex;
948:               col[0].j   = ey;
949:               col[0].k   = ez;
950:               col[0].loc = DOWN;
951:               col[0].c   = 0;
952:               valA[0]    = -1.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
953:               col[1].i   = ex;
954:               col[1].j   = ey - 1;
955:               col[1].k   = ez;
956:               col[1].loc = DOWN;
957:               col[1].c   = 0;
958:               valA[1]    = 1.0 / (hy * hy);
959:               col[2].i   = ex;
960:               col[2].j   = ey + 1;
961:               col[2].k   = ez;
962:               col[2].loc = DOWN;
963:               col[2].c   = 0;
964:               valA[2]    = 1.0 / (hy * hy);
965:               col[3].i   = ex - 1;
966:               col[3].j   = ey;
967:               col[3].k   = ez;
968:               col[3].loc = DOWN;
969:               col[3].c   = 0;
970:               valA[3]    = 1.0 / (hx * hx);
971:               /* Right term missing */
972:               col[4].i   = ex;
973:               col[4].j   = ey;
974:               col[4].k   = ez - 1;
975:               col[4].loc = DOWN;
976:               col[4].c   = 0;
977:               valA[4]    = 1.0 / (hz * hz);
978:               /* Front term missing */
979:               col[5].i   = ex;
980:               col[5].j   = ey - 1;
981:               col[5].k   = ez;
982:               col[5].loc = ELEMENT;
983:               col[5].c   = 0;
984:               valA[5]    = 1.0 / hy;
985:               col[6].i   = ex;
986:               col[6].j   = ey;
987:               col[6].k   = ez;
988:               col[6].loc = ELEMENT;
989:               col[6].c   = 0;
990:               valA[6]    = -1.0 / hy;
991:             } else {
992:               nEntries   = 8;
993:               col[0].i   = ex;
994:               col[0].j   = ey;
995:               col[0].k   = ez;
996:               col[0].loc = DOWN;
997:               col[0].c   = 0;
998:               valA[0]    = -1.0 / (hx * hx) + -2.0 / (hy * hy) - 2.0 / (hz * hz);
999:               col[1].i   = ex;
1000:               col[1].j   = ey - 1;
1001:               col[1].k   = ez;
1002:               col[1].loc = DOWN;
1003:               col[1].c   = 0;
1004:               valA[1]    = 1.0 / (hy * hy);
1005:               col[2].i   = ex;
1006:               col[2].j   = ey + 1;
1007:               col[2].k   = ez;
1008:               col[2].loc = DOWN;
1009:               col[2].c   = 0;
1010:               valA[2]    = 1.0 / (hy * hy);
1011:               col[3].i   = ex - 1;
1012:               col[3].j   = ey;
1013:               col[3].k   = ez;
1014:               col[3].loc = DOWN;
1015:               col[3].c   = 0;
1016:               valA[3]    = 1.0 / (hx * hx);
1017:               /* Right term missing */
1018:               col[4].i   = ex;
1019:               col[4].j   = ey;
1020:               col[4].k   = ez - 1;
1021:               col[4].loc = DOWN;
1022:               col[4].c   = 0;
1023:               valA[4]    = 1.0 / (hz * hz);
1024:               col[5].i   = ex;
1025:               col[5].j   = ey;
1026:               col[5].k   = ez + 1;
1027:               col[5].loc = DOWN;
1028:               col[5].c   = 0;
1029:               valA[5]    = 1.0 / (hz * hz);
1030:               col[6].i   = ex;
1031:               col[6].j   = ey - 1;
1032:               col[6].k   = ez;
1033:               col[6].loc = ELEMENT;
1034:               col[6].c   = 0;
1035:               valA[6]    = 1.0 / hy;
1036:               col[7].i   = ex;
1037:               col[7].j   = ey;
1038:               col[7].k   = ez;
1039:               col[7].loc = ELEMENT;
1040:               col[7].c   = 0;
1041:               valA[7]    = -1.0 / hy;
1042:             }
1043:           } else if (ez == 0) {
1044:             nEntries   = 8;
1045:             col[0].i   = ex;
1046:             col[0].j   = ey;
1047:             col[0].k   = ez;
1048:             col[0].loc = DOWN;
1049:             col[0].c   = 0;
1050:             valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
1051:             col[1].i   = ex;
1052:             col[1].j   = ey - 1;
1053:             col[1].k   = ez;
1054:             col[1].loc = DOWN;
1055:             col[1].c   = 0;
1056:             valA[1]    = 1.0 / (hy * hy);
1057:             col[2].i   = ex;
1058:             col[2].j   = ey + 1;
1059:             col[2].k   = ez;
1060:             col[2].loc = DOWN;
1061:             col[2].c   = 0;
1062:             valA[2]    = 1.0 / (hy * hy);
1063:             col[3].i   = ex - 1;
1064:             col[3].j   = ey;
1065:             col[3].k   = ez;
1066:             col[3].loc = DOWN;
1067:             col[3].c   = 0;
1068:             valA[3]    = 1.0 / (hx * hx);
1069:             col[4].i   = ex + 1;
1070:             col[4].j   = ey;
1071:             col[4].k   = ez;
1072:             col[4].loc = DOWN;
1073:             col[4].c   = 0;
1074:             valA[4]    = 1.0 / (hx * hx);
1075:             /* Back term missing */
1076:             col[5].i   = ex;
1077:             col[5].j   = ey;
1078:             col[5].k   = ez + 1;
1079:             col[5].loc = DOWN;
1080:             col[5].c   = 0;
1081:             valA[5]    = 1.0 / (hz * hz);
1082:             col[6].i   = ex;
1083:             col[6].j   = ey - 1;
1084:             col[6].k   = ez;
1085:             col[6].loc = ELEMENT;
1086:             col[6].c   = 0;
1087:             valA[6]    = 1.0 / hy;
1088:             col[7].i   = ex;
1089:             col[7].j   = ey;
1090:             col[7].k   = ez;
1091:             col[7].loc = ELEMENT;
1092:             col[7].c   = 0;
1093:             valA[7]    = -1.0 / hy;
1094:           } else if (ez == N[2] - 1) {
1095:             nEntries   = 8;
1096:             col[0].i   = ex;
1097:             col[0].j   = ey;
1098:             col[0].k   = ez;
1099:             col[0].loc = DOWN;
1100:             col[0].c   = 0;
1101:             valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 1.0 / (hz * hz);
1102:             col[1].i   = ex;
1103:             col[1].j   = ey - 1;
1104:             col[1].k   = ez;
1105:             col[1].loc = DOWN;
1106:             col[1].c   = 0;
1107:             valA[1]    = 1.0 / (hy * hy);
1108:             col[2].i   = ex;
1109:             col[2].j   = ey + 1;
1110:             col[2].k   = ez;
1111:             col[2].loc = DOWN;
1112:             col[2].c   = 0;
1113:             valA[2]    = 1.0 / (hy * hy);
1114:             col[3].i   = ex - 1;
1115:             col[3].j   = ey;
1116:             col[3].k   = ez;
1117:             col[3].loc = DOWN;
1118:             col[3].c   = 0;
1119:             valA[3]    = 1.0 / (hx * hx);
1120:             col[4].i   = ex + 1;
1121:             col[4].j   = ey;
1122:             col[4].k   = ez;
1123:             col[4].loc = DOWN;
1124:             col[4].c   = 0;
1125:             valA[4]    = 1.0 / (hx * hx);
1126:             col[5].i   = ex;
1127:             col[5].j   = ey;
1128:             col[5].k   = ez - 1;
1129:             col[5].loc = DOWN;
1130:             col[5].c   = 0;
1131:             valA[5]    = 1.0 / (hz * hz);
1132:             /* Front term missing */
1133:             col[6].i   = ex;
1134:             col[6].j   = ey - 1;
1135:             col[6].k   = ez;
1136:             col[6].loc = ELEMENT;
1137:             col[6].c   = 0;
1138:             valA[6]    = 1.0 / hy;
1139:             col[7].i   = ex;
1140:             col[7].j   = ey;
1141:             col[7].k   = ez;
1142:             col[7].loc = ELEMENT;
1143:             col[7].c   = 0;
1144:             valA[7]    = -1.0 / hy;
1145:           } else {
1146:             nEntries   = 9;
1147:             col[0].i   = ex;
1148:             col[0].j   = ey;
1149:             col[0].k   = ez;
1150:             col[0].loc = DOWN;
1151:             col[0].c   = 0;
1152:             valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 2.0 / (hz * hz);
1153:             col[1].i   = ex;
1154:             col[1].j   = ey - 1;
1155:             col[1].k   = ez;
1156:             col[1].loc = DOWN;
1157:             col[1].c   = 0;
1158:             valA[1]    = 1.0 / (hy * hy);
1159:             col[2].i   = ex;
1160:             col[2].j   = ey + 1;
1161:             col[2].k   = ez;
1162:             col[2].loc = DOWN;
1163:             col[2].c   = 0;
1164:             valA[2]    = 1.0 / (hy * hy);
1165:             col[3].i   = ex - 1;
1166:             col[3].j   = ey;
1167:             col[3].k   = ez;
1168:             col[3].loc = DOWN;
1169:             col[3].c   = 0;
1170:             valA[3]    = 1.0 / (hx * hx);
1171:             col[4].i   = ex + 1;
1172:             col[4].j   = ey;
1173:             col[4].k   = ez;
1174:             col[4].loc = DOWN;
1175:             col[4].c   = 0;
1176:             valA[4]    = 1.0 / (hx * hx);
1177:             col[5].i   = ex;
1178:             col[5].j   = ey;
1179:             col[5].k   = ez - 1;
1180:             col[5].loc = DOWN;
1181:             col[5].c   = 0;
1182:             valA[5]    = 1.0 / (hz * hz);
1183:             col[6].i   = ex;
1184:             col[6].j   = ey;
1185:             col[6].k   = ez + 1;
1186:             col[6].loc = DOWN;
1187:             col[6].c   = 0;
1188:             valA[6]    = 1.0 / (hz * hz);
1189:             col[7].i   = ex;
1190:             col[7].j   = ey - 1;
1191:             col[7].k   = ez;
1192:             col[7].loc = ELEMENT;
1193:             col[7].c   = 0;
1194:             valA[7]    = 1.0 / hy;
1195:             col[8].i   = ex;
1196:             col[8].j   = ey;
1197:             col[8].k   = ez;
1198:             col[8].loc = ELEMENT;
1199:             col[8].c   = 0;
1200:             valA[8]    = -1.0 / hy;
1201:           }
1202:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, nEntries, col, valA, INSERT_VALUES));
1203:           valRhs = fy(arrCoord[ez][ey][ex][icuy[0]], arrCoord[ez][ey][ex][icuy[1]], arrCoord[ez][ey][ex][icuy[2]]);
1204:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
1205:         }

1207:         /* Equation on back face of this element */
1208:         if (ez == 0) {
1209:           /* Back boundary velocity Dirichlet */
1210:           DMStagStencil     row;
1211:           PetscScalar       valRhs;
1212:           const PetscScalar valA = 1.0;
1213:           row.i                  = ex;
1214:           row.j                  = ey;
1215:           row.k                  = ez;
1216:           row.loc                = BACK;
1217:           row.c                  = 0;
1218:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, 1, &row, &valA, INSERT_VALUES));
1219:           valRhs = uzRef(arrCoord[ez][ey][ex][icuz[0]], arrCoord[ez][ey][ex][icuz[1]], arrCoord[ez][ey][ex][icuz[2]]);
1220:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
1221:         } else {
1222:           /* Z-momentum equation, (w_xx + w_yy + w_zz) - p_z = f^z */
1223:           DMStagStencil row, col[9];
1224:           PetscScalar   valA[9], valRhs;
1225:           PetscInt      nEntries;

1227:           row.i   = ex;
1228:           row.j   = ey;
1229:           row.k   = ez;
1230:           row.loc = BACK;
1231:           row.c   = 0;
1232:           if (ex == 0) {
1233:             if (ey == 0) {
1234:               nEntries   = 7;
1235:               col[0].i   = ex;
1236:               col[0].j   = ey;
1237:               col[0].k   = ez;
1238:               col[0].loc = BACK;
1239:               col[0].c   = 0;
1240:               valA[0]    = -1.0 / (hx * hx) + -1.0 / (hy * hy) - 2.0 / (hz * hz);
1241:               /* Down term missing */
1242:               col[1].i   = ex;
1243:               col[1].j   = ey + 1;
1244:               col[1].k   = ez;
1245:               col[1].loc = BACK;
1246:               col[1].c   = 0;
1247:               valA[1]    = 1.0 / (hy * hy);
1248:               /* Left term missing */
1249:               col[2].i   = ex + 1;
1250:               col[2].j   = ey;
1251:               col[2].k   = ez;
1252:               col[2].loc = BACK;
1253:               col[2].c   = 0;
1254:               valA[2]    = 1.0 / (hx * hx);
1255:               col[3].i   = ex;
1256:               col[3].j   = ey;
1257:               col[3].k   = ez - 1;
1258:               col[3].loc = BACK;
1259:               col[3].c   = 0;
1260:               valA[3]    = 1.0 / (hz * hz);
1261:               col[4].i   = ex;
1262:               col[4].j   = ey;
1263:               col[4].k   = ez + 1;
1264:               col[4].loc = BACK;
1265:               col[4].c   = 0;
1266:               valA[4]    = 1.0 / (hz * hz);
1267:               col[5].i   = ex;
1268:               col[5].j   = ey;
1269:               col[5].k   = ez - 1;
1270:               col[5].loc = ELEMENT;
1271:               col[5].c   = 0;
1272:               valA[5]    = 1.0 / hz;
1273:               col[6].i   = ex;
1274:               col[6].j   = ey;
1275:               col[6].k   = ez;
1276:               col[6].loc = ELEMENT;
1277:               col[6].c   = 0;
1278:               valA[6]    = -1.0 / hz;
1279:             } else if (ey == N[1] - 1) {
1280:               nEntries   = 7;
1281:               col[0].i   = ex;
1282:               col[0].j   = ey;
1283:               col[0].k   = ez;
1284:               col[0].loc = BACK;
1285:               col[0].c   = 0;
1286:               valA[0]    = -1.0 / (hx * hx) + -1.0 / (hy * hy) - 2.0 / (hz * hz);
1287:               col[1].i   = ex;
1288:               col[1].j   = ey - 1;
1289:               col[1].k   = ez;
1290:               col[1].loc = BACK;
1291:               col[1].c   = 0;
1292:               valA[1]    = 1.0 / (hy * hy);
1293:               /* Up term missing */
1294:               /* Left term missing */
1295:               col[2].i   = ex + 1;
1296:               col[2].j   = ey;
1297:               col[2].k   = ez;
1298:               col[2].loc = BACK;
1299:               col[2].c   = 0;
1300:               valA[2]    = 1.0 / (hx * hx);
1301:               col[3].i   = ex;
1302:               col[3].j   = ey;
1303:               col[3].k   = ez - 1;
1304:               col[3].loc = BACK;
1305:               col[3].c   = 0;
1306:               valA[3]    = 1.0 / (hz * hz);
1307:               col[4].i   = ex;
1308:               col[4].j   = ey;
1309:               col[4].k   = ez + 1;
1310:               col[4].loc = BACK;
1311:               col[4].c   = 0;
1312:               valA[4]    = 1.0 / (hz * hz);
1313:               col[5].i   = ex;
1314:               col[5].j   = ey;
1315:               col[5].k   = ez - 1;
1316:               col[5].loc = ELEMENT;
1317:               col[5].c   = 0;
1318:               valA[5]    = 1.0 / hz;
1319:               col[6].i   = ex;
1320:               col[6].j   = ey;
1321:               col[6].k   = ez;
1322:               col[6].loc = ELEMENT;
1323:               col[6].c   = 0;
1324:               valA[6]    = -1.0 / hz;
1325:             } else {
1326:               nEntries   = 8;
1327:               col[0].i   = ex;
1328:               col[0].j   = ey;
1329:               col[0].k   = ez;
1330:               col[0].loc = BACK;
1331:               col[0].c   = 0;
1332:               valA[0]    = -1.0 / (hx * hx) + -2.0 / (hy * hy) - 2.0 / (hz * hz);
1333:               col[1].i   = ex;
1334:               col[1].j   = ey - 1;
1335:               col[1].k   = ez;
1336:               col[1].loc = BACK;
1337:               col[1].c   = 0;
1338:               valA[1]    = 1.0 / (hy * hy);
1339:               col[2].i   = ex;
1340:               col[2].j   = ey + 1;
1341:               col[2].k   = ez;
1342:               col[2].loc = BACK;
1343:               col[2].c   = 0;
1344:               valA[2]    = 1.0 / (hy * hy);
1345:               /* Left term missing */
1346:               col[3].i   = ex + 1;
1347:               col[3].j   = ey;
1348:               col[3].k   = ez;
1349:               col[3].loc = BACK;
1350:               col[3].c   = 0;
1351:               valA[3]    = 1.0 / (hx * hx);
1352:               col[4].i   = ex;
1353:               col[4].j   = ey;
1354:               col[4].k   = ez - 1;
1355:               col[4].loc = BACK;
1356:               col[4].c   = 0;
1357:               valA[4]    = 1.0 / (hz * hz);
1358:               col[5].i   = ex;
1359:               col[5].j   = ey;
1360:               col[5].k   = ez + 1;
1361:               col[5].loc = BACK;
1362:               col[5].c   = 0;
1363:               valA[5]    = 1.0 / (hz * hz);
1364:               col[6].i   = ex;
1365:               col[6].j   = ey;
1366:               col[6].k   = ez - 1;
1367:               col[6].loc = ELEMENT;
1368:               col[6].c   = 0;
1369:               valA[6]    = 1.0 / hz;
1370:               col[7].i   = ex;
1371:               col[7].j   = ey;
1372:               col[7].k   = ez;
1373:               col[7].loc = ELEMENT;
1374:               col[7].c   = 0;
1375:               valA[7]    = -1.0 / hz;
1376:             }
1377:           } else if (ex == N[0] - 1) {
1378:             if (ey == 0) {
1379:               nEntries   = 7;
1380:               col[0].i   = ex;
1381:               col[0].j   = ey;
1382:               col[0].k   = ez;
1383:               col[0].loc = BACK;
1384:               col[0].c   = 0;
1385:               valA[0]    = -1.0 / (hx * hx) + -1.0 / (hy * hy) - 2.0 / (hz * hz);
1386:               /* Down term missing */
1387:               col[1].i   = ex;
1388:               col[1].j   = ey + 1;
1389:               col[1].k   = ez;
1390:               col[1].loc = BACK;
1391:               col[1].c   = 0;
1392:               valA[1]    = 1.0 / (hy * hy);
1393:               col[2].i   = ex - 1;
1394:               col[2].j   = ey;
1395:               col[2].k   = ez;
1396:               col[2].loc = BACK;
1397:               col[2].c   = 0;
1398:               valA[2]    = 1.0 / (hx * hx);
1399:               /* Right term missing */
1400:               col[3].i   = ex;
1401:               col[3].j   = ey;
1402:               col[3].k   = ez - 1;
1403:               col[3].loc = BACK;
1404:               col[3].c   = 0;
1405:               valA[3]    = 1.0 / (hz * hz);
1406:               col[4].i   = ex;
1407:               col[4].j   = ey;
1408:               col[4].k   = ez + 1;
1409:               col[4].loc = BACK;
1410:               col[4].c   = 0;
1411:               valA[4]    = 1.0 / (hz * hz);
1412:               col[5].i   = ex;
1413:               col[5].j   = ey;
1414:               col[5].k   = ez - 1;
1415:               col[5].loc = ELEMENT;
1416:               col[5].c   = 0;
1417:               valA[5]    = 1.0 / hz;
1418:               col[6].i   = ex;
1419:               col[6].j   = ey;
1420:               col[6].k   = ez;
1421:               col[6].loc = ELEMENT;
1422:               col[6].c   = 0;
1423:               valA[6]    = -1.0 / hz;
1424:             } else if (ey == N[1] - 1) {
1425:               nEntries   = 7;
1426:               col[0].i   = ex;
1427:               col[0].j   = ey;
1428:               col[0].k   = ez;
1429:               col[0].loc = BACK;
1430:               col[0].c   = 0;
1431:               valA[0]    = -1.0 / (hx * hx) + -1.0 / (hy * hy) - 2.0 / (hz * hz);
1432:               col[1].i   = ex;
1433:               col[1].j   = ey - 1;
1434:               col[1].k   = ez;
1435:               col[1].loc = BACK;
1436:               col[1].c   = 0;
1437:               valA[1]    = 1.0 / (hy * hy);
1438:               /* Up term missing */
1439:               col[2].i   = ex - 1;
1440:               col[2].j   = ey;
1441:               col[2].k   = ez;
1442:               col[2].loc = BACK;
1443:               col[2].c   = 0;
1444:               valA[2]    = 1.0 / (hx * hx);
1445:               /* Right term missing */
1446:               col[3].i   = ex;
1447:               col[3].j   = ey;
1448:               col[3].k   = ez - 1;
1449:               col[3].loc = BACK;
1450:               col[3].c   = 0;
1451:               valA[3]    = 1.0 / (hz * hz);
1452:               col[4].i   = ex;
1453:               col[4].j   = ey;
1454:               col[4].k   = ez + 1;
1455:               col[4].loc = BACK;
1456:               col[4].c   = 0;
1457:               valA[4]    = 1.0 / (hz * hz);
1458:               col[5].i   = ex;
1459:               col[5].j   = ey;
1460:               col[5].k   = ez - 1;
1461:               col[5].loc = ELEMENT;
1462:               col[5].c   = 0;
1463:               valA[5]    = 1.0 / hz;
1464:               col[6].i   = ex;
1465:               col[6].j   = ey;
1466:               col[6].k   = ez;
1467:               col[6].loc = ELEMENT;
1468:               col[6].c   = 0;
1469:               valA[6]    = -1.0 / hz;
1470:             } else {
1471:               nEntries   = 8;
1472:               col[0].i   = ex;
1473:               col[0].j   = ey;
1474:               col[0].k   = ez;
1475:               col[0].loc = BACK;
1476:               col[0].c   = 0;
1477:               valA[0]    = -1.0 / (hx * hx) + -2.0 / (hy * hy) - 2.0 / (hz * hz);
1478:               col[1].i   = ex;
1479:               col[1].j   = ey - 1;
1480:               col[1].k   = ez;
1481:               col[1].loc = BACK;
1482:               col[1].c   = 0;
1483:               valA[1]    = 1.0 / (hy * hy);
1484:               col[2].i   = ex;
1485:               col[2].j   = ey + 1;
1486:               col[2].k   = ez;
1487:               col[2].loc = BACK;
1488:               col[2].c   = 0;
1489:               valA[2]    = 1.0 / (hy * hy);
1490:               col[3].i   = ex - 1;
1491:               col[3].j   = ey;
1492:               col[3].k   = ez;
1493:               col[3].loc = BACK;
1494:               col[3].c   = 0;
1495:               valA[3]    = 1.0 / (hx * hx);
1496:               /* Right term missing */
1497:               col[4].i   = ex;
1498:               col[4].j   = ey;
1499:               col[4].k   = ez - 1;
1500:               col[4].loc = BACK;
1501:               col[4].c   = 0;
1502:               valA[4]    = 1.0 / (hz * hz);
1503:               col[5].i   = ex;
1504:               col[5].j   = ey;
1505:               col[5].k   = ez + 1;
1506:               col[5].loc = BACK;
1507:               col[5].c   = 0;
1508:               valA[5]    = 1.0 / (hz * hz);
1509:               col[6].i   = ex;
1510:               col[6].j   = ey;
1511:               col[6].k   = ez - 1;
1512:               col[6].loc = ELEMENT;
1513:               col[6].c   = 0;
1514:               valA[6]    = 1.0 / hz;
1515:               col[7].i   = ex;
1516:               col[7].j   = ey;
1517:               col[7].k   = ez;
1518:               col[7].loc = ELEMENT;
1519:               col[7].c   = 0;
1520:               valA[7]    = -1.0 / hz;
1521:             }
1522:           } else if (ey == 0) {
1523:             nEntries   = 8;
1524:             col[0].i   = ex;
1525:             col[0].j   = ey;
1526:             col[0].k   = ez;
1527:             col[0].loc = BACK;
1528:             col[0].c   = 0;
1529:             valA[0]    = -2.0 / (hx * hx) + -1.0 / (hy * hy) - 2.0 / (hz * hz);
1530:             /* Down term missing */
1531:             col[1].i   = ex;
1532:             col[1].j   = ey + 1;
1533:             col[1].k   = ez;
1534:             col[1].loc = BACK;
1535:             col[1].c   = 0;
1536:             valA[1]    = 1.0 / (hy * hy);
1537:             col[2].i   = ex - 1;
1538:             col[2].j   = ey;
1539:             col[2].k   = ez;
1540:             col[2].loc = BACK;
1541:             col[2].c   = 0;
1542:             valA[2]    = 1.0 / (hx * hx);
1543:             col[3].i   = ex + 1;
1544:             col[3].j   = ey;
1545:             col[3].k   = ez;
1546:             col[3].loc = BACK;
1547:             col[3].c   = 0;
1548:             valA[3]    = 1.0 / (hx * hx);
1549:             col[4].i   = ex;
1550:             col[4].j   = ey;
1551:             col[4].k   = ez - 1;
1552:             col[4].loc = BACK;
1553:             col[4].c   = 0;
1554:             valA[4]    = 1.0 / (hz * hz);
1555:             col[5].i   = ex;
1556:             col[5].j   = ey;
1557:             col[5].k   = ez + 1;
1558:             col[5].loc = BACK;
1559:             col[5].c   = 0;
1560:             valA[5]    = 1.0 / (hz * hz);
1561:             col[6].i   = ex;
1562:             col[6].j   = ey;
1563:             col[6].k   = ez - 1;
1564:             col[6].loc = ELEMENT;
1565:             col[6].c   = 0;
1566:             valA[6]    = 1.0 / hz;
1567:             col[7].i   = ex;
1568:             col[7].j   = ey;
1569:             col[7].k   = ez;
1570:             col[7].loc = ELEMENT;
1571:             col[7].c   = 0;
1572:             valA[7]    = -1.0 / hz;
1573:           } else if (ey == N[1] - 1) {
1574:             nEntries   = 8;
1575:             col[0].i   = ex;
1576:             col[0].j   = ey;
1577:             col[0].k   = ez;
1578:             col[0].loc = BACK;
1579:             col[0].c   = 0;
1580:             valA[0]    = -2.0 / (hx * hx) + -1.0 / (hy * hy) - 2.0 / (hz * hz);
1581:             col[1].i   = ex;
1582:             col[1].j   = ey - 1;
1583:             col[1].k   = ez;
1584:             col[1].loc = BACK;
1585:             col[1].c   = 0;
1586:             valA[1]    = 1.0 / (hy * hy);
1587:             /* Up term missing */
1588:             col[2].i   = ex - 1;
1589:             col[2].j   = ey;
1590:             col[2].k   = ez;
1591:             col[2].loc = BACK;
1592:             col[2].c   = 0;
1593:             valA[2]    = 1.0 / (hx * hx);
1594:             col[3].i   = ex + 1;
1595:             col[3].j   = ey;
1596:             col[3].k   = ez;
1597:             col[3].loc = BACK;
1598:             col[3].c   = 0;
1599:             valA[3]    = 1.0 / (hx * hx);
1600:             col[4].i   = ex;
1601:             col[4].j   = ey;
1602:             col[4].k   = ez - 1;
1603:             col[4].loc = BACK;
1604:             col[4].c   = 0;
1605:             valA[4]    = 1.0 / (hz * hz);
1606:             col[5].i   = ex;
1607:             col[5].j   = ey;
1608:             col[5].k   = ez + 1;
1609:             col[5].loc = BACK;
1610:             col[5].c   = 0;
1611:             valA[5]    = 1.0 / (hz * hz);
1612:             col[6].i   = ex;
1613:             col[6].j   = ey;
1614:             col[6].k   = ez - 1;
1615:             col[6].loc = ELEMENT;
1616:             col[6].c   = 0;
1617:             valA[6]    = 1.0 / hz;
1618:             col[7].i   = ex;
1619:             col[7].j   = ey;
1620:             col[7].k   = ez;
1621:             col[7].loc = ELEMENT;
1622:             col[7].c   = 0;
1623:             valA[7]    = -1.0 / hz;
1624:           } else {
1625:             nEntries   = 9;
1626:             col[0].i   = ex;
1627:             col[0].j   = ey;
1628:             col[0].k   = ez;
1629:             col[0].loc = BACK;
1630:             col[0].c   = 0;
1631:             valA[0]    = -2.0 / (hx * hx) + -2.0 / (hy * hy) - 2.0 / (hz * hz);
1632:             col[1].i   = ex;
1633:             col[1].j   = ey - 1;
1634:             col[1].k   = ez;
1635:             col[1].loc = BACK;
1636:             col[1].c   = 0;
1637:             valA[1]    = 1.0 / (hy * hy);
1638:             col[2].i   = ex;
1639:             col[2].j   = ey + 1;
1640:             col[2].k   = ez;
1641:             col[2].loc = BACK;
1642:             col[2].c   = 0;
1643:             valA[2]    = 1.0 / (hy * hy);
1644:             col[3].i   = ex - 1;
1645:             col[3].j   = ey;
1646:             col[3].k   = ez;
1647:             col[3].loc = BACK;
1648:             col[3].c   = 0;
1649:             valA[3]    = 1.0 / (hx * hx);
1650:             col[4].i   = ex + 1;
1651:             col[4].j   = ey;
1652:             col[4].k   = ez;
1653:             col[4].loc = BACK;
1654:             col[4].c   = 0;
1655:             valA[4]    = 1.0 / (hx * hx);
1656:             col[5].i   = ex;
1657:             col[5].j   = ey;
1658:             col[5].k   = ez - 1;
1659:             col[5].loc = BACK;
1660:             col[5].c   = 0;
1661:             valA[5]    = 1.0 / (hz * hz);
1662:             col[6].i   = ex;
1663:             col[6].j   = ey;
1664:             col[6].k   = ez + 1;
1665:             col[6].loc = BACK;
1666:             col[6].c   = 0;
1667:             valA[6]    = 1.0 / (hz * hz);
1668:             col[7].i   = ex;
1669:             col[7].j   = ey;
1670:             col[7].k   = ez - 1;
1671:             col[7].loc = ELEMENT;
1672:             col[7].c   = 0;
1673:             valA[7]    = 1.0 / hz;
1674:             col[8].i   = ex;
1675:             col[8].j   = ey;
1676:             col[8].k   = ez;
1677:             col[8].loc = ELEMENT;
1678:             col[8].c   = 0;
1679:             valA[8]    = -1.0 / hz;
1680:           }
1681:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, nEntries, col, valA, INSERT_VALUES));
1682:           valRhs = fz(arrCoord[ez][ey][ex][icuz[0]], arrCoord[ez][ey][ex][icuz[1]], arrCoord[ez][ey][ex][icuz[2]]);
1683:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
1684:         }

1686:         /* P equation : u_x + v_y + w_z = g
1687:            Note that this includes an explicit zero on the diagonal. This is only needed for
1688:            direct solvers (not required if using an iterative solver and setting the constant-pressure nullspace) */
1689:         if (pinPressure && ex == 0 && ey == 0 && ez == 0) { /* Pin the first pressure node, if requested */
1690:           DMStagStencil row;
1691:           PetscScalar   valA, valRhs;
1692:           row.i   = ex;
1693:           row.j   = ey;
1694:           row.k   = ez;
1695:           row.loc = ELEMENT;
1696:           row.c   = 0;
1697:           valA    = 1.0;
1698:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, 1, &row, &valA, INSERT_VALUES));
1699:           valRhs = pRef(arrCoord[ez][ey][ex][icp[0]], arrCoord[ez][ey][ex][icp[1]], arrCoord[ez][ey][ex][icp[2]]);
1700:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
1701:         } else {
1702:           DMStagStencil row, col[7];
1703:           PetscScalar   valA[7], valRhs;

1705:           row.i      = ex;
1706:           row.j      = ey;
1707:           row.k      = ez;
1708:           row.loc    = ELEMENT;
1709:           row.c      = 0;
1710:           col[0].i   = ex;
1711:           col[0].j   = ey;
1712:           col[0].k   = ez;
1713:           col[0].loc = LEFT;
1714:           col[0].c   = 0;
1715:           valA[0]    = -1.0 / hx;
1716:           col[1].i   = ex;
1717:           col[1].j   = ey;
1718:           col[1].k   = ez;
1719:           col[1].loc = RIGHT;
1720:           col[1].c   = 0;
1721:           valA[1]    = 1.0 / hx;
1722:           col[2].i   = ex;
1723:           col[2].j   = ey;
1724:           col[2].k   = ez;
1725:           col[2].loc = DOWN;
1726:           col[2].c   = 0;
1727:           valA[2]    = -1.0 / hy;
1728:           col[3].i   = ex;
1729:           col[3].j   = ey;
1730:           col[3].k   = ez;
1731:           col[3].loc = UP;
1732:           col[3].c   = 0;
1733:           valA[3]    = 1.0 / hy;
1734:           col[4].i   = ex;
1735:           col[4].j   = ey;
1736:           col[4].k   = ez;
1737:           col[4].loc = BACK;
1738:           col[4].c   = 0;
1739:           valA[4]    = -1.0 / hz;
1740:           col[5].i   = ex;
1741:           col[5].j   = ey;
1742:           col[5].k   = ez;
1743:           col[5].loc = FRONT;
1744:           col[5].c   = 0;
1745:           valA[5]    = 1.0 / hz;
1746:           col[6]     = row;
1747:           valA[6]    = 0.0;
1748:           PetscCall(DMStagMatSetValuesStencil(dmSol, A, 1, &row, 7, col, valA, INSERT_VALUES));
1749:           valRhs = g(arrCoord[ez][ey][ex][icp[0]], arrCoord[ez][ey][ex][icp[1]], arrCoord[ez][ey][ex][icp[2]]);
1750:           PetscCall(DMStagVecSetValuesStencil(dmSol, rhs, 1, &row, &valRhs, INSERT_VALUES));
1751:         }
1752:       }
1753:     }
1754:   }
1755:   PetscCall(DMStagVecRestoreArrayRead(dmCoord, coordLocal, &arrCoord));
1756:   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
1757:   PetscCall(VecAssemblyBegin(rhs));
1758:   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
1759:   PetscCall(VecAssemblyEnd(rhs));
1760:   PetscFunctionReturn(PETSC_SUCCESS);
1761: }

1763: /* Create a pressure-only DMStag and use it to generate a nullspace vector
1764:    - Create a compatible DMStag with one dof per element (and nothing else).
1765:    - Create a constant vector and normalize it
1766:    - Migrate it to a vector on the original dmSol (making use of the fact
1767:    that this will fill in zeros for "extra" dof)
1768:    - Set the nullspace for the operator
1769:    - Destroy everything (the operator keeps the references it needs) */
1770: static PetscErrorCode AttachNullspace(DM dmSol, Mat A)
1771: {
1772:   DM           dmPressure;
1773:   Vec          constantPressure, basis;
1774:   PetscReal    nrm;
1775:   MatNullSpace matNullSpace;

1777:   PetscFunctionBeginUser;
1778:   PetscCall(DMStagCreateCompatibleDMStag(dmSol, 0, 0, 0, 1, &dmPressure));
1779:   PetscCall(DMGetGlobalVector(dmPressure, &constantPressure));
1780:   PetscCall(VecSet(constantPressure, 1.0));
1781:   PetscCall(VecNorm(constantPressure, NORM_2, &nrm));
1782:   PetscCall(VecScale(constantPressure, 1.0 / nrm));
1783:   PetscCall(DMCreateGlobalVector(dmSol, &basis));
1784:   PetscCall(DMStagMigrateVec(dmPressure, constantPressure, dmSol, basis));
1785:   PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)dmSol), PETSC_FALSE, 1, &basis, &matNullSpace));
1786:   PetscCall(VecDestroy(&basis));
1787:   PetscCall(VecDestroy(&constantPressure));
1788:   PetscCall(MatSetNullSpace(A, matNullSpace));
1789:   PetscCall(MatNullSpaceDestroy(&matNullSpace));
1790:   PetscFunctionReturn(PETSC_SUCCESS);
1791: }

1793: static PetscErrorCode CreateReferenceSolution(DM dmSol, Vec *pSolRef)
1794: {
1795:   PetscInt        start[3], n[3], nExtra[3], ex, ey, ez, d;
1796:   PetscInt        ip, iux, iuy, iuz, icp[3], icux[3], icuy[3], icuz[3];
1797:   Vec             solRef, solRefLocal, coord, coordLocal;
1798:   DM              dmCoord;
1799:   PetscScalar ****arrSol, ****arrCoord;

1801:   PetscFunctionBeginUser;
1802:   PetscCall(DMCreateGlobalVector(dmSol, pSolRef));
1803:   solRef = *pSolRef;
1804:   PetscCall(DMStagGetCorners(dmSol, &start[0], &start[1], &start[2], &n[0], &n[1], &n[2], &nExtra[0], &nExtra[1], &nExtra[2]));
1805:   PetscCall(DMGetCoordinateDM(dmSol, &dmCoord));
1806:   PetscCall(DMGetCoordinates(dmSol, &coord));
1807:   PetscCall(DMGetLocalVector(dmCoord, &coordLocal));
1808:   PetscCall(DMGlobalToLocal(dmCoord, coord, INSERT_VALUES, coordLocal));
1809:   PetscCall(DMStagGetLocationSlot(dmSol, ELEMENT, 0, &ip));
1810:   PetscCall(DMStagGetLocationSlot(dmSol, LEFT, 0, &iux));
1811:   PetscCall(DMStagGetLocationSlot(dmSol, DOWN, 0, &iuy));
1812:   PetscCall(DMStagGetLocationSlot(dmSol, BACK, 0, &iuz));
1813:   for (d = 0; d < 3; ++d) {
1814:     PetscCall(DMStagGetLocationSlot(dmCoord, ELEMENT, d, &icp[d]));
1815:     PetscCall(DMStagGetLocationSlot(dmCoord, LEFT, d, &icux[d]));
1816:     PetscCall(DMStagGetLocationSlot(dmCoord, DOWN, d, &icuy[d]));
1817:     PetscCall(DMStagGetLocationSlot(dmCoord, BACK, d, &icuz[d]));
1818:   }
1819:   PetscCall(DMStagVecGetArrayRead(dmCoord, coordLocal, &arrCoord));
1820:   PetscCall(DMGetLocalVector(dmSol, &solRefLocal));
1821:   PetscCall(DMStagVecGetArray(dmSol, solRefLocal, &arrSol));
1822:   for (ez = start[2]; ez < start[2] + n[2] + nExtra[2]; ++ez) {
1823:     for (ey = start[1]; ey < start[1] + n[1] + nExtra[1]; ++ey) {
1824:       for (ex = start[0]; ex < start[0] + n[0] + nExtra[0]; ++ex) {
1825:         if (ex < start[0] + n[0] && ey < start[1] + n[1]) arrSol[ez][ey][ex][iuz] = uzRef(arrCoord[ez][ey][ex][icuz[0]], arrCoord[ez][ey][ex][icuz[1]], arrCoord[ez][ey][ex][icuz[2]]);
1826:         if (ex < start[0] + n[0] && ey < start[2] + n[2]) arrSol[ez][ey][ex][iuy] = uyRef(arrCoord[ez][ey][ex][icuy[0]], arrCoord[ez][ey][ex][icuy[1]], arrCoord[ez][ey][ex][icuy[2]]);
1827:         if (ex < start[1] + n[1] && ey < start[2] + n[2]) arrSol[ez][ey][ex][iux] = uxRef(arrCoord[ez][ey][ex][icux[0]], arrCoord[ez][ey][ex][icux[1]], arrCoord[ez][ey][ex][icux[2]]);
1828:         if (ex < start[0] + n[0] && ey < start[1] + n[1] && ez < start[2] + n[2]) arrSol[ez][ey][ex][ip] = pRef(arrCoord[ez][ey][ex][icp[0]], arrCoord[ez][ey][ex][icp[1]], arrCoord[ez][ey][ex][icp[2]]);
1829:       }
1830:     }
1831:   }
1832:   PetscCall(DMStagVecRestoreArrayRead(dmCoord, coordLocal, &arrCoord));
1833:   PetscCall(DMStagVecRestoreArray(dmSol, solRefLocal, &arrSol));
1834:   PetscCall(DMLocalToGlobal(dmSol, solRefLocal, INSERT_VALUES, solRef));
1835:   PetscCall(DMRestoreLocalVector(dmCoord, &coordLocal));
1836:   PetscCall(DMRestoreLocalVector(dmSol, &solRefLocal));
1837:   PetscFunctionReturn(PETSC_SUCCESS);
1838: }

1840: static PetscErrorCode CheckSolution(Vec sol, Vec solRef)
1841: {
1842:   Vec       diff;
1843:   PetscReal normsolRef, errAbs, errRel;

1845:   PetscFunctionBeginUser;
1846:   PetscCall(VecDuplicate(sol, &diff));
1847:   PetscCall(VecCopy(sol, diff));
1848:   PetscCall(VecAXPY(diff, -1.0, solRef));
1849:   PetscCall(VecNorm(diff, NORM_2, &errAbs));
1850:   PetscCall(VecNorm(solRef, NORM_2, &normsolRef));
1851:   errRel = errAbs / normsolRef;
1852:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Error (abs): %g\nError (rel): %g\n", (double)errAbs, (double)errRel));
1853:   PetscCall(VecDestroy(&diff));
1854:   PetscFunctionReturn(PETSC_SUCCESS);
1855: }

1857: /*TEST

1859:    test:
1860:       suffix: 1
1861:       requires: mumps
1862:       nsize: 27
1863:       args: -ksp_monitor_short -ksp_converged_reason -stag_ranks_x 3 -stag_ranks_y 3 -stag_ranks_z 3 -pc_fieldsplit_schur_fact_type diag -fieldsplit_0_ksp_type preonly -fieldsplit_1_pc_type none -fieldsplit_0_pc_type lu -fieldsplit_1_ksp_type gmres -fieldsplit_1_ksp_max_it 20

1865:    test:
1866:       suffix: 2
1867:       requires: !single
1868:       nsize: 4
1869:       args: -ksp_monitor_short -ksp_converged_reason -pc_fieldsplit_schur_fact_type diag -fieldsplit_0_ksp_type preonly -fieldsplit_1_pc_type none -fieldsplit_0_pc_type gamg -fieldsplit_0_ksp_type gmres -fieldsplit_0_pc_gamg_esteig_ksp_max_it 5

1871:    test:
1872:       suffix: direct_umfpack
1873:       requires: suitesparse
1874:       nsize: 1
1875:       args: -pinpressure 1 -stag_grid_x 5 -stag_grid_y 3 -stag_grid_z 4 -ksp_monitor_short -pc_type lu -pc_factor_mat_solver_type umfpack

1877: TEST*/