PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Macros | Functions | Variables
test_momentum_newton_krylov.c File Reference

Focused tests for the version-one matrix-free momentum solver. More...

#include "test_support.h"
#include "initialcondition.h"
#include "../../src/momentum_newton_krylov.c"
Include dependency graph for test_momentum_newton_krylov.c:

Go to the source code of this file.

Macros

#define MomentumSolver_NewtonKrylov   MomentumSolver_NewtonKrylov_PrivateCopy
 
#define MomentumSolver_NewtonKrylov_Destroy   MomentumSolver_NewtonKrylov_Destroy_PrivateCopy
 

Functions

static PetscErrorCode AssertNewtonLog (const char *path, PetscInt expected_rows, const char *needle_a, const char *needle_b)
 Checks a structured log's row count and required text after a collective solve.
 
static PetscErrorCode BuildNewtonFixture (const char *bcs, SimCtx **simCtx, UserCtx **user, char *tmpdir, size_t tmpdir_len)
 Builds and initializes a small runtime context for Newton tests.
 
static PetscErrorCode DestroyNewtonFixture (SimCtx **simCtx, char *tmpdir)
 Destroys a Newton test fixture and its temporary files.
 
static PetscErrorCode WriteNewtonPicSlice (const char *path)
 Writes one static 5x5 PICSLICE profile used by the full runtime fixture.
 
static PetscErrorCode CheckResidualRepeatabilityForBC (const char *bcs, const char *label)
 Checks callback repeatability, diagnostic-state independence, and X integrity.
 
static PetscBool OwnsStoredPoint (UserCtx *user, PetscInt i, PetscInt j, PetscInt k)
 Returns whether this rank owns one global DMDA grid point.
 
static PetscErrorCode PerturbStoredValue (UserCtx *user, Vec vec, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscScalar delta)
 Adds a scalar perturbation to one stored staggered component.
 
static PetscErrorCode GetStoredValue (UserCtx *user, Vec vec, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscScalar *value)
 Reads one globally indexed stored component on any MPI decomposition.
 
static PetscErrorCode MeasureStoredDerivative (UserCtx *user, Vec x, PetscInt row_i, PetscInt row_j, PetscInt row_k, PetscInt row_component, PetscInt col_i, PetscInt col_j, PetscInt col_k, PetscInt col_component, PetscReal *derivative)
 Finite-differences one callback row with respect to one stored unknown.
 
static PetscErrorCode CheckStoredDerivative (UserCtx *user, Vec x, PetscInt row_i, PetscInt row_j, PetscInt row_k, PetscInt row_component, PetscInt col_i, PetscInt col_j, PetscInt col_k, PetscInt col_component, PetscReal expected, PetscReal tolerance, const char *label)
 Asserts one finite-differenced callback row derivative equals an expected value.
 
static PetscErrorCode TestResidualRepeatabilityAndInputIntegrity (void)
 Verifies repeatable callback output and read-only trial input.
 
static PetscErrorCode TestConstraintRows (void)
 Verifies fixed, periodic-duplicate, and interior residual rows.
 
static PetscErrorCode TestFixedConstraintDerivativesAllFaces (void)
 Proves unit derivatives for every nonperiodic stored-row category and face.
 
static PetscErrorCode TestInletOutletConstraintDerivatives (void)
 Proves admitted inlet and outlet face-normal rows have unit self derivatives.
 
static PetscErrorCode CheckSingleAxisPeriodicDerivatives (const char *bcs, PetscInt axis)
 Checks one periodic configuration's endpoint derivatives on every component.
 
static PetscErrorCode TestPeriodicConstraintDerivativesAndIntersections (void)
 Proves single-, double-, triple-, and mixed-boundary periodic equations.
 
static PetscErrorCode TestMatrixFreeDerivative (void)
 Compares PETSc's matrix-free action with direct differencing.
 
static PetscErrorCode BuildMinimalWallOperatorFixture (SimCtx **simCtx, UserCtx **user, PetscBool x_periodic)
 Builds a compact all-wall operator fixture through real boundary handlers.
 
static PetscErrorCode TestFreeSlipWallsSolve (void)
 A Newton solve with free-slip walls converges, preconditioned and not.
 
static PetscErrorCode TestTwoDSolveHoldsMaskedComponent (void)
 A TwoD solve converges, preconditioned and not, and holds the masked component.
 
static PetscErrorCode TestWholeOperatorDirectJacobian (void)
 Forms the complete direct FD Jacobian, checks every row, and compares MFFD actions.
 
static PetscErrorCode TestPeriodicOperatorHasNoZeroRows (void)
 Audits every row of a complete operator containing periodic duplicates.
 
static PetscErrorCode TestSmallSolveAndRollback (void)
 Exercises a converged solve, a committed unconverged solve, a non-finite rollback, and cleanup.
 
static PetscErrorCode TestZeroIterationStructuredLogging (void)
 Verifies the six-wall zero-velocity case logs zero Newton/Krylov work.
 
static PetscErrorCode CheckFlatChannelStartup (PetscBool use_point_block)
 Exercises the straight-duct BDF1 startup path used by flat_channel.
 
static PetscErrorCode TestFlatChannelStartup (void)
 Guards flat_channel's initial BDF1 residual and both shipped NK PCs.
 
static PetscErrorCode TestRestartAndContinuationSolve (void)
 Verifies restarted Newton solves with both supported preconditioners.
 
static PetscErrorCode TestUnsupportedConfigurationFailsBeforeAllocation (void)
 Confirms unsupported features fail before workspace allocation.
 
static PetscErrorCode TestPostAllocationFailureCleanup (void)
 Verifies cleanup and rollback after an options failure following asset creation.
 
static PetscErrorCode TestLinearizationConfigParsing (void)
 Verifies finalized application-owned linearization option parsing.
 
static PetscErrorCode TestStencilToGlobal (UserCtx *user, MatStencil stencil, PetscInt *global_index)
 Converts an in-domain or periodic-ghost DMDA stencil to PETSc ordering.
 
static PetscErrorCode PreconditionerMatrixStencilEntry (UserCtx *user, Mat preconditioning_matrix, MatStencil row, MatStencil col, PetscScalar *value)
 Reads one DMDA-stencil matrix entry through collective basis vectors.
 
static PetscErrorCode AssertExactPointBlockMatrixAllocation (UserCtx *user, Mat matrix, PetscBool require_offrank_periodic)
 Verifies the exact AIJ layout and preallocation derived from row classes.
 
static PetscErrorCode TestPointBlockPreconditionerEngine (void)
 Verifies the point-block model and common preconditioning-engine wiring.
 
static PetscErrorCode AssertPeriodicPreconditionerRow (UserCtx *user, Mat matrix, MatStencil row, const char *message)
 Proves that one periodic matrix row has only its exact +1/-1 pair.
 
static PetscErrorCode TestJacobianInterfaceAndBaselineAlias (void)
 Verifies Jacobian creation/registration and baseline alias ownership.
 
static PetscErrorCode TestPointBlockPeriodicAssembly (void)
 Exercises engine-owned periodic duplicate rows on every MPI layout.
 
int main (int argc, char **argv)
 Runs the focused Newton–Krylov unit suite.
 

Variables

static const char * geometric_periodic_bcs
 
static const char * free_slip_wall_bcs
 
static const char * fixed_wall_bcs
 
static const char * parabolic_bcs
 
static const char * periodic_x_bcs
 
static const char * periodic_y_bcs
 
static const char * periodic_z_bcs
 
static const char * periodic_xy_bcs
 
static const char * periodic_xyz_bcs
 

Detailed Description

Focused tests for the version-one matrix-free momentum solver.

The implementation is included intentionally: its callback helpers remain private in production while this translation unit can verify them directly.

Definition in file test_momentum_newton_krylov.c.

Macro Definition Documentation

◆ MomentumSolver_NewtonKrylov

#define MomentumSolver_NewtonKrylov   MomentumSolver_NewtonKrylov_PrivateCopy

Definition at line 14 of file test_momentum_newton_krylov.c.

◆ MomentumSolver_NewtonKrylov_Destroy

#define MomentumSolver_NewtonKrylov_Destroy   MomentumSolver_NewtonKrylov_Destroy_PrivateCopy

Definition at line 15 of file test_momentum_newton_krylov.c.

Function Documentation

◆ AssertNewtonLog()

static PetscErrorCode AssertNewtonLog ( const char *  path,
PetscInt  expected_rows,
const char *  needle_a,
const char *  needle_b 
)
static

Checks a structured log's row count and required text after a collective solve.

Definition at line 77 of file test_momentum_newton_krylov.c.

79{
80 FILE *file = NULL;
81 char line[4096];
82 PetscInt rows = 0;
83 PetscBool found_a = needle_a ? PETSC_FALSE : PETSC_TRUE;
84 PetscBool found_b = needle_b ? PETSC_FALSE : PETSC_TRUE;
85
86 PetscFunctionBeginUser;
87 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
88 file = fopen(path, "r");
89 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
90 "Expected Newton log does not exist: %s", path);
91 while (fgets(line, sizeof(line), file)) {
92 if (line[0] >= '0' && line[0] <= '9') rows++; /* CSV data rows; the header and comments are not */
93 if (needle_a && strstr(line, needle_a)) found_a = PETSC_TRUE;
94 if (needle_b && strstr(line, needle_b)) found_b = PETSC_TRUE;
95 }
96 fclose(file);
97 if (expected_rows >= 0) {
98 PetscCall(PicurvAssertIntEqual(expected_rows, rows,
99 "Newton log must contain one nonduplicated row per solve"));
100 } else {
101 PetscCall(PicurvAssertBool((PetscBool)(rows >= -expected_rows),
102 "enabled Newton history must contain the expected iteration rows"));
103 }
104 if (!found_a || !found_b) {
105 PetscCall(PetscPrintf(PETSC_COMM_SELF, "%s lacks '%s'\n", path, !found_a ? needle_a : needle_b));
106 }
107 PetscCall(PicurvAssertBool(found_a, "Newton log is missing required structured content"));
108 PetscCall(PicurvAssertBool(found_b, "Newton log is missing required structured content"));
109 PetscFunctionReturn(PETSC_SUCCESS);
110}
PetscErrorCode PicurvAssertIntEqual(PetscInt expected, PetscInt actual, const char *context)
Asserts that two integer values are equal.
PetscErrorCode PicurvAssertBool(PetscBool value, const char *context)
Asserts that one boolean condition is true.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ BuildNewtonFixture()

static PetscErrorCode BuildNewtonFixture ( const char *  bcs,
SimCtx **  simCtx,
UserCtx **  user,
char *  tmpdir,
size_t  tmpdir_len 
)
static

Builds and initializes a small runtime context for Newton tests.

Parameters
bcsOptional boundary configuration text.
simCtxReturned simulation context.
userReturned finest-level block context.
tmpdirReturned temporary directory.
tmpdir_lenCapacity of tmpdir.
Returns
PetscErrorCode 0 on success.

Definition at line 121 of file test_momentum_newton_krylov.c.

123{
124 PetscFunctionBeginUser;
125 PetscCall(PicurvBuildTinyRuntimeContext(bcs, PETSC_FALSE, simCtx, user, tmpdir, tmpdir_len));
126 PetscCall(InitializeEulerianState(*simCtx));
127 (*simCtx)->mom_solver_type = MOMENTUM_SOLVER_NEWTON_KRYLOV;
128 PetscCall(PicurvAssertBool((PetscBool)((*user)->Rhs == NULL),
129 "Newton fixture must enter with no persistent Rhs workspace"));
130 PetscFunctionReturn(PETSC_SUCCESS);
131}
PetscErrorCode InitializeEulerianState(SimCtx *simCtx)
High-level orchestrator to set the complete initial state of the Eulerian solver.
PetscErrorCode PicurvBuildTinyRuntimeContext(const char *bcs_contents, PetscBool enable_particles, SimCtx **simCtx_out, UserCtx **user_out, char *tmpdir, size_t tmpdir_len)
Builds a tiny runtime context through the real setup path for behavior-level tests.
@ MOMENTUM_SOLVER_NEWTON_KRYLOV
Definition variables.h:732
Here is the call graph for this function:
Here is the caller graph for this function:

◆ DestroyNewtonFixture()

static PetscErrorCode DestroyNewtonFixture ( SimCtx **  simCtx,
char *  tmpdir 
)
static

Destroys a Newton test fixture and its temporary files.

Parameters
simCtxFixture simulation context.
tmpdirFixture temporary directory.
Returns
PetscErrorCode 0 on success.

Definition at line 139 of file test_momentum_newton_krylov.c.

140{
141 PetscFunctionBeginUser;
142 PetscCall(PicurvDestroyRuntimeContext(simCtx));
143 PetscCall(PicurvRemoveTempDir(tmpdir));
144 PetscFunctionReturn(PETSC_SUCCESS);
145}
PetscErrorCode PicurvDestroyRuntimeContext(SimCtx **simCtx_ptr)
Finalizes and frees a runtime context built by PicurvBuildTinyRuntimeContext.
PetscErrorCode PicurvRemoveTempDir(const char *path)
Recursively removes a temporary directory created by PicurvMakeTempDir.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteNewtonPicSlice()

static PetscErrorCode WriteNewtonPicSlice ( const char *  path)
static

Writes one static 5x5 PICSLICE profile used by the full runtime fixture.

Parameters
pathOutput profile path.
Returns
PetscErrorCode 0 on success.

Definition at line 152 of file test_momentum_newton_krylov.c.

153{
154 FILE *fd = NULL;
155
156 PetscFunctionBeginUser;
157 fd = fopen(path, "w");
158 PetscCheck(fd != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
159 "Could not create Newton test PICSLICE %s.", path);
160 PetscCheck(fprintf(fd, "PICSLICE\n1\n5 5\n") >= 0,
161 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE, "Could not write PICSLICE header.");
162 for (PetscInt row = 0; row < 25; ++row) {
163 PetscCheck(fprintf(fd, "%.16e\n", 1.0 + 0.01 * (double)row) >= 0,
164 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE, "Could not write PICSLICE value.");
165 }
166 fclose(fd);
167 PetscFunctionReturn(PETSC_SUCCESS);
168}
Here is the caller graph for this function:

◆ CheckResidualRepeatabilityForBC()

static PetscErrorCode CheckResidualRepeatabilityForBC ( const char *  bcs,
const char *  label 
)
static

Checks callback repeatability, diagnostic-state independence, and X integrity.

Parameters
bcsBoundary configuration text, or NULL for the standard inlet/outlet fixture.
labelConfiguration label used in assertion diagnostics.
Returns
PetscErrorCode 0 on success.

Definition at line 176 of file test_momentum_newton_krylov.c.

