PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Macros | Enumerations | 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
 

Enumerations

enum  {
  ORACLE_CENTER_AJ = 1 << 0 , ORACLE_CENTER_TRANSVERSE_METRICS = 1 << 1 , ORACLE_CENTER_VELOCITY = 1 << 2 , ORACLE_OMIT_A5 = 1 << 3 ,
  ORACLE_LEGACY_SIGN = 1 << 4
}
 

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 PetscReal LegacyOracleMetricNormSquared (Cmpnts metric)
 Test-owned metric norm used by the independent legacy transcription.
 
static void LegacyPointBlockOracle (const SimCtx *simCtx, const Cmpnts ***u, const Cmpnts ***csi, const Cmpnts ***eta, const Cmpnts ***zet, const PetscReal ***aj, PetscInt i, PetscInt j, PetscInt k, PetscInt flags, PetscScalar block[9])
 Independent transcription of the audited legacy mode-2 point block.
 
static PetscErrorCode SeedPointBlockOracleFields (UserCtx *user)
 Seeds nonuniform, index-distinguishing coefficient fields.
 
static PetscErrorCode CollectiveLegacyPointBlockOracle (UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt flags, PetscScalar block[9])
 Evaluates the independent oracle on the unique owner and broadcasts it.
 
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.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
ORACLE_CENTER_AJ 
ORACLE_CENTER_TRANSVERSE_METRICS 
ORACLE_CENTER_VELOCITY 
ORACLE_OMIT_A5 
ORACLE_LEGACY_SIGN 

Definition at line 1324 of file test_momentum_newton_krylov.c.

1324 {
1325 ORACLE_CENTER_AJ = 1 << 0,
1327 ORACLE_CENTER_VELOCITY = 1 << 2,
1328 ORACLE_OMIT_A5 = 1 << 3,
1329 ORACLE_LEGACY_SIGN = 1 << 4
1330};
@ ORACLE_CENTER_TRANSVERSE_METRICS

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:535
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:777
PetscReal FarFluxOutSum
Definition variables.h:777
Vec Rhs
Definition variables.h:912
PetscReal FluxOutSum
Definition variables.h:777
Vec Ucont
Definition variables.h:904
PetscReal FluxInSum
Definition variables.h:777
Vec Ucat
Definition variables.h:904
Vec lUcat
Definition variables.h:904
The master context for the entire simulation.
Definition variables.h:684
User-defined context containing data specific to a single computational grid level.
Definition variables.h:876
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:883
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:101
PetscScalar z
Definition variables.h:101
PetscScalar y
Definition variables.h:101
A 3D point or vector with PetscScalar components.
Definition variables.h:100
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;
463
464 coord[axis] = 0;
465 row = MomentumNewtonKrylov_ClassifyRow(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 = MomentumNewtonKrylov_ClassifyRow(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 = MomentumNewtonKrylov_ClassifyRow(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 = MomentumNewtonKrylov_ClassifyRow(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 = MomentumNewtonKrylov_ClassifyRow(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}
static MomentumNewtonKrylovRowType MomentumNewtonKrylov_ClassifyRow(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscInt *ri, PetscInt *rj, PetscInt *rk)
Classifies one stored staggered component row and its periodic representative.
MomentumNewtonKrylovRowType
@ MOM_NK_ROW_PHYSICAL
@ MOM_NK_ROW_FIXED_HOMOGENEOUS
@ MOM_NK_ROW_FIXED_CONDITIONED
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:744
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:290
@ WALL
Definition variables.h:284
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:314
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:303
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:259
@ BC_FACE_NEG_X
Definition variables.h:260
@ BC_FACE_POS_X
Definition variables.h:260
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
926 PetscFunctionBeginUser;
927 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
928 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_rtol", "1e-4"));
929 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "20"));
930 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_ksp_rtol", "1e-6"));
931 simCtx->mom_nk_monitor_history = PETSC_TRUE;
932 PetscCall(MomentumSolver_NewtonKrylov(user, NULL, NULL));
933 PetscCall(PicurvAssertBool(simCtx->mom_last_converged, "small Newton solve must converge"));
934 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL), "successful solve must release Rhs"));
935 PetscCall(PetscSNPrintf(summary_path, sizeof(summary_path),
936 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.log", simCtx->log_dir));
937 PetscCall(PetscSNPrintf(history_path, sizeof(history_path),
938 "%s/Momentum_Solver_Newton_Krylov_History_Block_0.log", simCtx->log_dir));
939 PetscCall(AssertNewtonLog(summary_path, 1, "solver: Newton Krylov", "state: committed"));
940 PetscCall(AssertNewtonLog(history_path, -2, "newton: 0", "nonlinear_norm:"));
941
942 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
943 tmpdir[0] = '\0';
944 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
945 PetscCall(VecSet(user->Ucont, 0.2));
946 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
947 PetscCall(ApplyBoundaryConditions(user));
948 PetscCall(VecDuplicate(user->Ucont, &entry));
949 PetscCall(VecDuplicate(user->Ucont, &delta));
950 PetscCall(VecCopy(user->Ucont, entry));
951 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_snes_max_it", "0"));
952 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
953 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
954 PetscCall(PetscPopErrorHandler());
955 PetscCall(PicurvAssertIntEqual(PETSC_ERR_CONV_FAILED, solve_ierr,
956 "forced nonconvergence must report PETSC_ERR_CONV_FAILED"));
957 PetscCall(VecWAXPY(delta, -1.0, entry, user->Ucont));
958 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
959 PetscCall(PicurvAssertRealNear(0.0, norm, 1.0e-12,
960 "failed Newton solve must restore the canonical entry state"));
961 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL), "failed solve must release Rhs"));
962 PetscCall(PetscSNPrintf(summary_path, sizeof(summary_path),
963 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.log", simCtx->log_dir));
964 PetscCall(AssertNewtonLog(summary_path, 1, "reason_code: -", "state: rolled_back"));
965
966 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_rtol"));
967 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_snes_max_it"));
968 PetscCall(PetscOptionsClearValue(NULL, "-mom_nk_ksp_rtol"));
969 PetscCall(VecDestroy(&delta));
970 PetscCall(VecDestroy(&entry));
971 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
972 PetscFunctionReturn(PETSC_SUCCESS);
973}
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:740
PetscBool mom_last_converged
Definition variables.h:738
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:709
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 976 of file test_momentum_newton_krylov.c.

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

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

1058{
1059 PetscFunctionBeginUser;
1060 PetscCall(CheckFlatChannelStartup(PETSC_FALSE));
1061 PetscCall(CheckFlatChannelStartup(PETSC_TRUE));
1062 PetscFunctionReturn(PETSC_SUCCESS);
1063}
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 1066 of file test_momentum_newton_krylov.c.

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

1122{
1123 SimCtx *simCtx = NULL;
1124 UserCtx *user = NULL;
1125 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1126 PetscErrorCode solve_ierr;
1127
1128 PetscFunctionBeginUser;
1129 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1130 {
1131 PetscInt *unsupported_flags[] = {
1132 &simCtx->immersed, &simCtx->movefsi, &simCtx->rotatefsi,
1133 &simCtx->moveframe, &simCtx->rotateframe, &simCtx->rans,
1134 &simCtx->clark, &simCtx->TwoD, &simCtx->wallfunction
1135 };
1136 for (size_t flag = 0; flag < sizeof(unsupported_flags) / sizeof(unsupported_flags[0]); ++flag) {
1137 *unsupported_flags[flag] = 1;
1138 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1139 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1140 PetscCall(PetscPopErrorHandler());
1141 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS),
1142 "unsupported Newton feature flag must fail"));
1143 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL),
1144 "feature validation must precede workspace allocation"));
1145 *unsupported_flags[flag] = 0;
1146 }
1147 }
1148 simCtx->block_number = 2;
1149 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1150 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1151 PetscCall(PetscPopErrorHandler());
1152 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS), "multiblock must fail"));
1153 simCtx->block_number = 1;
1154 PetscCall(VecSet(user->Nvert, 1.0));
1155 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1156 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1157 PetscCall(PetscPopErrorHandler());
1158 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS), "masked rows must fail"));
1159 PetscCall(VecSet(user->Nvert, 0.0));
1162 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1163 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1164 PetscCall(PetscPopErrorHandler());
1165 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS),
1166 "driven constant-flux controller must fail"));
1169 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1170 solve_ierr = MomentumSolver_NewtonKrylov(user, NULL, NULL);
1171 PetscCall(PetscPopErrorHandler());
1172 PetscCall(PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS),
1173 "unimplemented interpolated-file inlet must fail"));
1174 PetscCall(PicurvAssertBool((PetscBool)(user->Rhs == NULL),
1175 "all validation failures must precede workspace allocation"));
1176 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1177 PetscFunctionReturn(PETSC_SUCCESS);
1178}
PetscInt clark
Definition variables.h:790
PetscInt movefsi
Definition variables.h:714
@ INLET
Definition variables.h:288
PetscInt moveframe
Definition variables.h:715
PetscInt TwoD
Definition variables.h:715
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:896
PetscInt block_number
Definition variables.h:768
PetscInt rans
Definition variables.h:789
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:316
@ BC_HANDLER_INLET_INTERP_FROM_FILE
Definition variables.h:309
BCHandlerType handler_type
Definition variables.h:367
PetscInt rotatefsi
Definition variables.h:714
PetscInt wallfunction
Definition variables.h:790
Vec Nvert
Definition variables.h:904
BCType mathematical_type
Definition variables.h:366
PetscInt rotateframe
Definition variables.h:715
PetscInt immersed
Definition variables.h:714
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 1181 of file test_momentum_newton_krylov.c.

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

