PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Data Structures | Typedefs | Enumerations | Functions | Variables
momentum_newton_krylov.c File Reference
#include "momentumsolvers.h"
Include dependency graph for momentum_newton_krylov.c:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  MomentumNewtonJacobian
 
struct  MomentumPreconditionerDescription
 
struct  MomentumPreconditionerModelOps
 
struct  MomentumPreconditionerEngine
 
struct  MomentumNewtonKrylovContext
 

Typedefs

typedef struct MomentumNewtonKrylovContext MomentumNewtonKrylovContext
 

Enumerations

enum  MomentumNewtonJacobianType { MOM_NK_JACOBIAN_FINITE_DIFFERENCE }
 
enum  MomentumNewtonFiniteDifferenceMode { MOM_NK_FD_MODE_MATRIX_FREE }
 
enum  MomentumPreconditionerModel { MOM_NK_PC_MODEL_NONE , MOM_NK_PC_MODEL_FROZEN_MOMENTUM_JACOBIAN }
 
enum  MomentumPreconditionerStructure { MOM_NK_PC_STRUCTURE_NONE , MOM_NK_PC_STRUCTURE_POINT_BLOCK }
 

Functions

static PetscErrorCode MomentumNewtonKrylov_Validate (UserCtx *user)
 Rejects configurations outside the audited version-one feature set.
 
static PetscErrorCode MomentumNewtonKrylov_FormResidual (SNES snes, Vec X, Vec F, void *vctx)
 Adapts a PETSc trial vector to the existing momentum residual path.
 
static PetscErrorCode MomentumNewtonKrylov_Monitor (SNES snes, PetscInt iteration, PetscReal norm, void *vctx)
 Captures SNES iteration norms and optionally writes PICurv history rows.
 
static PetscErrorCode MomentumNewtonKrylov_LinearMonitor (KSP ksp, PetscInt iteration, PetscReal norm, void *vctx)
 Writes the effective KSP tolerance and PETSc-reported norm for each inner iteration.
 
static void MomentumNewtonKrylov_OpenHistory (MomentumNewtonKrylovContext *ctx)
 Opens the optional rank-zero Newton iteration-history files (per Newton and per Krylov iteration).
 
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.
 
static PetscErrorCode MomentumNewtonKrylov_ApplyConstraints (MomentumNewtonKrylovContext *ctx, Vec X, Vec F)
 Replaces every non-independent residual row with an explicit equation.
 
static PetscErrorCode MomentumNewtonKrylov_ReadLinearizationConfig (MomentumNewtonJacobian *jacobian, MomentumPreconditionerDescription *description)
 Reads application-owned Jacobian and preconditioner mathematics.
 
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.
 
static PetscErrorCode FrozenMomentumJacobian_AssemblePointBlocks (UserCtx *user, Vec current_solution, Mat preconditioning_matrix)
 Inserts only the audited interior frozen-momentum point blocks.
 
static PetscErrorCode MomentumPreconditionerEngine_CreateExactPointBlockMatrix (UserCtx *user, Mat *preconditioning_matrix)
 Creates the frozen point-block P matrix with its exact scalar pattern.
 
static FILE * MomentumNewtonKrylov_OpenCsv (const MomentumNewtonKrylovContext *ctx, const char *kind, const char *header)
 Opens one of the block's Newton CSV records on rank 0, or returns NULL.
 
static MomentumRowType MomentumNewtonKrylov_ClassifyRow (UserCtx *user, const PetscReal ***nvert, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscInt *ri, PetscInt *rj, PetscInt *rk)
 Row classification with solid-cell masking folded in.
 
static PetscReal FrozenMomentumJacobian_MetricNormSquared (Cmpnts metric)
 Returns the squared Euclidean norm of one metric vector.
 
static PetscReal FrozenMomentumJacobian_FaceEddyViscosity (const UserCtx *user, const PetscReal ***nu_t, PetscInt axis, PetscInt i, PetscInt j, PetscInt k)
 Returns the face-averaged eddy viscosity the residual uses on one face.
 
static void FrozenMomentumJacobian_PointBlock (const UserCtx *user, const Cmpnts ***ucont, const Cmpnts ***csi, const Cmpnts ***eta, const Cmpnts ***zet, const PetscReal ***aj, const PetscReal ***nu_t, PetscInt i, PetscInt j, PetscInt k, PetscScalar block[9])
 Returns the frozen-momentum point block for the current residual convention.
 
static PetscErrorCode FrozenMomentumJacobian_DescribePointBlock (UserCtx *user, MomentumPreconditionerDescription *description)
 Describes the audited frozen-coefficient point-block model.
 
static PetscErrorCode MomentumPreconditionerEngine_ApplyConstraintRows (UserCtx *user, Mat preconditioning_matrix)
 Inserts all common fixed, homogeneous, and periodic-duplicate rows.
 
static PetscErrorCode MomentumNewtonJacobian_Create (SNES snes, MomentumNewtonJacobian *jacobian)
 Creates the selected Jacobian operator; currently PETSc MFFD only.
 
static PetscErrorCode MomentumNewtonJacobian_Update (SNES snes, Vec current_solution, MomentumNewtonJacobian *jacobian)
 Updates the matrix-free finite-difference operator base.
 
static PetscErrorCode MomentumNewtonJacobian_Register (SNES snes, MomentumNewtonJacobian *jacobian, MomentumPreconditionerEngine *engine, MomentumNewtonKrylovContext *ctx)
 Registers the application orchestration callback and both SNES matrices.
 
static PetscErrorCode MomentumNewtonJacobian_Destroy (MomentumNewtonJacobian *jacobian)
 Destroys a partially or fully created Jacobian operator.
 
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 PetscErrorCode MomentumPreconditionerEngine_Assemble (MomentumPreconditionerEngine *engine, UserCtx *user, Vec current_solution)
 Runs model insertion, common row handling, and final assembly.
 
static PetscErrorCode MomentumPreconditionerEngine_ConfigurePetscPC (MomentumPreconditionerEngine *engine, PC pc)
 Applies the validated model/structure-to-PETSc-PC mapping.
 
static PetscErrorCode MomentumPreconditionerEngine_ValidatePetscPC (MomentumPreconditionerEngine *engine, PC pc)
 Rejects raw options that select an unvalidated PETSc PC backend.
 
static PetscErrorCode MomentumPreconditionerEngine_Destroy (MomentumPreconditionerEngine *engine)
 Destroys only a separately owned preconditioning matrix.
 
static PetscErrorCode MomentumNewtonKrylov_Build (UserCtx *user)
 Builds the solve context MomentumSolver_NewtonKrylov() keeps for the run.
 
PetscErrorCode MomentumSolver_NewtonKrylov_Destroy (UserCtx *user)
 Implementation of MomentumSolver_NewtonKrylov_Destroy().
 
static PetscErrorCode MomentumNewtonKrylov_AssessIterate (MomentumNewtonKrylovContext *ctx, Vec solution, PetscReal *residual, PetscReal *residual_2, PetscReal *reference, PetscBool *satisfied)
 Measure the residual at the iterate a failed SNES solve holds, for the failure policy in MomentumSolver_NewtonKrylov().
 
PetscErrorCode MomentumSolver_NewtonKrylov (UserCtx *user, IBMNodes *ibm, FSInfo *fsi)
 Solves one physical momentum step with matrix-free Newton–Krylov.
 

Variables

static const MomentumPreconditionerModelOps frozen_momentum_point_block_ops
 

Data Structure Documentation

◆ MomentumNewtonJacobian

struct MomentumNewtonJacobian

Definition at line 11 of file momentum_newton_krylov.c.

Data Fields
MomentumNewtonJacobianType type
MomentumNewtonFiniteDifferenceMode finite_difference_mode
Mat jacobian_operator

◆ MomentumPreconditionerDescription

struct MomentumPreconditionerDescription

Definition at line 27 of file momentum_newton_krylov.c.

Data Fields
MomentumPreconditionerModel model
MomentumPreconditionerStructure structure
PetscInt block_size
PetscInt stencil_width

◆ MomentumPreconditionerEngine

struct MomentumPreconditionerEngine

Definition at line 39 of file momentum_newton_krylov.c.

Collaboration diagram for MomentumPreconditionerEngine:
[legend]
Data Fields
MomentumPreconditionerDescription description
const MomentumPreconditionerModelOps * model_ops
Mat preconditioning_matrix
PetscBool aliases_jacobian_operator
PetscBool owns_preconditioning_matrix
const char * petsc_pc_type

◆ MomentumNewtonKrylovContext

struct MomentumNewtonKrylovContext

Definition at line 48 of file momentum_newton_krylov.c.

Collaboration diagram for MomentumNewtonKrylovContext:
[legend]
Data Fields
UserCtx * user
SNES snes
FILE * history_file
FILE * linear_history_file
PetscBool have_initial_norm
PetscReal initial_norm
MomentumNewtonJacobian jacobian
MomentumPreconditionerEngine preconditioning_engine
Vec solution
Vec entry_backup

Typedef Documentation

◆ MomentumNewtonKrylovContext

Enumeration Type Documentation

◆ MomentumNewtonJacobianType

Enumerator
MOM_NK_JACOBIAN_FINITE_DIFFERENCE 

Definition at line 3 of file momentum_newton_krylov.c.

◆ MomentumNewtonFiniteDifferenceMode

Enumerator
MOM_NK_FD_MODE_MATRIX_FREE 

Definition at line 7 of file momentum_newton_krylov.c.

◆ MomentumPreconditionerModel

Enumerator
MOM_NK_PC_MODEL_NONE 
MOM_NK_PC_MODEL_FROZEN_MOMENTUM_JACOBIAN 

Definition at line 17 of file momentum_newton_krylov.c.

◆ MomentumPreconditionerStructure

Enumerator
MOM_NK_PC_STRUCTURE_NONE 
MOM_NK_PC_STRUCTURE_POINT_BLOCK 

Definition at line 22 of file momentum_newton_krylov.c.

Function Documentation

◆ MomentumNewtonKrylov_Validate()

static PetscErrorCode MomentumNewtonKrylov_Validate ( UserCtx *  user)
static

Rejects configurations outside the audited version-one feature set.

Parameters
userSingle-block momentum context to validate.
Returns
PetscErrorCode 0 when the configuration is supported.

Definition at line 225 of file momentum_newton_krylov.c.

