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
 

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 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, forced rollback, and per-call 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 * 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.

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 65 of file test_momentum_newton_krylov.c.

67{
68 FILE *file = NULL;
69 char line[4096];
70 PetscInt rows = 0;
71 PetscBool found_a = needle_a ? PETSC_FALSE : PETSC_TRUE;
72 PetscBool found_b = needle_b ? PETSC_FALSE : PETSC_TRUE;
73
74 PetscFunctionBeginUser;
75 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
76 file = fopen(path, "r");
77 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
78 "Expected Newton log does not exist: %s", path);
79 while (fgets(line, sizeof(line), file)) {
80 if (strncmp(line, "step:", 5) == 0) rows++;
81 if (needle_a && strstr(line, needle_a)) found_a = PETSC_TRUE;
82 if (needle_b && strstr(line, needle_b)) found_b = PETSC_TRUE;
83 }
84 fclose(file);
85 if (expected_rows >= 0) {
86 PetscCall(PicurvAssertIntEqual(expected_rows, rows,
87 "Newton log must contain one nonduplicated row per solve"));
88 } else {
89 PetscCall(PicurvAssertBool((PetscBool)(rows >= -expected_rows),
90 "enabled Newton history must contain the expected iteration rows"));
91 }
92 PetscCall(PicurvAssertBool(found_a, "Newton log is missing required structured content"));
93 PetscCall(PicurvAssertBool(found_b, "Newton log is missing required structured content"));
94 PetscFunctionReturn(PETSC_SUCCESS);
95}
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 106 of file test_momentum_newton_krylov.c.

108{
109 PetscFunctionBeginUser;
110 PetscCall(PicurvBuildTinyRuntimeContext(bcs, PETSC_FALSE, simCtx, user, tmpdir, tmpdir_len));
111 PetscCall(InitializeEulerianState(*simCtx));
112 (*simCtx)->mom_solver_type = MOMENTUM_SOLVER_NEWTON_KRYLOV;
113 PetscCall(PicurvAssertBool((PetscBool)((*user)->Rhs == NULL),
114 "Newton fixture must enter with no persistent Rhs workspace"));
115 PetscFunctionReturn(PETSC_SUCCESS);
116}
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:702
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 124 of file test_momentum_newton_krylov.c.

125{
126 PetscFunctionBeginUser;
127 PetscCall(PicurvDestroyRuntimeContext(simCtx));
128 PetscCall(PicurvRemoveTempDir(tmpdir));
129 PetscFunctionReturn(PETSC_SUCCESS);
130}
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 137 of file test_momentum_newton_krylov.c.

138{
139 FILE *fd = NULL;
140
141 PetscFunctionBeginUser;
142 fd = fopen(path, "w");
143 PetscCheck(fd != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
144 "Could not create Newton test PICSLICE %s.", path);
145 PetscCheck(fprintf(fd, "PICSLICE\n1\n5 5\n") >= 0,
146 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE, "Could not write PICSLICE header.");
147 for (PetscInt row = 0; row < 25; ++row) {
148 PetscCheck(fprintf(fd, "%.16e\n", 1.0 + 0.01 * (double)row) >= 0,
149 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE, "Could not write PICSLICE value.");
150 }
151 fclose(fd);
152 PetscFunctionReturn(PETSC_SUCCESS);
153}
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 161 of file test_momentum_newton_krylov.c.

162{
163 SimCtx *simCtx = NULL;
164 UserCtx *user = NULL;
165 char tmpdir[PETSC_MAX_PATH_LEN] = "";
166 Vec x = NULL, x_copy = NULL, f1 = NULL, f2 = NULL, delta = NULL;
167 PetscReal norm = 0.0;
169
170 PetscFunctionBeginUser;
171 PetscCall(BuildNewtonFixture(bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
172 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
173 PetscCall(VecDuplicate(user->Ucont, &x));
174 PetscCall(VecDuplicate(user->Ucont, &x_copy));
175 PetscCall(VecDuplicate(user->Ucont, &f1));
176 PetscCall(VecDuplicate(user->Ucont, &f2));
177 PetscCall(VecDuplicate(user->Ucont, &delta));
178 PetscCall(VecCopy(user->Ucont, x));
179 PetscCall(VecShift(x, 0.125));
180 PetscCall(VecCopy(x, x_copy));
181 ctx.user = user;
182
183 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f1, &ctx));
184 /* Poison every piece of hidden state a non-deterministic residual could lean
185 * on: the boundary flux/area diagnostics AND the persistent Cartesian fields
186 * (Ucat/lUcat). The conservation-outlet handler reads lUcat during its first
187 * boundary sweep, so a callback that does not reconstruct the Cartesian state
188 * from X before applying boundary conditions would produce a different F here.
189 * This assertion therefore fails if the deterministic pre-boundary seed in
190 * MomentumNewtonKrylov_FormResidual() is ever removed. */
191 simCtx->FluxInSum = 1234.0;
192 simCtx->FluxOutSum = -4321.0;
193 simCtx->FarFluxInSum = 77.0;
194 simCtx->FarFluxOutSum = -88.0;
195 PetscCall(VecSet(user->Ucat, 7.0));
196 PetscCall(VecSet(user->lUcat, 7.0));
197 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f2, &ctx));
198 PetscCall(VecWAXPY(delta, -1.0, f1, f2));
199 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
200 PetscCheck(norm <= 1.0e-13, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
201 "%s residual changed between identical evaluations (inf norm=%g).", label, (double)norm);
202 PetscCall(VecNorm(delta, NORM_2, &norm));
203 PetscCheck(norm <= 1.0e-12, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
204 "%s residual changed between identical evaluations (L2 norm=%g).", label, (double)norm);
205 PetscCall(VecWAXPY(delta, -1.0, x_copy, x));
206 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
207 PetscCheck(norm == 0.0, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
208 "%s residual callback modified X (norm=%g).", label, (double)norm);
209
210 PetscCall(VecDestroy(&delta));
211 PetscCall(VecDestroy(&f2));
212 PetscCall(VecDestroy(&f1));
213 PetscCall(VecDestroy(&x_copy));
214 PetscCall(VecDestroy(&x));
215 PetscCall(VecDestroy(&user->Rhs));
216 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
217 PetscFunctionReturn(PETSC_SUCCESS);
218}
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:965
PetscReal FarFluxOutSum
Definition variables.h:965
PetscReal FluxOutSum
Definition variables.h:965
Vec Ucont
Definition variables.h:1111
PetscReal FluxInSum
Definition variables.h:965
Vec lUcat
Definition variables.h:1111
The master context for the entire simulation.
Definition variables.h:864
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1071
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 221 of file test_momentum_newton_krylov.c.

222{
223 return (PetscBool)(i >= user->info.xs && i < user->info.xs + user->info.xm &&
224 j >= user->info.ys && j < user->info.ys + user->info.ym &&
225 k >= user->info.zs && k < user->info.zs + user->info.zm);
226}
DMDALocalInfo info
Definition variables.h:1083
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 239 of file test_momentum_newton_krylov.c.

241{
242 Cmpnts ***a = NULL;
243
244 PetscFunctionBeginUser;
245 if (OwnsStoredPoint(user, i, j, k)) {
246 PetscCall(DMDAVecGetArray(user->fda, vec, &a));
247 if (component == 0) a[k][j][i].x += delta;
248 else if (component == 1) a[k][j][i].y += delta;
249 else a[k][j][i].z += delta;
250 PetscCall(DMDAVecRestoreArray(user->fda, vec, &a));
251 }
252 PetscFunctionReturn(PETSC_SUCCESS);
253}
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:122
PetscScalar z
Definition variables.h:122
PetscScalar y
Definition variables.h:122
A 3D point or vector with PetscScalar components.
Definition variables.h:121
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 266 of file test_momentum_newton_krylov.c.

268{
269 Cmpnts ***a = NULL;
270 PetscScalar local = 0.0;
271
272 PetscFunctionBeginUser;
273 if (OwnsStoredPoint(user, i, j, k)) {
274 PetscCall(DMDAVecGetArrayRead(user->fda, vec, &a));
275 local = component == 0 ? a[k][j][i].x : (component == 1 ? a[k][j][i].y : a[k][j][i].z);
276 PetscCall(DMDAVecRestoreArrayRead(user->fda, vec, &a));
277 }
278 PetscCallMPI(MPI_Allreduce(&local, value, 1, MPIU_SCALAR, MPIU_SUM, PETSC_COMM_WORLD));
279 PetscFunctionReturn(PETSC_SUCCESS);
280}
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 297 of file test_momentum_newton_krylov.c.

302{
303 const PetscReal epsilon = 1.0e-6;
304 Vec f0 = NULL, fp = NULL, xp = NULL;
305 PetscScalar base_value = 0.0, perturbed_value = 0.0;
306 MomentumNewtonKrylovContext ctx = {user};
307
308 PetscFunctionBeginUser;
309 PetscCall(VecDuplicate(x, &f0));
310 PetscCall(VecDuplicate(x, &fp));
311 PetscCall(VecDuplicate(x, &xp));
312 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
313 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
314 PetscCall(VecCopy(x, xp));
315 PetscCall(PerturbStoredValue(user, xp, col_i, col_j, col_k, col_component, epsilon));
316 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, xp, fp, &ctx));
317 PetscCall(GetStoredValue(user, f0, row_i, row_j, row_k, row_component, &base_value));
318 PetscCall(GetStoredValue(user, fp, row_i, row_j, row_k, row_component, &perturbed_value));
319 *derivative = PetscRealPart((perturbed_value - base_value) / epsilon);
320 PetscCall(VecDestroy(&xp));
321 PetscCall(VecDestroy(&fp));
322 PetscCall(VecDestroy(&f0));
323 PetscFunctionReturn(PETSC_SUCCESS);
324}
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 343 of file test_momentum_newton_krylov.c.

348{
349 PetscReal derivative = 0.0;
350
351 PetscFunctionBeginUser;
352 PetscCall(MeasureStoredDerivative(user, x, row_i, row_j, row_k, row_component,
353 col_i, col_j, col_k, col_component, &derivative));
354 PetscCall(PicurvAssertRealNear(expected, derivative, tolerance, label));
355 PetscFunctionReturn(PETSC_SUCCESS);
356}
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 359 of file test_momentum_newton_krylov.c.

