14#define MomentumSolver_NewtonKrylov MomentumSolver_NewtonKrylov_PrivateCopy
15#include "../../src/momentum_newton_krylov.c"
16#undef MomentumSolver_NewtonKrylov
19 "-Xi PERIODIC geometric\n"
20 "+Xi PERIODIC geometric\n"
23 "-Zeta INLET constant_velocity vx=0.0 vy=0.0 vz=1.5\n"
24 "+Zeta OUTLET conservation\n";
32 "+Zeta WALL noslip\n";
39 "-Zeta INLET parabolic v_max=1.5\n"
40 "+Zeta OUTLET conservation\n";
43 "-Xi PERIODIC geometric\n+Xi PERIODIC geometric\n"
44 "-Eta WALL noslip\n+Eta WALL noslip\n-Zeta WALL noslip\n+Zeta WALL noslip\n";
47 "-Xi WALL noslip\n+Xi WALL noslip\n"
48 "-Eta PERIODIC geometric\n+Eta PERIODIC geometric\n-Zeta WALL noslip\n+Zeta WALL noslip\n";
51 "-Xi WALL noslip\n+Xi WALL noslip\n-Eta WALL noslip\n+Eta WALL noslip\n"
52 "-Zeta PERIODIC geometric\n+Zeta PERIODIC geometric\n";
55 "-Xi PERIODIC geometric\n+Xi PERIODIC geometric\n"
56 "-Eta PERIODIC geometric\n+Eta PERIODIC geometric\n"
57 "-Zeta WALL noslip\n+Zeta WALL noslip\n";
60 "-Xi PERIODIC geometric\n+Xi PERIODIC geometric\n"
61 "-Eta PERIODIC geometric\n+Eta PERIODIC geometric\n"
62 "-Zeta PERIODIC geometric\n+Zeta PERIODIC geometric\n";
66 const char *needle_a,
const char *needle_b)
71 PetscBool found_a = needle_a ? PETSC_FALSE : PETSC_TRUE;
72 PetscBool found_b = needle_b ? PETSC_FALSE : PETSC_TRUE;
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;
85 if (expected_rows >= 0) {
87 "Newton log must contain one nonduplicated row per solve"));
90 "enabled Newton history must contain the expected iteration rows"));
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);
107 char *tmpdir,
size_t tmpdir_len)
109 PetscFunctionBeginUser;
114 "Newton fixture must enter with no persistent Rhs workspace"));
115 PetscFunctionReturn(PETSC_SUCCESS);
126 PetscFunctionBeginUser;
129 PetscFunctionReturn(PETSC_SUCCESS);
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.");
152 PetscFunctionReturn(PETSC_SUCCESS);
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;
170 PetscFunctionBeginUser;
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));
195 PetscCall(VecSet(user->
Ucat, 7.0));
196 PetscCall(VecSet(user->
lUcat, 7.0));
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);
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));
217 PetscFunctionReturn(PETSC_SUCCESS);
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);
240 PetscInt k, PetscInt component, PetscScalar delta)
244 PetscFunctionBeginUser;
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));
252 PetscFunctionReturn(PETSC_SUCCESS);
267 PetscInt k, PetscInt component, PetscScalar *value)
270 PetscScalar local = 0.0;
272 PetscFunctionBeginUser;
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));
278 PetscCallMPI(MPI_Allreduce(&local, value, 1, MPIU_SCALAR, MPIU_SUM, PETSC_COMM_WORLD));
279 PetscFunctionReturn(PETSC_SUCCESS);
298 PetscInt row_i, PetscInt row_j, PetscInt row_k,
299 PetscInt row_component, PetscInt col_i, PetscInt col_j,
300 PetscInt col_k, PetscInt col_component,
301 PetscReal *derivative)
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;
308 PetscFunctionBeginUser;
309 PetscCall(VecDuplicate(x, &f0));
310 PetscCall(VecDuplicate(x, &fp));
311 PetscCall(VecDuplicate(x, &xp));
314 PetscCall(VecCopy(x, xp));
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);
344 PetscInt row_i, PetscInt row_j, PetscInt row_k,
345 PetscInt row_component, PetscInt col_i, PetscInt col_j,
346 PetscInt col_k, PetscInt col_component,
347 PetscReal expected, PetscReal tolerance,
const char *label)
349 PetscReal derivative = 0.0;
351 PetscFunctionBeginUser;
353 col_i, col_j, col_k, col_component, &derivative));
355 PetscFunctionReturn(PETSC_SUCCESS);
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];
365 PetscFunctionBeginUser;
375 PetscCall(PetscSNPrintf(profile_path,
sizeof(profile_path),
"%s/inlet.picslice", profile_dir));
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));
382 PetscFunctionReturn(PETSC_SUCCESS);
390 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
391 Vec x = NULL, f = NULL;
392 Cmpnts ***xa = NULL, ***fa = NULL, ***conditioned = NULL, ***rhs = NULL;
395 PetscFunctionBeginUser;
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;
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));
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) {
418 "periodic duplicate row must be Xdup-Xrep"));
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) {
423 fa[2][0][2].y, 1.0e-12,
424 "fixed wall row must be X minus conditioned boundary value"));
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) {
430 "unconstrained interior row must retain the physical residual"));
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));
437 PetscCall(VecDestroy(&f));
438 PetscCall(VecDestroy(&x));
439 PetscCall(VecDestroy(&user->
Rhs));
441 PetscFunctionReturn(PETSC_SUCCESS);
449 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
451 const PetscInt size[3] = {7, 7, 7};
453 PetscFunctionBeginUser;
455 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
456 PetscCall(VecDuplicate(user->
Ucont, &x));
457 PetscCall(VecSet(x, 0.2));
459 for (PetscInt axis = 0; axis < 3; ++axis) {
460 PetscInt coord[3] = {2, 2, 2}, ri, rj, rk;
461 PetscInt tangent = (axis + 1) % 3;
467 "negative face normal row classification"));
469 coord[0], coord[1], coord[2], axis, 1.0, 1.0e-8,
470 "negative face normal fixed derivative"));
474 "negative face tangential row classification"));
476 coord[0], coord[1], coord[2], tangent, 1.0, 1.0e-8,
477 "negative face tangential homogeneous derivative"));
479 coord[axis] = size[axis] - 2;
482 "positive physical normal row classification"));
484 coord[0], coord[1], coord[2], axis, 1.0, 1.0e-8,
485 "positive physical normal fixed derivative"));
488 "positive physical tangential row classification"));
490 coord[axis] = size[axis] - 1;
491 for (PetscInt component = 0; component < 3; ++component) {
494 "positive dummy row classification"));
496 coord[0], coord[1], coord[2], component, 1.0, 1.0e-8,
497 "positive dummy homogeneous derivative"));
501 PetscCall(VecDestroy(&x));
502 PetscCall(VecDestroy(&user->
Rhs));
504 PetscFunctionReturn(PETSC_SUCCESS);
512 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
515 PetscFunctionBeginUser;
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"));
537 PetscReal d0 = 0.0, d1 = 0.0;
539 2, 2, user->
info.mz - 2, 2, &d0));
541 2, 2, user->
info.mz - 2, 2, &d1));
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.",
548 PetscCall(VecDestroy(&x));
549 PetscCall(VecDestroy(&user->
Rhs));
551 PetscFunctionReturn(PETSC_SUCCESS);
564 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
566 PetscInt size[3], dup[3] = {2, 2, 2}, rep[3] = {2, 2, 2}, unrelated[3] = {3, 3, 3};
568 PetscFunctionBeginUser;
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) {
579 dup[0], dup[1], dup[2], component, 1.0, 1.0e-8,
580 "periodic duplicate self derivative"));
582 rep[0], rep[1], rep[2], component, -1.0, 1.0e-8,
583 "periodic representative derivative"));
585 unrelated[0], unrelated[1], unrelated[2], (component + 1) % 3, 0.0, 1.0e-8,
586 "periodic constraint unrelated derivative"));
589 PetscCall(VecDestroy(&x));
590 PetscCall(VecDestroy(&user->
Rhs));
592 PetscFunctionReturn(PETSC_SUCCESS);
600 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
601 Vec x = NULL, f = NULL;
603 PetscScalar xdup, synced, residual;
605 PetscFunctionBeginUser;
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));
617 PetscCall(VecCopy(x, user->
Ucont));
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"));
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));
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"));
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));
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"));
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));
657 PetscFunctionReturn(PETSC_SUCCESS);
665 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
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;
672 PetscFunctionBeginUser;
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));
687 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
688 PetscCall(SNESSetDM(snes, user->
fda));
690 PetscCall(MatCreateSNESMF(snes, &J));
692 PetscCall(MatMFFDSetBase(J, x, f0));
693 PetscCall(MatMult(J, v, jv));
694 PetscCall(MatMFFDGetH(J, &h));
695 PetscCall(VecWAXPY(xp, h, v, x));
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"));
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));
716 PetscFunctionReturn(PETSC_SUCCESS);
727 PetscBool x_periodic)
729 PetscFunctionBeginUser;
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;
737 (*simCtx)->invicid = 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 &&
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 ?
752 &(*user)->boundary_faces[face].handler));
754 PetscFunctionReturn(PETSC_SUCCESS);
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;
774 PetscFunctionBeginUser;
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)));
795 PetscCall(VecRestoreArray(v[which], &a));
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)))
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));
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));
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).",
821 PetscCall(
CheckStoredDerivative(user, x, 2, 2, 2, 0, 2, 2, 2, 0,
822 10.0, 1.0e-5,
"interior BDF1 temporal diagonal"));
824 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
825 PetscCall(SNESSetDM(snes, user->
fda));
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);
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));
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);
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);
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));
866 PetscFunctionReturn(PETSC_SUCCESS);
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;
881 PetscFunctionBeginUser;
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));
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));
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));
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).",
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));
910 PetscFunctionReturn(PETSC_SUCCESS);
918 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
919 Vec entry = NULL, delta = NULL;
920 PetscErrorCode solve_ierr;
921 PetscReal norm = 0.0;
923 char summary_path[PETSC_MAX_PATH_LEN];
924 char history_path[PETSC_MAX_PATH_LEN];
926 PetscFunctionBeginUser;
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"));
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:"));
945 PetscCall(VecSet(user->
Ucont, 0.2));
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));
954 PetscCall(PetscPopErrorHandler());
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));
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"));
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));
972 PetscFunctionReturn(PETSC_SUCCESS);
980 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
981 char summary_path[PETSC_MAX_PATH_LEN];
984 PetscFunctionBeginUser;
986 PetscCall(VecZeroEntries(user->
Ucont));
987 PetscCall(VecZeroEntries(user->
Ucont_o));
988 PetscCall(VecZeroEntries(user->
Ucont_rm1));
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"));
997 PetscFunctionReturn(PETSC_SUCCESS);
1013 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1014 Vec x = NULL, f = NULL;
1015 PetscReal initial_norm = 0.0;
1018 PetscFunctionBeginUser;
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));
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));
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",
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"));
1053 PetscFunctionReturn(PETSC_SUCCESS);
1059 PetscFunctionBeginUser;
1062 PetscFunctionReturn(PETSC_SUCCESS);
1070 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1073 PetscFunctionBeginUser;
1075 PetscCall(VecSet(user->
Ucont, 0.2));
1076 PetscCall(VecZeroEntries(user->
Ucont_o));
1077 PetscCall(VecZeroEntries(user->
Ucont_rm1));
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"));
1091 "PCNONE Newton Krylov checkpoint restart must converge"));
1096 PetscCall(VecSet(user->
Ucont, 0.2));
1097 PetscCall(VecZeroEntries(user->
Ucont_o));
1098 PetscCall(VecZeroEntries(user->
Ucont_rm1));
1101 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
1102 "frozen_momentum_jacobian"));
1103 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"point_block"));
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"));
1117 PetscFunctionReturn(PETSC_SUCCESS);
1125 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1126 PetscErrorCode solve_ierr;
1128 PetscFunctionBeginUser;
1131 PetscInt *unsupported_flags[] = {
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));
1140 PetscCall(PetscPopErrorHandler());
1142 "unsupported Newton feature flag must fail"));
1144 "feature validation must precede workspace allocation"));
1145 *unsupported_flags[flag] = 0;
1149 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1151 PetscCall(PetscPopErrorHandler());
1152 PetscCall(
PicurvAssertBool((PetscBool)(solve_ierr != PETSC_SUCCESS),
"multiblock must fail"));
1154 PetscCall(VecSet(user->
Nvert, 1.0));
1155 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, 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));
1164 PetscCall(PetscPopErrorHandler());
1166 "driven constant-flux controller must fail"));
1169 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1171 PetscCall(PetscPopErrorHandler());
1173 "unimplemented interpolated-file inlet must fail"));
1175 "all validation failures must precede workspace allocation"));
1177 PetscFunctionReturn(PETSC_SUCCESS);
1185 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1186 Vec entry = NULL, delta = NULL;
1187 PetscErrorCode solve_ierr;
1188 PetscReal norm = 0.0;
1191 PetscFunctionBeginUser;
1193 PetscCall(VecSet(user->
Ucont, 0.2));
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));
1201 PetscCall(PetscPopErrorHandler());
1202 PetscCall(PetscOptionsClearValue(NULL,
"-mom_nk_pc_type"));
1204 "non-PCNONE option must fail after setup"));
1206 "post-allocation failure must destroy Rhs"));
1207 PetscCall(VecWAXPY(delta, -1.0, entry, user->
Ucont));
1208 PetscCall(VecNorm(delta, NORM_INFINITY, &norm));
1210 "post-allocation failure must restore canonical entry"));
1211 PetscCall(VecDestroy(&delta)); PetscCall(VecDestroy(&entry));
1213 PetscFunctionReturn(PETSC_SUCCESS);
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"
1229 PetscFunctionBeginUser;
1230 for (
size_t n = 0; n <
sizeof(option_names) /
sizeof(option_names[0]); ++n)
1231 PetscCall(PetscOptionsClearValue(NULL, option_names[n]));
1235 "default Jacobian type must be finite difference"));
1238 "default finite-difference mode must be matrix free"));
1240 "default preconditioner model must be none"));
1242 "default preconditioner structure must be none"));
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"));
1250 "explicit baseline Jacobian type must parse"));
1253 "explicit baseline finite-difference mode must parse"));
1255 "explicit baseline preconditioner model must parse"));
1257 "explicit baseline preconditioner structure must parse"));
1259 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
1260 "frozen_momentum_jacobian"));
1261 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"point_block"));
1264 "explicit Jacobian type must parse"));
1267 "explicit finite-difference mode must parse"));
1269 description.
model,
"frozen model must parse"));
1271 description.
structure,
"point-block structure must parse"));
1273 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_type",
"analytic"));
1274 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1276 PetscCall(PetscPopErrorHandler());
1278 "unsupported Jacobian type must fail"));
1279 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_type",
"finite_difference"));
1281 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_fd_mode",
"colored_sparse"));
1282 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1284 PetscCall(PetscPopErrorHandler());
1286 "unsupported finite-difference mode must fail"));
1287 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_jacobian_fd_mode",
"matrix_free"));
1289 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"none"));
1290 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1292 PetscCall(PetscPopErrorHandler());
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));
1299 PetscCall(PetscPopErrorHandler());
1301 "none model with point block must fail"));
1303 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_model",
"diagonal"));
1304 PetscCall(PetscOptionsSetValue(NULL,
"-mom_nk_preconditioner_structure",
"none"));
1305 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1307 PetscCall(PetscPopErrorHandler());
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));
1315 PetscCall(PetscPopErrorHandler());
1317 "unsupported preconditioner structure must fail"));
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);
1335 return metric.
x * metric.
x + metric.
y * metric.
y + metric.
z * metric.
z;
1346 const PetscReal ***aj, PetscInt i, PetscInt j, PetscInt k, PetscInt flags,
1347 PetscScalar block[9])
1349 PetscReal A[6][4] = {{0.0}};
1350 const PetscReal dtc = ((simCtx->
step != simCtx->
StartStep) && simCtx->
step != 1 ? 1.5 : 1.0) /
1353 0.5 * (aj[k][j][i] + aj[k][j][i + 1]);
1355 0.5 * (aj[k][j][i] + aj[k][j + 1][i]);
1357 0.5 * (aj[k][j][i] + aj[k + 1][j][i]);
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);
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);
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);
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);
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);
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);
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];
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);
1440 Cmpnts ***u = NULL, ***csi = NULL, ***eta = NULL, ***zet = NULL;
1441 PetscReal ***aj = NULL;
1442 DMDALocalInfo info = user->
info;
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};
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);
1487 PetscInt k, PetscInt flags, PetscScalar block[9])
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};
1497 PetscFunctionBeginUser;
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));
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));
1513 PetscCallMPI(MPI_Allreduce(&owns, &owners, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
1515 PetscCallMPI(MPI_Allreduce(local, block, 9, MPIU_SCALAR, MPI_SUM, PETSC_COMM_WORLD));
1516 PetscFunctionReturn(PETSC_SUCCESS);
1521 PetscInt *global_index)
1523 ISLocalToGlobalMapping local_to_global = NULL;
1524 PetscInt ghost_starts[3], ghost_sizes[3], local_index;
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]));
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,
1542 PetscFunctionReturn(PETSC_SUCCESS);
1547 Mat preconditioning_matrix, MatStencil row,
1548 MatStencil col, PetscScalar *value)
1550 Vec column_basis = NULL, row_basis = NULL, product = 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);
1578 UserCtx *user, Mat matrix, PetscBool require_offrank_periodic)
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;
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));
1599 "point-block global row dimension"));
1601 "point-block global column dimension"));
1603 "point-block local row dimension"));
1605 "point-block local column dimension"));
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));
1611 comm_size == 1 ? is_seq_aij : is_mpi_aij,
1612 "point-block matrix must use the expected AIJ implementation"));
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);
1624 PetscInt representative;
1626 (MatStencil){.i = ri, .j = rj, .k = rk, .c = component},
1628 if (representative < ownership_start || representative >= ownership_end)
1629 ++offrank_periodic_local;
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));
1642 (PetscReal)matrix_info.nz_allocated, 0.0,
1643 "point-block matrix must allocate exactly the classified scalar pattern"));
1645 (PetscReal)matrix_info.nz_used, 0.0,
1646 "point-block assembly must insert every classified structural entry"));
1648 "point-block insertion must not reallocate matrix storage"));
1649 if (require_offrank_periodic && comm_size > 1)
1651 "MPI periodic fixture must exercise off-rank preallocation"));
1652 PetscFunctionReturn(PETSC_SUCCESS);
1663 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
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}
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;
1687 Vec direction = NULL, product = NULL, px = NULL;
1688 PetscScalar px_sum = 0.0;
1689 PetscReal px_norm = 0.0;
1692 Vec pc_rhs = NULL, pc_solution = NULL;
1693 MatInfo initial_allocation_info, repeated_allocation_info;
1695 PetscFunctionBeginUser;
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));
1706 "engine must select the frozen-momentum model callbacks"));
1708 "point-block engine must own its separate matrix"));
1710 "point-block matrix must not alias the Jacobian operator"));
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));
1718 "model callback and common rows must insert matrix entries"));
1719 PetscCall(MatGetBlockSize(preconditioning_matrix, &block_size));
1721 PetscCall(DMDAGetInfo(user->
fda, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1722 &velocity_dof, NULL, NULL, NULL, NULL, NULL));
1727 for (PetscInt rr = 0; rr < 3; ++rr)
1728 for (PetscInt cc = 0; cc < 3; ++cc) {
1730 target[rr], target[cc], &values[3 * rr + cc]));
1732 PetscRealPart(values[3 * rr + cc]), 1e-13,
1733 "Cartesian point block entry must match independent oracle"));
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;
1741 conditioned, col, &conditioned_value));
1742 col = homogeneous; col.c = cc;
1744 homogeneous, col, &homogeneous_value));
1746 PetscRealPart(conditioned_value), 1e-14,
"conditioned row must be exact identity"));
1748 PetscRealPart(homogeneous_value), 1e-14,
"homogeneous row must be exact identity"));
1752 PetscCall(MatDuplicate(preconditioning_matrix, MAT_COPY_VALUES, &saved_matrix));
1753 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1754 PetscCall(SNESSetDM(snes, user->
fda));
1756 PetscCall(MatCreateSNESMF(snes, &mffd));
1757 PetscCall(VecDuplicate(x, &direction)); PetscCall(VecDuplicate(x, &product));
1758 PetscCall(VecSet(direction, 0.375));
1760 PetscCall(MatMFFDSetBase(mffd, x, f));
1761 PetscCall(MatMult(mffd, direction, product));
1763 PetscCall(MatAXPY(saved_matrix, -1.0, preconditioning_matrix, SAME_NONZERO_PATTERN));
1764 PetscCall(MatNorm(saved_matrix, NORM_FROBENIUS, &difference_norm));
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));
1772 PetscCall(VecCopy(user->
Ucont, x));
1776 for (PetscInt rr = 0; rr < 3; ++rr)
1777 for (PetscInt cc = 0; cc < 3; ++cc) {
1779 target[rr], target[cc], &values[3 * rr + cc]));
1781 PetscRealPart(values[3 * rr + cc]), 1e-13,
1782 "nonuniform point block entry must match independent oracle"));
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"));
1789 mutant_flag <<= 1) {
1790 PetscBool differs = PETSC_FALSE;
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"));
1799 PetscCall(
PicurvAssertBool((PetscBool)(PetscAbsScalar(reference[8] - mutant[8]) > 1e-8),
1800 "A[5] must contribute to the zeta diagonal"));
1803 for (PetscInt n = 0; n < 9; ++n)
1805 1e-13,
"modern block must be the negative legacy block"));
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"));
1816 MatStencil neighbor = target[0];
1817 PetscScalar neighbor_value = 0.0;
1820 target[0], neighbor, &neighbor_value));
1822 "point block must not insert unintended neighbor entries"));
1829 for (PetscInt n = 0; n < 9; ++n) {
1831 target[n / 3], target[n % 3], &values[n]));
1833 1e-13,
"BDF2 point block must match independent oracle"));
1834 if (n == 0 || n == 4 || n == 8)
1836 PetscRealPart(mutant[n] - reference[n]), 1e-10,
"BDF2 diagonal increment"));
1839 1e-13,
"BDF order must not change off-diagonal entries"));
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));
1846 "P*x global sum must be decomposition independent"));
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));
1855 PetscBool assembled = PETSC_FALSE;
1856 PetscCall(MatAssembled(preconditioning_matrix, &assembled));
1857 PetscCall(
PicurvAssertBool(assembled,
"engine must perform final matrix assembly"));
1859 PetscCall(MatNorm(preconditioning_matrix, NORM_FROBENIUS, &matrix_norm));
1860 PetscCall(MatShift(preconditioning_matrix, 7.0));
1862 PetscCall(MatGetInfo(preconditioning_matrix, MAT_GLOBAL_SUM,
1863 &repeated_allocation_info));
1865 (PetscReal)repeated_allocation_info.nz_allocated, 0.0,
1866 "repeated assembly must retain exact allocated storage"));
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));
1872 "repeated engine assembly must clear old entries"));
1874 PetscScalar value = 0.0;
1876 conditioned, conditioned, &value));
1878 "repeated engine assembly must clear old entries"));
1880 PetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
1881 PetscCall(KSPSetOperators(ksp, preconditioning_matrix, preconditioning_matrix));
1882 PetscCall(KSPGetPC(ksp, &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));
1894 "engine destroy must clear its owned matrix"));
1895 PetscCall(VecDestroy(&f)); PetscCall(VecDestroy(&x)); PetscCall(VecDestroy(&user->
Rhs));
1897 PetscFunctionReturn(PETSC_SUCCESS);
1902 MatStencil row,
const char *message)
1905 PetscInt ri, rj, rk, global_row, global_rep, lo, hi, local_checked = 0, checked = 0;
1907 const PetscInt *cols = NULL;
1908 const PetscScalar *values = NULL;
1911 PetscFunctionBeginUser;
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;
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;
1927 if (cols[n] == global_row) {
1928 found_self = PETSC_TRUE;
1930 "periodic row self entry"));
1931 }
else if (cols[n] == global_rep) {
1932 found_rep = PETSC_TRUE;
1934 "periodic row representative entry"));
1936 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB,
1937 "Periodic row contains an unintended nonzero column.");
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));
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);
1956 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
1962 Mat jacobian_operator = NULL, preconditioning_matrix = 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;
1971 PetscFunctionBeginUser;
1973 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
1974 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1975 PetscCall(SNESSetDM(snes, user->
fda));
1982 PetscCall(PetscStrcmp(jacobian_prefix,
"mom_nk_", &prefix_matches));
1984 "Jacobian interface must apply the application prefix"));
1989 PetscCall(SNESGetKSP(snes, &ksp));
1990 PetscCall(KSPGetPC(ksp, &pc));
1995 PetscCall(PCGetType(pc, &pc_type));
1996 PetscCall(PetscStrcmp(pc_type, PCNONE, &pc_is_none));
1998 "baseline engine must derive PETSc PCNONE"));
1999 PetscCall(SNESGetJacobian(snes, &jacobian_operator, &preconditioning_matrix, NULL, NULL));
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"));
2012 "destroying an alias engine must preserve the Jacobian"));
2014 PetscCall(SNESDestroy(&snes));
2015 PetscCall(VecDestroy(&user->
Rhs));
2017 PetscFunctionReturn(PETSC_SUCCESS);
2025 char tmpdir[PETSC_MAX_PATH_LEN] =
"";
2033 Vec x = NULL, f = NULL;
2034 PetscReal matrix_norm = 0.0;
2036 PetscFunctionBeginUser;
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));
2050 NORM_FROBENIUS, &matrix_norm));
2052 "periodic point-block assembly must produce a nonzero matrix"));
2055 (MatStencil){.i = 0, .j = 2, .k = 3, .c = 0},
2056 "single-axis periodic row must contain exact +1/-1 entries"));
2059 (MatStencil){.i = 0, .j = 0, .k = 3, .c = 1},
2060 "periodic intersection must contain exact +1/-1 entries"));
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));
2070 PetscFunctionReturn(PETSC_SUCCESS);
2081 PetscErrorCode ierr;
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;
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 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.
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
MomentumNewtonKrylovRowType
@ MOM_NK_ROW_PERIODIC_DUPLICATE
@ MOM_NK_ROW_FIXED_HOMOGENEOUS
@ MOM_NK_ROW_FIXED_CONDITIONED
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 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 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.
static PetscErrorCode TestSmallSolveAndRollback(void)
Exercises a converged solve, forced rollback, and per-call cleanup.
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 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 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.
static const char * geometric_periodic_bcs
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 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 TestMatrixFreeDerivative(void)
Compares PETSc's matrix-free action with direct differencing.
static PetscReal LegacyOracleMetricNormSquared(Cmpnts metric)
Test-owned metric norm used by the independent legacy transcription.
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.
@ ORACLE_CENTER_TRANSVERSE_METRICS
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]
SimCtx * simCtx
Back-pointer to the master simulation context.
PetscBool mom_last_converged
@ BC_HANDLER_PERIODIC_GEOMETRIC
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
@ BC_HANDLER_INLET_INTERP_FROM_FILE
BCHandlerType handler_type
@ MOMENTUM_SOLVER_NEWTON_KRYLOV
char log_dir[PETSC_MAX_PATH_LEN]
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.