226{
227 SimCtx *simCtx;
228 PetscReal mask_max = 0.0;
229 PetscInt velocity_dof = 0;
230
231 PetscFunctionBeginUser;
233 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
234 "Newton Krylov requires a non-NULL UserCtx.");
235 simCtx = user->simCtx;
236 PetscCheck(simCtx != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
237 "Newton Krylov requires UserCtx::simCtx.");
238 PetscCall(DMDAGetInfo(user->fda, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
239 &velocity_dof, NULL, NULL, NULL, NULL, NULL));
240 PetscCheck(velocity_dof == 3, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
241 "Newton Krylov requires a three-component velocity DMDA (got dof=%" PetscInt_FMT ").",
242 velocity_dof);
243 PetscCheck(simCtx->block_number == 1, PETSC_COMM_WORLD, PETSC_ERR_SUP,
244 "Newton Krylov version one supports exactly one block (got %" PetscInt_FMT ").",
245 simCtx->block_number);
246 PetscCheck(!simCtx->immersed, PETSC_COMM_WORLD, PETSC_ERR_SUP,
247 "Newton Krylov version one does not support immersed boundaries.");
248 PetscCheck(!simCtx->movefsi && !simCtx->rotatefsi, PETSC_COMM_WORLD, PETSC_ERR_SUP,
249 "Newton Krylov version one does not support moving or rotating bodies/FSI.");
250 PetscCheck(!simCtx->moveframe && !simCtx->rotateframe, PETSC_COMM_WORLD, PETSC_ERR_SUP,
251 "Newton Krylov version one does not support moving or rotating reference frames.");
252 for (PetscInt face = 0; face < 6; ++face) {
253 const BoundaryFaceConfig *cfg = &user->boundary_faces[face];
254 PetscBool supported = PETSC_FALSE;
255
256 switch (cfg->handler_type) {
259 /* As on an inlet, its velocity is refreshed once per step in PreStep. */
261 supported = (PetscBool)(cfg->mathematical_type == WALL);
262 break;
263 /* The inlet's velocity is refreshed once per step in PreStep, so every
264 * residual evaluation within the step sees the same boundary value. */
266 supported = (PetscBool)(cfg->mathematical_type == INLET);
267 break;
269 supported = (PetscBool)(cfg->mathematical_type == OUTLET);
270 break;
272 /* Both driven handlers freeze their correction once per timestep in
273 * PreStep, which the Newton solve never re-enters, so the momentum
274 * source stays constant across every residual evaluation. The paired
275 * face checks below still apply. */
278 supported = (PetscBool)(cfg->mathematical_type == PERIODIC);
279 break;
280 default:
281 supported = PETSC_FALSE;
282 break;
283 }
284 PetscCheck(supported, PETSC_COMM_WORLD, PETSC_ERR_SUP,
285 "Newton Krylov version one does not support boundary face %" PetscInt_FMT " with mathematical type %" PetscInt_FMT " and handler %" PetscInt_FMT ".",
286 face, (PetscInt)cfg->mathematical_type, (PetscInt)cfg->handler_type);
287 }
288
291 PETSC_COMM_WORLD, PETSC_ERR_SUP, "Newton Krylov requires paired x-periodic faces.");
294 PETSC_COMM_WORLD, PETSC_ERR_SUP, "Newton Krylov requires paired y-periodic faces.");
297 PETSC_COMM_WORLD, PETSC_ERR_SUP, "Newton Krylov requires paired z-periodic faces.");
298
299 PetscCheck(user->Nvert != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
300 "Newton Krylov requires the cell-mask vector Nvert.");
301 /* Solid cells no longer disqualify the solver: MomentumRowIsSolidMasked() gives
302 their rows the same constrained treatment as any other row carrying no unknown.
303 The immersed-boundary method itself remains unsupported above, because its
304 velocity reconstruction does not run inside this solver's residual. */
305 PetscCall(VecMax(user->Nvert, NULL, &mask_max));
306 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Newton Krylov solid-cell mask maximum Nvert=%g.\n",
307 (double)mask_max);
309 PetscFunctionReturn(PETSC_SUCCESS);
310}
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:214
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:1029
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled function: an always-on aggregate timer.
Definition logging.h:1000
PetscInt movefsi
Definition variables.h:950
@ INLET
Definition variables.h:323
@ OUTLET
Definition variables.h:322
@ PERIODIC
Definition variables.h:325
@ WALL
Definition variables.h:319
PetscInt moveframe
Definition variables.h:951
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:1182
PetscInt block_number
Definition variables.h:1009
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1160
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:352
@ BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX
Definition variables.h:355
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:354
@ BC_HANDLER_WALL_MOVING
Definition variables.h:346
@ BC_HANDLER_INLET_PRESCRIBED_VELOCITY
Definition variables.h:348
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:345
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:350
@ BC_HANDLER_WALL_FREE_SLIP
Definition variables.h:347
BCHandlerType handler_type
Definition variables.h:427
PetscInt rotatefsi
Refused at setup: immersed boundaries and moving bodies are not implemented.
Definition variables.h:950
Vec Nvert
Definition variables.h:1197
BCType mathematical_type
Definition variables.h:426
PetscInt rotateframe
moveframe/rotateframe are refused at setup.
Definition variables.h:951
PetscInt immersed
Definition variables.h:950
@ BC_FACE_NEG_X
Definition variables.h:309
@ BC_FACE_POS_Z
Definition variables.h:311
@ BC_FACE_POS_Y
Definition variables.h:310
@ BC_FACE_NEG_Z
Definition variables.h:311
@ BC_FACE_POS_X
Definition variables.h:309
@ BC_FACE_NEG_Y
Definition variables.h:310
Holds the complete configuration for one of the six boundary faces.
Definition variables.h:424
The master context for the entire simulation.
Definition variables.h:916
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_FormResidual()

static PetscErrorCode MomentumNewtonKrylov_FormResidual ( SNES  snes,
Vec  X,
Vec  F,
void *  vctx 
)
static

Adapts a PETSc trial vector to the existing momentum residual path.

A matrix-free SNES residual must be a deterministic function of the trial vector X alone: F(X) may not depend on any state left by a previous residual or MFFD evaluation, or finite-difference Jacobian actions become inconsistent. To honor that contract this callback fully derives the Cartesian velocity state (Ucat/lUcat) from X before the first boundary sweep – see the inline comment below for why ApplyBoundaryConditions()'s own internal reconstruction is not sufficient for the first outlet pass.

State invariants:

  • On entry, X is the only input that determines the result; user->Ucont, user->Ucat and their local ghosts are treated as scratch and are fully overwritten from X.
  • Supported handlers may overwrite flux totals and other diagnostics on every call, but those values must not affect a later call at the same X; the deterministic seed guarantees this.
  • No histories, pressure, viscosity, or controller state advance here.

Side effects: overwrites user->Ucont/lUcont, user->Ucat/lUcat, user->Rhs, the boundary Ubcs targets, and boundary flux/area diagnostics; writes F.

Parameters
snesCalling nonlinear solver.
XTrial solution (read-only).
FResidual output.
vctxPointer to MomentumNewtonKrylovContext.
Returns
PetscErrorCode 0 on success.

Definition at line 1135 of file momentum_newton_krylov.c.

1136{
1138 UserCtx *user = ctx->user;
1139 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
1140 const FieldId cell_fields[] = {FIELD_ID_UCAT};
1141
1142 PetscFunctionBeginUser;
1144 (void)snes;
1145 PetscCall(VecCopy(X, user->Ucont));
1146 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, staggered_fields));
1147
1148 /* Deterministic pre-boundary Cartesian seed. Establish the full Ucat/lUcat
1149 * state from the current X before any boundary handler runs:
1150 *
1151 * X -> Ucont/lUcont -> Ucat -> periodic Ucat -> lUcat -> boundaries
1152 *
1153 * Why this is required, and why it is NOT redundant with the reconstruction
1154 * already performed inside ApplyBoundaryConditions():
1155 *
1156 * 1. A matrix-free SNES residual must be a deterministic function of X. If
1157 * the Cartesian state is left over from a previous residual/MFFD call,
1158 * F(X) becomes history dependent and the finite-difference Jacobian
1159 * action Jv = (F(X+hv)-F(X))/h is invalidated.
1160 * 2. The conservation-outlet handler reads lUcat during the FIRST boundary
1161 * sweep (it measures the uncorrected outflow and builds the outlet
1162 * profile from the Cartesian field). Without this seed it would read the
1163 * stale lUcat from the preceding evaluation.
1164 * 3. ApplyBoundaryConditions() does reconstruct Ucat/lUcat, but only AFTER
1165 * each handler sweep (Contra2Cart runs after BoundarySystem_ExecuteStep
1166 * within every pass). Those internal updates therefore prepare passes 2
1167 * and 3 -- they cannot prepare the very first outlet read of pass 1.
1168 * 4. Contra2Cart() rebuilds the global Ucat interior from the current
1169 * lUcont, but it does not by itself refresh lUcat (nor lUcont; that was
1170 * done by the SynchronizePeriodicStaggeredFields call above).
1171 * 5. SynchronizePeriodicCellFields(FIELD_ID_UCAT) must run before the ghost
1172 * scatter so periodic duplicate planes are finalized consistently (it is
1173 * a no-op when no direction is periodic, as on the straight duct).
1174 * 6. UpdateLocalGhosts(FIELD_ID_UCAT) is required because the outlet handler reads
1175 * lUcat -- the local ghosted vector -- not merely the global Ucat.
1176 *
1177 * Do NOT "simplify" this to a bare Contra2Cart(user), and do NOT delete it
1178 * as apparently redundant with ApplyBoundaryConditions(): the three internal
1179 * boundary passes remain necessary (they refresh the Cartesian state after
1180 * each boundary correction), but only this sequence makes pass 1's input a
1181 * deterministic function of X. */
1182 PetscCall(Contra2Cart(user));
1183 PetscCall(SynchronizePeriodicCellFields(user, 1, cell_fields));
1184 PetscCall(UpdateLocalGhosts(user, FIELD_ID_UCAT));
1185
1186 PetscCall(ApplyBoundaryConditions(user));
1187 PetscCall(ComputeTotalResidual(user));
1188 PetscCall(VecCopy(user->Rhs, F));
1189 PetscCall(VecScale(F, -1.0));
1190 PetscCall(MomentumNewtonKrylov_ApplyConstraints(ctx, X, F));
1192 PetscFunctionReturn(PETSC_SUCCESS);
1193}
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 PROFILE_HELPER_BEGIN
Marks the beginning of a profiled helper: timed only when enabled by name.
Definition logging.h:1014
#define PROFILE_HELPER_END
Alias of PROFILE_FUNCTION_END for a function that began with PROFILE_HELPER_BEGIN.
Definition logging.h:1033
static PetscErrorCode MomentumNewtonKrylov_ApplyConstraints(MomentumNewtonKrylovContext *ctx, Vec X, Vec F)
Replaces every non-independent residual row with an explicit equation.
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:3313
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:2593
Vec Ucont
Definition variables.h:1197
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1157
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_Monitor()

static PetscErrorCode MomentumNewtonKrylov_Monitor ( SNES  snes,
PetscInt  iteration,
PetscReal  norm,
void *  vctx 
)
static

Captures SNES iteration norms and optionally writes PICurv history rows.

SNES supplies the already-computed norm, so this monitor never causes an additional nonlinear residual evaluation. PETSc monitors selected through -mom_nk_snes_monitor remain independent and may run alongside this callback.

Definition at line 95 of file momentum_newton_krylov.c.

97{
99 SimCtx *simCtx = ctx->user->simCtx;
100
101 (void)snes;
102 PetscFunctionBeginUser;
104 if (iteration == 0 && !ctx->have_initial_norm) {
105 ctx->initial_norm = norm;
106 ctx->have_initial_norm = PETSC_TRUE;
107 }
108 if (ctx->history_file) {
109 (void)fprintf(ctx->history_file, "%d,%d,%d,%.16e\n",
110 (int)simCtx->step, (int)ctx->user->_this, (int)iteration,
111 (double)norm);
112 (void)fflush(ctx->history_file);
113 }
115 PetscFunctionReturn(PETSC_SUCCESS);
116}
PetscInt _this
Definition variables.h:1175
PetscInt step
Definition variables.h:924
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_LinearMonitor()

static PetscErrorCode MomentumNewtonKrylov_LinearMonitor ( KSP  ksp,
PetscInt  iteration,
PetscReal  norm,
void *  vctx 
)
static

Writes the effective KSP tolerance and PETSc-reported norm for each inner iteration.

The tolerance is queried after SNES has applied any inexact-Newton forcing update, so the log distinguishes changing Eisenstat–Walker requests from changing linear convergence behavior.

Definition at line 124 of file momentum_newton_krylov.c.

126{
128 PetscInt newton_iteration = -1;
129 PetscReal relative_tolerance = 0.0;
130
131 PetscFunctionBeginUser;
133 if (!ctx->linear_history_file) { PROFILE_HELPER_END; PetscFunctionReturn(PETSC_SUCCESS); }
134 PetscCall(SNESGetIterationNumber(ctx->snes, &newton_iteration));
135 PetscCall(KSPGetTolerances(ksp, &relative_tolerance, NULL, NULL, NULL));
136 (void)fprintf(ctx->linear_history_file, "%d,%d,%d,%d,%.16e,%.16e\n",
137 (int)ctx->user->simCtx->step, (int)ctx->user->_this,
138 (int)newton_iteration, (int)iteration,
139 (double)relative_tolerance, (double)norm);
140 (void)fflush(ctx->linear_history_file);
142 PetscFunctionReturn(PETSC_SUCCESS);
143}
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_OpenHistory()

static void MomentumNewtonKrylov_OpenHistory ( MomentumNewtonKrylovContext *  ctx)
static

Opens the optional rank-zero Newton iteration-history files (per Newton and per Krylov iteration).

Definition at line 168 of file momentum_newton_krylov.c.