1218{
1219 MomentumNewtonJacobian jacobian = {0};
1220 MomentumPreconditionerDescription description = {0};
1221 PetscErrorCode config_ierr;
1222 const char *option_names[] = {
1223 "-mom_nk_jacobian_type",
1224 "-mom_nk_jacobian_fd_mode",
1225 "-mom_nk_preconditioner_model",
1226 "-mom_nk_preconditioner_structure"
1227 };
1228
1229 PetscFunctionBeginUser;
1230 for (size_t n = 0; n < sizeof(option_names) / sizeof(option_names[0]); ++n)
1231 PetscCall(PetscOptionsClearValue(NULL, option_names[n]));
1232
1233 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description));
1235 "default Jacobian type must be finite difference"));
1237 jacobian.finite_difference_mode,
1238 "default finite-difference mode must be matrix free"));
1239 PetscCall(PicurvAssertIntEqual(MOM_NK_PC_MODEL_NONE, description.model,
1240 "default preconditioner model must be none"));
1242 "default preconditioner structure must be none"));
1243
1244 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_type", "finite_difference"));
1245 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_fd_mode", "matrix_free"));
1246 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", "none"));
1247 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "none"));
1248 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description));
1250 "explicit baseline Jacobian type must parse"));
1252 jacobian.finite_difference_mode,
1253 "explicit baseline finite-difference mode must parse"));
1254 PetscCall(PicurvAssertIntEqual(MOM_NK_PC_MODEL_NONE, description.model,
1255 "explicit baseline preconditioner model must parse"));
1257 "explicit baseline preconditioner structure must parse"));
1258
1259 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1260 "frozen_momentum_jacobian"));
1261 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "point_block"));
1262 PetscCall(MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description));
1264 "explicit Jacobian type must parse"));
1266 jacobian.finite_difference_mode,
1267 "explicit finite-difference mode must parse"));
1269 description.model, "frozen model must parse"));
1271 description.structure, "point-block structure must parse"));
1272
1273 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_type", "analytic"));
1274 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1275 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1276 PetscCall(PetscPopErrorHandler());
1277 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1278 "unsupported Jacobian type must fail"));
1279 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_type", "finite_difference"));
1280
1281 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_fd_mode", "colored_sparse"));
1282 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1283 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1284 PetscCall(PetscPopErrorHandler());
1285 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1286 "unsupported finite-difference mode must fail"));
1287 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_jacobian_fd_mode", "matrix_free"));
1288
1289 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "none"));
1290 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1291 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1292 PetscCall(PetscPopErrorHandler());
1293 PetscCall(PicurvAssertIntEqual(PETSC_ERR_SUP, config_ierr,
1294 "frozen model without point block must fail"));
1295 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", "none"));
1296 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "point_block"));
1297 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1298 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1299 PetscCall(PetscPopErrorHandler());
1300 PetscCall(PicurvAssertIntEqual(PETSC_ERR_SUP, config_ierr,
1301 "none model with point block must fail"));
1302
1303 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model", "diagonal"));
1304 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "none"));
1305 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1306 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1307 PetscCall(PetscPopErrorHandler());
1308 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1309 "unsupported preconditioner model must fail"));
1310 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_model",
1311 "frozen_momentum_jacobian"));
1312 PetscCall(PetscOptionsSetValue(NULL, "-mom_nk_preconditioner_structure", "line"));
1313 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1314 config_ierr = MomentumNewtonKrylov_ReadLinearizationConfig(&jacobian, &description);
1315 PetscCall(PetscPopErrorHandler());
1316 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, config_ierr,
1317 "unsupported preconditioner structure must fail"));
1318
1319 for (size_t n = 0; n < sizeof(option_names) / sizeof(option_names[0]); ++n)
1320 PetscCall(PetscOptionsClearValue(NULL, option_names[n]));
1321 PetscFunctionReturn(PETSC_SUCCESS);
1322}
@ 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:

◆ LegacyOracleMetricNormSquared()

static PetscReal LegacyOracleMetricNormSquared ( Cmpnts  metric)
static

Test-owned metric norm used by the independent legacy transcription.

Definition at line 1333 of file test_momentum_newton_krylov.c.

1334{
1335 return metric.x * metric.x + metric.y * metric.y + metric.z * metric.z;
1336}
Here is the caller graph for this function:

◆ LegacyPointBlockOracle()

static void LegacyPointBlockOracle ( const SimCtx simCtx,
const Cmpnts ***  u,
const Cmpnts ***  csi,
const Cmpnts ***  eta,
const Cmpnts ***  zet,
const PetscReal ***  aj,
PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscInt  flags,
PetscScalar  block[9] 
)
static

Independent transcription of the audited legacy mode-2 point block.

This intentionally shares no coefficient helper with production. Mutant flags represent the historical failure modes that the nonuniform oracle must reject.

Definition at line 1344 of file test_momentum_newton_krylov.c.

