PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Functions
test_boundaries.c File Reference

C unit tests for boundary factories, inlet ownership, and face helpers. More...

#include "test_support.h"
#include "Boundaries.h"
#include "wallfunction.h"
Include dependency graph for test_boundaries.c:

Go to the source code of this file.

Functions

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 DestroyBoundaryHandler (BoundaryCondition **bc_ptr)
 Destroys one boundary-condition handler allocated by a boundary test.
 
static PetscInt InteriorSampleIndex (PetscInt nodes)
 Chooses a stable interior node index for face-sample assertions on tiny test grids.
 
static PetscReal GetFaceNormalComponent (Cmpnts value, BCFace face)
 Extracts the velocity component aligned with the supplied boundary-face normal.
 
static PetscReal GetFaceOrientationSign (BCFace face)
 Returns the sign convention used for face-normal flux expectations.
 
static const char * GetInletParamKey (BCFace face)
 Maps an inlet face to the matching configuration key used by the handler parser.
 
static PetscReal GetFaceInteriorPointCount (const UserCtx *user, BCFace face)
 Computes the number of face-interior sample points for a given boundary face.
 
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.
 
static void ResetBoundaryFaceConfig (BoundaryFaceConfig *cfg)
 Resets one boundary-face configuration entry to a neutral test-local baseline.
 
static PetscErrorCode TestCanRankServiceFaceMatchesInletWhenDefined (void)
 Tests that face-service detection matches a defined inlet face.
 
static PetscErrorCode TestCanRankServiceInletFaceRequiresDefinition (void)
 Tests that inlet-face service requires an inlet face to be defined.
 
static PetscErrorCode TestBoundaryConditionFactoryAssignments (void)
 Tests the boundary-condition factory assignments for representative handlers.
 
static PetscErrorCode ApplyInletAcrossFirstSteps (BoundaryCondition *bc, BCContext *ctx)
 Reapplies a static inlet through the first three time-step contexts.
 
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 TestInletProfileFromFileHandlerBehavior (void)
 Tests prescribed-flow inlet handler loading and applying one Z-face profile.
 
static PetscErrorCode TestBoundaryConditionFactoryRejectsUnsupportedHandler (void)
 Tests that unsupported handler types are rejected by the factory.
 
static PetscErrorCode TestGetDeterministicFaceGridLocationFaceMatrix (void)
 Tests deterministic inlet-face grid-location helpers across all faces.
 
static PetscErrorCode TestGetRandomCellAndLogicalCoordsOnInletFaceMatrix (void)
 Tests random inlet-face cell selection across all supported faces.
 
static PetscErrorCode TestInletConstantVelocityHandlerBehavior (void)
 Tests constant inlet handler initialization and face writes on a Z inlet.
 
static PetscErrorCode TestInletCommonApplicationCurvilinearMetric (void)
 Verifies curvilinear projection and Cartesian dummy reflection for the shared inlet path.
 
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 TestInletParabolicProfileHandlerBehavior (void)
 Tests parabolic inlet handler shape on a tiny Z-face.
 
static PetscErrorCode TestInletParabolicProfileUniformAlongPeriodicAxis (void)
 Tests that the parabolic inlet is uniform along a periodic cross-stream axis.
 
static PetscErrorCode TestInletParabolicProfileHandlerFaceMatrix (void)
 Tests parabolic-inlet centerline, wall, and flux behavior across all faces.
 
static PetscErrorCode TestOutletConservationHandlerBehavior (void)
 Tests outlet conservation handler correction and post-step flux accounting.
 
static PetscErrorCode TestOutletConservationHandlerFaceMatrix (void)
 Tests outlet-conservation correction and flux accounting across all outlet faces.
 
static PetscErrorCode TestFinalizePostProjectionCellFieldsNonPeriodic (void)
 Tests non-periodic cell-field finalization without modifying Ucont.
 
static PetscErrorCode TestEnforceRHSBoundaryConditionsMatchesRowClassification (void)
 EnforceRHSBoundaryConditions() must zero a row exactly when it is not an unknown.
 
static PetscErrorCode SeedWallFrictionVelocityStorage (UserCtx *user)
 Allocates the wall-model storage the way production allocates it.
 
static PetscErrorCode SeedWallFunctionFixture (UserCtx *user, PetscReal tangential_speed)
 Seeds the fields ApplyWallFunction() reads on a single wall face.
 
static PetscErrorCode TestApplyWallFunctionMatchesTheLogLawItDispatches (void)
 Tests that the wall-function integration reproduces the model it dispatches to.
 
static PetscErrorCode TestWallFunctionModelSelectorChoosesTheLaw (void)
 Tests that the model selector actually changes which law is applied.
 
static PetscErrorCode TestApplyWallFunctionSkipsNonWallFaces (void)
 Tests that a face which is not a wall is left alone by the wall function.
 
static PetscErrorCode TestWallModelFrictionVelocityRespondsToTheFlow (void)
 Tests that every wall law's friction velocity responds to the flow it is given.
 
static PetscErrorCode TestWallFrictionVelocityStorageMatchesTheCatalog (void)
 Tests that the friction-velocity storage matches what the field catalog declares.
 
static PetscErrorCode TestWallModelDiagnosticsRecordTheCorrectedCells (void)
 Tests that the wall pass records the statistics its diagnostics report.
 
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.
 

Detailed Description

C unit tests for boundary factories, inlet ownership, and face helpers.

Definition in file test_boundaries.c.

Function Documentation

◆ AppendBCParam()

static PetscErrorCode AppendBCParam ( BC_Param **  head,
const char *  key,
const char *  value 
)
static

Appends one key/value pair to a linked list of boundary-condition parameters.

Definition at line 14 of file test_boundaries.c.

15{
16 BC_Param *node = NULL;
17 BC_Param **cursor = head;
18
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));
24 while (*cursor) {
25 cursor = &(*cursor)->next;
26 }
27 *cursor = node;
28 PetscFunctionReturn(0);
29}
struct BC_Param_s * next
Definition variables.h:363
A node in a linked list for storing key-value parameters from the bcs.dat file.
Definition variables.h:360
A generic C-style linked list node for integers.
Definition variables.h:463
Here is the caller graph for this function:

◆ DestroyBoundaryHandler()

static PetscErrorCode DestroyBoundaryHandler ( BoundaryCondition **  bc_ptr)
static

Destroys one boundary-condition handler allocated by a boundary test.

Definition at line 34 of file test_boundaries.c.

35{
36 BoundaryCondition *bc = NULL;
37 PetscFunctionBeginUser;
38 if (!bc_ptr || !*bc_ptr) PetscFunctionReturn(0);
39
40 bc = *bc_ptr;
41 if (bc->Destroy) {
42 PetscCall(bc->Destroy(bc));
43 }
44 PetscCall(PetscFree(bc));
45 *bc_ptr = NULL;
46 PetscFunctionReturn(0);
47}
The "virtual table" struct for a boundary condition handler object.
Definition variables.h:377
PetscErrorCode(* Destroy)(BoundaryCondition *self)
Definition variables.h:386
Here is the caller graph for this function:

◆ InteriorSampleIndex()

static PetscInt InteriorSampleIndex ( PetscInt  nodes)
static

Chooses a stable interior node index for face-sample assertions on tiny test grids.

Definition at line 52 of file test_boundaries.c.

53{
54 PetscInt index = 3;
55
56 if (index >= nodes - 1) {
57 index = nodes - 2;
58 }
59 if (index < 1) {
60 index = 1;
61 }
62 return index;
63}
Here is the caller graph for this function:

◆ GetFaceNormalComponent()

static PetscReal GetFaceNormalComponent ( Cmpnts  value,
BCFace  face 
)
static

Extracts the velocity component aligned with the supplied boundary-face normal.

Definition at line 68 of file test_boundaries.c.

69{
70 switch (face) {
71 case BC_FACE_NEG_X:
72 case BC_FACE_POS_X:
73 return value.x;
74 case BC_FACE_NEG_Y:
75 case BC_FACE_POS_Y:
76 return value.y;
77 case BC_FACE_NEG_Z:
78 case BC_FACE_POS_Z:
79 return value.z;
80 }
81 return 0.0;
82}
PetscScalar x
Definition variables.h:122
PetscScalar z
Definition variables.h:122
PetscScalar y
Definition variables.h:122
@ BC_FACE_NEG_X
Definition variables.h:288
@ BC_FACE_POS_Z
Definition variables.h:290
@ BC_FACE_POS_Y
Definition variables.h:289
@ BC_FACE_NEG_Z
Definition variables.h:290
@ BC_FACE_POS_X
Definition variables.h:288
@ BC_FACE_NEG_Y
Definition variables.h:289
Here is the caller graph for this function:

◆ GetFaceOrientationSign()

static PetscReal GetFaceOrientationSign ( BCFace  face)
static

Returns the sign convention used for face-normal flux expectations.

Definition at line 87 of file test_boundaries.c.

88{
89 switch (face) {
90 case BC_FACE_NEG_X:
91 case BC_FACE_NEG_Y:
92 case BC_FACE_NEG_Z:
93 return 1.0;
94 case BC_FACE_POS_X:
95 case BC_FACE_POS_Y:
96 case BC_FACE_POS_Z:
97 return -1.0;
98 }
99 return 0.0;
100}
Here is the caller graph for this function:

◆ GetInletParamKey()

static const char * GetInletParamKey ( BCFace  face)
static

Maps an inlet face to the matching configuration key used by the handler parser.

Definition at line 105 of file test_boundaries.c.

106{
107 switch (face) {
108 case BC_FACE_NEG_X:
109 case BC_FACE_POS_X:
110 return "vx";
111 case BC_FACE_NEG_Y:
112 case BC_FACE_POS_Y:
113 return "vy";
114 case BC_FACE_NEG_Z:
115 case BC_FACE_POS_Z:
116 return "vz";
117 }
118 return "";
119}
Here is the caller graph for this function:

◆ GetFaceInteriorPointCount()

static PetscReal GetFaceInteriorPointCount ( const UserCtx *  user,
BCFace  face 
)
static

Computes the number of face-interior sample points for a given boundary face.

Definition at line 124 of file test_boundaries.c.

125{
126 switch (face) {
127 case BC_FACE_NEG_X:
128 case BC_FACE_POS_X:
129 return (PetscReal)(user->info.my - 2) * (PetscReal)(user->info.mz - 2);
130 case BC_FACE_NEG_Y:
131 case BC_FACE_POS_Y:
132 return (PetscReal)(user->info.mx - 2) * (PetscReal)(user->info.mz - 2);
133 case BC_FACE_NEG_Z:
134 case BC_FACE_POS_Z:
135 return (PetscReal)(user->info.mx - 2) * (PetscReal)(user->info.my - 2);
136 }
137 return 0.0;
138}
DMDALocalInfo info
Definition variables.h:1086
Here is the caller graph for this function:

◆ GetRepresentativeFaceSlots()

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 
)
static