360{
361 char profile_dir[PETSC_MAX_PATH_LEN] = "";
362 char profile_path[PETSC_MAX_PATH_LEN];
363 char file_bcs[2 * PETSC_MAX_PATH_LEN];
364
365 PetscFunctionBeginUser;
366 PetscCall(CheckResidualRepeatabilityForBC(fixed_wall_bcs, "fixed walls"));
367 PetscCall(CheckResidualRepeatabilityForBC(NULL, "constant inlet/conservation outlet"));
368 PetscCall(CheckResidualRepeatabilityForBC(parabolic_bcs, "parabolic inlet/conservation outlet"));
369 PetscCall(CheckResidualRepeatabilityForBC(periodic_x_bcs, "x periodic"));
370 PetscCall(CheckResidualRepeatabilityForBC(periodic_y_bcs, "y periodic"));
371 PetscCall(CheckResidualRepeatabilityForBC(periodic_z_bcs, "z periodic"));
372 PetscCall(CheckResidualRepeatabilityForBC(periodic_xy_bcs, "mixed x-y periodic"));
373
374 PetscCall(PicurvMakeTempDir(profile_dir, sizeof(profile_dir)));
375 PetscCall(PetscSNPrintf(profile_path, sizeof(profile_path), "%s/inlet.picslice", profile_dir));
376 PetscCall(WriteNewtonPicSlice(profile_path));
377 PetscCall(PetscSNPrintf(file_bcs, sizeof(file_bcs),
378 "-Xi WALL noslip\n+Xi WALL noslip\n-Eta WALL noslip\n+Eta WALL noslip\n"
379 "-Zeta INLET prescribed_flow source_file=%s\n+Zeta OUTLET conservation\n", profile_path));
380 PetscCall(CheckResidualRepeatabilityForBC(file_bcs, "file inlet/conservation outlet"));
381 PetscCall(PicurvRemoveTempDir(profile_dir));
382 PetscFunctionReturn(PETSC_SUCCESS);
383}
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 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 386 of file test_momentum_newton_krylov.c.

387{
388 SimCtx *simCtx = NULL;
389 UserCtx *user = NULL;
390 char tmpdir[PETSC_MAX_PATH_LEN] = "";
391 Vec x = NULL, f = NULL;
392 Cmpnts ***xa = NULL, ***fa = NULL, ***conditioned = NULL, ***rhs = NULL;
394
395 PetscFunctionBeginUser;
396 PetscCall(BuildNewtonFixture(geometric_periodic_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
397 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
398 PetscCall(VecDuplicate(user->Ucont, &x));
399 PetscCall(VecDuplicate(user->Ucont, &f));
400 PetscCall(VecSet(x, 0.25));
401 PetscCall(DMDAVecGetArray(user->fda, x, &xa));
402 if (user->info.xs == 0 && 2 >= user->info.ys && 2 < user->info.ys + user->info.ym &&
403 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) xa[2][2][0].x = 3.0;
404 if (user->info.xs + user->info.xm == user->info.mx &&
405 2 >= user->info.ys && 2 < user->info.ys + user->info.ym &&
406 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) xa[2][2][user->info.mx - 2].x = 1.25;
407 PetscCall(DMDAVecRestoreArray(user->fda, x, &xa));
408 ctx.user = user;
409 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
410
411 PetscCall(DMDAVecGetArrayRead(user->fda, x, &xa));
412 PetscCall(DMDAVecGetArrayRead(user->fda, f, &fa));
413 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &conditioned));
414 PetscCall(DMDAVecGetArrayRead(user->fda, user->Rhs, &rhs));
415 if (user->info.xs == 0 && 2 >= user->info.ys && 2 < user->info.ys + user->info.ym &&
416 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) {
417 PetscCall(PicurvAssertRealNear(1.75, fa[2][2][0].x, 1.0e-12,
418 "periodic duplicate row must be Xdup-Xrep"));
419 }
420 if (user->info.ys == 0 && 2 >= user->info.xs && 2 < user->info.xs + user->info.xm &&
421 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) {
422 PetscCall(PicurvAssertRealNear(xa[2][0][2].y - conditioned[2][0][2].y,
423 fa[2][0][2].y, 1.0e-12,
424 "fixed wall row must be X minus conditioned boundary value"));
425 }
426 if (2 >= user->info.xs && 2 < user->info.xs + user->info.xm &&
427 2 >= user->info.ys && 2 < user->info.ys + user->info.ym &&
428 2 >= user->info.zs && 2 < user->info.zs + user->info.zm) {
429 PetscCall(PicurvAssertRealNear(-rhs[2][2][2].z, fa[2][2][2].z, 1.0e-12,
430 "unconstrained interior row must retain the physical residual"));
431 }
432 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Rhs, &rhs));
433 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucont, &conditioned));
434 PetscCall(DMDAVecRestoreArrayRead(user->fda, f, &fa));
435 PetscCall(DMDAVecRestoreArrayRead(user->fda, x, &xa));
436
437 PetscCall(VecDestroy(&f));
438 PetscCall(VecDestroy(&x));
439 PetscCall(VecDestroy(&user->Rhs));
440 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
441 PetscFunctionReturn(PETSC_SUCCESS);
442}
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 445 of file test_momentum_newton_krylov.c.

446{
447 SimCtx *simCtx = NULL;
448 UserCtx *user = NULL;
449 char tmpdir[PETSC_MAX_PATH_LEN] = "";
450 Vec x = NULL;
451 const PetscInt size[3] = {7, 7, 7};
452
453 PetscFunctionBeginUser;
454 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
455 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
456 PetscCall(VecDuplicate(user->Ucont, &x));
457 PetscCall(VecSet(x, 0.2));
458
459 for (PetscInt axis = 0; axis < 3; ++axis) {
460 PetscInt coord[3] = {2, 2, 2}, ri, rj, rk;
461 PetscInt tangent = (axis + 1) % 3;
462 MomentumRowType row;
463
464 coord[axis] = 0;
465 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], axis, &ri, &rj, &rk);
467 "negative face normal row classification"));
468 PetscCall(CheckStoredDerivative(user, x, coord[0], coord[1], coord[2], axis,
469 coord[0], coord[1], coord[2], axis, 1.0, 1.0e-8,
470 "negative face normal fixed derivative"));
471
472 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], tangent, &ri, &rj, &rk);
474 "negative face tangential row classification"));
475 PetscCall(CheckStoredDerivative(user, x, coord[0], coord[1], coord[2], tangent,
476 coord[0], coord[1], coord[2], tangent, 1.0, 1.0e-8,
477 "negative face tangential homogeneous derivative"));
478
479 coord[axis] = size[axis] - 2;
480 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], axis, &ri, &rj, &rk);
482 "positive physical normal row classification"));
483 PetscCall(CheckStoredDerivative(user, x, coord[0], coord[1], coord[2], axis,
484 coord[0], coord[1], coord[2], axis, 1.0, 1.0e-8,
485 "positive physical normal fixed derivative"));
486 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], tangent, &ri, &rj, &rk);
488 "positive physical tangential row classification"));
489
490 coord[axis] = size[axis] - 1;
491 for (PetscInt component = 0; component < 3; ++component) {
492 row = ClassifyMomentumRow(user, coord[0], coord[1], coord[2], component, &ri, &rj, &rk);
494 "positive dummy row classification"));
495 PetscCall(CheckStoredDerivative(user, x, coord[0], coord[1], coord[2], component,
496 coord[0], coord[1], coord[2], component, 1.0, 1.0e-8,
497 "positive dummy homogeneous derivative"));
498 }
499 }
500
501 PetscCall(VecDestroy(&x));
502 PetscCall(VecDestroy(&user->Rhs));
503 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
504 PetscFunctionReturn(PETSC_SUCCESS);
505}
MomentumRowType
Classification of one staggered momentum row (location + component).
Definition Boundaries.h:252
@ MOM_ROW_FIXED_HOMOGENEOUS
Dummy/tangential row carrying no unknown at all.
Definition Boundaries.h:255
@ MOM_ROW_PHYSICAL
Independent unknown governed by the momentum equation.
Definition Boundaries.h:253
@ MOM_ROW_FIXED_CONDITIONED
Strong Dirichlet row; the value comes from ApplyBoundaryConditions().
Definition Boundaries.h:254
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:597
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 508 of file test_momentum_newton_krylov.c.

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

561{
562 SimCtx *simCtx = NULL;
563 UserCtx *user = NULL;
564 char tmpdir[PETSC_MAX_PATH_LEN] = "";
565 Vec x = NULL;
566 PetscInt size[3], dup[3] = {2, 2, 2}, rep[3] = {2, 2, 2}, unrelated[3] = {3, 3, 3};
567
568 PetscFunctionBeginUser;
569 PetscCall(BuildNewtonFixture(bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
570 size[0] = user->info.mx; size[1] = user->info.my; size[2] = user->info.mz;
571 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
572 PetscCall(VecDuplicate(user->Ucont, &x));
573 PetscCall(VecSet(x, 0.15));
574 for (PetscInt side = 0; side < 2; ++side) {
575 dup[axis] = side == 0 ? 0 : size[axis] - 1;
576 rep[axis] = side == 0 ? size[axis] - 2 : 1;
577 for (PetscInt component = 0; component < 3; ++component) {
578 PetscCall(CheckStoredDerivative(user, x, dup[0], dup[1], dup[2], component,
579 dup[0], dup[1], dup[2], component, 1.0, 1.0e-8,
580 "periodic duplicate self derivative"));
581 PetscCall(CheckStoredDerivative(user, x, dup[0], dup[1], dup[2], component,
582 rep[0], rep[1], rep[2], component, -1.0, 1.0e-8,
583 "periodic representative derivative"));
584 PetscCall(CheckStoredDerivative(user, x, dup[0], dup[1], dup[2], component,
585 unrelated[0], unrelated[1], unrelated[2], (component + 1) % 3, 0.0, 1.0e-8,
586 "periodic constraint unrelated derivative"));
587 }
588 }
589 PetscCall(VecDestroy(&x));
590 PetscCall(VecDestroy(&user->Rhs));
591 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
592 PetscFunctionReturn(PETSC_SUCCESS);
593}
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 596 of file test_momentum_newton_krylov.c.

597{
598 SimCtx *simCtx = NULL;
599 UserCtx *user = NULL;
600 char tmpdir[PETSC_MAX_PATH_LEN] = "";
601 Vec x = NULL, f = NULL;
603 PetscScalar xdup, synced, residual;
604
605 PetscFunctionBeginUser;
609
610 PetscCall(BuildNewtonFixture(periodic_xy_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
611 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
612 PetscCall(VecDuplicate(user->Ucont, &x));
613 PetscCall(VecDuplicate(user->Ucont, &f));
614 PetscCall(VecSet(x, 0.0));
615 PetscCall(PerturbStoredValue(user, x, 0, 0, 2, 0, 3.0));
616 PetscCall(PerturbStoredValue(user, x, user->info.mx - 2, user->info.my - 2, 2, 0, 1.25));
617 PetscCall(VecCopy(x, user->Ucont));
618 { const FieldId fields[] = {FIELD_ID_UCONT}; PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields)); }
619 PetscCall(GetStoredValue(user, x, 0, 0, 2, 0, &xdup));
620 PetscCall(GetStoredValue(user, user->Ucont, 0, 0, 2, 0, &synced));
621 ctx.user = user;
622 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
623 PetscCall(GetStoredValue(user, f, 0, 0, 2, 0, &residual));
624 PetscCall(PicurvAssertRealNear(PetscRealPart(xdup - synced), PetscRealPart(residual), 1.0e-12,
625 "doubly periodic edge must use production synchronized representative"));
626 PetscCall(CheckStoredDerivative(user, x, 0, 0, 2, 0, 0, 0, 2, 0, 1.0, 1.0e-8,
627 "doubly periodic edge self derivative"));
628 PetscCall(CheckStoredDerivative(user, x, 0, 0, 2, 0,
629 user->info.mx - 2, user->info.my - 2, 2, 0, -1.0, 1.0e-8,
630 "doubly periodic edge representative derivative"));
631 PetscCall(VecDestroy(&f)); PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->Rhs));
632 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
633
634 tmpdir[0] = '\0';
635 PetscCall(BuildNewtonFixture(periodic_xyz_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
636 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
637 PetscCall(VecDuplicate(user->Ucont, &x)); PetscCall(VecSet(x, 0.1));
638 PetscCall(CheckStoredDerivative(user, x, 0, 0, 0, 2, 0, 0, 0, 2, 1.0, 1.0e-8,
639 "fully periodic corner self derivative"));
640 PetscCall(CheckStoredDerivative(user, x, 0, 0, 0, 2,
641 user->info.mx - 2, user->info.my - 2, user->info.mz - 2, 2,
642 -1.0, 1.0e-8, "fully periodic corner representative derivative"));
643 PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->Rhs));
644 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
645
646 tmpdir[0] = '\0';
647 PetscCall(BuildNewtonFixture(periodic_x_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
648 PetscCall(VecDuplicate(user->Ucont, &user->Rhs)); PetscCall(VecDuplicate(user->Ucont, &x));
649 PetscCall(VecSet(x, 0.1));
650 PetscCall(CheckStoredDerivative(user, x, 0, 0, 2, 1, 0, 0, 2, 1, 1.0, 1.0e-8,
651 "periodic-wall intersection self derivative"));
652 PetscCall(CheckStoredDerivative(user, x, 0, 0, 2, 1,
653 user->info.mx - 2, 0, 2, 1, -1.0, 1.0e-8,
654 "periodic-wall intersection representative derivative"));
655 PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->Rhs));
656 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
657 PetscFunctionReturn(PETSC_SUCCESS);
658}
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 661 of file test_momentum_newton_krylov.c.