1348{
1349 PetscReal A[6][4] = {{0.0}};
1350 const PetscReal dtc = ((simCtx->step != simCtx->StartStep) && simCtx->step != 1 ? 1.5 : 1.0) /
1351 simCtx->dt;
1352 const PetscReal AJip = flags & ORACLE_CENTER_AJ ? aj[k][j][i] :
1353 0.5 * (aj[k][j][i] + aj[k][j][i + 1]);
1354 const PetscReal AJjp = flags & ORACLE_CENTER_AJ ? aj[k][j][i] :
1355 0.5 * (aj[k][j][i] + aj[k][j + 1][i]);
1356 const PetscReal AJkp = flags & ORACLE_CENTER_AJ ? aj[k][j][i] :
1357 0.5 * (aj[k][j][i] + aj[k + 1][j][i]);
1358 const PetscReal g11ip = LegacyOracleMetricNormSquared(csi[k][j][i]);
1359 const PetscReal g22ip = flags & ORACLE_CENTER_TRANSVERSE_METRICS ?
1360 LegacyOracleMetricNormSquared(eta[k][j][i]) : 0.25 * (
1361 LegacyOracleMetricNormSquared(eta[k][j][i]) + LegacyOracleMetricNormSquared(eta[k][j][i + 1]) +
1362 LegacyOracleMetricNormSquared(eta[k][j - 1][i]) + LegacyOracleMetricNormSquared(eta[k][j - 1][i + 1]));
1363 const PetscReal g33ip = flags & ORACLE_CENTER_TRANSVERSE_METRICS ?
1364 LegacyOracleMetricNormSquared(zet[k][j][i]) : 0.25 * (
1365 LegacyOracleMetricNormSquared(zet[k][j][i]) + LegacyOracleMetricNormSquared(zet[k][j][i + 1]) +
1366 LegacyOracleMetricNormSquared(zet[k - 1][j][i]) + LegacyOracleMetricNormSquared(zet[k - 1][j][i + 1]));
1367 const PetscReal g11jp = flags & ORACLE_CENTER_TRANSVERSE_METRICS ?
1368 LegacyOracleMetricNormSquared(csi[k][j][i]) : 0.25 * (
1369 LegacyOracleMetricNormSquared(csi[k][j][i]) + LegacyOracleMetricNormSquared(csi[k][j + 1][i]) +
1370 LegacyOracleMetricNormSquared(csi[k][j][i - 1]) + LegacyOracleMetricNormSquared(csi[k][j + 1][i - 1]));
1371 const PetscReal g22jp = LegacyOracleMetricNormSquared(eta[k][j][i]);
1372 const PetscReal g33jp = flags & ORACLE_CENTER_TRANSVERSE_METRICS ?
1373 LegacyOracleMetricNormSquared(zet[k][j][i]) : 0.25 * (
1374 LegacyOracleMetricNormSquared(zet[k][j][i]) + LegacyOracleMetricNormSquared(zet[k][j + 1][i]) +
1375 LegacyOracleMetricNormSquared(zet[k - 1][j][i]) + LegacyOracleMetricNormSquared(zet[k - 1][j + 1][i]));
1376 const PetscReal g11kp = flags & ORACLE_CENTER_TRANSVERSE_METRICS ?
1377 LegacyOracleMetricNormSquared(csi[k][j][i]) : 0.25 * (
1378 LegacyOracleMetricNormSquared(csi[k][j][i]) + LegacyOracleMetricNormSquared(csi[k + 1][j][i]) +
1379 LegacyOracleMetricNormSquared(csi[k][j][i - 1]) + LegacyOracleMetricNormSquared(csi[k + 1][j][i - 1]));
1380 const PetscReal g22kp = flags & ORACLE_CENTER_TRANSVERSE_METRICS ?
1381 LegacyOracleMetricNormSquared(eta[k][j][i]) : 0.25 * (
1382 LegacyOracleMetricNormSquared(eta[k][j][i]) + LegacyOracleMetricNormSquared(eta[k + 1][j][i]) +
1383 LegacyOracleMetricNormSquared(eta[k][j - 1][i]) + LegacyOracleMetricNormSquared(eta[k + 1][j - 1][i]));
1384 const PetscReal g33kp = LegacyOracleMetricNormSquared(zet[k][j][i]);
1385 const PetscReal U0jp = flags & ORACLE_CENTER_VELOCITY ? u[k][j][i].x : 0.25 *
1386 (u[k][j][i].x + u[k][j][i - 1].x + u[k][j + 1][i].x + u[k][j + 1][i - 1].x);
1387 const PetscReal U0kp = flags & ORACLE_CENTER_VELOCITY ? u[k][j][i].x : 0.25 *
1388 (u[k][j][i].x + u[k][j][i - 1].x + u[k + 1][j][i].x + u[k + 1][j][i - 1].x);
1389 const PetscReal U1ip = flags & ORACLE_CENTER_VELOCITY ? u[k][j][i].y : 0.25 *
1390 (u[k][j][i].y + u[k][j - 1][i].y + u[k][j][i + 1].y + u[k][j - 1][i + 1].y);
1391 const PetscReal U1kp = flags & ORACLE_CENTER_VELOCITY ? u[k][j][i].y : 0.25 *
1392 (u[k][j][i].y + u[k][j - 1][i].y + u[k + 1][j][i].y + u[k + 1][j - 1][i].y);
1393 const PetscReal U2ip = flags & ORACLE_CENTER_VELOCITY ? u[k][j][i].z : 0.25 *
1394 (u[k][j][i].z + u[k - 1][j][i].z + u[k][j][i + 1].z + u[k - 1][j][i + 1].z);
1395 const PetscReal U2jp = flags & ORACLE_CENTER_VELOCITY ? u[k][j][i].z : 0.25 *
1396 (u[k][j][i].z + u[k - 1][j][i].z + u[k][j + 1][i].z + u[k - 1][j + 1][i].z);
1397 PetscReal Su, Sv, Sw, sign = flags & ORACLE_LEGACY_SIGN ? -1.0 : 1.0;
1398
1399 A[0][0] = .125 * aj[k][j][i] * u[k][j][i].y;
1400 A[0][1] = -.125 * aj[k][j - 1][i] * u[k][j - 1][i].y;
1401 A[0][2] = .125 * aj[k][j][i + 1] * u[k][j][i + 1].y;
1402 A[0][3] = -.125 * aj[k][j - 1][i + 1] * u[k][j - 1][i + 1].y;
1403 A[1][0] = .125 * aj[k][j][i] * u[k][j][i].z;
1404 A[1][1] = -.125 * aj[k - 1][j][i] * u[k - 1][j][i].z;
1405 A[1][2] = .125 * aj[k][j][i + 1] * u[k][j][i + 1].z;
1406 A[1][3] = -.125 * aj[k - 1][j][i + 1] * u[k - 1][j][i + 1].z;
1407 A[2][0] = -.125 * aj[k][j + 1][i - 1] * u[k][j + 1][i - 1].x;
1408 A[2][1] = -.125 * aj[k][j][i - 1] * u[k][j][i - 1].x;
1409 A[2][2] = .125 * aj[k][j + 1][i] * u[k][j + 1][i].x;
1410 A[2][3] = .125 * aj[k][j][i] * u[k][j][i].x;
1411 A[3][0] = .125 * aj[k][j][i] * u[k][j][i].z;
1412 A[3][1] = -.125 * aj[k - 1][j][i] * u[k - 1][j][i].z;
1413 A[3][2] = .125 * aj[k][j + 1][i] * u[k][j + 1][i].z;
1414 A[3][3] = -.125 * aj[k - 1][j + 1][i] * u[k - 1][j + 1][i].z;
1415 A[4][0] = -.125 * aj[k + 1][j][i - 1] * u[k + 1][j][i - 1].x;
1416 A[4][1] = -.125 * aj[k][j][i - 1] * u[k][j][i - 1].x;
1417 A[4][2] = .125 * aj[k + 1][j][i] * u[k + 1][j][i].x;
1418 A[4][3] = .125 * aj[k][j][i] * u[k][j][i].x;
1419 A[5][0] = -.125 * aj[k + 1][j - 1][i] * u[k + 1][j - 1][i].y;
1420 A[5][1] = -.125 * aj[k][j - 1][i] * u[k][j - 1][i].y;
1421 A[5][2] = .125 * aj[k + 1][j][i] * u[k + 1][j][i].y;
1422 A[5][3] = .125 * aj[k][j][i] * u[k][j][i].y;
1423 Su = A[0][0] + A[0][1] + A[0][2] + A[0][3] + A[1][0] + A[1][1] + A[1][2] + A[1][3];
1424 Sv = A[2][0] + A[2][1] + A[2][2] + A[2][3] + A[3][0] + A[3][1] + A[3][2] + A[3][3];
1425 Sw = A[4][0] + A[4][1] + A[4][2] + A[4][3];
1426 if (!(flags & ORACLE_OMIT_A5)) Sw += A[5][0] + A[5][1] + A[5][2] + A[5][3];
1427
1428 block[0] = sign * (dtc + AJip * AJip * (g11ip + g22ip + g33ip) / simCtx->ren + Su);
1429 block[1] = sign * 0.5 * AJip * U1ip; block[2] = sign * 0.5 * AJip * U2ip;
1430 block[3] = sign * 0.5 * AJjp * U0jp;
1431 block[4] = sign * (dtc + AJjp * AJjp * (g11jp + g22jp + g33jp) / simCtx->ren + Sv);
1432 block[5] = sign * 0.5 * AJjp * U2jp;
1433 block[6] = sign * 0.5 * AJkp * U0kp; block[7] = sign * 0.5 * AJkp * U1kp;
1434 block[8] = sign * (dtc + AJkp * AJkp * (g11kp + g22kp + g33kp) / simCtx->ren + Sw);
1435}
static PetscReal LegacyOracleMetricNormSquared(Cmpnts metric)
Test-owned metric norm used by the independent legacy transcription.
PetscReal ren
Definition variables.h:732
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SeedPointBlockOracleFields()

static PetscErrorCode SeedPointBlockOracleFields ( UserCtx user)
static

Seeds nonuniform, index-distinguishing coefficient fields.

Definition at line 1438 of file test_momentum_newton_krylov.c.

