PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
momentum_newton_krylov.c
Go to the documentation of this file.
1#include "momentumsolvers.h"
2
6
10
16
21
26
33
34typedef struct {
35 PetscErrorCode (*Describe)(UserCtx *, MomentumPreconditionerDescription *);
36 PetscErrorCode (*AssembleInterior)(UserCtx *, Vec, Mat);
38
47
48typedef struct {
52 PetscReal initial_norm;
53 /* These objects are owned by the solve context. They are deliberately
54 * kept here (rather than in UserCtx) because an SNES is per physical solve. */
58
65
66static PetscErrorCode MomentumNewtonKrylov_Validate(UserCtx *user);
67static PetscErrorCode MomentumNewtonKrylov_FormResidual(SNES snes, Vec X, Vec F, void *ctx);
68static PetscErrorCode MomentumNewtonKrylov_Monitor(SNES snes, PetscInt iteration,
69 PetscReal norm, void *ctx);
72 SNESConvergedReason reason,
73 PetscInt nonlinear_its,
74 PetscInt function_evals,
75 PetscInt linear_its,
76 PetscReal final_norm,
77 PetscBool committed);
79 Vec X, Vec F);
81 UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt component,
82 PetscInt *ri, PetscInt *rj, PetscInt *rk);
85static PetscErrorCode MomentumNewtonKrylov_FormJacobian(SNES snes, Vec current_solution,
86 Mat jacobian_operator, Mat preconditioning_matrix, void *vctx);
88 UserCtx *user, Vec current_solution, Mat preconditioning_matrix);
90 UserCtx *user, Mat *preconditioning_matrix);
91
92/**
93 * @brief Captures SNES iteration norms and optionally writes PICurv history rows.
94 * @details SNES supplies the already-computed norm, so this monitor never causes
95 * an additional nonlinear residual evaluation. PETSc monitors selected through
96 * `-mom_nk_snes_monitor` remain independent and may run alongside this callback.
97 */
98static PetscErrorCode MomentumNewtonKrylov_Monitor(SNES snes, PetscInt iteration,
99 PetscReal norm, void *vctx)
100{
102 SimCtx *simCtx = ctx->user->simCtx;
103
104 (void)snes;
105 PetscFunctionBeginUser;
106 if (iteration == 0 && !ctx->have_initial_norm) {
107 ctx->initial_norm = norm;
108 ctx->have_initial_norm = PETSC_TRUE;
109 }
110 if (ctx->history_file) {
111 (void)fprintf(ctx->history_file,
112 "step: %d | block: %d | newton: %d | nonlinear_norm: %.16e\n",
113 (int)simCtx->step, (int)ctx->user->_this, (int)iteration,
114 (double)norm);
115 (void)fflush(ctx->history_file);
116 }
117 PetscFunctionReturn(PETSC_SUCCESS);
118}
119
120/** @brief Opens the optional rank-zero Newton iteration-history file. */
122{
123 SimCtx *simCtx = ctx->user->simCtx;
124 char path[PETSC_MAX_PATH_LEN + 128];
125 const char *mode;
126
127 if (!simCtx->mom_nk_monitor_history || simCtx->rank != 0) return;
128 if (PetscSNPrintf(path, sizeof(path),
129 "%s/Momentum_Solver_Newton_Krylov_History_Block_%d.log",
130 simCtx->log_dir, (int)ctx->user->_this)) return;
131 mode = (simCtx->step == simCtx->StartStep + 1 && !simCtx->continueMode) ? "w" : "a";
132 ctx->history_file = fopen(path, mode);
133 if (!ctx->history_file) {
134 LOG(GLOBAL, LOG_WARNING, "Could not open Newton iteration-history log '%s'.\n", path);
135 return;
136 }
137 if (mode[0] == 'w') {
138 (void)fprintf(ctx->history_file,
139 "# step | block | Newton iteration | nonlinear residual norm\n");
140 } else if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1) {
141 (void)fprintf(ctx->history_file, "# Continuation from step %d\n", (int)simCtx->StartStep);
142 }
143}
144
145/**
146 * @brief Appends one rank-zero structured Newton result for a physical step.
147 * @details File failures are deliberately diagnostic-only: rollback and PETSc
148 * cleanup must retain their original error behavior.
149 */
151 SNESConvergedReason reason,
152 PetscInt nonlinear_its,
153 PetscInt function_evals,
154 PetscInt linear_its,
155 PetscReal final_norm,
156 PetscBool committed)
157{
158 SimCtx *simCtx = ctx->user->simCtx;
159 char path[PETSC_MAX_PATH_LEN + 128];
160 const char *mode;
161 const char *reason_name;
162 FILE *file;
163
164 if (simCtx->rank != 0) return;
165 if (PetscSNPrintf(path, sizeof(path),
166 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_%d.log",
167 simCtx->log_dir, (int)ctx->user->_this)) return;
168 mode = (simCtx->step == simCtx->StartStep + 1 && !simCtx->continueMode) ? "w" : "a";
169 file = fopen(path, mode);
170 if (!file) {
171 LOG(GLOBAL, LOG_WARNING, "Could not open Newton summary log '%s'.\n", path);
172 return;
173 }
174 if (mode[0] == 'w') {
175 (void)fprintf(file,
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");
180 } else if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1) {
181 (void)fprintf(file, "# Continuation from step %d\n", (int)simCtx->StartStep);
182 }
183 reason_name = reason == SNES_CONVERGED_ITERATING
184 ? "SNES_CONVERGED_ITERATING" : SNESConvergedReasons[reason];
185 (void)fprintf(file,
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: ",
190 (int)simCtx->step, (int)ctx->user->_this,
192 "none" : "frozen_momentum_jacobian / point_block",
193 reason_name, (int)reason,
194 (int)nonlinear_its, (int)function_evals, (int)linear_its);
195 if (ctx->have_initial_norm) (void)fprintf(file, "%.16e", (double)ctx->initial_norm);
196 else (void)fprintf(file, "unavailable");
197 (void)fprintf(file, " | final: %.16e | state: %s\n", (double)final_norm,
198 committed ? "committed" : "rolled_back");
199 (void)fclose(file);
200}
201
202#undef __FUNCT__
203#define __FUNCT__ "MomentumNewtonKrylov_Validate"
204/**
205 * @brief Rejects configurations outside the audited version-one feature set.
206 * @param user Single-block momentum context to validate.
207 * @return PetscErrorCode 0 when the configuration is supported.
208 */
209static PetscErrorCode MomentumNewtonKrylov_Validate(UserCtx *user)
210{
211 SimCtx *simCtx;
212 PetscReal mask_max = 0.0;
213 PetscInt velocity_dof = 0;
214
215 PetscFunctionBeginUser;
216 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
217 "Newton Krylov requires a non-NULL UserCtx.");
218 simCtx = user->simCtx;
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).",
225 velocity_dof);
226 PetscCheck(simCtx->block_number == 1, PETSC_COMM_WORLD, PETSC_ERR_SUP,
227 "Newton Krylov version one supports exactly one block (got %d).",
228 simCtx->block_number);
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.");
233 PetscCheck(!simCtx->moveframe && !simCtx->rotateframe, PETSC_COMM_WORLD, PETSC_ERR_SUP,
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) {
244 const BoundaryFaceConfig *cfg = &user->boundary_faces[face];
245 PetscBool supported = PETSC_FALSE;
246
247 switch (cfg->handler_type) {
249 supported = (PetscBool)(cfg->mathematical_type == WALL);
250 break;
254 supported = (PetscBool)(cfg->mathematical_type == INLET);
255 break;
257 supported = (PetscBool)(cfg->mathematical_type == OUTLET);
258 break;
260 supported = (PetscBool)(cfg->mathematical_type == PERIODIC);
261 break;
262 default:
263 supported = PETSC_FALSE;
264 break;
265 }
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.",
268 face, (PetscInt)cfg->mathematical_type, (PetscInt)cfg->handler_type);
269 }
270
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.");
280
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).",
286 (double)mask_max);
287 PetscFunctionReturn(PETSC_SUCCESS);
288}
289
290#undef __FUNCT__
291#define __FUNCT__ "MomentumNewtonKrylov_ReadLinearizationConfig"
292/**
293 * @brief Reads application-owned Jacobian and preconditioner mathematics.
294 */
297{
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;
303
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);
321
322 PetscCall(PetscOptionsGetString(NULL, NULL, "-mom_nk_preconditioner_model",
323 model, sizeof(model), &set));
324 PetscCall(PetscStrcasecmp(model, "none", &match));
325 if (match) description->model = MOM_NK_PC_MODEL_NONE;
326 if (!match) {
327 PetscCall(PetscStrcasecmp(model, "frozen_momentum_jacobian", &match));
328 if (match) description->model = MOM_NK_PC_MODEL_FROZEN_MOMENTUM_JACOBIAN;
329 }
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));
336 if (match) description->structure = MOM_NK_PC_STRUCTURE_NONE;
337 if (!match) {
338 PetscCall(PetscStrcasecmp(structure, "point_block", &match));
339 if (match) description->structure = MOM_NK_PC_STRUCTURE_POINT_BLOCK;
340 }
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);
344 PetscCheck((description->model == MOM_NK_PC_MODEL_NONE &&
345 description->structure == MOM_NK_PC_STRUCTURE_NONE) ||
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);
352}
353
354/** @brief Returns the squared Euclidean norm of one metric vector. */
356{
357 return metric.x * metric.x + metric.y * metric.y + metric.z * metric.z;
358}
359
360/** @brief Returns the audited frozen-momentum point block in modern residual sign. */
362 const Cmpnts ***ucont, const Cmpnts ***csi, const Cmpnts ***eta,
363 const Cmpnts ***zet, const PetscReal ***aj, PetscInt i, PetscInt j,
364 PetscInt k, PetscScalar block[9])
365{
366 const PetscReal dtc = MomentumBDFCoefficient((SimCtx *)simCtx) / simCtx->dt;
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 * (
378 FrozenMomentumJacobian_MetricNormSquared(eta[k][j - 1][i + 1]));
379 const PetscReal g33ip = 0.25 * (
383 FrozenMomentumJacobian_MetricNormSquared(zet[k - 1][j][i + 1]));
384 const PetscReal g11jp = 0.25 * (
388 FrozenMomentumJacobian_MetricNormSquared(csi[k][j + 1][i - 1]));
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 * (
396 FrozenMomentumJacobian_MetricNormSquared(zet[k - 1][j + 1][i]));
397 const PetscReal g11kp = 0.25 * (
401 FrozenMomentumJacobian_MetricNormSquared(csi[k + 1][j][i - 1]));
402 const PetscReal g22kp = 0.25 * (
406 FrozenMomentumJacobian_MetricNormSquared(eta[k + 1][j - 1][i]));
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;
424
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;
458
459 /* The modern residual is the negative of the legacy residual. */
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;
463}
464
465#undef __FUNCT__
466#define __FUNCT__ "FrozenMomentumJacobian_DescribePointBlock"
467/** @brief Describes the audited frozen-coefficient point-block model. */
469 UserCtx *user, MomentumPreconditionerDescription *description)
470{
471 PetscFunctionBeginUser;
472 (void)user;
473 PetscCheck(description != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
474 "Preconditioner description is NULL.");
477 description->block_size = 3;
478 description->stencil_width = 0;
479 PetscFunctionReturn(PETSC_SUCCESS);
480}
481
482#undef __FUNCT__
483#define __FUNCT__ "FrozenMomentumJacobian_AssemblePointBlocks"
484/** @brief Inserts only the audited interior frozen-momentum point blocks. */
486 UserCtx *user, Vec current_solution, Mat preconditioning_matrix)
487{
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;
492
493 PetscFunctionBeginUser;
494 /*
495 * current_solution is the current SNES trial Ucont Vec: it is layout-compatible
496 * with user->fda/user->Ucont, but is not necessarily the canonical user->Ucont
497 * selected by UpdateLocalGhosts(FIELD_ID_UCONT). Scatter that trial Vec directly with
498 * user->fda so PETSc applies its MPI ownership, periodic topology, component
499 * ordering, and ghost mapping without canonical-field synchronization or mutation
500 * of current_solution. UpdateLocalGhosts additionally repairs the component-normal
501 * staggered buffers Uxi(i=-1,mx), Ueta(j=-1,my), and Uzeta(k=-1,mz); the audited
502 * point-block velocity stencil reads none of those planes, so the repair cannot
503 * change a coefficient. Revisit this choice and extend the periodic-localization
504 * tests if the model stencil is expanded to read any repaired normal buffer.
505 */
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};
520 PetscInt ri, rj, rk;
522 user, i, j, k, component, &ri, &rj, &rk);
523 if (type == MOM_NK_ROW_PHYSICAL) {
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}
529 };
530 FrozenMomentumJacobian_PointBlock(user->simCtx, (const Cmpnts ***)ucont,
531 (const Cmpnts ***)csi, (const Cmpnts ***)eta, (const Cmpnts ***)zet,
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;
536 }
537 }
538 }
539 }
540 }
541cleanup:
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);
548}
549
550#undef __FUNCT__
551#define __FUNCT__ "MomentumPreconditionerEngine_ApplyConstraintRows"
552/** @brief Inserts all common fixed, homogeneous, and periodic-duplicate rows. */
554 UserCtx *user, Mat preconditioning_matrix)
555{
556 DMDALocalInfo info = user->info;
557
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) {
563 PetscInt ri, rj, rk;
565 user, i, j, k, component, &ri, &rj, &rk);
566 if (type != MOM_NK_ROW_PHYSICAL) {
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;
571 if (type == MOM_NK_ROW_PERIODIC_DUPLICATE) {
572 columns[column_count++] = (MatStencil){
573 .i = ri, .j = rj, .k = rk, .c = component
574 };
575 }
576 PetscCall(MatSetValuesStencil(preconditioning_matrix, 1, &row,
577 column_count, columns, values,
578 INSERT_VALUES));
579 }
580 }
581 }
582 }
583 }
584 PetscFunctionReturn(PETSC_SUCCESS);
585}
586
591
592#undef __FUNCT__
593#define __FUNCT__ "MomentumNewtonJacobian_Create"
594/** @brief Creates the selected Jacobian operator; currently PETSc MFFD only. */
595static PetscErrorCode MomentumNewtonJacobian_Create(SNES snes,
596 MomentumNewtonJacobian *jacobian)
597{
598 PetscFunctionBeginUser;
599 PetscCheck(jacobian->type == MOM_NK_JACOBIAN_FINITE_DIFFERENCE &&
601 PETSC_COMM_WORLD, PETSC_ERR_SUP,
602 "Unsupported Newton Krylov Jacobian type/finite-difference-mode combination.");
603 PetscCall(MatCreateSNESMF(snes, &jacobian->jacobian_operator));
604 PetscCall(MatSetOptionsPrefix(jacobian->jacobian_operator, "mom_nk_"));
605 PetscCall(PetscObjectSetName((PetscObject)jacobian->jacobian_operator,
606 "momentum_jacobian_finite_difference_matrix_free"));
607 PetscFunctionReturn(PETSC_SUCCESS);
608}
609
610/** @brief Updates the matrix-free finite-difference operator base. */
611static PetscErrorCode MomentumNewtonJacobian_Update(SNES snes, Vec current_solution,
612 MomentumNewtonJacobian *jacobian)
613{
614 PetscFunctionBeginUser;
615 PetscCall(MatMFFDComputeJacobian(snes, current_solution, jacobian->jacobian_operator,
616 jacobian->jacobian_operator, NULL));
617 PetscFunctionReturn(PETSC_SUCCESS);
618}
619
620/** @brief Registers the application orchestration callback and both SNES matrices. */
621static PetscErrorCode MomentumNewtonJacobian_Register(SNES snes,
624{
625 PetscFunctionBeginUser;
626 PetscCall(SNESSetJacobian(snes, jacobian->jacobian_operator,
629 PetscFunctionReturn(PETSC_SUCCESS);
630}
631
632/** @brief Destroys a partially or fully created Jacobian operator. */
634{
635 PetscFunctionBeginUser;
636 PetscCall(MatDestroy(&jacobian->jacobian_operator));
637 PetscFunctionReturn(PETSC_SUCCESS);
638}
639
640#undef __FUNCT__
641#define __FUNCT__ "MomentumPreconditionerEngine_Create"
642/** @brief Validates a model/structure and creates or aliases its matrix. */
644 Mat jacobian_operator, const MomentumPreconditionerDescription *requested,
646{
647 PetscFunctionBeginUser;
648 engine->description = *requested;
649 if (requested->model == MOM_NK_PC_MODEL_NONE &&
650 requested->structure == MOM_NK_PC_STRUCTURE_NONE) {
651 engine->description.block_size = 0;
652 engine->description.stencil_width = 0;
653 engine->preconditioning_matrix = jacobian_operator;
654 engine->aliases_jacobian_operator = PETSC_TRUE;
655 engine->owns_preconditioning_matrix = PETSC_FALSE;
656 engine->petsc_pc_type = PCNONE;
657 } else if (requested->model == MOM_NK_PC_MODEL_FROZEN_MOMENTUM_JACOBIAN &&
660 PetscCall(engine->model_ops->Describe(user, &engine->description));
662 user, &engine->preconditioning_matrix));
663 engine->owns_preconditioning_matrix = PETSC_TRUE;
664 PetscCall(PetscObjectSetName((PetscObject)engine->preconditioning_matrix,
665 "momentum_preconditioner_frozen_point_block"));
666 engine->aliases_jacobian_operator = PETSC_FALSE;
667 engine->petsc_pc_type = PCPBJACOBI;
668 } else {
669 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP,
670 "Unsupported Newton Krylov preconditioner model/structure combination.");
671 }
672 PetscFunctionReturn(PETSC_SUCCESS);
673}
674
675#undef __FUNCT__
676#define __FUNCT__ "MomentumPreconditionerEngine_Assemble"
677/** @brief Runs model insertion, common row handling, and final assembly. */
679 MomentumPreconditionerEngine *engine, UserCtx *user, Vec current_solution)
680{
681 PetscFunctionBeginUser;
682 if (engine->aliases_jacobian_operator) PetscFunctionReturn(PETSC_SUCCESS);
683 PetscCall(MatZeroEntries(engine->preconditioning_matrix));
684 PetscCall(engine->model_ops->AssembleInterior(user, current_solution,
685 engine->preconditioning_matrix));
687 user, engine->preconditioning_matrix));
688 PetscCall(MatAssemblyBegin(engine->preconditioning_matrix, MAT_FINAL_ASSEMBLY));
689 PetscCall(MatAssemblyEnd(engine->preconditioning_matrix, MAT_FINAL_ASSEMBLY));
690 PetscFunctionReturn(PETSC_SUCCESS);
691}
692
693/** @brief Applies the validated model/structure-to-PETSc-PC mapping. */
695 MomentumPreconditionerEngine *engine, PC pc)
696{
697 PetscFunctionBeginUser;
698 PetscCall(PCSetType(pc, engine->petsc_pc_type));
699 PetscFunctionReturn(PETSC_SUCCESS);
700}
701
702/** @brief Rejects raw options that select an unvalidated PETSc PC backend. */
704 MomentumPreconditionerEngine *engine, PC pc)
705{
706 const char *actual_type = NULL;
707 PetscBool matches = PETSC_FALSE;
708
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);
717}
718
719/** @brief Destroys only a separately owned preconditioning matrix. */
722{
723 PetscFunctionBeginUser;
724 if (!engine->owns_preconditioning_matrix) engine->preconditioning_matrix = NULL;
725 PetscCall(MatDestroy(&engine->preconditioning_matrix));
726 engine->aliases_jacobian_operator = PETSC_FALSE;
727 engine->owns_preconditioning_matrix = PETSC_FALSE;
728 PetscFunctionReturn(PETSC_SUCCESS);
729}
730
731#undef __FUNCT__
732#define __FUNCT__ "MomentumNewtonKrylov_FormJacobian"
733/** @brief Updates the Jacobian and then assembles any separate preconditioning matrix. */
734static PetscErrorCode MomentumNewtonKrylov_FormJacobian(SNES snes, Vec current_solution,
735 Mat jacobian_operator, Mat preconditioning_matrix, void *vctx)
736{
738 PetscFunctionBeginUser;
739 (void)jacobian_operator;
740 (void)preconditioning_matrix;
741 PetscCall(MomentumNewtonJacobian_Update(snes, current_solution, &ctx->jacobian));
743 &ctx->preconditioning_engine, ctx->user, current_solution));
744 PetscFunctionReturn(PETSC_SUCCESS);
745}
746
747#undef __FUNCT__
748#define __FUNCT__ "MomentumNewtonKrylov_ClassifyRow"
749/**
750 * @brief Classifies one stored staggered component row and its periodic representative.
751 *
752 * Negative nonperiodic planes and positive dummy planes are zeroed by the legacy
753 * residual treatment. Only a face-normal row actually written by its boundary
754 * handler is conditioned; the remaining zeroed rows use F=X. Periodic endpoint
755 * rows use the same wrapped representatives as SynchronizePeriodicStaggeredFields().
756 * At periodic/nonperiodic intersections the periodic equation owns rows that a
757 * shrunken boundary-handler loop does not condition.
758 *
759 * @param user Block context containing boundary metadata and global dimensions.
760 * @param i Global i index.
761 * @param j Global j index.
762 * @param k Global k index.
763 * @param component Stored component, 0=x, 1=y, 2=z.
764 * @param ri Returned periodic representative i index.
765 * @param rj Returned periodic representative j index.
766 * @param rk Returned periodic representative k index.
767 * @return Exact version-one row category.
768 */
770 UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt component,
771 PetscInt *ri, PetscInt *rj, PetscInt *rk)
772{
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};
776 const BCFace neg_face[3] = {BC_FACE_NEG_X, BC_FACE_NEG_Y, BC_FACE_NEG_Z};
777 PetscBool periodic[3], periodic_duplicate = PETSC_FALSE;
778 PetscBool residual_zeroed = PETSC_FALSE, conditioned = PETSC_FALSE;
779
780 *ri = i; *rj = j; *rk = k;
781 for (PetscInt axis = 0; axis < 3; ++axis) {
782 periodic[axis] = (PetscBool)(
783 user->boundary_faces[neg_face[axis]].mathematical_type == PERIODIC);
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;
788 else *rk = -2;
789 }
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;
794 else *rk = mz + 1;
795 }
796
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;
801 }
802
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;
810 }
811 conditioned = tangential_interior;
812 }
813
814 if (conditioned) return MOM_NK_ROW_FIXED_CONDITIONED;
815 if (periodic_duplicate) return MOM_NK_ROW_PERIODIC_DUPLICATE;
816 if (residual_zeroed) return MOM_NK_ROW_FIXED_HOMOGENEOUS;
817 return MOM_NK_ROW_PHYSICAL;
818}
819
820#undef __FUNCT__
821#define __FUNCT__ "MomentumPreconditionerEngine_CreateExactPointBlockMatrix"
822/**
823 * @brief Creates the frozen point-block P matrix with its exact scalar pattern.
824 * @details Row and column ownership follows the velocity DMDA global vector.
825 * Physical rows reserve the three same-point components, fixed rows reserve
826 * only their diagonal, and periodic duplicate rows additionally reserve their
827 * wrapped representative. DMDA AO, local mapping, and stencil metadata are
828 * retained so the existing insertion paths keep their exact ordering.
829 */
831 UserCtx *user, Mat *preconditioning_matrix)
832{
833 DMDALocalInfo info;
834 ISLocalToGlobalMapping local_to_global = NULL;
835 Mat matrix = NULL;
836 MPI_Comm comm;
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;
841
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;
860
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);
871
872 if (type == MOM_NK_ROW_PHYSICAL) {
873 column_count = 3;
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
878 };
879 }
880 } else {
881 column_count = 1;
882 column_stencils[0] = row_stencil;
883 if (type == MOM_NK_ROW_PERIODIC_DUPLICATE) {
884 column_stencils[column_count++] = (MatStencil){
885 .i = ri, .j = rj, .k = rk, .c = component
886 };
887 }
888 }
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;
894 ++column_index) {
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,
904 PETSC_ERROR_INITIAL,
905 "Point-block column lies outside the DMDA ghost stencil.");
906 goto cleanup;
907 }
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])));
912 }
913 ierr = ISLocalToGlobalMappingApply(local_to_global, 1,
914 &row_local, &row);
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.");
923 goto cleanup;
924 }
925 for (PetscInt column_index = 0; column_index < column_count;
926 ++column_index) {
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.");
934 goto cleanup;
935 }
936 if (duplicate) continue;
937 if (columns[column_index] >= ownership_start &&
938 columns[column_index] < ownership_end)
939 ++diagonal_nnz[row - ownership_start];
940 else
941 ++offdiagonal_nnz[row - ownership_start];
942 }
943 }
944 }
945 }
946 }
947
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;
960
961 *preconditioning_matrix = matrix;
962 matrix = NULL;
963
964cleanup:
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);
969}
970
971#undef __FUNCT__
972#define __FUNCT__ "MomentumNewtonKrylov_ApplyConstraints"
973/**
974 * @brief Replaces every non-independent residual row with an explicit equation.
975 *
976 * Conditioned face-normal rows use F=X-Uconditioned, unconditioned legacy
977 * dummy/tangential rows use F=X, and periodic duplicates use Fdup=Xdup-Xrep.
978 * These equations prevent the zero Jacobian rows produced by simply retaining
979 * EnforceRHSBoundaryConditions() zeros in a matrix-free Newton operator. Immersed,
980 * masked, TwoD, and interface rows are rejected before this callback is installed.
981 * @param ctx Active solve context.
982 * @param X Unconditioned PETSc trial state.
983 * @param F Residual vector to update in place.
984 * @return PetscErrorCode 0 on success.
985 */
987 Vec X, Vec F)
988{
989 UserCtx *user = ctx->user;
990 DMDALocalInfo info = user->info;
991 Vec local_x = NULL;
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;
996
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));
1005
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;
1012
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;
1018
1019 if (row == MOM_NK_ROW_FIXED_CONDITIONED) fv[component] = xv[component] - cv[component];
1020 else if (row == MOM_NK_ROW_FIXED_HOMOGENEOUS) fv[component] = xv[component];
1021 else if (row == MOM_NK_ROW_PERIODIC_DUPLICATE) fv[component] = xv[component] - rv[component];
1022 }
1023 }
1024 }
1025 }
1026
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);
1033}
1034
1035#undef __FUNCT__
1036#define __FUNCT__ "MomentumNewtonKrylov_FormResidual"
1037/**
1038 * @brief Adapts a PETSc trial vector to the existing momentum residual path.
1039 *
1040 * A matrix-free SNES residual must be a deterministic function of the trial
1041 * vector X alone: F(X) may not depend on any state left by a previous residual
1042 * or MFFD evaluation, or finite-difference Jacobian actions become inconsistent.
1043 * To honor that contract this callback fully derives the Cartesian velocity
1044 * state (Ucat/lUcat) from X before the first boundary sweep -- see the inline
1045 * comment below for why ApplyBoundaryConditions()'s own internal reconstruction
1046 * is not sufficient for the first outlet pass.
1047 *
1048 * State invariants:
1049 * - On entry, X is the only input that determines the result; user->Ucont,
1050 * user->Ucat and their local ghosts are treated as scratch and are fully
1051 * overwritten from X.
1052 * - Supported handlers may overwrite flux totals and other diagnostics on
1053 * every call, but those values must not affect a later call at the same X;
1054 * the deterministic seed guarantees this.
1055 * - No histories, pressure, viscosity, or controller state advance here.
1056 *
1057 * Side effects: overwrites user->Ucont/lUcont, user->Ucat/lUcat, user->Rhs, the
1058 * boundary Ubcs targets, and boundary flux/area diagnostics; writes F.
1059 *
1060 * @param snes Calling nonlinear solver.
1061 * @param X Trial solution (read-only).
1062 * @param F Residual output.
1063 * @param vctx Pointer to MomentumNewtonKrylovContext.
1064 * @return PetscErrorCode 0 on success.
1065 */
1066static PetscErrorCode MomentumNewtonKrylov_FormResidual(SNES snes, Vec X, Vec F, void *vctx)
1067{
1069 UserCtx *user = ctx->user;
1070 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
1071 const FieldId cell_fields[] = {FIELD_ID_UCAT};
1072
1073 PetscFunctionBeginUser;
1074 (void)snes;
1075 PetscCall(VecCopy(X, user->Ucont));
1076 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, staggered_fields));
1077
1078 /* Deterministic pre-boundary Cartesian seed. Establish the full Ucat/lUcat
1079 * state from the current X before any boundary handler runs:
1080 *
1081 * X -> Ucont/lUcont -> Ucat -> periodic Ucat -> lUcat -> boundaries
1082 *
1083 * Why this is required, and why it is NOT redundant with the reconstruction
1084 * already performed inside ApplyBoundaryConditions():
1085 *
1086 * 1. A matrix-free SNES residual must be a deterministic function of X. If
1087 * the Cartesian state is left over from a previous residual/MFFD call,
1088 * F(X) becomes history dependent and the finite-difference Jacobian
1089 * action Jv = (F(X+hv)-F(X))/h is invalidated.
1090 * 2. The conservation-outlet handler reads lUcat during the FIRST boundary
1091 * sweep (it measures the uncorrected outflow and builds the outlet
1092 * profile from the Cartesian field). Without this seed it would read the
1093 * stale lUcat from the preceding evaluation.
1094 * 3. ApplyBoundaryConditions() does reconstruct Ucat/lUcat, but only AFTER
1095 * each handler sweep (Contra2Cart runs after BoundarySystem_ExecuteStep
1096 * within every pass). Those internal updates therefore prepare passes 2
1097 * and 3 -- they cannot prepare the very first outlet read of pass 1.
1098 * 4. Contra2Cart() rebuilds the global Ucat interior from the current
1099 * lUcont, but it does not by itself refresh lUcat (nor lUcont; that was
1100 * done by the SynchronizePeriodicStaggeredFields call above).
1101 * 5. SynchronizePeriodicCellFields(FIELD_ID_UCAT) must run before the ghost
1102 * scatter so periodic duplicate planes are finalized consistently (it is
1103 * a no-op when no direction is periodic, as on the straight duct).
1104 * 6. UpdateLocalGhosts(FIELD_ID_UCAT) is required because the outlet handler reads
1105 * lUcat -- the local ghosted vector -- not merely the global Ucat.
1106 *
1107 * Do NOT "simplify" this to a bare Contra2Cart(user), and do NOT delete it
1108 * as apparently redundant with ApplyBoundaryConditions(): the three internal
1109 * boundary passes remain necessary (they refresh the Cartesian state after
1110 * each boundary correction), but only this sequence makes pass 1's input a
1111 * deterministic function of X. */
1112 PetscCall(Contra2Cart(user));
1113 PetscCall(SynchronizePeriodicCellFields(user, 1, cell_fields));
1114 PetscCall(UpdateLocalGhosts(user, FIELD_ID_UCAT));
1115
1116 PetscCall(ApplyBoundaryConditions(user));
1117 PetscCall(ComputeTotalResidual(user));
1118 PetscCall(VecCopy(user->Rhs, F));
1119 PetscCall(VecScale(F, -1.0));
1120 PetscCall(MomentumNewtonKrylov_ApplyConstraints(ctx, X, F));
1121 PetscFunctionReturn(PETSC_SUCCESS);
1122}
1123
1124#undef __FUNCT__
1125#define __FUNCT__ "MomentumSolver_NewtonKrylov"
1126/*
1127 * Runs one per-call matrix-free Newton--Krylov momentum solve. The public
1128 * header owns the rendered API contract; this definition retains the detailed
1129 * lifecycle and rollback behavior below.
1130 */
1131PetscErrorCode MomentumSolver_NewtonKrylov(UserCtx *user, IBMNodes *ibm, FSInfo *fsi)
1132{
1133 PetscErrorCode ierr = PETSC_SUCCESS, cleanup_ierr;
1134 SimCtx *simCtx;
1135 SNES snes = NULL;
1136 Vec solution = NULL, entry_backup = NULL;
1137 KSP ksp = NULL;
1138 PC pc = NULL;
1139 MomentumPreconditionerDescription preconditioner_description = {0};
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;
1148 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
1149
1150 PetscFunctionBeginUser;
1151 PetscCall(MomentumNewtonKrylov_Validate(user));
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.");
1156 simCtx = user->simCtx;
1157
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;
1163
1164 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); if (ierr) goto cleanup;
1165 ierr = ApplyBoundaryConditions(user); 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;
1169
1170 ctx.user = user;
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;
1176 ierr = SNESSetFunction(snes, NULL, MomentumNewtonKrylov_FormResidual, &ctx); if (ierr) goto cleanup;
1177 ierr = MomentumNewtonJacobian_Create(snes, &ctx.jacobian); if (ierr) goto cleanup;
1179 &preconditioner_description,
1180 &ctx.preconditioning_engine); if (ierr) goto cleanup;
1181 ierr = MomentumNewtonJacobian_Register(snes, &ctx.jacobian,
1182 &ctx.preconditioning_engine, &ctx); if (ierr) goto cleanup;
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;
1186 ierr = MomentumPreconditionerEngine_ConfigurePetscPC(&ctx.preconditioning_engine, pc); if (ierr) goto cleanup;
1187 ierr = SNESSetFromOptions(snes); if (ierr) goto cleanup;
1188 ierr = SNESMonitorSet(snes, MomentumNewtonKrylov_Monitor, &ctx, NULL); if (ierr) goto cleanup;
1189 ierr = MomentumPreconditionerEngine_ValidatePetscPC(&ctx.preconditioning_engine, pc); if (ierr) goto cleanup;
1190
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",
1198
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;
1208
1209 if (reason > 0) {
1210 ierr = VecCopy(solution, user->Ucont); if (ierr) goto cleanup;
1211 simCtx->mom_last_converged = PETSC_TRUE;
1212 } else {
1213 ierr = VecCopy(entry_backup, user->Ucont); if (ierr) goto cleanup;
1214 simCtx->mom_last_converged = PETSC_FALSE;
1215 }
1216 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); if (ierr) goto cleanup;
1217 ierr = ApplyBoundaryConditions(user); if (ierr) goto cleanup;
1218 restore_entry = PETSC_FALSE;
1219 committed = (PetscBool)(reason > 0);
1220
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;
1226
1227cleanup:
1228 /* A PETSc solve error can bypass the normal statistics path. Query whatever
1229 SNES retained without replacing the primary error so a failed attempt is
1230 still represented in the structured log. */
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);
1237 }
1238 if (restore_entry && entry_backup) {
1239 cleanup_ierr = VecCopy(entry_backup, user->Ucont);
1240 if (!ierr) ierr = cleanup_ierr;
1241 cleanup_ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields);
1242 if (!ierr) ierr = cleanup_ierr;
1243 cleanup_ierr = ApplyBoundaryConditions(user);
1244 if (!ierr) ierr = cleanup_ierr;
1245 simCtx->mom_last_converged = PETSC_FALSE;
1246 committed = PETSC_FALSE;
1247 }
1248 if (ctx.history_file) {
1249 (void)fclose(ctx.history_file);
1250 ctx.history_file = NULL;
1251 }
1252 if (solve_started) {
1253 MomentumNewtonKrylov_WriteSummary(&ctx, reason, nonlinear_its, function_evals,
1254 linear_its, final_norm, committed);
1255 }
1256 if (rhs_created) {
1257 cleanup_ierr = VecDestroy(&user->Rhs);
1258 if (!ierr) ierr = cleanup_ierr;
1259 }
1260 cleanup_ierr = VecDestroy(&entry_backup); if (!ierr) ierr = cleanup_ierr;
1261 cleanup_ierr = VecDestroy(&solution); if (!ierr) ierr = cleanup_ierr;
1262 cleanup_ierr = MomentumPreconditionerEngine_Destroy(&ctx.preconditioning_engine); if (!ierr) ierr = cleanup_ierr;
1263 cleanup_ierr = MomentumNewtonJacobian_Destroy(&ctx.jacobian); if (!ierr) ierr = cleanup_ierr;
1264 cleanup_ierr = SNESDestroy(&snes); if (!ierr) ierr = cleanup_ierr;
1265 PetscFunctionReturn(ierr);
1266}
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.
@ FIELD_ID_UCAT
@ FIELD_ID_UCONT
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:45
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:199
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:83
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:30
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:29
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
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.
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.
static void MomentumNewtonKrylov_OpenHistory(MomentumNewtonKrylovContext *ctx)
Opens the optional rank-zero Newton iteration-history file.
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
static PetscErrorCode MomentumNewtonJacobian_Update(SNES snes, Vec current_solution, MomentumNewtonJacobian *jacobian)
Updates the matrix-free finite-difference operator base.
MomentumNewtonKrylovRowType
@ MOM_NK_ROW_PERIODIC_DUPLICATE
@ MOM_NK_ROW_PHYSICAL
@ 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
@ MOM_NK_PC_MODEL_NONE
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...
Definition setup.c:2564
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:1753
PetscErrorCode(* Describe)(UserCtx *, MomentumPreconditionerDescription *)
PetscErrorCode(* AssembleInterior)(UserCtx *, Vec, Mat)
PetscBool mom_nk_monitor_history
Definition variables.h:740
PetscInt clark
Definition variables.h:790
PetscInt movefsi
Definition variables.h:714
@ INLET
Definition variables.h:288
@ OUTLET
Definition variables.h:287
@ PERIODIC
Definition variables.h:290
@ WALL
Definition variables.h:284
PetscBool continueMode
Definition variables.h:701
PetscInt moveframe
Definition variables.h:715
PetscInt TwoD
Definition variables.h:715
Vec Rhs
Definition variables.h:912
PetscMPIInt rank
Definition variables.h:687
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:896
PetscInt block_number
Definition variables.h:768
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:879
PetscInt rans
Definition variables.h:789
Vec lZet
Definition variables.h:927
PetscBool mom_last_converged
Definition variables.h:738
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:314
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:307
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:306
@ BC_HANDLER_INLET_PROFILE_FROM_FILE
Definition variables.h:308
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:303
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:312
PetscReal ren
Definition variables.h:732
BCHandlerType handler_type
Definition variables.h:367
PetscInt _this
Definition variables.h:889
PetscReal dt
Definition variables.h:699
Vec Ucont
Definition variables.h:904
PetscInt StartStep
Definition variables.h:694
PetscInt rotatefsi
Definition variables.h:714
PetscScalar x
Definition variables.h:101
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:709
Vec lCsi
Definition variables.h:927
PetscScalar z
Definition variables.h:101
PetscInt wallfunction
Definition variables.h:790
Vec lUcont
Definition variables.h:904
PetscInt step
Definition variables.h:692
Vec lAj
Definition variables.h:927
DMDALocalInfo info
Definition variables.h:883
PetscScalar y
Definition variables.h:101
Vec lEta
Definition variables.h:927
Vec Nvert
Definition variables.h:904
BCType mathematical_type
Definition variables.h:366
PetscInt rotateframe
Definition variables.h:715
PetscInt immersed
Definition variables.h:714
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:259
@ BC_FACE_NEG_X
Definition variables.h:260
@ BC_FACE_POS_Z
Definition variables.h:262
@ BC_FACE_POS_Y
Definition variables.h:261
@ BC_FACE_NEG_Z
Definition variables.h:262
@ BC_FACE_POS_X
Definition variables.h:260
@ BC_FACE_NEG_Y
Definition variables.h:261
Holds the complete configuration for one of the six boundary faces.
Definition variables.h:364
A 3D point or vector with PetscScalar components.
Definition variables.h:100
Holds all data related to the state and motion of a body in FSI.
Definition variables.h:475
Represents a collection of nodes forming a surface for the IBM.
Definition variables.h:402
The master context for the entire simulation.
Definition variables.h:684
User-defined context containing data specific to a single computational grid level.
Definition variables.h:876