177{
178 SimCtx *simCtx = NULL;
179 UserCtx *user = NULL;
180 char tmpdir[PETSC_MAX_PATH_LEN] = "";
181 Vec x = NULL, x_copy = NULL, f1 = NULL, f2 = NULL, delta = NULL;
182 PetscReal norm = 0.0;
184
185 PetscFunctionBeginUser;
186 PetscCall(BuildNewtonFixture(bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
187 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
188 PetscCall(VecDuplicate(user->Ucont, &x));
189 PetscCall(VecDuplicate(user->Ucont, &x_copy));
190 PetscCall(VecDuplicate(user->Ucont, &f1));
191 PetscCall(VecDuplicate(user->Ucont, &f2));
192 PetscCall(VecDuplicate(user->Ucont, &delta));
193 PetscCall(VecCopy(user->Ucont, x));
194 PetscCall(VecShift(x, 0.125));
195 PetscCall(VecCopy(x, x_copy));
196 ctx.user = user;
197
198 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f1, &ctx));
199 /* Poison every piece of hidden state a non-deterministic residual could lean
200 * on: the boundary flux/area diagnostics AND the persistent Cartesian fields
201 * (Ucat/lUcat). The conservation-outlet handler reads lUcat during its first
202 * boundary sweep, so a callback that does not reconstruct the Cartesian state
203 * from X before applying boundary conditions would produce a different F here.
204 * This assertion therefore fails if the deterministic pre-boundary seed in
205 * MomentumNewtonKrylov_FormResidual() is ever removed. */
206 simCtx->FluxInSum = 1234.0;
207 simCtx->FluxOutSum = -4321.0;
208 simCtx->FarFluxInSum = 77.0;
209 simCtx->FarFluxOutSum = -88.0;
210 PetscCall(VecSet(user->Ucat, 7.0));
211 PetscCall(VecSet(user->lUcat, 7.0));
212 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f2, &ctx));
213 PetscCall(VecWAXPY(delta, -1.0, f1, f2));
214 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
215 PetscCheck(norm <= 1.0e-13, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
216 "%s residual changed between identical evaluations (inf norm=%g).", label, (double)norm);
217 PetscCall(VecNorm(delta, NORM_2, &norm));
218 PetscCheck(norm <= 1.0e-12, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
219 "%s residual changed between identical evaluations (L2 norm=%g).", label, (double)norm);
220 PetscCall(VecWAXPY(delta, -1.0, x_copy, x));
221 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
222 PetscCheck(norm == 0.0, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
223 "%s residual callback modified X (norm=%g).", label, (double)norm);
224
225 PetscCall(VecDestroy(&delta));
226 PetscCall(VecDestroy(&f2));
227 PetscCall(VecDestroy(&f1));
228 PetscCall(VecDestroy(&x_copy));
229 PetscCall(VecDestroy(&x));
230 PetscCall(VecDestroy(&user->Rhs));
231 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
232 PetscFunctionReturn(PETSC_SUCCESS);
233}
static PetscErrorCode MomentumNewtonKrylov_FormResidual(SNES snes, Vec X, Vec F, void *ctx)
Adapts a PETSc trial vector to the existing momentum residual path.
static PetscErrorCode BuildNewtonFixture(const char *bcs, SimCtx **simCtx, UserCtx **user, char *tmpdir, size_t tmpdir_len)
Builds and initializes a small runtime context for Newton tests.
static PetscErrorCode DestroyNewtonFixture(SimCtx **simCtx, char *tmpdir)
Destroys a Newton test fixture and its temporary files.
PetscReal FarFluxInSum
Definition variables.h:1016
PetscReal FarFluxOutSum
Definition variables.h:1016
PetscReal FluxOutSum
Definition variables.h:1016
Vec Ucont
Definition variables.h:1197
PetscReal FluxInSum
Definition variables.h:1016
Vec lUcat
Definition variables.h:1197
The master context for the entire simulation.
Definition variables.h:916
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:

◆ OwnsStoredPoint()

static PetscBool OwnsStoredPoint ( UserCtx *  user,
PetscInt  i,
PetscInt  j,
PetscInt  k 
)
static

Returns whether this rank owns one global DMDA grid point.

Definition at line 236 of file test_momentum_newton_krylov.c.

237{
238 return (PetscBool)(i >= user->info.xs && i < user->info.xs + user->info.xm &&
239 j >= user->info.ys && j < user->info.ys + user->info.ym &&
240 k >= user->info.zs && k < user->info.zs + user->info.zm);
241}
DMDALocalInfo info
Definition variables.h:1169
Here is the caller graph for this function:

◆ PerturbStoredValue()

static PetscErrorCode PerturbStoredValue ( UserCtx *  user,
Vec  vec,
PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscInt  component,
PetscScalar  delta 
)
static

Adds a scalar perturbation to one stored staggered component.

Parameters
userBlock context defining vector ownership.
vecVector to modify.
iGlobal i index.
jGlobal j index.
kGlobal k index.
componentComponent 0=x, 1=y, 2=z.
deltaIncrement to apply.
Returns
PetscErrorCode 0 on success.

Definition at line 254 of file test_momentum_newton_krylov.c.

256{
257 Cmpnts ***a = NULL;
258
259 PetscFunctionBeginUser;
260 if (OwnsStoredPoint(user, i, j, k)) {
261 PetscCall(DMDAVecGetArray(user->fda, vec, &a));
262 if (component == 0) a[k][j][i].x += delta;
263 else if (component == 1) a[k][j][i].y += delta;
264 else a[k][j][i].z += delta;
265 PetscCall(DMDAVecRestoreArray(user->fda, vec, &a));
266 }
267 PetscFunctionReturn(PETSC_SUCCESS);
268}
static PetscBool OwnsStoredPoint(UserCtx *user, PetscInt i, PetscInt j, PetscInt k)
Returns whether this rank owns one global DMDA grid point.
PetscScalar x
Definition variables.h:145
PetscScalar z
Definition variables.h:145
PetscScalar y
Definition variables.h:145
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:

◆ GetStoredValue()

static PetscErrorCode GetStoredValue ( UserCtx *  user,
Vec  vec,
PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscInt  component,
PetscScalar *  value 
)
static

Reads one globally indexed stored component on any MPI decomposition.

Parameters
userBlock context defining vector ownership.
vecVector to inspect.
iGlobal i index.
jGlobal j index.
kGlobal k index.
componentComponent 0=x, 1=y, 2=z.
valueReturned globally reduced scalar.
Returns
PetscErrorCode 0 on success.

Definition at line 281 of file test_momentum_newton_krylov.c.

283{
284 Cmpnts ***a = NULL;
285 PetscScalar local = 0.0;
286
287 PetscFunctionBeginUser;
288 if (OwnsStoredPoint(user, i, j, k)) {
289 PetscCall(DMDAVecGetArrayRead(user->fda, vec, &a));
290 local = component == 0 ? a[k][j][i].x : (component == 1 ? a[k][j][i].y : a[k][j][i].z);
291 PetscCall(DMDAVecRestoreArrayRead(user->fda, vec, &a));
292 }
293 PetscCallMPI(MPI_Allreduce(&local, value, 1, MPIU_SCALAR, MPIU_SUM, PETSC_COMM_WORLD));
294 PetscFunctionReturn(PETSC_SUCCESS);
295}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MeasureStoredDerivative()

static PetscErrorCode MeasureStoredDerivative ( UserCtx *  user,
Vec  x,
PetscInt  row_i,
PetscInt  row_j,
PetscInt  row_k,
PetscInt  row_component,
PetscInt  col_i,
PetscInt  col_j,
PetscInt  col_k,
PetscInt  col_component,
PetscReal *  derivative 
)
static

Finite-differences one callback row with respect to one stored unknown.

Parameters
userActive block context with allocated Rhs.
xBase trial vector.
row_iResidual-row i index.
row_jResidual-row j index.
row_kResidual-row k index.
row_componentResidual-row component.
col_iPerturbed unknown i index.
col_jPerturbed unknown j index.
col_kPerturbed unknown k index.
col_componentPerturbed unknown component.
derivativeReturned finite-difference derivative of the row w.r.t. the unknown.
Returns
PetscErrorCode 0 on success.

Definition at line 312 of file test_momentum_newton_krylov.c.

317{
318 const PetscReal epsilon = 1.0e-6;
319 Vec f0 = NULL, fp = NULL, xp = NULL;
320 PetscScalar base_value = 0.0, perturbed_value = 0.0;
321 MomentumNewtonKrylovContext ctx = {user};
322
323 PetscFunctionBeginUser;
324 PetscCall(VecDuplicate(x, &f0));
325 PetscCall(VecDuplicate(x, &fp));
326 PetscCall(VecDuplicate(x, &xp));
327 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
328 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
329 PetscCall(VecCopy(x, xp));
330 PetscCall(PerturbStoredValue(user, xp, col_i, col_j, col_k, col_component, epsilon));
331 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, xp, fp, &ctx));
332 PetscCall(GetStoredValue(user, f0, row_i, row_j, row_k, row_component, &base_value));
333 PetscCall(GetStoredValue(user, fp, row_i, row_j, row_k, row_component, &perturbed_value));
334 *derivative = PetscRealPart((perturbed_value - base_value) / epsilon);
335 PetscCall(VecDestroy(&xp));
336 PetscCall(VecDestroy(&fp));
337 PetscCall(VecDestroy(&f0));
338 PetscFunctionReturn(PETSC_SUCCESS);
339}
static PetscErrorCode GetStoredValue(UserCtx *user, Vec vec, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscScalar *value)
Reads one globally indexed stored component on any MPI decomposition.
static PetscErrorCode PerturbStoredValue(UserCtx *user, Vec vec, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscScalar delta)
Adds a scalar perturbation to one stored staggered component.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CheckStoredDerivative()

static PetscErrorCode CheckStoredDerivative ( UserCtx *  user,
Vec  x,
PetscInt  row_i,
PetscInt  row_j,
PetscInt  row_k,
PetscInt  row_component,
PetscInt  col_i,
PetscInt  col_j,
PetscInt  col_k,
PetscInt  col_component,
PetscReal  expected,
PetscReal  tolerance,
const char *  label 
)
static

Asserts one finite-differenced callback row derivative equals an expected value.

Parameters
userActive block context with allocated Rhs.
xBase trial vector.
row_iResidual-row i index.
row_jResidual-row j index.
row_kResidual-row k index.
row_componentResidual-row component.
col_iPerturbed unknown i index.
col_jPerturbed unknown j index.
col_kPerturbed unknown k index.
col_componentPerturbed unknown component.
expectedExpected derivative.
toleranceAbsolute derivative tolerance.
labelAssertion label.
Returns
PetscErrorCode 0 on success.

Definition at line 358 of file test_momentum_newton_krylov.c.

363{
364 PetscReal derivative = 0.0;
365
366 PetscFunctionBeginUser;
367 PetscCall(MeasureStoredDerivative(user, x, row_i, row_j, row_k, row_component,
368 col_i, col_j, col_k, col_component, &derivative));
369 PetscCall(PicurvAssertRealNear(expected, derivative, tolerance, label));
370 PetscFunctionReturn(PETSC_SUCCESS);
371}
static PetscErrorCode MeasureStoredDerivative(UserCtx *user, Vec x, PetscInt row_i, PetscInt row_j, PetscInt row_k, PetscInt row_component, PetscInt col_i, PetscInt col_j, PetscInt col_k, PetscInt col_component, PetscReal *derivative)
Finite-differences one callback row with respect to one stored unknown.
PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestResidualRepeatabilityAndInputIntegrity()

static PetscErrorCode TestResidualRepeatabilityAndInputIntegrity ( void  )
static

Verifies repeatable callback output and read-only trial input.

Definition at line 374 of file test_momentum_newton_krylov.c.

375{
376 char profile_dir[PETSC_MAX_PATH_LEN] = "";
377 char profile_path[PETSC_MAX_PATH_LEN];
378 char file_bcs[2 * PETSC_MAX_PATH_LEN];
379
380 PetscFunctionBeginUser;
381 PetscCall(CheckResidualRepeatabilityForBC(fixed_wall_bcs, "fixed walls"));
382 PetscCall(CheckResidualRepeatabilityForBC(free_slip_wall_bcs, "free-slip walls"));
383 PetscCall(CheckResidualRepeatabilityForBC(NULL, "constant inlet/conservation outlet"));
384 PetscCall(CheckResidualRepeatabilityForBC(parabolic_bcs, "parabolic inlet/conservation outlet"));
385 PetscCall(CheckResidualRepeatabilityForBC(periodic_x_bcs, "x periodic"));
386 PetscCall(CheckResidualRepeatabilityForBC(periodic_y_bcs, "y periodic"));
387 PetscCall(CheckResidualRepeatabilityForBC(periodic_z_bcs, "z periodic"));
388 PetscCall(CheckResidualRepeatabilityForBC(periodic_xy_bcs, "mixed x-y periodic"));
389
390 PetscCall(PicurvMakeTempDir(profile_dir, sizeof(profile_dir)));
391 PetscCall(PetscSNPrintf(profile_path, sizeof(profile_path), "%s/inlet.picslice", profile_dir));
392 PetscCall(WriteNewtonPicSlice(profile_path));
393 PetscCall(PetscSNPrintf(file_bcs, sizeof(file_bcs),
394 "-Xi WALL noslip\n+Xi WALL noslip\n-Eta WALL noslip\n+Eta WALL noslip\n"
395 "-Zeta INLET prescribed_velocity source_id=2 source_file=%s\n+Zeta OUTLET conservation\n", profile_path));
396 PetscCall(CheckResidualRepeatabilityForBC(file_bcs, "file inlet/conservation outlet"));
397 PetscCall(PicurvRemoveTempDir(profile_dir));
398 PetscFunctionReturn(PETSC_SUCCESS);
399}
static const char * fixed_wall_bcs
static const char * periodic_xy_bcs
static const char * periodic_x_bcs
static const char * periodic_y_bcs
static const char * periodic_z_bcs
static PetscErrorCode WriteNewtonPicSlice(const char *path)
Writes one static 5x5 PICSLICE profile used by the full runtime fixture.
static const char * parabolic_bcs
static const char * free_slip_wall_bcs
static PetscErrorCode CheckResidualRepeatabilityForBC(const char *bcs, const char *label)
Checks callback repeatability, diagnostic-state independence, and X integrity.
PetscErrorCode PicurvMakeTempDir(char *path, size_t path_len)
Creates a unique temporary directory for one test case.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestConstraintRows()

static PetscErrorCode TestConstraintRows ( void  )
static

Verifies fixed, periodic-duplicate, and interior residual rows.

Definition at line 402 of file test_momentum_newton_krylov.c.

403{
404 SimCtx *simCtx = NULL;
405 UserCtx *user = NULL;
406 char tmpdir[PETSC_MAX_PATH_LEN] = "";
407 Vec x = NULL, f = NULL;
408 Cmpnts ***xa = NULL, ***fa = NULL, ***conditioned = NULL, ***rhs = NULL;
410
411 PetscFunctionBeginUser;
412 PetscCall(BuildNewtonFixture(geometric_periodic_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
413 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
414 PetscCall(VecDuplicate(user->Ucont, &x));
415 PetscCall(VecDuplicate(user->Ucont, &f));
416 PetscCall(VecSet(x, 0.25));
417 PetscCall(DMDAVecGetArray(user->fda, x, &xa));
418 if (user->info.xs == 0 && 2 >= user->info.ys && 2 < user->info.ys + user->info.ym &&
419 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) xa[2][2][0].x = 3.0;
420 if (user->info.xs + user->info.xm == user->info.mx &&
421 2 >= user->info.ys && 2 < user->info.ys + user->info.ym &&
422 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) xa[2][2][user->info.mx - 2].x = 1.25;
423 PetscCall(DMDAVecRestoreArray(user->fda, x, &xa));
424 ctx.user = user;
425 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
426
427 PetscCall(DMDAVecGetArrayRead(user->fda, x, &xa));
428 PetscCall(DMDAVecGetArrayRead(user->fda, f, &fa));
429 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &conditioned));
430 PetscCall(DMDAVecGetArrayRead(user->fda, user->Rhs, &rhs));
431 if (user->info.xs == 0 && 2 >= user->info.ys && 2 < user->info.ys + user->info.ym &&
432 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) {
433 PetscCall(PicurvAssertRealNear(1.75, fa[2][2][0].x, 1.0e-12,
434 "periodic duplicate row must be Xdup-Xrep"));
435 }
436 if (user->info.ys == 0 && 2 >= user->info.xs && 2 < user->info.xs + user->info.xm &&
437 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) {
438 PetscCall(PicurvAssertRealNear(xa[2][0][2].y - conditioned[2][0][2].y,
439 fa[2][0][2].y, 1.0e-12,
440 "fixed wall row must be X minus conditioned boundary value"));
441 }
442 if (2 >= user->info.xs && 2 < user->info.xs + user->info.xm &&
443 2 >= user->info.ys && 2 < user->info.ys + user->info.ym &&
444 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) {
445 PetscCall(PicurvAssertRealNear(-rhs[2][2][2].z, fa[2][2][2].z, 1.0e-12,
446 "unconstrained interior row must retain the physical residual"));
447 }
448 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Rhs, &rhs));
449 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucont, &conditioned));
450 PetscCall(DMDAVecRestoreArrayRead(user->fda, f, &fa));
451 PetscCall(DMDAVecRestoreArrayRead(user->fda, x, &xa));
452
453 PetscCall(VecDestroy(&f));
454 PetscCall(VecDestroy(&x));
455 PetscCall(VecDestroy(&user->Rhs));
456 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
457 PetscFunctionReturn(PETSC_SUCCESS);
458}
static const char * geometric_periodic_bcs
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestFixedConstraintDerivativesAllFaces()

static PetscErrorCode TestFixedConstraintDerivativesAllFaces ( void  )
static

Proves unit derivatives for every nonperiodic stored-row category and face.

Definition at line 461 of file test_momentum_newton_krylov.c.

462{
463 SimCtx *simCtx = NULL;
464 UserCtx *user = NULL;
465 char tmpdir[PETSC_MAX_PATH_LEN] = "";
466 Vec x = NULL;
467 const PetscInt size[3] = {7, 7, 7};
468
469 PetscFunctionBeginUser;
470 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
471 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
472 PetscCall(VecDuplicate(user->Ucont, &x));
473 PetscCall(VecSet(x, 0.2));
474
475 for (PetscInt axis = 0; axis < 3; ++axis) {
476 PetscInt coord[3] = {2, 2, 2}, ri, rj, rk;
477 PetscInt tangent = (axis + 1) % 3;
478 MomentumRowType row;
479
480 coord[axis] = 0;
481 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], axis, &ri, &rj, &rk);
483 "negative face normal row classification"));
484 PetscCall(CheckStoredDerivative(user, x, coord[0], coord[1], coord[2], axis,
485 coord[0], coord[1], coord[2], axis, 1.0, 1.0e-8,
486 "negative face normal fixed derivative"));
487
488 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], tangent, &ri, &rj, &rk);
490 "negative face tangential row classification"));
491 PetscCall(CheckStoredDerivative(user, x, coord[0], coord[1], coord[2], tangent,
492 coord[0], coord[1], coord[2], tangent, 1.0, 1.0e-8,
493 "negative face tangential homogeneous derivative"));
494
495 coord[axis] = size[axis] - 2;
496 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], axis, &ri, &rj, &rk);
498 "positive physical normal row classification"));
499 PetscCall(CheckStoredDerivative(user, x, coord[0], coord[1], coord[2], axis,
500 coord[0], coord[1], coord[2], axis, 1.0, 1.0e-8,
501 "positive physical normal fixed derivative"));
502 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], tangent, &ri, &rj, &rk);
504 "positive physical tangential row classification"));
505
506 coord[axis] = size[axis] - 1;
507 for (PetscInt component = 0; component < 3; ++component) {
508 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], component, &ri, &rj, &rk);
510 "positive dummy row classification"));
511 PetscCall(CheckStoredDerivative(user, x, coord[0], coord[1], coord[2], component,
512 coord[0], coord[1], coord[2], component, 1.0, 1.0e-8,
513 "positive dummy homogeneous derivative"));
514 }
515 }
516
517 PetscCall(VecDestroy(&x));
518 PetscCall(VecDestroy(&user->Rhs));
519 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
520 PetscFunctionReturn(PETSC_SUCCESS);
521}
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_PHYSICAL
Independent unknown governed by the momentum equation.
Definition Boundaries.h:192
@ 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
static PetscErrorCode CheckStoredDerivative(UserCtx *user, Vec x, PetscInt row_i, PetscInt row_j, PetscInt row_k, PetscInt row_component, PetscInt col_i, PetscInt col_j, PetscInt col_k, PetscInt col_component, PetscReal expected, PetscReal tolerance, const char *label)
Asserts one finite-differenced callback row derivative equals an expected value.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestInletOutletConstraintDerivatives()

static PetscErrorCode TestInletOutletConstraintDerivatives ( void  )
static

Proves admitted inlet and outlet face-normal rows have unit self derivatives.

Definition at line 524 of file test_momentum_newton_krylov.c.

525{
526 SimCtx *simCtx = NULL;
527 UserCtx *user = NULL;
528 char tmpdir[PETSC_MAX_PATH_LEN] = "";
529 Vec x = NULL;
530
531 PetscFunctionBeginUser;
532 PetscCall(BuildNewtonFixture(NULL, &simCtx, &user, tmpdir, sizeof(tmpdir)));
533 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
534 PetscCall(VecDuplicate(user->Ucont, &x));
535 PetscCall(VecCopy(user->Ucont, x));
536 PetscCall(VecShift(x, 0.1));
537 PetscCall(CheckStoredDerivative(user, x, 2, 2, 0, 2, 2, 2, 0, 2,
538 1.0, 1.0e-8, "constant inlet fixed derivative"));
539 /* The constant-velocity inlet imposes a value independent of X, so its
540 * conditioned row F = X - cv has an exact unit self derivative.
541 *
542 * The conservation outlet is different: cv is the corrected outlet flux,
543 * which the deterministic residual now reconstructs from the current X
544 * (Ucat is seeded from X before the first outlet pass). Perturbing the
545 * outlet-normal DOF therefore changes cv, so the self derivative is
546 * 1 - dcv/dX and is strictly less than one. A self derivative of exactly
547 * 1.0 here was an artifact of the pre-fix residual reading a stale
548 * Cartesian state, i.e. an outlet correction decoupled from X. Assert the
549 * derivative is (a) deterministic across independent evaluations -- the
550 * residual-purity property -- and (b) reflects real conservation coupling
551 * (0 < d < 1), rather than asserting a fixture-specific magic number. */
552 {
553 PetscReal d0 = 0.0, d1 = 0.0;
554 PetscCall(MeasureStoredDerivative(user, x, 2, 2, user->info.mz - 2, 2,
555 2, 2, user->info.mz - 2, 2, &d0));
556 PetscCall(MeasureStoredDerivative(user, x, 2, 2, user->info.mz - 2, 2,
557 2, 2, user->info.mz - 2, 2, &d1));
558 PetscCall(PicurvAssertRealNear(d0, d1, 1.0e-9,
559 "conservation outlet self derivative must be deterministic"));
560 PetscCheck(d0 > 1.0e-3 && d0 < 1.0 - 1.0e-3, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
561 "conservation outlet self derivative must reflect X-coupling (0<d<1), got %g.",
562 (double)d0);
563 }
564 PetscCall(VecDestroy(&x));
565 PetscCall(VecDestroy(&user->Rhs));
566 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
567 PetscFunctionReturn(PETSC_SUCCESS);
568}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CheckSingleAxisPeriodicDerivatives()

static PetscErrorCode CheckSingleAxisPeriodicDerivatives ( const char *  bcs,
PetscInt  axis 
)
static

Checks one periodic configuration's endpoint derivatives on every component.

Parameters
bcsBoundary text selecting the periodic axis.
axisPeriodic axis index.
Returns
PetscErrorCode 0 on success.

Definition at line 576 of file test_momentum_newton_krylov.c.

577{
578 SimCtx *simCtx = NULL;
579 UserCtx *user = NULL;
580 char tmpdir[PETSC_MAX_PATH_LEN] = "";
581 Vec x = NULL;
582 PetscInt size[3], dup[3] = {2, 2, 2}, rep[3] = {2, 2, 2}, unrelated[3] = {3, 3, 3};
583
584 PetscFunctionBeginUser;
585 PetscCall(BuildNewtonFixture(bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
586 size[0] = user->info.mx; size[1] = user->info.my; size[2] = user->info.mz;
587 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
588 PetscCall(VecDuplicate(user->Ucont, &x));
589 PetscCall(VecSet(x, 0.15));
590 for (PetscInt side = 0; side < 2; ++side) {
591 dup[axis] = side == 0 ? 0 : size[axis] - 1;
592 rep[axis] = side == 0 ? size[axis] - 2 : 1;
593 for (PetscInt component = 0; component < 3; ++component) {
594 PetscCall(CheckStoredDerivative(user, x, dup[0], dup[1], dup[2], component,
595 dup[0], dup[1], dup[2], component, 1.0, 1.0e-8,
596 "periodic duplicate self derivative"));
597 PetscCall(CheckStoredDerivative(user, x, dup[0], dup[1], dup[2], component,
598 rep[0], rep[1], rep[2], component, -1.0, 1.0e-8,
599 "periodic representative derivative"));
600 PetscCall(CheckStoredDerivative(user, x, dup[0], dup[1], dup[2], component,
601 unrelated[0], unrelated[1], unrelated[2], (component + 1) % 3, 0.0, 1.0e-8,
602 "periodic constraint unrelated derivative"));
603 }
604 }
605 PetscCall(VecDestroy(&x));
606 PetscCall(VecDestroy(&user->Rhs));
607 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
608 PetscFunctionReturn(PETSC_SUCCESS);
609}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPeriodicConstraintDerivativesAndIntersections()

static PetscErrorCode TestPeriodicConstraintDerivativesAndIntersections ( void  )
static

Proves single-, double-, triple-, and mixed-boundary periodic equations.

Definition at line 612 of file test_momentum_newton_krylov.c.

613{
614 SimCtx *simCtx = NULL;
615 UserCtx *user = NULL;
616 char tmpdir[PETSC_MAX_PATH_LEN] = "";
617 Vec x = NULL, f = NULL;
619 PetscScalar xdup, synced, residual;
620
621 PetscFunctionBeginUser;
625
626 PetscCall(BuildNewtonFixture(periodic_xy_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
627 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
628 PetscCall(VecDuplicate(user->Ucont, &x));
629 PetscCall(VecDuplicate(user->Ucont, &f));
630 PetscCall(VecSet(x, 0.0));
631 PetscCall(PerturbStoredValue(user, x, 0, 0, 2, 0, 3.0));
632 PetscCall(PerturbStoredValue(user, x, user->info.mx - 2, user->info.my - 2, 2, 0, 1.25));
633 PetscCall(VecCopy(x, user->Ucont));
634 { const FieldId fields[] = {FIELD_ID_UCONT}; PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields)); }
635 PetscCall(GetStoredValue(user, x, 0, 0, 2, 0, &xdup));
636 PetscCall(GetStoredValue(user, user->Ucont, 0, 0, 2, 0, &synced));
637 ctx.user = user;
638 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
639 PetscCall(GetStoredValue(user, f, 0, 0, 2, 0, &residual));
640 PetscCall(PicurvAssertRealNear(PetscRealPart(xdup - synced), PetscRealPart(residual), 1.0e-12,
641 "doubly periodic edge must use production synchronized representative"));
642 PetscCall(CheckStoredDerivative(user, x, 0, 0, 2, 0, 0, 0, 2, 0, 1.0, 1.0e-8,
643 "doubly periodic edge self derivative"));
644 PetscCall(CheckStoredDerivative(user, x, 0, 0, 2, 0,
645 user->info.mx - 2, user->info.my - 2, 2, 0, -1.0, 1.0e-8,
646 "doubly periodic edge representative derivative"));
647 PetscCall(VecDestroy(&f)); PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->Rhs));
648 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
649
650 tmpdir[0] = '\0';
651 PetscCall(BuildNewtonFixture(periodic_xyz_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
652 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
653 PetscCall(VecDuplicate(user->Ucont, &x)); PetscCall(VecSet(x, 0.1));
654 PetscCall(CheckStoredDerivative(user, x, 0, 0, 0, 2, 0, 0, 0, 2, 1.0, 1.0e-8,
655 "fully periodic corner self derivative"));
656 PetscCall(CheckStoredDerivative(user, x, 0, 0, 0, 2,
657 user->info.mx - 2, user->info.my - 2, user->info.mz - 2, 2,
658 -1.0, 1.0e-8, "fully periodic corner representative derivative"));
659 PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->Rhs));
660 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
661
662 tmpdir[0] = '\0';
663 PetscCall(BuildNewtonFixture(periodic_x_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
664 PetscCall(VecDuplicate(user->Ucont, &user->Rhs)); PetscCall(VecDuplicate(user->Ucont, &x));
665 PetscCall(VecSet(x, 0.1));
666 PetscCall(CheckStoredDerivative(user, x, 0, 0, 2, 1, 0, 0, 2, 1, 1.0, 1.0e-8,
667 "periodic-wall intersection self derivative"));
668 PetscCall(CheckStoredDerivative(user, x, 0, 0, 2, 1,
669 user->info.mx - 2, 0, 2, 1, -1.0, 1.0e-8,
670 "periodic-wall intersection representative derivative"));
671 PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->Rhs));
672 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
673 PetscFunctionReturn(PETSC_SUCCESS);
674}
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent component-staggered vector fields.
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_UCONT
static const char * periodic_xyz_bcs
static PetscErrorCode CheckSingleAxisPeriodicDerivatives(const char *bcs, PetscInt axis)
Checks one periodic configuration's endpoint derivatives on every component.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestMatrixFreeDerivative()

static PetscErrorCode TestMatrixFreeDerivative ( void  )
static

Compares PETSc's matrix-free action with direct differencing.

Definition at line 677 of file test_momentum_newton_krylov.c.

678{
679 SimCtx *simCtx = NULL;
680 UserCtx *user = NULL;
681 char tmpdir[PETSC_MAX_PATH_LEN] = "";
682 SNES snes = NULL;
683 Mat J = NULL;
684 Vec x = NULL, xp = NULL, f0 = NULL, fp = NULL, v = NULL, jv = NULL, fd = NULL;
685 PetscReal h = 0.0, error = 0.0, scale = 0.0;
687
688 PetscFunctionBeginUser;
689 PetscCall(BuildNewtonFixture(NULL, &simCtx, &user, tmpdir, sizeof(tmpdir)));
690 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
691 PetscCall(VecDuplicate(user->Ucont, &x));
692 PetscCall(VecDuplicate(user->Ucont, &xp));
693 PetscCall(VecDuplicate(user->Ucont, &f0));
694 PetscCall(VecDuplicate(user->Ucont, &fp));
695 PetscCall(VecDuplicate(user->Ucont, &v));
696 PetscCall(VecDuplicate(user->Ucont, &jv));
697 PetscCall(VecDuplicate(user->Ucont, &fd));
698 PetscCall(VecCopy(user->Ucont, x));
699 PetscCall(VecShift(x, 0.05));
700 PetscCall(VecSet(v, 0.5));
701 ctx.user = user;
702
703 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
704 PetscCall(SNESSetDM(snes, user->fda));
705 PetscCall(SNESSetFunction(snes, f0, MomentumNewtonKrylov_FormResidual, &ctx));
706 PetscCall(MatCreateSNESMF(snes, &J));
707 PetscCall(MomentumNewtonKrylov_FormResidual(snes, x, f0, &ctx));
708 PetscCall(MatMFFDSetBase(J, x, f0));
709 PetscCall(MatMult(J, v, jv));
710 PetscCall(MatMFFDGetH(J, &h));
711 PetscCall(VecWAXPY(xp, h, v, x));
712 PetscCall(MomentumNewtonKrylov_FormResidual(snes, xp, fp, &ctx));
713 PetscCall(VecWAXPY(fd, -1.0, f0, fp));
714 PetscCall(VecScale(fd, 1.0 / h));
715 PetscCall(VecAXPY(fd, -1.0, jv));
716 PetscCall(VecNorm(fd, NORM_2, &error));
717 PetscCall(VecNorm(jv, NORM_2, &scale));
718 PetscCall(PicurvAssertBool((PetscBool)(error <= 1.0e-9 * PetscMax(1.0, scale)),
719 "matrix-free Jv must match direct differencing"));
720
721 PetscCall(VecDestroy(&fd));
722 PetscCall(VecDestroy(&jv));
723 PetscCall(VecDestroy(&v));
724 PetscCall(VecDestroy(&fp));
725 PetscCall(VecDestroy(&f0));
726 PetscCall(VecDestroy(&xp));
727 PetscCall(VecDestroy(&x));
728 PetscCall(MatDestroy(&J));
729 PetscCall(SNESDestroy(&snes));
730 PetscCall(VecDestroy(&user->Rhs));
731 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
732 PetscFunctionReturn(PETSC_SUCCESS);
733}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ BuildMinimalWallOperatorFixture()

