14#define MomentumSolver_NewtonKrylov MomentumSolver_NewtonKrylov_PrivateCopy
15#define MomentumSolver_NewtonKrylov_Destroy MomentumSolver_NewtonKrylov_Destroy_PrivateCopy
16#include "../../src/momentum_newton_krylov.c"
17#undef MomentumSolver_NewtonKrylov
18#undef MomentumSolver_NewtonKrylov_Destroy
21 "-Xi PERIODIC geometric\n"
22 "+Xi PERIODIC geometric\n"
25 "-Zeta INLET prescribed_velocity source_id=0\n"
26 "+Zeta OUTLET conservation\n";
31 "-Xi WALL free_slip\n"
32 "+Xi WALL free_slip\n"
36 "+Zeta WALL noslip\n";
44 "+Zeta WALL noslip\n";
51 "-Zeta INLET prescribed_velocity source_id=1\n"
52 "+Zeta OUTLET conservation\n";
55 "-Xi PERIODIC geometric\n+Xi PERIODIC geometric\n"
56 "-Eta WALL noslip\n+Eta WALL noslip\n-Zeta WALL noslip\n+Zeta WALL noslip\n";
59 "-Xi WALL noslip\n+Xi WALL noslip\n"
60 "-Eta PERIODIC geometric\n+Eta PERIODIC geometric\n-Zeta WALL noslip\n+Zeta WALL noslip\n";
63 "-Xi WALL noslip\n+Xi WALL noslip\n-Eta WALL noslip\n+Eta WALL noslip\n"
64 "-Zeta PERIODIC geometric\n+Zeta PERIODIC geometric\n";
67 "-Xi PERIODIC geometric\n+Xi PERIODIC geometric\n"
68 "-Eta PERIODIC geometric\n+Eta PERIODIC geometric\n"
69 "-Zeta WALL noslip\n+Zeta WALL noslip\n";
72 "-Xi PERIODIC geometric\n+Xi PERIODIC geometric\n"
73 "-Eta PERIODIC geometric\n+Eta PERIODIC geometric\n"
74 "-Zeta PERIODIC geometric\n+Zeta PERIODIC geometric\n";
78 const char *needle_a,
const char *needle_b)
83 PetscBool found_a = needle_a ? PETSC_FALSE : PETSC_TRUE;
84 PetscBool found_b = needle_b ? PETSC_FALSE : PETSC_TRUE;
86 PetscFunctionBeginUser;
87 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
88 file = fopen(path,
"r");
89 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
90 "Expected Newton log does not exist: %s", path);
91 while (fgets(line,
sizeof(line), file)) {
92 if (line[0] >=
'0' && line[0] <=
'9') rows++;
93 if (needle_a && strstr(line, needle_a)) found_a = PETSC_TRUE;
94 if (needle_b && strstr(line, needle_b)) found_b = PETSC_TRUE;
97 if (expected_rows >= 0) {
99 "Newton log must contain one nonduplicated row per solve"));
102 "enabled Newton history must contain the expected iteration rows"));
104 if (!found_a || !found_b) {
105 PetscCall(PetscPrintf(PETSC_COMM_SELF,
"%s lacks '%s'\n", path, !found_a ? needle_a : needle_b));
107 PetscCall(
PicurvAssertBool(found_a,
"Newton log is missing required structured content"));
108 PetscCall(
PicurvAssertBool(found_b,
"Newton log is missing required structured content"));
109 PetscFunctionReturn(PETSC_SUCCESS);
122 char *tmpdir,
size_t tmpdir_len)
124 PetscFunctionBeginUser;
129 "Newton fixture must enter with no persistent Rhs workspace"));
130 PetscFunctionReturn(PETSC_SUCCESS);
141 PetscFunctionBeginUser;
144 PetscFunctionReturn(PETSC_SUCCESS);
156 PetscFunctionBeginUser;
157 fd = fopen(path,
"w");
158 PetscCheck(fd != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
159 "Could not create Newton test PICSLICE %s.", path);
160 PetscCheck(fprintf(fd,
"PICSLICE\n1\n5 5\n") >= 0,
161 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
"Could not write PICSLICE header.");
162 for (PetscInt row = 0; row < 25; ++row) {
163 PetscCheck(fprintf(fd,
"%.16e\n", 1.0 + 0.01 * (
double)row) >= 0,
164 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
"Could not write PICSLICE value.");
167 PetscFunctionReturn(PETSC_SUCCESS);
180 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
181 Vec x = NULL, x_copy = NULL, f1 = NULL, f2 = NULL, delta = NULL;
182 PetscReal norm = 0.0;
185 PetscFunctionBeginUser;
187 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
188 PetscCall(VecDuplicate(user->
Ucont, &x));
189 PetscCall(VecDuplicate(user->
Ucont, &x_copy));
190 PetscCall(VecDuplicate(user->
Ucont, &f1));
191 PetscCall(VecDuplicate(user->
Ucont, &f2));
192 PetscCall(VecDuplicate(user->
Ucont, &delta));
193 PetscCall(VecCopy(user->
Ucont, x));
194 PetscCall(VecShift(x, 0.125));
195 PetscCall(VecCopy(x, x_copy));
210 PetscCall(VecSet(user->
Ucat, 7.0));
211 PetscCall(VecSet(user->
lUcat, 7.0));
213 PetscCall(VecWAXPY(delta, -1.0, f1, f2));
214 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
215 PetscCheck(norm <= 1.0e-13, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
216 "%s residual changed between identical evaluations (inf norm=%g).", label, (
double)norm);
217 PetscCall(VecNorm(delta, NORM_2, &norm));
218 PetscCheck(norm <= 1.0e-12, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
219 "%s residual changed between identical evaluations (L2 norm=%g).", label, (
double)norm);
220 PetscCall(VecWAXPY(delta, -1.0, x_copy, x));
221 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
222 PetscCheck(norm == 0.0, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
223 "%s residual callback modified X (norm=%g).", label, (
double)norm);
225 PetscCall(VecDestroy(&delta));
226 PetscCall(VecDestroy(&f2));
227 PetscCall(VecDestroy(&f1));
228 PetscCall(VecDestroy(&x_copy));
229 PetscCall(VecDestroy(&x));
230 PetscCall(VecDestroy(&user->
Rhs));
232 PetscFunctionReturn(PETSC_SUCCESS);
238 return (PetscBool)(i >= user->
info.xs && i < user->
info.xs + user->
info.xm &&
239 j >= user->
info.ys && j < user->
info.ys + user->
info.ym &&
240 k >= user->
info.zs && k < user->
info.zs + user->
info.zm);
255 PetscInt k, PetscInt component, PetscScalar delta)
259 PetscFunctionBeginUser;
261 PetscCall(DMDAVecGetArray(user->
fda, vec, &a));
262 if (component == 0) a[k][j][i].
x += delta;
263 else if (component == 1) a[k][j][i].
y += delta;
264 else a[k][j][i].
z += delta;
265 PetscCall(DMDAVecRestoreArray(user->
fda, vec, &a));
267 PetscFunctionReturn(PETSC_SUCCESS);
282 PetscInt k, PetscInt component, PetscScalar *value)
285 PetscScalar local = 0.0;
287 PetscFunctionBeginUser;
289 PetscCall(DMDAVecGetArrayRead(user->
fda, vec, &a));
290 local = component == 0 ? a[k][j][i].
x : (component == 1 ? a[k][j][i].
y : a[k][j][i].
z);
291 PetscCall(DMDAVecRestoreArrayRead(user->
fda, vec, &a));
293 PetscCallMPI(MPI_Allreduce(&local, value, 1, MPIU_SCALAR, MPIU_SUM, PETSC_COMM_WORLD));
294 PetscFunctionReturn(PETSC_SUCCESS);
313 PetscInt row_i, PetscInt row_j, PetscInt row_k,
314 PetscInt row_component, PetscInt col_i, PetscInt col_j,
315 PetscInt col_k, PetscInt col_component,
316 PetscReal *derivative)
318 const PetscReal epsilon = 1.0e-6;
319 Vec f0 = NULL, fp = NULL, xp = NULL;
320 PetscScalar base_value = 0.0, perturbed_value = 0.0;
323 PetscFunctionBeginUser;
324 PetscCall(VecDuplicate(x, &f0));
325 PetscCall(VecDuplicate(x, &fp));
326 PetscCall(VecDuplicate(x, &xp));
329 PetscCall(VecCopy(x, xp));
332 PetscCall(
GetStoredValue(user, f0, row_i, row_j, row_k, row_component, &base_value));
333 PetscCall(
GetStoredValue(user, fp, row_i, row_j, row_k, row_component, &perturbed_value));
334 *derivative = PetscRealPart((perturbed_value - base_value) / epsilon);
335 PetscCall(VecDestroy(&xp));
336 PetscCall(VecDestroy(&fp));
337 PetscCall(VecDestroy(&f0));
338 PetscFunctionReturn(PETSC_SUCCESS);
359 PetscInt row_i, PetscInt row_j, PetscInt row_k,
360 PetscInt row_component, PetscInt col_i, PetscInt col_j,
361 PetscInt col_k, PetscInt col_component,
362 PetscReal expected, PetscReal tolerance,
const char *label)
364 PetscReal derivative = 0.0;
366 PetscFunctionBeginUser;
368 col_i, col_j, col_k, col_component, &derivative));
370 PetscFunctionReturn(PETSC_SUCCESS);
376 char profile_dir[PETSC_MAX_PATH_LEN] =
"";
377 char profile_path[PETSC_MAX_PATH_LEN];
378 char file_bcs[2 * PETSC_MAX_PATH_LEN];
380 PetscFunctionBeginUser;
391 PetscCall(PetscSNPrintf(profile_path,
sizeof(profile_path),
"%s/inlet.picslice", profile_dir));
393 PetscCall(PetscSNPrintf(file_bcs,
sizeof(file_bcs),
394 "-Xi WALL noslip\n+Xi WALL noslip\n-Eta WALL noslip\n+Eta WALL noslip\n"
395 "-Zeta INLET prescribed_velocity source_id=2 source_file=%s\n+Zeta OUTLET conservation\n", profile_path));
398 PetscFunctionReturn(PETSC_SUCCESS);
406 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
407 Vec x = NULL, f = NULL;
408 Cmpnts ***xa = NULL, ***fa = NULL, ***conditioned = NULL, ***rhs = NULL;
411 PetscFunctionBeginUser;
413 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
414 PetscCall(VecDuplicate(user->
Ucont, &x));
415 PetscCall(VecDuplicate(user->
Ucont, &f));
416 PetscCall(VecSet(x, 0.25));
417 PetscCall(DMDAVecGetArray(user->
fda, x, &xa));
418 if (user->
info.xs == 0 && 2 >= user->
info.ys && 2 < user->
info.ys + user->
info.ym &&
419 2 >= user->
info.zs && 2 < user->
info.zs + user->
info.zm) xa[2][2][0].
x = 3.0;
421 2 >= user->
info.ys && 2 < user->
info.ys + user->
info.ym &&
422 2 >= user->
info.zs && 2 < user->
info.zs + user->
info.zm) xa[2][2][user->
info.mx - 2].
x = 1.25;
423 PetscCall(DMDAVecRestoreArray(user->
fda, x, &xa));
427 PetscCall(DMDAVecGetArrayRead(user->
fda, x, &xa));
428 PetscCall(DMDAVecGetArrayRead(user->
fda, f, &fa));
429 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &conditioned));
430 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Rhs, &rhs));
431 if (user->
info.xs == 0 && 2 >= user->
info.ys && 2 < user->
info.ys + user->
info.ym &&
432 2 >= user->
info.zs && 2 < user->
info.zs + user->
info.zm) {
434 "periodic duplicate row must be Xdup-Xrep"));
436 if (user->
info.ys == 0 && 2 >= user->
info.xs && 2 < user->
info.xs + user->
info.xm &&
437 2 >= user->
info.zs && 2 < user->
info.zs + user->
info.zm) {
439 fa[2][0][2].y, 1.0e-12,
440 "fixed wall row must be X minus conditioned boundary value"));
442 if (2 >= user->
info.xs && 2 < user->
info.xs + user->
info.xm &&
443 2 >= user->
info.ys && 2 < user->
info.ys + user->
info.ym &&
444 2 >= user->
info.zs && 2 < user->
info.zs + user->
info.zm) {
446 "unconstrained interior row must retain the physical residual"));
448 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Rhs, &rhs));
449 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &conditioned));
450 PetscCall(DMDAVecRestoreArrayRead(user->
fda, f, &fa));
451 PetscCall(DMDAVecRestoreArrayRead(user->
fda, x, &xa));
453 PetscCall(VecDestroy(&f));
454 PetscCall(VecDestroy(&x));
455 PetscCall(VecDestroy(&user->
Rhs));
457 PetscFunctionReturn(PETSC_SUCCESS);
465 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
467 const PetscInt size[3] = {7, 7, 7};
469 PetscFunctionBeginUser;
471 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
472 PetscCall(VecDuplicate(user->
Ucont, &x));
473 PetscCall(VecSet(x, 0.2));
475 for (PetscInt axis = 0; axis < 3; ++axis) {
476 PetscInt coord[3] = {2, 2, 2}, ri, rj, rk;
477 PetscInt tangent = (axis + 1) % 3;
483 "negative face normal row classification"));
485 coord[0], coord[1], coord[2], axis, 1.0, 1.0e-8,
486 "negative face normal fixed derivative"));
490 "negative face tangential row classification"));
492 coord[0], coord[1], coord[2], tangent, 1.0, 1.0e-8,
493 "negative face tangential homogeneous derivative"));
495 coord[axis] = size[axis] - 2;
498 "positive physical normal row classification"));
500 coord[0], coord[1], coord[2], axis, 1.0, 1.0e-8,
501 "positive physical normal fixed derivative"));
504 "positive physical tangential row classification"));
506 coord[axis] = size[axis] - 1;
507 for (PetscInt component = 0; component < 3; ++component) {
510 "positive dummy row classification"));
512 coord[0], coord[1], coord[2], component, 1.0, 1.0e-8,
513 "positive dummy homogeneous derivative"));
517 PetscCall(VecDestroy(&x));
518 PetscCall(VecDestroy(&user->
Rhs));
520 PetscFunctionReturn(PETSC_SUCCESS);
528 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
531 PetscFunctionBeginUser;
533 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
534 PetscCall(VecDuplicate(user->
Ucont, &x));
535 PetscCall(VecCopy(user->
Ucont, x));
536 PetscCall(VecShift(x, 0.1));
537 PetscCall(
CheckStoredDerivative(user, x, 2, 2, 0, 2, 2, 2, 0, 2,
538 1.0, 1.0e-8,
"constant inlet fixed derivative"));
553 PetscReal d0 = 0.0, d1 = 0.0;
555 2, 2, user->
info.mz - 2, 2, &d0));
557 2, 2, user->
info.mz - 2, 2, &d1));
559 "conservation outlet self derivative must be deterministic"));
560 PetscCheck(d0 > 1.0e-3 && d0 < 1.0 - 1.0e-3, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
561 "conservation outlet self derivative must reflect X-coupling (0<d<1), got %g.",
564 PetscCall(VecDestroy(&x));
565 PetscCall(VecDestroy(&user->
Rhs));
567 PetscFunctionReturn(PETSC_SUCCESS);
580 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
582 PetscInt size[3], dup[3] = {2, 2, 2}, rep[3] = {2, 2, 2}, unrelated[3] = {3, 3, 3};
584 PetscFunctionBeginUser;
586 size[0] = user->
info.mx; size[1] = user->
info.my; size[2] = user->
info.mz;
587 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
588 PetscCall(VecDuplicate(user->
Ucont, &x));
589 PetscCall(VecSet(x, 0.15));
590 for (PetscInt side = 0; side < 2; ++side) {
591 dup[axis] = side == 0 ? 0 : size[axis] - 1;
592 rep[axis] = side == 0 ? size[axis] - 2 : 1;
593 for (PetscInt component = 0; component < 3; ++component) {
595 dup[0], dup[1], dup[2], component, 1.0, 1.0e-8,
596 "periodic duplicate self derivative"));
598 rep[0], rep[1], rep[2], component, -1.0, 1.0e-8,
599 "periodic representative derivative"));
601 unrelated[0], unrelated[1], unrelated[2], (component + 1) % 3, 0.0, 1.0e-8,
602 "periodic constraint unrelated derivative"));
605 PetscCall(VecDestroy(&x));
606 PetscCall(VecDestroy(&user->
Rhs));
608 PetscFunctionReturn(PETSC_SUCCESS);
616 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
617 Vec x = NULL, f = NULL;
619 PetscScalar xdup, synced, residual;
621 PetscFunctionBeginUser;
627 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
628 PetscCall(VecDuplicate(user->
Ucont, &x));
629 PetscCall(VecDuplicate(user->
Ucont, &f));
630 PetscCall(VecSet(x, 0.0));
633 PetscCall(VecCopy(x, user->
Ucont));
641 "doubly periodic edge must use production synchronized representative"));
642 PetscCall(
CheckStoredDerivative(user, x, 0, 0, 2, 0, 0, 0, 2, 0, 1.0, 1.0e-8,
643 "doubly periodic edge self derivative"));
645 user->
info.mx - 2, user->
info.my - 2, 2, 0, -1.0, 1.0e-8,
646 "doubly periodic edge representative derivative"));
647 PetscCall(VecDestroy(&f)); PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->
Rhs));
652 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
653 PetscCall(VecDuplicate(user->
Ucont, &x)); PetscCall(VecSet(x, 0.1));
654 PetscCall(
CheckStoredDerivative(user, x, 0, 0, 0, 2, 0, 0, 0, 2, 1.0, 1.0e-8,
655 "fully periodic corner self derivative"));
657 user->
info.mx - 2, user->
info.my - 2, user->
info.mz - 2, 2,
658 -1.0, 1.0e-8,
"fully periodic corner representative derivative"));
659 PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->
Rhs));
664 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs)); PetscCall(VecDuplicate(user->
Ucont, &x));
665 PetscCall(VecSet(x, 0.1));
666 PetscCall(
CheckStoredDerivative(user, x, 0, 0, 2, 1, 0, 0, 2, 1, 1.0, 1.0e-8,
667 "periodic-wall intersection self derivative"));
669 user->
info.mx - 2, 0, 2, 1, -1.0, 1.0e-8,
670 "periodic-wall intersection representative derivative"));
671 PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->
Rhs));
673 PetscFunctionReturn(PETSC_SUCCESS);
681 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
684 Vec x = NULL, xp = NULL, f0 = NULL, fp = NULL, v = NULL, jv = NULL, fd = NULL;
685 PetscReal h = 0.0, error = 0.0, scale = 0.0;
688 PetscFunctionBeginUser;
690 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
691 PetscCall(VecDuplicate(user->
Ucont, &x));
692 PetscCall(VecDuplicate(user->
Ucont, &xp));
693 PetscCall(VecDuplicate(user->
Ucont, &f0));
694 PetscCall(VecDuplicate(user->
Ucont, &fp));
695 PetscCall(VecDuplicate(user->
Ucont, &v));
696 PetscCall(VecDuplicate(user->
Ucont, &jv));
697 PetscCall(VecDuplicate(user->
Ucont, &fd));
698 PetscCall(VecCopy(user->
Ucont, x));
699 PetscCall(VecShift(x, 0.05));
700 PetscCall(VecSet(v, 0.5));
703 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
704 PetscCall(SNESSetDM(snes, user->
fda));
706 PetscCall(MatCreateSNESMF(snes, &J));
708 PetscCall(MatMFFDSetBase(J, x, f0));
709 PetscCall(MatMult(J, v, jv));
710 PetscCall(MatMFFDGetH(J, &h));
711 PetscCall(VecWAXPY(xp, h, v, x));
713 PetscCall(VecWAXPY(fd, -1.0, f0, fp));
714 PetscCall(VecScale(fd, 1.0 / h));
715 PetscCall(VecAXPY(fd, -1.0, jv));
716 PetscCall(VecNorm(fd, NORM_2, &error));
717 PetscCall(VecNorm(jv, NORM_2, &scale));
718 PetscCall(
PicurvAssertBool((PetscBool)(error <= 1.0e-9 * PetscMax(1.0, scale)),
719 "matrix-free Jv must match direct differencing"));
721 PetscCall(VecDestroy(&fd));
722 PetscCall(VecDestroy(&jv));
723 PetscCall(VecDestroy(&v));
724 PetscCall(VecDestroy(&fp));
725 PetscCall(VecDestroy(&f0));
726 PetscCall(VecDestroy(&xp));
727 PetscCall(VecDestroy(&x));
728 PetscCall(MatDestroy(&J));
729 PetscCall(SNESDestroy(&snes));
730 PetscCall(VecDestroy(&user->
Rhs));
732 PetscFunctionReturn(PETSC_SUCCESS);
743 PetscBool x_periodic)
745 PetscFunctionBeginUser;
749 simCtx, user, 6, 6, 6, PETSC_TRUE, PETSC_TRUE, PETSC_TRUE));
750 (*simCtx)->i_periodic = x_periodic ? 1 : 0;
751 (*simCtx)->j_periodic = (*simCtx)->k_periodic = 0;
753 (*simCtx)->invicid = 1;
756 (*simCtx)->StartStep = 0;
757 PetscCall(VecSet((*user)->Ucont, 0.0));
758 PetscCall(VecSet((*user)->Ucont_o, 0.0));
759 PetscCall(VecSet((*user)->Ucont_rm1, 0.0));
760 for (PetscInt face = 0; face < 6; ++face) {
761 PetscBool periodic_face = (PetscBool)(x_periodic &&
763 (*user)->boundary_faces[face].face_id = (
BCFace)face;
764 (*user)->boundary_faces[face].mathematical_type = periodic_face ?
PERIODIC :
WALL;
765 (*user)->boundary_faces[face].handler_type = periodic_face ?
768 &(*user)->boundary_faces[face].handler));
770 PetscFunctionReturn(PETSC_SUCCESS);
782 const char *models[] = {
"none",
"frozen_momentum_jacobian"};
783 const char *structures[] = {
"none",
"point_block"};
785 PetscFunctionBeginUser;
786 for (
size_t n = 0; n <
sizeof(models) /
sizeof(models[0]); ++n) {
789 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
792 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_rtol",
"1e-4"));
793 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_max_it",
"20"));
794 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_ksp_rtol",
"1e-6"));
795 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model", models[n]));
796 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure", structures[n]));
797 PetscCall(VecSet(user->
Ucont, 0.1));
801 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_preconditioner_model"));
802 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_preconditioner_structure"));
804 PetscFunctionReturn(PETSC_SUCCESS);
817 const char *models[] = {
"none",
"frozen_momentum_jacobian"};
818 const char *structures[] = {
"none",
"point_block"};
820 PetscFunctionBeginUser;
821 for (
size_t n = 0; n <
sizeof(models) /
sizeof(models[0]); ++n) {
825 PetscReal drift = 0.0, global_drift = 0.0;
826 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
830 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_rtol",
"1e-8"));
831 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_max_it",
"20"));
832 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_ksp_rtol",
"1e-10"));
833 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model", models[n]));
834 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure", structures[n]));
835 PetscCall(VecSet(user->
Ucont, 0.0));
836 PetscCall(VecStrideSet(user->
Ucont, 0, 0.1));
837 PetscCall(VecStrideSet(user->
Ucont, 2, 0.05));
843 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
844 for (PetscInt k = PetscMax(user->
info.zs, 1); k < PetscMin(user->
info.zs + user->
info.zm, user->
info.mz - 2); ++k)
845 for (PetscInt j = PetscMax(user->
info.ys, 1); j < PetscMin(user->
info.ys + user->
info.ym, user->
info.my - 2); ++j)
846 for (PetscInt i = PetscMax(user->
info.xs, 1); i < PetscMin(user->
info.xs + user->
info.xm, user->
info.mx - 1); ++i)
847 drift = PetscMax(drift, PetscAbsReal(ucont[k][j][i].x - 0.1));
848 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
849 PetscCallMPI(MPI_Allreduce(&drift, &global_drift, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD));
851 "the masked component must keep its time-constant value"));
853 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_preconditioner_model"));
854 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_preconditioner_structure"));
856 PetscFunctionReturn(PETSC_SUCCESS);
868 Vec x = NULL, xp = NULL, f0 = NULL, fp = NULL, column = NULL, square = NULL;
869 Vec row_norm_sq = NULL, v[2] = {NULL, NULL}, dense_v[2] = {NULL, NULL};
870 Vec mffd_v = NULL, error_vec = NULL;
871 PetscInt n_global, lo, hi;
872 PetscReal min_row_sq = 0.0, error = 0.0, reference = 0.0;
873 const PetscReal epsilon = 1.0e-7;
876 PetscFunctionBeginUser;
878 PetscCall(VecDuplicate(user->
Ucont, &x)); PetscCall(VecSet(x, 0.0));
879 PetscCall(VecDuplicate(x, &xp)); PetscCall(VecDuplicate(x, &f0));
880 PetscCall(VecDuplicate(x, &fp)); PetscCall(VecDuplicate(x, &column));
881 PetscCall(VecDuplicate(x, &square)); PetscCall(VecDuplicate(x, &row_norm_sq));
882 PetscCall(VecDuplicate(x, &v[0])); PetscCall(VecDuplicate(x, &v[1]));
883 PetscCall(VecDuplicate(x, &dense_v[0])); PetscCall(VecDuplicate(x, &dense_v[1]));
884 PetscCall(VecDuplicate(x, &mffd_v)); PetscCall(VecDuplicate(x, &error_vec));
885 PetscCall(VecZeroEntries(row_norm_sq)); PetscCall(VecZeroEntries(dense_v[0]));
886 PetscCall(VecZeroEntries(dense_v[1]));
887 PetscCall(VecGetSize(x, &n_global));
888 PetscCall(VecGetOwnershipRange(x, &lo, &hi));
889 for (PetscInt which = 0; which < 2; ++which) {
890 PetscScalar *a = NULL;
891 PetscCall(VecGetArray(v[which], &a));
892 for (PetscInt local = 0; local < hi - lo; ++local) {
893 PetscInt global = lo + local;
894 a[local] = which == 0 ? (PetscScalar)(1.0 + 0.05 * (global % 9))
895 : (PetscScalar)(((global % 2) ? -1.0 : 1.0) * (0.5 + 0.03 * (global % 7)));
897 PetscCall(VecRestoreArray(v[which], &a));
903 for (PetscInt col = 0; col < n_global; ++col) {
904 PetscScalar coeff[2] = {
905 (PetscScalar)(1.0 + 0.05 * (col % 9)),
906 (PetscScalar)(((col % 2) ? -1.0 : 1.0) * (0.5 + 0.03 * (col % 7)))
908 PetscCall(VecCopy(x, xp));
909 if (col >= lo && col < hi) PetscCall(VecSetValue(xp, col, epsilon, ADD_VALUES));
910 PetscCall(VecAssemblyBegin(xp)); PetscCall(VecAssemblyEnd(xp));
912 PetscCall(VecWAXPY(column, -1.0, f0, fp));
913 PetscCall(VecScale(column, 1.0 / epsilon));
914 PetscCall(VecPointwiseMult(square, column, column));
915 PetscCall(VecAXPY(row_norm_sq, 1.0, square));
916 PetscCall(VecAXPY(dense_v[0], coeff[0], column));
917 PetscCall(VecAXPY(dense_v[1], coeff[1], column));
919 PetscCall(VecMin(row_norm_sq, NULL, &min_row_sq));
920 PetscCheck(min_row_sq > 0.5, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
921 "Complete Newton Jacobian contains an unexplained zero/weak row (min squared norm=%g).",
923 PetscCall(
CheckStoredDerivative(user, x, 2, 2, 2, 0, 2, 2, 2, 0,
924 10.0, 1.0e-5,
"interior BDF1 temporal diagonal"));
926 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
927 PetscCall(SNESSetDM(snes, user->
fda));
929 PetscCall(MatCreateSNESMF(snes, &J));
930 PetscCall(MatMFFDSetBase(J, x, f0));
931 for (PetscInt which = 0; which < 2; ++which) {
932 PetscCall(MatMult(J, v[which], mffd_v));
933 PetscCall(VecWAXPY(error_vec, -1.0, dense_v[which], mffd_v));
934 PetscCall(VecNorm(error_vec, NORM_2, &error));
935 PetscCall(VecNorm(dense_v[which], NORM_2, &reference));
936 PetscCheck(error <= 2.0e-5 * PetscMax(1.0, reference), PETSC_COMM_WORLD, PETSC_ERR_PLIB,
937 "Independent dense FD Jv differs from PETSc MFFD action %" PetscInt_FMT
": error=%g reference=%g.",
938 which, (
double)error, (
double)reference);
941 for (PetscInt which = 0; which < 2; ++which) {
942 const PetscReal steps[3] = {1.0e-4, 1.0e-6, 1.0e-8};
943 PetscReal best = PETSC_MAX_REAL;
944 for (PetscInt s = 0; s < 3; ++s) {
945 PetscCall(VecWAXPY(xp, steps[s], v[which], x));
947 PetscCall(VecWAXPY(column, -1.0, f0, fp));
948 PetscCall(VecScale(column, 1.0 / steps[s]));
949 PetscCall(VecAXPY(column, -1.0, dense_v[which]));
950 PetscCall(VecNorm(column, NORM_2, &error));
951 best = PetscMin(best, error);
953 PetscCall(VecNorm(dense_v[which], NORM_2, &reference));
954 PetscCheck(best <= 2.0e-5 * PetscMax(1.0, reference), PETSC_COMM_WORLD, PETSC_ERR_PLIB,
955 "Direct directional differences show no accuracy plateau for vector %" PetscInt_FMT
" (best=%g).",
956 which, (
double)best);
959 PetscCall(MatDestroy(&J)); PetscCall(SNESDestroy(&snes));
960 PetscCall(VecDestroy(&error_vec)); PetscCall(VecDestroy(&mffd_v));
961 PetscCall(VecDestroy(&dense_v[1])); PetscCall(VecDestroy(&dense_v[0]));
962 PetscCall(VecDestroy(&v[1])); PetscCall(VecDestroy(&v[0]));
963 PetscCall(VecDestroy(&row_norm_sq)); PetscCall(VecDestroy(&square));
964 PetscCall(VecDestroy(&column)); PetscCall(VecDestroy(&fp)); PetscCall(VecDestroy(&f0));
965 PetscCall(VecDestroy(&xp)); PetscCall(VecDestroy(&x));
968 PetscFunctionReturn(PETSC_SUCCESS);
976 Vec x = NULL, xp = NULL, f0 = NULL, fp = NULL, column = NULL;
977 Vec square = NULL, row_norm_sq = NULL;
978 PetscInt n_global, lo, hi;
979 PetscReal min_row_sq = 0.0;
980 const PetscReal epsilon = 1.0e-7;
983 PetscFunctionBeginUser;
985 PetscCall(VecDuplicate(user->
Ucont, &x)); PetscCall(VecSet(x, 0.0));
986 PetscCall(VecDuplicate(x, &xp)); PetscCall(VecDuplicate(x, &f0));
987 PetscCall(VecDuplicate(x, &fp)); PetscCall(VecDuplicate(x, &column));
988 PetscCall(VecDuplicate(x, &square)); PetscCall(VecDuplicate(x, &row_norm_sq));
989 PetscCall(VecZeroEntries(row_norm_sq));
990 PetscCall(VecGetSize(x, &n_global)); PetscCall(VecGetOwnershipRange(x, &lo, &hi));
994 for (PetscInt col = 0; col < n_global; ++col) {
995 PetscCall(VecCopy(x, xp));
996 if (col >= lo && col < hi) PetscCall(VecSetValue(xp, col, epsilon, ADD_VALUES));
997 PetscCall(VecAssemblyBegin(xp)); PetscCall(VecAssemblyEnd(xp));
999 PetscCall(VecWAXPY(column, -1.0, f0, fp)); PetscCall(VecScale(column, 1.0 / epsilon));
1000 PetscCall(VecPointwiseMult(square, column, column));
1001 PetscCall(VecAXPY(row_norm_sq, 1.0, square));
1003 PetscCall(VecMin(row_norm_sq, NULL, &min_row_sq));
1004 PetscCheck(min_row_sq > 0.5, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
1005 "Periodic Newton Jacobian contains a zero/weak row (min squared norm=%g).",
1006 (
double)min_row_sq);
1007 PetscCall(VecDestroy(&row_norm_sq)); PetscCall(VecDestroy(&square));
1008 PetscCall(VecDestroy(&column)); PetscCall(VecDestroy(&fp)); PetscCall(VecDestroy(&f0));
1009 PetscCall(VecDestroy(&xp)); PetscCall(VecDestroy(&x));
1012 PetscFunctionReturn(PETSC_SUCCESS);
1020 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1021 Vec entry = NULL, delta = NULL;
1022 PetscErrorCode solve_ierr;
1023 PetscReal norm = 0.0;
1025 char summary_path[PETSC_MAX_PATH_LEN];
1026 char history_path[PETSC_MAX_PATH_LEN];
1027 char linear_history_path[PETSC_MAX_PATH_LEN];
1029 PetscFunctionBeginUser;
1031 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_rtol",
"1e-4"));
1032 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_max_it",
"20"));
1033 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_ksp_rtol",
"1e-6"));
1038 "successful solve must keep its workspace for the next step"));
1039 PetscCall(PetscSNPrintf(summary_path,
sizeof(summary_path),
1040 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.csv", simCtx->
log_dir));
1041 PetscCall(PetscSNPrintf(history_path,
sizeof(history_path),
1042 "%s/Momentum_Solver_Newton_Krylov_History_Block_0.csv", simCtx->
log_dir));
1043 PetscCall(PetscSNPrintf(linear_history_path,
sizeof(linear_history_path),
1044 "%s/Momentum_Solver_Newton_Krylov_Linear_History_Block_0.csv",
1046 PetscCall(
AssertNewtonLog(summary_path, 1,
",Newton Krylov,",
",committed\n"));
1047 PetscCall(
AssertNewtonLog(history_path, -2,
"step,block,newton,nonlinear_norm",
",0,0,"));
1048 PetscCall(
AssertNewtonLog(linear_history_path, -2,
"krylov,requested_rtol,reported_residual_norm",
",0,0,0,"));
1053 PetscCall(VecSet(user->
Ucont, 0.2));
1056 PetscCall(VecDuplicate(user->
Ucont, &entry));
1057 PetscCall(VecDuplicate(user->
Ucont, &delta));
1058 PetscCall(VecCopy(user->
Ucont, entry));
1059 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_max_it",
"0"));
1062 "a finite residual no larger than at entry must commit the step"));
1064 "a committed SNES failure must be reported as not converged"));
1065 PetscCall(VecWAXPY(delta, -1.0, entry, user->
Ucont));
1066 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
1068 "a zero-iteration failure must commit the unchanged entry state"));
1070 "committed unconverged solve must keep its workspace for the next step"));
1071 PetscCall(PetscSNPrintf(summary_path,
sizeof(summary_path),
1072 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.csv", simCtx->
log_dir));
1073 PetscCall(
AssertNewtonLog(summary_path, 1,
",DIVERGED_",
",committed_unconverged\n"));
1078 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_max_it",
"20"));
1079 PetscCall(VecSet(user->
Ucont, PETSC_MAX_REAL));
1082 PetscCall(VecCopy(user->
Ucont, entry));
1083 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1085 PetscCall(PetscPopErrorHandler());
1087 "a non-finite residual must report PETSC_ERR_CONV_FAILED"));
1089 "a rolled-back solve must be reported as not converged"));
1090 PetscCall(VecWAXPY(delta, -1.0, entry, user->
Ucont));
1091 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
1093 "failed Newton solve must restore the canonical entry state"));
1094 PetscCall(
PicurvAssertBool((PetscBool)(user->
Rhs == NULL),
"failed solve must release Rhs"));
1095 PetscCall(
AssertNewtonLog(summary_path, 2,
",DIVERGED_",
",rolled_back\n"));
1097 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_snes_rtol"));
1098 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_snes_max_it"));
1099 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_ksp_rtol"));
1100 PetscCall(VecDestroy(&delta));
1101 PetscCall(VecDestroy(&entry));
1103 PetscFunctionReturn(PETSC_SUCCESS);
1111 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1112 char summary_path[PETSC_MAX_PATH_LEN];
1115 PetscFunctionBeginUser;
1117 PetscCall(VecZeroEntries(user->
Ucont));
1118 PetscCall(VecZeroEntries(user->
Ucont_o));
1119 PetscCall(VecZeroEntries(user->
Ucont_rm1));
1123 PetscCall(PetscSNPrintf(summary_path,
sizeof(summary_path),
1124 "%s/Momentum_Solver_Newton_Krylov_Summary_Block_0.csv", simCtx->
log_dir));
1126 ",0.0000000000000000e+00,committed\n"));
1128 PetscFunctionReturn(PETSC_SUCCESS);
1144 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1145 Vec x = NULL, f = NULL;
1146 PetscReal initial_norm = 0.0;
1149 PetscFunctionBeginUser;
1151 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
1152 PetscCall(VecDuplicate(user->
Ucont, &x));
1153 PetscCall(VecDuplicate(user->
Ucont, &f));
1154 PetscCall(VecCopy(user->
Ucont, x));
1157 PetscCall(VecNorm(f, NORM_2, &initial_norm));
1158 PetscCheck(!PetscIsInfOrNanReal(initial_norm), PETSC_COMM_WORLD, PETSC_ERR_FP,
1159 "flat-channel BDF1 initial residual is non-finite (%g) with %s.",
1160 (
double)initial_norm,
1161 use_point_block ?
"frozen-momentum point-block" :
"PCNONE");
1162 PetscCall(VecDestroy(&f));
1163 PetscCall(VecDestroy(&x));
1164 PetscCall(VecDestroy(&user->
Rhs));
1166 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_rtol",
"1e-4"));
1167 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_max_it",
"20"));
1168 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_ksp_rtol",
"1e-6"));
1169 if (use_point_block) {
1170 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
1171 "frozen_momentum_jacobian"));
1172 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
1177 "flat-channel BDF1 Newton solve must converge"));
1178 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_preconditioner_model"));
1179 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_preconditioner_structure"));
1180 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_snes_rtol"));
1181 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_snes_max_it"));
1182 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_ksp_rtol"));
1184 PetscFunctionReturn(PETSC_SUCCESS);
1190 PetscFunctionBeginUser;
1193 PetscFunctionReturn(PETSC_SUCCESS);
1201 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1204 PetscFunctionBeginUser;
1206 PetscCall(VecSet(user->
Ucont, 0.2));
1207 PetscCall(VecZeroEntries(user->
Ucont_o));
1208 PetscCall(VecZeroEntries(user->
Ucont_rm1));
1211 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_rtol",
"1e-4"));
1212 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_snes_max_it",
"20"));
1213 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_ksp_rtol",
"1e-6"));
1222 "PCNONE Newton Krylov checkpoint restart must converge"));
1227 PetscCall(VecSet(user->
Ucont, 0.2));
1228 PetscCall(VecZeroEntries(user->
Ucont_o));
1229 PetscCall(VecZeroEntries(user->
Ucont_rm1));
1232 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
1233 "frozen_momentum_jacobian"));
1234 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"point_block"));
1241 "frozen-momentum Newton Krylov --continue solve must converge"));
1242 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_preconditioner_model"));
1243 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_preconditioner_structure"));
1244 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_snes_rtol"));
1245 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_snes_max_it"));
1246 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_ksp_rtol"));
1248 PetscFunctionReturn(PETSC_SUCCESS);
1256 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1257 PetscErrorCode solve_ierr;
1259 PetscFunctionBeginUser;
1266 PetscInt *unsupported_flags[] = {
1270 for (
size_t flag = 0; flag <
sizeof(unsupported_flags) /
sizeof(unsupported_flags[0]); ++flag) {
1271 *unsupported_flags[flag] = 1;
1272 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1274 PetscCall(PetscPopErrorHandler());
1276 "unsupported Newton feature flag must fail"));
1278 "feature validation must precede workspace allocation"));
1279 *unsupported_flags[flag] = 0;
1283 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1285 PetscCall(PetscPopErrorHandler());
1286 PetscCall(
PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS),
"multiblock must fail"));
1295 PetscCall(VecSet(user->
Nvert, 1.0));
1296 PetscCall(VecSet(user->
lNvert, 1.0));
1299 "a fully masked domain must resolve to identity rows, not a rejection"));
1303 PetscCall(VecDestroy(&user->
Rhs));
1304 PetscCall(VecSet(user->
Nvert, 0.0));
1305 PetscCall(VecSet(user->
lNvert, 0.0));
1306 PetscCall(VecSet(user->
Nvert, 0.0));
1309 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1311 PetscCall(PetscPopErrorHandler());
1313 "driven constant-flux controller must fail"));
1315 "all validation failures must precede workspace allocation"));
1317 PetscFunctionReturn(PETSC_SUCCESS);
1325 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1326 Vec entry = NULL, delta = NULL;
1327 PetscErrorCode solve_ierr;
1328 PetscReal norm = 0.0;
1331 PetscFunctionBeginUser;
1333 PetscCall(VecSet(user->
Ucont, 0.2));
1336 PetscCall(VecDuplicate(user->
Ucont, &entry)); PetscCall(VecCopy(user->
Ucont, entry));
1337 PetscCall(VecDuplicate(user->
Ucont, &delta));
1338 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_pc_type",
"jacobi"));
1339 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1341 PetscCall(PetscPopErrorHandler());
1342 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_pc_type"));
1344 "non-PCNONE option must fail after setup"));
1346 "post-allocation failure must destroy Rhs"));
1347 PetscCall(VecWAXPY(delta, -1.0, entry, user->
Ucont));
1348 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
1350 "post-allocation failure must restore canonical entry"));
1351 PetscCall(VecDestroy(&delta)); PetscCall(VecDestroy(&entry));
1353 PetscFunctionReturn(PETSC_SUCCESS);
1361 PetscErrorCode config_ierr;
1362 const char *option_names[] = {
1363 "-mom_nk_jacobian_type",
1364 "-mom_nk_jacobian_fd_mode",
1365 "-mom_nk_preconditioner_model",
1366 "-mom_nk_preconditioner_structure"
1369 PetscFunctionBeginUser;
1370 for (
size_t n = 0; n <
sizeof(option_names) /
sizeof(option_names[0]); ++n)
1371 PetscCall(PetscOptionsClearValue(NULL, option_names[n]));
1375 "default Jacobian type must be finite difference"));
1378 "default finite-difference mode must be matrix free"));
1380 "default preconditioner model must be none"));
1382 "default preconditioner structure must be none"));
1384 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_type",
"finite_difference"));
1385 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_fd_mode",
"matrix_free"));
1386 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
"none"));
1387 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"none"));
1390 "explicit baseline Jacobian type must parse"));
1393 "explicit baseline finite-difference mode must parse"));
1395 "explicit baseline preconditioner model must parse"));
1397 "explicit baseline preconditioner structure must parse"));
1399 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
1400 "frozen_momentum_jacobian"));
1401 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"point_block"));
1404 "explicit Jacobian type must parse"));
1407 "explicit finite-difference mode must parse"));
1409 description.
model,
"frozen model must parse"));
1411 description.
structure,
"point-block structure must parse"));
1413 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_type",
"analytic"));
1414 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1416 PetscCall(PetscPopErrorHandler());
1418 "unsupported Jacobian type must fail"));
1419 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_type",
"finite_difference"));
1421 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_fd_mode",
"colored_sparse"));
1422 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1424 PetscCall(PetscPopErrorHandler());
1426 "unsupported finite-difference mode must fail"));
1427 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_fd_mode",
"matrix_free"));
1429 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"none"));
1430 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1432 PetscCall(PetscPopErrorHandler());
1434 "frozen model without point block must fail"));
1435 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
"none"));
1436 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"point_block"));
1437 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1439 PetscCall(PetscPopErrorHandler());
1441 "none model with point block must fail"));
1443 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
"diagonal"));
1444 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"none"));
1445 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1447 PetscCall(PetscPopErrorHandler());
1449 "unsupported preconditioner model must fail"));
1450 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
1451 "frozen_momentum_jacobian"));
1452 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"line"));
1453 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1455 PetscCall(PetscPopErrorHandler());
1457 "unsupported preconditioner structure must fail"));
1459 for (
size_t n = 0; n <
sizeof(option_names) /
sizeof(option_names[0]); ++n)
1460 PetscCall(PetscOptionsClearValue(NULL, option_names[n]));
1461 PetscFunctionReturn(PETSC_SUCCESS);
1466 PetscInt *global_index)
1468 ISLocalToGlobalMapping local_to_global = NULL;
1469 PetscInt ghost_starts[3], ghost_sizes[3], local_index;
1471 PetscFunctionBeginUser;
1472 PetscCall(DMDAGetGhostCorners(user->
fda,
1473 &ghost_starts[0], &ghost_starts[1], &ghost_starts[2],
1474 &ghost_sizes[0], &ghost_sizes[1], &ghost_sizes[2]));
1476 stencil.i >= ghost_starts[0] && stencil.i < ghost_starts[0] + ghost_sizes[0] &&
1477 stencil.j >= ghost_starts[1] && stencil.j < ghost_starts[1] + ghost_sizes[1] &&
1478 stencil.k >= ghost_starts[2] && stencil.k < ghost_starts[2] + ghost_sizes[2]),
1479 "test stencil must lie in the local DMDA ghost region"));
1480 local_index = stencil.c + 3 * (
1481 (stencil.i - ghost_starts[0]) + ghost_sizes[0] * (
1482 (stencil.j - ghost_starts[1]) + ghost_sizes[1] *
1483 (stencil.k - ghost_starts[2])));
1484 PetscCall(DMGetLocalToGlobalMapping(user->
fda, &local_to_global));
1485 PetscCall(ISLocalToGlobalMappingApply(local_to_global, 1, &local_index,
1487 PetscFunctionReturn(PETSC_SUCCESS);
1492 Mat preconditioning_matrix, MatStencil row,
1493 MatStencil col, PetscScalar *value)
1495 Vec column_basis = NULL, row_basis = NULL, product = NULL;
1497 PetscInt mx, my, row_index, col_index;
1498 PetscFunctionBeginUser;
1499 PetscCall(DMDAGetInfo(user->
fda, NULL, &mx, &my, NULL, NULL, NULL, NULL,
1500 NULL, NULL, NULL, NULL, NULL, NULL));
1501 row_index = row.c + 3 * (row.i + mx * (row.j + my * row.k));
1502 col_index = col.c + 3 * (col.i + mx * (col.j + my * col.k));
1503 PetscCall(DMDAGetAO(user->
fda, &ao));
1504 PetscCall(AOApplicationToPetsc(ao, 1, &row_index));
1505 PetscCall(AOApplicationToPetsc(ao, 1, &col_index));
1506 PetscCall(DMCreateGlobalVector(user->
fda, &column_basis));
1507 PetscCall(DMCreateGlobalVector(user->
fda, &row_basis));
1508 PetscCall(DMCreateGlobalVector(user->
fda, &product));
1509 PetscCall(VecSet(column_basis, 0.0)); PetscCall(VecSet(row_basis, 0.0));
1510 PetscCall(VecSetValue(column_basis, col_index, 1.0, INSERT_VALUES));
1511 PetscCall(VecSetValue(row_basis, row_index, 1.0, INSERT_VALUES));
1512 PetscCall(VecAssemblyBegin(column_basis)); PetscCall(VecAssemblyEnd(column_basis));
1513 PetscCall(VecAssemblyBegin(row_basis)); PetscCall(VecAssemblyEnd(row_basis));
1514 PetscCall(MatMult(preconditioning_matrix, column_basis, product));
1515 PetscCall(VecDot(row_basis, product, value));
1516 PetscCall(VecDestroy(&product)); PetscCall(VecDestroy(&row_basis));
1517 PetscCall(VecDestroy(&column_basis));
1518 PetscFunctionReturn(PETSC_SUCCESS);
1523 UserCtx *user, Mat matrix, PetscBool require_offrank_periodic)
1526 MatInfo matrix_info;
1527 PetscMPIInt comm_size = 1;
1528 PetscInt matrix_rows, matrix_cols, local_rows, local_cols;
1529 PetscInt vector_size, vector_local_size, block_size;
1530 PetscInt ownership_start, ownership_end;
1531 PetscInt expected_local = 0, expected_global = 0;
1532 PetscInt offrank_periodic_local = 0, offrank_periodic_global = 0;
1533 PetscBool is_seq_aij = PETSC_FALSE, is_mpi_aij = PETSC_FALSE;
1535 PetscFunctionBeginUser;
1536 PetscCall(DMDAGetLocalInfo(user->
fda, &info));
1537 PetscCall(MatGetSize(matrix, &matrix_rows, &matrix_cols));
1538 PetscCall(MatGetLocalSize(matrix, &local_rows, &local_cols));
1539 PetscCall(VecGetSize(user->
Ucont, &vector_size));
1540 PetscCall(VecGetLocalSize(user->
Ucont, &vector_local_size));
1541 PetscCall(VecGetOwnershipRange(user->
Ucont, &ownership_start, &ownership_end));
1542 PetscCall(MatGetBlockSize(matrix, &block_size));
1544 "point-block global row dimension"));
1546 "point-block global column dimension"));
1548 "point-block local row dimension"));
1550 "point-block local column dimension"));
1552 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)matrix), &comm_size));
1553 PetscCall(PetscObjectTypeCompare((PetscObject)matrix, MATSEQAIJ, &is_seq_aij));
1554 PetscCall(PetscObjectTypeCompare((PetscObject)matrix, MATMPIAIJ, &is_mpi_aij));
1556 comm_size == 1 ? is_seq_aij : is_mpi_aij,
1557 "point-block matrix must use the expected AIJ implementation"));
1559 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k) {
1560 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j) {
1561 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
1562 for (PetscInt component = 0; component < 3; ++component) {
1563 PetscInt ri, rj, rk;
1565 user, i, j, k, component, &ri, &rj, &rk);
1569 PetscInt representative;
1571 (MatStencil){.i = ri, .j = rj, .k = rk, .c = component},
1573 if (representative < ownership_start || representative >= ownership_end)
1574 ++offrank_periodic_local;
1580 PetscCallMPI(MPI_Allreduce(&expected_local, &expected_global, 1, MPIU_INT,
1581 MPI_SUM, PetscObjectComm((PetscObject)matrix)));
1582 PetscCallMPI(MPI_Allreduce(&offrank_periodic_local, &offrank_periodic_global,
1583 1, MPIU_INT, MPI_SUM,
1584 PetscObjectComm((PetscObject)matrix)));
1585 PetscCall(MatGetInfo(matrix, MAT_GLOBAL_SUM, &matrix_info));
1587 (PetscReal)matrix_info.nz_allocated, 0.0,
1588 "point-block matrix must allocate exactly the classified scalar pattern"));
1590 (PetscReal)matrix_info.nz_used, 0.0,
1591 "point-block assembly must insert every classified structural entry"));
1593 "point-block insertion must not reallocate matrix storage"));
1594 if (require_offrank_periodic && comm_size > 1)
1596 "MPI periodic fixture must exercise off-rank preallocation"));
1597 PetscFunctionReturn(PETSC_SUCCESS);
1608 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1617 Mat preconditioning_matrix = NULL;
1618 Vec x = NULL, f = NULL;
1619 PetscInt block_size = 0, velocity_dof = 0;
1620 PetscReal matrix_norm = 0.0, reassembled_norm = 0.0, difference_norm = 0.0;
1621 MatStencil conditioned = {.i = 0, .j = 2, .k = 3, .c = 0};
1622 MatStencil homogeneous = {.i = 0, .j = 2, .k = 3, .c = 1};
1623 Mat saved_matrix = NULL, mffd = NULL;
1625 Vec direction = NULL, product = NULL, px = NULL;
1626 PetscReal px_norm = 0.0;
1629 Vec pc_rhs = NULL, pc_solution = NULL;
1630 MatInfo initial_allocation_info, repeated_allocation_info;
1632 PetscFunctionBeginUser;
1634 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
1635 PetscCall(VecDuplicate(user->
Ucont, &x));
1636 PetscCall(VecDuplicate(user->
Ucont, &f));
1637 PetscCall(VecCopy(user->
Ucont, x));
1638 PetscCall(VecStrideSet(x, 0, 0.2));
1639 PetscCall(VecStrideSet(x, 1, 0.3));
1640 PetscCall(VecStrideSet(x, 2, 0.4));
1646 "engine must select the frozen-momentum model callbacks"));
1648 "point-block engine must own its separate matrix"));
1650 "point-block matrix must not alias the Jacobian operator"));
1653 user, preconditioning_matrix, PETSC_FALSE));
1654 PetscCall(MatGetInfo(preconditioning_matrix, MAT_GLOBAL_SUM,
1655 &initial_allocation_info));
1656 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &matrix_norm));
1658 "model callback and common rows must insert matrix entries"));
1659 PetscCall(MatGetBlockSize(preconditioning_matrix, &block_size));
1661 PetscCall(DMDAGetInfo(user->
fda, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1662 &velocity_dof, NULL, NULL, NULL, NULL, NULL));
1668 const MatStencil row_i = {.i = 2, .j = 2, .k = 2, .c = 0};
1669 const MatStencil row_j = {.i = 2, .j = 2, .k = 2, .c = 1};
1670 MatStencil col_i = row_i, col_j = row_j;
1671 PetscReal ***aj = NULL;
1672 PetscReal AJip, AJjp;
1673 PetscScalar dFi_dUj = 0.0, dFj_dUi = 0.0;
1675 PetscCall(DMDAVecGetArrayRead(user->
da, user->
lAj, &aj));
1676 AJip = 0.5 * (aj[2][2][2] + aj[2][2][3]);
1677 AJjp = 0.5 * (aj[2][2][2] + aj[2][3][2]);
1678 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
lAj, &aj));
1680 row_i, col_j, &dFi_dUj));
1682 row_j, col_i, &dFj_dUi));
1684 PetscRealPart(dFi_dUj), 1e-12,
1685 "row i, column j must differentiate with respect to U-j"));
1687 PetscRealPart(dFj_dUi), 1e-12,
1688 "row j, column i must differentiate with respect to U-i"));
1692 for (PetscInt cc = 0; cc < 3; ++cc) {
1693 MatStencil col = conditioned; col.c = cc;
1694 PetscScalar conditioned_value = 0.0, homogeneous_value = 0.0;
1696 conditioned, col, &conditioned_value));
1697 col = homogeneous; col.c = cc;
1699 homogeneous, col, &homogeneous_value));
1701 PetscRealPart(conditioned_value), 1e-14,
"conditioned row must be exact identity"));
1703 PetscRealPart(homogeneous_value), 1e-14,
"homogeneous row must be exact identity"));
1707 PetscCall(MatDuplicate(preconditioning_matrix, MAT_COPY_VALUES, &saved_matrix));
1708 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1709 PetscCall(SNESSetDM(snes, user->
fda));
1711 PetscCall(MatCreateSNESMF(snes, &mffd));
1712 PetscCall(VecDuplicate(x, &direction)); PetscCall(VecDuplicate(x, &product));
1713 PetscCall(VecSet(direction, 0.375));
1715 PetscCall(MatMFFDSetBase(mffd, x, f));
1716 PetscCall(MatMult(mffd, direction, product));
1718 PetscCall(MatAXPY(saved_matrix, -1.0, preconditioning_matrix, SAME_NONZERO_PATTERN));
1719 PetscCall(MatNorm(saved_matrix, NORM_FROBENIUS, &difference_norm));
1721 "assembly must be unchanged after residual and MFFD products"));
1722 PetscCall(VecDestroy(&product)); PetscCall(VecDestroy(&direction));
1723 PetscCall(MatDestroy(&mffd)); PetscCall(SNESDestroy(&snes)); PetscCall(MatDestroy(&saved_matrix));
1725 PetscCall(VecDuplicate(x, &px));
1726 PetscCall(MatMult(preconditioning_matrix, x, px));
1727 PetscCall(VecNorm(px, NORM_2, &px_norm));
1728 PetscCall(
PicurvAssertBool((PetscBool)(!PetscIsInfOrNanReal(px_norm) && px_norm > 0.0),
1729 "point-block product must be finite and nonzero"));
1730 PetscCall(VecDestroy(&px));
1733 PetscBool assembled = PETSC_FALSE;
1734 PetscCall(MatAssembled(preconditioning_matrix, &assembled));
1735 PetscCall(
PicurvAssertBool(assembled,
"engine must perform final matrix assembly"));
1737 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &matrix_norm));
1738 PetscCall(MatShift(preconditioning_matrix, 7.0));
1740 PetscCall(MatGetInfo(preconditioning_matrix, MAT_GLOBAL_SUM,
1741 &repeated_allocation_info));
1743 (PetscReal)repeated_allocation_info.nz_allocated, 0.0,
1744 "repeated assembly must retain exact allocated storage"));
1746 (PetscReal)repeated_allocation_info.mallocs, 0.0,
1747 "repeated assembly must not add insertion reallocations"));
1748 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &reassembled_norm));
1750 "repeated engine assembly must clear old entries"));
1752 PetscScalar value = 0.0;
1754 conditioned, conditioned, &value));
1756 "repeated engine assembly must clear old entries"));
1758 PetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
1759 PetscCall(KSPSetOperators(ksp, preconditioning_matrix, preconditioning_matrix));
1760 PetscCall(KSPGetPC(ksp, &pc));
1763 PetscCall(KSPSetUp(ksp));
1764 PetscCall(DMCreateGlobalVector(user->
fda, &pc_rhs));
1765 PetscCall(DMCreateGlobalVector(user->
fda, &pc_solution));
1766 PetscCall(VecSet(pc_rhs, 1.0));
1767 PetscCall(PCApply(pc, pc_rhs, pc_solution));
1768 PetscCall(VecDestroy(&pc_solution)); PetscCall(VecDestroy(&pc_rhs));
1769 PetscCall(KSPDestroy(&ksp));
1772 "engine destroy must clear its owned matrix"));
1773 PetscCall(VecDestroy(&f)); PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->
Rhs));
1775 PetscFunctionReturn(PETSC_SUCCESS);
1780 MatStencil row,
const char *message)
1783 PetscInt ri, rj, rk, global_row, global_rep, lo, hi, local_checked = 0, checked = 0;
1785 const PetscInt *cols = NULL;
1786 const PetscScalar *values = NULL;
1789 PetscFunctionBeginUser;
1792 global_row = row.c + 3 * (row.i + user->
info.mx * (row.j + user->
info.my * row.k));
1793 PetscCall(DMDAGetAO(user->
fda, &ao));
1794 PetscCall(AOApplicationToPetsc(ao, 1, &global_row));
1795 PetscCall(MatGetOwnershipRange(matrix, &lo, &hi));
1796 if (global_row >= lo && global_row < hi) {
1797 PetscBool found_self = PETSC_FALSE, found_rep = PETSC_FALSE;
1798 PetscInt nonzero_entries = 0;
1800 (MatStencil){.i = ri, .j = rj, .k = rk, .c = row.c}, &global_rep));
1801 PetscCall(MatGetRow(matrix, global_row, &ncols, &cols, &values));
1802 for (PetscInt n = 0; n < ncols; ++n) {
1803 if (PetscAbsScalar(values[n]) <= 1e-14)
continue;
1805 if (cols[n] == global_row) {
1806 found_self = PETSC_TRUE;
1808 "periodic row self entry"));
1809 }
else if (cols[n] == global_rep) {
1810 found_rep = PETSC_TRUE;
1812 "periodic row representative entry"));
1814 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB,
1815 "Periodic row contains an unintended nonzero column.");
1819 "periodic row must contain exactly two numerical entries"));
1820 PetscCall(
PicurvAssertBool((PetscBool)(found_self && found_rep), message));
1821 PetscCall(MatRestoreRow(matrix, global_row, &ncols, &cols, &values));
1824 PetscCallMPI(MPI_Allreduce(&local_checked, &checked, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
1825 PetscCall(
PicurvAssertIntEqual(1, checked,
"periodic row must have exactly one matrix owner"));
1826 PetscFunctionReturn(PETSC_SUCCESS);
1834 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1840 Mat jacobian_operator = NULL, preconditioning_matrix = NULL;
1843 PetscInt rows = 0, cols = 0;
1844 const char *jacobian_prefix = NULL;
1845 const char *pc_type = NULL;
1846 PetscBool prefix_matches = PETSC_FALSE;
1847 PetscBool pc_is_none = PETSC_FALSE;
1849 PetscFunctionBeginUser;
1851 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
1852 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1853 PetscCall(SNESSetDM(snes, user->
fda));
1860 PetscCall(PetscStrcmp(jacobian_prefix,
"mom_nk_", &prefix_matches));
1862 "Jacobian interface must apply the application prefix"));
1867 PetscCall(SNESGetKSP(snes, &ksp));
1868 PetscCall(KSPGetPC(ksp, &pc));
1873 PetscCall(PCGetType(pc, &pc_type));
1874 PetscCall(PetscStrcmp(pc_type, PCNONE, &pc_is_none));
1876 "baseline engine must derive PETSc PCNONE"));
1877 PetscCall(SNESGetJacobian(snes, &jacobian_operator, &preconditioning_matrix, NULL, NULL));
1880 "SNES must receive the Jacobian-interface MFFD operator"));
1881 PetscCall(
PicurvAssertBool((PetscBool)(preconditioning_matrix == jacobian_operator),
1882 "baseline preconditioning matrix must alias the Jacobian"));
1884 "baseline engine must record matrix aliasing"));
1886 "baseline engine must not own the Jacobian alias"));
1890 "destroying an alias engine must preserve the Jacobian"));
1892 PetscCall(SNESDestroy(&snes));
1893 PetscCall(VecDestroy(&user->
Rhs));
1895 PetscFunctionReturn(PETSC_SUCCESS);
1903 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1911 Vec x = NULL, f = NULL;
1912 PetscReal matrix_norm = 0.0;
1914 PetscFunctionBeginUser;
1916 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
1917 PetscCall(VecDuplicate(user->
Ucont, &x));
1918 PetscCall(VecDuplicate(user->
Ucont, &f));
1919 PetscCall(VecCopy(user->
Ucont, x));
1928 NORM_FROBENIUS, &matrix_norm));
1930 "periodic point-block assembly must produce a nonzero matrix"));
1933 (MatStencil){.i = 0, .j = 2, .k = 3, .c = 0},
1934 "single-axis periodic row must contain exact +1/-1 entries"));
1937 (MatStencil){.i = 0, .j = 0, .k = 3, .c = 1},
1938 "periodic intersection must contain exact +1/-1 entries"));
1941 (MatStencil){.i = 0, .j = 0, .k = 0, .c = 2},
1942 "periodic origin intersection must contain exact +1/-1 entries"));
1944 PetscCall(VecDestroy(&f));
1945 PetscCall(VecDestroy(&x));
1946 PetscCall(VecDestroy(&user->
Rhs));
1948 PetscFunctionReturn(PETSC_SUCCESS);
1959 PetscErrorCode ierr;
1983 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv Newton Krylov tests");
1984 if (ierr)
return (
int)ierr;
1985 ierr =
PicurvRunTests(
"unit-newton-krylov", cases,
sizeof(cases) /
sizeof(cases[0]));
1986 if (PetscFinalize())
return 1;
MomentumRowType
Classification of one staggered momentum row (location + component).
@ MOM_ROW_FIXED_HOMOGENEOUS
Dummy/tangential row carrying no unknown at all.
@ MOM_ROW_PHYSICAL
Independent unknown governed by the momentum equation.
@ MOM_ROW_PERIODIC_DUPLICATE
Duplicate of a wrapped representative row (see ri, rj, rk).
@ MOM_ROW_FIXED_CONDITIONED
Strong Dirichlet row; the value comes from ApplyBoundaryConditions().
MomentumRowType ClassifyMomentumRow(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscInt *ri, PetscInt *rj, PetscInt *rk)
Single source of truth for "which staggered momentum rows are unknowns".
PetscErrorCode BoundaryCondition_Create(BCHandlerType handler_type, BoundaryCondition **new_bc_ptr)
(Private) Creates and configures a specific BoundaryCondition handler object.
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent component-staggered vector fields.
PetscErrorCode ApplyBoundaryConditions(UserCtx *user)
Main boundary-condition orchestrator executed during solver timestepping.
PetscErrorCode BoundarySystem_Destroy(UserCtx *user)
Cleans up and destroys all boundary system resources.
FieldId
Compile-time identity for a catalogued Eulerian field.
PetscErrorCode InitializeEulerianState(SimCtx *simCtx)
High-level orchestrator to set the complete initial state of the Eulerian solver.
static PetscErrorCode MomentumNewtonJacobian_Register(SNES snes, MomentumNewtonJacobian *jacobian, MomentumPreconditionerEngine *engine, MomentumNewtonKrylovContext *ctx)
Registers the application orchestration callback and both SNES matrices.
static const MomentumPreconditionerModelOps frozen_momentum_point_block_ops
PetscBool aliases_jacobian_operator
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.
@ MOM_NK_PC_STRUCTURE_POINT_BLOCK
@ MOM_NK_PC_STRUCTURE_NONE
static PetscErrorCode MomentumPreconditionerEngine_Create(UserCtx *user, Mat jacobian_operator, const MomentumPreconditionerDescription *requested, MomentumPreconditionerEngine *engine)
Validates a model/structure and creates or aliases its matrix.
MomentumNewtonFiniteDifferenceMode finite_difference_mode
MomentumNewtonJacobianType type
static PetscErrorCode MomentumNewtonKrylov_ReadLinearizationConfig(MomentumNewtonJacobian *jacobian, MomentumPreconditionerDescription *description)
Reads application-owned Jacobian and preconditioner mathematics.
static PetscErrorCode MomentumPreconditionerEngine_Assemble(MomentumPreconditionerEngine *engine, UserCtx *user, Vec current_solution)
Runs model insertion, common row handling, and final assembly.
static PetscErrorCode MomentumNewtonJacobian_Destroy(MomentumNewtonJacobian *jacobian)
Destroys a partially or fully created Jacobian operator.
MomentumNewtonJacobian jacobian
MomentumPreconditionerModel model
static PetscErrorCode MomentumNewtonJacobian_Create(SNES snes, MomentumNewtonJacobian *jacobian)
Creates the selected Jacobian operator; currently PETSc MFFD only.
MomentumPreconditionerStructure structure
@ MOM_NK_JACOBIAN_FINITE_DIFFERENCE
Mat preconditioning_matrix
PetscBool owns_preconditioning_matrix
static PetscErrorCode MomentumPreconditionerEngine_Destroy(MomentumPreconditionerEngine *engine)
Destroys only a separately owned preconditioning matrix.
MomentumPreconditionerEngine preconditioning_engine
static PetscErrorCode MomentumNewtonKrylov_FormResidual(SNES snes, Vec X, Vec F, void *ctx)
Adapts a PETSc trial vector to the existing momentum residual path.
@ MOM_NK_FD_MODE_MATRIX_FREE
@ MOM_NK_PC_MODEL_FROZEN_MOMENTUM_JACOBIAN
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 const char * fixed_wall_bcs
static PetscErrorCode TestRestartAndContinuationSolve(void)
Verifies restarted Newton solves with both supported preconditioners.
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 TestUnsupportedConfigurationFailsBeforeAllocation(void)
Confirms unsupported features fail before workspace allocation.
static PetscErrorCode TestPeriodicOperatorHasNoZeroRows(void)
Audits every row of a complete operator containing periodic duplicates.
#define MomentumSolver_NewtonKrylov_Destroy
static PetscErrorCode TestSmallSolveAndRollback(void)
Exercises a converged solve, a committed unconverged solve, a non-finite rollback,...
static const char * periodic_xy_bcs
static PetscErrorCode TestConstraintRows(void)
Verifies fixed, periodic-duplicate, and interior residual rows.
static const char * periodic_xyz_bcs
static const char * periodic_x_bcs
static const char * periodic_y_bcs
#define MomentumSolver_NewtonKrylov
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 const char * periodic_z_bcs
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.
int main(int argc, char **argv)
Runs the focused Newton–Krylov unit suite.
static PetscErrorCode WriteNewtonPicSlice(const char *path)
Writes one static 5x5 PICSLICE profile used by the full runtime fixture.
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 CheckFlatChannelStartup(PetscBool use_point_block)
Exercises the straight-duct BDF1 startup path used by flat_channel.
static PetscBool OwnsStoredPoint(UserCtx *user, PetscInt i, PetscInt j, PetscInt k)
Returns whether this rank owns one global DMDA grid point.
static PetscErrorCode CheckSingleAxisPeriodicDerivatives(const char *bcs, PetscInt axis)
Checks one periodic configuration's endpoint derivatives on every component.
static PetscErrorCode TestResidualRepeatabilityAndInputIntegrity(void)
Verifies repeatable callback output and read-only trial input.
static PetscErrorCode TestTwoDSolveHoldsMaskedComponent(void)
A TwoD solve converges, preconditioned and not, and holds the masked component.
static PetscErrorCode TestPeriodicConstraintDerivativesAndIntersections(void)
Proves single-, double-, triple-, and mixed-boundary periodic equations.
static PetscErrorCode TestStencilToGlobal(UserCtx *user, MatStencil stencil, PetscInt *global_index)
Converts an in-domain or periodic-ghost DMDA stencil to PETSc ordering.
static PetscErrorCode AssertExactPointBlockMatrixAllocation(UserCtx *user, Mat matrix, PetscBool require_offrank_periodic)
Verifies the exact AIJ layout and preallocation derived from row classes.
static const char * geometric_periodic_bcs
static PetscErrorCode TestFreeSlipWallsSolve(void)
A Newton solve with free-slip walls converges, preconditioned and not.
static PetscErrorCode TestWholeOperatorDirectJacobian(void)
Forms the complete direct FD Jacobian, checks every row, and compares MFFD actions.
static PetscErrorCode TestLinearizationConfigParsing(void)
Verifies finalized application-owned linearization option parsing.
static PetscErrorCode TestFixedConstraintDerivativesAllFaces(void)
Proves unit derivatives for every nonperiodic stored-row category and face.
static PetscErrorCode TestPostAllocationFailureCleanup(void)
Verifies cleanup and rollback after an options failure following asset creation.
static PetscErrorCode 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 TestJacobianInterfaceAndBaselineAlias(void)
Verifies Jacobian creation/registration and baseline alias ownership.
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 TestPointBlockPeriodicAssembly(void)
Exercises engine-owned periodic duplicate rows on every MPI layout.
static const char * parabolic_bcs
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 TestMatrixFreeDerivative(void)
Compares PETSc's matrix-free action with direct differencing.
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 const char * free_slip_wall_bcs
static PetscErrorCode DestroyNewtonFixture(SimCtx **simCtx, char *tmpdir)
Destroys a Newton test fixture and its temporary files.
static PetscErrorCode TestZeroIterationStructuredLogging(void)
Verifies the six-wall zero-velocity case logs zero Newton/Krylov work.
static PetscErrorCode BuildMinimalWallOperatorFixture(SimCtx **simCtx, UserCtx **user, PetscBool x_periodic)
Builds a compact all-wall operator fixture through real boundary handlers.
static PetscErrorCode CheckResidualRepeatabilityForBC(const char *bcs, const char *label)
Checks callback repeatability, diagnostic-state independence, and X integrity.
static PetscErrorCode TestInletOutletConstraintDerivatives(void)
Proves admitted inlet and outlet face-normal rows have unit self derivatives.
PetscErrorCode PicurvMakeTempDir(char *path, size_t path_len)
Creates a unique temporary directory for one test case.
PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
PetscErrorCode PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
Destroys minimal SimCtx/UserCtx fixtures and all owned PETSc objects.
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.
PetscErrorCode PicurvDestroyRuntimeContext(SimCtx **simCtx_ptr)
Finalizes and frees a runtime context built by PicurvBuildTinyRuntimeContext.
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.
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.
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.
PetscErrorCode PicurvRemoveTempDir(const char *path)
Recursively removes a temporary directory created by PicurvMakeTempDir.
Shared declarations for the PICurv C test fixture and assertion layer.
Named test case descriptor consumed by PicurvRunTests.
PetscBool mom_nk_monitor_history
BoundaryFaceConfig boundary_faces[6]
PetscBool mom_last_converged
@ BC_HANDLER_PERIODIC_GEOMETRIC
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
BCHandlerType handler_type
PetscInt rotatefsi
Refused at setup: immersed boundaries and moving bodies are not implemented.
@ MOMENTUM_SOLVER_NEWTON_KRYLOV
char log_dir[PETSC_MAX_PATH_LEN]
struct MomentumNewtonKrylovContext * momentumNewtonKrylov
PetscInt rotateframe
moveframe/rotateframe are refused at setup.
BCFace
Identifies the six logical faces of a structured computational block.
A 3D point or vector with PetscScalar components.
The master context for the entire simulation.
User-defined context containing data specific to a single computational grid level.