662{
663 SimCtx *simCtx = NULL;
664 UserCtx *user = NULL;
665 char tmpdir[PETSC_MAX_PATH_LEN] = "";
666 SNES snes = NULL;
667 Mat J = NULL;
668 Vec x = NULL, xp = NULL, f0 = NULL, fp = NULL, v = NULL, jv = NULL, fd = NULL;
669 PetscReal h = 0.0, error = 0.0, scale = 0.0;
671
672 PetscFunctionBeginUser;
673 PetscCall(BuildNewtonFixture(NULL, &simCtx, &user, tmpdir, sizeof(tmpdir)));
674 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
675 PetscCall(VecDuplicate(user->Ucont, &x));
676 PetscCall(VecDuplicate(user->Ucont, &xp));
677 PetscCall(VecDuplicate(user->Ucont, &f0));
678 PetscCall(VecDuplicate(user->Ucont, &fp));
679 PetscCall(VecDuplicate(user->Ucont, &v));
680 PetscCall(VecDuplicate(user->Ucont, &jv));
681 PetscCall(VecDuplicate(user->Ucont, &fd));
682 PetscCall(VecCopy(user->Ucont, x));
683 PetscCall(VecShift(x, 0.05));
684 PetscCall(VecSet(v, 0.5));
685 ctx.user = user;
686
687 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
688 PetscCall(SNESSetDM(snes, user->fda));
689 PetscCall(SNESSetFunction(snes, f0, MomentumNewtonKrylov_FormResidual, &ctx));
690 PetscCall(MatCreateSNESMF(snes, &J));
691 PetscCall(MomentumNewtonKrylov_FormResidual(snes, x, f0, &ctx));
692 PetscCall(MatMFFDSetBase(J, x, f0));
693 PetscCall(MatMult(J, v, jv));
694 PetscCall(MatMFFDGetH(J, &h));
695 PetscCall(VecWAXPY(xp, h, v, x));
696 PetscCall(MomentumNewtonKrylov_FormResidual(snes, xp, fp, &ctx));
697 PetscCall(VecWAXPY(fd, -1.0, f0, fp));
698 PetscCall(VecScale(fd, 1.0 / h));
699 PetscCall(VecAXPY(fd, -1.0, jv));
700 PetscCall(VecNorm(fd, NORM_2, &error));
701 PetscCall(VecNorm(jv, NORM_2, &scale));
702 PetscCall(PicurvAssertBool((PetscBool)(error <= 1.0e-9 * PetscMax(1.0, scale)),
703 "matrix-free Jv must match direct differencing"));
704
705 PetscCall(VecDestroy(&fd));
706 PetscCall(VecDestroy(&jv));
707 PetscCall(VecDestroy(&v));
708 PetscCall(VecDestroy(&fp));
709 PetscCall(VecDestroy(&f0));
710 PetscCall(VecDestroy(&xp));
711 PetscCall(VecDestroy(&x));
712 PetscCall(MatDestroy(&J));
713 PetscCall(SNESDestroy(&snes));
714 PetscCall(VecDestroy(&user->Rhs));
715 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
716 PetscFunctionReturn(PETSC_SUCCESS);
717}
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 726 of file test_momentum_newton_krylov.c.

728{
729 PetscFunctionBeginUser;
730 /* Request the production-width (3) DMDA stencil so the complete RHS is safe
731 across MPI partitions; boundary metadata below still selects physical walls. */
733 simCtx, user, 6, 6, 6, PETSC_TRUE, PETSC_TRUE, PETSC_TRUE));
734 (*simCtx)->i_periodic = x_periodic ? 1 : 0;
735 (*simCtx)->j_periodic = (*simCtx)->k_periodic = 0;
736 (*simCtx)->mom_solver_type = MOMENTUM_SOLVER_NEWTON_KRYLOV;
737 (*simCtx)->invicid = 1;
738 (*simCtx)->dt = 0.1;
739 (*simCtx)->step = 1;
740 (*simCtx)->StartStep = 0;
741 PetscCall(VecSet((*user)->Ucont, 0.0));
742 PetscCall(VecSet((*user)->Ucont_o, 0.0));
743 PetscCall(VecSet((*user)->Ucont_rm1, 0.0));
744 for (PetscInt face = 0; face < 6; ++face) {
745 PetscBool periodic_face = (PetscBool)(x_periodic &&
746 (face == BC_FACE_NEG_X || face == BC_FACE_POS_X));
747 (*user)->boundary_faces[face].face_id = (BCFace)face;
748 (*user)->boundary_faces[face].mathematical_type = periodic_face ? PERIODIC : WALL;
749 (*user)->boundary_faces[face].handler_type = periodic_face ?
751 PetscCall(BoundaryCondition_Create((*user)->boundary_faces[face].handler_type,
752 &(*user)->boundary_faces[face].handler));
753 }
754 PetscFunctionReturn(PETSC_SUCCESS);
755}
PetscErrorCode BoundaryCondition_Create(BCHandlerType handler_type, BoundaryCondition **new_bc_ptr)
(Private) Creates and configures a specific BoundaryCondition handler object.
Definition Boundaries.c:729
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:323
@ WALL
Definition variables.h:317
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:347
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:336
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:292
@ BC_FACE_NEG_X
Definition variables.h:293
@ BC_FACE_POS_X
Definition variables.h:293
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 760 of file test_momentum_newton_krylov.c.