Selects representative Ucont and Ubcs slots for face-matrix assertions.

Definition at line 143 of file test_boundaries.c.

151{
152 const PetscInt sample_i = InteriorSampleIndex(user->info.mx);
153 const PetscInt sample_j = InteriorSampleIndex(user->info.my);
154 const PetscInt sample_k = InteriorSampleIndex(user->info.mz);
155
156 PetscFunctionBeginUser;
157 switch (face) {
158 case BC_FACE_NEG_X:
159 *ucont_k = sample_k; *ucont_j = sample_j; *ucont_i = 0;
160 *ubcs_k = sample_k; *ubcs_j = sample_j; *ubcs_i = 0;
161 break;
162 case BC_FACE_POS_X:
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;
165 break;
166 case BC_FACE_NEG_Y:
167 *ucont_k = sample_k; *ucont_j = 0; *ucont_i = sample_i;
168 *ubcs_k = sample_k; *ubcs_j = 0; *ubcs_i = sample_i;
169 break;
170 case BC_FACE_POS_Y:
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;
173 break;
174 case BC_FACE_NEG_Z:
175 *ucont_k = 0; *ucont_j = sample_j; *ucont_i = sample_i;
176 *ubcs_k = 0; *ubcs_j = sample_j; *ubcs_i = sample_i;
177 break;
178 case BC_FACE_POS_Z:
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;
181 break;
182 }
183 PetscFunctionReturn(0);
184}
static PetscInt InteriorSampleIndex(PetscInt nodes)
Chooses a stable interior node index for face-sample assertions on tiny test grids.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetParabolicSampleSlots()

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 
)
static

Selects center, off-center, and wall-adjacent slots for parabolic-face checks.

Definition at line 189 of file test_boundaries.c.

203{
204 PetscFunctionBeginUser;
205 switch (face) {
206 case BC_FACE_NEG_X:
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;
211 break;
212 case BC_FACE_POS_X:
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;
217 break;
218 case BC_FACE_NEG_Y:
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;
223 break;
224 case BC_FACE_POS_Y:
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;
229 break;
230 case BC_FACE_NEG_Z:
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;
235 break;
236 case BC_FACE_POS_Z:
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;
241 break;
242 }
243 PetscFunctionReturn(0);
244}
Here is the caller graph for this function:

◆ ResetBoundaryFaceConfig()

static void ResetBoundaryFaceConfig ( BoundaryFaceConfig *  cfg)
static

Resets one boundary-face configuration entry to a neutral test-local baseline.

Definition at line 249 of file test_boundaries.c.

250{
253 cfg->face_id = BC_FACE_NEG_X;
254 if (cfg->params) {
256 cfg->params = NULL;
257 }
258}
void FreeBC_ParamList(BC_Param *head)
Frees an entire linked list of boundary-condition parameters.
Definition io.c:672
@ INTERFACE
Definition variables.h:311
@ BC_HANDLER_UNDEFINED
Definition variables.h:330
BCHandlerType handler_type
Definition variables.h:393
BC_Param * params
Definition variables.h:394
BCType mathematical_type
Definition variables.h:392
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestCanRankServiceFaceMatchesInletWhenDefined()

static PetscErrorCode TestCanRankServiceFaceMatchesInletWhenDefined ( void  )
static

Tests that face-service detection matches a defined inlet face.

Definition at line 263 of file test_boundaries.c.

264{
265 SimCtx *simCtx = NULL;
266 UserCtx *user = NULL;
267 PetscBool generic_face = PETSC_FALSE;
268 PetscBool inlet_face = PETSC_FALSE;
269 PetscBool any_face_serviceable = PETSC_FALSE;
270
271 PetscFunctionBeginUser;
272 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 4, 4, 4));
273
274 for (BCFace face = BC_FACE_NEG_X; face <= BC_FACE_POS_Z; face++) {
275 PetscCall(CanRankServiceFace(&user->info, user->IM, user->JM, user->KM, face, &generic_face));
276 if (generic_face) {
277 any_face_serviceable = PETSC_TRUE;
278 }
279
280 user->inletFaceDefined = PETSC_TRUE;
281 user->identifiedInletBCFace = face;
282 PetscCall(CanRankServiceInletFace(user, &user->info, user->IM, user->JM, user->KM, &inlet_face));
283 PetscCall(PicurvAssertBool((PetscBool)(inlet_face == generic_face), "inlet face service check should match generic face check"));
284 }
285 PetscCall(PicurvAssertBool(any_face_serviceable, "at least one global face should be serviceable in a non-degenerate domain"));
286
287 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
288 PetscFunctionReturn(0);
289}
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,...
Definition Boundaries.c:11
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.
Definition Boundaries.c:127
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 PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
Destroys minimal SimCtx/UserCtx fixtures and all owned PETSc objects.
PetscErrorCode PicurvAssertBool(PetscBool value, const char *context)
Asserts that one boolean condition is true.
PetscBool inletFaceDefined
Definition variables.h:1100
BCFace identifiedInletBCFace
Definition variables.h:1101
PetscInt KM
Definition variables.h:1088
PetscInt JM
Definition variables.h:1088
PetscInt IM
Definition variables.h:1088
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:287
The master context for the entire simulation.
Definition variables.h:859
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1074
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestCanRankServiceInletFaceRequiresDefinition()

static PetscErrorCode TestCanRankServiceInletFaceRequiresDefinition ( void  )
static

Tests that inlet-face service requires an inlet face to be defined.

Definition at line 294 of file test_boundaries.c.

295{
296 SimCtx *simCtx = NULL;
297 UserCtx *user = NULL;
298 PetscBool can_service = PETSC_FALSE;
299
300 PetscFunctionBeginUser;
301 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 4, 4, 4));
302
303 user->inletFaceDefined = PETSC_FALSE;
304 for (BCFace face = BC_FACE_NEG_X; face <= BC_FACE_POS_Z; face++) {
305 user->identifiedInletBCFace = face;
306 PetscCall(CanRankServiceInletFace(user, &user->info, user->IM, user->JM, user->KM, &can_service));
307 PetscCall(PicurvAssertBool((PetscBool)!can_service, "undefined inlet face should not be serviceable"));
308 }
309
310 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
311 PetscFunctionReturn(0);
312}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestBoundaryConditionFactoryAssignments()

static PetscErrorCode TestBoundaryConditionFactoryAssignments ( void  )
static

Tests the boundary-condition factory assignments for representative handlers.

Definition at line 317 of file test_boundaries.c.

318{
319 BoundaryCondition *wall = NULL;
320 BoundaryCondition *inlet = NULL;
321 BoundaryCondition *parabolic = NULL;
322 BoundaryCondition *profile = NULL;
323
324 PetscFunctionBeginUser;
326 PetscCall(PicurvAssertBool((PetscBool)(wall != NULL), "factory should return wall handler"));
327 PetscCall(PicurvAssertIntEqual(BC_PRIORITY_WALL, wall->priority, "wall handler priority"));
328 PetscCall(PicurvAssertBool((PetscBool)(wall->Apply != NULL), "wall handler should expose Apply"));
329 PetscCall(PicurvAssertBool((PetscBool)(wall->Destroy == NULL), "wall handler should not require destroy hook"));
330 PetscCall(DestroyBoundaryHandler(&wall));
331
333 PetscCall(PicurvAssertBool((PetscBool)(inlet != NULL), "factory should return inlet handler"));
334 PetscCall(PicurvAssertIntEqual(BC_PRIORITY_INLET, inlet->priority, "inlet handler priority"));
335 PetscCall(PicurvAssertBool((PetscBool)(inlet->Initialize != NULL), "inlet handler should expose Initialize"));
336 PetscCall(PicurvAssertBool((PetscBool)(inlet->Apply != NULL), "inlet handler should expose Apply"));
337 PetscCall(PicurvAssertBool((PetscBool)(inlet->Destroy != NULL), "inlet handler should expose its destroy hook"));
340 PetscCall(PicurvAssertBool((PetscBool)(inlet->Apply == parabolic->Apply &&
341 inlet->Apply == profile->Apply),
342 "all profile-producing inlet modes should share one application hook"));
343 PetscCall(DestroyBoundaryHandler(&inlet));
344 PetscCall(DestroyBoundaryHandler(&parabolic));
345 PetscCall(DestroyBoundaryHandler(&profile));
346
347 PetscFunctionReturn(0);
348}
PetscErrorCode BoundaryCondition_Create(BCHandlerType handler_type, BoundaryCondition **new_bc_ptr)
(Private) Creates and configures a specific BoundaryCondition handler object.
Definition Boundaries.c:729
PetscErrorCode(* Initialize)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:381
PetscErrorCode(* Apply)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:383
BCPriorityType priority
Definition variables.h:379
static PetscErrorCode DestroyBoundaryHandler(BoundaryCondition **bc_ptr)
Destroys one boundary-condition handler allocated by a boundary test.
PetscErrorCode PicurvAssertIntEqual(PetscInt expected, PetscInt actual, const char *context)
Asserts that two integer values are equal.
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:335
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:334
@ BC_HANDLER_INLET_PROFILE_FROM_FILE
Definition variables.h:336
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:331
@ BC_PRIORITY_WALL
Definition variables.h:351
@ BC_PRIORITY_INLET
Definition variables.h:349
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ApplyInletAcrossFirstSteps()

static PetscErrorCode ApplyInletAcrossFirstSteps ( BoundaryCondition *  bc,
BCContext *  ctx 
)
static

Reapplies a static inlet through the first three time-step contexts.

Definition at line 353 of file test_boundaries.c.

354{
355 PetscFunctionBeginUser;
356 for (PetscInt step = 0; step < 3; step++) {
357 PetscReal local_inflow = 0.0, local_outflow = 0.0;
358 ctx->user->simCtx->step = step;
359 ctx->user->simCtx->ti = 0.1 * (PetscReal)step;
360 PetscCall(VecSet(ctx->user->Ucont, -91.0));
361 PetscCall(VecSet(ctx->user->Bcs.Ubcs, -92.0));
362 PetscCall(bc->PreStep(bc, ctx, &local_inflow, &local_outflow));
363 PetscCall(bc->Apply(bc, ctx));
364 }
365 PetscFunctionReturn(0);
366}
PetscErrorCode(* PreStep)(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow, PetscReal *local_outflow)
Definition variables.h:382
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1077
Vec Ucont
Definition variables.h:1113
Vec Ubcs
Physical Cartesian velocity at boundary faces. Full 3D array but only boundary-face entries are meani...
Definition variables.h:149
UserCtx * user
Definition variables.h:368
PetscInt step
Definition variables.h:867
PetscReal ti
Definition variables.h:868
Here is the caller graph for this function:

◆ TestBoundaryConditionFactoryImplementedHandlerMatrix()

static PetscErrorCode TestBoundaryConditionFactoryImplementedHandlerMatrix ( void  )
static

