18 PetscFunctionBeginUser;
19 PetscCheck(head != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"BC param list head cannot be NULL.");
20 PetscCall(PetscCalloc1(1, &
node));
21 PetscCall(PetscStrallocpy(key, &
node->key));
22 PetscCall(PetscStrallocpy(value, &
node->value));
24 cursor = &(*cursor)->
next;
27 PetscFunctionReturn(0);
36 PetscFunctionBeginUser;
37 if (!bc_ptr || !*bc_ptr) PetscFunctionReturn(0);
43 PetscCall(PetscFree(bc));
45 PetscFunctionReturn(0);
55 if (index >= nodes - 1) {
128 return (PetscReal)(user->
info.my - 2) * (PetscReal)(user->
info.mz - 2);
131 return (PetscReal)(user->
info.mx - 2) * (PetscReal)(user->
info.mz - 2);
134 return (PetscReal)(user->
info.mx - 2) * (PetscReal)(user->
info.my - 2);
155 PetscFunctionBeginUser;
158 *ucont_k = sample_k; *ucont_j = sample_j; *ucont_i = 0;
159 *ubcs_k = sample_k; *ubcs_j = sample_j; *ubcs_i = 0;
162 *ucont_k = sample_k; *ucont_j = sample_j; *ucont_i = user->
info.mx - 2;
163 *ubcs_k = sample_k; *ubcs_j = sample_j; *ubcs_i = user->
info.mx - 1;
166 *ucont_k = sample_k; *ucont_j = 0; *ucont_i = sample_i;
167 *ubcs_k = sample_k; *ubcs_j = 0; *ubcs_i = sample_i;
170 *ucont_k = sample_k; *ucont_j = user->
info.my - 2; *ucont_i = sample_i;
171 *ubcs_k = sample_k; *ubcs_j = user->
info.my - 1; *ubcs_i = sample_i;
174 *ucont_k = 0; *ucont_j = sample_j; *ucont_i = sample_i;
175 *ubcs_k = 0; *ubcs_j = sample_j; *ubcs_i = sample_i;
178 *ucont_k = user->
info.mz - 2; *ucont_j = sample_j; *ucont_i = sample_i;
179 *ubcs_k = user->
info.mz - 1; *ubcs_j = sample_j; *ubcs_i = sample_i;
182 PetscFunctionReturn(0);
199 PetscInt *ubcs_center_k,
200 PetscInt *ubcs_center_j,
201 PetscInt *ubcs_center_i)
203 PetscFunctionBeginUser;
206 *center_k = 3; *center_j = 3; *center_i = 0;
207 *off_k = 2; *off_j = 2; *off_i = 0;
208 *wall_k = 1; *wall_j = 1; *wall_i = 0;
209 *ubcs_center_k = 3; *ubcs_center_j = 3; *ubcs_center_i = 0;
212 *center_k = 3; *center_j = 3; *center_i = user->
info.mx - 2;
213 *off_k = 2; *off_j = 2; *off_i = user->
info.mx - 2;
214 *wall_k = 1; *wall_j = 1; *wall_i = user->
info.mx - 2;
215 *ubcs_center_k = 3; *ubcs_center_j = 3; *ubcs_center_i = user->
info.mx - 1;
218 *center_k = 3; *center_j = 0; *center_i = 3;
219 *off_k = 2; *off_j = 0; *off_i = 2;
220 *wall_k = 1; *wall_j = 0; *wall_i = 1;
221 *ubcs_center_k = 3; *ubcs_center_j = 0; *ubcs_center_i = 3;
224 *center_k = 3; *center_j = user->
info.my - 2; *center_i = 3;
225 *off_k = 2; *off_j = user->
info.my - 2; *off_i = 2;
226 *wall_k = 1; *wall_j = user->
info.my - 2; *wall_i = 1;
227 *ubcs_center_k = 3; *ubcs_center_j = user->
info.my - 1; *ubcs_center_i = 3;
230 *center_k = 0; *center_j = 3; *center_i = 3;
231 *off_k = 0; *off_j = 2; *off_i = 2;
232 *wall_k = 0; *wall_j = 1; *wall_i = 1;
233 *ubcs_center_k = 0; *ubcs_center_j = 3; *ubcs_center_i = 3;
236 *center_k = user->
info.mz - 2; *center_j = 3; *center_i = 3;
237 *off_k = user->
info.mz - 2; *off_j = 2; *off_i = 2;
238 *wall_k = user->
info.mz - 2; *wall_j = 1; *wall_i = 1;
239 *ubcs_center_k = user->
info.mz - 1; *ubcs_center_j = 3; *ubcs_center_i = 3;
242 PetscFunctionReturn(0);
266 PetscBool generic_face = PETSC_FALSE;
267 PetscBool inlet_face = PETSC_FALSE;
268 PetscBool any_face_serviceable = PETSC_FALSE;
270 PetscFunctionBeginUser;
276 any_face_serviceable = PETSC_TRUE;
282 PetscCall(
PicurvAssertBool((PetscBool)(inlet_face == generic_face),
"inlet face service check should match generic face check"));
284 PetscCall(
PicurvAssertBool(any_face_serviceable,
"at least one global face should be serviceable in a non-degenerate domain"));
287 PetscFunctionReturn(0);
297 PetscBool can_service = PETSC_FALSE;
299 PetscFunctionBeginUser;
306 PetscCall(
PicurvAssertBool((PetscBool)!can_service,
"undefined inlet face should not be serviceable"));
310 PetscFunctionReturn(0);
321 PetscFunctionBeginUser;
323 PetscCall(
PicurvAssertBool((PetscBool)(wall != NULL),
"factory should return wall handler"));
326 PetscCall(
PicurvAssertBool((PetscBool)(wall->
Destroy == NULL),
"wall handler should not require destroy hook"));
330 PetscCall(
PicurvAssertBool((PetscBool)(inlet != NULL),
"factory should return inlet handler"));
334 PetscCall(
PicurvAssertBool((PetscBool)(inlet->
Destroy != NULL),
"inlet handler should expose its destroy hook"));
337 PetscFunctionReturn(0);
345 struct HandlerExpectation {
348 PetscBool expect_apply;
349 PetscBool expect_initialize;
351 const struct HandlerExpectation expectations[] = {
359 PetscFunctionBeginUser;
360 for (
size_t i = 0; i <
sizeof(expectations) /
sizeof(expectations[0]); ++i) {
363 PetscCall(
PicurvAssertBool((PetscBool)(bc != NULL),
"factory should allocate a handler object"));
365 PetscCall(
PicurvAssertBool((PetscBool)((bc->
Apply != NULL) == expectations[i].expect_apply),
"Apply hook expectation mismatch"));
366 PetscCall(
PicurvAssertBool((PetscBool)((bc->
Initialize != NULL) == expectations[i].expect_initialize),
"Initialize hook expectation mismatch"));
369 PetscFunctionReturn(0);
379 PetscFunctionBeginUser;
380 fd = fopen(path,
"w");
381 PetscCheck(fd != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Could not open %s for writing.", path);
382 PetscCheck(fprintf(fd,
"PICSLICE\n1\n%d %d\n", (
int)n1, (
int)n2) >= 0,
383 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
"Could not write PICSLICE header.");
384 for (PetscInt a = 0; a < n1; a++) {
385 for (PetscInt b = 0; b < n2; b++) {
386 PetscReal value = base + (PetscReal)(10 * a + b);
387 PetscCheck(fprintf(fd,
"%.16e\n", (
double)value) >= 0,
388 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
"Could not write PICSLICE value.");
392 PetscFunctionReturn(0);
406 char profile_path[PETSC_MAX_PATH_LEN];
407 PetscReal local_inflow = 0.0;
408 PetscReal local_outflow = 0.0;
410 PetscFunctionBeginUser;
411 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
415 PetscCall(PetscSNPrintf(profile_path,
sizeof(profile_path),
"/tmp/picurv_unit_profile_%d.picslice", (
int)getpid()));
423 PetscCall(VecSet(user->
Ucont, 0.0));
424 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
429 PetscCall(bc->
PreStep(bc, &ctx, &local_inflow, &local_outflow));
430 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"profile inlet PreStep should leave inflow unchanged"));
431 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"profile inlet PreStep should leave outflow unchanged"));
433 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
435 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
436 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
437 PetscCall(
PicurvAssertRealNear(2.0, ucont[0][1][1].z, 1.0e-12,
"profile inlet should apply first scalar speed"));
438 PetscCall(
PicurvAssertRealNear(13.0, ucont[0][2][2].z, 1.0e-12,
"profile inlet should preserve PICSLICE ordering"));
439 PetscCall(
PicurvAssertRealNear(24.0, ubcs[0][3][3].z, 1.0e-12,
"profile inlet should write boundary velocity"));
440 PetscCall(
PicurvAssertRealNear(35.0, ucont[0][4][4].z, 1.0e-12,
"profile inlet should consume the last face-cell slot"));
441 PetscCall(
PicurvAssertRealNear(35.0, ubcs[0][4][4].z, 1.0e-12,
"profile inlet should write the last face-cell boundary value"));
442 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
443 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
444 PetscCall(
PicurvAssertBool((PetscBool)(local_inflow > 0.0),
"profile inlet PostStep should accumulate positive negative-face flux"));
445 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"profile inlet should not add to outflow"));
450 remove(profile_path);
452 PetscFunctionReturn(0);
461 PetscErrorCode ierr_create;
463 PetscFunctionBeginUser;
464 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
466 PetscCall(PetscPopErrorHandler());
468 PetscCall(
PicurvAssertBool((PetscBool)(ierr_create != 0),
"unsupported handler should return a non-zero error code"));
470 PetscCall(PetscFree(bc));
472 PetscFunctionReturn(0);
483 PetscFunctionBeginUser;
488 PetscInt ci = -1, cj = -1, ck = -1;
489 PetscReal xi = -1.0, eta = -1.0, zta = -1.0;
490 PetscBool placed = PETSC_FALSE;
498 &ci, &cj, &ck, &xi, &eta, &zta, &placed));
500 PetscCall(
PicurvAssertBool(placed,
"single-rank deterministic face placement should succeed"));
501 PetscCall(
PicurvAssertBool((PetscBool)(ci >= user->
info.xs && ci < user->info.xs + user->
info.xm),
"ci must map to owned node window"));
502 PetscCall(
PicurvAssertBool((PetscBool)(cj >= user->
info.ys && cj < user->info.ys + user->
info.ym),
"cj must map to owned node window"));
503 PetscCall(
PicurvAssertBool((PetscBool)(ck >= user->
info.zs && ck < user->info.zs + user->
info.zm),
"ck must map to owned node window"));
504 PetscCall(
PicurvAssertBool((PetscBool)(xi >= 0.0 && xi < 1.0),
"xi should be in [0,1)"));
505 PetscCall(
PicurvAssertBool((PetscBool)(eta >= 0.0 && eta < 1.0),
"eta should be in [0,1)"));
506 PetscCall(
PicurvAssertBool((PetscBool)(zta >= 0.0 && zta < 1.0),
"zta should be in [0,1)"));
509 PetscCall(
PicurvAssertRealNear(0.5, xi, 1.0e-10,
"deterministic x-face placement should sit halfway into boundary-adjacent cell"));
511 PetscCall(
PicurvAssertRealNear(0.5, eta, 1.0e-10,
"deterministic y-face placement should sit halfway into boundary-adjacent cell"));
513 PetscCall(
PicurvAssertRealNear(0.5, zta, 1.0e-10,
"deterministic z-face placement should sit halfway into boundary-adjacent cell"));
518 PetscFunctionReturn(0);
528 PetscRandom rand_i = NULL, rand_j = NULL, rand_k = NULL;
530 PetscFunctionBeginUser;
533 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand_i));
534 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand_j));
535 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand_k));
536 PetscCall(PetscRandomSetInterval(rand_i, 0.0, 1.0));
537 PetscCall(PetscRandomSetInterval(rand_j, 0.0, 1.0));
538 PetscCall(PetscRandomSetInterval(rand_k, 0.0, 1.0));
539 PetscCall(PetscRandomSetFromOptions(rand_i));
540 PetscCall(PetscRandomSetFromOptions(rand_j));
541 PetscCall(PetscRandomSetFromOptions(rand_k));
544 PetscInt ci = -1, cj = -1, ck = -1;
545 PetscReal xi = -1.0, eta = -1.0, zta = -1.0;
546 const PetscReal boundary_eps = 5.0e-4;
553 &rand_i, &rand_j, &rand_k,
557 PetscCall(
PicurvAssertBool((PetscBool)(ci >= user->
info.xs && ci < user->info.xs + user->
info.xm),
"ci must map to owned node window"));
558 PetscCall(
PicurvAssertBool((PetscBool)(cj >= user->
info.ys && cj < user->info.ys + user->
info.ym),
"cj must map to owned node window"));
559 PetscCall(
PicurvAssertBool((PetscBool)(ck >= user->
info.zs && ck < user->info.zs + user->
info.zm),
"ck must map to owned node window"));
560 PetscCall(
PicurvAssertBool((PetscBool)(xi >= 0.0 && xi <= 1.0),
"xi should be in [0,1]"));
561 PetscCall(
PicurvAssertBool((PetscBool)(eta >= 0.0 && eta <= 1.0),
"eta should be in [0,1]"));
562 PetscCall(
PicurvAssertBool((PetscBool)(zta >= 0.0 && zta <= 1.0),
"zta should be in [0,1]"));
566 PetscCall(
PicurvAssertBool((PetscBool)(xi <= boundary_eps),
"NEG_X should pin xi near 0"));
569 PetscCall(
PicurvAssertBool((PetscBool)(xi >= 1.0 - boundary_eps),
"POS_X should pin xi near 1"));
572 PetscCall(
PicurvAssertBool((PetscBool)(eta <= boundary_eps),
"NEG_Y should pin eta near 0"));
575 PetscCall(
PicurvAssertBool((PetscBool)(eta >= 1.0 - boundary_eps),
"POS_Y should pin eta near 1"));
578 PetscCall(
PicurvAssertBool((PetscBool)(zta <= boundary_eps),
"NEG_Z should pin zta near 0"));
581 PetscCall(
PicurvAssertBool((PetscBool)(zta >= 1.0 - boundary_eps),
"POS_Z should pin zta near 1"));
586 PetscCall(PetscRandomDestroy(&rand_i));
587 PetscCall(PetscRandomDestroy(&rand_j));
588 PetscCall(PetscRandomDestroy(&rand_k));
590 PetscFunctionReturn(0);
604 PetscReal local_inflow = 0.0;
605 PetscReal local_outflow = 0.0;
606 PetscReal expected_flux = 0.0;
608 PetscFunctionBeginUser;
609 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
611 PetscCall(VecSet(user->
Ucont, 0.0));
612 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
624 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
626 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
627 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
628 PetscCall(
PicurvAssertRealNear(2.5, ucont[0][3][3].z, 1.0e-12,
"constant inlet should set Ucont normal component"));
629 PetscCall(
PicurvAssertRealNear(2.5, ubcs[0][3][3].z, 1.0e-12,
"constant inlet should set Ubcs normal component"));
630 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
631 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
633 expected_flux = (PetscReal)(user->
info.mx - 2) * (PetscReal)(user->
info.my - 2) * 2.5;
634 PetscCall(
PicurvAssertRealNear(expected_flux, local_inflow, 1.0e-12,
"constant inlet PostStep should sum the face flux"));
635 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"constant inlet should not contribute to outflow accumulation"));
641 PetscFunctionReturn(0);
661 PetscFunctionBeginUser;
662 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
666 for (
size_t n = 0; n <
sizeof(faces) /
sizeof(faces[0]); ++n) {
667 const BCFace face = faces[n];
668 PetscInt ucont_k, ucont_j, ucont_i, ubcs_k, ubcs_j, ubcs_i;
670 PetscCall(VecSet(user->
Ucont, 5.0));
671 PetscCall(VecSet(user->
Bcs.
Ubcs, 7.0));
674 PetscCall(bc->
Apply(bc, &ctx));
677 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
678 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
680 PetscCall(
PicurvAssertRealNear(0.0, ubcs[ubcs_k][ubcs_j][ubcs_i].x, 1.0e-12,
"wall no-slip should zero Ubcs.x"));
681 PetscCall(
PicurvAssertRealNear(0.0, ubcs[ubcs_k][ubcs_j][ubcs_i].y, 1.0e-12,
"wall no-slip should zero Ubcs.y"));
682 PetscCall(
PicurvAssertRealNear(0.0, ubcs[ubcs_k][ubcs_j][ubcs_i].z, 1.0e-12,
"wall no-slip should zero Ubcs.z"));
683 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
684 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
689 PetscFunctionReturn(0);
697 struct ConstantInletCase {
700 const char *value_text;
702 const struct ConstantInletCase cases[] = {
716 PetscFunctionBeginUser;
717 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
721 for (
size_t n = 0; n <
sizeof(cases) /
sizeof(cases[0]); ++n) {
722 const struct ConstantInletCase *test_case = &cases[n];
724 PetscReal local_inflow = 0.0;
725 PetscReal local_outflow = 0.0;
726 PetscInt ucont_k, ucont_j, ucont_i, ubcs_k, ubcs_j, ubcs_i;
730 PetscCall(VecSet(user->
Ucont, 0.0));
731 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
738 test_case->value_text));
743 PetscCall(bc->
PreStep(bc, &ctx, &local_inflow, &local_outflow));
744 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"constant inlet PreStep should leave inflow unchanged"));
745 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"constant inlet PreStep should leave outflow unchanged"));
747 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
750 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
751 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
757 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
758 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
759 PetscCall(
PicurvAssertRealNear(expected_flux, local_inflow, 1.0e-12,
"constant inlet PostStep should integrate the face flux with orientation"));
760 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"constant inlet should not add to outflow"));
767 PetscFunctionReturn(0);
781 PetscFunctionBeginUser;
782 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
796 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
797 PetscCall(
PicurvAssertRealNear(4.0, ucont[0][3][3].z, 1.0e-12,
"parabolic inlet should peak at the face centerline"));
798 PetscCall(
PicurvAssertRealNear(0.0, ucont[0][1][1].z, 1.0e-12,
"parabolic inlet should vanish at wall-adjacent locations"));
799 PetscCall(
PicurvAssertBool((PetscBool)(ucont[0][3][3].z > ucont[0][2][2].z),
"parabolic inlet centerline should exceed off-center velocity"));
800 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
806 PetscFunctionReturn(0);
825 PetscFunctionBeginUser;
826 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
830 for (
size_t n = 0; n <
sizeof(faces) /
sizeof(faces[0]); ++n) {
831 const BCFace face = faces[n];
833 PetscReal local_inflow = 0.0;
834 PetscReal local_outflow = 0.0;
835 PetscInt center_k, center_j, center_i;
836 PetscInt off_k, off_j, off_i;
837 PetscInt wall_k, wall_j, wall_i;
838 PetscInt ubcs_center_k, ubcs_center_j, ubcs_center_i;
841 PetscCall(VecSet(user->
Ucont, 0.0));
842 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
852 PetscCall(bc->
PreStep(bc, &ctx, &local_inflow, &local_outflow));
853 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"parabolic inlet PreStep should leave inflow unchanged"));
854 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"parabolic inlet PreStep should leave outflow unchanged"));
856 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
859 ¢er_k, ¢er_j, ¢er_i,
860 &off_k, &off_j, &off_i,
861 &wall_k, &wall_j, &wall_i,
862 &ubcs_center_k, &ubcs_center_j, &ubcs_center_i));
863 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
864 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
870 "parabolic inlet centerline magnitude should exceed the off-center magnitude"));
871 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
872 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
873 PetscCall(
PicurvAssertBool((PetscBool)(PetscAbsReal(local_inflow) > 0.0),
"parabolic inlet PostStep should accumulate a non-zero flux"));
875 "parabolic inlet PostStep should preserve the face orientation"));
876 PetscCall(
PicurvAssertRealNear(0.0, local_outflow, 1.0e-12,
"parabolic inlet should not add to outflow"));
883 PetscFunctionReturn(0);
895 PetscReal global_inflow = 30.0;
896 PetscReal global_farfield_in = 0.0;
897 PetscReal global_farfield_out = 0.0;
898 PetscReal global_outflow = 25.0;
899 PetscReal local_inflow = 0.0;
900 PetscReal local_outflow = 0.0;
904 PetscFunctionBeginUser;
905 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
908 PetscCall(VecSet(user->
Ucat, 1.0));
909 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
910 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
925 PetscCall(bc->
Apply(bc, &ctx));
926 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
928 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
929 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
lUcat, &ucat));
930 PetscCall(
PicurvAssertRealNear(1.2, ucont[5][3][3].z, 1.0e-12,
"outlet correction should add the expected flux trim"));
931 PetscCall(
PicurvAssertRealNear(1.0, ucat[5][3][3].z, 1.0e-12,
"outlet handler should preserve the interior Ucat reference"));
932 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
933 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
lUcat, &ucat));
935 PetscCall(
PicurvAssertRealNear(30.0, local_outflow, 1.0e-12,
"outlet PostStep should report corrected flux"));
936 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"outlet handler should not accumulate inflow"));
940 PetscFunctionReturn(0);
959 PetscFunctionBeginUser;
960 PetscCall(PetscMemzero(&ctx,
sizeof(ctx)));
963 PetscCall(VecSet(user->
Ucat, 1.0));
964 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
965 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
967 for (
size_t n = 0; n <
sizeof(faces) /
sizeof(faces[0]); ++n) {
968 const BCFace face = faces[n];
970 PetscReal global_inflow = 0.0;
971 PetscReal global_farfield_in = 0.0;
972 PetscReal global_farfield_out = 0.0;
973 PetscReal global_outflow = 0.0;
974 PetscReal local_inflow = 0.0;
975 PetscReal local_outflow = 0.0;
977 PetscInt ucont_k, ucont_j, ucont_i, ubcs_k, ubcs_j, ubcs_i;
979 PetscCall(VecSet(user->
Ucont, 0.0));
980 PetscCall(VecSet(user->
Bcs.
Ubcs, 0.0));
995 PetscCall(bc->
PreStep(bc, &ctx, &local_inflow, &local_outflow));
996 PetscCall(
PicurvAssertRealNear(area, local_outflow, 1.0e-12,
"outlet PreStep should measure the uncorrected face flux"));
998 global_inflow = area + 0.2 * area;
999 global_outflow = local_outflow;
1000 local_outflow = 0.0;
1001 PetscCall(bc->
Apply(bc, &ctx));
1002 PetscCall(bc->
PostStep(bc, &ctx, &local_inflow, &local_outflow));
1005 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
1006 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
1008 PetscCall(
PicurvAssertRealNear(1.0, ubcs[ubcs_k][ubcs_j][ubcs_i].x, 1.0e-12,
"outlet handler should copy Ucat into Ubcs.x"));
1009 PetscCall(
PicurvAssertRealNear(1.0, ubcs[ubcs_k][ubcs_j][ubcs_i].y, 1.0e-12,
"outlet handler should copy Ucat into Ubcs.y"));
1010 PetscCall(
PicurvAssertRealNear(1.0, ubcs[ubcs_k][ubcs_j][ubcs_i].z, 1.0e-12,
"outlet handler should copy Ucat into Ubcs.z"));
1011 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
1012 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Bcs.
Ubcs, &ubcs));
1013 PetscCall(
PicurvAssertRealNear(0.0, local_inflow, 1.0e-12,
"outlet handler should not add to inflow"));
1014 PetscCall(
PicurvAssertRealNear(1.2 * area, local_outflow, 1.0e-12,
"outlet PostStep should report the corrected flux"));
1021 PetscFunctionReturn(0);
1029 PetscErrorCode ierr;
1048 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv boundary tests");
1053 ierr =
PicurvRunTests(
"unit-boundaries", cases,
sizeof(cases) /
sizeof(cases[0]));
1059 ierr = PetscFinalize();
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 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 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.
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 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 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.
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 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 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 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 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 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 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 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 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 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
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...
const PetscReal * global_inflow_sum
const PetscReal * global_farfield_inflow_sum
BCFace
Identifies the six logical faces of a structured computational block.
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.