169{
171 SimCtx *simCtx = ctx->user->simCtx;
172
173 if (!simCtx->mom_nk_monitor_history || simCtx->rank != 0) { PROFILE_FUNCTION_END; return; }
174 ctx->history_file = MomentumNewtonKrylov_OpenCsv(ctx, "History", "step,block,newton,nonlinear_norm");
175 if (!ctx->history_file) { PROFILE_FUNCTION_END; return; }
176 ctx->linear_history_file = MomentumNewtonKrylov_OpenCsv(ctx, "Linear_History",
177 "step,block,newton,krylov,requested_rtol,reported_residual_norm");
179}
static FILE * MomentumNewtonKrylov_OpenCsv(const MomentumNewtonKrylovContext *ctx, const char *kind, const char *header)
Opens one of the block's Newton CSV records on rank 0, or returns NULL.
PetscBool mom_nk_monitor_history
Definition variables.h:975
PetscMPIInt rank
Definition variables.h:919
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_WriteSummary()

static void MomentumNewtonKrylov_WriteSummary ( const MomentumNewtonKrylovContext *  ctx,
SNESConvergedReason  reason,
PetscInt  nonlinear_its,
PetscInt  function_evals,
PetscInt  linear_its,
PetscReal  final_norm,
PetscBool  committed 
)
static

Appends one rank-zero structured Newton result for a physical step.

File failures are deliberately diagnostic-only: rollback and PETSc cleanup must retain their original error behavior.

Definition at line 186 of file momentum_newton_krylov.c.

193{
195 SimCtx *simCtx = ctx->user->simCtx;
196 const char *reason_name;
197 FILE *file;
198
199 if (simCtx->rank != 0) { PROFILE_FUNCTION_END; return; }
200 file = MomentumNewtonKrylov_OpenCsv(ctx, "Summary",
201 "step,block,solver,jacobian,preconditioner,reason,reason_code,newton,evals,krylov,"
202 "initial_norm,final_norm,state");
203 if (!file) { PROFILE_FUNCTION_END; return; }
204 reason_name = reason == SNES_CONVERGED_ITERATING
205 ? "SNES_CONVERGED_ITERATING" : SNESConvergedReasons[reason];
206 (void)fprintf(file, "%d,%d,Newton Krylov,finite_difference / matrix_free,%s,%s,%d,%d,%d,%d,",
207 (int)simCtx->step, (int)ctx->user->_this,
209 "none" : "frozen_momentum_jacobian / point_block",
210 reason_name, (int)reason,
211 (int)nonlinear_its, (int)function_evals, (int)linear_its);
212 if (ctx->have_initial_norm) (void)fprintf(file, "%.16e", (double)ctx->initial_norm);
213 (void)fprintf(file, ",%.16e,%s\n", (double)final_norm,
214 !committed ? "rolled_back" :
215 simCtx->mom_last_converged ? "committed" : "committed_unconverged");
216 (void)fclose(file);
218}
MomentumPreconditionerDescription description
MomentumPreconditionerEngine preconditioning_engine
PetscBool mom_last_converged
Definition variables.h:971
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_ApplyConstraints()

static PetscErrorCode MomentumNewtonKrylov_ApplyConstraints ( MomentumNewtonKrylovContext *  ctx,
Vec  X,
Vec  F 
)
static

Replaces every non-independent residual row with an explicit equation.

Which rows are non-independent is decided by ClassifyMomentumRow(), shared with the residual path; only the action taken differs. Conditioned face-normal rows use F=X-Uconditioned, unconditioned legacy dummy/tangential rows use F=X, and periodic duplicates use Fdup=Xdup-Xrep. These equations prevent the zero Jacobian rows that EnforceRHSBoundaryConditions()'s zeroing would produce in a matrix-free Newton operator. Immersed and interface rows are rejected before this callback is installed. A TwoD-masked component needs no constraint: ComputeRHS() zeroes its spatial terms, so its row is the time derivative alone, which holds a velocity that is constant in time exactly, as the Picard solver's skipped row does.

Parameters
ctxActive solve context.
XUnconditioned PETSc trial state.
FResidual vector to update in place.
Returns
PetscErrorCode 0 on success.

Definition at line 1055 of file momentum_newton_krylov.c.

1057{
1058 UserCtx *user = ctx->user;
1059 DMDALocalInfo info = user->info;
1060 Vec local_x = NULL;
1061 Cmpnts ***x = NULL, ***conditioned = NULL, ***f = NULL, ***lx = NULL;
1062 const PetscInt xs = info.xs, xe = info.xs + info.xm;
1063 const PetscInt ys = info.ys, ye = info.ys + info.ym;
1064 const PetscInt zs = info.zs, ze = info.zs + info.zm;
1065
1066 PetscFunctionBeginUser;
1068 PetscCall(DMGetLocalVector(user->fda, &local_x));
1069 PetscCall(DMGlobalToLocalBegin(user->fda, X, INSERT_VALUES, local_x));
1070 PetscCall(DMGlobalToLocalEnd(user->fda, X, INSERT_VALUES, local_x));
1071 PetscCall(DMDAVecGetArrayRead(user->fda, X, &x));
1072 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &conditioned));
1073 PetscCall(DMDAVecGetArray(user->fda, F, &f));
1074 PetscCall(DMDAVecGetArrayRead(user->fda, local_x, &lx));
1075
1076 for (PetscInt k = zs; k < ze; ++k) {
1077 for (PetscInt j = ys; j < ye; ++j) {
1078 for (PetscInt i = xs; i < xe; ++i) {
1079 PetscScalar *fv = &f[k][j][i].x;
1080 const PetscScalar *xv = &x[k][j][i].x;
1081 const PetscScalar *cv = &conditioned[k][j][i].x;
1082
1083 for (PetscInt component = 0; component < 3; ++component) {
1084 PetscInt ri, rj, rk;
1086 user, i, j, k, component, &ri, &rj, &rk);
1087 const PetscScalar *rv = &lx[rk][rj][ri].x;
1088
1089 if (row == MOM_ROW_FIXED_CONDITIONED) fv[component] = xv[component] - cv[component];
1090 else if (row == MOM_ROW_FIXED_HOMOGENEOUS) fv[component] = xv[component];
1091 else if (row == MOM_ROW_PERIODIC_DUPLICATE) fv[component] = xv[component] - rv[component];
1092 }
1093 }
1094 }
1095 }
1096
1097 PetscCall(DMDAVecRestoreArrayRead(user->fda, local_x, &lx));
1098 PetscCall(DMDAVecRestoreArray(user->fda, F, &f));
1099 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucont, &conditioned));
1100 PetscCall(DMDAVecRestoreArrayRead(user->fda, X, &x));
1101 PetscCall(DMRestoreLocalVector(user->fda, &local_x));
1103 PetscFunctionReturn(PETSC_SUCCESS);
1104}
MomentumRowType
Classification of one staggered momentum row (location + component).
Definition Boundaries.h:191
@ MOM_ROW_FIXED_HOMOGENEOUS
Dummy/tangential row carrying no unknown at all.
Definition Boundaries.h:194
@ MOM_ROW_PERIODIC_DUPLICATE
Duplicate of a wrapped representative row (see ri, rj, rk).
Definition Boundaries.h:195
@ MOM_ROW_FIXED_CONDITIONED
Strong Dirichlet row; the value comes from ApplyBoundaryConditions().
Definition Boundaries.h:193
MomentumRowType ClassifyMomentumRow(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscInt *ri, PetscInt *rj, PetscInt *rk)
Single source of truth for "which staggered momentum rows are unknowns".
Definition Boundaries.c:139
PetscScalar x
Definition variables.h:145
DMDALocalInfo info
Definition variables.h:1169
A 3D point or vector with PetscScalar components.
Definition variables.h:144
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_ReadLinearizationConfig()

static PetscErrorCode MomentumNewtonKrylov_ReadLinearizationConfig ( MomentumNewtonJacobian *  jacobian,
MomentumPreconditionerDescription *  description 
)
static

Reads application-owned Jacobian and preconditioner mathematics.

Definition at line 315 of file momentum_newton_krylov.c.

317{
318 char type[48] = "finite_difference";
319 char finite_difference_mode[32] = "matrix_free";
320 char model[48] = "none";
321 char structure[32] = "none";
322 PetscBool set = PETSC_FALSE, match = PETSC_FALSE;
323
324 PetscFunctionBeginUser;
326 PetscCheck(jacobian != NULL && description != NULL, PETSC_COMM_SELF,
327 PETSC_ERR_ARG_NULL, "Newton Krylov linearization configuration is NULL.");
328 PetscCall(PetscOptionsGetString(NULL, NULL, "-mom_nk_jacobian_type",
329 type, sizeof(type), &set));
330 PetscCall(PetscStrcasecmp(type, "finite_difference", &match));
331 PetscCheck(match, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
332 "-mom_nk_jacobian_type must be 'finite_difference' (got '%s').", type);
334 PetscCall(PetscOptionsGetString(NULL, NULL, "-mom_nk_jacobian_fd_mode",
335 finite_difference_mode,
336 sizeof(finite_difference_mode), &set));
337 PetscCall(PetscStrcasecmp(finite_difference_mode, "matrix_free", &match));
338 PetscCheck(match, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
339 "-mom_nk_jacobian_fd_mode must be 'matrix_free' (got '%s').",
340 finite_difference_mode);
342
343 PetscCall(PetscOptionsGetString(NULL, NULL, "-mom_nk_preconditioner_model",
344 model, sizeof(model), &set));
345 PetscCall(PetscStrcasecmp(model, "none", &match));
346 if (match) description->model = MOM_NK_PC_MODEL_NONE;
347 if (!match) {
348 PetscCall(PetscStrcasecmp(model, "frozen_momentum_jacobian", &match));
349 if (match) description->model = MOM_NK_PC_MODEL_FROZEN_MOMENTUM_JACOBIAN;
350 }
351 PetscCheck(match, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
352 "-mom_nk_preconditioner_model must be 'none' or "
353 "'frozen_momentum_jacobian' (got '%s').", model);
354 PetscCall(PetscOptionsGetString(NULL, NULL, "-mom_nk_preconditioner_structure",
355 structure, sizeof(structure), &set));
356 PetscCall(PetscStrcasecmp(structure, "none", &match));
357 if (match) description->structure = MOM_NK_PC_STRUCTURE_NONE;
358 if (!match) {
359 PetscCall(PetscStrcasecmp(structure, "point_block", &match));
360 if (match) description->structure = MOM_NK_PC_STRUCTURE_POINT_BLOCK;
361 }
362 PetscCheck(match, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
363 "-mom_nk_preconditioner_structure must be 'none' or 'point_block' "
364 "(got '%s').", structure);
365 PetscCheck((description->model == MOM_NK_PC_MODEL_NONE &&
366 description->structure == MOM_NK_PC_STRUCTURE_NONE) ||
369 PETSC_COMM_WORLD, PETSC_ERR_SUP,
370 "Unsupported Newton Krylov preconditioner model/structure combination: "
371 "model='%s', structure='%s'.", model, structure);
373 PetscFunctionReturn(PETSC_SUCCESS);
374}
MomentumNewtonFiniteDifferenceMode finite_difference_mode
MomentumNewtonJacobianType type
MomentumPreconditionerStructure structure
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_FormJacobian()

static PetscErrorCode MomentumNewtonKrylov_FormJacobian ( SNES  snes,
Vec  current_solution,
Mat  jacobian_operator,
Mat  preconditioning_matrix,
void *  vctx 
)
static

Updates the Jacobian and then assembles any separate preconditioning matrix.

Definition at line 868 of file momentum_newton_krylov.c.

870{
872 PetscFunctionBeginUser;
874 (void)jacobian_operator;
875 (void)preconditioning_matrix;
876 PetscCall(MomentumNewtonJacobian_Update(snes, current_solution, &ctx->jacobian));
878 &ctx->preconditioning_engine, ctx->user, current_solution));
880 PetscFunctionReturn(PETSC_SUCCESS);
881}
static PetscErrorCode MomentumPreconditionerEngine_Assemble(MomentumPreconditionerEngine *engine, UserCtx *user, Vec current_solution)
Runs model insertion, common row handling, and final assembly.
static PetscErrorCode MomentumNewtonJacobian_Update(SNES snes, Vec current_solution, MomentumNewtonJacobian *jacobian)
Updates the matrix-free finite-difference operator base.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FrozenMomentumJacobian_AssemblePointBlocks()