static PetscErrorCode BuildMinimalWallOperatorFixture ( SimCtx **  simCtx,
UserCtx **  user,
PetscBool  x_periodic 
)
static

Builds a compact all-wall operator fixture through real boundary handlers.

Parameters
simCtxReturned simulation context.
userReturned block context.
x_periodicWhether the x faces use geometric periodicity.
Returns
PetscErrorCode 0 on success.

Definition at line 742 of file test_momentum_newton_krylov.c.

744{
745 PetscFunctionBeginUser;
746 /* Request the production-width (3) DMDA stencil so the complete RHS is safe
747 across MPI partitions; boundary metadata below still selects physical walls. */
749 simCtx, user, 6, 6, 6, PETSC_TRUE, PETSC_TRUE, PETSC_TRUE));
750 (*simCtx)->i_periodic = x_periodic ? 1 : 0;
751 (*simCtx)->j_periodic = (*simCtx)->k_periodic = 0;
752 (*simCtx)->mom_solver_type = MOMENTUM_SOLVER_NEWTON_KRYLOV;
753 (*simCtx)->invicid = 1;
754 (*simCtx)->dt = 0.1;
755 (*simCtx)->step = 1;
756 (*simCtx)->StartStep = 0;
757 PetscCall(VecSet((*user)->Ucont, 0.0));
758 PetscCall(VecSet((*user)->Ucont_o, 0.0));
759 PetscCall(VecSet((*user)->Ucont_rm1, 0.0));
760 for (PetscInt face = 0; face < 6; ++face) {
761 PetscBool periodic_face = (PetscBool)(x_periodic &&
762 (face == BC_FACE_NEG_X || face == BC_FACE_POS_X));
763 (*user)->boundary_faces[face].face_id = (BCFace)face;
764 (*user)->boundary_faces[face].mathematical_type = periodic_face ? PERIODIC : WALL;
765 (*user)->boundary_faces[face].handler_type = periodic_face ?
767 PetscCall(BoundaryCondition_Create((*user)->boundary_faces[face].handler_type,
768 &(*user)->boundary_faces[face].handler));
769 }
770 PetscFunctionReturn(PETSC_SUCCESS);
771}
PetscErrorCode BoundaryCondition_Create(BCHandlerType handler_type, BoundaryCondition **new_bc_ptr)
(Private) Creates and configures a specific BoundaryCondition handler object.
Definition Boundaries.c:265
PetscErrorCode PicurvCreateMinimalContextsWithPeriodicity(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz, PetscBool x_periodic, PetscBool y_periodic, PetscBool z_periodic)
Builds minimal SimCtx and UserCtx fixtures for C unit tests with configurable periodicity.
@ PERIODIC
Definition variables.h:325
@ WALL
Definition variables.h:319
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:352
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:345
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:308
@ BC_FACE_NEG_X
Definition variables.h:309
@ BC_FACE_POS_X
Definition variables.h:309
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestFreeSlipWallsSolve()

static PetscErrorCode TestFreeSlipWallsSolve ( void  )
static

A Newton solve with free-slip walls converges, preconditioned and not.

Free-slip walls are supported by Newton Krylov from the start: the matrix-free action carries the reflected boundary through the residual, and the point-block preconditioner approximates the operator without wall-specific terms.

Definition at line 780 of file test_momentum_newton_krylov.c.

781{
782 const char *models[] = {"none", "frozen_momentum_jacobian"};
783 const char *structures[] = {"none", "point_block"};
784
785 PetscFunctionBeginUser;
786 for (size_t n = 0; n < sizeof(models) / sizeof(models[0]); ++n) {
787 SimCtx *simCtx = NULL;
788 UserCtx *user = NULL;
789 char tmpdir[PETSC_MAX_PATH_LEN] = "";
790
791 PetscCall(BuildNewtonFixture(free_slip_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
792 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_rtol", "1e-4"));
793 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
794 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_ksp_rtol", "1e-6"));
795 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", models[n]));
796 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", structures[n]));
797 PetscCall(VecSet(user->Ucont, 0.1));
798 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
799 PetscCall(PicurvAssertBool(simCtx->mom_last_converged, "a solve with free-slip walls must converge"));
800 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
801 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_model"));
802 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_structure"));
803 }
804 PetscFunctionReturn(PETSC_SUCCESS);
805}
#define MomentumSolver_NewtonKrylov
PetscBool mom_last_converged
Definition variables.h:971
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestTwoDSolveHoldsMaskedComponent()

static PetscErrorCode TestTwoDSolveHoldsMaskedComponent ( void  )
static

A TwoD solve converges, preconditioned and not, and holds the masked component.

ComputeRHS() zeroes the masked component's spatial terms, so its residual row is the time derivative alone. With the same value at every history level, BDF2's solution of that row is the value itself, so a nonzero xi flux must survive the solve unchanged, as the Picard solver's skipped row keeps it.

Definition at line 815 of file test_momentum_newton_krylov.c.

816{
817 const char *models[] = {"none", "frozen_momentum_jacobian"};
818 const char *structures[] = {"none", "point_block"};
819
820 PetscFunctionBeginUser;
821 for (size_t n = 0; n < sizeof(models) / sizeof(models[0]); ++n) {
822 SimCtx *simCtx = NULL;
823 UserCtx *user = NULL;
824 Cmpnts ***ucont = NULL;
825 PetscReal drift = 0.0, global_drift = 0.0;
826 char tmpdir[PETSC_MAX_PATH_LEN] = "";
827
828 PetscCall(BuildNewtonFixture(periodic_x_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
829 simCtx->TwoD = 1;
830 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_rtol", "1e-8"));
831 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
832 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_ksp_rtol", "1e-10"));
833 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", models[n]));
834 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", structures[n]));
835 PetscCall(VecSet(user->Ucont, 0.0));
836 PetscCall(VecStrideSet(user->Ucont, 0, 0.1));
837 PetscCall(VecStrideSet(user->Ucont, 2, 0.05));
838 PetscCall(VecCopy(user->Ucont, user->Ucont_o));
839 PetscCall(VecCopy(user->Ucont, user->Ucont_rm1));
840 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
841 PetscCall(PicurvAssertBool(simCtx->mom_last_converged, "a TwoD solve must converge"));
842
843 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &ucont));
844 for (PetscInt k = PetscMax(user->info.zs, 1); k < PetscMin(user->info.zs + user->info.zm, user->info.mz - 2); ++k)
845 for (PetscInt j = PetscMax(user->info.ys, 1); j < PetscMin(user->info.ys + user->info.ym, user->info.my - 2); ++j)
846 for (PetscInt i = PetscMax(user->info.xs, 1); i < PetscMin(user->info.xs + user->info.xm, user->info.mx - 1); ++i)
847 drift = PetscMax(drift, PetscAbsReal(ucont[k][j][i].x - 0.1));
848 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucont, &ucont));
849 PetscCallMPI(MPI_Allreduce(&drift, &global_drift, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD));
850 PetscCall(PicurvAssertRealNear(0.0, global_drift, 1.0e-12,
851 "the masked component must keep its time-constant value"));
852 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
853 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_model"));
854 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_structure"));
855 }
856 PetscFunctionReturn(PETSC_SUCCESS);
857}
PetscInt TwoD
Definition variables.h:951
Vec Ucont_o
Definition variables.h:1204
Vec Ucont_rm1
Definition variables.h:1207
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestWholeOperatorDirectJacobian()

static PetscErrorCode TestWholeOperatorDirectJacobian ( void  )
static

Forms the complete direct FD Jacobian, checks every row, and compares MFFD actions.

Definition at line 862 of file test_momentum_newton_krylov.c.

863{
864 SimCtx *simCtx = NULL;
865 UserCtx *user = NULL;
866 SNES snes = NULL;
867 Mat J = NULL;
868 Vec x = NULL, xp = NULL, f0 = NULL, fp = NULL, column = NULL, square = NULL;
869 Vec row_norm_sq = NULL, v[2] = {NULL, NULL}, dense_v[2] = {NULL, NULL};
870 Vec mffd_v = NULL, error_vec = NULL;
871 PetscInt n_global, lo, hi;
872 PetscReal min_row_sq = 0.0, error = 0.0, reference = 0.0;
873 const PetscReal epsilon = 1.0e-7;
875
876 PetscFunctionBeginUser;
877 PetscCall(BuildMinimalWallOperatorFixture(&simCtx, &user, PETSC_FALSE));
878 PetscCall(VecDuplicate(user->Ucont, &x)); PetscCall(VecSet(x, 0.0));
879 PetscCall(VecDuplicate(x, &xp)); PetscCall(VecDuplicate(x, &f0));
880 PetscCall(VecDuplicate(x, &fp)); PetscCall(VecDuplicate(x, &column));
881 PetscCall(VecDuplicate(x, &square)); PetscCall(VecDuplicate(x, &row_norm_sq));
882 PetscCall(VecDuplicate(x, &v[0])); PetscCall(VecDuplicate(x, &v[1]));
883 PetscCall(VecDuplicate(x, &dense_v[0])); PetscCall(VecDuplicate(x, &dense_v[1]));
884 PetscCall(VecDuplicate(x, &mffd_v)); PetscCall(VecDuplicate(x, &error_vec));
885 PetscCall(VecZeroEntries(row_norm_sq)); PetscCall(VecZeroEntries(dense_v[0]));
886 PetscCall(VecZeroEntries(dense_v[1]));
887 PetscCall(VecGetSize(x, &n_global));
888 PetscCall(VecGetOwnershipRange(x, &lo, &hi));
889 for (PetscInt which = 0; which < 2; ++which) {
890 PetscScalar *a = NULL;
891 PetscCall(VecGetArray(v[which], &a));
892 for (PetscInt local = 0; local < hi - lo; ++local) {
893 PetscInt global = lo + local;
894 a[local] = which == 0 ? (PetscScalar)(1.0 + 0.05 * (global % 9))
895 : (PetscScalar)(((global % 2) ? -1.0 : 1.0) * (0.5 + 0.03 * (global % 7)));
896 }
897 PetscCall(VecRestoreArray(v[which], &a));
898 }
899
900 ctx.user = user;
901 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
902 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
903 for (PetscInt col = 0; col < n_global; ++col) {
904 PetscScalar coeff[2] = {
905 (PetscScalar)(1.0 + 0.05 * (col % 9)),
906 (PetscScalar)(((col % 2) ? -1.0 : 1.0) * (0.5 + 0.03 * (col % 7)))
907 };
908 PetscCall(VecCopy(x, xp));
909 if (col >= lo && col < hi) PetscCall(VecSetValue(xp, col, epsilon, ADD_VALUES));
910 PetscCall(VecAssemblyBegin(xp)); PetscCall(VecAssemblyEnd(xp));
911 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, xp, fp, &ctx));
912 PetscCall(VecWAXPY(column, -1.0, f0, fp));
913 PetscCall(VecScale(column, 1.0 / epsilon));
914 PetscCall(VecPointwiseMult(square, column, column));
915 PetscCall(VecAXPY(row_norm_sq, 1.0, square));
916 PetscCall(VecAXPY(dense_v[0], coeff[0], column));
917 PetscCall(VecAXPY(dense_v[1], coeff[1], column));
918 }
919 PetscCall(VecMin(row_norm_sq, NULL, &min_row_sq));
920 PetscCheck(min_row_sq > 0.5, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
921 "Complete Newton Jacobian contains an unexplained zero/weak row (min squared norm=%g).",
922 (double)min_row_sq);
923 PetscCall(CheckStoredDerivative(user, x, 2, 2, 2, 0, 2, 2, 2, 0,
924 10.0, 1.0e-5, "interior BDF1 temporal diagonal"));
925
926 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
927 PetscCall(SNESSetDM(snes, user->fda));
928 PetscCall(SNESSetFunction(snes, f0, MomentumNewtonKrylov_FormResidual, &ctx));
929 PetscCall(MatCreateSNESMF(snes, &J));
930 PetscCall(MatMFFDSetBase(J, x, f0));
931 for (PetscInt which = 0; which < 2; ++which) {
932 PetscCall(MatMult(J, v[which], mffd_v));
933 PetscCall(VecWAXPY(error_vec, -1.0, dense_v[which], mffd_v));
934 PetscCall(VecNorm(error_vec, NORM_2, &error));
935 PetscCall(VecNorm(dense_v[which], NORM_2, &reference));
936 PetscCheck(error <= 2.0e-5 * PetscMax(1.0, reference), PETSC_COMM_WORLD, PETSC_ERR_PLIB,
937 "Independent dense FD Jv differs from PETSc MFFD action %" PetscInt_FMT ": error=%g reference=%g.",
938 which, (double)error, (double)reference);
939 }
940
941 for (PetscInt which = 0; which < 2; ++which) {
942 const PetscReal steps[3] = {1.0e-4, 1.0e-6, 1.0e-8};
943 PetscReal best = PETSC_MAX_REAL;
944 for (PetscInt s = 0; s < 3; ++s) {
945 PetscCall(VecWAXPY(xp, steps[s], v[which], x));
946 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, xp, fp, &ctx));
947 PetscCall(VecWAXPY(column, -1.0, f0, fp));
948 PetscCall(VecScale(column, 1.0 / steps[s]));
949 PetscCall(VecAXPY(column, -1.0, dense_v[which]));
950 PetscCall(VecNorm(column, NORM_2, &error));
951 best = PetscMin(best, error);
952 }
953 PetscCall(VecNorm(dense_v[which], NORM_2, &reference));
954 PetscCheck(best <= 2.0e-5 * PetscMax(1.0, reference), PETSC_COMM_WORLD, PETSC_ERR_PLIB,
955 "Direct directional differences show no accuracy plateau for vector %" PetscInt_FMT " (best=%g).",
956 which, (double)best);
957 }
958
959 PetscCall(MatDestroy(&J)); PetscCall(SNESDestroy(&snes));
960 PetscCall(VecDestroy(&error_vec)); PetscCall(VecDestroy(&mffd_v));
961 PetscCall(VecDestroy(&dense_v[1])); PetscCall(VecDestroy(&dense_v[0]));
962 PetscCall(VecDestroy(&v[1])); PetscCall(VecDestroy(&v[0]));
963 PetscCall(VecDestroy(&row_norm_sq)); PetscCall(VecDestroy(&square));
964 PetscCall(VecDestroy(&column)); PetscCall(VecDestroy(&fp)); PetscCall(VecDestroy(&f0));
965 PetscCall(VecDestroy(&xp)); PetscCall(VecDestroy(&x));
966 PetscCall(BoundarySystem_Destroy(user));
967 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
968 PetscFunctionReturn(PETSC_SUCCESS);
969}
PetscErrorCode BoundarySystem_Destroy(UserCtx *user)
Cleans up and destroys all boundary system resources.
static PetscErrorCode BuildMinimalWallOperatorFixture(SimCtx **simCtx, UserCtx **user, PetscBool x_periodic)
Builds a compact all-wall operator fixture through real boundary handlers.
PetscErrorCode PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
Destroys minimal SimCtx/UserCtx fixtures and all owned PETSc objects.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPeriodicOperatorHasNoZeroRows()

static PetscErrorCode TestPeriodicOperatorHasNoZeroRows ( void  )
static

Audits every row of a complete operator containing periodic duplicates.

Definition at line 972 of file test_momentum_newton_krylov.c.