Tests the implemented-handler matrix exposed by the factory.

Definition at line 371 of file test_boundaries.c.

372{
373 struct HandlerExpectation {
374 BCHandlerType handler;
375 BCPriorityType priority;
376 PetscBool expect_apply;
377 PetscBool expect_initialize;
378 };
379 const struct HandlerExpectation expectations[] = {
380 {BC_HANDLER_WALL_NOSLIP, BC_PRIORITY_WALL, PETSC_TRUE, PETSC_FALSE},
381 {BC_HANDLER_INLET_CONSTANT_VELOCITY, BC_PRIORITY_INLET, PETSC_TRUE, PETSC_TRUE},
382 {BC_HANDLER_INLET_PARABOLIC, BC_PRIORITY_INLET, PETSC_TRUE, PETSC_TRUE},
383 {BC_HANDLER_INLET_PROFILE_FROM_FILE, BC_PRIORITY_INLET, PETSC_TRUE, PETSC_TRUE},
384 {BC_HANDLER_OUTLET_CONSERVATION, BC_PRIORITY_OUTLET, PETSC_TRUE, PETSC_FALSE},
385 };
386
387 PetscFunctionBeginUser;
388 for (size_t i = 0; i < sizeof(expectations) / sizeof(expectations[0]); ++i) {
389 BoundaryCondition *bc = NULL;
390 PetscCall(BoundaryCondition_Create(expectations[i].handler, &bc));
391 PetscCall(PicurvAssertBool((PetscBool)(bc != NULL), "factory should allocate a handler object"));
392 PetscCall(PicurvAssertIntEqual(expectations[i].priority, bc->priority, "handler priority should match expectation"));
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"));
395 PetscCall(DestroyBoundaryHandler(&bc));
396 }
397 PetscFunctionReturn(0);
398}
BCHandlerType
Defines the specific computational "strategy" for a boundary handler.
Definition variables.h:329
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:338
BCPriorityType
Definition variables.h:347
@ BC_PRIORITY_OUTLET
Definition variables.h:352
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WritePicSliceForTests()

static PetscErrorCode WritePicSliceForTests ( const char *  path,
PetscInt  n1,
PetscInt  n2,
PetscReal  base 
)
static

Writes a tiny canonical PICSLICE profile for boundary handler tests.

Definition at line 403 of file test_boundaries.c.

404{
405 FILE *fd = NULL;
406
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.");
417 }
418 }
419 fclose(fd);
420 PetscFunctionReturn(0);
421}
Here is the caller graph for this function:

◆ TestInletProfileFromFileHandlerBehavior()

static PetscErrorCode TestInletProfileFromFileHandlerBehavior ( void  )
static

Tests prescribed-flow inlet handler loading and applying one Z-face profile.

Definition at line 426 of file test_boundaries.c.

427{
428 SimCtx *simCtx = NULL;
429 UserCtx *user = NULL;
430 BoundaryCondition *bc = NULL;
431 BCContext ctx;
432 Cmpnts ***ucont = NULL;
433 Cmpnts ***ubcs = NULL;
434 char profile_path[PETSC_MAX_PATH_LEN];
435 PetscReal local_inflow = 0.0;
436 PetscReal local_outflow = 0.0;
437
438 PetscFunctionBeginUser;
439 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
440 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 5, 5, 5));
441 PetscCall(PicurvPopulateIdentityMetrics(user));
442
443 PetscCall(PetscSNPrintf(profile_path, sizeof(profile_path), "/tmp/picurv_unit_profile_%d.picslice", (int)getpid()));
444 PetscCall(WritePicSliceForTests(profile_path, user->JM - 1, user->IM - 1, 2.0));
445
449 PetscCall(AppendBCParam(&user->boundary_faces[BC_FACE_NEG_Z].params, "source_file", profile_path));
450
451 PetscCall(VecSet(user->Ucont, 0.0));
452 PetscCall(VecSet(user->Bcs.Ubcs, 0.0));
453
454 ctx.user = user;
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"));
460 PetscCall(bc->Initialize(bc, &ctx));
461 PetscCall(ApplyInletAcrossFirstSteps(bc, &ctx));
462 PetscCall(bc->PostStep(bc, &ctx, &local_inflow, &local_outflow));
463
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"));
475
476 PetscCall(DestroyBoundaryHandler(&bc));
478 user->boundary_faces[BC_FACE_NEG_Z].params = NULL;
479 remove(profile_path);
480 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
481 PetscFunctionReturn(0);
482}
PetscErrorCode(* PostStep)(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow, PetscReal *local_outflow)
Definition variables.h:384
static PetscErrorCode ApplyInletAcrossFirstSteps(BoundaryCondition *bc, BCContext *ctx)
Reapplies a static inlet through the first three time-step contexts.
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 WritePicSliceForTests(const char *path, PetscInt n1, PetscInt n2, PetscReal base)
Writes a tiny canonical PICSLICE profile for boundary handler tests.
PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
PetscErrorCode PicurvPopulateIdentityMetrics(UserCtx *user)
Populates identity metric vectors on the minimal grid fixture.
@ INLET
Definition variables.h:316
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:1099
BCFace face_id
Definition variables.h:369
Provides execution context for a boundary condition handler.
Definition variables.h:367
A 3D point or vector with PetscScalar components.
Definition variables.h:121
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestBoundaryConditionFactoryRejectsUnsupportedHandler()

static PetscErrorCode TestBoundaryConditionFactoryRejectsUnsupportedHandler ( void  )
static

Tests that unsupported handler types are rejected by the factory.

Definition at line 487 of file test_boundaries.c.

488{
489 BoundaryCondition *bc = NULL;
490 PetscErrorCode ierr_create;
491
492 PetscFunctionBeginUser;
493 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
495 PetscCall(PetscPopErrorHandler());
496
497 PetscCall(PicurvAssertBool((PetscBool)(ierr_create != 0), "unsupported handler should return a non-zero error code"));
498 if (bc) {
499 PetscCall(PetscFree(bc));
500 }
501 PetscFunctionReturn(0);
502}
@ BC_HANDLER_OUTLET_PRESSURE
Definition variables.h:339
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestGetDeterministicFaceGridLocationFaceMatrix()

static PetscErrorCode TestGetDeterministicFaceGridLocationFaceMatrix ( void  )
static

Tests deterministic inlet-face grid-location helpers across all faces.

Definition at line 507 of file test_boundaries.c.

508{
509 SimCtx *simCtx = NULL;
510 UserCtx *user = NULL;
511
512 PetscFunctionBeginUser;
513 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 8, 8, 8));
514 simCtx->np = 128;
515
516 for (BCFace face = BC_FACE_NEG_X; face <= BC_FACE_POS_Z; ++face) {
517 PetscInt ci = -1, cj = -1, ck = -1;
518 PetscReal xi = -1.0, eta = -1.0, zta = -1.0;
519 PetscBool placed = PETSC_FALSE;
520
521 user->identifiedInletBCFace = face;
523 user, &user->info,
524 user->info.xs, user->info.ys, user->info.zs,
525 user->info.mx, user->info.my, user->info.mz,
526 0,
527 &ci, &cj, &ck, &xi, &eta, &zta, &placed));
528
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)"));
536
537 if (face == BC_FACE_NEG_X || face == BC_FACE_POS_X) {
538 PetscCall(PicurvAssertRealNear(0.5, xi, 1.0e-10, "deterministic x-face placement should sit halfway into boundary-adjacent cell"));
539 } else if (face == BC_FACE_NEG_Y || face == BC_FACE_POS_Y) {
540 PetscCall(PicurvAssertRealNear(0.5, eta, 1.0e-10, "deterministic y-face placement should sit halfway into boundary-adjacent cell"));
541 } else {
542 PetscCall(PicurvAssertRealNear(0.5, zta, 1.0e-10, "deterministic z-face placement should sit halfway into boundary-adjacent cell"));
543 }
544 }
545
546 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
547 PetscFunctionReturn(0);
548}
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.
Definition Boundaries.c:213
PetscInt np
Definition variables.h:990
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestGetRandomCellAndLogicalCoordsOnInletFaceMatrix()

static PetscErrorCode TestGetRandomCellAndLogicalCoordsOnInletFaceMatrix ( void  )
static

Tests random inlet-face cell selection across all supported faces.

Definition at line 553 of file test_boundaries.c.

554{
555 SimCtx *simCtx = NULL;
556 UserCtx *user = NULL;
557 PetscRandom rand_i = NULL, rand_j = NULL, rand_k = NULL;
558
559 PetscFunctionBeginUser;
560 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 8, 8, 8));
561
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));
571
572 for (BCFace face = BC_FACE_NEG_X; face <= BC_FACE_POS_Z; ++face) {
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;
576
577 user->identifiedInletBCFace = face;
579 user, &user->info,
580 user->info.xs, user->info.ys, user->info.zs,
581 user->info.mx, user->info.my, user->info.mz,
582 &rand_i, &rand_j, &rand_k,
583 &ci, &cj, &ck,
584 &xi, &eta, &zta));
585
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]"));
592
593 switch (face) {
594 case BC_FACE_NEG_X:
595 PetscCall(PicurvAssertBool((PetscBool)(xi <= boundary_eps), "NEG_X should pin xi near 0"));
596 break;
597 case BC_FACE_POS_X:
598 PetscCall(PicurvAssertBool((PetscBool)(xi >= 1.0 - boundary_eps), "POS_X should pin xi near 1"));
599 break;
600 case BC_FACE_NEG_Y:
601 PetscCall(PicurvAssertBool((PetscBool)(eta <= boundary_eps), "NEG_Y should pin eta near 0"));
602 break;
603 case BC_FACE_POS_Y:
604 PetscCall(PicurvAssertBool((PetscBool)(eta >= 1.0 - boundary_eps), "POS_Y should pin eta near 1"));
605 break;
606 case BC_FACE_NEG_Z:
607 PetscCall(PicurvAssertBool((PetscBool)(zta <= boundary_eps), "NEG_Z should pin zta near 0"));
608 break;
609 case BC_FACE_POS_Z:
610 PetscCall(PicurvAssertBool((PetscBool)(zta >= 1.0 - boundary_eps), "POS_Z should pin zta near 1"));
611 break;
612 }
613 }
614
615 PetscCall(PetscRandomDestroy(&rand_i));
616 PetscCall(PetscRandomDestroy(&rand_j));
617 PetscCall(PetscRandomDestroy(&rand_k));
618 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
619 PetscFunctionReturn(0);
620}
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...
Definition Boundaries.c:400
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestInletConstantVelocityHandlerBehavior()

static PetscErrorCode TestInletConstantVelocityHandlerBehavior ( void  )
static

Tests constant inlet handler initialization and face writes on a Z inlet.

Definition at line 625 of file test_boundaries.c.