static PetscErrorCode FrozenMomentumJacobian_AssemblePointBlocks ( UserCtx *  user,
Vec  current_solution,
Mat  preconditioning_matrix 
)
static

Inserts only the audited interior frozen-momentum point blocks.

Definition at line 586 of file momentum_newton_krylov.c.

588{
589 DMDALocalInfo info = user->info;
590 Cmpnts ***ucont = NULL, ***csi = NULL, ***eta = NULL, ***zet = NULL;
591 PetscReal ***aj = NULL, ***nvert = NULL, ***nu_t = NULL;
592 SimCtx *simCtx = user->simCtx;
593 /* The eddy viscosity enters the preconditioner only when a turbulence model is
594 actually producing one; without it the field may not even be allocated. */
595 const PetscBool has_eddy_viscosity =
596 (PetscBool)(simCtx->les && user->lNu_t != NULL);
597 PetscErrorCode ierr = PETSC_SUCCESS, cleanup_ierr;
598
599 PetscFunctionBeginUser;
601 /*
602 * current_solution is the current SNES trial Ucont Vec: it is layout-compatible
603 * with user->fda/user->Ucont, but is not necessarily the canonical user->Ucont
604 * selected by UpdateLocalGhosts(FIELD_ID_UCONT). Scatter that trial Vec directly with
605 * user->fda so PETSc applies its MPI ownership, periodic topology, component
606 * ordering, and ghost mapping without canonical-field synchronization or mutation
607 * of current_solution. UpdateLocalGhosts additionally repairs the component-normal
608 * staggered buffers Uxi(i=-1,mx), Ueta(j=-1,my), and Uzeta(k=-1,mz); the audited
609 * point-block velocity stencil reads none of those planes, so the repair cannot
610 * change a coefficient. Revisit this choice and extend the periodic-localization
611 * tests if the model stencil is expanded to read any repaired normal buffer.
612 */
613 ierr = DMGlobalToLocalBegin(user->fda, current_solution, INSERT_VALUES, user->lUcont);
614 if (ierr) goto cleanup;
615 ierr = DMGlobalToLocalEnd(user->fda, current_solution, INSERT_VALUES, user->lUcont);
616 if (ierr) goto cleanup;
617 ierr = DMDAVecGetArrayRead(user->fda, user->lUcont, &ucont); if (ierr) goto cleanup;
618 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, &csi); if (ierr) goto cleanup;
619 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, &eta); if (ierr) goto cleanup;
620 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, &zet); if (ierr) goto cleanup;
621 ierr = DMDAVecGetArrayRead(user->da, user->lAj, &aj); if (ierr) goto cleanup;
622 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, &nvert); if (ierr) goto cleanup;
623 if (has_eddy_viscosity) {
624 ierr = DMDAVecGetArrayRead(user->da, user->lNu_t, &nu_t); if (ierr) goto cleanup;
625 }
626 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
627 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
628 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
629 for (PetscInt component = 0; component < 3; ++component) {
630 MatStencil row = {.i = i, .j = j, .k = k, .c = component};
631 PetscInt ri, rj, rk;
633 user, (const PetscReal ***)nvert, i, j, k, component, &ri, &rj, &rk);
634 if (type == MOM_ROW_PHYSICAL) {
635 PetscScalar block[9];
636 MatStencil cols[3] = {
637 {.i = i, .j = j, .k = k, .c = 0},
638 {.i = i, .j = j, .k = k, .c = 1},
639 {.i = i, .j = j, .k = k, .c = 2}
640 };
641 FrozenMomentumJacobian_PointBlock(user, (const Cmpnts ***)ucont,
642 (const Cmpnts ***)csi, (const Cmpnts ***)eta, (const Cmpnts ***)zet,
643 (const PetscReal ***)aj, (const PetscReal ***)nu_t, i, j, k, block);
644 /* A TwoD-masked row is the time derivative alone in the residual,
645 so its block row is that derivative's diagonal. */
646 if (simCtx->TwoD == component + 1) {
647 for (PetscInt c = 0; c < 3; ++c)
648 block[3 * component + c] = (c == component)
649 ? MomentumBDFCoefficient(simCtx) / simCtx->dt : 0.0;
650 }
651 ierr = MatSetValuesStencil(preconditioning_matrix, 1, &row, 3, cols,
652 &block[3 * component], INSERT_VALUES);
653 if (ierr) goto cleanup;
654 }
655 }
656 }
657 }
658 }
659cleanup:
660 if (nu_t) { cleanup_ierr = DMDAVecRestoreArrayRead(user->da, user->lNu_t, &nu_t); if (!ierr) ierr = cleanup_ierr; }
661 if (nvert) { cleanup_ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, &nvert); if (!ierr) ierr = cleanup_ierr; }
662 if (aj) { cleanup_ierr = DMDAVecRestoreArrayRead(user->da, user->lAj, &aj); if (!ierr) ierr = cleanup_ierr; }
663 if (zet) { cleanup_ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet); if (!ierr) ierr = cleanup_ierr; }
664 if (eta) { cleanup_ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta); if (!ierr) ierr = cleanup_ierr; }
665 if (csi) { cleanup_ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi); if (!ierr) ierr = cleanup_ierr; }
666 if (ucont) { cleanup_ierr = DMDAVecRestoreArrayRead(user->fda, user->lUcont, &ucont); if (!ierr) ierr = cleanup_ierr; }
668 PetscFunctionReturn(ierr);
669}
@ MOM_ROW_PHYSICAL
Independent unknown governed by the momentum equation.
Definition Boundaries.h:192
static void FrozenMomentumJacobian_PointBlock(const UserCtx *user, const Cmpnts ***ucont, const Cmpnts ***csi, const Cmpnts ***eta, const Cmpnts ***zet, const PetscReal ***aj, const PetscReal ***nu_t, PetscInt i, PetscInt j, PetscInt k, PetscScalar block[9])
Returns the frozen-momentum point block for the current residual convention.
static MomentumRowType MomentumNewtonKrylov_ClassifyRow(UserCtx *user, const PetscReal ***nvert, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscInt *ri, PetscInt *rj, PetscInt *rk)
Row classification with solid-cell masking folded in.
PetscReal MomentumBDFCoefficient(SimCtx *simCtx)
Returns the BDF physical-time coefficient a0 for the current step.
PetscInt TwoD
Definition variables.h:951
Vec lNvert
Definition variables.h:1197
PetscReal dt
Definition variables.h:933
Vec lNu_t
Definition variables.h:1259
Vec lUcont
Definition variables.h:1197
PetscInt les
Active LES closure; an LESModelType value.
Definition variables.h:1041
double nu_t(double yplus)
Computes turbulent eddy viscosity ratio (ν_t / ν)
Here is the call graph for this function:

◆ MomentumPreconditionerEngine_CreateExactPointBlockMatrix()

static PetscErrorCode MomentumPreconditionerEngine_CreateExactPointBlockMatrix ( UserCtx *  user,
Mat *  preconditioning_matrix 
)
static

Creates the frozen point-block P matrix with its exact scalar pattern.

Row and column ownership follows the velocity DMDA global vector. Physical rows reserve the three same-point components, fixed rows reserve only their diagonal, and periodic duplicate rows additionally reserve their wrapped representative. DMDA AO, local mapping, and stencil metadata are retained so the existing insertion paths keep their exact ordering.

Definition at line 891 of file momentum_newton_krylov.c.

893{
894 DMDALocalInfo info;
895 ISLocalToGlobalMapping local_to_global = NULL;
896 Mat matrix = NULL;
897 MPI_Comm comm;
898 PetscInt local_size, global_size, ownership_start, ownership_end;
899 PetscInt *diagonal_nnz = NULL, *offdiagonal_nnz = NULL;
900 PetscInt ghost_starts[4] = {0, 0, 0, 0}, ghost_sizes[3] = {0, 0, 0};
901 const PetscReal ***nvert = NULL;
902 PetscErrorCode ierr = PETSC_SUCCESS, cleanup_ierr;
903
904 PetscFunctionBeginUser;
906 PetscCheck(preconditioning_matrix != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
907 "Point-block matrix output is NULL.");
908 *preconditioning_matrix = NULL;
909 comm = PetscObjectComm((PetscObject)user->fda);
910 info = user->info;
911 PetscCall(VecGetLocalSize(user->Ucont, &local_size));
912 PetscCall(VecGetSize(user->Ucont, &global_size));
913 PetscCall(VecGetOwnershipRange(user->Ucont, &ownership_start, &ownership_end));
914 PetscCheck(local_size == ownership_end - ownership_start, comm, PETSC_ERR_PLIB,
915 "Velocity ownership range does not match its local size.");
916 ierr = PetscCalloc2(local_size, &diagonal_nnz,
917 local_size, &offdiagonal_nnz); if (ierr) goto cleanup;
918 ierr = DMGetLocalToGlobalMapping(user->fda, &local_to_global); if (ierr) goto cleanup;
919 ghost_starts[0] = info.gxs; ghost_starts[1] = info.gys; ghost_starts[2] = info.gzs;
920 ghost_sizes[0] = info.gxm; ghost_sizes[1] = info.gym; ghost_sizes[2] = info.gzm;
921 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, &nvert); if (ierr) goto cleanup;
922
923 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
924 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
925 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
926 for (PetscInt component = 0; component < 3; ++component) {
927 PetscInt ri, rj, rk, column_count;
928 MatStencil row_stencil = {.i = i, .j = j, .k = k, .c = component};
929 MatStencil column_stencils[3];
930 PetscInt row_local, row, column_locals[3], columns[3];
932 user, nvert, i, j, k, component, &ri, &rj, &rk);
933
934 if (type == MOM_ROW_PHYSICAL) {
935 column_count = 3;
936 for (PetscInt column_component = 0; column_component < 3;
937 ++column_component) {
938 column_stencils[column_component] = (MatStencil){
939 .i = i, .j = j, .k = k, .c = column_component
940 };
941 }
942 } else {
943 column_count = 1;
944 column_stencils[0] = row_stencil;
945 if (type == MOM_ROW_PERIODIC_DUPLICATE) {
946 column_stencils[column_count++] = (MatStencil){
947 .i = ri, .j = rj, .k = rk, .c = component
948 };
949 }
950 }
951 row_local = component + 3 * (
952 (i - ghost_starts[0]) + ghost_sizes[0] * (
953 (j - ghost_starts[1]) + ghost_sizes[1] *
954 (k - ghost_starts[2])));
955 for (PetscInt column_index = 0; column_index < column_count;
956 ++column_index) {
957 const MatStencil column = column_stencils[column_index];
958 if (!(column.i >= ghost_starts[0] &&
959 column.i < ghost_starts[0] + ghost_sizes[0] &&
960 column.j >= ghost_starts[1] &&
961 column.j < ghost_starts[1] + ghost_sizes[1] &&
962 column.k >= ghost_starts[2] &&
963 column.k < ghost_starts[2] + ghost_sizes[2])) {
964 ierr = PetscError(comm, __LINE__, PETSC_FUNCTION_NAME, __FILE__,
965 PETSC_ERR_ARG_OUTOFRANGE,
966 PETSC_ERROR_INITIAL,
967 "Point-block column lies outside the DMDA ghost stencil.");
968 goto cleanup;
969 }
970 column_locals[column_index] = column.c + 3 * (
971 (column.i - ghost_starts[0]) + ghost_sizes[0] * (
972 (column.j - ghost_starts[1]) + ghost_sizes[1] *
973 (column.k - ghost_starts[2])));
974 }
975 ierr = ISLocalToGlobalMappingApply(local_to_global, 1,
976 &row_local, &row);
977 if (ierr) goto cleanup;
978 ierr = ISLocalToGlobalMappingApply(local_to_global, column_count,
979 column_locals, columns);
980 if (ierr) goto cleanup;
981 if (!(row >= ownership_start && row < ownership_end)) {
982 ierr = PetscError(comm, __LINE__, PETSC_FUNCTION_NAME, __FILE__,
983 PETSC_ERR_PLIB, PETSC_ERROR_INITIAL,
984 "DMDA-mapped point-block row is not locally owned.");
985 goto cleanup;
986 }
987 for (PetscInt column_index = 0; column_index < column_count;
988 ++column_index) {
989 PetscBool duplicate = PETSC_FALSE;
990 for (PetscInt previous = 0; previous < column_index; ++previous)
991 if (columns[previous] == columns[column_index]) duplicate = PETSC_TRUE;
992 if (columns[column_index] < 0) {
993 ierr = PetscError(comm, __LINE__, PETSC_FUNCTION_NAME, __FILE__,
994 PETSC_ERR_PLIB, PETSC_ERROR_INITIAL,
995 "DMDA-mapped point-block column is invalid.");
996 goto cleanup;
997 }
998 if (duplicate) continue;
999 if (columns[column_index] >= ownership_start &&
1000 columns[column_index] < ownership_end)
1001 ++diagonal_nnz[row - ownership_start];
1002 else
1003 ++offdiagonal_nnz[row - ownership_start];
1004 }
1005 }
1006 }
1007 }
1008 }
1009
1010 ierr = MatCreateAIJ(comm, local_size, local_size, global_size, global_size,
1011 0, diagonal_nnz, 0, offdiagonal_nnz, &matrix);
1012 if (ierr) goto cleanup;
1013 ierr = MatSetBlockSize(matrix, 3); if (ierr) goto cleanup;
1014 ierr = MatSetLocalToGlobalMapping(matrix, local_to_global, local_to_global);
1015 if (ierr) goto cleanup;
1016 ierr = MatSetStencil(matrix, 3, ghost_sizes, ghost_starts, 3);
1017 if (ierr) goto cleanup;
1018 ierr = MatSetDM(matrix, user->fda); if (ierr) goto cleanup;
1019 ierr = MatSetOption(matrix, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE);
1020 if (ierr) goto cleanup;
1021 ierr = PetscFree2(diagonal_nnz, offdiagonal_nnz); if (ierr) goto cleanup;
1022
1023 *preconditioning_matrix = matrix;
1024 matrix = NULL;
1025
1026cleanup:
1027 if (nvert) {
1028 cleanup_ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, &nvert);
1029 if (!ierr) ierr = cleanup_ierr;
1030 }
1031 cleanup_ierr = MatDestroy(&matrix); if (!ierr) ierr = cleanup_ierr;
1032 cleanup_ierr = PetscFree2(diagonal_nnz, offdiagonal_nnz);
1033 if (!ierr) ierr = cleanup_ierr;
1035 PetscFunctionReturn(ierr);
1036}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_OpenCsv()

