36 PetscErrorCode (*AssembleInterior)(
UserCtx *, Vec, Mat);
69 PetscReal norm,
void *ctx);
72 SNESConvergedReason reason,
73 PetscInt nonlinear_its,
74 PetscInt function_evals,
81 UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt component,
82 PetscInt *ri, PetscInt *rj, PetscInt *rk);
86 Mat jacobian_operator, Mat preconditioning_matrix,
void *vctx);
88 UserCtx *user, Vec current_solution, Mat preconditioning_matrix);
90 UserCtx *user, Mat *preconditioning_matrix);
99 PetscReal norm,
void *vctx)
105 PetscFunctionBeginUser;
112 "step: %d | block: %d | newton: %d | nonlinear_norm: %.16e\n",
117 PetscFunctionReturn(PETSC_SUCCESS);
124 char path[PETSC_MAX_PATH_LEN + 128];
128 if (PetscSNPrintf(path,
sizeof(path),
129 "%s/Momentum_Solver_Newton_Krylov_History_Block_%d.log",
137 if (mode[0] ==
'w') {
139 "# step | block | Newton iteration | nonlinear residual norm\n");
151 SNESConvergedReason reason,
152 PetscInt nonlinear_its,
153 PetscInt function_evals,
155 PetscReal final_norm,
159 char path[PETSC_MAX_PATH_LEN + 128];
161 const char *reason_name;
164 if (simCtx->
rank != 0)
return;
165 if (PetscSNPrintf(path,
sizeof(path),
166 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_%d.log",
169 file = fopen(path, mode);
174 if (mode[0] ==
'w') {
176 "# step | block | solver | Jacobian | preconditioner | SNES reason | "
177 "reason code | Newton iterations | "
178 "residual evaluations | Krylov iterations | initial nonlinear norm | "
179 "final nonlinear norm | state\n");
181 (void)fprintf(file,
"# Continuation from step %d\n", (
int)simCtx->
StartStep);
183 reason_name = reason == SNES_CONVERGED_ITERATING
184 ?
"SNES_CONVERGED_ITERATING" : SNESConvergedReasons[reason];
186 "step: %d | block: %d | solver: Newton Krylov | "
187 "Jacobian: finite_difference / matrix_free | Preconditioner: %s | "
188 "reason: %s | reason_code: %d | "
189 "newton: %d | evals: %d | krylov: %d | initial: ",
192 "none" :
"frozen_momentum_jacobian / point_block",
193 reason_name, (int)reason,
194 (
int)nonlinear_its, (int)function_evals, (
int)linear_its);
196 else (
void)fprintf(file,
"unavailable");
197 (void)fprintf(file,
" | final: %.16e | state: %s\n", (
double)final_norm,
198 committed ?
"committed" :
"rolled_back");
203#define __FUNCT__ "MomentumNewtonKrylov_Validate"
212 PetscReal mask_max = 0.0;
213 PetscInt velocity_dof = 0;
215 PetscFunctionBeginUser;
216 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
217 "Newton Krylov requires a non-NULL UserCtx.");
219 PetscCheck(simCtx != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
220 "Newton Krylov requires UserCtx::simCtx.");
221 PetscCall(DMDAGetInfo(user->
fda, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
222 &velocity_dof, NULL, NULL, NULL, NULL, NULL));
223 PetscCheck(velocity_dof == 3, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
224 "Newton Krylov requires a three-component velocity DMDA (got dof=%d).",
226 PetscCheck(simCtx->
block_number == 1, PETSC_COMM_WORLD, PETSC_ERR_SUP,
227 "Newton Krylov version one supports exactly one block (got %d).",
229 PetscCheck(!simCtx->
immersed, PETSC_COMM_WORLD, PETSC_ERR_SUP,
230 "Newton Krylov version one does not support immersed boundaries.");
231 PetscCheck(!simCtx->
movefsi && !simCtx->
rotatefsi, PETSC_COMM_WORLD, PETSC_ERR_SUP,
232 "Newton Krylov version one does not support moving or rotating bodies/FSI.");
234 "Newton Krylov version one does not support moving or rotating reference frames.");
235 PetscCheck(!simCtx->
rans, PETSC_COMM_WORLD, PETSC_ERR_SUP,
236 "Newton Krylov version one does not support RANS.");
237 PetscCheck(!simCtx->
clark, PETSC_COMM_WORLD, PETSC_ERR_SUP,
238 "Newton Krylov version one does not support the Clark model.");
239 PetscCheck(!simCtx->
TwoD, PETSC_COMM_WORLD, PETSC_ERR_SUP,
240 "Newton Krylov version one does not support TwoD component masking.");
241 PetscCheck(!simCtx->
wallfunction, PETSC_COMM_WORLD, PETSC_ERR_SUP,
242 "Newton Krylov version one does not support wall functions.");
243 for (PetscInt face = 0; face < 6; ++face) {
245 PetscBool supported = PETSC_FALSE;
263 supported = PETSC_FALSE;
266 PetscCheck(supported, PETSC_COMM_WORLD, PETSC_ERR_SUP,
267 "Newton Krylov version one does not support boundary face %d with mathematical type %d and handler %d.",
273 PETSC_COMM_WORLD, PETSC_ERR_SUP,
"Newton Krylov requires paired x-periodic faces.");
276 PETSC_COMM_WORLD, PETSC_ERR_SUP,
"Newton Krylov requires paired y-periodic faces.");
279 PETSC_COMM_WORLD, PETSC_ERR_SUP,
"Newton Krylov requires paired z-periodic faces.");
281 PetscCheck(user->
Nvert != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
282 "Newton Krylov requires the cell-mask vector Nvert.");
283 PetscCall(VecMax(user->
Nvert, NULL, &mask_max));
284 PetscCheck(mask_max <= 0.1, PETSC_COMM_WORLD, PETSC_ERR_SUP,
285 "Newton Krylov version one does not define equations for masked solid cells (max Nvert=%g).",
287 PetscFunctionReturn(PETSC_SUCCESS);
291#define __FUNCT__ "MomentumNewtonKrylov_ReadLinearizationConfig"
298 char type[48] =
"finite_difference";
299 char finite_difference_mode[32] =
"matrix_free";
300 char model[48] =
"none";
301 char structure[32] =
"none";
302 PetscBool set = PETSC_FALSE, match = PETSC_FALSE;
304 PetscFunctionBeginUser;
305 PetscCheck(jacobian != NULL && description != NULL, PETSC_COMM_SELF,
306 PETSC_ERR_ARG_NULL,
"Newton Krylov linearization configuration is NULL.");
307 PetscCall(PetscOptionsGetString(NULL, NULL,
"-mom_nk_jacobian_type",
308 type,
sizeof(type), &set));
309 PetscCall(PetscStrcasecmp(type,
"finite_difference", &match));
310 PetscCheck(match, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
311 "-mom_nk_jacobian_type must be 'finite_difference' (got '%s').", type);
313 PetscCall(PetscOptionsGetString(NULL, NULL,
"-mom_nk_jacobian_fd_mode",
314 finite_difference_mode,
315 sizeof(finite_difference_mode), &set));
316 PetscCall(PetscStrcasecmp(finite_difference_mode,
"matrix_free", &match));
317 PetscCheck(match, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
318 "-mom_nk_jacobian_fd_mode must be 'matrix_free' (got '%s').",
319 finite_difference_mode);
322 PetscCall(PetscOptionsGetString(NULL, NULL,
"-mom_nk_preconditioner_model",
323 model,
sizeof(model), &set));
324 PetscCall(PetscStrcasecmp(model,
"none", &match));
327 PetscCall(PetscStrcasecmp(model,
"frozen_momentum_jacobian", &match));
330 PetscCheck(match, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
331 "-mom_nk_preconditioner_model must be 'none' or "
332 "'frozen_momentum_jacobian' (got '%s').", model);
333 PetscCall(PetscOptionsGetString(NULL, NULL,
"-mom_nk_preconditioner_structure",
334 structure,
sizeof(structure), &set));
335 PetscCall(PetscStrcasecmp(structure,
"none", &match));
338 PetscCall(PetscStrcasecmp(structure,
"point_block", &match));
341 PetscCheck(match, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
342 "-mom_nk_preconditioner_structure must be 'none' or 'point_block' "
343 "(got '%s').", structure);
348 PETSC_COMM_WORLD, PETSC_ERR_SUP,
349 "Unsupported Newton Krylov preconditioner model/structure combination: "
350 "model='%s', structure='%s'.", model, structure);
351 PetscFunctionReturn(PETSC_SUCCESS);
357 return metric.
x * metric.
x + metric.
y * metric.
y + metric.
z * metric.
z;
363 const Cmpnts ***zet,
const PetscReal ***aj, PetscInt i, PetscInt j,
364 PetscInt k, PetscScalar block[9])
367 const PetscReal inverse_reynolds = simCtx->
ren > 0.0 ? 1.0 / simCtx->
ren : 0.0;
368 const PetscReal AJip = 0.5 * (aj[k][j][i] + aj[k][j][i + 1]);
369 const PetscReal AJjp = 0.5 * (aj[k][j][i] + aj[k][j + 1][i]);
370 const PetscReal AJkp = 0.5 * (aj[k][j][i] + aj[k + 1][j][i]);
371 const PetscReal g11ip = csi[k][j][i].
x * csi[k][j][i].
x +
372 csi[k][j][i].
y * csi[k][j][i].
y +
373 csi[k][j][i].
z * csi[k][j][i].
z;
374 const PetscReal g22ip = 0.25 * (
379 const PetscReal g33ip = 0.25 * (
384 const PetscReal g11jp = 0.25 * (
389 const PetscReal g22jp = eta[k][j][i].
x * eta[k][j][i].
x +
390 eta[k][j][i].
y * eta[k][j][i].
y +
391 eta[k][j][i].
z * eta[k][j][i].
z;
392 const PetscReal g33jp = 0.25 * (
397 const PetscReal g11kp = 0.25 * (
402 const PetscReal g22kp = 0.25 * (
407 const PetscReal g33kp = zet[k][j][i].
x * zet[k][j][i].
x +
408 zet[k][j][i].
y * zet[k][j][i].
y +
409 zet[k][j][i].
z * zet[k][j][i].
z;
410 const PetscReal U0jp = 0.25 * (ucont[k][j][i].
x + ucont[k][j][i - 1].
x +
411 ucont[k][j + 1][i].
x + ucont[k][j + 1][i - 1].
x);
412 const PetscReal U0kp = 0.25 * (ucont[k][j][i].
x + ucont[k][j][i - 1].
x +
413 ucont[k + 1][j][i].
x + ucont[k + 1][j][i - 1].
x);
414 const PetscReal U1ip = 0.25 * (ucont[k][j][i].
y + ucont[k][j - 1][i].
y +
415 ucont[k][j][i + 1].
y + ucont[k][j - 1][i + 1].
y);
416 const PetscReal U1kp = 0.25 * (ucont[k][j][i].
y + ucont[k][j - 1][i].
y +
417 ucont[k + 1][j][i].
y + ucont[k + 1][j - 1][i].
y);
418 const PetscReal U2ip = 0.25 * (ucont[k][j][i].
z + ucont[k - 1][j][i].
z +
419 ucont[k][j][i + 1].
z + ucont[k - 1][j][i + 1].
z);
420 const PetscReal U2jp = 0.25 * (ucont[k][j][i].
z + ucont[k - 1][j][i].
z +
421 ucont[k][j + 1][i].
z + ucont[k - 1][j + 1][i].
z);
422 PetscReal A[6][4] = {{0.0}};
423 PetscReal Su, Sv, Sw, nui, nuj, nuk;
425 A[0][0] = 0.125 * aj[k][j][i] * ucont[k][j][i].
y;
426 A[0][1] = -0.125 * aj[k][j - 1][i] * ucont[k][j - 1][i].
y;
427 A[0][2] = 0.125 * aj[k][j][i + 1] * ucont[k][j][i + 1].
y;
428 A[0][3] = -0.125 * aj[k][j - 1][i + 1] * ucont[k][j - 1][i + 1].
y;
429 A[1][0] = 0.125 * aj[k][j][i] * ucont[k][j][i].
z;
430 A[1][1] = -0.125 * aj[k - 1][j][i] * ucont[k - 1][j][i].
z;
431 A[1][2] = 0.125 * aj[k][j][i + 1] * ucont[k][j][i + 1].
z;
432 A[1][3] = -0.125 * aj[k - 1][j][i + 1] * ucont[k - 1][j][i + 1].
z;
433 A[2][0] = -0.125 * aj[k][j + 1][i - 1] * ucont[k][j + 1][i - 1].
x;
434 A[2][1] = -0.125 * aj[k][j][i - 1] * ucont[k][j][i - 1].
x;
435 A[2][2] = 0.125 * aj[k][j + 1][i] * ucont[k][j + 1][i].
x;
436 A[2][3] = 0.125 * aj[k][j][i] * ucont[k][j][i].
x;
437 A[3][0] = 0.125 * aj[k][j][i] * ucont[k][j][i].
z;
438 A[3][1] = -0.125 * aj[k - 1][j][i] * ucont[k - 1][j][i].
z;
439 A[3][2] = 0.125 * aj[k][j + 1][i] * ucont[k][j + 1][i].
z;
440 A[3][3] = -0.125 * aj[k - 1][j + 1][i] * ucont[k - 1][j + 1][i].
z;
441 A[4][0] = -0.125 * aj[k + 1][j][i - 1] * ucont[k + 1][j][i - 1].
x;
442 A[4][1] = -0.125 * aj[k][j][i - 1] * ucont[k][j][i - 1].
x;
443 A[4][2] = 0.125 * aj[k + 1][j][i] * ucont[k + 1][j][i].
x;
444 A[4][3] = 0.125 * aj[k][j][i] * ucont[k][j][i].
x;
445 A[5][0] = -0.125 * aj[k + 1][j - 1][i] * ucont[k + 1][j - 1][i].
y;
446 A[5][1] = -0.125 * aj[k][j - 1][i] * ucont[k][j - 1][i].
y;
447 A[5][2] = 0.125 * aj[k + 1][j][i] * ucont[k + 1][j][i].
y;
448 A[5][3] = 0.125 * aj[k][j][i] * ucont[k][j][i].
y;
449 Su = A[0][0] + A[0][1] + A[0][2] + A[0][3] +
450 A[1][0] + A[1][1] + A[1][2] + A[1][3];
451 Sv = A[2][0] + A[2][1] + A[2][2] + A[2][3] +
452 A[3][0] + A[3][1] + A[3][2] + A[3][3];
453 Sw = A[4][0] + A[4][1] + A[4][2] + A[4][3] +
454 A[5][0] + A[5][1] + A[5][2] + A[5][3];
455 nui = AJip * AJip * (g11ip + g22ip + g33ip) * inverse_reynolds;
456 nuj = AJjp * AJjp * (g11jp + g22jp + g33jp) * inverse_reynolds;
457 nuk = AJkp * AJkp * (g11kp + g22kp + g33kp) * inverse_reynolds;
460 block[0] = dtc + nui + Su; block[1] = 0.5 * AJip * U1ip; block[2] = 0.5 * AJip * U2ip;
461 block[3] = 0.5 * AJjp * U0jp; block[4] = dtc + nuj + Sv; block[5] = 0.5 * AJjp * U2jp;
462 block[6] = 0.5 * AJkp * U0kp; block[7] = 0.5 * AJkp * U1kp; block[8] = dtc + nuk + Sw;
466#define __FUNCT__ "FrozenMomentumJacobian_DescribePointBlock"
471 PetscFunctionBeginUser;
473 PetscCheck(description != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
474 "Preconditioner description is NULL.");
479 PetscFunctionReturn(PETSC_SUCCESS);
483#define __FUNCT__ "FrozenMomentumJacobian_AssemblePointBlocks"
486 UserCtx *user, Vec current_solution, Mat preconditioning_matrix)
488 DMDALocalInfo info = user->
info;
489 Cmpnts ***ucont = NULL, ***csi = NULL, ***eta = NULL, ***zet = NULL;
490 PetscReal ***aj = NULL;
491 PetscErrorCode ierr = PETSC_SUCCESS, cleanup_ierr;
493 PetscFunctionBeginUser;
506 ierr = DMGlobalToLocalBegin(user->
fda, current_solution, INSERT_VALUES, user->
lUcont);
507 if (ierr)
goto cleanup;
508 ierr = DMGlobalToLocalEnd(user->
fda, current_solution, INSERT_VALUES, user->
lUcont);
509 if (ierr)
goto cleanup;
510 ierr = DMDAVecGetArrayRead(user->
fda, user->
lUcont, &ucont);
if (ierr)
goto cleanup;
511 ierr = DMDAVecGetArrayRead(user->
fda, user->
lCsi, &csi);
if (ierr)
goto cleanup;
512 ierr = DMDAVecGetArrayRead(user->
fda, user->
lEta, &eta);
if (ierr)
goto cleanup;
513 ierr = DMDAVecGetArrayRead(user->
fda, user->
lZet, &zet);
if (ierr)
goto cleanup;
514 ierr = DMDAVecGetArrayRead(user->
da, user->
lAj, &aj);
if (ierr)
goto cleanup;
515 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
516 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
517 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
518 for (PetscInt component = 0; component < 3; ++component) {
519 MatStencil row = {.i = i, .j = j, .k = k, .c = component};
522 user, i, j, k, component, &ri, &rj, &rk);
524 PetscScalar block[9];
525 MatStencil cols[3] = {
526 {.i = i, .j = j, .k = k, .c = 0},
527 {.i = i, .j = j, .k = k, .c = 1},
528 {.i = i, .j = j, .k = k, .c = 2}
532 (
const PetscReal ***)aj, i, j, k, block);
533 ierr = MatSetValuesStencil(preconditioning_matrix, 1, &row, 3, cols,
534 &block[3 * component], INSERT_VALUES);
535 if (ierr)
goto cleanup;
542 if (aj) { cleanup_ierr = DMDAVecRestoreArrayRead(user->
da, user->
lAj, &aj);
if (!ierr) ierr = cleanup_ierr; }
543 if (zet) { cleanup_ierr = DMDAVecRestoreArrayRead(user->
fda, user->
lZet, &zet);
if (!ierr) ierr = cleanup_ierr; }
544 if (eta) { cleanup_ierr = DMDAVecRestoreArrayRead(user->
fda, user->
lEta, &eta);
if (!ierr) ierr = cleanup_ierr; }
545 if (csi) { cleanup_ierr = DMDAVecRestoreArrayRead(user->
fda, user->
lCsi, &csi);
if (!ierr) ierr = cleanup_ierr; }
546 if (ucont) { cleanup_ierr = DMDAVecRestoreArrayRead(user->
fda, user->
lUcont, &ucont);
if (!ierr) ierr = cleanup_ierr; }
547 PetscFunctionReturn(ierr);
551#define __FUNCT__ "MomentumPreconditionerEngine_ApplyConstraintRows"
554 UserCtx *user, Mat preconditioning_matrix)
556 DMDALocalInfo info = user->
info;
558 PetscFunctionBeginUser;
559 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
560 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
561 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
562 for (PetscInt component = 0; component < 3; ++component) {
565 user, i, j, k, component, &ri, &rj, &rk);
567 MatStencil row = {.i = i, .j = j, .k = k, .c = component};
568 MatStencil columns[2] = {row, row};
569 PetscScalar values[2] = {1.0, -1.0};
570 PetscInt column_count = 1;
572 columns[column_count++] = (MatStencil){
573 .i = ri, .j = rj, .k = rk, .c = component
576 PetscCall(MatSetValuesStencil(preconditioning_matrix, 1, &row,
577 column_count, columns, values,
584 PetscFunctionReturn(PETSC_SUCCESS);
593#define __FUNCT__ "MomentumNewtonJacobian_Create"
598 PetscFunctionBeginUser;
601 PETSC_COMM_WORLD, PETSC_ERR_SUP,
602 "Unsupported Newton Krylov Jacobian type/finite-difference-mode combination.");
606 "momentum_jacobian_finite_difference_matrix_free"));
607 PetscFunctionReturn(PETSC_SUCCESS);
614 PetscFunctionBeginUser;
615 PetscCall(MatMFFDComputeJacobian(snes, current_solution, jacobian->
jacobian_operator,
617 PetscFunctionReturn(PETSC_SUCCESS);
625 PetscFunctionBeginUser;
629 PetscFunctionReturn(PETSC_SUCCESS);
635 PetscFunctionBeginUser;
637 PetscFunctionReturn(PETSC_SUCCESS);
641#define __FUNCT__ "MomentumPreconditionerEngine_Create"
647 PetscFunctionBeginUser;
665 "momentum_preconditioner_frozen_point_block"));
669 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP,
670 "Unsupported Newton Krylov preconditioner model/structure combination.");
672 PetscFunctionReturn(PETSC_SUCCESS);
676#define __FUNCT__ "MomentumPreconditionerEngine_Assemble"
681 PetscFunctionBeginUser;
690 PetscFunctionReturn(PETSC_SUCCESS);
697 PetscFunctionBeginUser;
699 PetscFunctionReturn(PETSC_SUCCESS);
706 const char *actual_type = NULL;
707 PetscBool matches = PETSC_FALSE;
709 PetscFunctionBeginUser;
710 PetscCall(PCGetType(pc, &actual_type));
711 PetscCall(PetscStrcmp(actual_type, engine->
petsc_pc_type, &matches));
712 PetscCheck(matches, PETSC_COMM_WORLD, PETSC_ERR_SUP,
713 "Newton Krylov preconditioner model/structure requires internal PETSc PC "
714 "type '%s', but raw option processing selected '%s'.",
715 engine->
petsc_pc_type, actual_type ? actual_type :
"(unset)");
716 PetscFunctionReturn(PETSC_SUCCESS);
723 PetscFunctionBeginUser;
728 PetscFunctionReturn(PETSC_SUCCESS);
732#define __FUNCT__ "MomentumNewtonKrylov_FormJacobian"
735 Mat jacobian_operator, Mat preconditioning_matrix,
void *vctx)
738 PetscFunctionBeginUser;
739 (void)jacobian_operator;
740 (void)preconditioning_matrix;
744 PetscFunctionReturn(PETSC_SUCCESS);
748#define __FUNCT__ "MomentumNewtonKrylov_ClassifyRow"
770 UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt component,
771 PetscInt *ri, PetscInt *rj, PetscInt *rk)
773 const PetscInt mx = user->
info.mx, my = user->
info.my, mz = user->
info.mz;
774 const PetscInt coord[3] = {i, j, k};
775 const PetscInt size[3] = {mx, my, mz};
777 PetscBool periodic[3], periodic_duplicate = PETSC_FALSE;
778 PetscBool residual_zeroed = PETSC_FALSE, conditioned = PETSC_FALSE;
780 *ri = i; *rj = j; *rk = k;
781 for (PetscInt axis = 0; axis < 3; ++axis) {
782 periodic[axis] = (PetscBool)(
784 if (periodic[axis] && coord[axis] == 0) {
785 periodic_duplicate = PETSC_TRUE;
786 if (axis == 0) *ri = -2;
787 else if (axis == 1) *rj = -2;
790 if (periodic[axis] && coord[axis] == size[axis] - 1) {
791 periodic_duplicate = PETSC_TRUE;
792 if (axis == 0) *ri = mx + 1;
793 else if (axis == 1) *rj = my + 1;
797 if (!periodic[axis] && coord[axis] == 0) residual_zeroed = PETSC_TRUE;
798 if (coord[axis] == size[axis] - 1) residual_zeroed = PETSC_TRUE;
799 if (!periodic[axis] && coord[axis] == size[axis] - 2 && component == axis)
800 residual_zeroed = PETSC_TRUE;
803 if (!periodic[component] &&
804 (coord[component] == 0 || coord[component] == size[component] - 2)) {
805 PetscBool tangential_interior = PETSC_TRUE;
806 for (PetscInt axis = 0; axis < 3; ++axis) {
807 if (axis == component)
continue;
808 if (coord[axis] < 1 || coord[axis] > size[axis] - 2)
809 tangential_interior = PETSC_FALSE;
811 conditioned = tangential_interior;
821#define __FUNCT__ "MomentumPreconditionerEngine_CreateExactPointBlockMatrix"
831 UserCtx *user, Mat *preconditioning_matrix)
834 ISLocalToGlobalMapping local_to_global = NULL;
837 PetscInt local_size, global_size, ownership_start, ownership_end;
838 PetscInt *diagonal_nnz = NULL, *offdiagonal_nnz = NULL;
839 PetscInt ghost_starts[4] = {0, 0, 0, 0}, ghost_sizes[3] = {0, 0, 0};
840 PetscErrorCode ierr = PETSC_SUCCESS, cleanup_ierr;
842 PetscFunctionBeginUser;
843 PetscCheck(preconditioning_matrix != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
844 "Point-block matrix output is NULL.");
845 *preconditioning_matrix = NULL;
846 comm = PetscObjectComm((PetscObject)user->
fda);
847 PetscCall(DMDAGetLocalInfo(user->
fda, &info));
848 PetscCall(VecGetLocalSize(user->
Ucont, &local_size));
849 PetscCall(VecGetSize(user->
Ucont, &global_size));
850 PetscCall(VecGetOwnershipRange(user->
Ucont, &ownership_start, &ownership_end));
851 PetscCheck(local_size == ownership_end - ownership_start, comm, PETSC_ERR_PLIB,
852 "Velocity ownership range does not match its local size.");
853 ierr = PetscCalloc2(local_size, &diagonal_nnz,
854 local_size, &offdiagonal_nnz);
if (ierr)
goto cleanup;
855 ierr = DMGetLocalToGlobalMapping(user->
fda, &local_to_global);
if (ierr)
goto cleanup;
856 ierr = DMDAGetGhostCorners(user->
fda,
857 &ghost_starts[0], &ghost_starts[1], &ghost_starts[2],
858 &ghost_sizes[0], &ghost_sizes[1], &ghost_sizes[2]);
859 if (ierr)
goto cleanup;
861 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
862 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
863 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
864 for (PetscInt component = 0; component < 3; ++component) {
865 PetscInt ri, rj, rk, column_count;
866 MatStencil row_stencil = {.i = i, .j = j, .k = k, .c = component};
867 MatStencil column_stencils[3];
868 PetscInt row_local, row, column_locals[3], columns[3];
870 user, i, j, k, component, &ri, &rj, &rk);
874 for (PetscInt column_component = 0; column_component < 3;
875 ++column_component) {
876 column_stencils[column_component] = (MatStencil){
877 .i = i, .j = j, .k = k, .c = column_component
882 column_stencils[0] = row_stencil;
884 column_stencils[column_count++] = (MatStencil){
885 .i = ri, .j = rj, .k = rk, .c = component
889 row_local = component + 3 * (
890 (i - ghost_starts[0]) + ghost_sizes[0] * (
891 (j - ghost_starts[1]) + ghost_sizes[1] *
892 (k - ghost_starts[2])));
893 for (PetscInt column_index = 0; column_index < column_count;
895 const MatStencil column = column_stencils[column_index];
896 if (!(column.i >= ghost_starts[0] &&
897 column.i < ghost_starts[0] + ghost_sizes[0] &&
898 column.j >= ghost_starts[1] &&
899 column.j < ghost_starts[1] + ghost_sizes[1] &&
900 column.k >= ghost_starts[2] &&
901 column.k < ghost_starts[2] + ghost_sizes[2])) {
902 ierr = PetscError(comm, __LINE__, PETSC_FUNCTION_NAME, __FILE__,
903 PETSC_ERR_ARG_OUTOFRANGE,
905 "Point-block column lies outside the DMDA ghost stencil.");
908 column_locals[column_index] = column.c + 3 * (
909 (column.i - ghost_starts[0]) + ghost_sizes[0] * (
910 (column.j - ghost_starts[1]) + ghost_sizes[1] *
911 (column.k - ghost_starts[2])));
913 ierr = ISLocalToGlobalMappingApply(local_to_global, 1,
915 if (ierr)
goto cleanup;
916 ierr = ISLocalToGlobalMappingApply(local_to_global, column_count,
917 column_locals, columns);
918 if (ierr)
goto cleanup;
919 if (!(row >= ownership_start && row < ownership_end)) {
920 ierr = PetscError(comm, __LINE__, PETSC_FUNCTION_NAME, __FILE__,
921 PETSC_ERR_PLIB, PETSC_ERROR_INITIAL,
922 "DMDA-mapped point-block row is not locally owned.");
925 for (PetscInt column_index = 0; column_index < column_count;
927 PetscBool duplicate = PETSC_FALSE;
928 for (PetscInt previous = 0; previous < column_index; ++previous)
929 if (columns[previous] == columns[column_index]) duplicate = PETSC_TRUE;
930 if (columns[column_index] < 0) {
931 ierr = PetscError(comm, __LINE__, PETSC_FUNCTION_NAME, __FILE__,
932 PETSC_ERR_PLIB, PETSC_ERROR_INITIAL,
933 "DMDA-mapped point-block column is invalid.");
936 if (duplicate)
continue;
937 if (columns[column_index] >= ownership_start &&
938 columns[column_index] < ownership_end)
939 ++diagonal_nnz[row - ownership_start];
941 ++offdiagonal_nnz[row - ownership_start];
948 ierr = MatCreateAIJ(comm, local_size, local_size, global_size, global_size,
949 0, diagonal_nnz, 0, offdiagonal_nnz, &matrix);
950 if (ierr)
goto cleanup;
951 ierr = MatSetBlockSize(matrix, 3);
if (ierr)
goto cleanup;
952 ierr = MatSetLocalToGlobalMapping(matrix, local_to_global, local_to_global);
953 if (ierr)
goto cleanup;
954 ierr = MatSetStencil(matrix, 3, ghost_sizes, ghost_starts, 3);
955 if (ierr)
goto cleanup;
956 ierr = MatSetDM(matrix, user->
fda);
if (ierr)
goto cleanup;
957 ierr = MatSetOption(matrix, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE);
958 if (ierr)
goto cleanup;
959 ierr = PetscFree2(diagonal_nnz, offdiagonal_nnz);
if (ierr)
goto cleanup;
961 *preconditioning_matrix = matrix;
965 cleanup_ierr = MatDestroy(&matrix);
if (!ierr) ierr = cleanup_ierr;
966 cleanup_ierr = PetscFree2(diagonal_nnz, offdiagonal_nnz);
967 if (!ierr) ierr = cleanup_ierr;
968 PetscFunctionReturn(ierr);
972#define __FUNCT__ "MomentumNewtonKrylov_ApplyConstraints"
990 DMDALocalInfo info = user->
info;
992 Cmpnts ***x = NULL, ***conditioned = NULL, ***f = NULL, ***lx = NULL;
993 const PetscInt xs = info.xs, xe = info.xs + info.xm;
994 const PetscInt ys = info.ys, ye = info.ys + info.ym;
995 const PetscInt zs = info.zs, ze = info.zs + info.zm;
997 PetscFunctionBeginUser;
998 PetscCall(DMGetLocalVector(user->
fda, &local_x));
999 PetscCall(DMGlobalToLocalBegin(user->
fda, X, INSERT_VALUES, local_x));
1000 PetscCall(DMGlobalToLocalEnd(user->
fda, X, INSERT_VALUES, local_x));
1001 PetscCall(DMDAVecGetArrayRead(user->
fda, X, &x));
1002 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &conditioned));
1003 PetscCall(DMDAVecGetArray(user->
fda, F, &f));
1004 PetscCall(DMDAVecGetArrayRead(user->
fda, local_x, &lx));
1006 for (PetscInt k = zs; k < ze; ++k) {
1007 for (PetscInt j = ys; j < ye; ++j) {
1008 for (PetscInt i = xs; i < xe; ++i) {
1009 PetscScalar *fv = &f[k][j][i].x;
1010 const PetscScalar *xv = &x[k][j][i].
x;
1011 const PetscScalar *cv = &conditioned[k][j][i].x;
1013 for (PetscInt component = 0; component < 3; ++component) {
1014 PetscInt ri, rj, rk;
1016 user, i, j, k, component, &ri, &rj, &rk);
1017 const PetscScalar *rv = &lx[rk][rj][ri].x;
1027 PetscCall(DMDAVecRestoreArrayRead(user->
fda, local_x, &lx));
1028 PetscCall(DMDAVecRestoreArray(user->
fda, F, &f));
1029 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &conditioned));
1030 PetscCall(DMDAVecRestoreArrayRead(user->
fda, X, &x));
1031 PetscCall(DMRestoreLocalVector(user->
fda, &local_x));
1032 PetscFunctionReturn(PETSC_SUCCESS);
1036#define __FUNCT__ "MomentumNewtonKrylov_FormResidual"
1073 PetscFunctionBeginUser;
1075 PetscCall(VecCopy(X, user->
Ucont));
1118 PetscCall(VecCopy(user->
Rhs, F));
1119 PetscCall(VecScale(F, -1.0));
1121 PetscFunctionReturn(PETSC_SUCCESS);
1125#define __FUNCT__ "MomentumSolver_NewtonKrylov"
1133 PetscErrorCode ierr = PETSC_SUCCESS, cleanup_ierr;
1136 Vec solution = NULL, entry_backup = NULL;
1140 PetscBool restore_entry = PETSC_FALSE;
1141 PetscBool rhs_created = PETSC_FALSE;
1142 PetscBool solve_started = PETSC_FALSE;
1143 PetscBool committed = PETSC_FALSE;
1144 SNESConvergedReason reason = SNES_CONVERGED_ITERATING;
1145 PetscInt nonlinear_its = 0, function_evals = 0, linear_its = 0;
1146 PetscReal final_norm = PETSC_MAX_REAL;
1150 PetscFunctionBeginUser;
1152 PetscCheck(ibm == NULL && fsi == NULL, PETSC_COMM_WORLD, PETSC_ERR_SUP,
1153 "Newton Krylov version one does not accept IBM or FSI objects.");
1154 PetscCheck(user->
Rhs == NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
1155 "Newton Krylov requires UserCtx::Rhs to be unallocated on entry.");
1158 ierr = VecDuplicate(user->
Ucont, &solution);
if (ierr)
goto cleanup;
1159 ierr = VecDuplicate(user->
Ucont, &entry_backup);
if (ierr)
goto cleanup;
1160 ierr = VecDuplicate(user->
Ucont, &user->
Rhs);
if (ierr)
goto cleanup;
1161 rhs_created = PETSC_TRUE;
1162 ierr = SNESCreate(PetscObjectComm((PetscObject)user->
Ucont), &snes);
if (ierr)
goto cleanup;
1166 ierr = VecCopy(user->
Ucont, entry_backup);
if (ierr)
goto cleanup;
1167 restore_entry = PETSC_TRUE;
1168 ierr = VecCopy(user->
Ucont, solution);
if (ierr)
goto cleanup;
1172 &ctx.
jacobian, &preconditioner_description);
if (ierr)
goto cleanup;
1173 ierr = SNESSetOptionsPrefix(snes,
"mom_nk_");
if (ierr)
goto cleanup;
1174 ierr = SNESSetType(snes, SNESNEWTONLS);
if (ierr)
goto cleanup;
1175 ierr = SNESSetDM(snes, user->
fda);
if (ierr)
goto cleanup;
1179 &preconditioner_description,
1183 ierr = SNESGetKSP(snes, &ksp);
if (ierr)
goto cleanup;
1184 ierr = KSPSetType(ksp, KSPGMRES);
if (ierr)
goto cleanup;
1185 ierr = KSPGetPC(ksp, &pc);
if (ierr)
goto cleanup;
1187 ierr = SNESSetFromOptions(snes);
if (ierr)
goto cleanup;
1192 "Newton Krylov Jacobian: finite_difference / matrix_free; "
1193 "Preconditioner: %s; PETSc Jacobian matrix type: MATMFFD; "
1194 "PETSc PC type: %s.\n",
1196 "none" :
"frozen_momentum_jacobian / point_block",
1200 solve_started = PETSC_TRUE;
1201 ierr = SNESSolve(snes, NULL, solution);
1202 if (ierr)
goto cleanup;
1203 ierr = SNESGetConvergedReason(snes, &reason);
if (ierr)
goto cleanup;
1204 ierr = SNESGetIterationNumber(snes, &nonlinear_its);
if (ierr)
goto cleanup;
1205 ierr = SNESGetNumberFunctionEvals(snes, &function_evals);
if (ierr)
goto cleanup;
1206 ierr = SNESGetLinearSolveIterations(snes, &linear_its);
if (ierr)
goto cleanup;
1207 ierr = SNESGetFunctionNorm(snes, &final_norm);
if (ierr)
goto cleanup;
1210 ierr = VecCopy(solution, user->
Ucont);
if (ierr)
goto cleanup;
1213 ierr = VecCopy(entry_backup, user->
Ucont);
if (ierr)
goto cleanup;
1218 restore_entry = PETSC_FALSE;
1219 committed = (PetscBool)(reason > 0);
1222 "Newton Krylov momentum solve: reason=%s (%d), Newton iterations=%d, residual evaluations=%d, Krylov iterations=%d, final norm=%.6e, state=%s.\n",
1223 SNESConvergedReasons[reason], (PetscInt)reason, nonlinear_its, function_evals,
1224 linear_its, (
double)final_norm, reason > 0 ?
"committed" :
"rolled back");
1225 if (reason <= 0) ierr = PETSC_ERR_CONV_FAILED;
1231 if (solve_started && snes) {
1232 (void)SNESGetConvergedReason(snes, &reason);
1233 (void)SNESGetIterationNumber(snes, &nonlinear_its);
1234 (void)SNESGetNumberFunctionEvals(snes, &function_evals);
1235 (void)SNESGetLinearSolveIterations(snes, &linear_its);
1236 (void)SNESGetFunctionNorm(snes, &final_norm);
1238 if (restore_entry && entry_backup) {
1239 cleanup_ierr = VecCopy(entry_backup, user->
Ucont);
1240 if (!ierr) ierr = cleanup_ierr;
1242 if (!ierr) ierr = cleanup_ierr;
1244 if (!ierr) ierr = cleanup_ierr;
1246 committed = PETSC_FALSE;
1252 if (solve_started) {
1254 linear_its, final_norm, committed);
1257 cleanup_ierr = VecDestroy(&user->
Rhs);
1258 if (!ierr) ierr = cleanup_ierr;
1260 cleanup_ierr = VecDestroy(&entry_backup);
if (!ierr) ierr = cleanup_ierr;
1261 cleanup_ierr = VecDestroy(&solution);
if (!ierr) ierr = cleanup_ierr;
1264 cleanup_ierr = SNESDestroy(&snes);
if (!ierr) ierr = cleanup_ierr;
1265 PetscFunctionReturn(ierr);
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent component-staggered vector fields.
PetscErrorCode ApplyBoundaryConditions(UserCtx *user)
Main boundary-condition orchestrator executed during solver timestepping.
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes periodic endpoint cells for a list of cell-centered fields.
FieldId
Compile-time identity for a catalogued Eulerian field.
#define GLOBAL
Scope for global logging across all processes.
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
@ LOG_INFO
Informational messages about program execution.
@ LOG_WARNING
Non-critical issues that warrant attention.
static PetscErrorCode MomentumNewtonJacobian_Register(SNES snes, MomentumNewtonJacobian *jacobian, MomentumPreconditionerEngine *engine, MomentumNewtonKrylovContext *ctx)
Registers the application orchestration callback and both SNES matrices.
static const MomentumPreconditionerModelOps frozen_momentum_point_block_ops
PetscBool aliases_jacobian_operator
const MomentumPreconditionerModelOps * model_ops
static PetscErrorCode MomentumPreconditionerEngine_ConfigurePetscPC(MomentumPreconditionerEngine *engine, PC pc)
Applies the validated model/structure-to-PETSc-PC mapping.
static MomentumNewtonKrylovRowType MomentumNewtonKrylov_ClassifyRow(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscInt *ri, PetscInt *rj, PetscInt *rk)
Classifies one stored staggered component row and its periodic representative.
static PetscErrorCode FrozenMomentumJacobian_DescribePointBlock(UserCtx *user, MomentumPreconditionerDescription *description)
Describes the audited frozen-coefficient point-block model.
static PetscErrorCode MomentumNewtonKrylov_ApplyConstraints(MomentumNewtonKrylovContext *ctx, Vec X, Vec F)
Replaces every non-independent residual row with an explicit equation.
static PetscErrorCode MomentumPreconditionerEngine_CreateExactPointBlockMatrix(UserCtx *user, Mat *preconditioning_matrix)
Creates the frozen point-block P matrix with its exact scalar pattern.
static PetscErrorCode MomentumPreconditionerEngine_ValidatePetscPC(MomentumPreconditionerEngine *engine, PC pc)
Rejects raw options that select an unvalidated PETSc PC backend.
static PetscReal FrozenMomentumJacobian_MetricNormSquared(Cmpnts metric)
Returns the squared Euclidean norm of one metric vector.
MomentumPreconditionerStructure
@ MOM_NK_PC_STRUCTURE_POINT_BLOCK
@ MOM_NK_PC_STRUCTURE_NONE
static PetscErrorCode MomentumNewtonKrylov_FormJacobian(SNES snes, Vec current_solution, Mat jacobian_operator, Mat preconditioning_matrix, void *vctx)
Updates the Jacobian and then assembles any separate preconditioning matrix.
MomentumPreconditionerDescription description
static PetscErrorCode MomentumPreconditionerEngine_Create(UserCtx *user, Mat jacobian_operator, const MomentumPreconditionerDescription *requested, MomentumPreconditionerEngine *engine)
Validates a model/structure and creates or aliases its matrix.
const char * petsc_pc_type
static void MomentumNewtonKrylov_WriteSummary(const MomentumNewtonKrylovContext *ctx, SNESConvergedReason reason, PetscInt nonlinear_its, PetscInt function_evals, PetscInt linear_its, PetscReal final_norm, PetscBool committed)
Appends one rank-zero structured Newton result for a physical step.
MomentumNewtonFiniteDifferenceMode finite_difference_mode
static void FrozenMomentumJacobian_PointBlock(const SimCtx *simCtx, const Cmpnts ***ucont, const Cmpnts ***csi, const Cmpnts ***eta, const Cmpnts ***zet, const PetscReal ***aj, PetscInt i, PetscInt j, PetscInt k, PetscScalar block[9])
Returns the audited frozen-momentum point block in modern residual sign.
MomentumNewtonJacobianType type
static PetscErrorCode MomentumNewtonKrylov_ReadLinearizationConfig(MomentumNewtonJacobian *jacobian, MomentumPreconditionerDescription *description)
Reads application-owned Jacobian and preconditioner mathematics.
static PetscErrorCode MomentumPreconditionerEngine_Assemble(MomentumPreconditionerEngine *engine, UserCtx *user, Vec current_solution)
Runs model insertion, common row handling, and final assembly.
static PetscErrorCode MomentumNewtonJacobian_Destroy(MomentumNewtonJacobian *jacobian)
Destroys a partially or fully created Jacobian operator.
MomentumNewtonJacobian jacobian
static void MomentumNewtonKrylov_OpenHistory(MomentumNewtonKrylovContext *ctx)
Opens the optional rank-zero Newton iteration-history file.
MomentumPreconditionerModel model
static PetscErrorCode MomentumNewtonJacobian_Create(SNES snes, MomentumNewtonJacobian *jacobian)
Creates the selected Jacobian operator; currently PETSc MFFD only.
MomentumPreconditionerStructure structure
static PetscErrorCode FrozenMomentumJacobian_AssemblePointBlocks(UserCtx *user, Vec current_solution, Mat preconditioning_matrix)
Inserts only the audited interior frozen-momentum point blocks.
MomentumNewtonJacobianType
@ MOM_NK_JACOBIAN_FINITE_DIFFERENCE
Mat preconditioning_matrix
static PetscErrorCode MomentumNewtonJacobian_Update(SNES snes, Vec current_solution, MomentumNewtonJacobian *jacobian)
Updates the matrix-free finite-difference operator base.
PetscBool have_initial_norm
PetscBool owns_preconditioning_matrix
MomentumNewtonKrylovRowType
@ MOM_NK_ROW_PERIODIC_DUPLICATE
@ MOM_NK_ROW_FIXED_HOMOGENEOUS
@ MOM_NK_ROW_FIXED_CONDITIONED
static PetscErrorCode MomentumNewtonKrylov_Validate(UserCtx *user)
Rejects configurations outside the audited version-one feature set.
static PetscErrorCode MomentumPreconditionerEngine_Destroy(MomentumPreconditionerEngine *engine)
Destroys only a separately owned preconditioning matrix.
MomentumPreconditionerEngine preconditioning_engine
static PetscErrorCode MomentumNewtonKrylov_FormResidual(SNES snes, Vec X, Vec F, void *ctx)
Adapts a PETSc trial vector to the existing momentum residual path.
MomentumNewtonFiniteDifferenceMode
@ MOM_NK_FD_MODE_MATRIX_FREE
static PetscErrorCode MomentumNewtonKrylov_Monitor(SNES snes, PetscInt iteration, PetscReal norm, void *ctx)
Captures SNES iteration norms and optionally writes PICurv history rows.
static PetscErrorCode MomentumPreconditionerEngine_ApplyConstraintRows(UserCtx *user, Mat preconditioning_matrix)
Inserts all common fixed, homogeneous, and periodic-duplicate rows.
MomentumPreconditionerModel
@ MOM_NK_PC_MODEL_FROZEN_MOMENTUM_JACOBIAN
PetscReal MomentumBDFCoefficient(SimCtx *simCtx)
Returns the BDF physical-time coefficient a0 for the current step.
PetscErrorCode ComputeTotalResidual(UserCtx *user)
Computes the shared spatial-plus-BDF momentum residual in user->Rhs.
PetscErrorCode Contra2Cart(UserCtx *user)
Reconstructs Cartesian velocity (Ucat) at cell centers from contravariant velocity (Ucont) defined on...
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
PetscErrorCode(* Describe)(UserCtx *, MomentumPreconditionerDescription *)
PetscErrorCode(* AssembleInterior)(UserCtx *, Vec, Mat)
#define MomentumSolver_NewtonKrylov
PetscBool mom_nk_monitor_history
BoundaryFaceConfig boundary_faces[6]
SimCtx * simCtx
Back-pointer to the master simulation context.
PetscBool mom_last_converged
@ BC_HANDLER_PERIODIC_GEOMETRIC
@ BC_HANDLER_INLET_PARABOLIC
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
@ BC_HANDLER_INLET_PROFILE_FROM_FILE
@ BC_HANDLER_OUTLET_CONSERVATION
BCHandlerType handler_type
char log_dir[PETSC_MAX_PATH_LEN]
BCFace
Identifies the six logical faces of a structured computational block.
Holds the complete configuration for one of the six boundary faces.
A 3D point or vector with PetscScalar components.
Holds all data related to the state and motion of a body in FSI.
Represents a collection of nodes forming a surface for the IBM.
The master context for the entire simulation.
User-defined context containing data specific to a single computational grid level.