1439{
1440 Cmpnts ***u = NULL, ***csi = NULL, ***eta = NULL, ***zet = NULL;
1441 PetscReal ***aj = NULL;
1442 DMDALocalInfo info = user->info;
1443
1444 PetscFunctionBeginUser;
1445 PetscCall(DMDAVecGetArray(user->fda, user->Ucont, &u));
1446 PetscCall(DMDAVecGetArray(user->fda, user->Csi, &csi));
1447 PetscCall(DMDAVecGetArray(user->fda, user->Eta, &eta));
1448 PetscCall(DMDAVecGetArray(user->fda, user->Zet, &zet));
1449 PetscCall(DMDAVecGetArray(user->da, user->Aj, &aj));
1450 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k)
1451 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j)
1452 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
1453 aj[k][j][i] = 0.7 + .019 * i + .043 * j + .071 * k + .003 * i * j + .002 * j * k;
1454 u[k][j][i] = (Cmpnts){.x = .2 + .031 * i - .017 * j + .013 * k + .004 * i * k,
1455 .y = -.3 + .011 * i + .037 * j - .019 * k + .003 * j * k,
1456 .z = .4 - .023 * i + .007 * j + .041 * k + .002 * i * j};
1457 csi[k][j][i] = (Cmpnts){.x = 1.1 + .029 * i + .007 * j * k,
1458 .y = .13 + .017 * j + .003 * i * k,
1459 .z = -.09 + .011 * k + .002 * i * j};
1460 eta[k][j][i] = (Cmpnts){.x = -.12 + .013 * i + .004 * j * k,
1461 .y = .9 + .031 * j + .003 * i * k,
1462 .z = .16 + .019 * k + .002 * i * j};
1463 zet[k][j][i] = (Cmpnts){.x = .08 + .023 * i + .002 * j * k,
1464 .y = -.14 + .011 * j + .005 * i * k,
1465 .z = 1.2 + .037 * k + .003 * i * j};
1466 }
1467 PetscCall(DMDAVecRestoreArray(user->da, user->Aj, &aj));
1468 PetscCall(DMDAVecRestoreArray(user->fda, user->Zet, &zet));
1469 PetscCall(DMDAVecRestoreArray(user->fda, user->Eta, &eta));
1470 PetscCall(DMDAVecRestoreArray(user->fda, user->Csi, &csi));
1471 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucont, &u));
1472 PetscCall(DMGlobalToLocalBegin(user->fda, user->Ucont, INSERT_VALUES, user->lUcont));
1473 PetscCall(DMGlobalToLocalEnd(user->fda, user->Ucont, INSERT_VALUES, user->lUcont));
1474 PetscCall(DMGlobalToLocalBegin(user->fda, user->Csi, INSERT_VALUES, user->lCsi));
1475 PetscCall(DMGlobalToLocalEnd(user->fda, user->Csi, INSERT_VALUES, user->lCsi));
1476 PetscCall(DMGlobalToLocalBegin(user->fda, user->Eta, INSERT_VALUES, user->lEta));
1477 PetscCall(DMGlobalToLocalEnd(user->fda, user->Eta, INSERT_VALUES, user->lEta));
1478 PetscCall(DMGlobalToLocalBegin(user->fda, user->Zet, INSERT_VALUES, user->lZet));
1479 PetscCall(DMGlobalToLocalEnd(user->fda, user->Zet, INSERT_VALUES, user->lZet));
1480 PetscCall(DMGlobalToLocalBegin(user->da, user->Aj, INSERT_VALUES, user->lAj));
1481 PetscCall(DMGlobalToLocalEnd(user->da, user->Aj, INSERT_VALUES, user->lAj));
1482 PetscFunctionReturn(PETSC_SUCCESS);
1483}
Vec Zet
Definition variables.h:927
Vec lZet
Definition variables.h:927
Vec Csi
Definition variables.h:927
Vec Eta
Definition variables.h:927
Vec lCsi
Definition variables.h:927
Vec lUcont
Definition variables.h:904
Vec lAj
Definition variables.h:927
Vec lEta
Definition variables.h:927
Here is the caller graph for this function:

◆ CollectiveLegacyPointBlockOracle()

static PetscErrorCode CollectiveLegacyPointBlockOracle ( UserCtx user,
PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscInt  flags,
PetscScalar  block[9] 
)
static

Evaluates the independent oracle on the unique owner and broadcasts it.

Definition at line 1486 of file test_momentum_newton_krylov.c.