626{
627 SimCtx *simCtx = NULL;
628 UserCtx *user = NULL;
629 BoundaryCondition *bc = NULL;
630 BCContext ctx;
631 Cmpnts ***ucont = NULL;
632 Cmpnts ***ubcs = NULL;
633 PetscReal local_inflow = 0.0;
634 PetscReal local_outflow = 0.0;
635 PetscReal expected_flux = 0.0;
636
637 PetscFunctionBeginUser;
638 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
639 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
640 PetscCall(VecSet(user->Ucont, 0.0));
641 PetscCall(VecSet(user->Bcs.Ubcs, 0.0));
642 PetscCall(PicurvPopulateIdentityMetrics(user));
643
647 PetscCall(AppendBCParam(&user->boundary_faces[BC_FACE_NEG_Z].params, "vz", "2.5"));
648
649 ctx.user = user;
652 PetscCall(bc->Initialize(bc, &ctx));
653 PetscCall(ApplyInletAcrossFirstSteps(bc, &ctx));
654 PetscCall(bc->PostStep(bc, &ctx, &local_inflow, &local_outflow));
655
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));
662
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"));
666
667 PetscCall(DestroyBoundaryHandler(&bc));
669 user->boundary_faces[BC_FACE_NEG_Z].params = NULL;
670 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
671 PetscFunctionReturn(0);
672}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestInletCommonApplicationCurvilinearMetric()

static PetscErrorCode TestInletCommonApplicationCurvilinearMetric ( void  )
static

Verifies curvilinear projection and Cartesian dummy reflection for the shared inlet path.

Definition at line 677 of file test_boundaries.c.

678{
679 SimCtx *simCtx = NULL;
680 UserCtx *user = NULL;
681 BoundaryCondition *bc = NULL;
682 BCContext ctx;
683 Cmpnts ***zet = NULL, ***ucont = NULL, ***ubcs = NULL, ***ucat = NULL;
684
685 PetscFunctionBeginUser;
686 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
687 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
688 PetscCall(PicurvPopulateIdentityMetrics(user));
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));
694
698 PetscCall(AppendBCParam(&user->boundary_faces[BC_FACE_NEG_Z].params, "vz", "2.0"));
699 ctx.user = user;
702 PetscCall(bc->Initialize(bc, &ctx));
703 PetscCall(bc->Apply(bc, &ctx));
704
705 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &ucont));
706 PetscCall(DMDAVecGetArrayRead(user->fda, user->Bcs.Ubcs, &ubcs));
707 PetscCall(PicurvAssertRealNear(10.0, ucont[0][3][3].z, 1.0e-12,
708 "Ucont should be the Cartesian inlet velocity dotted with the face metric"));
709 PetscCall(PicurvAssertRealNear(0.0, ubcs[0][3][3].x, 1.0e-12,
710 "curvilinear normal velocity should preserve its Cartesian x component"));
711 PetscCall(PicurvAssertRealNear(1.2, ubcs[0][3][3].y, 1.0e-12,
712 "curvilinear normal velocity should include Cartesian y"));
713 PetscCall(PicurvAssertRealNear(1.6, ubcs[0][3][3].z, 1.0e-12,
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));
717
718 PetscCall(VecSet(user->Ucat, 1.0));
719 PetscCall(UpdateDummyCells(user, FIELD_ID_UCAT));
720 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucat, &ucat));
721 PetscCall(PicurvAssertRealNear(-1.0, ucat[0][3][3].x, 1.0e-12,
722 "dummy reflection should act on Cartesian x"));
723 PetscCall(PicurvAssertRealNear(1.4, ucat[0][3][3].y, 1.0e-12,
724 "dummy reflection should act on Cartesian y"));
725 PetscCall(PicurvAssertRealNear(2.2, ucat[0][3][3].z, 1.0e-12,
726 "dummy reflection should act on Cartesian z"));
727 PetscCall(PicurvAssertRealNear(1.0, ucat[1][3][3].y, 1.0e-12,
728 "dummy reflection should not overwrite the adjacent interior value"));
729 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucat, &ucat));
730
731 PetscCall(DestroyBoundaryHandler(&bc));
733 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
734 PetscFunctionReturn(0);
735}
PetscErrorCode UpdateDummyCells(UserCtx *user, FieldId field_id)
Fills the dummy cells on the non-periodic faces of one cell-centred field.
@ FIELD_ID_UCAT
static void ResetBoundaryFaceConfig(BoundaryFaceConfig *cfg)
Resets one boundary-face configuration entry to a neutral test-local baseline.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestWallNoSlipHandlerFaceMatrix()

static PetscErrorCode TestWallNoSlipHandlerFaceMatrix ( void  )
static

Tests wall no-slip application across the full face matrix.

Definition at line 740 of file test_boundaries.c.

741{
742 SimCtx *simCtx = NULL;
743 UserCtx *user = NULL;
744 BoundaryCondition *bc = NULL;
745 BCContext ctx;
746 Cmpnts ***ucont = NULL;
747 Cmpnts ***ubcs = NULL;
748 const BCFace faces[] = {
752 };
753
754 PetscFunctionBeginUser;
755 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
756 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
758
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;
762
763 PetscCall(VecSet(user->Ucont, 5.0));
764 PetscCall(VecSet(user->Bcs.Ubcs, 7.0));
765 ctx.user = user;
766 ctx.face_id = face;
767 PetscCall(bc->Apply(bc, &ctx));
768
769 PetscCall(GetRepresentativeFaceSlots(user, face, &ucont_k, &ucont_j, &ucont_i, &ubcs_k, &ubcs_j, &ubcs_i));
770 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &ucont));
771 PetscCall(DMDAVecGetArrayRead(user->fda, user->Bcs.Ubcs, &ubcs));
772 PetscCall(PicurvAssertRealNear(0.0, GetFaceNormalComponent(ucont[ucont_k][ucont_j][ucont_i], face), 1.0e-12, "wall no-slip should zero the normal flux component"));
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));
778 }
779
780 PetscCall(DestroyBoundaryHandler(&bc));
781 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
782 PetscFunctionReturn(0);
783}
static PetscReal GetFaceNormalComponent(Cmpnts value, BCFace face)
Extracts the velocity component aligned with the supplied boundary-face normal.
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.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestInletConstantVelocityHandlerFaceMatrix()

static PetscErrorCode TestInletConstantVelocityHandlerFaceMatrix ( void  )
static

Tests constant-inlet initialization, flux accounting, and face writes across all faces.

Definition at line 788 of file test_boundaries.c.

789{
790 struct ConstantInletCase {
791 BCFace face;
792 PetscReal velocity;
793 const char *value_text;
794 };
795 const struct ConstantInletCase cases[] = {
796 {BC_FACE_NEG_X, 1.5, "1.5"},
797 {BC_FACE_POS_X, 2.0, "2.0"},
798 {BC_FACE_NEG_Y, 2.5, "2.5"},
799 {BC_FACE_POS_Y, 3.0, "3.0"},
800 {BC_FACE_NEG_Z, 3.5, "3.5"},
801 {BC_FACE_POS_Z, 4.0, "4.0"},
802 };
803 SimCtx *simCtx = NULL;
804 UserCtx *user = NULL;
805 BCContext ctx;
806 Cmpnts ***ucont = NULL;
807 Cmpnts ***ubcs = NULL;
808
809 PetscFunctionBeginUser;
810 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
811 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
812 PetscCall(PicurvPopulateIdentityMetrics(user));
813
814 for (size_t n = 0; n < sizeof(cases) / sizeof(cases[0]); ++n) {
815 const struct ConstantInletCase *test_case = &cases[n];
816 BoundaryCondition *bc = NULL;
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;
820 const PetscReal expected_value = GetFaceOrientationSign(test_case->face) * test_case->velocity;
821 const PetscReal expected_flux = GetFaceInteriorPointCount(user, test_case->face) * expected_value;
822
823 PetscCall(VecSet(user->Ucont, 0.0));
824 PetscCall(VecSet(user->Bcs.Ubcs, 0.0));
825 ResetBoundaryFaceConfig(&user->boundary_faces[test_case->face]);
826 user->boundary_faces[test_case->face].face_id = test_case->face;
827 user->boundary_faces[test_case->face].mathematical_type = INLET;
829 PetscCall(AppendBCParam(&user->boundary_faces[test_case->face].params,
830 GetInletParamKey(test_case->face),
831 test_case->value_text));
832
833 ctx.user = user;
834 ctx.face_id = test_case->face;
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"));
839 PetscCall(bc->Initialize(bc, &ctx));
840 PetscCall(bc->Apply(bc, &ctx));
841 PetscCall(bc->PostStep(bc, &ctx, &local_inflow, &local_outflow));
842
843 PetscCall(GetRepresentativeFaceSlots(user, test_case->face, &ucont_k, &ucont_j, &ucont_i, &ubcs_k, &ubcs_j, &ubcs_i));
844 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &ucont));
845 PetscCall(DMDAVecGetArrayRead(user->fda, user->Bcs.Ubcs, &ubcs));
846 PetscCall(PicurvAssertRealNear(expected_value, GetFaceNormalComponent(ucont[ucont_k][ucont_j][ucont_i], test_case->face), 1.0e-12, "constant inlet should set the face-normal Ucont component"));
847 PetscCall(PicurvAssertRealNear(expected_value, GetFaceNormalComponent(ubcs[ubcs_k][ubcs_j][ubcs_i], test_case->face), 1.0e-12, "constant inlet should set the face-normal Ubcs component"));
848 PetscCall(PicurvAssertRealNear((test_case->face == BC_FACE_NEG_X || test_case->face == BC_FACE_POS_X) ? expected_value : 0.0, ubcs[ubcs_k][ubcs_j][ubcs_i].x, 1.0e-12, "constant inlet should only set the expected Ubcs axis"));
849 PetscCall(PicurvAssertRealNear((test_case->face == BC_FACE_NEG_Y || test_case->face == BC_FACE_POS_Y) ? expected_value : 0.0, ubcs[ubcs_k][ubcs_j][ubcs_i].y, 1.0e-12, "constant inlet should only set the expected Ubcs axis"));
850 PetscCall(PicurvAssertRealNear((test_case->face == BC_FACE_NEG_Z || test_case->face == BC_FACE_POS_Z) ? expected_value : 0.0, ubcs[ubcs_k][ubcs_j][ubcs_i].z, 1.0e-12, "constant inlet should only set the expected Ubcs axis"));
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"));
855
856 PetscCall(DestroyBoundaryHandler(&bc));
857 ResetBoundaryFaceConfig(&user->boundary_faces[test_case->face]);
858 }
859
860 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
861 PetscFunctionReturn(0);
862}
static PetscReal GetFaceInteriorPointCount(const UserCtx *user, BCFace face)
Computes the number of face-interior sample points for a given boundary face.
static PetscReal GetFaceOrientationSign(BCFace face)
Returns the sign convention used for face-normal flux expectations.
static const char * GetInletParamKey(BCFace face)
Maps an inlet face to the matching configuration key used by the handler parser.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestInletParabolicProfileHandlerBehavior()

static PetscErrorCode TestInletParabolicProfileHandlerBehavior ( void  )
static

Tests parabolic inlet handler shape on a tiny Z-face.

Definition at line 867 of file test_boundaries.c.

868{
869 SimCtx *simCtx = NULL;
870 UserCtx *user = NULL;
871 BoundaryCondition *bc = NULL;
872 BCContext ctx;
873 Cmpnts ***ucont = NULL;
874
875 PetscFunctionBeginUser;
876 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
877 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
878 PetscCall(PicurvPopulateIdentityMetrics(user));
879
883 PetscCall(AppendBCParam(&user->boundary_faces[BC_FACE_NEG_Z].params, "v_max", "4.0"));
884
885 ctx.user = user;
888 PetscCall(bc->Initialize(bc, &ctx));
889 PetscCall(ApplyInletAcrossFirstSteps(bc, &ctx));
890
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"));
893 /* Five cells per axis: the wall-adjacent centre sits at s = -0.8, so each factor is 0.36. */
894 PetscCall(PicurvAssertRealNear(4.0 * 0.36 * 0.36, ucont[0][1][1].z, 1.0e-12,
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));
898
899 PetscCall(DestroyBoundaryHandler(&bc));
901 user->boundary_faces[BC_FACE_NEG_Z].params = NULL;
902 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
903 PetscFunctionReturn(0);
904}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestInletParabolicProfileUniformAlongPeriodicAxis()

static PetscErrorCode TestInletParabolicProfileUniformAlongPeriodicAxis ( void  )
static

Tests that the parabolic inlet is uniform along a periodic cross-stream axis.

A channel periodic in i has walls only in j, so its inlet is the exact one-dimensional parabola in j.

Definition at line 911 of file test_boundaries.c.

912{
913 SimCtx *simCtx = NULL;
914 UserCtx *user = NULL;
915 BoundaryCondition *bc = NULL;
916 BCContext ctx;
917 Cmpnts ***ucont = NULL;
918
919 PetscFunctionBeginUser;
920 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
921 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
922 PetscCall(PicurvPopulateIdentityMetrics(user));
923 simCtx->i_periodic = 1;
924
928 PetscCall(AppendBCParam(&user->boundary_faces[BC_FACE_NEG_Z].params, "v_max", "4.0"));
929 ctx.user = user;
932 PetscCall(bc->Initialize(bc, &ctx));
933
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));
939
940 PetscCall(DestroyBoundaryHandler(&bc));
942 user->boundary_faces[BC_FACE_NEG_Z].params = NULL;
943 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
944 PetscFunctionReturn(0);
945}
PetscInt i_periodic
Definition variables.h:953
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestInletParabolicProfileHandlerFaceMatrix()

static PetscErrorCode TestInletParabolicProfileHandlerFaceMatrix ( void  )
static

Tests parabolic-inlet centerline, wall, and flux behavior across all faces.

Definition at line 950 of file test_boundaries.c.

951{
952 const BCFace faces[] = {
956 };
957 SimCtx *simCtx = NULL;
958 UserCtx *user = NULL;
959 BCContext ctx;
960 Cmpnts ***ucont = NULL;
961 Cmpnts ***ubcs = NULL;
962
963 PetscFunctionBeginUser;
964 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
965 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
966 PetscCall(PicurvPopulateIdentityMetrics(user));
967
968 for (size_t n = 0; n < sizeof(faces) / sizeof(faces[0]); ++n) {
969 const BCFace face = faces[n];
970 BoundaryCondition *bc = NULL;
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;
977 const PetscReal expected_center = GetFaceOrientationSign(face) * 4.0;
978
979 PetscCall(VecSet(user->Ucont, 0.0));
980 PetscCall(VecSet(user->Bcs.Ubcs, 0.0));
982 user->boundary_faces[face].face_id = face;
985 PetscCall(AppendBCParam(&user->boundary_faces[face].params, "v_max", "4.0"));
986
987 ctx.user = user;
988 ctx.face_id = face;
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"));
993 PetscCall(bc->Initialize(bc, &ctx));
994 PetscCall(bc->Apply(bc, &ctx));
995 PetscCall(bc->PostStep(bc, &ctx, &local_inflow, &local_outflow));
996
997 PetscCall(GetParabolicSampleSlots(user, face,
998 &center_k, &center_j, &center_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));
1004 PetscCall(PicurvAssertRealNear(expected_center, GetFaceNormalComponent(ucont[center_k][center_j][center_i], face), 1.0e-12, "parabolic inlet should peak at the face centerline"));
1005 PetscCall(PicurvAssertRealNear(expected_center, GetFaceNormalComponent(ubcs[ubcs_center_k][ubcs_center_j][ubcs_center_i], face), 1.0e-12, "parabolic inlet should write the centerline boundary velocity"));
1006 PetscCall(PicurvAssertRealNear(expected_center * 0.36 * 0.36, GetFaceNormalComponent(ucont[wall_k][wall_j][wall_i], face), 1.0e-12,
1007 "parabolic inlet should take its wall-adjacent value, vanishing only on the wall"));
1008 PetscCall(PicurvAssertBool((PetscBool)(PetscAbsReal(GetFaceNormalComponent(ucont[center_k][center_j][center_i], face)) >
1009 PetscAbsReal(GetFaceNormalComponent(ucont[off_k][off_j][off_i], face))),
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"));
1014 PetscCall(PicurvAssertBool((PetscBool)((local_inflow > 0.0) == (GetFaceOrientationSign(face) > 0.0)),
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"));
1017
1018 PetscCall(DestroyBoundaryHandler(&bc));
1020 }
1021
1022 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1023 PetscFunctionReturn(0);
1024}
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.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestOutletConservationHandlerBehavior()

static PetscErrorCode TestOutletConservationHandlerBehavior ( void  )
static

Tests outlet conservation handler correction and post-step flux accounting.

Definition at line 1029 of file test_boundaries.c.

1030{
1031 SimCtx *simCtx = NULL;
1032 UserCtx *user = NULL;
1033 BoundaryCondition *bc = NULL;
1034 BCContext ctx;
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;
1041 Cmpnts ***ucont = NULL;
1042 Cmpnts ***ucat = NULL;
1043
1044 PetscFunctionBeginUser;
1045 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
1046 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
1047 PetscCall(PicurvPopulateIdentityMetrics(user));
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);
1052
1056
1057 ctx.user = user;
1058 ctx.face_id = BC_FACE_POS_Z;
1059 ctx.global_inflow_sum = &global_inflow;
1060 ctx.global_farfield_inflow_sum = &global_farfield_in;
1061 ctx.global_farfield_outflow_sum = &global_farfield_out;
1062 ctx.global_outflow_sum = &global_outflow;
1063
1065 PetscCall(bc->Apply(bc, &ctx));
1066 PetscCall(bc->PostStep(bc, &ctx, &local_inflow, &local_outflow));
1067
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));
1074
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"));
1077
1078 PetscCall(DestroyBoundaryHandler(&bc));
1079 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1080 PetscFunctionReturn(0);
1081}
const PetscReal * global_outflow_sum
Definition variables.h:373
@ OUTLET
Definition variables.h:315
const PetscReal * global_farfield_outflow_sum
Definition variables.h:372
const PetscReal * global_inflow_sum
Definition variables.h:370
const PetscReal * global_farfield_inflow_sum
Definition variables.h:371
PetscReal AreaOutSum
Definition variables.h:979
Vec lUcat
Definition variables.h:1113
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestOutletConservationHandlerFaceMatrix()

static PetscErrorCode TestOutletConservationHandlerFaceMatrix ( void  )
static

Tests outlet-conservation correction and flux accounting across all outlet faces.

Definition at line 1086 of file test_boundaries.c.

1087{
1088 const BCFace faces[] = {
1092 };
1093 SimCtx *simCtx = NULL;
1094 UserCtx *user = NULL;
1095 BCContext ctx;
1096 Cmpnts ***ucont = NULL;
1097 Cmpnts ***ubcs = NULL;
1098
1099 PetscFunctionBeginUser;
1100 PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
1101 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
1102 PetscCall(PicurvPopulateIdentityMetrics(user));
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));
1106
1107 for (size_t n = 0; n < sizeof(faces) / sizeof(faces[0]); ++n) {
1108 const BCFace face = faces[n];
1109 BoundaryCondition *bc = NULL;
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;
1116 PetscReal area = GetFaceInteriorPointCount(user, face);
1117 PetscInt ucont_k, ucont_j, ucont_i, ubcs_k, ubcs_j, ubcs_i;
1118
1119 PetscCall(VecSet(user->Ucont, 0.0));
1120 PetscCall(VecSet(user->Bcs.Ubcs, 0.0));
1122 user->boundary_faces[face].face_id = face;
1125 user->simCtx->AreaOutSum = area;
1126
1127 ctx.user = user;
1128 ctx.face_id = face;
1129 ctx.global_inflow_sum = &global_inflow;
1130 ctx.global_farfield_inflow_sum = &global_farfield_in;
1131 ctx.global_farfield_outflow_sum = &global_farfield_out;
1132 ctx.global_outflow_sum = &global_outflow;
1133
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"));
1137
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));
1143
1144 PetscCall(GetRepresentativeFaceSlots(user, face, &ucont_k, &ucont_j, &ucont_i, &ubcs_k, &ubcs_j, &ubcs_i));
1145 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &ucont));
1146 PetscCall(DMDAVecGetArrayRead(user->fda, user->Bcs.Ubcs, &ubcs));
1147 PetscCall(PicurvAssertRealNear(1.2, GetFaceNormalComponent(ucont[ucont_k][ucont_j][ucont_i], face), 1.0e-12, "outlet correction should add the configured flux trim"));
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"));
1155
1156 PetscCall(DestroyBoundaryHandler(&bc));
1158 }
1159
1160 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1161 PetscFunctionReturn(0);
1162}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestFinalizePostProjectionCellFieldsNonPeriodic()

static PetscErrorCode TestFinalizePostProjectionCellFieldsNonPeriodic ( void  )
static

Tests non-periodic cell-field finalization without modifying Ucont.

Definition at line 1167 of file test_boundaries.c.