973{
974 SimCtx *simCtx = NULL;
975 UserCtx *user = NULL;
976 Vec x = NULL, xp = NULL, f0 = NULL, fp = NULL, column = NULL;
977 Vec square = NULL, row_norm_sq = NULL;
978 PetscInt n_global, lo, hi;
979 PetscReal min_row_sq = 0.0;
980 const PetscReal epsilon = 1.0e-7;
982
983 PetscFunctionBeginUser;
984 PetscCall(BuildMinimalWallOperatorFixture(&simCtx, &user, PETSC_TRUE));
985 PetscCall(VecDuplicate(user->Ucont, &x)); PetscCall(VecSet(x, 0.0));
986 PetscCall(VecDuplicate(x, &xp)); PetscCall(VecDuplicate(x, &f0));
987 PetscCall(VecDuplicate(x, &fp)); PetscCall(VecDuplicate(x, &column));
988 PetscCall(VecDuplicate(x, &square)); PetscCall(VecDuplicate(x, &row_norm_sq));
989 PetscCall(VecZeroEntries(row_norm_sq));
990 PetscCall(VecGetSize(x, &n_global)); PetscCall(VecGetOwnershipRange(x, &lo, &hi));
991 ctx.user = user;
992 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
993 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
994 for (PetscInt col = 0; col < n_global; ++col) {
995 PetscCall(VecCopy(x, xp));
996 if (col >= lo && col < hi) PetscCall(VecSetValue(xp, col, epsilon, ADD_VALUES));
997 PetscCall(VecAssemblyBegin(xp)); PetscCall(VecAssemblyEnd(xp));
998 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, xp, fp, &ctx));
999 PetscCall(VecWAXPY(column, -1.0, f0, fp)); PetscCall(VecScale(column, 1.0 / epsilon));
1000 PetscCall(VecPointwiseMult(square, column, column));
1001 PetscCall(VecAXPY(row_norm_sq, 1.0, square));
1002 }
1003 PetscCall(VecMin(row_norm_sq, NULL, &min_row_sq));
1004 PetscCheck(min_row_sq > 0.5, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
1005 "Periodic Newton Jacobian contains a zero/weak row (min squared norm=%g).",
1006 (double)min_row_sq);
1007 PetscCall(VecDestroy(&row_norm_sq)); PetscCall(VecDestroy(&square));
1008 PetscCall(VecDestroy(&column)); PetscCall(VecDestroy(&fp)); PetscCall(VecDestroy(&f0));
1009 PetscCall(VecDestroy(&xp)); PetscCall(VecDestroy(&x));
1010 PetscCall(BoundarySystem_Destroy(user));
1011 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1012 PetscFunctionReturn(PETSC_SUCCESS);
1013}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestSmallSolveAndRollback()

static PetscErrorCode TestSmallSolveAndRollback ( void  )
static

Exercises a converged solve, a committed unconverged solve, a non-finite rollback, and cleanup.

Definition at line 1016 of file test_momentum_newton_krylov.c.

1017{
1018 SimCtx *simCtx = NULL;
1019 UserCtx *user = NULL;
1020 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1021 Vec entry = NULL, delta = NULL;
1022 PetscErrorCode solve_ierr;
1023 PetscReal norm = 0.0;
1024 const FieldId fields[] = {FIELD_ID_UCONT};
1025 char summary_path[PETSC_MAX_PATH_LEN];
1026 char history_path[PETSC_MAX_PATH_LEN];
1027 char linear_history_path[PETSC_MAX_PATH_LEN];
1028
1029 PetscFunctionBeginUser;
1030 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1031 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_rtol", "1e-4"));
1032 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
1033 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_ksp_rtol", "1e-6"));
1034 simCtx->mom_nk_monitor_history = PETSC_TRUE;
1035 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
1036 PetscCall(PicurvAssertBool(simCtx->mom_last_converged, "small Newton solve must converge"));
1037 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs != NULL && user->momentumNewtonKrylov != NULL),
1038 "successful solve must keep its workspace for the next step"));
1039 PetscCall(PetscSNPrintf(summary_path, sizeof(summary_path),
1040 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.csv", simCtx->log_dir));
1041 PetscCall(PetscSNPrintf(history_path, sizeof(history_path),
1042 "%s/Momentum_Solver_Newton_Krylov_History_Block_0.csv", simCtx->log_dir));
1043 PetscCall(PetscSNPrintf(linear_history_path, sizeof(linear_history_path),
1044 "%s/Momentum_Solver_Newton_Krylov_Linear_History_Block_0.csv",
1045 simCtx->log_dir));
1046 PetscCall(AssertNewtonLog(summary_path, 1, ",Newton Krylov,", ",committed\n"));
1047 PetscCall(AssertNewtonLog(history_path, -2, "step,block,newton,nonlinear_norm", ",0,0,"));
1048 PetscCall(AssertNewtonLog(linear_history_path, -2, "krylov,requested_rtol,reported_residual_norm", ",0,0,0,"));
1049
1050 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1051 tmpdir[0] = '\0';
1052 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1053 PetscCall(VecSet(user->Ucont, 0.2));
1054 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
1055 PetscCall(ApplyBoundaryConditions(user));
1056 PetscCall(VecDuplicate(user->Ucont, &entry));
1057 PetscCall(VecDuplicate(user->Ucont, &delta));
1058 PetscCall(VecCopy(user->Ucont, entry));
1059 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "0"));
1060 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1061 PetscCall(PicurvAssertIntEqual(0, solve_ierr,
1062 "a finite residual no larger than at entry must commit the step"));
1063 PetscCall(PicurvAssertBool((PetscBool)!simCtx->mom_last_converged,
1064 "a committed SNES failure must be reported as not converged"));
1065 PetscCall(VecWAXPY(delta, -1.0, entry, user->Ucont));
1066 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
1067 PetscCall(PicurvAssertRealNear(0.0, norm, 1.0e-12,
1068 "a zero-iteration failure must commit the unchanged entry state"));
1069 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs != NULL && user->momentumNewtonKrylov != NULL),
1070 "committed unconverged solve must keep its workspace for the next step"));
1071 PetscCall(PetscSNPrintf(summary_path, sizeof(summary_path),
1072 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.csv", simCtx->log_dir));
1073 PetscCall(AssertNewtonLog(summary_path, 1, ",DIVERGED_", ",committed_unconverged\n"));
1074
1075 /* A non-finite residual is the one failure that must still roll back and stop the run.
1076 Options are read when the solve context is built, so release it to apply the new limit. */
1077 PetscCall(MomentumSolver_NewtonKrylov_Destroy(user));
1078 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
1079 PetscCall(VecSet(user->Ucont, PETSC_MAX_REAL));
1080 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
1081 PetscCall(ApplyBoundaryConditions(user));
1082 PetscCall(VecCopy(user->Ucont, entry));
1083 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1084 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1085 PetscCall(PetscPopErrorHandler());
1086 PetscCall(PicurvAssertIntEqual(PETSC_ERR_CONV_FAILED, solve_ierr,
1087 "a non-finite residual must report PETSC_ERR_CONV_FAILED"));
1088 PetscCall(PicurvAssertBool((PetscBool)!simCtx->mom_last_converged,
1089 "a rolled-back solve must be reported as not converged"));
1090 PetscCall(VecWAXPY(delta, -1.0, entry, user->Ucont));
1091 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
1092 PetscCall(PicurvAssertRealNear(0.0, norm, 1.0e-12,
1093 "failed Newton solve must restore the canonical entry state"));
1094 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL), "failed solve must release Rhs"));
1095 PetscCall(AssertNewtonLog(summary_path, 2, ",DIVERGED_", ",rolled_back\n"));
1096
1097 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_rtol"));
1098 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_max_it"));
1099 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_ksp_rtol"));
1100 PetscCall(VecDestroy(&delta));
1101 PetscCall(VecDestroy(&entry));
1102 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1103 PetscFunctionReturn(PETSC_SUCCESS);
1104}
PetscErrorCode ApplyBoundaryConditions(UserCtx *user)
Main boundary-condition orchestrator executed during solver timestepping.
#define MomentumSolver_NewtonKrylov_Destroy
static PetscErrorCode AssertNewtonLog(const char *path, PetscInt expected_rows, const char *needle_a, const char *needle_b)
Checks a structured log's row count and required text after a collective solve.
PetscBool mom_nk_monitor_history
Definition variables.h:975
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:941
struct MomentumNewtonKrylovContext * momentumNewtonKrylov
Definition variables.h:1235
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestZeroIterationStructuredLogging()

static PetscErrorCode TestZeroIterationStructuredLogging ( void  )
static

Verifies the six-wall zero-velocity case logs zero Newton/Krylov work.

Definition at line 1107 of file test_momentum_newton_krylov.c.

1108{
1109 SimCtx *simCtx = NULL;
1110 UserCtx *user = NULL;
1111 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1112 char summary_path[PETSC_MAX_PATH_LEN];
1113 const FieldId fields[] = {FIELD_ID_UCONT};
1114
1115 PetscFunctionBeginUser;
1116 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1117 PetscCall(VecZeroEntries(user->Ucont));
1118 PetscCall(VecZeroEntries(user->Ucont_o));
1119 PetscCall(VecZeroEntries(user->Ucont_rm1));
1120 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
1121 PetscCall(ApplyBoundaryConditions(user));
1122 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
1123 PetscCall(PetscSNPrintf(summary_path, sizeof(summary_path),
1124 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.csv", simCtx->log_dir));
1125 PetscCall(AssertNewtonLog(summary_path, 1, ",0,1,0,",
1126 ",0.0000000000000000e+00,committed\n"));
1127 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1128 PetscFunctionReturn(PETSC_SUCCESS);
1129}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CheckFlatChannelStartup()

static PetscErrorCode CheckFlatChannelStartup ( PetscBool  use_point_block)
static

Exercises the straight-duct BDF1 startup path used by flat_channel.

The conservation outlet consumes lUcat during its first boundary pass. This test deliberately evaluates the callback at the initialized state before installing SNES, then completes the first nonlinear solve with each shipped preconditioner. It catches a missing Ucont -> Ucat -> lUcat seed as a non-finite initial residual rather than hiding it behind later MFFD work.

Definition at line 1140 of file test_momentum_newton_krylov.c.

1141{
1142 SimCtx *simCtx = NULL;
1143 UserCtx *user = NULL;
1144 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1145 Vec x = NULL, f = NULL;
1146 PetscReal initial_norm = 0.0;
1148
1149 PetscFunctionBeginUser;
1150 PetscCall(BuildNewtonFixture(NULL, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1151 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1152 PetscCall(VecDuplicate(user->Ucont, &x));
1153 PetscCall(VecDuplicate(user->Ucont, &f));
1154 PetscCall(VecCopy(user->Ucont, x));
1155 ctx.user = user;
1156 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
1157 PetscCall(VecNorm(f, NORM_2, &initial_norm));
1158 PetscCheck(!PetscIsInfOrNanReal(initial_norm), PETSC_COMM_WORLD, PETSC_ERR_FP,
1159 "flat-channel BDF1 initial residual is non-finite (%g) with %s.",
1160 (double)initial_norm,
1161 use_point_block ? "frozen-momentum point-block" : "PCNONE");
1162 PetscCall(VecDestroy(&f));
1163 PetscCall(VecDestroy(&x));
1164 PetscCall(VecDestroy(&user->Rhs));
1165
1166 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_rtol", "1e-4"));
1167 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
1168 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_ksp_rtol", "1e-6"));
1169 if (use_point_block) {
1170 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1171 "frozen_momentum_jacobian"));
1172 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure",
1173 "point_block"));
1174 }
1175 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
1176 PetscCall(PicurvAssertBool(simCtx->mom_last_converged,
1177 "flat-channel BDF1 Newton solve must converge"));
1178 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_model"));
1179 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_structure"));
1180 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_rtol"));
1181 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_max_it"));
1182 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_ksp_rtol"));
1183 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1184 PetscFunctionReturn(PETSC_SUCCESS);
1185}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestFlatChannelStartup()

static PetscErrorCode TestFlatChannelStartup ( void  )
static

Guards flat_channel's initial BDF1 residual and both shipped NK PCs.

Definition at line 1188 of file test_momentum_newton_krylov.c.

1189{
1190 PetscFunctionBeginUser;
1191 PetscCall(CheckFlatChannelStartup(PETSC_FALSE));
1192 PetscCall(CheckFlatChannelStartup(PETSC_TRUE));
1193 PetscFunctionReturn(PETSC_SUCCESS);
1194}
static PetscErrorCode CheckFlatChannelStartup(PetscBool use_point_block)
Exercises the straight-duct BDF1 startup path used by flat_channel.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestRestartAndContinuationSolve()

static PetscErrorCode TestRestartAndContinuationSolve ( void  )
static

Verifies restarted Newton solves with both supported preconditioners.

Definition at line 1197 of file test_momentum_newton_krylov.c.

1198{
1199 SimCtx *simCtx = NULL;
1200 UserCtx *user = NULL;
1201 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1202 const FieldId fields[] = {FIELD_ID_UCONT};
1203
1204 PetscFunctionBeginUser;
1205 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1206 PetscCall(VecSet(user->Ucont, 0.2));
1207 PetscCall(VecZeroEntries(user->Ucont_o));
1208 PetscCall(VecZeroEntries(user->Ucont_rm1));
1209 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
1210 PetscCall(ApplyBoundaryConditions(user));
1211 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_rtol", "1e-4"));
1212 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
1213 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_ksp_rtol", "1e-6"));
1214
1215 /* AdvanceSimulation() solves StartStep+1 first. Use a nonzero checkpoint
1216 * state so that SNES takes a Newton/Krylov path rather than accepting a
1217 * trivial residual. */
1218 simCtx->StartStep = 7;
1219 simCtx->step = simCtx->StartStep + 1;
1220 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
1221 PetscCall(PicurvAssertBool(simCtx->mom_last_converged,
1222 "PCNONE Newton Krylov checkpoint restart must converge"));
1223
1224 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1225 tmpdir[0] = '\0';
1226 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1227 PetscCall(VecSet(user->Ucont, 0.2));
1228 PetscCall(VecZeroEntries(user->Ucont_o));
1229 PetscCall(VecZeroEntries(user->Ucont_rm1));
1230 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
1231 PetscCall(ApplyBoundaryConditions(user));
1232 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1233 "frozen_momentum_jacobian"));
1234 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "point_block"));
1235
1236 simCtx->continueMode = PETSC_TRUE;
1237 simCtx->StartStep = 8;
1238 simCtx->step = simCtx->StartStep + 1;
1239 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
1240 PetscCall(PicurvAssertBool(simCtx->mom_last_converged,
1241 "frozen-momentum Newton Krylov --continue solve must converge"));
1242 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_model"));
1243 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_structure"));
1244 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_rtol"));
1245 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_max_it"));
1246 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_ksp_rtol"));
1247 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1248 PetscFunctionReturn(PETSC_SUCCESS);
1249}
PetscBool continueMode
Definition variables.h:935
PetscInt StartStep
Definition variables.h:926
PetscInt step
Definition variables.h:924
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestUnsupportedConfigurationFailsBeforeAllocation()

static PetscErrorCode TestUnsupportedConfigurationFailsBeforeAllocation ( void  )
static

Confirms unsupported features fail before workspace allocation.

Definition at line 1252 of file test_momentum_newton_krylov.c.