1488{
1489 Cmpnts ***u = NULL, ***csi = NULL, ***eta = NULL, ***zet = NULL;
1490 PetscReal ***aj = NULL;
1491 PetscInt owns = i >= user->info.xs && i < user->info.xs + user->info.xm &&
1492 j >= user->info.ys && j < user->info.ys + user->info.ym &&
1493 k >= user->info.zs && k < user->info.zs + user->info.zm;
1494 PetscInt owners = 0;
1495 PetscScalar local[9] = {0.0};
1496
1497 PetscFunctionBeginUser;
1498 if (owns) {
1499 PetscCall(DMDAVecGetArrayRead(user->fda, user->lUcont, &u));
1500 PetscCall(DMDAVecGetArrayRead(user->fda, user->lCsi, &csi));
1501 PetscCall(DMDAVecGetArrayRead(user->fda, user->lEta, &eta));
1502 PetscCall(DMDAVecGetArrayRead(user->fda, user->lZet, &zet));
1503 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, &aj));
1504 LegacyPointBlockOracle(user->simCtx, (const Cmpnts ***)u, (const Cmpnts ***)csi,
1505 (const Cmpnts ***)eta, (const Cmpnts ***)zet, (const PetscReal ***)aj,
1506 i, j, k, flags, local);
1507 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, &aj));
1508 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet));
1509 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta));
1510 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi));
1511 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lUcont, &u));
1512 }
1513 PetscCallMPI(MPI_Allreduce(&owns, &owners, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
1514 PetscCall(PicurvAssertIntEqual(1, owners, "oracle point must have exactly one owner"));
1515 PetscCallMPI(MPI_Allreduce(local, block, 9, MPIU_SCALAR, MPI_SUM, PETSC_COMM_WORLD));
1516 PetscFunctionReturn(PETSC_SUCCESS);
1517}
static void LegacyPointBlockOracle(const SimCtx *simCtx, const Cmpnts ***u, const Cmpnts ***csi, const Cmpnts ***eta, const Cmpnts ***zet, const PetscReal ***aj, PetscInt i, PetscInt j, PetscInt k, PetscInt flags, PetscScalar block[9])
Independent transcription of the audited legacy mode-2 point block.
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:879
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 1520 of file test_momentum_newton_krylov.c.

1522{
1523 ISLocalToGlobalMapping local_to_global = NULL;
1524 PetscInt ghost_starts[3], ghost_sizes[3], local_index;
1525
1526 PetscFunctionBeginUser;
1527 PetscCall(DMDAGetGhostCorners(user->fda,
1528 &ghost_starts[0], &ghost_starts[1], &ghost_starts[2],
1529 &ghost_sizes[0], &ghost_sizes[1], &ghost_sizes[2]));
1530 PetscCall(PicurvAssertBool((PetscBool)(
1531 stencil.i >= ghost_starts[0] && stencil.i < ghost_starts[0] + ghost_sizes[0] &&
1532 stencil.j >= ghost_starts[1] && stencil.j < ghost_starts[1] + ghost_sizes[1] &&
1533 stencil.k >= ghost_starts[2] && stencil.k < ghost_starts[2] + ghost_sizes[2]),
1534 "test stencil must lie in the local DMDA ghost region"));
1535 local_index = stencil.c + 3 * (
1536 (stencil.i - ghost_starts[0]) + ghost_sizes[0] * (
1537 (stencil.j - ghost_starts[1]) + ghost_sizes[1] *
1538 (stencil.k - ghost_starts[2])));
1539 PetscCall(DMGetLocalToGlobalMapping(user->fda, &local_to_global));
1540 PetscCall(ISLocalToGlobalMappingApply(local_to_global, 1, &local_index,
1541 global_index));
1542 PetscFunctionReturn(PETSC_SUCCESS);
1543}
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 1546 of file test_momentum_newton_krylov.c.

1549{
1550 Vec column_basis = NULL, row_basis = NULL, product = NULL;
1551 AO ao = NULL;
1552 PetscInt mx, my, row_index, col_index;
1553 PetscFunctionBeginUser;
1554 PetscCall(DMDAGetInfo(user->fda, NULL, &mx, &my, NULL, NULL, NULL, NULL,
1555 NULL, NULL, NULL, NULL, NULL, NULL));
1556 row_index = row.c + 3 * (row.i + mx * (row.j + my * row.k));
1557 col_index = col.c + 3 * (col.i + mx * (col.j + my * col.k));
1558 PetscCall(DMDAGetAO(user->fda, &ao));
1559 PetscCall(AOApplicationToPetsc(ao, 1, &row_index));
1560 PetscCall(AOApplicationToPetsc(ao, 1, &col_index));
1561 PetscCall(DMCreateGlobalVector(user->fda, &column_basis));
1562 PetscCall(DMCreateGlobalVector(user->fda, &row_basis));
1563 PetscCall(DMCreateGlobalVector(user->fda, &product));
1564 PetscCall(VecSet(column_basis, 0.0)); PetscCall(VecSet(row_basis, 0.0));
1565 PetscCall(VecSetValue(column_basis, col_index, 1.0, INSERT_VALUES));
1566 PetscCall(VecSetValue(row_basis, row_index, 1.0, INSERT_VALUES));
1567 PetscCall(VecAssemblyBegin(column_basis)); PetscCall(VecAssemblyEnd(column_basis));
1568 PetscCall(VecAssemblyBegin(row_basis)); PetscCall(VecAssemblyEnd(row_basis));
1569 PetscCall(MatMult(preconditioning_matrix, column_basis, product));
1570 PetscCall(VecDot(row_basis, product, value));
1571 PetscCall(VecDestroy(&product)); PetscCall(VecDestroy(&row_basis));
1572 PetscCall(VecDestroy(&column_basis));
1573 PetscFunctionReturn(PETSC_SUCCESS);
1574}
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 1577 of file test_momentum_newton_krylov.c.

1579{
1580 DMDALocalInfo info;
1581 MatInfo matrix_info;
1582 PetscMPIInt comm_size = 1;
1583 PetscInt matrix_rows, matrix_cols, local_rows, local_cols;
1584 PetscInt vector_size, vector_local_size, block_size;
1585 PetscInt ownership_start, ownership_end;
1586 PetscInt expected_local = 0, expected_global = 0;
1587 PetscInt offrank_periodic_local = 0, offrank_periodic_global = 0;
1588 PetscBool is_seq_aij = PETSC_FALSE, is_mpi_aij = PETSC_FALSE;
1589
1590 PetscFunctionBeginUser;
1591 PetscCall(DMDAGetLocalInfo(user->fda, &info));
1592 PetscCall(MatGetSize(matrix, &matrix_rows, &matrix_cols));
1593 PetscCall(MatGetLocalSize(matrix, &local_rows, &local_cols));
1594 PetscCall(VecGetSize(user->Ucont, &vector_size));
1595 PetscCall(VecGetLocalSize(user->Ucont, &vector_local_size));
1596 PetscCall(VecGetOwnershipRange(user->Ucont, &ownership_start, &ownership_end));
1597 PetscCall(MatGetBlockSize(matrix, &block_size));
1598 PetscCall(PicurvAssertIntEqual(vector_size, matrix_rows,
1599 "point-block global row dimension"));
1600 PetscCall(PicurvAssertIntEqual(vector_size, matrix_cols,
1601 "point-block global column dimension"));
1602 PetscCall(PicurvAssertIntEqual(vector_local_size, local_rows,
1603 "point-block local row dimension"));
1604 PetscCall(PicurvAssertIntEqual(vector_local_size, local_cols,
1605 "point-block local column dimension"));
1606 PetscCall(PicurvAssertIntEqual(3, block_size, "point-block logical block size"));
1607 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)matrix), &comm_size));
1608 PetscCall(PetscObjectTypeCompare((PetscObject)matrix, MATSEQAIJ, &is_seq_aij));
1609 PetscCall(PetscObjectTypeCompare((PetscObject)matrix, MATMPIAIJ, &is_mpi_aij));
1610 PetscCall(PicurvAssertBool(
1611 comm_size == 1 ? is_seq_aij : is_mpi_aij,
1612 "point-block matrix must use the expected AIJ implementation"));
1613
1614 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
1615 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
1616 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
1617 for (PetscInt component = 0; component < 3; ++component) {
1618 PetscInt ri, rj, rk;
1620 user, i, j, k, component, &ri, &rj, &rk);
1621 expected_local += type == MOM_NK_ROW_PHYSICAL ? 3 :
1622 type == MOM_NK_ROW_PERIODIC_DUPLICATE ? 2 : 1;
1623 if (type == MOM_NK_ROW_PERIODIC_DUPLICATE) {
1624 PetscInt representative;
1625 PetscCall(TestStencilToGlobal(user,
1626 (MatStencil){.i = ri, .j = rj, .k = rk, .c = component},
1627 &representative));
1628 if (representative < ownership_start || representative >= ownership_end)
1629 ++offrank_periodic_local;
1630 }
1631 }
1632 }
1633 }
1634 }
1635 PetscCallMPI(MPI_Allreduce(&expected_local, &expected_global, 1, MPIU_INT,
1636 MPI_SUM, PetscObjectComm((PetscObject)matrix)));
1637 PetscCallMPI(MPI_Allreduce(&offrank_periodic_local, &offrank_periodic_global,
1638 1, MPIU_INT, MPI_SUM,
1639 PetscObjectComm((PetscObject)matrix)));
1640 PetscCall(MatGetInfo(matrix, MAT_GLOBAL_SUM, &matrix_info));
1641 PetscCall(PicurvAssertRealNear((PetscReal)expected_global,
1642 (PetscReal)matrix_info.nz_allocated, 0.0,
1643 "point-block matrix must allocate exactly the classified scalar pattern"));
1644 PetscCall(PicurvAssertRealNear((PetscReal)expected_global,
1645 (PetscReal)matrix_info.nz_used, 0.0,
1646 "point-block assembly must insert every classified structural entry"));
1647 PetscCall(PicurvAssertRealNear(0.0, (PetscReal)matrix_info.mallocs, 0.0,
1648 "point-block insertion must not reallocate matrix storage"));
1649 if (require_offrank_periodic && comm_size > 1)
1650 PetscCall(PicurvAssertBool((PetscBool)(offrank_periodic_global > 0),
1651 "MPI periodic fixture must exercise off-rank preallocation"));
1652 PetscFunctionReturn(PETSC_SUCCESS);
1653}
@ MOM_NK_ROW_PERIODIC_DUPLICATE
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 1659 of file test_momentum_newton_krylov.c.