1168{
1169 SimCtx *simCtx = NULL;
1170 UserCtx *user = NULL;
1171 Cmpnts ***ucat = NULL;
1172 Cmpnts ***lucat = NULL;
1173
1174 PetscFunctionBeginUser;
1175 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 4, 4, 4));
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));
1180
1181 PetscCall(FinalizePostProjectionCellFields(user));
1182
1183 PetscCall(PicurvAssertVecConstant(user->Ucont, 7.0, 1.0e-12,
1184 "post-projection cell finalization must preserve non-periodic Ucont"));
1185 PetscCall(PicurvAssertVecConstant(user->P, 3.0, 1.0e-12,
1186 "constant pressure should remain unchanged during cell finalization"));
1187
1188 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucat, &ucat));
1189 PetscCall(DMDAVecGetArrayRead(user->fda, user->lUcat, &lucat));
1190 PetscCall(PicurvAssertRealNear(-2.0, ucat[2][2][0].x, 1.0e-12,
1191 "non-periodic Ucat dummy face should use Ubcs extrapolation"));
1192 PetscCall(PicurvAssertRealNear(-2.0, lucat[2][2][0].x, 1.0e-12,
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));
1196
1197 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1198 PetscFunctionReturn(0);
1199}
PetscErrorCode FinalizePostProjectionCellFields(UserCtx *user)
Finalizes cell-centered fields after the projection step.
PetscErrorCode PicurvAssertVecConstant(Vec vec, PetscScalar expected, PetscReal tol, const char *context)
Asserts that a PETSc vector is spatially constant within tolerance.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestEnforceRHSBoundaryConditionsMatchesRowClassification()

static PetscErrorCode TestEnforceRHSBoundaryConditionsMatchesRowClassification ( void  )
static

EnforceRHSBoundaryConditions() must zero a row exactly when it is not an unknown.

This is the invariant that ties the residual path to ClassifyMomentumRow(). It is checked across a non-periodic, a singly-periodic and a fully-periodic configuration because the defect it guards against was visible only when an axis was periodic: the duplicate column at index 0 was left unzeroed, so ComputeTotalResidual()'s BDF term accumulated there on every call and the residual norm stopped describing the state. A sentinel fill makes an untouched row detectable rather than accidentally correct.

Definition at line 1211 of file test_boundaries.c.

1212{
1213 const struct {
1214 const char *label;
1215 PetscBool x_periodic, y_periodic, z_periodic;
1216 } configs[] = {
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 },
1220 };
1221 const PetscScalar sentinel = 7.0;
1222
1223 PetscFunctionBeginUser;
1224 for (size_t c = 0; c < sizeof(configs) / sizeof(configs[0]); ++c) {
1225 SimCtx *simCtx = NULL;
1226 UserCtx *user = NULL;
1227 Cmpnts ***rhs = NULL;
1228 const PetscBool periodic[3] = {configs[c].x_periodic, configs[c].y_periodic, configs[c].z_periodic};
1229 const BCFace neg_face[3] = {BC_FACE_NEG_X, BC_FACE_NEG_Y, BC_FACE_NEG_Z};
1230 const BCFace pos_face[3] = {BC_FACE_POS_X, BC_FACE_POS_Y, BC_FACE_POS_Z};
1231
1232 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 6, 6, 6,
1233 configs[c].x_periodic,
1234 configs[c].y_periodic,
1235 configs[c].z_periodic));
1236 for (PetscInt axis = 0; axis < 3; ++axis) {
1237 const BCType type = periodic[axis] ? PERIODIC : WALL;
1238 user->boundary_faces[neg_face[axis]].mathematical_type = type;
1239 user->boundary_faces[pos_face[axis]].mathematical_type = type;
1240 }
1241
1242 PetscCall(VecDuplicate(user->Ucont, &user->Rhs));
1243 PetscCall(VecSet(user->Rhs, sentinel));
1244 PetscCall(EnforceRHSBoundaryConditions(user));
1245
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;
1253 const MomentumRowType type =
1254 ClassifyMomentumRow(user, i, j, k, component, &ri, &rj, &rk);
1255 const PetscScalar expected = (type == MOM_ROW_PHYSICAL) ? sentinel : 0.0;
1256 char context[192];
1257
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,
1261 (type == MOM_ROW_PHYSICAL) ? "left untouched" : "zeroed"));
1262 PetscCall(PicurvAssertRealNear((PetscReal)expected, (PetscReal)row[component],
1263 1.0e-14, context));
1264 }
1265 }
1266 }
1267 }
1268 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Rhs, &rhs));
1269
1270 PetscCall(VecDestroy(&user->Rhs));
1271 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1272 }
1273 PetscFunctionReturn(0);
1274}
MomentumRowType
Classification of one staggered momentum row (location + component).
Definition Boundaries.h:252
@ MOM_ROW_PHYSICAL
Independent unknown governed by the momentum equation.
Definition Boundaries.h:253
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".
Definition Boundaries.c:597
PetscErrorCode EnforceRHSBoundaryConditions(UserCtx *user)
Zeroes every momentum RHS row that does not carry an independent unknown.
Definition Boundaries.c:686
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.
BCType
Defines the general mathematical/physical Category of a boundary.
Definition variables.h:309
@ PERIODIC
Definition variables.h:318
@ WALL
Definition variables.h:312
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SeedWallFrictionVelocityStorage()

static PetscErrorCode SeedWallFrictionVelocityStorage ( UserCtx *  user)
static

Allocates the wall-model storage the way production allocates it.

Production creates each global on da and ghosts it into the local view. A fixture that allocates differently - a different DM, only one of a pair, or only one of the fields the same block creates - tests a layout the solver never uses, which is how a DM/dof mismatch survived in ApplyWallFunction while these cases passed. The wall eddy viscosity is allocated in that same block and cleared at the top of every pass, so a fixture omitting it dereferences a null vector before reaching any assertion. Every wall case routes through here so the two cannot drift.

Definition at line 1287 of file test_boundaries.c.

1288{
1289 PetscFunctionBeginUser;
1290 PetscCall(DMCreateGlobalVector(user->da, &user->Friction_Velocity));
1291 PetscCall(VecSet(user->Friction_Velocity, 0.0));
1292 PetscCall(DMCreateLocalVector(user->da, &user->lFriction_Velocity));
1293 PetscCall(VecSet(user->lFriction_Velocity, 0.0));
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);
1299}
Vec lFriction_Velocity
Definition variables.h:1106
Vec lNu_Wall
Definition variables.h:1110
Vec Nu_Wall
Definition variables.h:1110
Vec Friction_Velocity
Definition variables.h:1106
Here is the caller graph for this function:

◆ SeedWallFunctionFixture()

static PetscErrorCode SeedWallFunctionFixture ( UserCtx *  user,
PetscReal  tangential_speed 
)
static

Seeds the fields ApplyWallFunction() reads on a single wall face.

A uniform tangential velocity and unit metrics make the wall distances and the face normal analytically known, so the test can reproduce the model's own arithmetic without copying the integration's index choices.

Definition at line 1308 of file test_boundaries.c.

1309{
1310 Cmpnts ***ucat = NULL, ***csi = NULL;
1311 PetscReal ***aj = NULL;
1312 DMDALocalInfo info = user->info;
1313
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) {
1322 /* Flow along eta, so the whole velocity is tangential to a xi wall. */
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};
1325 aj[k][j][i] = 1.0;
1326 }
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);
1335}
Here is the caller graph for this function:

◆ TestApplyWallFunctionMatchesTheLogLawItDispatches()

static PetscErrorCode TestApplyWallFunctionMatchesTheLogLawItDispatches ( void  )
static

Tests that the wall-function integration reproduces the model it dispatches to.

The kernels are covered separately; what is unverified without this is the wiring around them, where the plausible failures are silent. Feeding the reference and boundary wall distances in the wrong order, taking the reference velocity from the wrong cell, or normalising the face normal incorrectly all still produce a plausible-looking corrected velocity. The check here is that the corrected first-interior velocity is exactly what the log-law model predicts at that cell's distance, for the friction velocity the integration itself stored.

Definition at line 1348 of file test_boundaries.c.

1349{
1350 SimCtx *simCtx = NULL;
1351 UserCtx *user = NULL;
1352 Cmpnts ***ucat = NULL, ***ubcs = NULL;
1353 PetscReal ***utau = NULL;
1354 const PetscReal speed = 1.0;
1355 const PetscReal roughness = 1.0e-16; /* smooth wall */
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;
1359
1360 PetscFunctionBeginUser;
1361 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
1362 simCtx->ren = 1000.0;
1363 simCtx->wall_roughness_height = roughness;
1364 kinematic_viscosity = 1.0 / simCtx->ren;
1365
1366 PetscCall(SeedWallFrictionVelocityStorage(user));
1368 PetscCall(SeedWallFunctionFixture(user, speed));
1369
1370 /* With unit Jacobians and a unit face area the model's distances are exact:
1371 the first cell centre sits half a cell from the wall, the second one and a
1372 half. Reproducing them here rather than reading them back is what makes a
1373 swapped-distance wiring error detectable. */
1374 distance_first = 0.5;
1375 distance_second = 2.0 * distance_first + 0.5;
1376
1377 /* Disabled: the routine must not touch the field at all. */
1378 simCtx->wallfunction = 0;
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));
1382 PetscCall(ApplyWallFunction(user));
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));
1386 PetscCall(PicurvAssertRealNear(untouched_before, untouched_after, 0.0,
1387 "a disabled wall function must leave the velocity untouched"));
1388
1389 /* Enabled: the first interior cell is corrected onto the modelled profile. */
1390 simCtx->wallfunction = 1;
1391 PetscCall(ApplyWallFunction(user));
1392
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));
1396 PetscCall(PicurvAssertBool((PetscBool)(stored_utau > 0.0),
1397 "the integration must store a positive friction velocity"));
1398
1399 /* The stored friction velocity must be the one that reproduces the *reference*
1400 cell's speed at the *reference* distance. Swapping the two distances passes the
1401 next check but fails this one. */
1402 reference_check = u_hydset_roughness(kinematic_viscosity, distance_second,
1403 stored_utau, roughness);
1404 PetscCall(PicurvAssertRealNear(speed, reference_check, 1.0e-6,
1405 "the friction velocity must invert the log law at the "
1406 "reference cell's distance"));
1407
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));
1411
1412 expected_speed = u_hydset_roughness(kinematic_viscosity, distance_first,
1413 stored_utau, roughness);
1414 PetscCall(PicurvAssertRealNear(expected_speed, corrected_speed, 1.0e-9,
1415 "the corrected velocity must be the modelled profile at "
1416 "the first interior cell's distance"));
1417 PetscCall(PicurvAssertBool((PetscBool)(corrected_speed < speed),
1418 "a log-law wall correction must slow the first interior cell "
1419 "relative to the reference cell"));
1420
1421 /* The ghost row carries no boundary velocity of its own. */
1422 PetscCall(DMDAVecGetArrayRead(user->fda, user->Bcs.Ubcs, (const Cmpnts ***)&ubcs));
1423 PetscCall(PicurvAssertRealNear(0.0, ubcs[2][2][0].y, 0.0,
1424 "the wall ghost row must carry a zero boundary velocity"));
1425 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Bcs.Ubcs, (const Cmpnts ***)&ubcs));
1426
1427 PetscCall(VecDestroy(&user->Friction_Velocity));
1428 PetscCall(VecDestroy(&user->lFriction_Velocity));
1429 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1430 PetscFunctionReturn(0);
1431}
PetscErrorCode ApplyWallFunction(UserCtx *user)
Applies wall function modeling to near-wall velocities for all wall-type boundaries.
static PetscErrorCode SeedWallFrictionVelocityStorage(UserCtx *user)
Allocates the wall-model storage the way production allocates it.
static PetscErrorCode SeedWallFunctionFixture(UserCtx *user, PetscReal tangential_speed)
Seeds the fields ApplyWallFunction() reads on a single wall face.
PetscReal ren
Definition variables.h:906
PetscReal wall_roughness_height
Definition variables.h:947
PetscInt wallfunction
Enable wall functions on WALL faces.
Definition variables.h:985
double u_hydset_roughness(double kinematic_viscosity, double wall_distance, double friction_velocity, double roughness_height)
Computes velocity from log-law for rough walls.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestWallFunctionModelSelectorChoosesTheLaw()