1253{
1254 SimCtx *simCtx = NULL;
1255 UserCtx *user = NULL;
1256 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1257 PetscErrorCode solve_ierr;
1258
1259 PetscFunctionBeginUser;
1260 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1261 {
1262 /* The gradient (Clark) model and wall functions are no longer rejected: both
1263 reach the solver through the shared residual, which this solver already
1264 evaluates. Neither is verified under Newton-Krylov yet, so neither is
1265 claimed as supported; they are simply no longer refused. */
1266 PetscInt *unsupported_flags[] = {
1267 &simCtx->immersed, &simCtx->movefsi, &simCtx->rotatefsi,
1268 &simCtx->moveframe, &simCtx->rotateframe
1269 };
1270 for (size_t flag = 0; flag < sizeof(unsupported_flags) / sizeof(unsupported_flags[0]); ++flag) {
1271 *unsupported_flags[flag] = 1;
1272 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1273 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1274 PetscCall(PetscPopErrorHandler());
1275 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS),
1276 "unsupported Newton feature flag must fail"));
1277 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL),
1278 "feature validation must precede workspace allocation"));
1279 *unsupported_flags[flag] = 0;
1280 }
1281 }
1282 simCtx->block_number = 2;
1283 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1284 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1285 PetscCall(PetscPopErrorHandler());
1286 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS), "multiblock must fail"));
1287 simCtx->block_number = 1;
1288 /* Solid cells are no longer a rejection. The residual already zeroes their rows, so
1289 they carry no unknown; the solver now gives them the same identity treatment as
1290 any other row without one, instead of refusing the whole solve. A fully masked
1291 domain is the degenerate case of that: every row is an identity row and the
1292 system is trivially consistent. This checks the structural contract only; the
1293 masked path has no physics verification because nothing in the tree currently
1294 populates Nvert with a nonzero value. */
1295 PetscCall(VecSet(user->Nvert, 1.0));
1296 PetscCall(VecSet(user->lNvert, 1.0));
1297 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1298 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr == PETSC_SUCCESS),
1299 "a fully masked domain must resolve to identity rows, not a rejection"));
1300 /* That solve kept a context built for the all-solid mask; release it so the remaining
1301 checks see the fixture's unallocated workspace again. */
1302 PetscCall(MomentumSolver_NewtonKrylov_Destroy(user));
1303 PetscCall(VecDestroy(&user->Rhs));
1304 PetscCall(VecSet(user->Nvert, 0.0));
1305 PetscCall(VecSet(user->lNvert, 0.0));
1306 PetscCall(VecSet(user->Nvert, 0.0));
1309 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1310 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1311 PetscCall(PetscPopErrorHandler());
1312 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS),
1313 "driven constant-flux controller must fail"));
1314 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL),
1315 "all validation failures must precede workspace allocation"));
1316 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1317 PetscFunctionReturn(PETSC_SUCCESS);
1318}
PetscInt movefsi
Definition variables.h:950
PetscInt moveframe
Definition variables.h:951
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:1182
PetscInt block_number
Definition variables.h:1009
Vec lNvert
Definition variables.h:1197
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:354
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
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPostAllocationFailureCleanup()

static PetscErrorCode TestPostAllocationFailureCleanup ( void  )
static

Verifies cleanup and rollback after an options failure following asset creation.

Definition at line 1321 of file test_momentum_newton_krylov.c.

1322{
1323 SimCtx *simCtx = NULL;
1324 UserCtx *user = NULL;
1325 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1326 Vec entry = NULL, delta = NULL;
1327 PetscErrorCode solve_ierr;
1328 PetscReal norm = 0.0;
1329 const FieldId fields[] = {FIELD_ID_UCONT};
1330
1331 PetscFunctionBeginUser;
1332 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1333 PetscCall(VecSet(user->Ucont, 0.2));
1334 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
1335 PetscCall(ApplyBoundaryConditions(user));
1336 PetscCall(VecDuplicate(user->Ucont, &entry)); PetscCall(VecCopy(user->Ucont, entry));
1337 PetscCall(VecDuplicate(user->Ucont, &delta));
1338 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_pc_type", "jacobi"));
1339 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1340 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1341 PetscCall(PetscPopErrorHandler());
1342 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_pc_type"));
1343 PetscCall(PicurvAssertIntEqual(PETSC_ERR_SUP, solve_ierr,
1344 "non-PCNONE option must fail after setup"));
1345 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL),
1346 "post-allocation failure must destroy Rhs"));
1347 PetscCall(VecWAXPY(delta, -1.0, entry, user->Ucont));
1348 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
1349 PetscCall(PicurvAssertRealNear(0.0, norm, 1.0e-12,
1350 "post-allocation failure must restore canonical entry"));
1351 PetscCall(VecDestroy(&delta)); PetscCall(VecDestroy(&entry));
1352 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1353 PetscFunctionReturn(PETSC_SUCCESS);
1354}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestLinearizationConfigParsing()

static PetscErrorCode TestLinearizationConfigParsing ( void  )
static

Verifies finalized application-owned linearization option parsing.

Definition at line 1357 of file test_momentum_newton_krylov.c.

1358{
1359 MomentumNewtonJacobian jacobian = {0};
1360 MomentumPreconditionerDescription description = {0};
1361 PetscErrorCode config_ierr;
1362 const char *option_names[] = {
1363 "-mom_nk_jacobian_type",
1364 "-mom_nk_jacobian_fd_mode",
1365 "-mom_nk_preconditioner_model",
1366 "-mom_nk_preconditioner_structure"
1367 };
1368
1369 PetscFunctionBeginUser;
1370 for (size_t n = 0; n < sizeof(option_names) / sizeof(option_names[0]); ++n)
1371 PetscCall(PetscOptionsClearValue(NULL, option_names[n]));
1372
1373 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description));
1375 "default Jacobian type must be finite difference"));
1377 jacobian.finite_difference_mode,
1378 "default finite-difference mode must be matrix free"));
1379 PetscCall(PicurvAssertIntEqual(MOM_NK_PC_MODEL_NONE, description.model,
1380 "default preconditioner model must be none"));
1382 "default preconditioner structure must be none"));
1383
1384 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_type", "finite_difference"));
1385 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_fd_mode", "matrix_free"));
1386 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", "none"));
1387 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "none"));
1388 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description));
1390 "explicit baseline Jacobian type must parse"));
1392 jacobian.finite_difference_mode,
1393 "explicit baseline finite-difference mode must parse"));
1394 PetscCall(PicurvAssertIntEqual(MOM_NK_PC_MODEL_NONE, description.model,
1395 "explicit baseline preconditioner model must parse"));
1397 "explicit baseline preconditioner structure must parse"));
1398
1399 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1400 "frozen_momentum_jacobian"));
1401 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "point_block"));
1402 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description));
1404 "explicit Jacobian type must parse"));
1406 jacobian.finite_difference_mode,
1407 "explicit finite-difference mode must parse"));
1409 description.model, "frozen model must parse"));
1411 description.structure, "point-block structure must parse"));
1412
1413 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_type", "analytic"));
1414 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1415 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1416 PetscCall(PetscPopErrorHandler());
1417 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1418 "unsupported Jacobian type must fail"));
1419 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_type", "finite_difference"));
1420
1421 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_fd_mode", "colored_sparse"));
1422 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1423 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1424 PetscCall(PetscPopErrorHandler());
1425 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1426 "unsupported finite-difference mode must fail"));
1427 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_fd_mode", "matrix_free"));
1428
1429 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "none"));
1430 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1431 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1432 PetscCall(PetscPopErrorHandler());
1433 PetscCall(PicurvAssertIntEqual(PETSC_ERR_SUP, config_ierr,
1434 "frozen model without point block must fail"));
1435 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", "none"));
1436 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "point_block"));
1437 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1438 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1439 PetscCall(PetscPopErrorHandler());
1440 PetscCall(PicurvAssertIntEqual(PETSC_ERR_SUP, config_ierr,
1441 "none model with point block must fail"));
1442
1443 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", "diagonal"));
1444 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "none"));
1445 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1446 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1447 PetscCall(PetscPopErrorHandler());
1448 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1449 "unsupported preconditioner model must fail"));
1450 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1451 "frozen_momentum_jacobian"));
1452 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "line"));
1453 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1454 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1455 PetscCall(PetscPopErrorHandler());
1456 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1457 "unsupported preconditioner structure must fail"));
1458
1459 for (size_t n = 0; n < sizeof(option_names) / sizeof(option_names[0]); ++n)
1460 PetscCall(PetscOptionsClearValue(NULL, option_names[n]));
1461 PetscFunctionReturn(PETSC_SUCCESS);
1462}
@ MOM_NK_PC_STRUCTURE_POINT_BLOCK
@ MOM_NK_PC_STRUCTURE_NONE
MomentumNewtonFiniteDifferenceMode finite_difference_mode
MomentumNewtonJacobianType type
static PetscErrorCode MomentumNewtonKrylov_ReadLinearizationConfig(MomentumNewtonJacobian *jacobian, MomentumPreconditionerDescription *description)
Reads application-owned Jacobian and preconditioner mathematics.
MomentumPreconditionerStructure structure
@ MOM_NK_JACOBIAN_FINITE_DIFFERENCE
@ MOM_NK_FD_MODE_MATRIX_FREE
@ MOM_NK_PC_MODEL_FROZEN_MOMENTUM_JACOBIAN
@ MOM_NK_PC_MODEL_NONE
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestStencilToGlobal()

static PetscErrorCode TestStencilToGlobal ( UserCtx *  user,
MatStencil  stencil,
PetscInt *  global_index 
)
static

Converts an in-domain or periodic-ghost DMDA stencil to PETSc ordering.

Definition at line 1465 of file test_momentum_newton_krylov.c.

1467{
1468 ISLocalToGlobalMapping local_to_global = NULL;
1469 PetscInt ghost_starts[3], ghost_sizes[3], local_index;
1470
1471 PetscFunctionBeginUser;
1472 PetscCall(DMDAGetGhostCorners(user->fda,
1473 &ghost_starts[0], &ghost_starts[1], &ghost_starts[2],
1474 &ghost_sizes[0], &ghost_sizes[1], &ghost_sizes[2]));
1475 PetscCall(PicurvAssertBool((PetscBool)(
1476 stencil.i >= ghost_starts[0] && stencil.i < ghost_starts[0] + ghost_sizes[0] &&
1477 stencil.j >= ghost_starts[1] && stencil.j < ghost_starts[1] + ghost_sizes[1] &&
1478 stencil.k >= ghost_starts[2] && stencil.k < ghost_starts[2] + ghost_sizes[2]),
1479 "test stencil must lie in the local DMDA ghost region"));
1480 local_index = stencil.c + 3 * (
1481 (stencil.i - ghost_starts[0]) + ghost_sizes[0] * (
1482 (stencil.j - ghost_starts[1]) + ghost_sizes[1] *
1483 (stencil.k - ghost_starts[2])));
1484 PetscCall(DMGetLocalToGlobalMapping(user->fda, &local_to_global));
1485 PetscCall(ISLocalToGlobalMappingApply(local_to_global, 1, &local_index,
1486 global_index));
1487 PetscFunctionReturn(PETSC_SUCCESS);
1488}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PreconditionerMatrixStencilEntry()

static PetscErrorCode PreconditionerMatrixStencilEntry ( UserCtx *  user,
Mat  preconditioning_matrix,
MatStencil  row,
MatStencil  col,
PetscScalar *  value 
)
static

Reads one DMDA-stencil matrix entry through collective basis vectors.

Definition at line 1491 of file test_momentum_newton_krylov.c.

1494{
1495 Vec column_basis = NULL, row_basis = NULL, product = NULL;
1496 AO ao = NULL;
1497 PetscInt mx, my, row_index, col_index;
1498 PetscFunctionBeginUser;
1499 PetscCall(DMDAGetInfo(user->fda, NULL, &mx, &my, NULL, NULL, NULL, NULL,
1500 NULL, NULL, NULL, NULL, NULL, NULL));
1501 row_index = row.c + 3 * (row.i + mx * (row.j + my * row.k));
1502 col_index = col.c + 3 * (col.i + mx * (col.j + my * col.k));
1503 PetscCall(DMDAGetAO(user->fda, &ao));
1504 PetscCall(AOApplicationToPetsc(ao, 1, &row_index));
1505 PetscCall(AOApplicationToPetsc(ao, 1, &col_index));
1506 PetscCall(DMCreateGlobalVector(user->fda, &column_basis));
1507 PetscCall(DMCreateGlobalVector(user->fda, &row_basis));
1508 PetscCall(DMCreateGlobalVector(user->fda, &product));
1509 PetscCall(VecSet(column_basis, 0.0)); PetscCall(VecSet(row_basis, 0.0));
1510 PetscCall(VecSetValue(column_basis, col_index, 1.0, INSERT_VALUES));
1511 PetscCall(VecSetValue(row_basis, row_index, 1.0, INSERT_VALUES));
1512 PetscCall(VecAssemblyBegin(column_basis)); PetscCall(VecAssemblyEnd(column_basis));
1513 PetscCall(VecAssemblyBegin(row_basis)); PetscCall(VecAssemblyEnd(row_basis));
1514 PetscCall(MatMult(preconditioning_matrix, column_basis, product));
1515 PetscCall(VecDot(row_basis, product, value));
1516 PetscCall(VecDestroy(&product)); PetscCall(VecDestroy(&row_basis));
1517 PetscCall(VecDestroy(&column_basis));
1518 PetscFunctionReturn(PETSC_SUCCESS);
1519}
Here is the caller graph for this function:

◆ AssertExactPointBlockMatrixAllocation()

static PetscErrorCode AssertExactPointBlockMatrixAllocation ( UserCtx *  user,
Mat  matrix,
PetscBool  require_offrank_periodic 
)
static

Verifies the exact AIJ layout and preallocation derived from row classes.

Definition at line 1522 of file test_momentum_newton_krylov.c.

1524{
1525 DMDALocalInfo info;
1526 MatInfo matrix_info;
1527 PetscMPIInt comm_size = 1;
1528 PetscInt matrix_rows, matrix_cols, local_rows, local_cols;
1529 PetscInt vector_size, vector_local_size, block_size;
1530 PetscInt ownership_start, ownership_end;
1531 PetscInt expected_local = 0, expected_global = 0;
1532 PetscInt offrank_periodic_local = 0, offrank_periodic_global = 0;
1533 PetscBool is_seq_aij = PETSC_FALSE, is_mpi_aij = PETSC_FALSE;
1534
1535 PetscFunctionBeginUser;
1536 PetscCall(DMDAGetLocalInfo(user->fda, &info));
1537 PetscCall(MatGetSize(matrix, &matrix_rows, &matrix_cols));
1538 PetscCall(MatGetLocalSize(matrix, &local_rows, &local_cols));
1539 PetscCall(VecGetSize(user->Ucont, &vector_size));
1540 PetscCall(VecGetLocalSize(user->Ucont, &vector_local_size));
1541 PetscCall(VecGetOwnershipRange(user->Ucont, &ownership_start, &ownership_end));
1542 PetscCall(MatGetBlockSize(matrix, &block_size));
1543 PetscCall(PicurvAssertIntEqual(vector_size, matrix_rows,
1544 "point-block global row dimension"));
1545 PetscCall(PicurvAssertIntEqual(vector_size, matrix_cols,
1546 "point-block global column dimension"));
1547 PetscCall(PicurvAssertIntEqual(vector_local_size, local_rows,
1548 "point-block local row dimension"));
1549 PetscCall(PicurvAssertIntEqual(vector_local_size, local_cols,
1550 "point-block local column dimension"));
1551 PetscCall(PicurvAssertIntEqual(3, block_size, "point-block logical block size"));
1552 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)matrix), &comm_size));
1553 PetscCall(PetscObjectTypeCompare((PetscObject)matrix, MATSEQAIJ, &is_seq_aij));
1554 PetscCall(PetscObjectTypeCompare((PetscObject)matrix, MATMPIAIJ, &is_mpi_aij));
1555 PetscCall(PicurvAssertBool(
1556 comm_size == 1 ? is_seq_aij : is_mpi_aij,
1557 "point-block matrix must use the expected AIJ implementation"));
1558
1559 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
1560 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
1561 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
1562 for (PetscInt component = 0; component < 3; ++component) {
1563 PetscInt ri, rj, rk;
1565 user, i, j, k, component, &ri, &rj, &rk);
1566 expected_local += type == MOM_ROW_PHYSICAL ? 3 :
1567 type == MOM_ROW_PERIODIC_DUPLICATE ? 2 : 1;
1568 if (type == MOM_ROW_PERIODIC_DUPLICATE) {
1569 PetscInt representative;
1570 PetscCall(TestStencilToGlobal(user,
1571 (MatStencil){.i = ri, .j = rj, .k = rk, .c = component},
1572 &representative));
1573 if (representative < ownership_start || representative >= ownership_end)
1574 ++offrank_periodic_local;
1575 }
1576 }
1577 }
1578 }
1579 }
1580 PetscCallMPI(MPI_Allreduce(&expected_local, &expected_global, 1, MPIU_INT,
1581 MPI_SUM, PetscObjectComm((PetscObject)matrix)));
1582 PetscCallMPI(MPI_Allreduce(&offrank_periodic_local, &offrank_periodic_global,
1583 1, MPIU_INT, MPI_SUM,
1584 PetscObjectComm((PetscObject)matrix)));
1585 PetscCall(MatGetInfo(matrix, MAT_GLOBAL_SUM, &matrix_info));
1586 PetscCall(PicurvAssertRealNear((PetscReal)expected_global,
1587 (PetscReal)matrix_info.nz_allocated, 0.0,
1588 "point-block matrix must allocate exactly the classified scalar pattern"));
1589 PetscCall(PicurvAssertRealNear((PetscReal)expected_global,
1590 (PetscReal)matrix_info.nz_used, 0.0,
1591 "point-block assembly must insert every classified structural entry"));
1592 PetscCall(PicurvAssertRealNear(0.0, (PetscReal)matrix_info.mallocs, 0.0,
1593 "point-block insertion must not reallocate matrix storage"));
1594 if (require_offrank_periodic && comm_size > 1)
1595 PetscCall(PicurvAssertBool((PetscBool)(offrank_periodic_global > 0),
1596 "MPI periodic fixture must exercise off-rank preallocation"));
1597 PetscFunctionReturn(PETSC_SUCCESS);
1598}
@ MOM_ROW_PERIODIC_DUPLICATE
Duplicate of a wrapped representative row (see ri, rj, rk).
Definition Boundaries.h:195
static PetscErrorCode TestStencilToGlobal(UserCtx *user, MatStencil stencil, PetscInt *global_index)
Converts an in-domain or periodic-ghost DMDA stencil to PETSc ordering.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPointBlockPreconditionerEngine()

static PetscErrorCode TestPointBlockPreconditionerEngine ( void  )
static

Verifies the point-block model and common preconditioning-engine wiring.

Returns
PETSc error code.

Definition at line 1604 of file test_momentum_newton_krylov.c.

1605{
1606 SimCtx *simCtx = NULL;
1607 UserCtx *user = NULL;
1608 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1610 MomentumPreconditionerDescription description = {
1613 0,
1614 0
1615 };
1616 MomentumPreconditionerEngine engine = {0};
1617 Mat preconditioning_matrix = NULL;
1618 Vec x = NULL, f = NULL;
1619 PetscInt block_size = 0, velocity_dof = 0;
1620 PetscReal matrix_norm = 0.0, reassembled_norm = 0.0, difference_norm = 0.0;
1621 MatStencil conditioned = {.i = 0, .j = 2, .k = 3, .c = 0};
1622 MatStencil homogeneous = {.i = 0, .j = 2, .k = 3, .c = 1};
1623 Mat saved_matrix = NULL, mffd = NULL;
1624 SNES snes = NULL;
1625 Vec direction = NULL, product = NULL, px = NULL;
1626 PetscReal px_norm = 0.0;
1627 KSP ksp = NULL;
1628 PC pc = NULL;
1629 Vec pc_rhs = NULL, pc_solution = NULL;
1630 MatInfo initial_allocation_info, repeated_allocation_info;
1631
1632 PetscFunctionBeginUser;
1633 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1634 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1635 PetscCall(VecDuplicate(user->Ucont, &x));
1636 PetscCall(VecDuplicate(user->Ucont, &f));
1637 PetscCall(VecCopy(user->Ucont, x));
1638 PetscCall(VecStrideSet(x, 0, 0.2));
1639 PetscCall(VecStrideSet(x, 1, 0.3));
1640 PetscCall(VecStrideSet(x, 2, 0.4));
1641 ctx.user = user;
1642 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
1643 PetscCall(MomentumPreconditionerEngine_Create(user, NULL, &description, &engine));
1644 preconditioning_matrix = engine.preconditioning_matrix;
1645 PetscCall(PicurvAssertBool((PetscBool)(engine.model_ops == &frozen_momentum_point_block_ops),
1646 "engine must select the frozen-momentum model callbacks"));
1648 "point-block engine must own its separate matrix"));
1649 PetscCall(PicurvAssertBool((PetscBool)!engine.aliases_jacobian_operator,
1650 "point-block matrix must not alias the Jacobian operator"));
1651 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1653 user, preconditioning_matrix, PETSC_FALSE));
1654 PetscCall(MatGetInfo(preconditioning_matrix, MAT_GLOBAL_SUM,
1655 &initial_allocation_info));
1656 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &matrix_norm));
1657 PetscCall(PicurvAssertBool((PetscBool)(matrix_norm > 0.0),
1658 "model callback and common rows must insert matrix entries"));
1659 PetscCall(MatGetBlockSize(preconditioning_matrix, &block_size));
1660 PetscCall(PicurvAssertIntEqual(3, block_size, "point-block matrix block size"));
1661 PetscCall(DMDAGetInfo(user->fda, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1662 &velocity_dof, NULL, NULL, NULL, NULL, NULL));
1663 PetscCall(PicurvAssertIntEqual(3, velocity_dof, "Newton velocity DMDA dof"));
1664
1665 /* The coefficient formulas define columns by the differentiated Ucont
1666 component. Check an asymmetric pair so a row/column transpose cannot pass. */
1667 {
1668 const MatStencil row_i = {.i = 2, .j = 2, .k = 2, .c = 0};
1669 const MatStencil row_j = {.i = 2, .j = 2, .k = 2, .c = 1};
1670 MatStencil col_i = row_i, col_j = row_j;
1671 PetscReal ***aj = NULL;
1672 PetscReal AJip, AJjp;
1673 PetscScalar dFi_dUj = 0.0, dFj_dUi = 0.0;
1674
1675 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, &aj));
1676 AJip = 0.5 * (aj[2][2][2] + aj[2][2][3]);
1677 AJjp = 0.5 * (aj[2][2][2] + aj[2][3][2]);
1678 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, &aj));
1679 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1680 row_i, col_j, &dFi_dUj));
1681 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1682 row_j, col_i, &dFj_dUi));
1683 PetscCall(PicurvAssertRealNear(0.5 * AJjp * 0.2,
1684 PetscRealPart(dFi_dUj), 1e-12,
1685 "row i, column j must differentiate with respect to U-j"));
1686 PetscCall(PicurvAssertRealNear(0.5 * AJip * 0.3,
1687 PetscRealPart(dFj_dUi), 1e-12,
1688 "row j, column i must differentiate with respect to U-i"));
1689 }
1690
1691 /* Both fixed categories are exact identity rows, with no same-cell coupling. */
1692 for (PetscInt cc = 0; cc < 3; ++cc) {
1693 MatStencil col = conditioned; col.c = cc;
1694 PetscScalar conditioned_value = 0.0, homogeneous_value = 0.0;
1695 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1696 conditioned, col, &conditioned_value));
1697 col = homogeneous; col.c = cc;
1698 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1699 homogeneous, col, &homogeneous_value));
1700 PetscCall(PicurvAssertRealNear(cc == conditioned.c ? 1.0 : 0.0,
1701 PetscRealPart(conditioned_value), 1e-14, "conditioned row must be exact identity"));
1702 PetscCall(PicurvAssertRealNear(cc == homogeneous.c ? 1.0 : 0.0,
1703 PetscRealPart(homogeneous_value), 1e-14, "homogeneous row must be exact identity"));
1704 }
1705
1706 /* A real residual and MFFD product cannot change subsequent assembly. */
1707 PetscCall(MatDuplicate(preconditioning_matrix, MAT_COPY_VALUES, &saved_matrix));
1708 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1709 PetscCall(SNESSetDM(snes, user->fda));
1710 PetscCall(SNESSetFunction(snes, f, MomentumNewtonKrylov_FormResidual, &ctx));
1711 PetscCall(MatCreateSNESMF(snes, &mffd));
1712 PetscCall(VecDuplicate(x, &direction)); PetscCall(VecDuplicate(x, &product));
1713 PetscCall(VecSet(direction, 0.375));
1714 PetscCall(MomentumNewtonKrylov_FormResidual(snes, x, f, &ctx));
1715 PetscCall(MatMFFDSetBase(mffd, x, f));
1716 PetscCall(MatMult(mffd, direction, product));
1717 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1718 PetscCall(MatAXPY(saved_matrix, -1.0, preconditioning_matrix, SAME_NONZERO_PATTERN));
1719 PetscCall(MatNorm(saved_matrix, NORM_FROBENIUS, &difference_norm));
1720 PetscCall(PicurvAssertRealNear(0.0, difference_norm, 1e-12,
1721 "assembly must be unchanged after residual and MFFD products"));
1722 PetscCall(VecDestroy(&product)); PetscCall(VecDestroy(&direction));
1723 PetscCall(MatDestroy(&mffd)); PetscCall(SNESDestroy(&snes)); PetscCall(MatDestroy(&saved_matrix));
1724
1725 PetscCall(VecDuplicate(x, &px));
1726 PetscCall(MatMult(preconditioning_matrix, x, px));
1727 PetscCall(VecNorm(px, NORM_2, &px_norm));
1728 PetscCall(PicurvAssertBool((PetscBool)(!PetscIsInfOrNanReal(px_norm) && px_norm > 0.0),
1729 "point-block product must be finite and nonzero"));
1730 PetscCall(VecDestroy(&px));
1731
1732 {
1733 PetscBool assembled = PETSC_FALSE;
1734 PetscCall(MatAssembled(preconditioning_matrix, &assembled));
1735 PetscCall(PicurvAssertBool(assembled, "engine must perform final matrix assembly"));
1736 }
1737 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &matrix_norm));
1738 PetscCall(MatShift(preconditioning_matrix, 7.0));
1739 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1740 PetscCall(MatGetInfo(preconditioning_matrix, MAT_GLOBAL_SUM,
1741 &repeated_allocation_info));
1742 PetscCall(PicurvAssertRealNear((PetscReal)initial_allocation_info.nz_allocated,
1743 (PetscReal)repeated_allocation_info.nz_allocated, 0.0,
1744 "repeated assembly must retain exact allocated storage"));
1745 PetscCall(PicurvAssertRealNear((PetscReal)initial_allocation_info.mallocs,
1746 (PetscReal)repeated_allocation_info.mallocs, 0.0,
1747 "repeated assembly must not add insertion reallocations"));
1748 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &reassembled_norm));
1749 PetscCall(PicurvAssertRealNear(matrix_norm, reassembled_norm, 1e-12,
1750 "repeated engine assembly must clear old entries"));
1751 {
1752 PetscScalar value = 0.0;
1753 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1754 conditioned, conditioned, &value));
1755 PetscCall(PicurvAssertRealNear(1.0, PetscRealPart(value), 1e-14,
1756 "repeated engine assembly must clear old entries"));
1757 }
1758 PetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
1759 PetscCall(KSPSetOperators(ksp, preconditioning_matrix, preconditioning_matrix));
1760 PetscCall(KSPGetPC(ksp, &pc));
1761 PetscCall(MomentumPreconditionerEngine_ConfigurePetscPC(&engine, pc));
1762 PetscCall(MomentumPreconditionerEngine_ValidatePetscPC(&engine, pc));
1763 PetscCall(KSPSetUp(ksp));
1764 PetscCall(DMCreateGlobalVector(user->fda, &pc_rhs));
1765 PetscCall(DMCreateGlobalVector(user->fda, &pc_solution));
1766 PetscCall(VecSet(pc_rhs, 1.0));
1767 PetscCall(PCApply(pc, pc_rhs, pc_solution));
1768 PetscCall(VecDestroy(&pc_solution)); PetscCall(VecDestroy(&pc_rhs));
1769 PetscCall(KSPDestroy(&ksp));
1770 PetscCall(MomentumPreconditionerEngine_Destroy(&engine));
1771 PetscCall(PicurvAssertBool((PetscBool)(engine.preconditioning_matrix == NULL),
1772 "engine destroy must clear its owned matrix"));
1773 PetscCall(VecDestroy(&f)); PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->Rhs));
1774 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1775 PetscFunctionReturn(PETSC_SUCCESS);
1776}
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 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 MomentumPreconditionerEngine_Assemble(MomentumPreconditionerEngine *engine, UserCtx *user, Vec current_solution)
Runs model insertion, common row handling, and final assembly.
static PetscErrorCode MomentumPreconditionerEngine_Destroy(MomentumPreconditionerEngine *engine)
Destroys only a separately owned preconditioning matrix.
static PetscErrorCode PreconditionerMatrixStencilEntry(UserCtx *user, Mat preconditioning_matrix, MatStencil row, MatStencil col, PetscScalar *value)
Reads one DMDA-stencil matrix entry through collective basis vectors.
static PetscErrorCode AssertExactPointBlockMatrixAllocation(UserCtx *user, Mat matrix, PetscBool require_offrank_periodic)
Verifies the exact AIJ layout and preallocation derived from row classes.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ AssertPeriodicPreconditionerRow()

static PetscErrorCode AssertPeriodicPreconditionerRow ( UserCtx *  user,
Mat  matrix,
MatStencil  row,
const char *  message 
)
static

Proves that one periodic matrix row has only its exact +1/-1 pair.

Definition at line 1779 of file test_momentum_newton_krylov.c.