static FILE * MomentumNewtonKrylov_OpenCsv ( const MomentumNewtonKrylovContext *  ctx,
const char *  kind,
const char *  header 
)
static

Opens one of the block's Newton CSV records on rank 0, or returns NULL.

PicurvOpenRunCsv() owns the lifecycle: a fresh run's first open truncates the file, the header is written to an empty file, and a continued run's first open marks the seam. A failure only warns: these files are diagnostics, and must not change the solve's error behaviour.

Definition at line 152 of file momentum_newton_krylov.c.

153{
155 SimCtx *simCtx = ctx->user->simCtx;
156 char name[128];
157 FILE *file = NULL;
158
159 if (PetscSNPrintf(name, sizeof(name), "Momentum_Solver_Newton_Krylov_%s_Block_%d.csv", kind, (int)ctx->user->_this) ||
160 PicurvOpenRunCsv(simCtx, simCtx->log_dir, name, header, PICURV_CSV_TRUNCATE_ON_FRESH, PETSC_FALSE, &file)) {
161 file = NULL;
162 }
164 return file;
165}
PetscErrorCode PicurvOpenRunCsv(SimCtx *simCtx, const char *dir, const char *filename, const char *header, PicurvCsvLifecycle lifecycle, PetscBool required, FILE **file)
Opens one of the run's CSV records (diagnostics, solver history, profiling) for writing.
Definition logging.c:3552
@ PICURV_CSV_TRUNCATE_ON_FRESH
A fresh run's first write empties the file; a continued run appends.
Definition logging.h:1146
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:941
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_ClassifyRow()

static MomentumRowType MomentumNewtonKrylov_ClassifyRow ( UserCtx *  user,
const PetscReal ***  nvert,
PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscInt  component,
PetscInt *  ri,
PetscInt *  rj,
PetscInt *  rk 
)
static

Row classification with solid-cell masking folded in.

A masked row's residual is identically zero for every X, so it has a zero Jacobian row and a zero column and its unknown is undetermined. It therefore needs the same identity treatment as any other row carrying no unknown. Checked after the boundary classification so a periodic duplicate keeps its representative column.

Definition at line 384 of file momentum_newton_krylov.c.

387{
389 const MomentumRowType type = ClassifyMomentumRow(user, i, j, k, component, ri, rj, rk);
390
391 if (type == MOM_ROW_PHYSICAL && MomentumRowIsSolidMasked(nvert, i, j, k, component))
394 return type;
395}
PetscBool MomentumRowIsSolidMasked(const PetscReal ***nvert, PetscInt i, PetscInt j, PetscInt k, PetscInt component)
Reports whether a momentum row is masked out by the solid-cell field.
Definition Boundaries.c:193
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FrozenMomentumJacobian_MetricNormSquared()

static PetscReal FrozenMomentumJacobian_MetricNormSquared ( Cmpnts  metric)
inlinestatic

Returns the squared Euclidean norm of one metric vector.

Definition at line 398 of file momentum_newton_krylov.c.

399{
400 return metric.x * metric.x + metric.y * metric.y + metric.z * metric.z;
401}
PetscScalar z
Definition variables.h:145
PetscScalar y
Definition variables.h:145
Here is the caller graph for this function:

◆ FrozenMomentumJacobian_FaceEddyViscosity()

static PetscReal FrozenMomentumJacobian_FaceEddyViscosity ( const UserCtx *  user,
const PetscReal ***  nu_t,
PetscInt  axis,
PetscInt  i,
PetscInt  j,
PetscInt  k 
)
static

Returns the face-averaged eddy viscosity the residual uses on one face.

The preconditioner has to reproduce the operator it preconditions, so the interior face average here tracks ComputeViscous().

The WALL branch below does not, and is retained only because it cannot currently change an assembled entry. It is unreachable by construction, not by configuration: FrozenMomentumJacobian_PointBlock() puts the eddy viscosity only on the three diagonal entries, and the row-at-a-time insertion in FrozenMomentumJacobian_AssemblePointBlocks() means row c carries only axis == c. The two coordinates that trigger the branch are exactly those ClassifyMomentumRow() reports as non-MOM_ROW_PHYSICAL, and only physical rows assemble a block; the WALL-on-a-periodic-axis loophole is closed by the pairing check in BoundarySystem_Validate(). Both rules state the same boundary fact: the wall-normal flux at a no-slip wall is not an unknown.

On a wall-resolved face ComputeViscous() uses the molecular gradient flux with no eddy viscosity, which this branch matches. On a wall-modelled face it applies the wall stress ApplyWallFunction() stored, with no gradient flux at all: that flux depends on the reference cell, the second from the wall, through the law, and not on the cells either side of the face. Widening the stencil would make the branch reachable, and the block would then need the law's derivative with respect to the reference velocity in place of any viscosity here. Tracked as issue #8.

Definition at line 428 of file momentum_newton_krylov.c.

431{
433 const BCFace neg_face[3] = {BC_FACE_NEG_X, BC_FACE_NEG_Y, BC_FACE_NEG_Z};
434 const BCFace pos_face[3] = {BC_FACE_POS_X, BC_FACE_POS_Y, BC_FACE_POS_Z};
435 const PetscInt coord[3] = {i, j, k};
436 const PetscInt size[3] = {user->info.mx, user->info.my, user->info.mz};
437 PetscReal neighbour;
438
439 if (nu_t == NULL) { PROFILE_HELPER_END; return 0.0; }
440 if ((BoundaryFaceHasWallShear(&user->boundary_faces[neg_face[axis]]) && coord[axis] == 0) ||
441 (BoundaryFaceHasWallShear(&user->boundary_faces[pos_face[axis]]) && coord[axis] == size[axis] - 2))
442 { PROFILE_HELPER_END; return 0.0; }
443
444 neighbour = (axis == 0) ? nu_t[k][j][i + 1]
445 : (axis == 1) ? nu_t[k][j + 1][i]
446 : nu_t[k + 1][j][i];
448 return 0.5 * (nu_t[k][j][i] + neighbour);
449}
PetscBool BoundaryFaceHasWallShear(const BoundaryFaceConfig *face)
Reports whether a boundary face is a wall that exerts shear on the flow.
Definition Boundaries.c:9
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:308
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FrozenMomentumJacobian_PointBlock()

static void FrozenMomentumJacobian_PointBlock ( const UserCtx *  user,
const Cmpnts ***  ucont,
const Cmpnts ***  csi,
const Cmpnts ***  eta,
const Cmpnts ***  zet,
const PetscReal ***  aj,
const PetscReal ***  nu_t,
PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscScalar  block[9] 
)
static

Returns the frozen-momentum point block for the current residual convention.

Definition at line 452 of file momentum_newton_krylov.c.