1660{
1661 SimCtx *simCtx = NULL;
1662 UserCtx *user = NULL;
1663 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1665 MomentumPreconditionerDescription description = {
1668 0,
1669 0
1670 };
1671 MomentumPreconditionerEngine engine = {0};
1672 Mat preconditioning_matrix = NULL;
1673 Vec x = NULL, f = NULL;
1674 PetscInt block_size = 0, velocity_dof = 0;
1675 PetscReal matrix_norm = 0.0, reassembled_norm = 0.0, difference_norm = 0.0;
1676 PetscScalar reference[9], values[9], mutant[9], legacy[9];
1677 const PetscInt target_i = 1, target_j = 2, target_k = 3;
1678 MatStencil target[3] = {
1679 {.i = 1, .j = 2, .k = 3, .c = 0},
1680 {.i = 1, .j = 2, .k = 3, .c = 1},
1681 {.i = 1, .j = 2, .k = 3, .c = 2}
1682 };
1683 MatStencil conditioned = {.i = 0, .j = 2, .k = 3, .c = 0};
1684 MatStencil homogeneous = {.i = 0, .j = 2, .k = 3, .c = 1};
1685 Mat saved_matrix = NULL, mffd = NULL;
1686 SNES snes = NULL;
1687 Vec direction = NULL, product = NULL, px = NULL;
1688 PetscScalar px_sum = 0.0;
1689 PetscReal px_norm = 0.0;
1690 KSP ksp = NULL;
1691 PC pc = NULL;
1692 Vec pc_rhs = NULL, pc_solution = NULL;
1693 MatInfo initial_allocation_info, repeated_allocation_info;
1694
1695 PetscFunctionBeginUser;
1696 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1697 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1698 PetscCall(VecDuplicate(user->Ucont, &x));
1699 PetscCall(VecDuplicate(user->Ucont, &f));
1700 PetscCall(VecCopy(user->Ucont, x));
1701 ctx.user = user;
1702 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
1703 PetscCall(MomentumPreconditionerEngine_Create(user, NULL, &description, &engine));
1704 preconditioning_matrix = engine.preconditioning_matrix;
1705 PetscCall(PicurvAssertBool((PetscBool)(engine.model_ops == &frozen_momentum_point_block_ops),
1706 "engine must select the frozen-momentum model callbacks"));
1708 "point-block engine must own its separate matrix"));
1709 PetscCall(PicurvAssertBool((PetscBool)!engine.aliases_jacobian_operator,
1710 "point-block matrix must not alias the Jacobian operator"));
1711 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1713 user, preconditioning_matrix, PETSC_FALSE));
1714 PetscCall(MatGetInfo(preconditioning_matrix, MAT_GLOBAL_SUM,
1715 &initial_allocation_info));
1716 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &matrix_norm));
1717 PetscCall(PicurvAssertBool((PetscBool)(matrix_norm > 0.0),
1718 "model callback and common rows must insert matrix entries"));
1719 PetscCall(MatGetBlockSize(preconditioning_matrix, &block_size));
1720 PetscCall(PicurvAssertIntEqual(3, block_size, "point-block matrix block size"));
1721 PetscCall(DMDAGetInfo(user->fda, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1722 &velocity_dof, NULL, NULL, NULL, NULL, NULL));
1723 PetscCall(PicurvAssertIntEqual(3, velocity_dof, "Newton velocity DMDA dof"));
1724
1725 /* Cartesian limit: independently evaluate and compare every entry. */
1726 PetscCall(CollectiveLegacyPointBlockOracle(user, target_i, target_j, target_k, 0, reference));
1727 for (PetscInt rr = 0; rr < 3; ++rr)
1728 for (PetscInt cc = 0; cc < 3; ++cc) {
1729 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1730 target[rr], target[cc], &values[3 * rr + cc]));
1731 PetscCall(PicurvAssertRealNear(PetscRealPart(reference[3 * rr + cc]),
1732 PetscRealPart(values[3 * rr + cc]), 1e-13,
1733 "Cartesian point block entry must match independent oracle"));
1734 }
1735
1736 /* Both fixed categories are exact identity rows, with no same-cell coupling. */
1737 for (PetscInt cc = 0; cc < 3; ++cc) {
1738 MatStencil col = conditioned; col.c = cc;
1739 PetscScalar conditioned_value = 0.0, homogeneous_value = 0.0;
1740 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1741 conditioned, col, &conditioned_value));
1742 col = homogeneous; col.c = cc;
1743 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1744 homogeneous, col, &homogeneous_value));
1745 PetscCall(PicurvAssertRealNear(cc == conditioned.c ? 1.0 : 0.0,
1746 PetscRealPart(conditioned_value), 1e-14, "conditioned row must be exact identity"));
1747 PetscCall(PicurvAssertRealNear(cc == homogeneous.c ? 1.0 : 0.0,
1748 PetscRealPart(homogeneous_value), 1e-14, "homogeneous row must be exact identity"));
1749 }
1750
1751 /* A real residual and MFFD product cannot change subsequent assembly. */
1752 PetscCall(MatDuplicate(preconditioning_matrix, MAT_COPY_VALUES, &saved_matrix));
1753 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1754 PetscCall(SNESSetDM(snes, user->fda));
1755 PetscCall(SNESSetFunction(snes, f, MomentumNewtonKrylov_FormResidual, &ctx));
1756 PetscCall(MatCreateSNESMF(snes, &mffd));
1757 PetscCall(VecDuplicate(x, &direction)); PetscCall(VecDuplicate(x, &product));
1758 PetscCall(VecSet(direction, 0.375));
1759 PetscCall(MomentumNewtonKrylov_FormResidual(snes, x, f, &ctx));
1760 PetscCall(MatMFFDSetBase(mffd, x, f));
1761 PetscCall(MatMult(mffd, direction, product));
1762 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1763 PetscCall(MatAXPY(saved_matrix, -1.0, preconditioning_matrix, SAME_NONZERO_PATTERN));
1764 PetscCall(MatNorm(saved_matrix, NORM_FROBENIUS, &difference_norm));
1765 PetscCall(PicurvAssertRealNear(0.0, difference_norm, 1e-12,
1766 "assembly must be unchanged after residual and MFFD products"));
1767 PetscCall(VecDestroy(&product)); PetscCall(VecDestroy(&direction));
1768 PetscCall(MatDestroy(&mffd)); PetscCall(SNESDestroy(&snes)); PetscCall(MatDestroy(&saved_matrix));
1769
1770 /* Nonuniform oracle: i/j/k, every component, and all samples are distinct. */
1771 PetscCall(SeedPointBlockOracleFields(user));
1772 PetscCall(VecCopy(user->Ucont, x));
1773 simCtx->step = 1;
1774 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1775 PetscCall(CollectiveLegacyPointBlockOracle(user, target_i, target_j, target_k, 0, reference));
1776 for (PetscInt rr = 0; rr < 3; ++rr)
1777 for (PetscInt cc = 0; cc < 3; ++cc) {
1778 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1779 target[rr], target[cc], &values[3 * rr + cc]));
1780 PetscCall(PicurvAssertRealNear(PetscRealPart(reference[3 * rr + cc]),
1781 PetscRealPart(values[3 * rr + cc]), 1e-13,
1782 "nonuniform point block entry must match independent oracle"));
1783 }
1784 PetscCall(PicurvAssertBool((PetscBool)(PetscAbsScalar(reference[1] - reference[3]) > 1e-6 &&
1785 PetscAbsScalar(reference[2] - reference[6]) > 1e-6 &&
1786 PetscAbsScalar(reference[5] - reference[7]) > 1e-6),
1787 "oracle must preserve nonsymmetric component ordering"));
1788 for (PetscInt mutant_flag = ORACLE_CENTER_AJ; mutant_flag <= ORACLE_LEGACY_SIGN;
1789 mutant_flag <<= 1) {
1790 PetscBool differs = PETSC_FALSE;
1791 PetscCall(CollectiveLegacyPointBlockOracle(user, target_i, target_j, target_k,
1792 mutant_flag, mutant));
1793 for (PetscInt n = 0; n < 9; ++n)
1794 if (PetscAbsScalar(reference[n] - mutant[n]) > 1e-8) differs = PETSC_TRUE;
1795 PetscCall(PicurvAssertBool(differs, "independent oracle must reject audited mutant"));
1796 }
1797 PetscCall(CollectiveLegacyPointBlockOracle(user, target_i, target_j, target_k,
1798 ORACLE_OMIT_A5, mutant));
1799 PetscCall(PicurvAssertBool((PetscBool)(PetscAbsScalar(reference[8] - mutant[8]) > 1e-8),
1800 "A[5] must contribute to the zeta diagonal"));
1801 PetscCall(CollectiveLegacyPointBlockOracle(user, target_i, target_j, target_k,
1802 ORACLE_LEGACY_SIGN, legacy));
1803 for (PetscInt n = 0; n < 9; ++n)
1804 PetscCall(PicurvAssertRealNear(PetscRealPart(reference[n]), -PetscRealPart(legacy[n]),
1805 1e-13, "modern block must be the negative legacy block"));
1806
1807 /* A coordinate permutation cannot accidentally address the intended block. */
1808 PetscCall(CollectiveLegacyPointBlockOracle(user, target_k, target_j, target_i, 0, mutant));
1809 {
1810 PetscBool differs = PETSC_FALSE;
1811 for (PetscInt n = 0; n < 9; ++n)
1812 if (PetscAbsScalar(reference[n] - mutant[n]) > 1e-8) differs = PETSC_TRUE;
1813 PetscCall(PicurvAssertBool(differs, "permuted MatStencil coordinates must be detectable"));
1814 }
1815 {
1816 MatStencil neighbor = target[0];
1817 PetscScalar neighbor_value = 0.0;
1818 neighbor.i++;
1819 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1820 target[0], neighbor, &neighbor_value));
1821 PetscCall(PicurvAssertRealNear(0.0, PetscRealPart(neighbor_value), 1e-14,
1822 "point block must not insert unintended neighbor entries"));
1823 }
1824
1825 /* The shared time coefficient supplies BDF1 and BDF2 diagonals only. */
1826 simCtx->step = 2;
1827 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1828 PetscCall(CollectiveLegacyPointBlockOracle(user, target_i, target_j, target_k, 0, mutant));
1829 for (PetscInt n = 0; n < 9; ++n) {
1830 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1831 target[n / 3], target[n % 3], &values[n]));
1832 PetscCall(PicurvAssertRealNear(PetscRealPart(mutant[n]), PetscRealPart(values[n]),
1833 1e-13, "BDF2 point block must match independent oracle"));
1834 if (n == 0 || n == 4 || n == 8)
1835 PetscCall(PicurvAssertRealNear(0.5 / simCtx->dt,
1836 PetscRealPart(mutant[n] - reference[n]), 1e-10, "BDF2 diagonal increment"));
1837 else
1838 PetscCall(PicurvAssertRealNear(0.0, PetscRealPart(mutant[n] - reference[n]),
1839 1e-13, "BDF order must not change off-diagonal entries"));
1840 }
1841
1842 PetscCall(VecDuplicate(x, &px));
1843 PetscCall(MatMult(preconditioning_matrix, x, px));
1844 PetscCall(VecSum(px, &px_sum)); PetscCall(VecNorm(px, NORM_2, &px_norm));
1845 PetscCall(PicurvAssertRealNear(8.40570474622236e4, PetscRealPart(px_sum), 5e-9,
1846 "P*x global sum must be decomposition independent"));
1847 PetscCall(PicurvAssertRealNear(9.05678688399519e3, px_norm, 5e-10,
1848 "P*x norm must be decomposition independent"));
1849 PetscCall(PetscPrintf(PETSC_COMM_WORLD,
1850 "POINT_BLOCK_MPI_SIGNATURE sum=%.16e norm2=%.16e\n",
1851 (double)PetscRealPart(px_sum), (double)px_norm));
1852 PetscCall(VecDestroy(&px));
1853
1854 {
1855 PetscBool assembled = PETSC_FALSE;
1856 PetscCall(MatAssembled(preconditioning_matrix, &assembled));
1857 PetscCall(PicurvAssertBool(assembled, "engine must perform final matrix assembly"));
1858 }
1859 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &matrix_norm));
1860 PetscCall(MatShift(preconditioning_matrix, 7.0));
1861 PetscCall(MomentumPreconditionerEngine_Assemble(&engine, user, x));
1862 PetscCall(MatGetInfo(preconditioning_matrix, MAT_GLOBAL_SUM,
1863 &repeated_allocation_info));
1864 PetscCall(PicurvAssertRealNear((PetscReal)initial_allocation_info.nz_allocated,
1865 (PetscReal)repeated_allocation_info.nz_allocated, 0.0,
1866 "repeated assembly must retain exact allocated storage"));
1867 PetscCall(PicurvAssertRealNear((PetscReal)initial_allocation_info.mallocs,
1868 (PetscReal)repeated_allocation_info.mallocs, 0.0,
1869 "repeated assembly must not add insertion reallocations"));
1870 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &reassembled_norm));
1871 PetscCall(PicurvAssertRealNear(matrix_norm, reassembled_norm, 1e-12,
1872 "repeated engine assembly must clear old entries"));
1873 {
1874 PetscScalar value = 0.0;
1875 PetscCall(PreconditionerMatrixStencilEntry(user, preconditioning_matrix,
1876 conditioned, conditioned, &value));
1877 PetscCall(PicurvAssertRealNear(1.0, PetscRealPart(value), 1e-14,
1878 "repeated engine assembly must clear old entries"));
1879 }
1880 PetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
1881 PetscCall(KSPSetOperators(ksp, preconditioning_matrix, preconditioning_matrix));
1882 PetscCall(KSPGetPC(ksp, &pc));
1883 PetscCall(MomentumPreconditionerEngine_ConfigurePetscPC(&engine, pc));
1884 PetscCall(MomentumPreconditionerEngine_ValidatePetscPC(&engine, pc));
1885 PetscCall(KSPSetUp(ksp));
1886 PetscCall(DMCreateGlobalVector(user->fda, &pc_rhs));
1887 PetscCall(DMCreateGlobalVector(user->fda, &pc_solution));
1888 PetscCall(VecSet(pc_rhs, 1.0));
1889 PetscCall(PCApply(pc, pc_rhs, pc_solution));
1890 PetscCall(VecDestroy(&pc_solution)); PetscCall(VecDestroy(&pc_rhs));
1891 PetscCall(KSPDestroy(&ksp));
1892 PetscCall(MomentumPreconditionerEngine_Destroy(&engine));
1893 PetscCall(PicurvAssertBool((PetscBool)(engine.preconditioning_matrix == NULL),
1894 "engine destroy must clear its owned matrix"));
1895 PetscCall(VecDestroy(&f)); PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->Rhs));
1896 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
1897 PetscFunctionReturn(PETSC_SUCCESS);
1898}
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 CollectiveLegacyPointBlockOracle(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt flags, PetscScalar block[9])
Evaluates the independent oracle on the unique owner and broadcasts it.
static PetscErrorCode SeedPointBlockOracleFields(UserCtx *user)
Seeds nonuniform, index-distinguishing coefficient fields.
static PetscErrorCode AssertExactPointBlockMatrixAllocation(UserCtx *user, Mat matrix, PetscBool require_offrank_periodic)
Verifies the exact AIJ layout and preallocation derived from row classes.
PetscReal dt
Definition variables.h:699
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 1901 of file test_momentum_newton_krylov.c.