1781{
1782 AO ao = NULL;
1783 PetscInt ri, rj, rk, global_row, global_rep, lo, hi, local_checked = 0, checked = 0;
1784 PetscInt ncols = 0;
1785 const PetscInt *cols = NULL;
1786 const PetscScalar *values = NULL;
1787 MomentumRowType type;
1788
1789 PetscFunctionBeginUser;
1790 type = ClassifyMomentumRow(user, row.i, row.j, row.k, row.c, &ri, &rj, &rk);
1791 PetscCall(PicurvAssertIntEqual(MOM_ROW_PERIODIC_DUPLICATE, type, message));
1792 global_row = row.c + 3 * (row.i + user->info.mx * (row.j + user->info.my * row.k));
1793 PetscCall(DMDAGetAO(user->fda, &ao));
1794 PetscCall(AOApplicationToPetsc(ao, 1, &global_row));
1795 PetscCall(MatGetOwnershipRange(matrix, &lo, &hi));
1796 if (global_row >= lo && global_row < hi) {
1797 PetscBool found_self = PETSC_FALSE, found_rep = PETSC_FALSE;
1798 PetscInt nonzero_entries = 0;
1799 PetscCall(TestStencilToGlobal(user,
1800 (MatStencil){.i = ri, .j = rj, .k = rk, .c = row.c}, &global_rep));
1801 PetscCall(MatGetRow(matrix, global_row, &ncols, &cols, &values));
1802 for (PetscInt n = 0; n < ncols; ++n) {
1803 if (PetscAbsScalar(values[n]) <= 1e-14) continue;
1804 nonzero_entries++;
1805 if (cols[n] == global_row) {
1806 found_self = PETSC_TRUE;
1807 PetscCall(PicurvAssertRealNear(1.0, PetscRealPart(values[n]), 1e-14,
1808 "periodic row self entry"));
1809 } else if (cols[n] == global_rep) {
1810 found_rep = PETSC_TRUE;
1811 PetscCall(PicurvAssertRealNear(-1.0, PetscRealPart(values[n]), 1e-14,
1812 "periodic row representative entry"));
1813 } else {
1814 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB,
1815 "Periodic row contains an unintended nonzero column.");
1816 }
1817 }
1818 PetscCall(PicurvAssertIntEqual(2, nonzero_entries,
1819 "periodic row must contain exactly two numerical entries"));
1820 PetscCall(PicurvAssertBool((PetscBool)(found_self && found_rep), message));
1821 PetscCall(MatRestoreRow(matrix, global_row, &ncols, &cols, &values));
1822 local_checked = 1;
1823 }
1824 PetscCallMPI(MPI_Allreduce(&local_checked, &checked, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
1825 PetscCall(PicurvAssertIntEqual(1, checked, "periodic row must have exactly one matrix owner"));
1826 PetscFunctionReturn(PETSC_SUCCESS);
1827}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestJacobianInterfaceAndBaselineAlias()

static PetscErrorCode TestJacobianInterfaceAndBaselineAlias ( void  )
static

Verifies Jacobian creation/registration and baseline alias ownership.

Definition at line 1830 of file test_momentum_newton_krylov.c.

1831{
1832 SimCtx *simCtx = NULL;
1833 UserCtx *user = NULL;
1834 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1836 MomentumPreconditionerDescription description = {
1838 };
1839 SNES snes = NULL;
1840 Mat jacobian_operator = NULL, preconditioning_matrix = NULL;
1841 KSP ksp = NULL;
1842 PC pc = NULL;
1843 PetscInt rows = 0, cols = 0;
1844 const char *jacobian_prefix = NULL;
1845 const char *pc_type = NULL;
1846 PetscBool prefix_matches = PETSC_FALSE;
1847 PetscBool pc_is_none = PETSC_FALSE;
1848
1849 PetscFunctionBeginUser;
1850 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1851 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1852 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1853 PetscCall(SNESSetDM(snes, user->fda));
1854 ctx.user = user;
1857 PetscCall(SNESSetFunction(snes, NULL, MomentumNewtonKrylov_FormResidual, &ctx));
1858 PetscCall(MomentumNewtonJacobian_Create(snes, &ctx.jacobian));
1859 PetscCall(MatGetOptionsPrefix(ctx.jacobian.jacobian_operator, &jacobian_prefix));
1860 PetscCall(PetscStrcmp(jacobian_prefix, "mom_nk_", &prefix_matches));
1861 PetscCall(PicurvAssertBool(prefix_matches,
1862 "Jacobian interface must apply the application prefix"));
1864 user, ctx.jacobian.jacobian_operator, &description, &ctx.preconditioning_engine));
1866 snes, &ctx.jacobian, &ctx.preconditioning_engine, &ctx));
1867 PetscCall(SNESGetKSP(snes, &ksp));
1868 PetscCall(KSPGetPC(ksp, &pc));
1870 &ctx.preconditioning_engine, pc));
1872 &ctx.preconditioning_engine, pc));
1873 PetscCall(PCGetType(pc, &pc_type));
1874 PetscCall(PetscStrcmp(pc_type, PCNONE, &pc_is_none));
1875 PetscCall(PicurvAssertBool(pc_is_none,
1876 "baseline engine must derive PETSc PCNONE"));
1877 PetscCall(SNESGetJacobian(snes, &jacobian_operator, &preconditioning_matrix, NULL, NULL));
1878 PetscCall(PicurvAssertBool(
1879 (PetscBool)(jacobian_operator == ctx.jacobian.jacobian_operator),
1880 "SNES must receive the Jacobian-interface MFFD operator"));
1881 PetscCall(PicurvAssertBool((PetscBool)(preconditioning_matrix == jacobian_operator),
1882 "baseline preconditioning matrix must alias the Jacobian"));
1884 "baseline engine must record matrix aliasing"));
1886 "baseline engine must not own the Jacobian alias"));
1888 PetscCall(MatGetSize(ctx.jacobian.jacobian_operator, &rows, &cols));
1889 PetscCall(PicurvAssertBool((PetscBool)(rows > 0 && cols > 0),
1890 "destroying an alias engine must preserve the Jacobian"));
1892 PetscCall(SNESDestroy(&snes));
1893 PetscCall(VecDestroy(&user->Rhs));
1894 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1895 PetscFunctionReturn(PETSC_SUCCESS);
1896}
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 MomentumNewtonJacobian_Create(SNES snes, MomentumNewtonJacobian *jacobian)
Creates the selected Jacobian operator; currently PETSc MFFD only.
MomentumPreconditionerEngine preconditioning_engine
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPointBlockPeriodicAssembly()

static PetscErrorCode TestPointBlockPeriodicAssembly ( void  )
static

Exercises engine-owned periodic duplicate rows on every MPI layout.

Definition at line 1899 of file test_momentum_newton_krylov.c.

1900{
1901 SimCtx *simCtx = NULL;
1902 UserCtx *user = NULL;
1903 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1905 MomentumPreconditionerDescription description = {
1908 0,
1909 0
1910 };
1911 Vec x = NULL, f = NULL;
1912 PetscReal matrix_norm = 0.0;
1913
1914 PetscFunctionBeginUser;
1915 PetscCall(BuildNewtonFixture(periodic_xyz_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1916 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1917 PetscCall(VecDuplicate(user->Ucont, &x));
1918 PetscCall(VecDuplicate(user->Ucont, &f));
1919 PetscCall(VecCopy(user->Ucont, x));
1920 ctx.user = user;
1921 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
1923 user, NULL, &description, &ctx.preconditioning_engine));
1926 user, ctx.preconditioning_engine.preconditioning_matrix, PETSC_TRUE));
1927 PetscCall(MatNorm(ctx.preconditioning_engine.preconditioning_matrix,
1928 NORM_FROBENIUS, &matrix_norm));
1929 PetscCall(PicurvAssertBool((PetscBool)(matrix_norm > 0.0),
1930 "periodic point-block assembly must produce a nonzero matrix"));
1931 PetscCall(AssertPeriodicPreconditionerRow(user,
1933 (MatStencil){.i = 0, .j = 2, .k = 3, .c = 0},
1934 "single-axis periodic row must contain exact +1/-1 entries"));
1935 PetscCall(AssertPeriodicPreconditionerRow(user,
1937 (MatStencil){.i = 0, .j = 0, .k = 3, .c = 1},
1938 "periodic intersection must contain exact +1/-1 entries"));
1939 PetscCall(AssertPeriodicPreconditionerRow(user,
1941 (MatStencil){.i = 0, .j = 0, .k = 0, .c = 2},
1942 "periodic origin intersection must contain exact +1/-1 entries"));
1944 PetscCall(VecDestroy(&f));
1945 PetscCall(VecDestroy(&x));
1946 PetscCall(VecDestroy(&user->Rhs));
1947 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1948 PetscFunctionReturn(PETSC_SUCCESS);
1949}
static PetscErrorCode AssertPeriodicPreconditionerRow(UserCtx *user, Mat matrix, MatStencil row, const char *message)
Proves that one periodic matrix row has only its exact +1/-1 pair.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char **  argv 
)

Runs the focused Newton–Krylov unit suite.

Parameters
argcCommand-line argument count.
argvCommand-line argument vector.
Returns
Process exit status.

Definition at line 1957 of file test_momentum_newton_krylov.c.

1958{
1959 PetscErrorCode ierr;
1960 const PicurvTestCase cases[] = {
1961 {"residual-repeatability-and-input-integrity", TestResidualRepeatabilityAndInputIntegrity},
1962 {"constraint-rows", TestConstraintRows},
1963 {"fixed-constraint-derivatives-all-faces", TestFixedConstraintDerivativesAllFaces},
1964 {"inlet-outlet-constraint-derivatives", TestInletOutletConstraintDerivatives},
1965 {"periodic-constraint-derivatives-and-intersections", TestPeriodicConstraintDerivativesAndIntersections},
1966 {"matrix-free-derivative", TestMatrixFreeDerivative},
1967 {"whole-operator-direct-jacobian", TestWholeOperatorDirectJacobian},
1968 {"periodic-operator-has-no-zero-rows", TestPeriodicOperatorHasNoZeroRows},
1969 {"zero-iteration-structured-logging", TestZeroIterationStructuredLogging},
1970 {"flat-channel-bdf1-startup", TestFlatChannelStartup},
1971 {"restart-and-continuation-solve", TestRestartAndContinuationSolve},
1972 {"small-solve-and-rollback", TestSmallSolveAndRollback},
1973 {"free-slip-walls-solve", TestFreeSlipWallsSolve},
1974 {"two-d-solve-holds-masked-component", TestTwoDSolveHoldsMaskedComponent},
1975 {"unsupported-configuration-fails-before-allocation", TestUnsupportedConfigurationFailsBeforeAllocation},
1976 {"post-allocation-failure-cleanup", TestPostAllocationFailureCleanup},
1977 {"linearization-config-parsing", TestLinearizationConfigParsing},
1978 {"point-block-preconditioner-engine", TestPointBlockPreconditionerEngine},
1979 {"jacobian-interface-and-baseline-alias", TestJacobianInterfaceAndBaselineAlias},
1980 {"point-block-periodic-assembly", TestPointBlockPeriodicAssembly},
1981 };
1982
1983 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv Newton Krylov tests");
1984 if (ierr) return (int)ierr;
1985 ierr = PicurvRunTests("unit-newton-krylov", cases, sizeof(cases) / sizeof(cases[0]));
1986 if (PetscFinalize()) return 1;
1987 return (int)ierr;
1988}
static PetscErrorCode TestRestartAndContinuationSolve(void)
Verifies restarted Newton solves with both supported preconditioners.
static PetscErrorCode TestUnsupportedConfigurationFailsBeforeAllocation(void)
Confirms unsupported features fail before workspace allocation.
static PetscErrorCode TestPeriodicOperatorHasNoZeroRows(void)
Audits every row of a complete operator containing periodic duplicates.
static PetscErrorCode TestSmallSolveAndRollback(void)
Exercises a converged solve, a committed unconverged solve, a non-finite rollback,...
static PetscErrorCode TestConstraintRows(void)
Verifies fixed, periodic-duplicate, and interior residual rows.
static PetscErrorCode TestPointBlockPreconditionerEngine(void)
Verifies the point-block model and common preconditioning-engine wiring.
static PetscErrorCode TestFlatChannelStartup(void)
Guards flat_channel's initial BDF1 residual and both shipped NK PCs.
static PetscErrorCode TestResidualRepeatabilityAndInputIntegrity(void)
Verifies repeatable callback output and read-only trial input.
static PetscErrorCode TestTwoDSolveHoldsMaskedComponent(void)
A TwoD solve converges, preconditioned and not, and holds the masked component.
static PetscErrorCode TestPeriodicConstraintDerivativesAndIntersections(void)
Proves single-, double-, triple-, and mixed-boundary periodic equations.
static PetscErrorCode TestFreeSlipWallsSolve(void)
A Newton solve with free-slip walls converges, preconditioned and not.
static PetscErrorCode TestWholeOperatorDirectJacobian(void)
Forms the complete direct FD Jacobian, checks every row, and compares MFFD actions.
static PetscErrorCode TestLinearizationConfigParsing(void)
Verifies finalized application-owned linearization option parsing.
static PetscErrorCode TestFixedConstraintDerivativesAllFaces(void)
Proves unit derivatives for every nonperiodic stored-row category and face.
static PetscErrorCode TestPostAllocationFailureCleanup(void)
Verifies cleanup and rollback after an options failure following asset creation.
static PetscErrorCode TestJacobianInterfaceAndBaselineAlias(void)
Verifies Jacobian creation/registration and baseline alias ownership.
static PetscErrorCode TestPointBlockPeriodicAssembly(void)
Exercises engine-owned periodic duplicate rows on every MPI layout.
static PetscErrorCode TestMatrixFreeDerivative(void)
Compares PETSc's matrix-free action with direct differencing.
static PetscErrorCode TestZeroIterationStructuredLogging(void)
Verifies the six-wall zero-velocity case logs zero Newton/Krylov work.
static PetscErrorCode TestInletOutletConstraintDerivatives(void)
Proves admitted inlet and outlet face-normal rows have unit self derivatives.
PetscErrorCode PicurvRunTests(const char *suite_name, const PicurvTestCase *cases, size_t case_count)
Runs a named C test suite and prints pass/fail progress markers.
Named test case descriptor consumed by PicurvRunTests.
Here is the call graph for this function:

Variable Documentation

◆ geometric_periodic_bcs

const char* geometric_periodic_bcs
static
Initial value:
=
"-Xi PERIODIC geometric\n"
"+Xi PERIODIC geometric\n"
"-Eta WALL noslip\n"
"+Eta WALL noslip\n"
"-Zeta INLET prescribed_velocity source_id=0\n"
"+Zeta OUTLET conservation\n"

Definition at line 20 of file test_momentum_newton_krylov.c.

◆ free_slip_wall_bcs

const char* free_slip_wall_bcs
static
Initial value:
=
"-Xi WALL free_slip\n"
"+Xi WALL free_slip\n"
"-Eta WALL noslip\n"
"+Eta WALL noslip\n"
"-Zeta WALL noslip\n"
"+Zeta WALL noslip\n"

Definition at line 30 of file test_momentum_newton_krylov.c.

◆ fixed_wall_bcs

const char* fixed_wall_bcs
static
Initial value:
=
"-Xi WALL noslip\n"
"+Xi WALL noslip\n"
"-Eta WALL noslip\n"
"+Eta WALL noslip\n"
"-Zeta WALL noslip\n"
"+Zeta WALL noslip\n"

Definition at line 38 of file test_momentum_newton_krylov.c.

◆ parabolic_bcs

const char* parabolic_bcs
static
Initial value:
=
"-Xi WALL noslip\n"
"+Xi WALL noslip\n"
"-Eta WALL noslip\n"
"+Eta WALL noslip\n"
"-Zeta INLET prescribed_velocity source_id=1\n"
"+Zeta OUTLET conservation\n"

Definition at line 46 of file test_momentum_newton_krylov.c.

◆ periodic_x_bcs

const char* periodic_x_bcs
static
Initial value:
=
"-Xi PERIODIC geometric\n+Xi PERIODIC geometric\n"
"-Eta WALL noslip\n+Eta WALL noslip\n-Zeta WALL noslip\n+Zeta WALL noslip\n"

Definition at line 54 of file test_momentum_newton_krylov.c.

◆ periodic_y_bcs

const char* periodic_y_bcs
static
Initial value:
=
"-Xi WALL noslip\n+Xi WALL noslip\n"
"-Eta PERIODIC geometric\n+Eta PERIODIC geometric\n-Zeta WALL noslip\n+Zeta WALL noslip\n"

Definition at line 58 of file test_momentum_newton_krylov.c.

◆ periodic_z_bcs

const char* periodic_z_bcs
static
Initial value:
=
"-Xi WALL noslip\n+Xi WALL noslip\n-Eta WALL noslip\n+Eta WALL noslip\n"
"-Zeta PERIODIC geometric\n+Zeta PERIODIC geometric\n"

Definition at line 62 of file test_momentum_newton_krylov.c.

◆ periodic_xy_bcs

const char* periodic_xy_bcs
static
Initial value:
=
"-Xi PERIODIC geometric\n+Xi PERIODIC geometric\n"
"-Eta PERIODIC geometric\n+Eta PERIODIC geometric\n"
"-Zeta WALL noslip\n+Zeta WALL noslip\n"

Definition at line 66 of file test_momentum_newton_krylov.c.

◆ periodic_xyz_bcs

const char* periodic_xyz_bcs
static
Initial value:
=
"-Xi PERIODIC geometric\n+Xi PERIODIC geometric\n"
"-Eta PERIODIC geometric\n+Eta PERIODIC geometric\n"
"-Zeta PERIODIC geometric\n+Zeta PERIODIC geometric\n"

Definition at line 71 of file test_momentum_newton_krylov.c.