456{
458 const SimCtx *simCtx = user->simCtx;
459 const PetscReal dtc = MomentumBDFCoefficient((SimCtx *)simCtx) / simCtx->dt;
460 const PetscReal molecular = simCtx->ren > 0.0 ? 1.0 / simCtx->ren : 0.0;
461 /* The residual diffuses with nu + nu_t, so the block must too; nu_t alone is the
462 eddy contribution. Omitting it left the preconditioner modelling a viscous
463 diagonal smaller than the operator's by the eddy-to-molecular ratio, which on a
464 developed LES is order one or more. */
465 const PetscReal nu_eff_i = molecular + FrozenMomentumJacobian_FaceEddyViscosity(user, nu_t, 0, i, j, k);
466 const PetscReal nu_eff_j = molecular + FrozenMomentumJacobian_FaceEddyViscosity(user, nu_t, 1, i, j, k);
467 const PetscReal nu_eff_k = molecular + FrozenMomentumJacobian_FaceEddyViscosity(user, nu_t, 2, i, j, k);
468 const PetscReal AJip = 0.5 * (aj[k][j][i] + aj[k][j][i + 1]);
469 const PetscReal AJjp = 0.5 * (aj[k][j][i] + aj[k][j + 1][i]);
470 const PetscReal AJkp = 0.5 * (aj[k][j][i] + aj[k + 1][j][i]);
471 const PetscReal g11ip = csi[k][j][i].x * csi[k][j][i].x +
472 csi[k][j][i].y * csi[k][j][i].y +
473 csi[k][j][i].z * csi[k][j][i].z;
474 const PetscReal g22ip = 0.25 * (
478 FrozenMomentumJacobian_MetricNormSquared(eta[k][j - 1][i + 1]));
479 const PetscReal g33ip = 0.25 * (
483 FrozenMomentumJacobian_MetricNormSquared(zet[k - 1][j][i + 1]));
484 const PetscReal g11jp = 0.25 * (
488 FrozenMomentumJacobian_MetricNormSquared(csi[k][j + 1][i - 1]));
489 const PetscReal g22jp = eta[k][j][i].x * eta[k][j][i].x +
490 eta[k][j][i].y * eta[k][j][i].y +
491 eta[k][j][i].z * eta[k][j][i].z;
492 const PetscReal g33jp = 0.25 * (
496 FrozenMomentumJacobian_MetricNormSquared(zet[k - 1][j + 1][i]));
497 const PetscReal g11kp = 0.25 * (
501 FrozenMomentumJacobian_MetricNormSquared(csi[k + 1][j][i - 1]));
502 const PetscReal g22kp = 0.25 * (
506 FrozenMomentumJacobian_MetricNormSquared(eta[k + 1][j - 1][i]));
507 const PetscReal g33kp = zet[k][j][i].x * zet[k][j][i].x +
508 zet[k][j][i].y * zet[k][j][i].y +
509 zet[k][j][i].z * zet[k][j][i].z;
510 const PetscReal U0jp = 0.25 * (ucont[k][j][i].x + ucont[k][j][i - 1].x +
511 ucont[k][j + 1][i].x + ucont[k][j + 1][i - 1].x);
512 const PetscReal U0kp = 0.25 * (ucont[k][j][i].x + ucont[k][j][i - 1].x +
513 ucont[k + 1][j][i].x + ucont[k + 1][j][i - 1].x);
514 const PetscReal U1ip = 0.25 * (ucont[k][j][i].y + ucont[k][j - 1][i].y +
515 ucont[k][j][i + 1].y + ucont[k][j - 1][i + 1].y);
516 const PetscReal U1kp = 0.25 * (ucont[k][j][i].y + ucont[k][j - 1][i].y +
517 ucont[k + 1][j][i].y + ucont[k + 1][j - 1][i].y);
518 const PetscReal U2ip = 0.25 * (ucont[k][j][i].z + ucont[k - 1][j][i].z +
519 ucont[k][j][i + 1].z + ucont[k - 1][j][i + 1].z);
520 const PetscReal U2jp = 0.25 * (ucont[k][j][i].z + ucont[k - 1][j][i].z +
521 ucont[k][j + 1][i].z + ucont[k - 1][j + 1][i].z);
522 PetscReal A[6][4] = {{0.0}};
523 PetscReal Su, Sv, Sw, nui, nuj, nuk;
524
525 A[0][0] = 0.125 * aj[k][j][i] * ucont[k][j][i].y;
526 A[0][1] = -0.125 * aj[k][j - 1][i] * ucont[k][j - 1][i].y;
527 A[0][2] = 0.125 * aj[k][j][i + 1] * ucont[k][j][i + 1].y;
528 A[0][3] = -0.125 * aj[k][j - 1][i + 1] * ucont[k][j - 1][i + 1].y;
529 A[1][0] = 0.125 * aj[k][j][i] * ucont[k][j][i].z;
530 A[1][1] = -0.125 * aj[k - 1][j][i] * ucont[k - 1][j][i].z;
531 A[1][2] = 0.125 * aj[k][j][i + 1] * ucont[k][j][i + 1].z;
532 A[1][3] = -0.125 * aj[k - 1][j][i + 1] * ucont[k - 1][j][i + 1].z;
533 A[2][0] = -0.125 * aj[k][j + 1][i - 1] * ucont[k][j + 1][i - 1].x;
534 A[2][1] = -0.125 * aj[k][j][i - 1] * ucont[k][j][i - 1].x;
535 A[2][2] = 0.125 * aj[k][j + 1][i] * ucont[k][j + 1][i].x;
536 A[2][3] = 0.125 * aj[k][j][i] * ucont[k][j][i].x;
537 A[3][0] = 0.125 * aj[k][j][i] * ucont[k][j][i].z;
538 A[3][1] = -0.125 * aj[k - 1][j][i] * ucont[k - 1][j][i].z;
539 A[3][2] = 0.125 * aj[k][j + 1][i] * ucont[k][j + 1][i].z;
540 A[3][3] = -0.125 * aj[k - 1][j + 1][i] * ucont[k - 1][j + 1][i].z;
541 A[4][0] = -0.125 * aj[k + 1][j][i - 1] * ucont[k + 1][j][i - 1].x;
542 A[4][1] = -0.125 * aj[k][j][i - 1] * ucont[k][j][i - 1].x;
543 A[4][2] = 0.125 * aj[k + 1][j][i] * ucont[k + 1][j][i].x;
544 A[4][3] = 0.125 * aj[k][j][i] * ucont[k][j][i].x;
545 A[5][0] = -0.125 * aj[k + 1][j - 1][i] * ucont[k + 1][j - 1][i].y;
546 A[5][1] = -0.125 * aj[k][j - 1][i] * ucont[k][j - 1][i].y;
547 A[5][2] = 0.125 * aj[k + 1][j][i] * ucont[k + 1][j][i].y;
548 A[5][3] = 0.125 * aj[k][j][i] * ucont[k][j][i].y;
549 Su = A[0][0] + A[0][1] + A[0][2] + A[0][3] +
550 A[1][0] + A[1][1] + A[1][2] + A[1][3];
551 Sv = A[2][0] + A[2][1] + A[2][2] + A[2][3] +
552 A[3][0] + A[3][1] + A[3][2] + A[3][3];
553 Sw = A[4][0] + A[4][1] + A[4][2] + A[4][3] +
554 A[5][0] + A[5][1] + A[5][2] + A[5][3];
555 nui = AJip * AJip * (g11ip + g22ip + g33ip) * nu_eff_i;
556 nuj = AJjp * AJjp * (g11jp + g22jp + g33jp) * nu_eff_j;
557 nuk = AJkp * AJkp * (g11kp + g22kp + g33kp) * nu_eff_k;
558
559 /* MomentumNewtonKrylov_FormResidual forms F = -R, fixing this block's sign. */
560 /* `block` is row-major: each row is one residual component and each column
561 is one same-cell Ucont component. */
562 block[0] = dtc + nui + Su; block[1] = 0.5 * AJjp * U0jp; block[2] = 0.5 * AJkp * U0kp;
563 block[3] = 0.5 * AJip * U1ip; block[4] = dtc + nuj + Sv; block[5] = 0.5 * AJkp * U1kp;
564 block[6] = 0.5 * AJip * U2ip; block[7] = 0.5 * AJjp * U2jp; block[8] = dtc + nuk + Sw;
566}
static PetscReal FrozenMomentumJacobian_FaceEddyViscosity(const UserCtx *user, const PetscReal ***nu_t, PetscInt axis, PetscInt i, PetscInt j, PetscInt k)
Returns the face-averaged eddy viscosity the residual uses on one face.
static PetscReal FrozenMomentumJacobian_MetricNormSquared(Cmpnts metric)
Returns the squared Euclidean norm of one metric vector.
PetscReal ren
Definition variables.h:965
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FrozenMomentumJacobian_DescribePointBlock()

static PetscErrorCode FrozenMomentumJacobian_DescribePointBlock ( UserCtx *  user,
MomentumPreconditionerDescription *  description 
)
static

Describes the audited frozen-coefficient point-block model.

Definition at line 569 of file momentum_newton_krylov.c.

571{
572 PetscFunctionBeginUser;
574 (void)user;
575 PetscCheck(description != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
576 "Preconditioner description is NULL.");
579 description->block_size = 3;
580 description->stencil_width = 0;
582 PetscFunctionReturn(PETSC_SUCCESS);
583}

◆ MomentumPreconditionerEngine_ApplyConstraintRows()

static PetscErrorCode MomentumPreconditionerEngine_ApplyConstraintRows ( UserCtx *  user,
Mat  preconditioning_matrix 
)
static

Inserts all common fixed, homogeneous, and periodic-duplicate rows.

Definition at line 672 of file momentum_newton_krylov.c.

674{
675 DMDALocalInfo info = user->info;
676 const PetscReal ***nvert = NULL;
677
678 PetscFunctionBeginUser;
680 PetscCall(DMDAVecGetArrayRead(user->da, user->lNvert, &nvert));
681 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
682 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
683 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
684 for (PetscInt component = 0; component < 3; ++component) {
685 PetscInt ri, rj, rk;
687 user, nvert, i, j, k, component, &ri, &rj, &rk);
688 if (type != MOM_ROW_PHYSICAL) {
689 MatStencil row = {.i = i, .j = j, .k = k, .c = component};
690 MatStencil columns[2] = {row, row};
691 PetscScalar values[2] = {1.0, -1.0};
692 PetscInt column_count = 1;
693 if (type == MOM_ROW_PERIODIC_DUPLICATE) {
694 columns[column_count++] = (MatStencil){
695 .i = ri, .j = rj, .k = rk, .c = component
696 };
697 }
698 PetscCall(MatSetValuesStencil(preconditioning_matrix, 1, &row,
699 column_count, columns, values,
700 INSERT_VALUES));
701 }
702 }
703 }
704 }
705 }
706 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lNvert, &nvert));
708 PetscFunctionReturn(PETSC_SUCCESS);
709}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumNewtonJacobian_Create()

static PetscErrorCode MomentumNewtonJacobian_Create ( SNES  snes,
MomentumNewtonJacobian *  jacobian 
)
static

Creates the selected Jacobian operator; currently PETSc MFFD only.

Definition at line 717 of file momentum_newton_krylov.c.

719{
720 PetscFunctionBeginUser;
722 PetscCheck(jacobian->type == MOM_NK_JACOBIAN_FINITE_DIFFERENCE &&
724 PETSC_COMM_WORLD, PETSC_ERR_SUP,
725 "Unsupported Newton Krylov Jacobian type/finite-difference-mode combination.");
726 PetscCall(MatCreateSNESMF(snes, &jacobian->jacobian_operator));
727 PetscCall(MatSetOptionsPrefix(jacobian->jacobian_operator, "mom_nk_"));
728 PetscCall(PetscObjectSetName((PetscObject)jacobian->jacobian_operator,
729 "momentum_jacobian_finite_difference_matrix_free"));
731 PetscFunctionReturn(PETSC_SUCCESS);
732}
Here is the caller graph for this function:

◆ MomentumNewtonJacobian_Update()

static PetscErrorCode MomentumNewtonJacobian_Update ( SNES  snes,
Vec  current_solution,
MomentumNewtonJacobian *  jacobian 
)
static

Updates the matrix-free finite-difference operator base.

Definition at line 735 of file momentum_newton_krylov.c.

737{
738 PetscFunctionBeginUser;
740 PetscCall(MatMFFDComputeJacobian(snes, current_solution, jacobian->jacobian_operator,
741 jacobian->jacobian_operator, NULL));
743 PetscFunctionReturn(PETSC_SUCCESS);
744}
Here is the caller graph for this function:

◆ MomentumNewtonJacobian_Register()

static PetscErrorCode MomentumNewtonJacobian_Register ( SNES  snes,
MomentumNewtonJacobian *  jacobian,
MomentumPreconditionerEngine *  engine,
MomentumNewtonKrylovContext *  ctx 
)
static

Registers the application orchestration callback and both SNES matrices.

Definition at line 747 of file momentum_newton_krylov.c.

750{
751 PetscFunctionBeginUser;
753 PetscCall(SNESSetJacobian(snes, jacobian->jacobian_operator,
757 PetscFunctionReturn(PETSC_SUCCESS);
758}
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.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumNewtonJacobian_Destroy()

static PetscErrorCode MomentumNewtonJacobian_Destroy ( MomentumNewtonJacobian *  jacobian)
static

Destroys a partially or fully created Jacobian operator.

Definition at line 761 of file momentum_newton_krylov.c.

762{
763 PetscFunctionBeginUser;
765 PetscCall(MatDestroy(&jacobian->jacobian_operator));
767 PetscFunctionReturn(PETSC_SUCCESS);
768}
Here is the caller graph for this function:

◆ MomentumPreconditionerEngine_Create()

static PetscErrorCode MomentumPreconditionerEngine_Create ( UserCtx *  user,
Mat  jacobian_operator,
const MomentumPreconditionerDescription *  requested,
MomentumPreconditionerEngine *  engine 
)
static

Validates a model/structure and creates or aliases its matrix.

Definition at line 771 of file momentum_newton_krylov.c.

774{
775 PetscFunctionBeginUser;
777 engine->description = *requested;
778 if (requested->model == MOM_NK_PC_MODEL_NONE &&
779 requested->structure == MOM_NK_PC_STRUCTURE_NONE) {
780 engine->description.block_size = 0;
781 engine->description.stencil_width = 0;
782 engine->preconditioning_matrix = jacobian_operator;
783 engine->aliases_jacobian_operator = PETSC_TRUE;
784 engine->owns_preconditioning_matrix = PETSC_FALSE;
785 engine->petsc_pc_type = PCNONE;
786 } else if (requested->model == MOM_NK_PC_MODEL_FROZEN_MOMENTUM_JACOBIAN &&
789 PetscCall(engine->model_ops->Describe(user, &engine->description));
791 user, &engine->preconditioning_matrix));
792 engine->owns_preconditioning_matrix = PETSC_TRUE;
793 PetscCall(PetscObjectSetName((PetscObject)engine->preconditioning_matrix,
794 "momentum_preconditioner_frozen_point_block"));
795 engine->aliases_jacobian_operator = PETSC_FALSE;
796 engine->petsc_pc_type = PCPBJACOBI;
797 } else {
798 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP,
799 "Unsupported Newton Krylov preconditioner model/structure combination.");
800 }
802 PetscFunctionReturn(PETSC_SUCCESS);
803}
static const MomentumPreconditionerModelOps frozen_momentum_point_block_ops
const MomentumPreconditionerModelOps * model_ops
static PetscErrorCode MomentumPreconditionerEngine_CreateExactPointBlockMatrix(UserCtx *user, Mat *preconditioning_matrix)
Creates the frozen point-block P matrix with its exact scalar pattern.
PetscErrorCode(* Describe)(UserCtx *, MomentumPreconditionerDescription *)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumPreconditionerEngine_Assemble()