761{
762 SimCtx *simCtx = NULL;
763 UserCtx *user = NULL;
764 SNES snes = NULL;
765 Mat J = NULL;
766 Vec x = NULL, xp = NULL, f0 = NULL, fp = NULL, column = NULL, square = NULL;
767 Vec row_norm_sq = NULL, v[2] = {NULL, NULL}, dense_v[2] = {NULL, NULL};
768 Vec mffd_v = NULL, error_vec = NULL;
769 PetscInt n_global, lo, hi;
770 PetscReal min_row_sq = 0.0, error = 0.0, reference = 0.0;
771 const PetscReal epsilon = 1.0e-7;
773
774 PetscFunctionBeginUser;
775 PetscCall(BuildMinimalWallOperatorFixture(&simCtx, &user, PETSC_FALSE));
776 PetscCall(VecDuplicate(user->Ucont, &x)); PetscCall(VecSet(x, 0.0));
777 PetscCall(VecDuplicate(x, &xp)); PetscCall(VecDuplicate(x, &f0));
778 PetscCall(VecDuplicate(x, &fp)); PetscCall(VecDuplicate(x, &column));
779 PetscCall(VecDuplicate(x, &square)); PetscCall(VecDuplicate(x, &row_norm_sq));
780 PetscCall(VecDuplicate(x, &v[0])); PetscCall(VecDuplicate(x, &v[1]));
781 PetscCall(VecDuplicate(x, &dense_v[0])); PetscCall(VecDuplicate(x, &dense_v[1]));
782 PetscCall(VecDuplicate(x, &mffd_v)); PetscCall(VecDuplicate(x, &error_vec));
783 PetscCall(VecZeroEntries(row_norm_sq)); PetscCall(VecZeroEntries(dense_v[0]));
784 PetscCall(VecZeroEntries(dense_v[1]));
785 PetscCall(VecGetSize(x, &n_global));
786 PetscCall(VecGetOwnershipRange(x, &lo, &hi));
787 for (PetscInt which = 0; which < 2; ++which) {
788 PetscScalar *a = NULL;
789 PetscCall(VecGetArray(v[which], &a));
790 for (PetscInt local = 0; local < hi - lo; ++local) {
791 PetscInt global = lo + local;
792 a[local] = which == 0 ? (PetscScalar)(1.0 + 0.05 * (global % 9))
793 : (PetscScalar)(((global % 2) ? -1.0 : 1.0) * (0.5 + 0.03 * (global % 7)));
794 }
795 PetscCall(VecRestoreArray(v[which], &a));
796 }
797
798 ctx.user = user;
799 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
800 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
801 for (PetscInt col = 0; col < n_global; ++col) {
802 PetscScalar coeff[2] = {
803 (PetscScalar)(1.0 + 0.05 * (col % 9)),
804 (PetscScalar)(((col % 2) ? -1.0 : 1.0) * (0.5 + 0.03 * (col % 7)))
805 };
806 PetscCall(VecCopy(x, xp));
807 if (col >= lo && col < hi) PetscCall(VecSetValue(xp, col, epsilon, ADD_VALUES));
808 PetscCall(VecAssemblyBegin(xp)); PetscCall(VecAssemblyEnd(xp));
809 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, xp, fp, &ctx));
810 PetscCall(VecWAXPY(column, -1.0, f0, fp));
811 PetscCall(VecScale(column, 1.0 / epsilon));
812 PetscCall(VecPointwiseMult(square, column, column));
813 PetscCall(VecAXPY(row_norm_sq, 1.0, square));
814 PetscCall(VecAXPY(dense_v[0], coeff[0], column));
815 PetscCall(VecAXPY(dense_v[1], coeff[1], column));
816 }
817 PetscCall(VecMin(row_norm_sq, NULL, &min_row_sq));
818 PetscCheck(min_row_sq > 0.5, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
819 "Complete Newton Jacobian contains an unexplained zero/weak row (min squared norm=%g).",
820 (double)min_row_sq);
821 PetscCall(CheckStoredDerivative(user, x, 2, 2, 2, 0, 2, 2, 2, 0,
822 10.0, 1.0e-5, "interior BDF1 temporal diagonal"));
823
824 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
825 PetscCall(SNESSetDM(snes, user->fda));
826 PetscCall(SNESSetFunction(snes, f0, MomentumNewtonKrylov_FormResidual, &ctx));
827 PetscCall(MatCreateSNESMF(snes, &J));
828 PetscCall(MatMFFDSetBase(J, x, f0));
829 for (PetscInt which = 0; which < 2; ++which) {
830 PetscCall(MatMult(J, v[which], mffd_v));
831 PetscCall(VecWAXPY(error_vec, -1.0, dense_v[which], mffd_v));
832 PetscCall(VecNorm(error_vec, NORM_2, &error));
833 PetscCall(VecNorm(dense_v[which], NORM_2, &reference));
834 PetscCheck(error <= 2.0e-5 * PetscMax(1.0, reference), PETSC_COMM_WORLD, PETSC_ERR_PLIB,
835 "Independent dense FD Jv differs from PETSc MFFD action %d: error=%g reference=%g.",
836 which, (double)error, (double)reference);
837 }
838
839 for (PetscInt which = 0; which < 2; ++which) {
840 const PetscReal steps[3] = {1.0e-4, 1.0e-6, 1.0e-8};
841 PetscReal best = PETSC_MAX_REAL;
842 for (PetscInt s = 0; s < 3; ++s) {
843 PetscCall(VecWAXPY(xp, steps[s], v[which], x));
844 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, xp, fp, &ctx));
845 PetscCall(VecWAXPY(column, -1.0, f0, fp));
846 PetscCall(VecScale(column, 1.0 / steps[s]));
847 PetscCall(VecAXPY(column, -1.0, dense_v[which]));
848 PetscCall(VecNorm(column, NORM_2, &error));
849 best = PetscMin(best, error);
850 }
851 PetscCall(VecNorm(dense_v[which], NORM_2, &reference));
852 PetscCheck(best <= 2.0e-5 * PetscMax(1.0, reference), PETSC_COMM_WORLD, PETSC_ERR_PLIB,
853 "Direct directional differences show no accuracy plateau for vector %d (best=%g).",
854 which, (double)best);
855 }
856
857 PetscCall(MatDestroy(&J)); PetscCall(SNESDestroy(&snes));
858 PetscCall(VecDestroy(&error_vec)); PetscCall(VecDestroy(&mffd_v));
859 PetscCall(VecDestroy(&dense_v[1])); PetscCall(VecDestroy(&dense_v[0]));
860 PetscCall(VecDestroy(&v[1])); PetscCall(VecDestroy(&v[0]));
861 PetscCall(VecDestroy(&row_norm_sq)); PetscCall(VecDestroy(&square));
862 PetscCall(VecDestroy(&column)); PetscCall(VecDestroy(&fp)); PetscCall(VecDestroy(&f0));
863 PetscCall(VecDestroy(&xp)); PetscCall(VecDestroy(&x));
864 PetscCall(BoundarySystem_Destroy(user));
865 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
866 PetscFunctionReturn(PETSC_SUCCESS);
867}
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 870 of file test_momentum_newton_krylov.c.

871{
872 SimCtx *simCtx = NULL;
873 UserCtx *user = NULL;
874 Vec x = NULL, xp = NULL, f0 = NULL, fp = NULL, column = NULL;
875 Vec square = NULL, row_norm_sq = NULL;
876 PetscInt n_global, lo, hi;
877 PetscReal min_row_sq = 0.0;
878 const PetscReal epsilon = 1.0e-7;
880
881 PetscFunctionBeginUser;
882 PetscCall(BuildMinimalWallOperatorFixture(&simCtx, &user, PETSC_TRUE));
883 PetscCall(VecDuplicate(user->Ucont, &x)); PetscCall(VecSet(x, 0.0));
884 PetscCall(VecDuplicate(x, &xp)); PetscCall(VecDuplicate(x, &f0));
885 PetscCall(VecDuplicate(x, &fp)); PetscCall(VecDuplicate(x, &column));
886 PetscCall(VecDuplicate(x, &square)); PetscCall(VecDuplicate(x, &row_norm_sq));
887 PetscCall(VecZeroEntries(row_norm_sq));
888 PetscCall(VecGetSize(x, &n_global)); PetscCall(VecGetOwnershipRange(x, &lo, &hi));
889 ctx.user = user;
890 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
891 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f0, &ctx));
892 for (PetscInt col = 0; col < n_global; ++col) {
893 PetscCall(VecCopy(x, xp));
894 if (col >= lo && col < hi) PetscCall(VecSetValue(xp, col, epsilon, ADD_VALUES));
895 PetscCall(VecAssemblyBegin(xp)); PetscCall(VecAssemblyEnd(xp));
896 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, xp, fp, &ctx));
897 PetscCall(VecWAXPY(column, -1.0, f0, fp)); PetscCall(VecScale(column, 1.0 / epsilon));
898 PetscCall(VecPointwiseMult(square, column, column));
899 PetscCall(VecAXPY(row_norm_sq, 1.0, square));
900 }
901 PetscCall(VecMin(row_norm_sq, NULL, &min_row_sq));
902 PetscCheck(min_row_sq > 0.5, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
903 "Periodic Newton Jacobian contains a zero/weak row (min squared norm=%g).",
904 (double)min_row_sq);
905 PetscCall(VecDestroy(&row_norm_sq)); PetscCall(VecDestroy(&square));
906 PetscCall(VecDestroy(&column)); PetscCall(VecDestroy(&fp)); PetscCall(VecDestroy(&f0));
907 PetscCall(VecDestroy(&xp)); PetscCall(VecDestroy(&x));
908 PetscCall(BoundarySystem_Destroy(user));
909 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
910 PetscFunctionReturn(PETSC_SUCCESS);
911}
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, forced rollback, and per-call cleanup.

Definition at line 914 of file test_momentum_newton_krylov.c.

