19 PetscFunctionBeginUser;
20 PetscCheck(head != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"BC param list head cannot be NULL.");
21 PetscCall(PetscCalloc1(1, &
node));
22 PetscCall(PetscStrallocpy(key, &
node->key));
23 PetscCall(PetscStrallocpy(value, &
node->value));
25 cursor = &(*cursor)->
next;
28 PetscFunctionReturn(0);
37 PetscFunctionBeginUser;
38 if (!bc_ptr || !*bc_ptr) PetscFunctionReturn(0);
44 PetscCall(PetscFree(bc));
46 PetscFunctionReturn(0);
56 if (index >= nodes - 1) {
129 return (PetscReal)(user->
info.my - 2) * (PetscReal)(user->
info.mz - 2);
132 return (PetscReal)(user->
info.mx - 2) * (PetscReal)(user->
info.mz - 2);
135 return (PetscReal)(user->
info.mx - 2) * (PetscReal)(user->
info.my - 2);
156 PetscFunctionBeginUser;
159 *ucont_k = sample_k; *ucont_j = sample_j; *ucont_i = 0;
160 *ubcs_k = sample_k; *ubcs_j = sample_j; *ubcs_i = 0;
163 *ucont_k = sample_k; *ucont_j = sample_j; *ucont_i = user->
info.mx - 2;
164 *ubcs_k = sample_k; *ubcs_j = sample_j; *ubcs_i = user->
info.mx - 1;
167 *ucont_k = sample_k; *ucont_j = 0; *ucont_i = sample_i;
168 *ubcs_k = sample_k; *ubcs_j = 0; *ubcs_i = sample_i;
171 *ucont_k = sample_k; *ucont_j = user->
info.my - 2; *ucont_i = sample_i;
172 *ubcs_k = sample_k; *ubcs_j = user->
info.my - 1; *ubcs_i = sample_i;
175 *ucont_k = 0; *ucont_j = sample_j; *ucont_i = sample_i;
176 *ubcs_k = 0; *ubcs_j = sample_j; *ubcs_i = sample_i;
179 *ucont_k = user->
info.mz - 2; *ucont_j = sample_j; *ucont_i = sample_i;
180 *ubcs_k = user->
info.mz - 1; *ubcs_j = sample_j; *ubcs_i = sample_i;
183 PetscFunctionReturn(0);
200 PetscInt *ubcs_center_k,
201 PetscInt *ubcs_center_j,
202 PetscInt *ubcs_center_i)
204 PetscFunctionBeginUser;
207 *center_k = 3; *center_j = 3; *center_i = 0;
208 *off_k = 2; *off_j = 2; *off_i = 0;
209 *wall_k = 1; *wall_j = 1; *wall_i = 0;
210 *ubcs_center_k = 3; *ubcs_center_j = 3; *ubcs_center_i = 0;
213 *center_k = 3; *center_j = 3; *center_i = user->
info.mx - 2;
214 *off_k = 2; *off_j = 2; *off_i = user->
info.mx - 2;
215 *wall_k = 1; *wall_j = 1; *wall_i = user->
info.mx - 2;
216 *ubcs_center_k = 3; *ubcs_center_j = 3; *ubcs_center_i = user->
info.mx - 1;
219 *center_k = 3; *center_j = 0; *center_i = 3;
220 *off_k = 2; *off_j = 0; *off_i = 2;
221 *wall_k = 1; *wall_j = 0; *wall_i = 1;
222 *ubcs_center_k = 3; *ubcs_center_j = 0; *ubcs_center_i = 3;
225 *center_k = 3; *center_j = user->
info.my - 2; *center_i = 3;
226 *off_k = 2; *off_j = user->
info.my - 2; *off_i = 2;
227 *wall_k = 1; *wall_j = user->
info.my - 2; *wall_i = 1;
228 *ubcs_center_k = 3; *ubcs_center_j = user->
info.my - 1; *ubcs_center_i = 3;
231 *center_k = 0; *center_j = 3; *center_i = 3;
232 *off_k = 0; *off_j = 2; *off_i = 2;
233 *wall_k = 0; *wall_j = 1; *wall_i = 1;
234 *ubcs_center_k = 0; *ubcs_center_j = 3; *ubcs_center_i = 3;
237 *center_k = user->
info.mz - 2; *center_j = 3; *center_i = 3;
238 *off_k = user->
info.mz - 2; *off_j = 2; *off_i = 2;
239 *wall_k = user->
info.mz - 2; *wall_j = 1; *wall_i = 1;
240 *ubcs_center_k = user->
info.mz - 1; *ubcs_center_j = 3; *ubcs_center_i = 3;
243 PetscFunctionReturn(0);
267 PetscBool generic_face = PETSC_FALSE;
268 PetscBool inlet_face = PETSC_FALSE;
269 PetscBool any_face_serviceable = PETSC_FALSE;
271 PetscFunctionBeginUser;
277 any_face_serviceable = PETSC_TRUE;
283 PetscCall(
PicurvAssertBool((PetscBool)(inlet_face == generic_face),
"inlet face service check should match generic face check"));
285 PetscCall(
PicurvAssertBool(any_face_serviceable,
"at least one global face should be serviceable in a non-degenerate domain"));
288 PetscFunctionReturn(0);
298 PetscBool can_service = PETSC_FALSE;
300 PetscFunctionBeginUser;
307 PetscCall(
PicurvAssertBool((PetscBool)!can_service,
"undefined inlet face should not be serviceable"));
311 PetscFunctionReturn(0);
324 PetscFunctionBeginUser;
326 PetscCall(
PicurvAssertBool((PetscBool)(wall != NULL),
"factory should return wall handler"));
329 PetscCall(
PicurvAssertBool((PetscBool)(wall->
Destroy == NULL),
"wall handler should not require destroy hook"));
333 PetscCall(
PicurvAssertBool((PetscBool)(inlet != NULL),
"factory should return inlet handler"));
337 PetscCall(
PicurvAssertBool((PetscBool)(inlet->
Destroy != NULL),
"inlet handler should expose its destroy hook"));
342 "all profile-producing inlet modes should share one application hook"));
347 PetscFunctionReturn(0);
355 PetscFunctionBeginUser;
356 for (PetscInt step = 0; step < 3; step++) {
357 PetscReal local_inflow = 0.0, local_outflow = 0.0;
360 PetscCall(VecSet(ctx->
user->
Ucont, -91.0));
362 PetscCall(bc->
PreStep(bc, ctx, &local_inflow, &local_outflow));
363 PetscCall(bc->
Apply(bc, ctx));
365 PetscFunctionReturn(0);
373 struct HandlerExpectation {
376 PetscBool expect_apply;
377 PetscBool expect_initialize;
379 const struct HandlerExpectation expectations[] = {
387 PetscFunctionBeginUser;
388 for (
size_t i = 0; i <
sizeof(expectations) /
sizeof(expectations[0]); ++i) {
391 PetscCall(
PicurvAssertBool((PetscBool)(bc != NULL),
"factory should allocate a handler object"));
393 PetscCall(
PicurvAssertBool((PetscBool)((bc->
Apply != NULL) == expectations[i].expect_apply),
"Apply hook expectation mismatch"));
394 PetscCall(
PicurvAssertBool((PetscBool)((bc->
Initialize != NULL) == expectations[i].expect_initialize),
"Initialize hook expectation mismatch"));
397 PetscFunctionReturn(0);
407 PetscFunctionBeginUser;
408 fd = fopen(path,
"w");
409 PetscCheck(fd != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Could not open %s for writing.", path);
410 PetscCheck(fprintf(fd,
"PICSLICE\n1\n%d %d\n", (
int)n1, (
int)n2) >= 0,
411 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
"Could not write PICSLICE header.");
412 for (PetscInt a = 0; a < n1; a++) {
413 for (PetscInt b = 0; b < n2; b++) {
414 PetscReal value = base + (PetscReal)(10 * a + b);
415 PetscCheck(fprintf(fd,
"%.16e\n", (
double)value) >= 0,
416 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
"Could not write PICSLICE value.");
420 PetscFunctionReturn(0);
434 char profile_path[PETSC_MAX_PATH_LEN];
435 PetscReal local_inflow = 0.0;
436 PetscReal local_outflow = 0.0;
438 PetscFunctionBeginUser;
439 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
443 PetscCall(PetscSNPrintf(profile_path,
sizeof(profile_path),
"/tmp/picurv_unit_profile_%d.picslice", (
int)getpid()));
451 PetscCall(VecSet(user->
Ucont, 0.0));
452 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
457 PetscCall(bc->
PreStep(bc, &ctx, &local_inflow, &local_outflow));
458 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"profile inlet PreStep should leave inflow unchanged"));
459 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"profile inlet PreStep should leave outflow unchanged"));
462 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
464 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
465 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
466 PetscCall(
PicurvAssertRealNear(2.0, ucont[0][1][1].z, 1.0e-12,
"profile inlet should apply first scalar speed"));
467 PetscCall(
PicurvAssertRealNear(13.0, ucont[0][2][2].z, 1.0e-12,
"profile inlet should preserve PICSLICE ordering"));
468 PetscCall(
PicurvAssertRealNear(24.0, ubcs[0][3][3].z, 1.0e-12,
"profile inlet should write boundary velocity"));
469 PetscCall(
PicurvAssertRealNear(35.0, ucont[0][4][4].z, 1.0e-12,
"profile inlet should consume the last face-cell slot"));
470 PetscCall(
PicurvAssertRealNear(35.0, ubcs[0][4][4].z, 1.0e-12,
"profile inlet should write the last face-cell boundary value"));
471 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
472 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
473 PetscCall(
PicurvAssertBool((PetscBool)(local_inflow > 0.0),
"profile inlet PostStep should accumulate positive negative-face flux"));
474 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"profile inlet should not add to outflow"));
479 remove(profile_path);
481 PetscFunctionReturn(0);
490 PetscErrorCode ierr_create;
492 PetscFunctionBeginUser;
493 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
495 PetscCall(PetscPopErrorHandler());
497 PetscCall(
PicurvAssertBool((PetscBool)(ierr_create != 0),
"unsupported handler should return a non-zero error code"));
499 PetscCall(PetscFree(bc));
501 PetscFunctionReturn(0);
512 PetscFunctionBeginUser;
517 PetscInt ci = -1, cj = -1, ck = -1;
518 PetscReal xi = -1.0, eta = -1.0, zta = -1.0;
519 PetscBool placed = PETSC_FALSE;
527 &ci, &cj, &ck, &xi, &eta, &zta, &placed));
529 PetscCall(
PicurvAssertBool(placed,
"single-rank deterministic face placement should succeed"));
530 PetscCall(
PicurvAssertBool((PetscBool)(ci >= user->
info.xs && ci < user->info.xs + user->
info.xm),
"ci must map to owned node window"));
531 PetscCall(
PicurvAssertBool((PetscBool)(cj >= user->
info.ys && cj < user->info.ys + user->
info.ym),
"cj must map to owned node window"));
532 PetscCall(
PicurvAssertBool((PetscBool)(ck >= user->
info.zs && ck < user->info.zs + user->
info.zm),
"ck must map to owned node window"));
533 PetscCall(
PicurvAssertBool((PetscBool)(xi >= 0.0 && xi < 1.0),
"xi should be in [0,1)"));
534 PetscCall(
PicurvAssertBool((PetscBool)(eta >= 0.0 && eta < 1.0),
"eta should be in [0,1)"));
535 PetscCall(
PicurvAssertBool((PetscBool)(zta >= 0.0 && zta < 1.0),
"zta should be in [0,1)"));
538 PetscCall(
PicurvAssertRealNear(0.5, xi, 1.0e-10,
"deterministic x-face placement should sit halfway into boundary-adjacent cell"));
540 PetscCall(
PicurvAssertRealNear(0.5, eta, 1.0e-10,
"deterministic y-face placement should sit halfway into boundary-adjacent cell"));
542 PetscCall(
PicurvAssertRealNear(0.5, zta, 1.0e-10,
"deterministic z-face placement should sit halfway into boundary-adjacent cell"));
547 PetscFunctionReturn(0);
557 PetscRandom rand_i = NULL, rand_j = NULL, rand_k = NULL;
559 PetscFunctionBeginUser;
562 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand_i));
563 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand_j));
564 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand_k));
565 PetscCall(PetscRandomSetInterval(rand_i, 0.0, 1.0));
566 PetscCall(PetscRandomSetInterval(rand_j, 0.0, 1.0));
567 PetscCall(PetscRandomSetInterval(rand_k, 0.0, 1.0));
568 PetscCall(PetscRandomSetFromOptions(rand_i));
569 PetscCall(PetscRandomSetFromOptions(rand_j));
570 PetscCall(PetscRandomSetFromOptions(rand_k));
573 PetscInt ci = -1, cj = -1, ck = -1;
574 PetscReal xi = -1.0, eta = -1.0, zta = -1.0;
575 const PetscReal boundary_eps = 5.0e-4;
582 &rand_i, &rand_j, &rand_k,
586 PetscCall(
PicurvAssertBool((PetscBool)(ci >= user->
info.xs && ci < user->info.xs + user->
info.xm),
"ci must map to owned node window"));
587 PetscCall(
PicurvAssertBool((PetscBool)(cj >= user->
info.ys && cj < user->info.ys + user->
info.ym),
"cj must map to owned node window"));
588 PetscCall(
PicurvAssertBool((PetscBool)(ck >= user->
info.zs && ck < user->info.zs + user->
info.zm),
"ck must map to owned node window"));
589 PetscCall(
PicurvAssertBool((PetscBool)(xi >= 0.0 && xi <= 1.0),
"xi should be in [0,1]"));
590 PetscCall(
PicurvAssertBool((PetscBool)(eta >= 0.0 && eta <= 1.0),
"eta should be in [0,1]"));
591 PetscCall(
PicurvAssertBool((PetscBool)(zta >= 0.0 && zta <= 1.0),
"zta should be in [0,1]"));
595 PetscCall(
PicurvAssertBool((PetscBool)(xi <= boundary_eps),
"NEG_X should pin xi near 0"));
598 PetscCall(
PicurvAssertBool((PetscBool)(xi >= 1.0 - boundary_eps),
"POS_X should pin xi near 1"));
601 PetscCall(
PicurvAssertBool((PetscBool)(eta <= boundary_eps),
"NEG_Y should pin eta near 0"));
604 PetscCall(
PicurvAssertBool((PetscBool)(eta >= 1.0 - boundary_eps),
"POS_Y should pin eta near 1"));
607 PetscCall(
PicurvAssertBool((PetscBool)(zta <= boundary_eps),
"NEG_Z should pin zta near 0"));
610 PetscCall(
PicurvAssertBool((PetscBool)(zta >= 1.0 - boundary_eps),
"POS_Z should pin zta near 1"));
615 PetscCall(PetscRandomDestroy(&rand_i));
616 PetscCall(PetscRandomDestroy(&rand_j));
617 PetscCall(PetscRandomDestroy(&rand_k));
619 PetscFunctionReturn(0);
633 PetscReal local_inflow = 0.0;
634 PetscReal local_outflow = 0.0;
635 PetscReal expected_flux = 0.0;
637 PetscFunctionBeginUser;
638 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
640 PetscCall(VecSet(user->
Ucont, 0.0));
641 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
654 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
656 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
657 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
658 PetscCall(
PicurvAssertRealNear(2.5, ucont[0][3][3].z, 1.0e-12,
"constant inlet should set Ucont normal component"));
659 PetscCall(
PicurvAssertRealNear(2.5, ubcs[0][3][3].z, 1.0e-12,
"constant inlet should set Ubcs normal component"));
660 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
661 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
663 expected_flux = (PetscReal)(user->
info.mx - 2) * (PetscReal)(user->
info.my - 2) * 2.5;
664 PetscCall(
PicurvAssertRealNear(expected_flux, local_inflow, 1.0e-12,
"constant inlet PostStep should sum the face flux"));
665 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"constant inlet should not contribute to outflow accumulation"));
671 PetscFunctionReturn(0);
683 Cmpnts ***zet = NULL, ***ucont = NULL, ***ubcs = NULL, ***ucat = NULL;
685 PetscFunctionBeginUser;
686 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
689 PetscCall(DMDAVecGetArray(user->
fda, user->
lZet, &zet));
690 zet[0][3][3].
x = 0.0;
691 zet[0][3][3].
y = 3.0;
692 zet[0][3][3].
z = 4.0;
693 PetscCall(DMDAVecRestoreArray(user->
fda, user->
lZet, &zet));
703 PetscCall(bc->
Apply(bc, &ctx));
705 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
706 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
708 "Ucont should be the Cartesian inlet velocity dotted with the face metric"));
710 "curvilinear normal velocity should preserve its Cartesian x component"));
712 "curvilinear normal velocity should include Cartesian y"));
714 "curvilinear normal velocity should include Cartesian z"));
715 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
716 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
718 PetscCall(VecSet(user->
Ucat, 1.0));
720 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, &ucat));
722 "dummy reflection should act on Cartesian x"));
724 "dummy reflection should act on Cartesian y"));
726 "dummy reflection should act on Cartesian z"));
728 "dummy reflection should not overwrite the adjacent interior value"));
729 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, &ucat));
734 PetscFunctionReturn(0);
754 PetscFunctionBeginUser;
755 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
759 for (
size_t n = 0; n <
sizeof(faces) /
sizeof(faces[0]); ++n) {
760 const BCFace face = faces[n];
761 PetscInt ucont_k, ucont_j, ucont_i, ubcs_k, ubcs_j, ubcs_i;
763 PetscCall(VecSet(user->
Ucont, 5.0));
764 PetscCall(VecSet(user->
Bcs.
Ubcs, 7.0));
767 PetscCall(bc->
Apply(bc, &ctx));
770 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
771 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
773 PetscCall(
PicurvAssertRealNear(0.0, ubcs[ubcs_k][ubcs_j][ubcs_i].x, 1.0e-12,
"wall no-slip should zero Ubcs.x"));
774 PetscCall(
PicurvAssertRealNear(0.0, ubcs[ubcs_k][ubcs_j][ubcs_i].y, 1.0e-12,
"wall no-slip should zero Ubcs.y"));
775 PetscCall(
PicurvAssertRealNear(0.0, ubcs[ubcs_k][ubcs_j][ubcs_i].z, 1.0e-12,
"wall no-slip should zero Ubcs.z"));
776 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
777 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
782 PetscFunctionReturn(0);
790 struct ConstantInletCase {
793 const char *value_text;
795 const struct ConstantInletCase cases[] = {
809 PetscFunctionBeginUser;
810 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
814 for (
size_t n = 0; n <
sizeof(cases) /
sizeof(cases[0]); ++n) {
815 const struct ConstantInletCase *test_case = &cases[n];
817 PetscReal local_inflow = 0.0;
818 PetscReal local_outflow = 0.0;
819 PetscInt ucont_k, ucont_j, ucont_i, ubcs_k, ubcs_j, ubcs_i;
823 PetscCall(VecSet(user->
Ucont, 0.0));
824 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
831 test_case->value_text));
836 PetscCall(bc->
PreStep(bc, &ctx, &local_inflow, &local_outflow));
837 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"constant inlet PreStep should leave inflow unchanged"));
838 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"constant inlet PreStep should leave outflow unchanged"));
840 PetscCall(bc->
Apply(bc, &ctx));
841 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
844 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
845 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
851 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
852 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
853 PetscCall(
PicurvAssertRealNear(expected_flux, local_inflow, 1.0e-12,
"constant inlet PostStep should integrate the face flux with orientation"));
854 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"constant inlet should not add to outflow"));
861 PetscFunctionReturn(0);
875 PetscFunctionBeginUser;
876 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
891 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
892 PetscCall(
PicurvAssertRealNear(4.0, ucont[0][3][3].z, 1.0e-12,
"parabolic inlet should peak at the face centerline"));
895 "parabolic inlet should vanish on the wall, not at the wall-adjacent centre"));
896 PetscCall(
PicurvAssertBool((PetscBool)(ucont[0][3][3].z > ucont[0][2][2].z),
"parabolic inlet centerline should exceed off-center velocity"));
897 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
903 PetscFunctionReturn(0);
919 PetscFunctionBeginUser;
920 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
934 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
935 PetscCall(
PicurvAssertRealNear(4.0, ucont[0][3][1].z, 1.0e-12,
"periodic axis carries no parabola factor"));
936 PetscCall(
PicurvAssertRealNear(ucont[0][3][1].z, ucont[0][3][4].z, 1.0e-12,
"profile is uniform along the periodic axis"));
937 PetscCall(
PicurvAssertRealNear(4.0 * 0.36, ucont[0][1][3].z, 1.0e-12,
"wall-bounded axis keeps its parabola"));
938 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
944 PetscFunctionReturn(0);
963 PetscFunctionBeginUser;
964 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
968 for (
size_t n = 0; n <
sizeof(faces) /
sizeof(faces[0]); ++n) {
969 const BCFace face = faces[n];
971 PetscReal local_inflow = 0.0;
972 PetscReal local_outflow = 0.0;
973 PetscInt center_k, center_j, center_i;
974 PetscInt off_k, off_j, off_i;
975 PetscInt wall_k, wall_j, wall_i;
976 PetscInt ubcs_center_k, ubcs_center_j, ubcs_center_i;
979 PetscCall(VecSet(user->
Ucont, 0.0));
980 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
990 PetscCall(bc->
PreStep(bc, &ctx, &local_inflow, &local_outflow));
991 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"parabolic inlet PreStep should leave inflow unchanged"));
992 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"parabolic inlet PreStep should leave outflow unchanged"));
994 PetscCall(bc->
Apply(bc, &ctx));
995 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
998 ¢er_k, ¢er_j, ¢er_i,
999 &off_k, &off_j, &off_i,
1000 &wall_k, &wall_j, &wall_i,
1001 &ubcs_center_k, &ubcs_center_j, &ubcs_center_i));
1002 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
1003 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
1007 "parabolic inlet should take its wall-adjacent value, vanishing only on the wall"));
1010 "parabolic inlet centerline magnitude should exceed the off-center magnitude"));
1011 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
1012 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
1013 PetscCall(
PicurvAssertBool((PetscBool)(PetscAbsReal(local_inflow) > 0.0),
"parabolic inlet PostStep should accumulate a non-zero flux"));
1015 "parabolic inlet PostStep should preserve the face orientation"));
1016 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"parabolic inlet should not add to outflow"));
1023 PetscFunctionReturn(0);
1035 PetscReal global_inflow = 30.0;
1036 PetscReal global_farfield_in = 0.0;
1037 PetscReal global_farfield_out = 0.0;
1038 PetscReal global_outflow = 25.0;
1039 PetscReal local_inflow = 0.0;
1040 PetscReal local_outflow = 0.0;
1044 PetscFunctionBeginUser;
1045 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
1048 PetscCall(VecSet(user->
Ucat, 1.0));
1049 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
1050 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
1051 simCtx->
AreaOutSum = (PetscReal)(user->
info.mx - 2) * (PetscReal)(user->
info.my - 2);
1065 PetscCall(bc->
Apply(bc, &ctx));
1066 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
1068 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
1069 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
lUcat, &ucat));
1070 PetscCall(
PicurvAssertRealNear(1.2, ucont[5][3][3].z, 1.0e-12,
"outlet correction should add the expected flux trim"));
1071 PetscCall(
PicurvAssertRealNear(1.0, ucat[5][3][3].z, 1.0e-12,
"outlet handler should preserve the interior Ucat reference"));
1072 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
1073 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
lUcat, &ucat));
1075 PetscCall(
PicurvAssertRealNear(30.0, local_outflow, 1.0e-12,
"outlet PostStep should report corrected flux"));
1076 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"outlet handler should not accumulate inflow"));
1080 PetscFunctionReturn(0);
1099 PetscFunctionBeginUser;
1100 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
1103 PetscCall(VecSet(user->
Ucat, 1.0));
1104 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
1105 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
1107 for (
size_t n = 0; n <
sizeof(faces) /
sizeof(faces[0]); ++n) {
1108 const BCFace face = faces[n];
1110 PetscReal global_inflow = 0.0;
1111 PetscReal global_farfield_in = 0.0;
1112 PetscReal global_farfield_out = 0.0;
1113 PetscReal global_outflow = 0.0;
1114 PetscReal local_inflow = 0.0;
1115 PetscReal local_outflow = 0.0;
1117 PetscInt ucont_k, ucont_j, ucont_i, ubcs_k, ubcs_j, ubcs_i;
1119 PetscCall(VecSet(user->
Ucont, 0.0));
1120 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
1135 PetscCall(bc->
PreStep(bc, &ctx, &local_inflow, &local_outflow));
1136 PetscCall(
PicurvAssertRealNear(area, local_outflow, 1.0e-12,
"outlet PreStep should measure the uncorrected face flux"));
1138 global_inflow = area + 0.2 * area;
1139 global_outflow = local_outflow;
1140 local_outflow = 0.0;
1141 PetscCall(bc->
Apply(bc, &ctx));
1142 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
1145 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
1146 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
1148 PetscCall(
PicurvAssertRealNear(1.0, ubcs[ubcs_k][ubcs_j][ubcs_i].x, 1.0e-12,
"outlet handler should copy Ucat into Ubcs.x"));
1149 PetscCall(
PicurvAssertRealNear(1.0, ubcs[ubcs_k][ubcs_j][ubcs_i].y, 1.0e-12,
"outlet handler should copy Ucat into Ubcs.y"));
1150 PetscCall(
PicurvAssertRealNear(1.0, ubcs[ubcs_k][ubcs_j][ubcs_i].z, 1.0e-12,
"outlet handler should copy Ucat into Ubcs.z"));
1151 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
1152 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
1153 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"outlet handler should not add to inflow"));
1154 PetscCall(
PicurvAssertRealNear(1.2 * area, local_outflow, 1.0e-12,
"outlet PostStep should report the corrected flux"));
1161 PetscFunctionReturn(0);
1174 PetscFunctionBeginUser;
1176 PetscCall(VecSet(user->
Ucat, 4.0));
1177 PetscCall(VecSet(user->
Bcs.
Ubcs, 1.0));
1178 PetscCall(VecSet(user->
P, 3.0));
1179 PetscCall(VecSet(user->
Ucont, 7.0));
1184 "post-projection cell finalization must preserve non-periodic Ucont"));
1186 "constant pressure should remain unchanged during cell finalization"));
1188 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, &ucat));
1189 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
lUcat, &lucat));
1191 "non-periodic Ucat dummy face should use Ubcs extrapolation"));
1193 "non-periodic local Ucat should contain the finalized dummy face"));
1194 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, &ucat));
1195 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
lUcat, &lucat));
1198 PetscFunctionReturn(0);
1215 PetscBool x_periodic, y_periodic, z_periodic;
1217 {
"non-periodic", PETSC_FALSE, PETSC_FALSE, PETSC_FALSE},
1218 {
"xi-periodic", PETSC_TRUE, PETSC_FALSE, PETSC_FALSE},
1219 {
"fully-periodic", PETSC_TRUE, PETSC_TRUE, PETSC_TRUE },
1221 const PetscScalar sentinel = 7.0;
1223 PetscFunctionBeginUser;
1224 for (
size_t c = 0; c <
sizeof(configs) /
sizeof(configs[0]); ++c) {
1228 const PetscBool periodic[3] = {configs[c].x_periodic, configs[c].y_periodic, configs[c].z_periodic};
1233 configs[c].x_periodic,
1234 configs[c].y_periodic,
1235 configs[c].z_periodic));
1236 for (PetscInt axis = 0; axis < 3; ++axis) {
1242 PetscCall(VecDuplicate(user->
Ucont, &user->
Rhs));
1243 PetscCall(VecSet(user->
Rhs, sentinel));
1246 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Rhs, &rhs));
1247 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k) {
1248 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j) {
1249 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
1250 const PetscScalar *row = &rhs[k][j][i].
x;
1251 for (PetscInt component = 0; component < 3; ++component) {
1252 PetscInt ri, rj, rk;
1258 PetscCall(PetscSNPrintf(context,
sizeof(context),
1259 "%s: row (i=%d,j=%d,k=%d,c=%d) classified %d must be %s",
1260 configs[c].label, (
int)i, (
int)j, (
int)k, (
int)component, (
int)type,
1268 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Rhs, &rhs));
1270 PetscCall(VecDestroy(&user->
Rhs));
1273 PetscFunctionReturn(0);
1289 PetscFunctionBeginUser;
1294 PetscCall(DMCreateGlobalVector(user->
da, &user->
Nu_Wall));
1295 PetscCall(VecSet(user->
Nu_Wall, 0.0));
1296 PetscCall(DMCreateLocalVector(user->
da, &user->
lNu_Wall));
1297 PetscCall(VecSet(user->
lNu_Wall, 0.0));
1298 PetscFunctionReturn(0);
1310 Cmpnts ***ucat = NULL, ***csi = NULL;
1311 PetscReal ***aj = NULL;
1312 DMDALocalInfo info = user->
info;
1314 PetscFunctionBeginUser;
1315 PetscCall(VecSet(user->
Aj, 1.0));
1316 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
1317 PetscCall(DMDAVecGetArray(user->
fda, user->
Csi, &csi));
1318 PetscCall(DMDAVecGetArray(user->
da, user->
Aj, &aj));
1319 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k)
1320 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j)
1321 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
1323 ucat[k][j][i] = (
Cmpnts){.
x = 0.0, .y = tangential_speed, .z = 0.0};
1324 csi[k][j][i] = (
Cmpnts){.x = 1.0, .y = 0.0, .z = 0.0};
1327 PetscCall(DMDAVecRestoreArray(user->
da, user->
Aj, &aj));
1328 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Csi, &csi));
1329 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
1330 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Aj, INSERT_VALUES, user->
lAj));
1331 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Aj, INSERT_VALUES, user->
lAj));
1332 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Csi, INSERT_VALUES, user->
lCsi));
1333 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Csi, INSERT_VALUES, user->
lCsi));
1334 PetscFunctionReturn(PETSC_SUCCESS);
1352 Cmpnts ***ucat = NULL, ***ubcs = NULL;
1353 PetscReal ***utau = NULL;
1354 const PetscReal speed = 1.0;
1355 const PetscReal roughness = 1.0e-16;
1356 PetscReal kinematic_viscosity, distance_first, distance_second;
1357 PetscReal stored_utau, corrected_speed, expected_speed, reference_check;
1358 PetscReal untouched_before = 0.0, untouched_after = 0.0;
1360 PetscFunctionBeginUser;
1362 simCtx->
ren = 1000.0;
1364 kinematic_viscosity = 1.0 / simCtx->
ren;
1374 distance_first = 0.5;
1375 distance_second = 2.0 * distance_first + 0.5;
1379 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1380 untouched_before = ucat[2][2][1].
y;
1381 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1383 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1384 untouched_after = ucat[2][2][1].
y;
1385 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1387 "a disabled wall function must leave the velocity untouched"));
1393 PetscCall(DMDAVecGetArrayRead(user->
da, user->
Friction_Velocity, (
const PetscReal ***)&utau));
1394 stored_utau = utau[2][2][1];
1395 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
Friction_Velocity, (
const PetscReal ***)&utau));
1397 "the integration must store a positive friction velocity"));
1403 stored_utau, roughness);
1405 "the friction velocity must invert the log law at the "
1406 "reference cell's distance"));
1408 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1409 corrected_speed = ucat[2][2][1].
y;
1410 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1413 stored_utau, roughness);
1415 "the corrected velocity must be the modelled profile at "
1416 "the first interior cell's distance"));
1418 "a log-law wall correction must slow the first interior cell "
1419 "relative to the reference cell"));
1422 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, (
const Cmpnts ***)&ubcs));
1424 "the wall ghost row must carry a zero boundary velocity"));
1425 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, (
const Cmpnts ***)&ubcs));
1430 PetscFunctionReturn(0);
1445 PetscReal corrected[4] = {0.0, 0.0, 0.0, 0.0};
1448 PetscFunctionBeginUser;
1450 simCtx->
ren = 1000.0;
1455 for (PetscInt m = 0; m < 3; ++m) {
1459 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1460 corrected[m] = ucat[2][2][1].
y;
1461 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1462 PetscCall(
PicurvAssertBool((PetscBool)(corrected[m] > 0.0 && corrected[m] < 1.0),
1463 "every wall model must slow the first interior cell"));
1467 PetscCall(
PicurvAssertBool((PetscBool)(PetscAbsReal(corrected[0] - corrected[1]) > 1.0e-9),
1468 "the Werner-Wengle model must differ from the log law"));
1469 PetscCall(
PicurvAssertBool((PetscBool)(PetscAbsReal(corrected[0] - corrected[2]) > 1.0e-9),
1470 "the Cabot model must differ from the log law"));
1475 PetscFunctionReturn(0);
1486 PetscReal before = 0.0, after = 0.0;
1488 PetscFunctionBeginUser;
1490 simCtx->
ren = 1000.0;
1497 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1498 before = ucat[2][2][1].
y;
1499 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1501 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1502 after = ucat[2][2][1].
y;
1503 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, (
const Cmpnts ***)&ucat));
1506 "a face that is not a wall must not be corrected"));
1511 PetscFunctionReturn(0);
1531 PetscReal ***utau = NULL;
1534 const char *names[3] = {
"the log law must respond to the flow it is given",
1535 "Werner-Wengle must respond to the flow it is given",
1536 "Cabot must respond to the flow it is given"};
1537 const PetscReal speeds[2] = {0.5, 2.0};
1539 PetscFunctionBeginUser;
1541 simCtx->
ren = 1000.0;
1546 for (PetscInt m = 0; m < 3; ++m) {
1547 PetscReal stored[2] = {0.0, 0.0};
1550 for (PetscInt s = 0; s < 2; ++s) {
1554 (
const PetscReal ***)&utau));
1555 stored[s] = utau[2][2][1];
1557 (
const PetscReal ***)&utau));
1559 "a wall law must report a positive friction velocity"));
1571 PetscFunctionReturn(0);
1589 PetscInt global_size = 0, local_size = 0;
1590 PetscInt xm = 0, ym = 0, zm = 0, gxm = 0, gym = 0, gzm = 0;
1591 PetscReal ***utau = NULL;
1593 PetscFunctionBeginUser;
1599 "the friction velocity is one scalar per cell"));
1601 "a scalar cell field belongs on the dof-1 DM"));
1606 "the catalog must resolve to the vectors setup allocated"));
1609 PetscCall(DMDAGetCorners(user->
da, NULL, NULL, NULL, &xm, &ym, &zm));
1610 PetscCall(DMDAGetGhostCorners(user->
da, NULL, NULL, NULL, &gxm, &gym, &gzm));
1614 "the global friction velocity must match the owned extent"));
1616 "the local friction velocity must match the ghosted extent"));
1625 PetscFunctionReturn(0);
1640 PetscReal kinematic_viscosity = 0.0;
1641 PetscReal expected_y_plus = 0.0;
1642 const PetscReal distance_first = 0.5;
1644 PetscFunctionBeginUser;
1646 simCtx->
ren = 1000.0;
1648 kinematic_viscosity = 1.0 / simCtx->
ren;
1657 "a disabled wall model must record no corrected cells"));
1662 "an enabled wall model must record the cells it corrected"));
1671 expected_y_plus, 1.0e-9,
1672 "y+ must be formed from the same distance the "
1673 "correction used"));
1676 distance_first, 1.0e-9,
1677 "the recorded wall distance must be the first cell's"));
1680 "the recorded extrema must bracket each other"));
1688 "each pass must replace the previous pass's sample set"));
1694 PetscFunctionReturn(0);
1711 PetscFunctionBeginUser;
1713 const DMDALocalInfo info = user->
info;
1714 const PetscInt mx = info.mx, my = info.my, mz = info.mz;
1715 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
1717 PetscReal ***p = NULL, ***psi = NULL;
1718 Cmpnts ***ucat = NULL, ***ubcs = NULL;
1720 PetscCall(DMDAVecGetArray(user->
da, user->
P, &p));
1721 PetscCall(DMDAVecGetArray(user->
da, user->
Psi, &psi));
1722 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
1723 PetscCall(DMDAVecGetArray(user->
fda, user->
Bcs.
Ubcs, &ubcs));
1724 for (PetscInt k = info.zs; k < info.zs + info.zm; k++)
1725 for (PetscInt j = info.ys; j < info.ys + info.ym; j++)
1726 for (PetscInt i = info.xs; i < info.xs + info.xm; i++) {
1727 const PetscBool interior = (PetscBool)(i > 0 && i < mx - 1 && j > 0 && j < my - 1 && k > 0 && k < mz - 1);
1728 const PetscReal sentinel = 100.0 * k + 10.0 * j + i;
1730 p[k][j][i] = interior ? sentinel : -999.0;
1731 psi[k][j][i] = interior ? -sentinel : -999.0;
1732 ucat[k][j][i].
x = interior ? sentinel : -999.0;
1733 ucat[k][j][i].
y = interior ? 2.0 * sentinel : -999.0;
1734 ucat[k][j][i].
z = interior ? 3.0 * sentinel : -999.0;
1735 ubcs[k][j][i] = (
Cmpnts){1.0, 2.0, 3.0};
1737 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Bcs.
Ubcs, &ubcs));
1738 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
1739 PetscCall(DMDAVecRestoreArray(user->
da, user->
Psi, &psi));
1740 PetscCall(DMDAVecRestoreArray(user->
da, user->
P, &p));
1745 for (
size_t n = 0; n <
sizeof(scalars) /
sizeof(scalars[0]); ++n) {
1750 for (
size_t n = 0; n <
sizeof(scalars) /
sizeof(scalars[0]); ++n) {
1752 PetscReal ***f = NULL;
1755 PetscCall(DMDAVecGetArrayRead(user->
da, view.
global_vec, &f));
1756 for (PetscInt k = 1; k < mz - 1; k++)
for (PetscInt j = 1; j < my - 1; j++) {
1757 PetscCall(
PicurvAssertRealNear(f[k][j][1], f[k][j][0], 0.0,
"-x dummy copies the adjacent cell"));
1758 PetscCall(
PicurvAssertRealNear(f[k][j][mx - 2], f[k][j][mx - 1], 0.0,
"+x dummy copies the adjacent cell"));
1760 for (PetscInt k = 1; k < mz - 1; k++)
for (PetscInt i = 1; i < mx - 1; i++) {
1761 PetscCall(
PicurvAssertRealNear(f[k][1][i], f[k][0][i], 0.0,
"-y dummy copies the adjacent cell"));
1762 PetscCall(
PicurvAssertRealNear(f[k][my - 2][i], f[k][my - 1][i], 0.0,
"+y dummy copies the adjacent cell"));
1764 for (PetscInt j = 1; j < my - 1; j++)
for (PetscInt i = 1; i < mx - 1; i++) {
1765 PetscCall(
PicurvAssertRealNear(f[1][j][i], f[0][j][i], 0.0,
"-z dummy copies the adjacent cell"));
1766 PetscCall(
PicurvAssertRealNear(f[mz - 2][j][i], f[mz - 1][j][i], 0.0,
"+z dummy copies the adjacent cell"));
1768 for (PetscInt j = 0; j < my; j++) {
1770 "a (-z,-x) edge dummy is the mean of its face neighbours"));
1772 PetscCall(
PicurvAssertBool((PetscBool)(f[0][0][0] != -999.0 && f[mz - 1][my - 1][mx - 1] != -999.0),
1773 "corner dummies are written"));
1774 PetscCall(DMDAVecRestoreArrayRead(user->
da, view.
global_vec, &f));
1777 const Cmpnts ***ucat = NULL;
1779 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, &ucat));
1780 for (PetscInt k = 1; k < mz - 1; k++)
for (PetscInt j = 1; j < my - 1; j++) {
1782 "a velocity dummy puts the face average on Ubcs"));
1783 PetscCall(
PicurvAssertRealNear(3.0, 0.5 * (ucat[k][j][mx - 1].z + ucat[k][j][mx - 2].z), 1.0e-12,
1784 "a velocity dummy puts the face average on Ubcs"));
1786 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, &ucat));
1790 PetscErrorCode refused;
1792 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1794 PetscCall(PetscPopErrorHandler());
1795 PetscCall(
PicurvAssertIntEqual(PETSC_ERR_SUP, refused,
"a field with no boundary rule is refused"));
1796 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1798 PetscCall(PetscPopErrorHandler());
1799 PetscCall(
PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, refused,
"a field that is not cell-centred is refused"));
1803 PetscFunctionReturn(0);
1812 PetscErrorCode ierr;
1842 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv boundary tests");
1847 ierr =
PicurvRunTests(
"unit-boundaries", cases,
sizeof(cases) /
sizeof(cases[0]));
1853 ierr = PetscFinalize();
MomentumRowType
Classification of one staggered momentum row (location + component).
@ MOM_ROW_PHYSICAL
Independent unknown governed by the momentum equation.
PetscErrorCode GetRandomCellAndLogicalCoordsOnInletFace(UserCtx *user, const DMDALocalInfo *info, PetscInt xs_gnode_rank, PetscInt ys_gnode_rank, PetscInt zs_gnode_rank, PetscInt IM_nodes_global, PetscInt JM_nodes_global, PetscInt KM_nodes_global, PetscRandom *rand_logic_i_ptr, PetscRandom *rand_logic_j_ptr, PetscRandom *rand_logic_k_ptr, PetscInt *ci_metric_lnode_out, PetscInt *cj_metric_lnode_out, PetscInt *ck_metric_lnode_out, PetscReal *xi_metric_logic_out, PetscReal *eta_metric_logic_out, PetscReal *zta_metric_logic_out)
Assuming the current rank services the inlet face, this function selects a random cell (owned by this...
PetscErrorCode ApplyWallFunction(UserCtx *user)
Applies wall function modeling to near-wall velocities for all wall-type boundaries.
MomentumRowType ClassifyMomentumRow(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscInt component, PetscInt *ri, PetscInt *rj, PetscInt *rk)
Single source of truth for "which staggered momentum rows are unknowns".
PetscErrorCode EnforceRHSBoundaryConditions(UserCtx *user)
Zeroes every momentum RHS row that does not carry an independent unknown.
PetscErrorCode UpdateDummyCells(UserCtx *user, FieldId field_id)
Fills the dummy cells on the non-periodic faces of one cell-centred field.
PetscErrorCode BoundaryCondition_Create(BCHandlerType handler_type, BoundaryCondition **new_bc_ptr)
(Private) Creates and configures a specific BoundaryCondition handler object.
PetscErrorCode CanRankServiceInletFace(UserCtx *user, const DMDALocalInfo *info, PetscInt IM_nodes_global, PetscInt JM_nodes_global, PetscInt KM_nodes_global, PetscBool *can_service_inlet_out)
Determines if the current MPI rank owns any part of the globally defined inlet face,...
PetscErrorCode FinalizePostProjectionCellFields(UserCtx *user)
Finalizes cell-centered fields after the projection step.
PetscErrorCode UpdateCornerNodes(UserCtx *user, FieldId field_id)
Fills the edge and corner dummy cells of one cell-centred field by averaging.
PetscErrorCode CanRankServiceFace(const DMDALocalInfo *info, PetscInt IM_nodes_global, PetscInt JM_nodes_global, PetscInt KM_nodes_global, BCFace face_id, PetscBool *can_service_out)
Determines if the current MPI rank owns any part of a specified global face.
PetscErrorCode GetDeterministicFaceGridLocation(UserCtx *user, const DMDALocalInfo *info, PetscInt xs_gnode_rank, PetscInt ys_gnode_rank, PetscInt zs_gnode_rank, PetscInt IM_cells_global, PetscInt JM_cells_global, PetscInt KM_cells_global, PetscInt64 particle_global_id, PetscInt *ci_metric_lnode_out, PetscInt *cj_metric_lnode_out, PetscInt *ck_metric_lnode_out, PetscReal *xi_metric_logic_out, PetscReal *eta_metric_logic_out, PetscReal *zta_metric_logic_out, PetscBool *placement_successful_out)
Places particles in a deterministic grid/raster pattern on a specified domain face.
PetscErrorCode FieldGetView(UserCtx *user, FieldId field_id, FieldView *view)
Resolve the existing DM and global/local vectors for one field.
PetscErrorCode FieldGetDescriptor(FieldId field_id, const FieldDescriptor **descriptor)
Return immutable metadata for a valid field identifier.
FieldId
Compile-time identity for a catalogued Eulerian field.
Immutable metadata for one field identity.
Non-owning runtime objects resolved for one field and UserCtx.
void FreeBC_ParamList(BC_Param *head)
Frees an entire linked list of boundary-condition parameters.
The "virtual table" struct for a boundary condition handler object.
PetscErrorCode(* PostStep)(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow, PetscReal *local_outflow)
PetscErrorCode(* PreStep)(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow, PetscReal *local_outflow)
PetscErrorCode(* Destroy)(BoundaryCondition *self)
PetscErrorCode(* Initialize)(BoundaryCondition *self, BCContext *ctx)
PetscErrorCode(* Apply)(BoundaryCondition *self, BCContext *ctx)
static PetscErrorCode TestOutletConservationHandlerFaceMatrix(void)
Tests outlet-conservation correction and flux accounting across all outlet faces.
static PetscErrorCode TestCanRankServiceFaceMatchesInletWhenDefined(void)
Tests that face-service detection matches a defined inlet face.
static PetscErrorCode TestInletCommonApplicationCurvilinearMetric(void)
Verifies curvilinear projection and Cartesian dummy reflection for the shared inlet path.
static PetscErrorCode ApplyInletAcrossFirstSteps(BoundaryCondition *bc, BCContext *ctx)
Reapplies a static inlet through the first three time-step contexts.
static PetscErrorCode TestInletParabolicProfileUniformAlongPeriodicAxis(void)
Tests that the parabolic inlet is uniform along a periodic cross-stream axis.
static PetscErrorCode TestBoundaryConditionFactoryAssignments(void)
Tests the boundary-condition factory assignments for representative handlers.
static PetscErrorCode AppendBCParam(BC_Param **head, const char *key, const char *value)
Appends one key/value pair to a linked list of boundary-condition parameters.
static PetscErrorCode TestApplyWallFunctionMatchesTheLogLawItDispatches(void)
Tests that the wall-function integration reproduces the model it dispatches to.
static PetscReal GetFaceInteriorPointCount(const UserCtx *user, BCFace face)
Computes the number of face-interior sample points for a given boundary face.
static PetscErrorCode TestInletProfileFromFileHandlerBehavior(void)
Tests prescribed-flow inlet handler loading and applying one Z-face profile.
static PetscErrorCode TestDummyCellsFollowEachFieldsBoundaryRule(void)
Checks each cell-centred field's dummy-cell rule, faces then edges.
int main(int argc, char **argv)
Runs the unit-boundaries PETSc test binary.
static PetscErrorCode TestInletParabolicProfileHandlerBehavior(void)
Tests parabolic inlet handler shape on a tiny Z-face.
static PetscErrorCode TestWallModelDiagnosticsRecordTheCorrectedCells(void)
Tests that the wall pass records the statistics its diagnostics report.
static PetscReal GetFaceOrientationSign(BCFace face)
Returns the sign convention used for face-normal flux expectations.
static PetscErrorCode TestGetDeterministicFaceGridLocationFaceMatrix(void)
Tests deterministic inlet-face grid-location helpers across all faces.
static PetscErrorCode TestInletConstantVelocityHandlerBehavior(void)
Tests constant inlet handler initialization and face writes on a Z inlet.
static PetscErrorCode SeedWallFrictionVelocityStorage(UserCtx *user)
Allocates the wall-model storage the way production allocates it.
static PetscErrorCode TestBoundaryConditionFactoryRejectsUnsupportedHandler(void)
Tests that unsupported handler types are rejected by the factory.
static PetscErrorCode TestGetRandomCellAndLogicalCoordsOnInletFaceMatrix(void)
Tests random inlet-face cell selection across all supported faces.
static PetscErrorCode TestWallFunctionModelSelectorChoosesTheLaw(void)
Tests that the model selector actually changes which law is applied.
static PetscErrorCode TestBoundaryConditionFactoryImplementedHandlerMatrix(void)
Tests the implemented-handler matrix exposed by the factory.
static PetscErrorCode WritePicSliceForTests(const char *path, PetscInt n1, PetscInt n2, PetscReal base)
Writes a tiny canonical PICSLICE profile for boundary handler tests.
static PetscErrorCode TestCanRankServiceInletFaceRequiresDefinition(void)
Tests that inlet-face service requires an inlet face to be defined.
static PetscErrorCode TestInletParabolicProfileHandlerFaceMatrix(void)
Tests parabolic-inlet centerline, wall, and flux behavior across all faces.
static PetscErrorCode TestWallNoSlipHandlerFaceMatrix(void)
Tests wall no-slip application across the full face matrix.
static PetscErrorCode TestInletConstantVelocityHandlerFaceMatrix(void)
Tests constant-inlet initialization, flux accounting, and face writes across all faces.
static PetscErrorCode SeedWallFunctionFixture(UserCtx *user, PetscReal tangential_speed)
Seeds the fields ApplyWallFunction() reads on a single wall face.
static PetscErrorCode TestFinalizePostProjectionCellFieldsNonPeriodic(void)
Tests non-periodic cell-field finalization without modifying Ucont.
static PetscErrorCode TestWallModelFrictionVelocityRespondsToTheFlow(void)
Tests that every wall law's friction velocity responds to the flow it is given.
static const char * GetInletParamKey(BCFace face)
Maps an inlet face to the matching configuration key used by the handler parser.
static PetscErrorCode DestroyBoundaryHandler(BoundaryCondition **bc_ptr)
Destroys one boundary-condition handler allocated by a boundary test.
static PetscErrorCode TestApplyWallFunctionSkipsNonWallFaces(void)
Tests that a face which is not a wall is left alone by the wall function.
static PetscErrorCode TestOutletConservationHandlerBehavior(void)
Tests outlet conservation handler correction and post-step flux accounting.
static void ResetBoundaryFaceConfig(BoundaryFaceConfig *cfg)
Resets one boundary-face configuration entry to a neutral test-local baseline.
static PetscReal GetFaceNormalComponent(Cmpnts value, BCFace face)
Extracts the velocity component aligned with the supplied boundary-face normal.
static PetscInt InteriorSampleIndex(PetscInt nodes)
Chooses a stable interior node index for face-sample assertions on tiny test grids.
static PetscErrorCode TestEnforceRHSBoundaryConditionsMatchesRowClassification(void)
EnforceRHSBoundaryConditions() must zero a row exactly when it is not an unknown.
static PetscErrorCode GetRepresentativeFaceSlots(const UserCtx *user, BCFace face, PetscInt *ucont_k, PetscInt *ucont_j, PetscInt *ucont_i, PetscInt *ubcs_k, PetscInt *ubcs_j, PetscInt *ubcs_i)
Selects representative Ucont and Ubcs slots for face-matrix assertions.
static PetscErrorCode TestWallFrictionVelocityStorageMatchesTheCatalog(void)
Tests that the friction-velocity storage matches what the field catalog declares.
static PetscErrorCode GetParabolicSampleSlots(const UserCtx *user, BCFace face, PetscInt *center_k, PetscInt *center_j, PetscInt *center_i, PetscInt *off_k, PetscInt *off_j, PetscInt *off_i, PetscInt *wall_k, PetscInt *wall_j, PetscInt *wall_i, PetscInt *ubcs_center_k, PetscInt *ubcs_center_j, PetscInt *ubcs_center_i)
Selects center, off-center, and wall-adjacent slots for parabolic-face checks.
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 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 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 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 PicurvPopulateIdentityMetrics(UserCtx *user)
Populates identity metric vectors on the minimal grid fixture.
PetscErrorCode PicurvAssertBool(PetscBool value, const char *context)
Asserts that one boolean condition is true.
Shared declarations for the PICurv C test fixture and assertion layer.
Named test case descriptor consumed by PicurvRunTests.
const PetscReal * global_outflow_sum
BCType
Defines the general mathematical/physical Category of a boundary.
PetscBool inletFaceDefined
BoundaryFaceConfig boundary_faces[6]
BCFace identifiedInletBCFace
SimCtx * simCtx
Back-pointer to the master simulation context.
BCHandlerType
Defines the specific computational "strategy" for a boundary handler.
@ BC_HANDLER_INLET_PARABOLIC
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
@ BC_HANDLER_INLET_PROFILE_FROM_FILE
@ BC_HANDLER_OUTLET_CONSERVATION
@ BC_HANDLER_OUTLET_PRESSURE
BCHandlerType handler_type
const PetscReal * global_farfield_outflow_sum
Vec Ubcs
Physical Cartesian velocity at boundary faces. Full 3D array but only boundary-face entries are meani...
PetscReal wall_distance_sum
Sum of the first-cell wall distance.
PetscReal friction_velocity_min
Smallest u_tau this rank corrected.
const PetscReal * global_inflow_sum
PetscReal friction_velocity_max
Largest u_tau this rank corrected.
PetscReal y_plus_sum
Sum of u_tau * y / nu over corrected cells.
PetscReal wall_roughness_height
PetscReal friction_velocity_sum
Sum of u_tau over corrected cells.
PetscInt wallfunction
Enable wall functions on WALL faces.
const PetscReal * global_farfield_inflow_sum
PetscInt cells
Number of cells this rank corrected.
BCFace
Identifies the six logical faces of a structured computational block.
WallModelDiagnosticsState wall_diagnostics
Near-wall statistics from the last wall-model pass.
Provides execution context for a boundary condition handler.
A node in a linked list for storing key-value parameters from the bcs.dat file.
Holds the complete configuration for one of the six boundary faces.
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.
A generic C-style linked list node for integers.
double u_hydset_roughness(double kinematic_viscosity, double wall_distance, double friction_velocity, double roughness_height)
Computes velocity from log-law for rough walls.