static PetscErrorCode MomentumPreconditionerEngine_Assemble ( MomentumPreconditionerEngine *  engine,
UserCtx *  user,
Vec  current_solution 
)
static

Runs model insertion, common row handling, and final assembly.

Definition at line 806 of file momentum_newton_krylov.c.

808{
809 PetscFunctionBeginUser;
811 if (engine->aliases_jacobian_operator) { PROFILE_FUNCTION_END; PetscFunctionReturn(PETSC_SUCCESS); }
812 PetscCall(MatZeroEntries(engine->preconditioning_matrix));
813 PetscCall(engine->model_ops->AssembleInterior(user, current_solution,
814 engine->preconditioning_matrix));
816 user, engine->preconditioning_matrix));
817 PetscCall(MatAssemblyBegin(engine->preconditioning_matrix, MAT_FINAL_ASSEMBLY));
818 PetscCall(MatAssemblyEnd(engine->preconditioning_matrix, MAT_FINAL_ASSEMBLY));
820 PetscFunctionReturn(PETSC_SUCCESS);
821}
static PetscErrorCode MomentumPreconditionerEngine_ApplyConstraintRows(UserCtx *user, Mat preconditioning_matrix)
Inserts all common fixed, homogeneous, and periodic-duplicate rows.
PetscErrorCode(* AssembleInterior)(UserCtx *, Vec, Mat)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumPreconditionerEngine_ConfigurePetscPC()

static PetscErrorCode MomentumPreconditionerEngine_ConfigurePetscPC ( MomentumPreconditionerEngine *  engine,
PC  pc 
)
static

Applies the validated model/structure-to-PETSc-PC mapping.

Definition at line 824 of file momentum_newton_krylov.c.

826{
827 PetscFunctionBeginUser;
829 PetscCall(PCSetType(pc, engine->petsc_pc_type));
831 PetscFunctionReturn(PETSC_SUCCESS);
832}
Here is the caller graph for this function:

◆ MomentumPreconditionerEngine_ValidatePetscPC()

static PetscErrorCode MomentumPreconditionerEngine_ValidatePetscPC ( MomentumPreconditionerEngine *  engine,
PC  pc 
)
static

Rejects raw options that select an unvalidated PETSc PC backend.

Definition at line 835 of file momentum_newton_krylov.c.

837{
838 const char *actual_type = NULL;
839 PetscBool matches = PETSC_FALSE;
840
841 PetscFunctionBeginUser;
843 PetscCall(PCGetType(pc, &actual_type));
844 PetscCall(PetscStrcmp(actual_type, engine->petsc_pc_type, &matches));
845 PetscCheck(matches, PETSC_COMM_WORLD, PETSC_ERR_SUP,
846 "Newton Krylov preconditioner model/structure requires internal PETSc PC "
847 "type '%s', but raw option processing selected '%s'.",
848 engine->petsc_pc_type, actual_type ? actual_type : "(unset)");
850 PetscFunctionReturn(PETSC_SUCCESS);
851}
Here is the caller graph for this function:

◆ MomentumPreconditionerEngine_Destroy()

static PetscErrorCode MomentumPreconditionerEngine_Destroy ( MomentumPreconditionerEngine *  engine)
static

Destroys only a separately owned preconditioning matrix.

Definition at line 854 of file momentum_newton_krylov.c.

856{
857 PetscFunctionBeginUser;
859 if (!engine->owns_preconditioning_matrix) engine->preconditioning_matrix = NULL;
860 PetscCall(MatDestroy(&engine->preconditioning_matrix));
861 engine->aliases_jacobian_operator = PETSC_FALSE;
862 engine->owns_preconditioning_matrix = PETSC_FALSE;
864 PetscFunctionReturn(PETSC_SUCCESS);
865}
Here is the caller graph for this function:

◆ MomentumNewtonKrylov_Build()

static PetscErrorCode MomentumNewtonKrylov_Build ( UserCtx *  user)
static

Builds the solve context MomentumSolver_NewtonKrylov() keeps for the run.

Definition at line 1196 of file momentum_newton_krylov.c.

1197{
1199 MomentumPreconditionerDescription preconditioner_description = {0};
1200 KSP ksp = NULL;
1201 PC pc = NULL;
1202
1203 PetscFunctionBeginUser;
1205 PetscCall(PetscNew(&user->momentumNewtonKrylov));
1206 ctx = user->momentumNewtonKrylov;
1207 ctx->user = user;
1208 PetscCall(VecDuplicate(user->Ucont, &ctx->solution));
1209 PetscCall(VecDuplicate(user->Ucont, &ctx->entry_backup));
1210 PetscCall(SNESCreate(PetscObjectComm((PetscObject)user->Ucont), &ctx->snes));
1211 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&ctx->jacobian, &preconditioner_description));
1212 PetscCall(SNESSetOptionsPrefix(ctx->snes, "mom_nk_"));
1213 PetscCall(SNESSetType(ctx->snes, SNESNEWTONLS));
1214 PetscCall(SNESSetDM(ctx->snes, user->fda));
1215 /* The residual and Jacobian callbacks are registered by MomentumSolver_NewtonKrylov() before
1216 every solve, so none is registered here. */
1217 PetscCall(MomentumNewtonJacobian_Create(ctx->snes, &ctx->jacobian));
1219 &preconditioner_description,
1220 &ctx->preconditioning_engine));
1221 PetscCall(SNESGetKSP(ctx->snes, &ksp));
1222 PetscCall(KSPSetType(ksp, KSPGMRES));
1223 PetscCall(KSPGetPC(ksp, &pc));
1225 PetscCall(SNESSetFromOptions(ctx->snes));
1226 PetscCall(SNESMonitorSet(ctx->snes, MomentumNewtonKrylov_Monitor, ctx, NULL));
1227 PetscCall(KSPMonitorSet(ksp, MomentumNewtonKrylov_LinearMonitor, ctx, NULL));
1230 PetscFunctionReturn(PETSC_SUCCESS);
1231}
static PetscErrorCode MomentumPreconditionerEngine_ConfigurePetscPC(MomentumPreconditionerEngine *engine, PC pc)
Applies the validated model/structure-to-PETSc-PC mapping.
static PetscErrorCode MomentumPreconditionerEngine_ValidatePetscPC(MomentumPreconditionerEngine *engine, PC pc)
Rejects raw options that select an unvalidated PETSc PC backend.
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 PetscErrorCode MomentumNewtonKrylov_ReadLinearizationConfig(MomentumNewtonJacobian *jacobian, MomentumPreconditionerDescription *description)
Reads application-owned Jacobian and preconditioner mathematics.
static PetscErrorCode MomentumNewtonKrylov_LinearMonitor(KSP ksp, PetscInt iteration, PetscReal norm, void *ctx)
Writes the effective KSP tolerance and PETSc-reported norm for each inner iteration.
static PetscErrorCode MomentumNewtonJacobian_Create(SNES snes, MomentumNewtonJacobian *jacobian)
Creates the selected Jacobian operator; currently PETSc MFFD only.
static PetscErrorCode MomentumNewtonKrylov_Monitor(SNES snes, PetscInt iteration, PetscReal norm, void *ctx)
Captures SNES iteration norms and optionally writes PICurv history rows.
struct MomentumNewtonKrylovContext * momentumNewtonKrylov
Definition variables.h:1235
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumSolver_NewtonKrylov_Destroy()

PetscErrorCode MomentumSolver_NewtonKrylov_Destroy ( UserCtx *  user)

Implementation of MomentumSolver_NewtonKrylov_Destroy().

Releases the Newton–Krylov solve context MomentumSolver_NewtonKrylov() keeps for the run.

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

See also
MomentumSolver_NewtonKrylov_Destroy()

Definition at line 1239 of file momentum_newton_krylov.c.

1240{
1242
1243 PetscFunctionBeginUser;
1245 if (ctx) {
1246 PetscCall(VecDestroy(&ctx->entry_backup));
1247 PetscCall(VecDestroy(&ctx->solution));
1249 PetscCall(MomentumNewtonJacobian_Destroy(&ctx->jacobian));
1250 PetscCall(SNESDestroy(&ctx->snes));
1251 PetscCall(PetscFree(user->momentumNewtonKrylov));
1252 }
1254 PetscFunctionReturn(PETSC_SUCCESS);
1255}
static PetscErrorCode MomentumNewtonJacobian_Destroy(MomentumNewtonJacobian *jacobian)
Destroys a partially or fully created Jacobian operator.
static PetscErrorCode MomentumPreconditionerEngine_Destroy(MomentumPreconditionerEngine *engine)
Destroys only a separately owned preconditioning matrix.
Here is the call graph for this function:

◆ MomentumNewtonKrylov_AssessIterate()

static PetscErrorCode MomentumNewtonKrylov_AssessIterate ( MomentumNewtonKrylovContext *  ctx,
Vec  solution,
PetscReal *  residual,
PetscReal *  residual_2,
PetscReal *  reference,
PetscBool *  satisfied 
)
static

Measure the residual at the iterate a failed SNES solve holds, for the failure policy in MomentumSolver_NewtonKrylov().

SNES judges the residual by absolute and relative tolerances that do not scale with the grid or the flow. Near a steady state the residual can reach the round-off floor of its own evaluation; the line search then cannot decrease it and SNES reports divergence for a state that is as converged as the discretization allows. The caller therefore judges the held iterate itself: by the dual-time solver's dimensionless test, |R|_inf <= mom_resid_atol * a0 |U|_inf / dt, and by whether its residual is finite and no larger than the residual the step started from.

Parameters
[in]ctxSolve context; its SNES supplies the residual function and vector.
[in]solutionIterate to test; it is left unchanged.
[out]residual|R|_inf at solution.
[out]residual_2|R|_2 at solution, the norm SNES reports as its initial norm.
[out]referencea0 |U|_inf / dt at solution.
[out]satisfiedTrue when mom_resid_atol > 0 and the dimensionless test holds.

Definition at line 1276 of file momentum_newton_krylov.c.

1279{
1280 SimCtx *simCtx = ctx->user->simCtx;
1281 Vec F = NULL;
1282 PetscReal ucont_inf;
1283
1284 PetscFunctionBeginUser;
1286 PetscCall(SNESGetFunction(ctx->snes, &F, NULL, NULL));
1287 PetscCall(SNESComputeFunction(ctx->snes, solution, F));
1288 PetscCall(VecNorm(F, NORM_INFINITY, residual));
1289 PetscCall(VecNorm(F, NORM_2, residual_2));
1290 PetscCall(VecNorm(solution, NORM_INFINITY, &ucont_inf));
1291 *reference = MomentumBDFCoefficient(simCtx) * ucont_inf / simCtx->dt;
1292 *satisfied = (PetscBool)(simCtx->mom_resid_atol > 0.0 && *reference > 0.0 &&
1293 *residual <= simCtx->mom_resid_atol * *reference);
1295 PetscFunctionReturn(PETSC_SUCCESS);
1296}
PetscReal mom_resid_atol
Definition variables.h:960
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumSolver_NewtonKrylov()

PetscErrorCode MomentumSolver_NewtonKrylov ( UserCtx *  user,
IBMNodes *  ibm,
FSInfo *  fsi 
)

Solves one physical momentum step with matrix-free Newton–Krylov.

The Jacobian operator is the finite-difference, matrix-free operator of the complete deterministic residual. With no mathematical preconditioner its matrix is also passed to PETSc's preconditioning slot and PCNONE is derived. The optional provisional frozen-momentum, point-block model approximates same-cell frozen coupling, owns a separate matrix, and derives a block-Jacobi PETSc backend. It does not relax the version-one physics or boundary-condition restrictions. The PETSc solver objects are built on the first call and kept for the run in UserCtx::momentumNewtonKrylov (MomentumSolver_NewtonKrylov_Destroy() releases them), with UserCtx::Rhs; a step that fails releases both, so the next call builds afresh. Rows removed by legacy boundary residual enforcement are made explicit: conditioned normal rows use X-Uconditioned, untouched dummy/tangential rows use X, and periodic duplicates use Xdup-Xrepresentative. Unsupported masked, interface, and component-disabled rows are rejected before setup.