915{
916 SimCtx *simCtx = NULL;
917 UserCtx *user = NULL;
918 char tmpdir[PETSC_MAX_PATH_LEN] = "";
919 Vec entry = NULL, delta = NULL;
920 PetscErrorCode solve_ierr;
921 PetscReal norm = 0.0;
922 const FieldId fields[] = {FIELD_ID_UCONT};
923 char summary_path[PETSC_MAX_PATH_LEN];
924 char history_path[PETSC_MAX_PATH_LEN];
925 char linear_history_path[PETSC_MAX_PATH_LEN];
926
927 PetscFunctionBeginUser;
928 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
929 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_rtol", "1e-4"));
930 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
931 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_ksp_rtol", "1e-6"));
932 simCtx->mom_nk_monitor_history = PETSC_TRUE;
933 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
934 PetscCall(PicurvAssertBool(simCtx->mom_last_converged, "small Newton solve must converge"));
935 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL), "successful solve must release Rhs"));
936 PetscCall(PetscSNPrintf(summary_path, sizeof(summary_path),
937 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.log", simCtx->log_dir));
938 PetscCall(PetscSNPrintf(history_path, sizeof(history_path),
939 "%s/Momentum_Solver_Newton_Krylov_History_Block_0.log", simCtx->log_dir));
940 PetscCall(PetscSNPrintf(linear_history_path, sizeof(linear_history_path),
941 "%s/Momentum_Solver_Newton_Krylov_Linear_History_Block_0.log",
942 simCtx->log_dir));
943 PetscCall(AssertNewtonLog(summary_path, 1, "solver: Newton Krylov", "state: committed"));
944 PetscCall(AssertNewtonLog(history_path, -2, "newton: 0", "nonlinear_norm:"));
945 PetscCall(AssertNewtonLog(linear_history_path, -2, "krylov: 0", "requested_rtol:"));
946
947 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
948 tmpdir[0] = '\0';
949 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
950 PetscCall(VecSet(user->Ucont, 0.2));
951 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
952 PetscCall(ApplyBoundaryConditions(user));
953 PetscCall(VecDuplicate(user->Ucont, &entry));
954 PetscCall(VecDuplicate(user->Ucont, &delta));
955 PetscCall(VecCopy(user->Ucont, entry));
956 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "0"));
957 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
958 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
959 PetscCall(PetscPopErrorHandler());
960 PetscCall(PicurvAssertIntEqual(PETSC_ERR_CONV_FAILED, solve_ierr,
961 "forced nonconvergence must report PETSC_ERR_CONV_FAILED"));
962 PetscCall(VecWAXPY(delta, -1.0, entry, user->Ucont));
963 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
964 PetscCall(PicurvAssertRealNear(0.0, norm, 1.0e-12,
965 "failed Newton solve must restore the canonical entry state"));
966 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL), "failed solve must release Rhs"));
967 PetscCall(PetscSNPrintf(summary_path, sizeof(summary_path),
968 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.log", simCtx->log_dir));
969 PetscCall(AssertNewtonLog(summary_path, 1, "reason_code: -", "state: rolled_back"));
970
971 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_rtol"));
972 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_max_it"));
973 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_ksp_rtol"));
974 PetscCall(VecDestroy(&delta));
975 PetscCall(VecDestroy(&entry));
976 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
977 PetscFunctionReturn(PETSC_SUCCESS);
978}
PetscErrorCode ApplyBoundaryConditions(UserCtx *user)
Main boundary-condition orchestrator executed during solver timestepping.
#define MomentumSolver_NewtonKrylov
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:919
PetscBool mom_last_converged
Definition variables.h:917
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:887
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 981 of file test_momentum_newton_krylov.c.

982{
983 SimCtx *simCtx = NULL;
984 UserCtx *user = NULL;
985 char tmpdir[PETSC_MAX_PATH_LEN] = "";
986 char summary_path[PETSC_MAX_PATH_LEN];
987 const FieldId fields[] = {FIELD_ID_UCONT};
988
989 PetscFunctionBeginUser;
990 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
991 PetscCall(VecZeroEntries(user->Ucont));
992 PetscCall(VecZeroEntries(user->Ucont_o));
993 PetscCall(VecZeroEntries(user->Ucont_rm1));
994 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
995 PetscCall(ApplyBoundaryConditions(user));
996 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
997 PetscCall(PetscSNPrintf(summary_path, sizeof(summary_path),
998 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.log", simCtx->log_dir));
999 PetscCall(AssertNewtonLog(summary_path, 1, "newton: 0 | evals: 1 | krylov: 0",
1000 "final: 0.0000000000000000e+00 | state: committed"));
1001 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1002 PetscFunctionReturn(PETSC_SUCCESS);
1003}
Vec Ucont_o
Definition variables.h:1118
Vec Ucont_rm1
Definition variables.h:1119
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 1014 of file test_momentum_newton_krylov.c.

1015{
1016 SimCtx *simCtx = NULL;
1017 UserCtx *user = NULL;
1018 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1019 Vec x = NULL, f = NULL;
1020 PetscReal initial_norm = 0.0;
1022
1023 PetscFunctionBeginUser;
1024 PetscCall(BuildNewtonFixture(NULL, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1025 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1026 PetscCall(VecDuplicate(user->Ucont, &x));
1027 PetscCall(VecDuplicate(user->Ucont, &f));
1028 PetscCall(VecCopy(user->Ucont, x));
1029 ctx.user = user;
1030 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
1031 PetscCall(VecNorm(f, NORM_2, &initial_norm));
1032 PetscCheck(!PetscIsInfOrNanReal(initial_norm), PETSC_COMM_WORLD, PETSC_ERR_FP,
1033 "flat-channel BDF1 initial residual is non-finite (%g) with %s.",
1034 (double)initial_norm,
1035 use_point_block ? "frozen-momentum point-block" : "PCNONE");
1036 PetscCall(VecDestroy(&f));
1037 PetscCall(VecDestroy(&x));
1038 PetscCall(VecDestroy(&user->Rhs));
1039
1040 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_rtol", "1e-4"));
1041 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
1042 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_ksp_rtol", "1e-6"));
1043 if (use_point_block) {
1044 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1045 "frozen_momentum_jacobian"));
1046 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure",
1047 "point_block"));
1048 }
1049 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
1050 PetscCall(PicurvAssertBool(simCtx->mom_last_converged,
1051 "flat-channel BDF1 Newton solve must converge"));
1052 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_model"));
1053 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_structure"));
1054 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_rtol"));
1055 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_max_it"));
1056 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_ksp_rtol"));
1057 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1058 PetscFunctionReturn(PETSC_SUCCESS);
1059}
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 1062 of file test_momentum_newton_krylov.c.

1063{
1064 PetscFunctionBeginUser;
1065 PetscCall(CheckFlatChannelStartup(PETSC_FALSE));
1066 PetscCall(CheckFlatChannelStartup(PETSC_TRUE));
1067 PetscFunctionReturn(PETSC_SUCCESS);
1068}
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 1071 of file test_momentum_newton_krylov.c.

1072{
1073 SimCtx *simCtx = NULL;
1074 UserCtx *user = NULL;
1075 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1076 const FieldId fields[] = {FIELD_ID_UCONT};
1077
1078 PetscFunctionBeginUser;
1079 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1080 PetscCall(VecSet(user->Ucont, 0.2));
1081 PetscCall(VecZeroEntries(user->Ucont_o));
1082 PetscCall(VecZeroEntries(user->Ucont_rm1));
1083 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
1084 PetscCall(ApplyBoundaryConditions(user));
1085 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_rtol", "1e-4"));
1086 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
1087 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_ksp_rtol", "1e-6"));
1088
1089 /* AdvanceSimulation() solves StartStep+1 first. Use a nonzero checkpoint
1090 * state so that SNES takes a Newton/Krylov path rather than accepting a
1091 * trivial residual. */
1092 simCtx->StartStep = 7;
1093 simCtx->step = simCtx->StartStep + 1;
1094 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
1095 PetscCall(PicurvAssertBool(simCtx->mom_last_converged,
1096 "PCNONE Newton Krylov checkpoint restart must converge"));
1097
1098 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1099 tmpdir[0] = '\0';
1100 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1101 PetscCall(VecSet(user->Ucont, 0.2));
1102 PetscCall(VecZeroEntries(user->Ucont_o));
1103 PetscCall(VecZeroEntries(user->Ucont_rm1));
1104 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
1105 PetscCall(ApplyBoundaryConditions(user));
1106 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1107 "frozen_momentum_jacobian"));
1108 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "point_block"));
1109
1110 simCtx->continueMode = PETSC_TRUE;
1111 simCtx->StartStep = 8;
1112 simCtx->step = simCtx->StartStep + 1;
1113 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
1114 PetscCall(PicurvAssertBool(simCtx->mom_last_converged,
1115 "frozen-momentum Newton Krylov --continue solve must converge"));
1116 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_model"));
1117 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_preconditioner_structure"));
1118 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_rtol"));
1119 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_max_it"));
1120 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_ksp_rtol"));
1121 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1122 PetscFunctionReturn(PETSC_SUCCESS);
1123}
PetscBool continueMode
Definition variables.h:881
PetscInt StartStep
Definition variables.h:874
PetscInt step
Definition variables.h:872
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 1126 of file test_momentum_newton_krylov.c.

1127{
1128 SimCtx *simCtx = NULL;
1129 UserCtx *user = NULL;
1130 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1131 PetscErrorCode solve_ierr;
1132
1133 PetscFunctionBeginUser;
1134 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1135 {
1136 /* The gradient (Clark) model and wall functions are no longer rejected: both
1137 reach the solver through the shared residual, which this solver already
1138 evaluates. Neither is verified under Newton-Krylov yet, so neither is
1139 claimed as supported; they are simply no longer refused. */
1140 PetscInt *unsupported_flags[] = {
1141 &simCtx->immersed, &simCtx->movefsi, &simCtx->rotatefsi,
1142 &simCtx->moveframe, &simCtx->rotateframe,
1143 &simCtx->TwoD
1144 };
1145 for (size_t flag = 0; flag < sizeof(unsupported_flags) / sizeof(unsupported_flags[0]); ++flag) {
1146 *unsupported_flags[flag] = 1;
1147 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1148 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1149 PetscCall(PetscPopErrorHandler());
1150 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS),
1151 "unsupported Newton feature flag must fail"));
1152 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL),
1153 "feature validation must precede workspace allocation"));
1154 *unsupported_flags[flag] = 0;
1155 }
1156 }
1157 simCtx->block_number = 2;
1158 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1159 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1160 PetscCall(PetscPopErrorHandler());
1161 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS), "multiblock must fail"));
1162 simCtx->block_number = 1;
1163 /* Solid cells are no longer a rejection. The residual already zeroes their rows, so
1164 they carry no unknown; the solver now gives them the same identity treatment as
1165 any other row without one, instead of refusing the whole solve. A fully masked
1166 domain is the degenerate case of that: every row is an identity row and the
1167 system is trivially consistent. This checks the structural contract only; the
1168 masked path has no physics verification because nothing in the tree currently
1169 populates Nvert with a nonzero value. */
1170 PetscCall(VecSet(user->Nvert, 1.0));
1171 PetscCall(VecSet(user->lNvert, 1.0));
1172 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1173 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr == PETSC_SUCCESS),
1174 "a fully masked domain must resolve to identity rows, not a rejection"));
1175 PetscCall(VecSet(user->Nvert, 0.0));
1176 PetscCall(VecSet(user->lNvert, 0.0));
1177 PetscCall(VecSet(user->Nvert, 0.0));
1180 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1181 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1182 PetscCall(PetscPopErrorHandler());
1183 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS),
1184 "driven constant-flux controller must fail"));
1185 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL),
1186 "all validation failures must precede workspace allocation"));
1187 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1188 PetscFunctionReturn(PETSC_SUCCESS);
1189}
PetscInt movefsi
Definition variables.h:896
PetscInt moveframe
Definition variables.h:897
PetscInt TwoD
Definition variables.h:897
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:1096
PetscInt block_number
Definition variables.h:958
Vec lNvert
Definition variables.h:1111
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:349
BCHandlerType handler_type
Definition variables.h:400
PetscInt rotatefsi
Refused at setup: immersed boundaries and moving bodies are not implemented.
Definition variables.h:896
Vec Nvert
Definition variables.h:1111
BCType mathematical_type
Definition variables.h:399
PetscInt rotateframe
moveframe/rotateframe are refused at setup.
Definition variables.h:897
PetscInt immersed
Definition variables.h:896
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 1192 of file test_momentum_newton_krylov.c.

1193{
1194 SimCtx *simCtx = NULL;
1195 UserCtx *user = NULL;
1196 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1197 Vec entry = NULL, delta = NULL;
1198 PetscErrorCode solve_ierr;
1199 PetscReal norm = 0.0;
1200 const FieldId fields[] = {FIELD_ID_UCONT};
1201
1202 PetscFunctionBeginUser;
1203 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1204 PetscCall(VecSet(user->Ucont, 0.2));
1205 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
1206 PetscCall(ApplyBoundaryConditions(user));
1207 PetscCall(VecDuplicate(user->Ucont, &entry)); PetscCall(VecCopy(user->Ucont, entry));
1208 PetscCall(VecDuplicate(user->Ucont, &delta));
1209 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_pc_type", "jacobi"));
1210 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1211 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1212 PetscCall(PetscPopErrorHandler());
1213 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_pc_type"));
1214 PetscCall(PicurvAssertIntEqual(PETSC_ERR_SUP, solve_ierr,
1215 "non-PCNONE option must fail after setup"));
1216 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL),
1217 "post-allocation failure must destroy Rhs"));
1218 PetscCall(VecWAXPY(delta, -1.0, entry, user->Ucont));
1219 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
1220 PetscCall(PicurvAssertRealNear(0.0, norm, 1.0e-12,
1221 "post-allocation failure must restore canonical entry"));
1222 PetscCall(VecDestroy(&delta)); PetscCall(VecDestroy(&entry));
1223 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1224 PetscFunctionReturn(PETSC_SUCCESS);
1225}
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 1228 of file test_momentum_newton_krylov.c.