static PetscErrorCode TestWallFunctionModelSelectorChoosesTheLaw ( void  )
static

Tests that the model selector actually changes which law is applied.

The selector was previously validated in the configuration layer and then dropped, so every case ran the log law whatever it asked for. Distinct laws must produce distinct corrections from identical inputs, or the selector is decorative again.

Definition at line 1440 of file test_boundaries.c.

1441{
1442 SimCtx *simCtx = NULL;
1443 UserCtx *user = NULL;
1444 Cmpnts ***ucat = NULL;
1445 PetscReal corrected[4] = {0.0, 0.0, 0.0, 0.0};
1446 const PetscInt models[3] = {WALL_FUNCTION_LOG_LAW, WALL_FUNCTION_WERNER, WALL_FUNCTION_CABOT};
1447
1448 PetscFunctionBeginUser;
1449 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
1450 simCtx->ren = 1000.0;
1451 simCtx->wall_roughness_height = 1.0e-16;
1452 PetscCall(SeedWallFrictionVelocityStorage(user));
1454
1455 for (PetscInt m = 0; m < 3; ++m) {
1456 PetscCall(SeedWallFunctionFixture(user, 1.0));
1457 simCtx->wallfunction = models[m];
1458 PetscCall(ApplyWallFunction(user));
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"));
1464 }
1465
1466 /* Each law has its own profile, so no two may coincide. */
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"));
1471
1472 PetscCall(VecDestroy(&user->Friction_Velocity));
1473 PetscCall(VecDestroy(&user->lFriction_Velocity));
1474 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1475 PetscFunctionReturn(0);
1476}
@ WALL_FUNCTION_CABOT
Definition variables.h:569
@ WALL_FUNCTION_LOG_LAW
Definition variables.h:567
@ WALL_FUNCTION_WERNER
Definition variables.h:568
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestApplyWallFunctionSkipsNonWallFaces()

static PetscErrorCode TestApplyWallFunctionSkipsNonWallFaces ( void  )
static

Tests that a face which is not a wall is left alone by the wall function.

Definition at line 1481 of file test_boundaries.c.

1482{
1483 SimCtx *simCtx = NULL;
1484 UserCtx *user = NULL;
1485 Cmpnts ***ucat = NULL;
1486 PetscReal before = 0.0, after = 0.0;
1487
1488 PetscFunctionBeginUser;
1489 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
1490 simCtx->ren = 1000.0;
1491 simCtx->wallfunction = 1;
1492 simCtx->wall_roughness_height = 1.0e-16;
1493 PetscCall(SeedWallFrictionVelocityStorage(user));
1494 /* Every face left at its default, so none is a wall. */
1495 PetscCall(SeedWallFunctionFixture(user, 1.0));
1496
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));
1500 PetscCall(ApplyWallFunction(user));
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));
1504
1505 PetscCall(PicurvAssertRealNear(before, after, 0.0,
1506 "a face that is not a wall must not be corrected"));
1507
1508 PetscCall(VecDestroy(&user->Friction_Velocity));
1509 PetscCall(VecDestroy(&user->lFriction_Velocity));
1510 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1511 PetscFunctionReturn(0);
1512}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestWallModelFrictionVelocityRespondsToTheFlow()

static PetscErrorCode TestWallModelFrictionVelocityRespondsToTheFlow ( void  )
static

Tests that every wall law's friction velocity responds to the flow it is given.

A law that ignores the resolved velocity and reports a fixed number still slows the first cell and still differs from the other laws, so the selector test above passes over it. Driving a law at two different speeds and requiring the friction velocity to follow is what distinguishes a model from a constant.

All three laws are covered. Werner-Wengle is the reason this case exists: it used to report its Newton initial guess rather than solving, so every wall cell in a run carried the same u_tau while still slowing the cell and still differing from the other laws, which is everything the selector test above checks.

Definition at line 1527 of file test_boundaries.c.

1528{
1529 SimCtx *simCtx = NULL;
1530 UserCtx *user = NULL;
1531 PetscReal ***utau = NULL;
1532 const PetscInt models[3] = {WALL_FUNCTION_LOG_LAW, WALL_FUNCTION_WERNER,
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};
1538
1539 PetscFunctionBeginUser;
1540 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
1541 simCtx->ren = 1000.0;
1542 simCtx->wall_roughness_height = 1.0e-16;
1543 PetscCall(SeedWallFrictionVelocityStorage(user));
1545
1546 for (PetscInt m = 0; m < 3; ++m) {
1547 PetscReal stored[2] = {0.0, 0.0};
1548
1549 simCtx->wallfunction = models[m];
1550 for (PetscInt s = 0; s < 2; ++s) {
1551 PetscCall(SeedWallFunctionFixture(user, speeds[s]));
1552 PetscCall(ApplyWallFunction(user));
1553 PetscCall(DMDAVecGetArrayRead(user->da, user->Friction_Velocity,
1554 (const PetscReal ***)&utau));
1555 stored[s] = utau[2][2][1];
1556 PetscCall(DMDAVecRestoreArrayRead(user->da, user->Friction_Velocity,
1557 (const PetscReal ***)&utau));
1558 PetscCall(PicurvAssertBool((PetscBool)(stored[s] > 0.0),
1559 "a wall law must report a positive friction velocity"));
1560 }
1561
1562 /* Faster flow, more wall stress. Any law that reports its initial guess, or any
1563 other constant, fails here whatever that constant is. */
1564 PetscCall(PicurvAssertBool((PetscBool)(stored[1] > stored[0] * 1.05),
1565 names[m]));
1566 }
1567
1568 PetscCall(VecDestroy(&user->Friction_Velocity));
1569 PetscCall(VecDestroy(&user->lFriction_Velocity));
1570 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1571 PetscFunctionReturn(0);
1572}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestWallFrictionVelocityStorageMatchesTheCatalog()

static PetscErrorCode TestWallFrictionVelocityStorageMatchesTheCatalog ( void  )
static

Tests that the friction-velocity storage matches what the field catalog declares.

The wall model reaches its storage through a DM, and PETSc only rejects a mismatched DM/vector pairing at the moment of access - inside a branch that runs only when wall functions are enabled on a case that has a WALL face. That is how storage created from a dof-3 DM and read through a dof-1 one stayed latent. Comparing the allocation against the catalog descriptor turns that into a failure here.

Definition at line 1583 of file test_boundaries.c.

1584{
1585 SimCtx *simCtx = NULL;
1586 UserCtx *user = NULL;
1587 const FieldDescriptor *descriptor = NULL;
1588 FieldView view;
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;
1592
1593 PetscFunctionBeginUser;
1594 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
1595 PetscCall(SeedWallFrictionVelocityStorage(user));
1596
1597 PetscCall(FieldGetDescriptor(FIELD_ID_U_TAU, &descriptor));
1598 PetscCall(PicurvAssertBool((PetscBool)(descriptor->dof == 1),
1599 "the friction velocity is one scalar per cell"));
1600 PetscCall(PicurvAssertBool((PetscBool)(descriptor->dm_kind == FIELD_DM_DA),
1601 "a scalar cell field belongs on the dof-1 DM"));
1602
1603 PetscCall(FieldGetView(user, FIELD_ID_U_TAU, &view));
1604 PetscCall(PicurvAssertBool((PetscBool)(view.global_vec == user->Friction_Velocity &&
1605 view.local_vec == user->lFriction_Velocity),
1606 "the catalog must resolve to the vectors setup allocated"));
1607
1608 /* The sizes the DM implies, against the sizes the vectors actually have. */
1609 PetscCall(DMDAGetCorners(user->da, NULL, NULL, NULL, &xm, &ym, &zm));
1610 PetscCall(DMDAGetGhostCorners(user->da, NULL, NULL, NULL, &gxm, &gym, &gzm));
1611 PetscCall(VecGetLocalSize(user->Friction_Velocity, &global_size));
1612 PetscCall(VecGetLocalSize(user->lFriction_Velocity, &local_size));
1613 PetscCall(PicurvAssertBool((PetscBool)(global_size == xm * ym * zm * descriptor->dof),
1614 "the global friction velocity must match the owned extent"));
1615 PetscCall(PicurvAssertBool((PetscBool)(local_size == gxm * gym * gzm * descriptor->dof),
1616 "the local friction velocity must match the ghosted extent"));
1617
1618 /* The access the wall model itself performs. A mismatched pairing fails right here. */
1619 PetscCall(DMDAVecGetArray(user->da, user->Friction_Velocity, &utau));
1620 PetscCall(DMDAVecRestoreArray(user->da, user->Friction_Velocity, &utau));
1621
1622 PetscCall(VecDestroy(&user->Friction_Velocity));
1623 PetscCall(VecDestroy(&user->lFriction_Velocity));
1624 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1625 PetscFunctionReturn(0);
1626}
PetscErrorCode FieldGetView(UserCtx *user, FieldId field_id, FieldView *view)
Resolve the existing DM and global/local vectors for one field.
FieldDMKind dm_kind
PetscErrorCode FieldGetDescriptor(FieldId field_id, const FieldDescriptor **descriptor)
Return immutable metadata for a valid field identifier.
@ FIELD_DM_DA
@ FIELD_ID_U_TAU
Immutable metadata for one field identity.
Non-owning runtime objects resolved for one field and UserCtx.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestWallModelDiagnosticsRecordTheCorrectedCells()

static PetscErrorCode TestWallModelDiagnosticsRecordTheCorrectedCells ( void  )
static

Tests that the wall pass records the statistics its diagnostics report.

y+ cannot be recovered downstream, so it is formed where the wall distance is still in hand. This checks that what is accumulated there is the same quantity the reported column claims to be, and that a pass over no wall face records nothing rather than recording zeros.

Definition at line 1636 of file test_boundaries.c.