Parameters
userSingle-block momentum context.
ibmMust be NULL; immersed boundaries are not supported in version one.
fsiMust be NULL; moving-body coupling is not supported in version one.
Returns
0 when the step is committed: on SNES convergence, or when SNES fails but the residual at its iterate is finite and no larger than at entry (then simCtx->mom_last_converged is false). PETSC_ERR_CONV_FAILED after rollback when that residual is non-finite or grew.

Definition at line 1303 of file momentum_newton_krylov.c.

1304{
1305 PetscErrorCode ierr = PETSC_SUCCESS, cleanup_ierr;
1306 SimCtx *simCtx;
1307 SNES snes = NULL;
1308 Vec solution = NULL, entry_backup = NULL;
1309 MomentumNewtonKrylovContext *ctx = NULL;
1310 PetscBool restore_entry = PETSC_FALSE;
1311 PetscBool solve_started = PETSC_FALSE;
1312 PetscBool committed = PETSC_FALSE;
1313 PetscBool converged = PETSC_FALSE;
1314 SNESConvergedReason reason = SNES_CONVERGED_ITERATING;
1315 PetscInt nonlinear_its = 0, function_evals = 0, linear_its = 0;
1316 PetscReal final_norm = PETSC_MAX_REAL;
1317 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
1318
1319 PetscFunctionBeginUser;
1321 PetscCall(MomentumNewtonKrylov_Validate(user));
1322 PetscCheck(ibm == NULL && fsi == NULL, PETSC_COMM_WORLD, PETSC_ERR_SUP,
1323 "Newton Krylov version one does not accept IBM or FSI objects.");
1324 simCtx = user->simCtx;
1325
1326 /* Rhs is kept for the run and zeroed for each step. */
1327 if (!user->Rhs) { ierr = VecDuplicate(user->Ucont, &user->Rhs); if (ierr) goto cleanup; }
1328 ierr = VecZeroEntries(user->Rhs); if (ierr) goto cleanup;
1329
1330 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); if (ierr) goto cleanup;
1331 ierr = ApplyBoundaryConditions(user); if (ierr) goto cleanup;
1332 if (!user->momentumNewtonKrylov) { ierr = MomentumNewtonKrylov_Build(user); if (ierr) goto cleanup; }
1333 ctx = user->momentumNewtonKrylov;
1334 ctx->have_initial_norm = PETSC_FALSE;
1335 ctx->initial_norm = 0.0;
1336 snes = ctx->snes;
1337 /* The residual and Jacobian callbacks live on fda's DMSNES, which every SNES on fda shares;
1338 registering them before each solve keeps another solver on fda from leaving its own in place. */
1339 ierr = SNESSetFunction(snes, NULL, MomentumNewtonKrylov_FormResidual, ctx); if (ierr) goto cleanup;
1340 ierr = MomentumNewtonJacobian_Register(snes, &ctx->jacobian,
1341 &ctx->preconditioning_engine, ctx); if (ierr) goto cleanup;
1342 solution = ctx->solution;
1343 entry_backup = ctx->entry_backup;
1344 ierr = VecCopy(user->Ucont, entry_backup); if (ierr) goto cleanup;
1345 restore_entry = PETSC_TRUE;
1346 ierr = VecCopy(user->Ucont, solution); if (ierr) goto cleanup;
1347
1349 "Newton Krylov Jacobian: finite_difference / matrix_free; "
1350 "Preconditioner: %s; PETSc Jacobian matrix type: MATMFFD; "
1351 "PETSc PC type: %s.\n",
1353 "none" : "frozen_momentum_jacobian / point_block",
1354 ctx->preconditioning_engine.petsc_pc_type);
1355
1357 solve_started = PETSC_TRUE;
1358 ierr = SNESSolve(snes, NULL, solution);
1359 if (ierr) goto cleanup;
1360 ierr = SNESGetConvergedReason(snes, &reason); if (ierr) goto cleanup;
1361 ierr = SNESGetIterationNumber(snes, &nonlinear_its); if (ierr) goto cleanup;
1362 ierr = SNESGetNumberFunctionEvals(snes, &function_evals); if (ierr) goto cleanup;
1363 ierr = SNESGetLinearSolveIterations(snes, &linear_its); if (ierr) goto cleanup;
1364 ierr = SNESGetFunctionNorm(snes, &final_norm); if (ierr) goto cleanup;
1365
1366 /* Failure policy, shared with the dual-time solver: a solve SNES did not declare
1367 converged still commits its iterate when the residual there is finite and no larger
1368 than at entry, and the step is reported as not converged. Only a non-finite or
1369 grown residual rolls the step back and stops the run. */
1370 if (reason > 0) {
1371 committed = PETSC_TRUE;
1372 converged = PETSC_TRUE;
1373 } else {
1374 PetscReal held_residual = 0.0, held_residual_2 = 0.0, held_reference = 0.0;
1375 ierr = MomentumNewtonKrylov_AssessIterate(ctx, solution, &held_residual, &held_residual_2,
1376 &held_reference, &converged); if (ierr) goto cleanup;
1377 committed = (PetscBool)(!PetscIsInfOrNanReal(held_residual_2) && ctx->have_initial_norm &&
1378 held_residual_2 <= ctx->initial_norm);
1379 if (!committed) converged = PETSC_FALSE;
1380 if (converged) {
1382 "Newton Krylov step %" PetscInt_FMT ": SNES reported %s, but |R|_inf = %.3e is within "
1383 "residual_absolute_tol (%.1e) x a0|U|/dt = %.3e; the step is committed as converged.\n",
1384 simCtx->step, SNESConvergedReasons[reason], (double)held_residual,
1385 (double)simCtx->mom_resid_atol, (double)held_reference);
1386 } else if (committed) {
1388 "Newton Krylov step %" PetscInt_FMT ": SNES reported %s with |R|_inf = %.3e, above "
1389 "residual_absolute_tol (%.1e) x a0|U|/dt = %.3e; |R|_2 = %.3e is no larger than %.3e at "
1390 "entry, so the step continues from the SNES iterate, not converged.\n",
1391 simCtx->step, SNESConvergedReasons[reason], (double)held_residual,
1392 (double)simCtx->mom_resid_atol, (double)held_reference,
1393 (double)held_residual_2, (double)ctx->initial_norm);
1394 } else {
1395 if (ctx->have_initial_norm) {
1397 "Newton Krylov step %" PetscInt_FMT ": SNES reported %s and |R|_2 at its iterate is "
1398 "%.3e against %.3e at entry; the step is rolled back.\n",
1399 simCtx->step, SNESConvergedReasons[reason], (double)held_residual_2,
1400 (double)ctx->initial_norm);
1401 } else {
1403 "Newton Krylov step %" PetscInt_FMT ": SNES reported %s before an entry residual norm "
1404 "was available (|R|_2 at its iterate is %.3e); the step is rolled back.\n",
1405 simCtx->step, SNESConvergedReasons[reason], (double)held_residual_2);
1406 }
1407 }
1408 }
1409
1410 ierr = VecCopy(committed ? solution : entry_backup, user->Ucont); if (ierr) goto cleanup;
1411 simCtx->mom_last_converged = converged;
1412 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); if (ierr) goto cleanup;
1413 ierr = ApplyBoundaryConditions(user); if (ierr) goto cleanup;
1414 restore_entry = PETSC_FALSE;
1415
1417 "Newton Krylov momentum solve: reason=%s (%" PetscInt_FMT "), Newton iterations=%" PetscInt_FMT ", residual evaluations=%" PetscInt_FMT ", Krylov iterations=%" PetscInt_FMT ", final norm=%.6e, state=%s.\n",
1418 SNESConvergedReasons[reason], (PetscInt)reason, nonlinear_its, function_evals,
1419 linear_its, (double)final_norm,
1420 !committed ? "rolled back" : converged ? "committed" : "committed, not converged");
1421 if (!committed) ierr = PETSC_ERR_CONV_FAILED;
1422
1423cleanup:
1424 /* A PETSc solve error can bypass the normal statistics path. Query whatever
1425 SNES retained without replacing the primary error so a failed attempt is
1426 still represented in the structured log. */
1427 if (solve_started && snes) {
1428 (void)SNESGetConvergedReason(snes, &reason);
1429 (void)SNESGetIterationNumber(snes, &nonlinear_its);
1430 (void)SNESGetNumberFunctionEvals(snes, &function_evals);
1431 (void)SNESGetLinearSolveIterations(snes, &linear_its);
1432 (void)SNESGetFunctionNorm(snes, &final_norm);
1433 }
1434 if (restore_entry && entry_backup) {
1435 cleanup_ierr = VecCopy(entry_backup, user->Ucont);
1436 if (!ierr) ierr = cleanup_ierr;
1437 cleanup_ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields);
1438 if (!ierr) ierr = cleanup_ierr;
1439 cleanup_ierr = ApplyBoundaryConditions(user);
1440 if (!ierr) ierr = cleanup_ierr;
1441 simCtx->mom_last_converged = PETSC_FALSE;
1442 committed = PETSC_FALSE;
1443 }
1444 if (ctx && ctx->history_file) {
1445 (void)fclose(ctx->history_file);
1446 ctx->history_file = NULL;
1447 }
1448 if (ctx && ctx->linear_history_file) {
1449 (void)fclose(ctx->linear_history_file);
1450 ctx->linear_history_file = NULL;
1451 }
1452 if (solve_started) {
1453 MomentumNewtonKrylov_WriteSummary(ctx, reason, nonlinear_its, function_evals,
1454 linear_its, final_norm, committed);
1455 }
1456 /* A failed step, or a failed first build, releases what the solver holds, so the next call
1457 starts afresh; a committed step keeps it for the next. */
1458 if (ierr) {
1459 cleanup_ierr = VecDestroy(&user->Rhs); if (!ierr) ierr = cleanup_ierr;
1460 cleanup_ierr = MomentumSolver_NewtonKrylov_Destroy(user); if (!ierr) ierr = cleanup_ierr;
1461 }
1463 PetscFunctionReturn(ierr);
1464}
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:86
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:29
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
static PetscErrorCode MomentumNewtonJacobian_Register(SNES snes, MomentumNewtonJacobian *jacobian, MomentumPreconditionerEngine *engine, MomentumNewtonKrylovContext *ctx)
Registers the application orchestration callback and both SNES matrices.
static PetscErrorCode MomentumNewtonKrylov_AssessIterate(MomentumNewtonKrylovContext *ctx, Vec solution, PetscReal *residual, PetscReal *residual_2, PetscReal *reference, PetscBool *satisfied)
Measure the residual at the iterate a failed SNES solve holds, for the failure policy in MomentumSolv...
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.
static void MomentumNewtonKrylov_OpenHistory(MomentumNewtonKrylovContext *ctx)
Opens the optional rank-zero Newton iteration-history files (per Newton and per Krylov iteration).
static PetscErrorCode MomentumNewtonKrylov_Build(UserCtx *user)
Builds the solve context MomentumSolver_NewtonKrylov() keeps for the run.
static PetscErrorCode MomentumNewtonKrylov_Validate(UserCtx *user)
Rejects configurations outside the audited version-one feature set.
static PetscErrorCode MomentumNewtonKrylov_FormResidual(SNES snes, Vec X, Vec F, void *ctx)
Adapts a PETSc trial vector to the existing momentum residual path.
#define MomentumSolver_NewtonKrylov_Destroy
Here is the call graph for this function:

Variable Documentation

◆ frozen_momentum_point_block_ops

const MomentumPreconditionerModelOps frozen_momentum_point_block_ops
static
Initial value:
= {
}
static PetscErrorCode FrozenMomentumJacobian_DescribePointBlock(UserCtx *user, MomentumPreconditionerDescription *description)
Describes the audited frozen-coefficient point-block model.
static PetscErrorCode FrozenMomentumJacobian_AssemblePointBlocks(UserCtx *user, Vec current_solution, Mat preconditioning_matrix)
Inserts only the audited interior frozen-momentum point blocks.

Definition at line 711 of file momentum_newton_krylov.c.