1229{
1230 MomentumNewtonJacobian jacobian = {0};
1231 MomentumPreconditionerDescription description = {0};
1232 PetscErrorCode config_ierr;
1233 const char *option_names[] = {
1234 "-mom_nk_jacobian_type",
1235 "-mom_nk_jacobian_fd_mode",
1236 "-mom_nk_preconditioner_model",
1237 "-mom_nk_preconditioner_structure"
1238 };
1239
1240 PetscFunctionBeginUser;
1241 for (size_t n = 0; n < sizeof(option_names) / sizeof(option_names[0]); ++n)
1242 PetscCall(PetscOptionsClearValue(NULL, option_names[n]));
1243
1244 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description));
1246 "default Jacobian type must be finite difference"));
1248 jacobian.finite_difference_mode,
1249 "default finite-difference mode must be matrix free"));
1250 PetscCall(PicurvAssertIntEqual(MOM_NK_PC_MODEL_NONE, description.model,
1251 "default preconditioner model must be none"));
1253 "default preconditioner structure must be none"));
1254
1255 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_type", "finite_difference"));
1256 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_fd_mode", "matrix_free"));
1257 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", "none"));
1258 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "none"));
1259 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description));
1261 "explicit baseline Jacobian type must parse"));
1263 jacobian.finite_difference_mode,
1264 "explicit baseline finite-difference mode must parse"));
1265 PetscCall(PicurvAssertIntEqual(MOM_NK_PC_MODEL_NONE, description.model,
1266 "explicit baseline preconditioner model must parse"));
1268 "explicit baseline preconditioner structure must parse"));
1269
1270 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1271 "frozen_momentum_jacobian"));
1272 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "point_block"));
1273 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description));
1275 "explicit Jacobian type must parse"));
1277 jacobian.finite_difference_mode,
1278 "explicit finite-difference mode must parse"));
1280 description.model, "frozen model must parse"));
1282 description.structure, "point-block structure must parse"));
1283
1284 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_type", "analytic"));
1285 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1286 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1287 PetscCall(PetscPopErrorHandler());
1288 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1289 "unsupported Jacobian type must fail"));
1290 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_type", "finite_difference"));
1291
1292 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_fd_mode", "colored_sparse"));
1293 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1294 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1295 PetscCall(PetscPopErrorHandler());
1296 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1297 "unsupported finite-difference mode must fail"));
1298 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_fd_mode", "matrix_free"));
1299
1300 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "none"));
1301 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1302 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1303 PetscCall(PetscPopErrorHandler());
1304 PetscCall(PicurvAssertIntEqual(PETSC_ERR_SUP, config_ierr,
1305 "frozen model without point block must fail"));
1306 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", "none"));
1307 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "point_block"));
1308 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1309 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1310 PetscCall(PetscPopErrorHandler());
1311 PetscCall(PicurvAssertIntEqual(PETSC_ERR_SUP, config_ierr,
1312 "none model with point block must fail"));
1313
1314 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", "diagonal"));
1315 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "none"));
1316 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1317 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1318 PetscCall(PetscPopErrorHandler());
1319 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1320 "unsupported preconditioner model must fail"));
1321 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1322 "frozen_momentum_jacobian"));
1323 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "line"));
1324 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1325 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1326 PetscCall(PetscPopErrorHandler());
1327 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1328 "unsupported preconditioner structure must fail"));
1329
1330 for (size_t n = 0; n < sizeof(option_names) / sizeof(option_names[0]); ++n)
1331 PetscCall(PetscOptionsClearValue(NULL, option_names[n]));
1332 PetscFunctionReturn(PETSC_SUCCESS);
1333}
@ 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 1336 of file test_momentum_newton_krylov.c.

1338{
1339 ISLocalToGlobalMapping local_to_global = NULL;
1340 PetscInt ghost_starts[3], ghost_sizes[3], local_index;
1341
1342 PetscFunctionBeginUser;
1343 PetscCall(DMDAGetGhostCorners(user->fda,
1344 &ghost_starts[0], &ghost_starts[1], &ghost_starts[2],
1345 &ghost_sizes[0], &ghost_sizes[1], &ghost_sizes[2]));
1346 PetscCall(PicurvAssertBool((PetscBool)(
1347 stencil.i >= ghost_starts[0] && stencil.i < ghost_starts[0] + ghost_sizes[0] &&
1348 stencil.j >= ghost_starts[1] && stencil.j < ghost_starts[1] + ghost_sizes[1] &&
1349 stencil.k >= ghost_starts[2] && stencil.k < ghost_starts[2] + ghost_sizes[2]),
1350 "test stencil must lie in the local DMDA ghost region"));
1351 local_index = stencil.c + 3 * (
1352 (stencil.i - ghost_starts[0]) + ghost_sizes[0] * (
1353 (stencil.j - ghost_starts[1]) + ghost_sizes[1] *
1354 (stencil.k - ghost_starts[2])));
1355 PetscCall(DMGetLocalToGlobalMapping(user->fda, &local_to_global));
1356 PetscCall(ISLocalToGlobalMappingApply(local_to_global, 1, &local_index,
1357 global_index));
1358 PetscFunctionReturn(PETSC_SUCCESS);
1359}
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 1362 of file test_momentum_newton_krylov.c.

1365{
1366 Vec column_basis = NULL, row_basis = NULL, product = NULL;
1367 AO ao = NULL;
1368 PetscInt mx, my, row_index, col_index;
1369 PetscFunctionBeginUser;
1370 PetscCall(DMDAGetInfo(user->fda, NULL, &mx, &my, NULL, NULL, NULL, NULL,
1371 NULL, NULL, NULL, NULL, NULL, NULL));
1372 row_index = row.c + 3 * (row.i + mx * (row.j + my * row.k));
1373 col_index = col.c + 3 * (col.i + mx * (col.j + my * col.k));
1374 PetscCall(DMDAGetAO(user->fda, &ao));
1375 PetscCall(AOApplicationToPetsc(ao, 1, &row_index));
1376 PetscCall(AOApplicationToPetsc(ao, 1, &col_index));
1377 PetscCall(DMCreateGlobalVector(user->fda, &column_basis));
1378 PetscCall(DMCreateGlobalVector(user->fda, &row_basis));
1379 PetscCall(DMCreateGlobalVector(user->fda, &product));
1380 PetscCall(VecSet(column_basis, 0.0)); PetscCall(VecSet(row_basis, 0.0));
1381 PetscCall(VecSetValue(column_basis, col_index, 1.0, INSERT_VALUES));
1382 PetscCall(VecSetValue(row_basis, row_index, 1.0, INSERT_VALUES));
1383 PetscCall(VecAssemblyBegin(column_basis)); PetscCall(VecAssemblyEnd(column_basis));
1384 PetscCall(VecAssemblyBegin(row_basis)); PetscCall(VecAssemblyEnd(row_basis));
1385 PetscCall(MatMult(preconditioning_matrix, column_basis, product));
1386 PetscCall(VecDot(row_basis, product, value));
1387 PetscCall(VecDestroy(&product)); PetscCall(VecDestroy(&row_basis));
1388 PetscCall(VecDestroy(&column_basis));
1389 PetscFunctionReturn(PETSC_SUCCESS);
1390}
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 1393 of file test_momentum_newton_krylov.c.

1395{
1396 DMDALocalInfo info;
1397 MatInfo matrix_info;
1398 PetscMPIInt comm_size = 1;
1399 PetscInt matrix_rows, matrix_cols, local_rows, local_cols;
1400 PetscInt vector_size, vector_local_size, block_size;
1401 PetscInt ownership_start, ownership_end;
1402 PetscInt expected_local = 0, expected_global = 0;
1403 PetscInt offrank_periodic_local = 0, offrank_periodic_global = 0;
1404 PetscBool is_seq_aij = PETSC_FALSE, is_mpi_aij = PETSC_FALSE;
1405
1406 PetscFunctionBeginUser;
1407 PetscCall(DMDAGetLocalInfo(user->fda, &info));
1408 PetscCall(MatGetSize(matrix, &matrix_rows, &matrix_cols));
1409 PetscCall(MatGetLocalSize(matrix, &local_rows, &local_cols));
1410 PetscCall(VecGetSize(user->Ucont, &vector_size));
1411 PetscCall(VecGetLocalSize(user->Ucont, &vector_local_size));
1412 PetscCall(VecGetOwnershipRange(user->Ucont, &ownership_start, &ownership_end));
1413 PetscCall(MatGetBlockSize(matrix, &block_size));
1414 PetscCall(PicurvAssertIntEqual(vector_size, matrix_rows,
1415 "point-block global row dimension"));
1416 PetscCall(PicurvAssertIntEqual(vector_size, matrix_cols,
1417 "point-block global column dimension"));
1418 PetscCall(PicurvAssertIntEqual(vector_local_size, local_rows,
1419 "point-block local row dimension"));
1420 PetscCall(PicurvAssertIntEqual(vector_local_size, local_cols,
1421 "point-block local column dimension"));
1422 PetscCall(PicurvAssertIntEqual(3, block_size, "point-block logical block size"));
1423 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)matrix), &comm_size));
1424 PetscCall(PetscObjectTypeCompare((PetscObject)matrix, MATSEQAIJ, &is_seq_aij));
1425 PetscCall(PetscObjectTypeCompare((PetscObject)matrix, MATMPIAIJ, &is_mpi_aij));
1426 PetscCall(PicurvAssertBool(
1427 comm_size == 1 ? is_seq_aij : is_mpi_aij,
1428 "point-block matrix must use the expected AIJ implementation"));
1429
1430 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
1431 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
1432 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
1433 for (PetscInt component = 0; component < 3; ++component) {
1434 PetscInt ri, rj, rk;
1436 user, i, j, k, component, &ri, &rj, &rk);
1437 expected_local += type == MOM_ROW_PHYSICAL ? 3 :
1438 type == MOM_ROW_PERIODIC_DUPLICATE ? 2 : 1;
1439 if (type == MOM_ROW_PERIODIC_DUPLICATE) {
1440 PetscInt representative;
1441 PetscCall(TestStencilToGlobal(user,
1442 (MatStencil){.i = ri, .j = rj, .k = rk, .c = component},
1443 &representative));
1444 if (representative < ownership_start || representative >= ownership_end)
1445 ++offrank_periodic_local;
1446 }
1447 }
1448 }
1449 }
1450 }
1451 PetscCallMPI(MPI_Allreduce(&expected_local, &expected_global, 1, MPIU_INT,
1452 MPI_SUM, PetscObjectComm((PetscObject)matrix)));
1453 PetscCallMPI(MPI_Allreduce(&offrank_periodic_local, &offrank_periodic_global,
1454 1, MPIU_INT, MPI_SUM,
1455 PetscObjectComm((PetscObject)matrix)));
1456 PetscCall(MatGetInfo(matrix, MAT_GLOBAL_SUM, &matrix_info));
1457 PetscCall(PicurvAssertRealNear((PetscReal)expected_global,
1458 (PetscReal)matrix_info.nz_allocated, 0.0,
1459 "point-block matrix must allocate exactly the classified scalar pattern"));
1460 PetscCall(PicurvAssertRealNear((PetscReal)expected_global,
1461 (PetscReal)matrix_info.nz_used, 0.0,
1462 "point-block assembly must insert every classified structural entry"));
1463 PetscCall(PicurvAssertRealNear(0.0, (PetscReal)matrix_info.mallocs, 0.0,
1464 "point-block insertion must not reallocate matrix storage"));
1465 if (require_offrank_periodic && comm_size > 1)
1466 PetscCall(PicurvAssertBool((PetscBool)(offrank_periodic_global > 0),
1467 "MPI periodic fixture must exercise off-rank preallocation"));
1468 PetscFunctionReturn(PETSC_SUCCESS);
1469}
@ MOM_ROW_PERIODIC_DUPLICATE
Duplicate of a wrapped representative row (see ri, rj, rk).
Definition Boundaries.h:256
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 1475 of file test_momentum_newton_krylov.c.