1637{
1638 SimCtx *simCtx = NULL;
1639 UserCtx *user = NULL;
1640 PetscReal kinematic_viscosity = 0.0;
1641 PetscReal expected_y_plus = 0.0;
1642 const PetscReal distance_first = 0.5;
1643
1644 PetscFunctionBeginUser;
1645 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
1646 simCtx->ren = 1000.0;
1647 simCtx->wall_roughness_height = 1.0e-16;
1648 kinematic_viscosity = 1.0 / simCtx->ren;
1649 PetscCall(SeedWallFrictionVelocityStorage(user));
1651 PetscCall(SeedWallFunctionFixture(user, 1.0));
1652
1653 /* Disabled: nothing is corrected, so nothing is recorded. */
1654 simCtx->wallfunction = 0;
1655 PetscCall(ApplyWallFunction(user));
1656 PetscCall(PicurvAssertBool((PetscBool)(user->wall_diagnostics.cells == 0),
1657 "a disabled wall model must record no corrected cells"));
1658
1659 simCtx->wallfunction = 1;
1660 PetscCall(ApplyWallFunction(user));
1661 PetscCall(PicurvAssertBool((PetscBool)(user->wall_diagnostics.cells > 0),
1662 "an enabled wall model must record the cells it corrected"));
1663
1664 /* One face, so the sums are one cell's values and y+ is exactly u_tau * y / nu.
1665 Reproducing it from the stored friction velocity is what makes a wrong distance
1666 or a wrong viscosity in the accumulation visible. */
1667 expected_y_plus = user->wall_diagnostics.friction_velocity_sum * distance_first /
1668 kinematic_viscosity / (PetscReal)user->wall_diagnostics.cells;
1670 (PetscReal)user->wall_diagnostics.cells,
1671 expected_y_plus, 1.0e-9,
1672 "y+ must be formed from the same distance the "
1673 "correction used"));
1675 (PetscReal)user->wall_diagnostics.cells,
1676 distance_first, 1.0e-9,
1677 "the recorded wall distance must be the first cell's"));
1678 PetscCall(PicurvAssertBool((PetscBool)(user->wall_diagnostics.friction_velocity_min <=
1680 "the recorded extrema must bracket each other"));
1681
1682 /* A second pass must describe that pass alone, not both. */
1683 {
1684 const PetscInt first_pass_cells = user->wall_diagnostics.cells;
1685
1686 PetscCall(ApplyWallFunction(user));
1687 PetscCall(PicurvAssertBool((PetscBool)(user->wall_diagnostics.cells == first_pass_cells),
1688 "each pass must replace the previous pass's sample set"));
1689 }
1690
1691 PetscCall(VecDestroy(&user->Friction_Velocity));
1692 PetscCall(VecDestroy(&user->lFriction_Velocity));
1693 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1694 PetscFunctionReturn(0);
1695}
PetscReal wall_distance_sum
Sum of the first-cell wall distance.
Definition variables.h:677
PetscReal friction_velocity_min
Smallest u_tau this rank corrected.
Definition variables.h:675
PetscReal friction_velocity_max
Largest u_tau this rank corrected.
Definition variables.h:676
PetscReal y_plus_sum
Sum of u_tau * y / nu over corrected cells.
Definition variables.h:678
PetscReal friction_velocity_sum
Sum of u_tau over corrected cells.
Definition variables.h:673
PetscInt cells
Number of cells this rank corrected.
Definition variables.h:681
WallModelDiagnosticsState wall_diagnostics
Near-wall statistics from the last wall-model pass.
Definition variables.h:1158
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestDummyCellsFollowEachFieldsBoundaryRule()

static PetscErrorCode TestDummyCellsFollowEachFieldsBoundaryRule ( void  )
static

Checks each cell-centred field's dummy-cell rule, faces then edges.

Interior cells carry index-unique sentinels and every dummy cell starts poisoned, so a wrong offset or a missed face shows. Velocity dummies must put the face average on Ubcs; pressure and the particle scalar copy the adjacent cell (zero normal gradient); each edge dummy is the mean of its two face neighbours; and a field with no rule, or one that is not cell-centred, is refused.

Definition at line 1705 of file test_boundaries.c.

1706{
1707 SimCtx *simCtx = NULL;
1708 UserCtx *user = NULL;
1709 const FieldId scalars[] = {FIELD_ID_P, FIELD_ID_PSI};
1710
1711 PetscFunctionBeginUser;
1712 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 5, 6, 7));
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));
1716 {
1717 PetscReal ***p = NULL, ***psi = NULL;
1718 Cmpnts ***ucat = NULL, ***ubcs = NULL;
1719
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;
1729
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};
1736 }
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));
1741 }
1742
1743 PetscCall(UpdateDummyCells(user, FIELD_ID_UCAT));
1744 PetscCall(UpdateCornerNodes(user, FIELD_ID_UCAT));
1745 for (size_t n = 0; n < sizeof(scalars) / sizeof(scalars[0]); ++n) {
1746 PetscCall(UpdateDummyCells(user, scalars[n]));
1747 PetscCall(UpdateCornerNodes(user, scalars[n]));
1748 }
1749
1750 for (size_t n = 0; n < sizeof(scalars) / sizeof(scalars[0]); ++n) {
1751 FieldView view;
1752 PetscReal ***f = NULL;
1753
1754 PetscCall(FieldGetView(user, scalars[n], &view));
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"));
1759 }
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"));
1763 }
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"));
1767 }
1768 for (PetscInt j = 0; j < my; j++) {
1769 PetscCall(PicurvAssertRealNear(0.5 * (f[1][j][0] + f[0][j][1]), f[0][j][0], 0.0,
1770 "a (-z,-x) edge dummy is the mean of its face neighbours"));
1771 }
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));
1775 }
1776 {
1777 const Cmpnts ***ucat = NULL;
1778
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++) {
1781 PetscCall(PicurvAssertRealNear(1.0, 0.5 * (ucat[k][j][0].x + ucat[k][j][1].x), 1.0e-12,
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"));
1785 }
1786 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucat, &ucat));
1787 }
1788
1789 {
1790 PetscErrorCode refused;
1791
1792 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1793 refused = UpdateDummyCells(user, FIELD_ID_NVERT);
1794 PetscCall(PetscPopErrorHandler());
1795 PetscCall(PicurvAssertIntEqual(PETSC_ERR_SUP, refused, "a field with no boundary rule is refused"));
1796 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1797 refused = UpdateDummyCells(user, FIELD_ID_COORDINATES);
1798 PetscCall(PetscPopErrorHandler());
1799 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, refused, "a field that is not cell-centred is refused"));
1800 }
1801
1802 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
1803 PetscFunctionReturn(0);
1804}
PetscErrorCode UpdateCornerNodes(UserCtx *user, FieldId field_id)
Fills the edge and corner dummy cells of one cell-centred field by averaging.
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_PSI
@ FIELD_ID_NVERT
@ FIELD_ID_COORDINATES
@ FIELD_ID_P
Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char **  argv 
)

Runs the unit-boundaries PETSc test binary.

Definition at line 1810 of file test_boundaries.c.

1811{
1812 PetscErrorCode ierr;
1813 const PicurvTestCase cases[] = {
1814 {"can-rank-service-face-matches-inlet-when-defined", TestCanRankServiceFaceMatchesInletWhenDefined},
1815 {"can-rank-service-inlet-face-requires-definition", TestCanRankServiceInletFaceRequiresDefinition},
1816 {"boundary-condition-factory-assignments", TestBoundaryConditionFactoryAssignments},
1817 {"boundary-condition-factory-implemented-handler-matrix", TestBoundaryConditionFactoryImplementedHandlerMatrix},
1818 {"boundary-condition-factory-rejects-unsupported-handler", TestBoundaryConditionFactoryRejectsUnsupportedHandler},
1819 {"deterministic-face-grid-location-matrix", TestGetDeterministicFaceGridLocationFaceMatrix},
1820 {"random-inlet-face-location-matrix", TestGetRandomCellAndLogicalCoordsOnInletFaceMatrix},
1821 {"wall-no-slip-handler-face-matrix", TestWallNoSlipHandlerFaceMatrix},
1822 {"dummy-cells-follow-each-fields-boundary-rule", TestDummyCellsFollowEachFieldsBoundaryRule},
1823 {"inlet-constant-velocity-handler-behavior", TestInletConstantVelocityHandlerBehavior},
1824 {"inlet-common-application-curvilinear-metric", TestInletCommonApplicationCurvilinearMetric},
1825 {"inlet-constant-velocity-handler-face-matrix", TestInletConstantVelocityHandlerFaceMatrix},
1826 {"inlet-parabolic-profile-handler-behavior", TestInletParabolicProfileHandlerBehavior},
1827 {"inlet-parabolic-profile-handler-face-matrix", TestInletParabolicProfileHandlerFaceMatrix},
1828 {"inlet-parabolic-profile-uniform-along-periodic-axis", TestInletParabolicProfileUniformAlongPeriodicAxis},
1829 {"inlet-profile-from-file-handler-behavior", TestInletProfileFromFileHandlerBehavior},
1830 {"outlet-conservation-handler-behavior", TestOutletConservationHandlerBehavior},
1831 {"outlet-conservation-handler-face-matrix", TestOutletConservationHandlerFaceMatrix},
1832 {"finalize-post-projection-cell-fields-non-periodic", TestFinalizePostProjectionCellFieldsNonPeriodic},
1833 {"enforce-rhs-bcs-matches-row-classification", TestEnforceRHSBoundaryConditionsMatchesRowClassification},
1834 {"apply-wall-function-matches-the-log-law", TestApplyWallFunctionMatchesTheLogLawItDispatches},
1835 {"apply-wall-function-skips-non-wall-faces", TestApplyWallFunctionSkipsNonWallFaces},
1836 {"wall-function-model-selector-chooses-the-law", TestWallFunctionModelSelectorChoosesTheLaw},
1837 {"wall-model-friction-velocity-responds-to-the-flow", TestWallModelFrictionVelocityRespondsToTheFlow},
1838 {"wall-friction-velocity-storage-matches-the-catalog", TestWallFrictionVelocityStorageMatchesTheCatalog},
1839 {"wall-model-diagnostics-record-the-corrected-cells", TestWallModelDiagnosticsRecordTheCorrectedCells},
1840 };
1841
1842 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv boundary tests");
1843 if (ierr) {
1844 return (int)ierr;
1845 }
1846
1847 ierr = PicurvRunTests("unit-boundaries", cases, sizeof(cases) / sizeof(cases[0]));
1848 if (ierr) {
1849 PetscFinalize();
1850 return (int)ierr;
1851 }
1852
1853 ierr = PetscFinalize();
1854 return (int)ierr;
1855}
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 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 TestApplyWallFunctionMatchesTheLogLawItDispatches(void)
Tests that the wall-function integration reproduces the model it dispatches to.
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.
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 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 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 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 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 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 PetscErrorCode TestEnforceRHSBoundaryConditionsMatchesRowClassification(void)
EnforceRHSBoundaryConditions() must zero a row exactly when it is not an unknown.
static PetscErrorCode TestWallFrictionVelocityStorageMatchesTheCatalog(void)
Tests that the friction-velocity storage matches what the field catalog declares.
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.
Named test case descriptor consumed by PicurvRunTests.
Here is the call graph for this function: