18 PetscFunctionBeginUser;
19 if (!user->
Phi) PetscCall(DMCreateGlobalVector(user->
da, &user->
Phi));
20 if (!user->
lPhi) PetscCall(DMCreateLocalVector(user->
da, &user->
lPhi));
21 if (!user->
Aj) PetscCall(DMCreateGlobalVector(user->
da, &user->
Aj));
22 if (!user->
lAj) PetscCall(DMCreateLocalVector(user->
da, &user->
lAj));
25 PetscFunctionReturn(0);
36 PetscFunctionBeginUser;
39 PetscCall(VecSet(user->
P, 1.0));
40 PetscCall(VecSet(user->
Phi, 0.25));
46 PetscFunctionReturn(0);
58 PetscFunctionBeginUser;
63 PetscCall(VecSet(user->
Ucont, 0.0));
64 PetscCall(VecSet(user->
Nvert, 0.0));
65 PetscCall(VecSet(user->
Aj, 1.0));
66 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
67 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
68 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
69 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
70 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Aj, INSERT_VALUES, user->
lAj));
71 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Aj, INSERT_VALUES, user->
lAj));
73 PetscCall(VecDuplicate(user->
P, &B));
75 PetscCall(
PicurvAssertVecConstant(B, 0.0, 1.0e-12,
"zero velocity divergence should produce zero Poisson RHS"));
78 PetscCall(VecDestroy(&B));
80 PetscFunctionReturn(0);
96 PetscFunctionBeginUser;
98 PetscCall(VecDuplicate(user->
Ucont, &rct));
99 PetscCall(VecZeroEntries(rct));
111 PetscCall(VecSet(user->
Nvert, 0.0));
112 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
113 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
114 PetscCall(DMDAVecGetArray(user->
fda, user->
lCsi, &l_csi));
115 PetscCall(DMDAVecGetArray(user->
fda, user->
lEta, &l_eta));
116 PetscCall(DMDAVecGetArray(user->
fda, user->
lZet, &l_zet));
117 l_csi[1][1][1].
x = 1.0; l_csi[1][1][1].
y = 0.0; l_csi[1][1][1].
z = 0.0;
118 l_eta[1][1][1].
x = 0.0; l_eta[1][1][1].
y = 1.0; l_eta[1][1][1].
z = 0.0;
119 l_zet[1][1][1].
x = 0.0; l_zet[1][1][1].
y = 0.0; l_zet[1][1][1].
z = 1.0;
120 PetscCall(DMDAVecRestoreArray(user->
fda, user->
lZet, &l_zet));
121 PetscCall(DMDAVecRestoreArray(user->
fda, user->
lEta, &l_eta));
122 PetscCall(DMDAVecRestoreArray(user->
fda, user->
lCsi, &l_csi));
125 PetscCall(DMDAVecGetArrayRead(user->
fda, rct, &rct_arr));
127 "ComputeBodyForces should update the driven-flow controller magnitude"));
128 PetscCall(
PicurvAssertRealNear(0.0, rct_arr[1][1][1].y, 1.0e-12,
"ComputeBodyForces should keep y unchanged"));
129 PetscCall(
PicurvAssertRealNear(0.0, rct_arr[1][1][1].z, 1.0e-12,
"ComputeBodyForces should keep z unchanged"));
130 PetscCall(DMDAVecRestoreArrayRead(user->
fda, rct, &rct_arr));
132 PetscCall(VecDestroy(&rct));
134 PetscFunctionReturn(0);
144 PetscReal ***diff_arr = NULL;
146 PetscFunctionBeginUser;
152 simCtx->
les = PETSC_FALSE;
156 PetscCall(DMDAVecGetArrayRead(user->
da, user->
Diffusivity, &diff_arr));
157 PetscCall(
PicurvAssertRealNear(0.125, diff_arr[1][1][1], 1.0e-12,
"molecular diffusivity interior value"));
158 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
Diffusivity, &diff_arr));
161 PetscFunctionReturn(0);
173 PetscFunctionBeginUser;
175 PetscCall(VecDuplicate(user->
lUcont, &conv));
176 PetscCall(VecSet(user->
Ucont, 0.0));
177 PetscCall(VecSet(user->
Ucat, 0.0));
178 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
179 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
180 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
181 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
186 PetscCall(VecDestroy(&conv));
188 PetscFunctionReturn(0);
200 PetscFunctionBeginUser;
202 PetscCall(VecDuplicate(user->
lUcont, &visc));
203 PetscCall(VecSet(user->
Ucont, 1.0));
204 PetscCall(VecSet(user->
Ucat, 1.0));
205 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
206 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
207 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
208 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
213 PetscCall(VecDestroy(&visc));
215 PetscFunctionReturn(0);
226 Vec jacobians[4] = {user->
Aj, user->
IAj, user->
JAj, user->
KAj};
229 PetscFunctionBeginUser;
230 for (PetscInt v = 0; v < 12; ++v) {
231 PetscCall(VecScale(area_vectors[v], h * h));
232 PetscCall(DMGlobalToLocalBegin(user->
fda, area_vectors[v], INSERT_VALUES, local_area[v]));
233 PetscCall(DMGlobalToLocalEnd(user->
fda, area_vectors[v], INSERT_VALUES, local_area[v]));
235 for (PetscInt v = 0; v < 4; ++v) {
236 PetscCall(VecSet(jacobians[v], 1.0 / (h * h * h)));
237 PetscCall(DMGlobalToLocalBegin(user->
da, jacobians[v], INSERT_VALUES, local_jac[v]));
238 PetscCall(DMGlobalToLocalEnd(user->
da, jacobians[v], INSERT_VALUES, local_jac[v]));
240 PetscFunctionReturn(0);
256 Vec molecular = NULL, with_clark = NULL;
257 Cmpnts ***ucat = NULL, ***visc0 = NULL, ***visc1 = NULL;
258 const PetscInt n = 10, mid = n / 2;
260 PetscFunctionBeginUser;
266 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
267 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k)
268 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j)
269 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
270 const PetscReal x = (i - 0.5) * h;
271 ucat[k][j][i].
x = x * x; ucat[k][j][i].
y = 0.0; ucat[k][j][i].
z = 0.0;
273 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
274 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
275 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
276 PetscCall(VecSet(user->
lUcont, 0.0));
278 PetscCall(VecDuplicate(user->
lUcont, &molecular));
279 PetscCall(VecDuplicate(user->
lUcont, &with_clark));
285 PetscCall(DMDAVecGetArrayRead(user->
fda, molecular, &visc0));
286 PetscCall(DMDAVecGetArrayRead(user->
fda, with_clark, &visc1));
288 const PetscReal v_molecular = visc0[mid][mid][probe].x;
289 const PetscReal v_clark = visc1[mid][mid][probe].x - v_molecular;
290 PetscCheck(PetscAbsReal(v_molecular) > 0.0, PETSC_COMM_SELF, PETSC_ERR_PLIB,
291 "The molecular viscous term vanished at the probe; the fixture is wrong.");
292 *ratio_per_x = (v_clark / v_molecular) / ((probe - 0.5) * h);
294 PetscCall(DMDAVecRestoreArrayRead(user->
fda, with_clark, &visc1));
295 PetscCall(DMDAVecRestoreArrayRead(user->
fda, molecular, &visc0));
296 PetscCall(VecDestroy(&with_clark));
297 PetscCall(VecDestroy(&molecular));
299 PetscFunctionReturn(0);
313 PetscReal coarse = 0.0, fine = 0.0;
315 PetscFunctionBeginUser;
321 "the gradient model removes momentum where du/dx grows along x"));
323 "halving the filter width divides the gradient-model force by four"));
329 "gradient-model force matches (Delta^2/12) du/dx du/dx at h = 0.2"));
331 "gradient-model force matches (Delta^2/12) du/dx du/dx at h = 0.1"));
332 PetscFunctionReturn(0);
344 PetscFunctionBeginUser;
346 PetscCall(VecSet(user->
Ucont, 0.0));
347 PetscCall(VecSet(user->
Ucat, 0.0));
348 PetscCall(VecSet(user->
Nvert, 0.0));
349 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
350 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
351 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
352 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
353 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
354 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
357 PetscCall(
PicurvAssertVecConstant(user->
Rhs, 0.0, 1.0e-12,
"ComputeRHS should remain zero without forcing on a quiescent field"));
360 PetscFunctionReturn(0);
371 PetscFunctionBeginUser;
381 PetscFunctionReturn(0);
391 PetscReal ***diff_arr = NULL;
392 Cmpnts ***grad_arr = NULL;
394 const PetscReal gamma0 = 0.5;
395 const PetscReal slope_x = 0.25;
397 PetscFunctionBeginUser;
407 "verification diffusivity override should report as active"));
411 PetscCall(DMDAVecGetArrayRead(user->
da, user->
Diffusivity, &diff_arr));
412 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Cent, ¢));
414 "verification override should populate the linear diffusivity field"));
415 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Cent, ¢));
416 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
Diffusivity, &diff_arr));
420 "linear verification diffusivity should yield the current finite-difference x gradient"));
422 "linear verification diffusivity should keep y gradient zero"));
424 "linear verification diffusivity should keep z gradient zero"));
428 PetscFunctionReturn(0);
437 PetscInt rows = 0, cols = 0;
439 const PetscInt *col_idx = NULL;
440 const PetscScalar *values = NULL;
441 PetscInt interior_row = 0;
443 PetscFunctionBeginUser;
445 PetscCall(VecSet(user->
Nvert, 0.0));
446 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
447 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
450 PetscCall(
PicurvAssertBool((PetscBool)(user->
A != NULL),
"AssemblePoissonOperator should allocate the Poisson operator"));
452 PetscCall(MatGetSize(user->
A, &rows, &cols));
456 interior_row = (2 * user->
info.my + 2) * user->
info.mx + 2;
457 PetscCall(MatGetRow(user->
A, interior_row, &ncols, &col_idx, &values));
458 PetscCall(
PicurvAssertBool((PetscBool)(ncols > 1),
"Interior Poisson row should couple to neighboring nodes"));
459 PetscCall(
PicurvAssertBool((PetscBool)(values != NULL),
"Interior Poisson row should expose non-null coefficients"));
460 PetscCall(MatRestoreRow(user->
A, interior_row, &ncols, &col_idx, &values));
463 PetscFunctionReturn(0);
475 Vec jacobians[4] = {user->
Aj, user->
IAj, user->
JAj, user->
KAj};
479 PetscFunctionBeginUser;
480 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rnd));
481 PetscCall(PetscRandomSetInterval(rnd, 0.5, 1.5));
482 PetscCall(PetscRandomSetSeed(rnd, 2026));
483 PetscCall(PetscRandomSeed(rnd));
484 for (PetscInt v = 0; v < 9; ++v) {
485 PetscCall(VecSetRandom(face_metrics[v], rnd));
486 PetscCall(DMGlobalToLocalBegin(user->
fda, face_metrics[v], INSERT_VALUES, local_metrics[v]));
487 PetscCall(DMGlobalToLocalEnd(user->
fda, face_metrics[v], INSERT_VALUES, local_metrics[v]));
489 for (PetscInt v = 0; v < 4; ++v) {
490 PetscCall(VecSetRandom(jacobians[v], rnd));
491 PetscCall(DMGlobalToLocalBegin(user->
da, jacobians[v], INSERT_VALUES, local_jac[v]));
492 PetscCall(DMGlobalToLocalEnd(user->
da, jacobians[v], INSERT_VALUES, local_jac[v]));
494 PetscCall(PetscRandomDestroy(&rnd));
495 PetscFunctionReturn(0);
510 Vec B = NULL, APhi = NULL;
513 const PetscReal ***b, ***aphi;
514 PetscReal worst = 0.0;
516 PetscFunctionBeginUser;
522 PetscCall(VecSet(user->
Nvert, 0.0));
523 PetscCall(DMDAVecGetArray(user->
da, user->
Nvert, &nvert));
524 if (user->
info.xs <= 3 && 3 < user->
info.xs + user->
info.xm &&
525 user->
info.ys <= 3 && 3 < user->
info.ys + user->
info.ym &&
526 user->
info.zs <= 3 && 3 < user->
info.zs + user->
info.zm) nvert[3][3][3] = 1.0;
527 PetscCall(DMDAVecRestoreArray(user->
da, user->
Nvert, &nvert));
528 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
529 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
531 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rnd));
532 PetscCall(PetscRandomSetSeed(rnd, 7));
533 PetscCall(PetscRandomSeed(rnd));
534 PetscCall(VecSetRandom(user->
Phi, rnd));
535 PetscCall(PetscRandomDestroy(&rnd));
536 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Phi, INSERT_VALUES, user->
lPhi));
537 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Phi, INSERT_VALUES, user->
lPhi));
538 PetscCall(VecSet(user->
Ucont, 0.0));
539 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
540 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
544 PetscCall(VecDuplicate(user->
P, &B));
546 PetscCall(VecDuplicate(user->
Phi, &APhi));
547 PetscCall(MatMult(user->
A, user->
Phi, APhi));
549 PetscCall(DMDAVecGetArrayRead(user->
da, B, &b));
550 PetscCall(DMDAVecGetArrayRead(user->
da, APhi, &aphi));
551 PetscCall(DMDAVecGetArray(user->
da, user->
Nvert, &nvert));
552 for (PetscInt k = PetscMax(user->
info.zs, 1); k < PetscMin(user->
info.zs + user->
info.zm, user->
info.mz - 1); k++) {
553 for (PetscInt j = PetscMax(user->
info.ys, 1); j < PetscMin(user->
info.ys + user->
info.ym, user->
info.my - 1); j++) {
554 for (PetscInt i = PetscMax(user->
info.xs, 1); i < PetscMin(user->
info.xs + user->
info.xm, user->
info.mx - 1); i++) {
555 if (nvert[k][j][i] > 0.1)
continue;
556 worst = PetscMax(worst, PetscAbsReal(b[k][j][i] + aphi[k][j][i]) / (1.0 + PetscAbsReal(aphi[k][j][i])));
560 PetscCall(DMDAVecRestoreArray(user->
da, user->
Nvert, &nvert));
561 PetscCall(DMDAVecRestoreArrayRead(user->
da, APhi, &aphi));
562 PetscCall(DMDAVecRestoreArrayRead(user->
da, B, &b));
564 "the right-hand side of the projected flux should equal -A Phi on every fluid row"));
566 PetscCall(VecDestroy(&APhi));
567 PetscCall(VecDestroy(&B));
569 PetscFunctionReturn(0);
580 PetscFunctionBeginUser;
582 PetscCall(VecSet(user->
Ucont, 0.0));
583 PetscCall(VecSet(user->
Phi, 0.0));
584 PetscCall(VecSet(user->
Nvert, 0.0));
585 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
586 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
587 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Phi, INSERT_VALUES, user->
lPhi));
588 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Phi, INSERT_VALUES, user->
lPhi));
589 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
590 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
593 PetscCall(
PicurvAssertVecConstant(user->
Ucont, 0.0, 1.0e-12,
"ProjectVelocity should leave a zero-velocity field unchanged when Phi is zero"));
596 PetscFunctionReturn(0);
605 PetscReal ***phi = NULL;
608 PetscFunctionBeginUser;
611 PetscCall(VecSet(user->
Ucont, 0.0));
612 PetscCall(VecSet(user->
Nvert, 0.0));
613 PetscCall(DMDAVecGetArray(user->
da, user->
Phi, &phi));
614 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k) {
615 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j) {
616 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
617 phi[k][j][i] = (PetscReal)i;
621 PetscCall(DMDAVecRestoreArray(user->
da, user->
Phi, &phi));
622 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
623 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
624 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Phi, INSERT_VALUES, user->
lPhi));
625 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Phi, INSERT_VALUES, user->
lPhi));
626 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
627 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Nvert, INSERT_VALUES, user->
lNvert));
631 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
632 PetscCall(
PicurvAssertRealNear(-1.0, ucont[2][2][2].x, 1.0e-10,
"ProjectVelocity should subtract the x pressure gradient under identity metrics"));
633 PetscCall(
PicurvAssertRealNear(0.0, ucont[2][2][2].y, 1.0e-10,
"ProjectVelocity should leave the y component unchanged for an x-only gradient"));
634 PetscCall(
PicurvAssertRealNear(0.0, ucont[2][2][2].z, 1.0e-10,
"ProjectVelocity should leave the z component unchanged for an x-only gradient"));
635 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
638 PetscFunctionReturn(0);
647 DMDALocalInfo info = user->
info;
650 PetscFunctionBeginUser;
651 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucont, &ucont));
652 for (PetscInt k = PetscMax(info.zs, 1); k < PetscMin(info.zs + info.zm, info.mz - 1); k++) {
653 for (PetscInt j = PetscMax(info.ys, 1); j < PetscMin(info.ys + info.ym, info.my - 1); j++) {
654 for (PetscInt i = PetscMax(info.xs, 1); i < PetscMin(info.xs + info.xm, info.mx - 1); i++) {
655 if (i <= info.mx - 3) ucont[k][j][i].
x += amplitude * PetscSinReal(1.3 * i + 0.7 * j + 0.4 * k);
656 if (j <= info.my - 3) ucont[k][j][i].
y += amplitude * PetscCosReal(0.9 * i + 1.1 * j + 0.3 * k);
657 if (k <= info.mz - 3) ucont[k][j][i].
z += amplitude * PetscSinReal(0.5 * i + 0.8 * j + 1.7 * k);
661 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucont, &ucont));
663 PetscFunctionReturn(0);
676 char tmpdir[PETSC_MAX_PATH_LEN];
679 PetscFunctionBeginUser;
682 "-im 17\n-jm 17\n-km 17\n-mg_level 3\n"
683 "-ps_ksp_rtol 1.0e-12\n-ps_ksp_atol 1.0e-14\n-ps_ksp_max_it 200\n",
684 &simCtx, &user, tmpdir,
sizeof(tmpdir)));
690 PetscCall(
PicurvAssertBool((PetscBool)(before > 1.0e-2),
"perturbation should make the field divergent"));
698 "multigrid solve plus projection should leave no divergence above the solve tolerance"));
702 PetscFunctionReturn(0);
713 PetscFunctionBeginUser;
715 file = fopen(path,
"r");
716 if (!file) PetscFunctionReturn(0);
717 while (fgets(line,
sizeof(line), file)) {
718 if (strstr(line, needle)) (*count)++;
721 PetscFunctionReturn(0);
733 char tmpdir[PETSC_MAX_PATH_LEN], log_path[PETSC_MAX_PATH_LEN + 64];
736 KSPConvergedReason reason;
737 PetscInt headers = 0;
739 PetscFunctionBeginUser;
742 "-im 17\n-jm 17\n-km 17\n-mg_level 3\n"
743 "-ps_ksp_rtol 1.0e-12\n-ps_ksp_atol 1.0e-14\n-ps_ksp_max_it 200\n",
744 &simCtx, &user, tmpdir,
sizeof(tmpdir)));
749 first_ksp = user->
ksp;
750 first_operator = user->
A;
751 PetscCall(
PicurvAssertBool((PetscBool)(first_ksp != NULL),
"the first solve should build and keep the solver"));
752 PetscCall(KSPGetConvergedReason(user->
ksp, &reason));
753 PetscCall(
PicurvAssertBool((PetscBool)(reason > 0),
"the first solve should converge"));
758 PetscCall(
PicurvAssertBool((PetscBool)(user->
ksp == first_ksp),
"the second solve should reuse the solver"));
759 PetscCall(
PicurvAssertBool((PetscBool)(user->
A == first_operator),
"the second solve should reuse the operator"));
760 PetscCall(KSPGetConvergedReason(user->
ksp, &reason));
761 PetscCall(
PicurvAssertBool((PetscBool)(reason > 0),
"the reused solver should converge"));
763 PetscCall(PetscSNPrintf(log_path,
sizeof(log_path),
"%s/Poisson_Solver_Convergence_History_Block_0.log", simCtx->
log_dir));
765 PetscCall(
PicurvAssertIntEqual(2, headers,
"the convergence log should hold one header per solve"));
769 PetscFunctionReturn(0);
781 const char *
const configurations[2] = {
782 "-im 17\n-jm 17\n-km 17\n-mg_level 3\n-mg_pre_it 1\n-mg_post_it 3\n"
783 "-ps_mg_levels_1_ksp_type richardson\n-ps_mg_levels_2_ksp_type richardson\n"
784 "-ps_ksp_rtol 1.0e-12\n-ps_ksp_atol 1.0e-14\n-ps_ksp_max_it 200\n",
785 "-im 17\n-jm 17\n-km 17\n-mg_level 3\n-mg_pre_it 2\n-mg_post_it 2\n",
788 PetscFunctionBeginUser;
789 for (PetscInt c = 0; c < 2; c++) {
792 char tmpdir[PETSC_MAX_PATH_LEN];
797 &simCtx, &user, tmpdir,
sizeof(tmpdir)));
802 PetscCall(KSPGetPC(user->
ksp, &pc));
805 KSP pre_smoother, post_smoother;
806 PetscInt pre_its, post_its;
807 PetscBool is_richardson;
809 PetscCall(PCMGGetSmootherDown(pc, l, &pre_smoother));
810 PetscCall(KSPGetTolerances(pre_smoother, NULL, NULL, NULL, &pre_its));
812 PetscCall(PCMGGetSmootherUp(pc, l, &post_smoother));
813 PetscCall(KSPGetTolerances(post_smoother, NULL, NULL, NULL, &post_its));
814 PetscCall(
PicurvAssertBool((PetscBool)(pre_smoother != post_smoother),
"unequal sweeps should give a separate post-smoother"));
816 PetscCall(
PicurvAssertIntEqual(3, post_its,
"post-smoothing should run post_sweeps iterations"));
817 PetscCall(PetscObjectTypeCompare((PetscObject)post_smoother, KSPRICHARDSON, &is_richardson));
818 PetscCall(
PicurvAssertBool(is_richardson,
"the post-smoother should keep the configured smoother type"));
822 PetscCall(
PicurvAssertIntEqual(2, pre_its,
"the shared smoother should run the configured sweeps"));
832 "the solve with distinct sweeps should still remove the divergence"));
837 PetscFunctionReturn(0);
849 char tmpdir[PETSC_MAX_PATH_LEN];
850 MatNullSpace nullsp = NULL;
852 PetscReal ***xa, norm, sum;
854 PetscFunctionBeginUser;
856 NULL, PETSC_FALSE,
"-im 9\n-jm 9\n-km 9\n-mg_level 2\n", &simCtx, &user, tmpdir,
sizeof(tmpdir)));
858 PetscCall(MatGetNullSpace(user->
A, &nullsp));
859 PetscCall(
PicurvAssertBool((PetscBool)(nullsp != NULL),
"the operator should carry the Neumann null space"));
861 PetscCall(VecDuplicate(user->
Phi, &x));
862 PetscCall(VecSet(x, 3.0));
863 PetscCall(MatNullSpaceRemove(nullsp, x));
864 PetscCall(VecNorm(x, NORM_INFINITY, &norm));
865 PetscCall(
PicurvAssertRealNear(0.0, norm, 1.0e-12,
"removing the null space should annihilate a constant"));
867 PetscCall(DMDAVecGetArray(user->
da, x, &xa));
868 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; k++)
869 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; j++)
870 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; i++) xa[k][j][i] = (PetscReal)i;
871 PetscCall(DMDAVecRestoreArray(user->
da, x, &xa));
872 PetscCall(MatNullSpaceRemove(nullsp, x));
873 PetscCall(VecSum(x, &sum));
875 PetscCall(DMDAVecGetArray(user->
da, x, &xa));
877 PetscCall(DMDAVecRestoreArray(user->
da, x, &xa));
879 PetscCall(VecDestroy(&x));
882 PetscFunctionReturn(0);
895 char tmpdir[PETSC_MAX_PATH_LEN];
896 PetscErrorCode solve_error;
898 PetscFunctionBeginUser;
901 "-im 9\n-jm 9\n-km 9\n-mg_level 3\n"
903 "-ps_ksp_type fgmres\n-ps_pc_type mg\n"
904 "-ps_mg_coarse_ksp_type preonly\n-ps_mg_coarse_pc_type redundant\n",
905 &simCtx, &user, tmpdir,
sizeof(tmpdir)));
908 PetscCall(PetscPushErrorHandler(PetscReturnErrorHandler, NULL));
910 PetscCall(PetscPopErrorHandler());
912 "an unfactorable coarse level should stop the solve, not reach the projection"));
916 PetscFunctionReturn(0);
947 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv Poisson/RHS tests");
952 ierr =
PicurvRunTests(
"unit-poisson-rhs", cases,
sizeof(cases) /
sizeof(cases[0]));
958 ierr = PetscFinalize();
Pressure-Poisson projection: the multigrid pressure-correction solve, the pressure update,...
PetscErrorCode AssemblePoissonOperator(UserCtx *user)
Assembles the pressure-correction operator on one multigrid level.
PetscErrorCode UpdatePressure(UserCtx *user)
Adds the pressure correction to the pressure, P += Phi, and refreshes both fields' periodic images an...
PetscErrorCode ProjectVelocity(UserCtx *user)
Corrects the contravariant flux with the gradient of Phi.
PetscErrorCode PoissonSolver_Multigrid(UserMG *usermg)
Solves the pressure-correction equation for every block with geometric multigrid.
PetscErrorCode ComputePoissonRHS(UserCtx *user, Vec B)
Forms the right-hand side of the pressure-correction equation.
PetscErrorCode ComputeBodyForces(UserCtx *user, Vec Rct)
General dispatcher for applying all active body forces (momentum sources).
PetscErrorCode Viscous(UserCtx *user, Vec Ucont, Vec Ucat, Vec Visc)
Computes the viscous contribution to the contravariant momentum RHS.
PetscErrorCode ComputeEulerianDiffusivity(UserCtx *user)
Computes the effective diffusivity scalar field (Gamma_eff) on the Eulerian grid.
PetscErrorCode ComputeEulerianDiffusivityGradient(UserCtx *user)
Computes the Eulerian gradient of the effective diffusivity field.
PetscErrorCode Convection(UserCtx *user, Vec Ucont, Vec Ucat, Vec Conv)
Computes the convective contribution to the contravariant momentum RHS.
PetscErrorCode ComputeRHS(UserCtx *user, Vec Rhs)
Computes the Right-Hand Side (RHS) of the momentum equations.
PetscErrorCode ComputeDivergence(UserCtx *user)
Computes the discrete divergence of the contravariant velocity field.
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
static PetscErrorCode TestPoissonSolverMultigridReusesItsSolver(void)
Tests that the multigrid solver is built once and reused on later steps.
static PetscErrorCode TestClarkGradientModelScalesWithFilterWidthSquared(void)
Tests that the Clark gradient model scales as the square of the filter width.
static PetscErrorCode TestComputeEulerianDiffusivityMolecularOnly(void)
Tests Eulerian diffusivity for the molecular-only configuration.
static PetscErrorCode TestComputeRHSZeroFieldNoForcing(void)
Tests that the full RHS remains zero without forcing on a quiescent field.
static PetscErrorCode TestViscousUniformField(void)
Tests that viscous terms vanish for a uniform field.
int main(int argc, char **argv)
Runs the unit-poisson-rhs PETSc test binary.
static PetscErrorCode TestConvectionZeroField(void)
Tests that convection vanishes for a quiescent field.
static PetscErrorCode TestComputeBodyForcesDispatcher(void)
Tests the body-force dispatcher across supported source terms.
static PetscErrorCode PerturbInteriorFaceFluxes(UserCtx *user, PetscReal amplitude)
Adds a smooth divergent perturbation to every interior face flux.
static PetscErrorCode RandomizeMinimalFaceMetrics(UserCtx *user)
Fills every face metric and Jacobian of the minimal fixture with reproducible random values,...
static PetscErrorCode TestUpdatePressureAddsPhi(void)
Tests that pressure updates add the correction potential.
static PetscErrorCode TestProjectionZeroPhiLeavesVelocityUnchanged(void)
Tests that projection leaves a zero pressure-correction field unchanged.
static PetscErrorCode TestOperatorAndProjectionShareOneFaceGradient(void)
Tests that the operator is exactly the divergence of the projection's gradient.
static PetscErrorCode TestPoissonSolverMultigridRefusesAnOvercoarsenedHierarchy(void)
Tests that an over-coarsened hierarchy stops the run at the Poisson solve.
static PetscErrorCode TestPoissonSolverMultigridProjectsToDivergenceFree(void)
Tests that the production multigrid Poisson solve and projection remove a divergence.
static PetscErrorCode TestPoissonSolverMultigridHonoursDistinctSweeps(void)
Tests that unequal pre- and post-smoothing counts give two distinct smoothers.
static PetscErrorCode ScaleMinimalMetricsToSpacing(UserCtx *user, PetscReal h)
Rescales the minimal fixture's unit metrics to cubic cells of edge h.
static PetscErrorCode TestPoissonRHSZeroDivergence(void)
Tests that the Poisson RHS is zero for zero-divergence velocity.
static PetscErrorCode TestComputeEulerianDiffusivityVerificationLinearX(void)
Tests verification-driven linear diffusivity override and its gradient.
static PetscErrorCode EnsurePoissonAndRhsVectors(UserCtx *user)
Allocates Poisson/RHS support vectors required by the tests.
static PetscErrorCode TestPoissonNullSpaceRemovesTheInteriorMean(void)
Tests the null space attached to the operator.
static PetscErrorCode TestAssemblePoissonOperatorPopulatesRows(void)
Tests that Poisson matrix assembly produces a populated operator on a tiny Cartesian grid.
static PetscErrorCode TestProjectionLinearPhiCorrectsVelocity(void)
Tests that projection applies the expected x-direction correction for a linear pressure field.
static PetscErrorCode CountFileOccurrences(const char *path, const char *needle, PetscInt *count)
Counts the occurrences of needle in a text file; a missing file counts zero.
static PetscErrorCode ClarkToMolecularRatioPerX(PetscReal h, PetscInt probe, PetscReal *ratio_per_x)
Returns the Clark contribution over the molecular viscous one, per unit x, at one cell.
static PetscErrorCode TestComputeEulerianDiffusivityGradientConstantField(void)
Tests diffusivity-gradient computation on a constant field.
PetscErrorCode PicurvCreateMinimalContexts(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz)
Builds minimal SimCtx and UserCtx fixtures for C unit tests.
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 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 PicurvBuildTinyRuntimeContextWithOptions(const char *bcs_contents, PetscBool enable_particles, const char *extra_options, SimCtx **simCtx_out, UserCtx **user_out, char *tmpdir, size_t tmpdir_len)
Builds the tiny runtime context with extra control-file options appended.
PetscErrorCode PicurvAssertVecConstant(Vec vec, PetscScalar expected, PetscReal tol, const char *context)
Asserts that a PETSc vector is spatially constant within tolerance.
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.
BoundaryFaceConfig boundary_faces[6]
PetscReal forceScalingFactor
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
BCHandlerType handler_type
PetscReal poissonSourceImbalance
PetscReal bulkVelocityCorrection
char log_dir[PETSC_MAX_PATH_LEN]
PetscInt les_gradient_model
Add the Clark gradient (tensor-diffusivity) term to the viscous flux.
char eulerianSource[PETSC_MAX_PATH_LEN]
PetscInt les
Active LES closure; an LESModelType value.
#define COEF_TIME_ACCURACY
Coefficient controlling the temporal accuracy scheme (e.g., 1.5 for 2nd Order Backward Difference).
VerificationDiffusivityConfig verificationDiffusivity
PetscReal drivingForceMagnitude
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.
PetscBool VerificationDiffusivityOverrideActive(const SimCtx *simCtx)
Reports whether a verification-only diffusivity override is active.