1476{
1477 SimCtx *simCtx = NULL;
1478 UserCtx *user = NULL;
1479 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1481 MomentumPreconditionerDescription description = {
1484 0,
1485 0
1486 };
1487 MomentumPreconditionerEngine engine = {0};
1488 Mat preconditioning_matrix = NULL;
1489 Vec x = NULL, f = NULL;
1490 PetscInt block_size = 0, velocity_dof = 0;
1491 PetscReal matrix_norm = 0.0, reassembled_norm = 0.0, difference_norm = 0.0;
1492 MatStencil conditioned = {.i = 0, .j = 2, .k = 3, .c = 0};
1493 MatStencil homogeneous = {.i = 0, .j = 2, .k = 3, .c = 1};
1494 Mat saved_matrix = NULL, mffd = NULL;
1495 SNES snes = NULL;
1496 Vec direction = NULL, product = NULL, px = NULL;
1497 PetscReal px_norm = 0.0;
1498 KSP ksp = NULL;
1499 PC pc = NULL;
1500 Vec pc_rhs = NULL, pc_solution = NULL;
1501 MatInfo initial_allocation_info, repeated_allocation_info;
1502
1503 PetscFunctionBeginUser;
1504 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1505 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1506 PetscCall(VecDuplicate(user->Ucont, &x));
1507 PetscCall(VecDuplicate(user->Ucont, &f));
1508 PetscCall(VecCopy(user->Ucont, x));
1509 PetscCall(VecStrideSet(x, 0, 0.2));
1510 PetscCall(VecStrideSet(x, 1, 0.3));
1511 PetscCall(VecStrideSet(x, 2, 0.4));
1512 ctx.user = user;
1513 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
1514 PetscCall(MomentumPreconditionerEngine_Create(user, NULL, &description, &engine));
1515 preconditioning_matrix = engine.preconditioning_matrix;
1516 PetscCall(PicurvAssertBool((PetscBool)(engine.model_ops == &frozen_momentum_point_block_ops),
1517 "engine must select the frozen-momentum model callbacks"));
1519 "point-block engine must own its separate matrix"));
1520 PetscCall(PicurvAssertBool((PetscBool)!engine.aliases_jacobian_operator,
1521 "point-block matrix must not alias the Jacobian operator"));
1522 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1524 user, preconditioning_matrix, PETSC_FALSE));
1525 PetscCall(MatGetInfo(preconditioning_matrix, MAT_GLOBAL_SUM,
1526 &initial_allocation_info));
1527 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &matrix_norm));
1528 PetscCall(PicurvAssertBool((PetscBool)(matrix_norm > 0.0),
1529 "model callback and common rows must insert matrix entries"));
1530 PetscCall(MatGetBlockSize(preconditioning_matrix, &block_size));
1531 PetscCall(PicurvAssertIntEqual(3, block_size, "point-block matrix block size"));
1532 PetscCall(DMDAGetInfo(user->fda, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1533 &velocity_dof, NULL, NULL, NULL, NULL, NULL));
1534 PetscCall(PicurvAssertIntEqual(3, velocity_dof, "Newton velocity DMDA dof"));
1535
1536 /* The coefficient formulas define columns by the differentiated Ucont
1537 component. Check an asymmetric pair so a row/column transpose cannot pass. */
1538 {
1539 const MatStencil row_i = {.i = 2, .j = 2, .k = 2, .c = 0};
1540 const MatStencil row_j = {.i = 2, .j = 2, .k = 2, .c = 1};
1541 MatStencil col_i = row_i, col_j = row_j;
1542 PetscReal ***aj = NULL;
1543 PetscReal AJip, AJjp;
1544 PetscScalar dFi_dUj = 0.0, dFj_dUi = 0.0;
1545
1546 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, &aj));
1547 AJip = 0.5 * (aj[2][2][2] + aj[2][2][3]);
1548 AJjp = 0.5 * (aj[2][2][2] + aj[2][3][2]);
1549 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, &aj));
1550 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1551 row_i, col_j, &dFi_dUj));
1552 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1553 row_j, col_i, &dFj_dUi));
1554 PetscCall(PicurvAssertRealNear(0.5 * AJjp * 0.2,
1555 PetscRealPart(dFi_dUj), 1e-12,
1556 "row i, column j must differentiate with respect to U-j"));
1557 PetscCall(PicurvAssertRealNear(0.5 * AJip * 0.3,
1558 PetscRealPart(dFj_dUi), 1e-12,
1559 "row j, column i must differentiate with respect to U-i"));
1560 }
1561
1562 /* Both fixed categories are exact identity rows, with no same-cell coupling. */
1563 for (PetscInt cc = 0; cc < 3; ++cc) {
1564 MatStencil col = conditioned; col.c = cc;
1565 PetscScalar conditioned_value = 0.0, homogeneous_value = 0.0;
1566 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1567 conditioned, col, &conditioned_value));
1568 col = homogeneous; col.c = cc;
1569 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1570 homogeneous, col, &homogeneous_value));
1571 PetscCall(PicurvAssertRealNear(cc == conditioned.c ? 1.0 : 0.0,
1572 PetscRealPart(conditioned_value), 1e-14, "conditioned row must be exact identity"));
1573 PetscCall(PicurvAssertRealNear(cc == homogeneous.c ? 1.0 : 0.0,
1574 PetscRealPart(homogeneous_value), 1e-14, "homogeneous row must be exact identity"));
1575 }
1576
1577 /* A real residual and MFFD product cannot change subsequent assembly. */
1578 PetscCall(MatDuplicate(preconditioning_matrix, MAT_COPY_VALUES, &saved_matrix));
1579 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1580 PetscCall(SNESSetDM(snes, user->fda));
1581 PetscCall(SNESSetFunction(snes, f, MomentumNewtonKrylov_FormResidual, &ctx));
1582 PetscCall(MatCreateSNESMF(snes, &mffd));
1583 PetscCall(VecDuplicate(x, &direction)); PetscCall(VecDuplicate(x, &product));
1584 PetscCall(VecSet(direction, 0.375));
1585 PetscCall(MomentumNewtonKrylov_FormResidual(snes, x, f, &ctx));
1586 PetscCall(MatMFFDSetBase(mffd, x, f));
1587 PetscCall(MatMult(mffd, direction, product));
1588 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1589 PetscCall(MatAXPY(saved_matrix, -1.0, preconditioning_matrix, SAME_NONZERO_PATTERN));
1590 PetscCall(MatNorm(saved_matrix, NORM_FROBENIUS, &difference_norm));
1591 PetscCall(PicurvAssertRealNear(0.0, difference_norm, 1e-12,
1592 "assembly must be unchanged after residual and MFFD products"));
1593 PetscCall(VecDestroy(&product)); PetscCall(VecDestroy(&direction));
1594 PetscCall(MatDestroy(&mffd)); PetscCall(SNESDestroy(&snes)); PetscCall(MatDestroy(&saved_matrix));
1595
1596 PetscCall(VecDuplicate(x, &px));
1597 PetscCall(MatMult(preconditioning_matrix, x, px));
1598 PetscCall(VecNorm(px, NORM_2, &px_norm));
1599 PetscCall(PicurvAssertBool((PetscBool)(!PetscIsInfOrNanReal(px_norm) && px_norm > 0.0),
1600 "point-block product must be finite and nonzero"));
1601 PetscCall(VecDestroy(&px));
1602
1603 {
1604 PetscBool assembled = PETSC_FALSE;
1605 PetscCall(MatAssembled(preconditioning_matrix, &assembled));
1606 PetscCall(PicurvAssertBool(assembled, "engine must perform final matrix assembly"));
1607 }
1608 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &matrix_norm));
1609 PetscCall(MatShift(preconditioning_matrix, 7.0));
1610 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1611 PetscCall(MatGetInfo(preconditioning_matrix, MAT_GLOBAL_SUM,
1612 &repeated_allocation_info));
1613 PetscCall(PicurvAssertRealNear((PetscReal)initial_allocation_info.nz_allocated,
1614 (PetscReal)repeated_allocation_info.nz_allocated, 0.0,
1615 "repeated assembly must retain exact allocated storage"));
1616 PetscCall(PicurvAssertRealNear((PetscReal)initial_allocation_info.mallocs,
1617 (PetscReal)repeated_allocation_info.mallocs, 0.0,
1618 "repeated assembly must not add insertion reallocations"));
1619 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &reassembled_norm));
1620 PetscCall(PicurvAssertRealNear(matrix_norm, reassembled_norm, 1e-12,
1621 "repeated engine assembly must clear old entries"));
1622 {
1623 PetscScalar value = 0.0;
1624 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1625 conditioned, conditioned, &value));
1626 PetscCall(PicurvAssertRealNear(1.0, PetscRealPart(value), 1e-14,
1627 "repeated engine assembly must clear old entries"));
1628 }
1629 PetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
1630 PetscCall(KSPSetOperators(ksp, preconditioning_matrix, preconditioning_matrix));
1631 PetscCall(KSPGetPC(ksp, &pc));
1632 PetscCall(MomentumPreconditionerEngine_ConfigurePetscPC(&engine, pc));
1633 PetscCall(MomentumPreconditionerEngine_ValidatePetscPC(&engine, pc));
1634 PetscCall(KSPSetUp(ksp));
1635 PetscCall(DMCreateGlobalVector(user->fda, &pc_rhs));
1636 PetscCall(DMCreateGlobalVector(user->fda, &pc_solution));
1637 PetscCall(VecSet(pc_rhs, 1.0));
1638 PetscCall(PCApply(pc, pc_rhs, pc_solution));
1639 PetscCall(VecDestroy(&pc_solution)); PetscCall(VecDestroy(&pc_rhs));
1640 PetscCall(KSPDestroy(&ksp));
1641 PetscCall(MomentumPreconditionerEngine_Destroy(&engine));
1642 PetscCall(PicurvAssertBool((PetscBool)(engine.preconditioning_matrix == NULL),
1643 "engine destroy must clear its owned matrix"));
1644 PetscCall(VecDestroy(&f)); PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->Rhs));
1645 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1646 PetscFunctionReturn(PETSC_SUCCESS);
1647}
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 1650 of file test_momentum_newton_krylov.c.

1652{
1653 AO ao = NULL;
1654 PetscInt ri, rj, rk, global_row, global_rep, lo, hi, local_checked = 0, checked = 0;
1655 PetscInt ncols = 0;
1656 const PetscInt *cols = NULL;
1657 const PetscScalar *values = NULL;
1658 MomentumRowType type;
1659
1660 PetscFunctionBeginUser;
1661 type = ClassifyMomentumRow(user, row.i, row.j, row.k, row.c, &ri, &rj, &rk);
1662 PetscCall(PicurvAssertIntEqual(MOM_ROW_PERIODIC_DUPLICATE, type, message));
1663 global_row = row.c + 3 * (row.i + user->info.mx * (row.j + user->info.my * row.k));
1664 PetscCall(DMDAGetAO(user->fda, &ao));
1665 PetscCall(AOApplicationToPetsc(ao, 1, &global_row));
1666 PetscCall(MatGetOwnershipRange(matrix, &lo, &hi));
1667 if (global_row >= lo && global_row < hi) {
1668 PetscBool found_self = PETSC_FALSE, found_rep = PETSC_FALSE;
1669 PetscInt nonzero_entries = 0;
1670 PetscCall(TestStencilToGlobal(user,
1671 (MatStencil){.i = ri, .j = rj, .k = rk, .c = row.c}, &global_rep));
1672 PetscCall(MatGetRow(matrix, global_row, &ncols, &cols, &values));
1673 for (PetscInt n = 0; n < ncols; ++n) {
1674 if (PetscAbsScalar(values[n]) <= 1e-14) continue;
1675 nonzero_entries++;
1676 if (cols[n] == global_row) {
1677 found_self = PETSC_TRUE;
1678 PetscCall(PicurvAssertRealNear(1.0, PetscRealPart(values[n]), 1e-14,
1679 "periodic row self entry"));
1680 } else if (cols[n] == global_rep) {
1681 found_rep = PETSC_TRUE;
1682 PetscCall(PicurvAssertRealNear(-1.0, PetscRealPart(values[n]), 1e-14,
1683 "periodic row representative entry"));
1684 } else {
1685 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB,
1686 "Periodic row contains an unintended nonzero column.");
1687 }
1688 }
1689 PetscCall(PicurvAssertIntEqual(2, nonzero_entries,
1690 "periodic row must contain exactly two numerical entries"));
1691 PetscCall(PicurvAssertBool((PetscBool)(found_self && found_rep), message));
1692 PetscCall(MatRestoreRow(matrix, global_row, &ncols, &cols, &values));
1693 local_checked = 1;
1694 }
1695 PetscCallMPI(MPI_Allreduce(&local_checked, &checked, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
1696 PetscCall(PicurvAssertIntEqual(1, checked, "periodic row must have exactly one matrix owner"));
1697 PetscFunctionReturn(PETSC_SUCCESS);
1698}
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 1701 of file test_momentum_newton_krylov.c.

1702{
1703 SimCtx *simCtx = NULL;
1704 UserCtx *user = NULL;
1705 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1707 MomentumPreconditionerDescription description = {
1709 };
1710 SNES snes = NULL;
1711 Mat jacobian_operator = NULL, preconditioning_matrix = NULL;
1712 KSP ksp = NULL;
1713 PC pc = NULL;
1714 PetscInt rows = 0, cols = 0;
1715 const char *jacobian_prefix = NULL;
1716 const char *pc_type = NULL;
1717 PetscBool prefix_matches = PETSC_FALSE;
1718 PetscBool pc_is_none = PETSC_FALSE;
1719
1720 PetscFunctionBeginUser;
1721 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1722 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1723 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1724 PetscCall(SNESSetDM(snes, user->fda));
1725 ctx.user = user;
1728 PetscCall(SNESSetFunction(snes, NULL, MomentumNewtonKrylov_FormResidual, &ctx));
1729 PetscCall(MomentumNewtonJacobian_Create(snes, &ctx.jacobian));
1730 PetscCall(MatGetOptionsPrefix(ctx.jacobian.jacobian_operator, &jacobian_prefix));
1731 PetscCall(PetscStrcmp(jacobian_prefix, "mom_nk_", &prefix_matches));
1732 PetscCall(PicurvAssertBool(prefix_matches,
1733 "Jacobian interface must apply the application prefix"));
1735 user, ctx.jacobian.jacobian_operator, &description, &ctx.preconditioning_engine));
1737 snes, &ctx.jacobian, &ctx.preconditioning_engine, &ctx));
1738 PetscCall(SNESGetKSP(snes, &ksp));
1739 PetscCall(KSPGetPC(ksp, &pc));
1741 &ctx.preconditioning_engine, pc));
1743 &ctx.preconditioning_engine, pc));
1744 PetscCall(PCGetType(pc, &pc_type));
1745 PetscCall(PetscStrcmp(pc_type, PCNONE, &pc_is_none));
1746 PetscCall(PicurvAssertBool(pc_is_none,
1747 "baseline engine must derive PETSc PCNONE"));
1748 PetscCall(SNESGetJacobian(snes, &jacobian_operator, &preconditioning_matrix, NULL, NULL));
1749 PetscCall(PicurvAssertBool(
1750 (PetscBool)(jacobian_operator == ctx.jacobian.jacobian_operator),
1751 "SNES must receive the Jacobian-interface MFFD operator"));
1752 PetscCall(PicurvAssertBool((PetscBool)(preconditioning_matrix == jacobian_operator),
1753 "baseline preconditioning matrix must alias the Jacobian"));
1755 "baseline engine must record matrix aliasing"));
1757 "baseline engine must not own the Jacobian alias"));
1759 PetscCall(MatGetSize(ctx.jacobian.jacobian_operator, &rows, &cols));
1760 PetscCall(PicurvAssertBool((PetscBool)(rows > 0 && cols > 0),
1761 "destroying an alias engine must preserve the Jacobian"));
1763 PetscCall(SNESDestroy(&snes));
1764 PetscCall(VecDestroy(&user->Rhs));
1765 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1766 PetscFunctionReturn(PETSC_SUCCESS);
1767}
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 1770 of file test_momentum_newton_krylov.c.

1771{
1772 SimCtx *simCtx = NULL;
1773 UserCtx *user = NULL;
1774 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1776 MomentumPreconditionerDescription description = {
1779 0,
1780 0
1781 };
1782 Vec x = NULL, f = NULL;
1783 PetscReal matrix_norm = 0.0;
1784
1785 PetscFunctionBeginUser;
1786 PetscCall(BuildNewtonFixture(periodic_xyz_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1787 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1788 PetscCall(VecDuplicate(user->Ucont, &x));
1789 PetscCall(VecDuplicate(user->Ucont, &f));
1790 PetscCall(VecCopy(user->Ucont, x));
1791 ctx.user = user;
1792 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
1794 user, NULL, &description, &ctx.preconditioning_engine));
1797 user, ctx.preconditioning_engine.preconditioning_matrix, PETSC_TRUE));
1798 PetscCall(MatNorm(ctx.preconditioning_engine.preconditioning_matrix,
1799 NORM_FROBENIUS, &matrix_norm));
1800 PetscCall(PicurvAssertBool((PetscBool)(matrix_norm > 0.0),
1801 "periodic point-block assembly must produce a nonzero matrix"));
1802 PetscCall(AssertPeriodicPreconditionerRow(user,
1804 (MatStencil){.i = 0, .j = 2, .k = 3, .c = 0},
1805 "single-axis periodic row must contain exact +1/-1 entries"));
1806 PetscCall(AssertPeriodicPreconditionerRow(user,
1808 (MatStencil){.i = 0, .j = 0, .k = 3, .c = 1},
1809 "periodic intersection must contain exact +1/-1 entries"));
1810 PetscCall(AssertPeriodicPreconditionerRow(user,
1812 (MatStencil){.i = 0, .j = 0, .k = 0, .c = 2},
1813 "periodic origin intersection must contain exact +1/-1 entries"));
1815 PetscCall(VecDestroy(&f));
1816 PetscCall(VecDestroy(&x));
1817 PetscCall(VecDestroy(&user->Rhs));
1818 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1819 PetscFunctionReturn(PETSC_SUCCESS);
1820}
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 1828 of file test_momentum_newton_krylov.c.

1829{
1830 PetscErrorCode ierr;
1831 const PicurvTestCase cases[] = {
1832 {"residual-repeatability-and-input-integrity", TestResidualRepeatabilityAndInputIntegrity},
1833 {"constraint-rows", TestConstraintRows},
1834 {"fixed-constraint-derivatives-all-faces", TestFixedConstraintDerivativesAllFaces},
1835 {"inlet-outlet-constraint-derivatives", TestInletOutletConstraintDerivatives},
1836 {"periodic-constraint-derivatives-and-intersections", TestPeriodicConstraintDerivativesAndIntersections},
1837 {"matrix-free-derivative", TestMatrixFreeDerivative},
1838 {"whole-operator-direct-jacobian", TestWholeOperatorDirectJacobian},
1839 {"periodic-operator-has-no-zero-rows", TestPeriodicOperatorHasNoZeroRows},
1840 {"zero-iteration-structured-logging", TestZeroIterationStructuredLogging},
1841 {"flat-channel-bdf1-startup", TestFlatChannelStartup},
1842 {"restart-and-continuation-solve", TestRestartAndContinuationSolve},
1843 {"small-solve-and-rollback", TestSmallSolveAndRollback},
1844 {"unsupported-configuration-fails-before-allocation", TestUnsupportedConfigurationFailsBeforeAllocation},
1845 {"post-allocation-failure-cleanup", TestPostAllocationFailureCleanup},
1846 {"linearization-config-parsing", TestLinearizationConfigParsing},
1847 {"point-block-preconditioner-engine", TestPointBlockPreconditionerEngine},
1848 {"jacobian-interface-and-baseline-alias", TestJacobianInterfaceAndBaselineAlias},
1849 {"point-block-periodic-assembly", TestPointBlockPeriodicAssembly},
1850 };
1851
1852 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv Newton Krylov tests");
1853 if (ierr) return (int)ierr;
1854 ierr = PicurvRunTests("unit-newton-krylov", cases, sizeof(cases) / sizeof(cases[0]));
1855 if (PetscFinalize()) return 1;
1856 return (int)ierr;
1857}
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, forced rollback, and per-call cleanup.
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 TestPeriodicConstraintDerivativesAndIntersections(void)
Proves single-, double-, triple-, and mixed-boundary periodic equations.
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 constant_velocity vx=0.0 vy=0.0 vz=1.5\n"
"+Zeta OUTLET conservation\n"

Definition at line 18 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 26 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 parabolic v_max=1.5\n"
"+Zeta OUTLET conservation\n"

Definition at line 34 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 42 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 46 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 50 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 54 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 59 of file test_momentum_newton_krylov.c.