1903{
1904 AO ao = NULL;
1905 PetscInt ri, rj, rk, global_row, global_rep, lo, hi, local_checked = 0, checked = 0;
1906 PetscInt ncols = 0;
1907 const PetscInt *cols = NULL;
1908 const PetscScalar *values = NULL;
1910
1911 PetscFunctionBeginUser;
1912 type = MomentumNewtonKrylov_ClassifyRow(user, row.i, row.j, row.k, row.c, &ri, &rj, &rk);
1913 PetscCall(PicurvAssertIntEqual(MOM_NK_ROW_PERIODIC_DUPLICATE, type, message));
1914 global_row = row.c + 3 * (row.i + user->info.mx * (row.j + user->info.my * row.k));
1915 PetscCall(DMDAGetAO(user->fda, &ao));
1916 PetscCall(AOApplicationToPetsc(ao, 1, &global_row));
1917 PetscCall(MatGetOwnershipRange(matrix, &lo, &hi));
1918 if (global_row >= lo && global_row < hi) {
1919 PetscBool found_self = PETSC_FALSE, found_rep = PETSC_FALSE;
1920 PetscInt nonzero_entries = 0;
1921 PetscCall(TestStencilToGlobal(user,
1922 (MatStencil){.i = ri, .j = rj, .k = rk, .c = row.c}, &global_rep));
1923 PetscCall(MatGetRow(matrix, global_row, &ncols, &cols, &values));
1924 for (PetscInt n = 0; n < ncols; ++n) {
1925 if (PetscAbsScalar(values[n]) <= 1e-14) continue;
1926 nonzero_entries++;
1927 if (cols[n] == global_row) {
1928 found_self = PETSC_TRUE;
1929 PetscCall(PicurvAssertRealNear(1.0, PetscRealPart(values[n]), 1e-14,
1930 "periodic row self entry"));
1931 } else if (cols[n] == global_rep) {
1932 found_rep = PETSC_TRUE;
1933 PetscCall(PicurvAssertRealNear(-1.0, PetscRealPart(values[n]), 1e-14,
1934 "periodic row representative entry"));
1935 } else {
1936 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB,
1937 "Periodic row contains an unintended nonzero column.");
1938 }
1939 }
1940 PetscCall(PicurvAssertIntEqual(2, nonzero_entries,
1941 "periodic row must contain exactly two numerical entries"));
1942 PetscCall(PicurvAssertBool((PetscBool)(found_self && found_rep), message));
1943 PetscCall(MatRestoreRow(matrix, global_row, &ncols, &cols, &values));
1944 local_checked = 1;
1945 }
1946 PetscCallMPI(MPI_Allreduce(&local_checked, &checked, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
1947 PetscCall(PicurvAssertIntEqual(1, checked, "periodic row must have exactly one matrix owner"));
1948 PetscFunctionReturn(PETSC_SUCCESS);
1949}
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 1952 of file test_momentum_newton_krylov.c.

1953{
1954 SimCtx *simCtx = NULL;
1955 UserCtx *user = NULL;
1956 char tmpdir[PETSC_MAX_PATH_LEN] = "";
1958 MomentumPreconditionerDescription description = {
1960 };
1961 SNES snes = NULL;
1962 Mat jacobian_operator = NULL, preconditioning_matrix = NULL;
1963 KSP ksp = NULL;
1964 PC pc = NULL;
1965 PetscInt rows = 0, cols = 0;
1966 const char *jacobian_prefix = NULL;
1967 const char *pc_type = NULL;
1968 PetscBool prefix_matches = PETSC_FALSE;
1969 PetscBool pc_is_none = PETSC_FALSE;
1970
1971 PetscFunctionBeginUser;
1972 PetscCall(BuildNewtonFixture(fixed_wall_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
1973 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1974 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1975 PetscCall(SNESSetDM(snes, user->fda));
1976 ctx.user = user;
1979 PetscCall(SNESSetFunction(snes, NULL, MomentumNewtonKrylov_FormResidual, &ctx));
1980 PetscCall(MomentumNewtonJacobian_Create(snes, &ctx.jacobian));
1981 PetscCall(MatGetOptionsPrefix(ctx.jacobian.jacobian_operator, &jacobian_prefix));
1982 PetscCall(PetscStrcmp(jacobian_prefix, "mom_nk_", &prefix_matches));
1983 PetscCall(PicurvAssertBool(prefix_matches,
1984 "Jacobian interface must apply the application prefix"));
1986 user, ctx.jacobian.jacobian_operator, &description, &ctx.preconditioning_engine));
1988 snes, &ctx.jacobian, &ctx.preconditioning_engine, &ctx));
1989 PetscCall(SNESGetKSP(snes, &ksp));
1990 PetscCall(KSPGetPC(ksp, &pc));
1992 &ctx.preconditioning_engine, pc));
1994 &ctx.preconditioning_engine, pc));
1995 PetscCall(PCGetType(pc, &pc_type));
1996 PetscCall(PetscStrcmp(pc_type, PCNONE, &pc_is_none));
1997 PetscCall(PicurvAssertBool(pc_is_none,
1998 "baseline engine must derive PETSc PCNONE"));
1999 PetscCall(SNESGetJacobian(snes, &jacobian_operator, &preconditioning_matrix, NULL, NULL));
2000 PetscCall(PicurvAssertBool(
2001 (PetscBool)(jacobian_operator == ctx.jacobian.jacobian_operator),
2002 "SNES must receive the Jacobian-interface MFFD operator"));
2003 PetscCall(PicurvAssertBool((PetscBool)(preconditioning_matrix == jacobian_operator),
2004 "baseline preconditioning matrix must alias the Jacobian"));
2006 "baseline engine must record matrix aliasing"));
2008 "baseline engine must not own the Jacobian alias"));
2010 PetscCall(MatGetSize(ctx.jacobian.jacobian_operator, &rows, &cols));
2011 PetscCall(PicurvAssertBool((PetscBool)(rows > 0 && cols > 0),
2012 "destroying an alias engine must preserve the Jacobian"));
2014 PetscCall(SNESDestroy(&snes));
2015 PetscCall(VecDestroy(&user->Rhs));
2016 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
2017 PetscFunctionReturn(PETSC_SUCCESS);
2018}
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 2021 of file test_momentum_newton_krylov.c.

2022{
2023 SimCtx *simCtx = NULL;
2024 UserCtx *user = NULL;
2025 char tmpdir[PETSC_MAX_PATH_LEN] = "";
2027 MomentumPreconditionerDescription description = {
2030 0,
2031 0
2032 };
2033 Vec x = NULL, f = NULL;
2034 PetscReal matrix_norm = 0.0;
2035
2036 PetscFunctionBeginUser;
2037 PetscCall(BuildNewtonFixture(periodic_xyz_bcs, &simCtx, &user, tmpdir, sizeof(tmpdir)));
2038 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
2039 PetscCall(VecDuplicate(user->Ucont, &x));
2040 PetscCall(VecDuplicate(user->Ucont, &f));
2041 PetscCall(VecCopy(user->Ucont, x));
2042 ctx.user = user;
2043 PetscCall(MomentumNewtonKrylov_FormResidual(NULL, x, f, &ctx));
2045 user, NULL, &description, &ctx.preconditioning_engine));
2048 user, ctx.preconditioning_engine.preconditioning_matrix, PETSC_TRUE));
2049 PetscCall(MatNorm(ctx.preconditioning_engine.preconditioning_matrix,
2050 NORM_FROBENIUS, &matrix_norm));
2051 PetscCall(PicurvAssertBool((PetscBool)(matrix_norm > 0.0),
2052 "periodic point-block assembly must produce a nonzero matrix"));
2053 PetscCall(AssertPeriodicPreconditionerRow(user,
2055 (MatStencil){.i = 0, .j = 2, .k = 3, .c = 0},
2056 "single-axis periodic row must contain exact +1/-1 entries"));
2057 PetscCall(AssertPeriodicPreconditionerRow(user,
2059 (MatStencil){.i = 0, .j = 0, .k = 3, .c = 1},
2060 "periodic intersection must contain exact +1/-1 entries"));
2061 PetscCall(AssertPeriodicPreconditionerRow(user,
2063 (MatStencil){.i = 0, .j = 0, .k = 0, .c = 2},
2064 "periodic origin intersection must contain exact +1/-1 entries"));
2066 PetscCall(VecDestroy(&f));
2067 PetscCall(VecDestroy(&x));
2068 PetscCall(VecDestroy(&user->Rhs));
2069 PetscCall(DestroyNewtonFixture(&simCtx, tmpdir));
2070 PetscFunctionReturn(PETSC_SUCCESS);
2071}
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 2079 of file test_momentum_newton_krylov.c.

2080{
2081 PetscErrorCode ierr;
2082 const PicurvTestCase cases[] = {
2083 {"residual-repeatability-and-input-integrity", TestResidualRepeatabilityAndInputIntegrity},
2084 {"constraint-rows", TestConstraintRows},
2085 {"fixed-constraint-derivatives-all-faces", TestFixedConstraintDerivativesAllFaces},
2086 {"inlet-outlet-constraint-derivatives", TestInletOutletConstraintDerivatives},
2087 {"periodic-constraint-derivatives-and-intersections", TestPeriodicConstraintDerivativesAndIntersections},
2088 {"matrix-free-derivative", TestMatrixFreeDerivative},
2089 {"whole-operator-direct-jacobian", TestWholeOperatorDirectJacobian},
2090 {"periodic-operator-has-no-zero-rows", TestPeriodicOperatorHasNoZeroRows},
2091 {"zero-iteration-structured-logging", TestZeroIterationStructuredLogging},
2092 {"flat-channel-bdf1-startup", TestFlatChannelStartup},
2093 {"restart-and-continuation-solve", TestRestartAndContinuationSolve},
2094 {"small-solve-and-rollback", TestSmallSolveAndRollback},
2095 {"unsupported-configuration-fails-before-allocation", TestUnsupportedConfigurationFailsBeforeAllocation},
2096 {"post-allocation-failure-cleanup", TestPostAllocationFailureCleanup},
2097 {"linearization-config-parsing", TestLinearizationConfigParsing},
2098 {"point-block-preconditioner-engine", TestPointBlockPreconditionerEngine},
2099 {"jacobian-interface-and-baseline-alias", TestJacobianInterfaceAndBaselineAlias},
2100 {"point-block-periodic-assembly", TestPointBlockPeriodicAssembly},
2101 };
2102
2103 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv Newton Krylov tests");
2104 if (ierr) return (int)ierr;
2105 ierr = PicurvRunTests("unit-newton-krylov", cases, sizeof(cases) / sizeof(cases[0]));
2106 if (PetscFinalize()) return 1;
2107 return (int)ierr;
2108}
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.