PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
test_boundaries.c
Go to the documentation of this file.
1/**
2 * @file test_boundaries.c
3 * @brief C test module for PICurv.
4 */
5
6#include "test_support.h"
7
8#include "Boundaries.h"
9/**
10 * @brief Test-local routine.
11 */
12
13static PetscErrorCode DestroyBoundaryHandler(BoundaryCondition **bc_ptr)
14{
15 BoundaryCondition *bc = NULL;
16 PetscFunctionBeginUser;
17 if (!bc_ptr || !*bc_ptr) PetscFunctionReturn(0);
18
19 bc = *bc_ptr;
20 if (bc->Destroy) {
21 PetscCall(bc->Destroy(bc));
22 }
23 PetscCall(PetscFree(bc));
24 *bc_ptr = NULL;
25 PetscFunctionReturn(0);
26}
27/**
28 * @brief Test-local routine.
29 */
30
32{
33 SimCtx *simCtx = NULL;
34 UserCtx *user = NULL;
35 PetscBool generic_face = PETSC_FALSE;
36 PetscBool inlet_face = PETSC_FALSE;
37 PetscBool any_face_serviceable = PETSC_FALSE;
38
39 PetscFunctionBeginUser;
40 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 4, 4, 4));
41
42 for (BCFace face = BC_FACE_NEG_X; face <= BC_FACE_POS_Z; face++) {
43 PetscCall(CanRankServiceFace(&user->info, user->IM, user->JM, user->KM, face, &generic_face));
44 if (generic_face) {
45 any_face_serviceable = PETSC_TRUE;
46 }
47
48 user->inletFaceDefined = PETSC_TRUE;
49 user->identifiedInletBCFace = face;
50 PetscCall(CanRankServiceInletFace(user, &user->info, user->IM, user->JM, user->KM, &inlet_face));
51 PetscCall(PicurvAssertBool((PetscBool)(inlet_face == generic_face), "inlet face service check should match generic face check"));
52 }
53 PetscCall(PicurvAssertBool(any_face_serviceable, "at least one global face should be serviceable in a non-degenerate domain"));
54
55 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
56 PetscFunctionReturn(0);
57}
58/**
59 * @brief Test-local routine.
60 */
61
63{
64 SimCtx *simCtx = NULL;
65 UserCtx *user = NULL;
66 PetscBool can_service = PETSC_FALSE;
67
68 PetscFunctionBeginUser;
69 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 4, 4, 4));
70
71 user->inletFaceDefined = PETSC_FALSE;
72 for (BCFace face = BC_FACE_NEG_X; face <= BC_FACE_POS_Z; face++) {
73 user->identifiedInletBCFace = face;
74 PetscCall(CanRankServiceInletFace(user, &user->info, user->IM, user->JM, user->KM, &can_service));
75 PetscCall(PicurvAssertBool((PetscBool)!can_service, "undefined inlet face should not be serviceable"));
76 }
77
78 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
79 PetscFunctionReturn(0);
80}
81/**
82 * @brief Test-local routine.
83 */
84
85static PetscErrorCode TestBoundaryConditionFactoryAssignments(void)
86{
87 BoundaryCondition *wall = NULL;
88 BoundaryCondition *driven = NULL;
89
90 PetscFunctionBeginUser;
92 PetscCall(PicurvAssertBool((PetscBool)(wall != NULL), "factory should return wall handler"));
93 PetscCall(PicurvAssertIntEqual(BC_PRIORITY_WALL, wall->priority, "wall handler priority"));
94 PetscCall(PicurvAssertBool((PetscBool)(wall->Apply != NULL), "wall handler should expose Apply"));
95 PetscCall(PicurvAssertBool((PetscBool)(wall->Destroy == NULL), "wall handler should not require destroy hook"));
96 PetscCall(DestroyBoundaryHandler(&wall));
97
99 PetscCall(PicurvAssertBool((PetscBool)(driven != NULL), "factory should return driven periodic handler"));
100 PetscCall(PicurvAssertIntEqual(BC_PRIORITY_INLET, driven->priority, "driven handler priority"));
101 PetscCall(PicurvAssertBool((PetscBool)(driven->Initialize != NULL), "driven handler should expose Initialize"));
102 PetscCall(PicurvAssertBool((PetscBool)(driven->PreStep != NULL), "driven handler should expose PreStep"));
103 PetscCall(PicurvAssertBool((PetscBool)(driven->Apply != NULL), "driven handler should expose Apply"));
104 PetscCall(PicurvAssertBool((PetscBool)(driven->Destroy != NULL), "driven handler should expose Destroy"));
105 PetscCall(DestroyBoundaryHandler(&driven));
106
107 PetscFunctionReturn(0);
108}
109/**
110 * @brief Test-local routine.
111 */
112
114{
115 struct HandlerExpectation {
116 BCHandlerType handler;
117 BCPriorityType priority;
118 PetscBool expect_apply;
119 PetscBool expect_initialize;
120 };
121 const struct HandlerExpectation expectations[] = {
122 {BC_HANDLER_WALL_NOSLIP, BC_PRIORITY_WALL, PETSC_TRUE, PETSC_FALSE},
123 {BC_HANDLER_INLET_CONSTANT_VELOCITY, BC_PRIORITY_INLET, PETSC_TRUE, PETSC_TRUE},
124 {BC_HANDLER_INLET_PARABOLIC, BC_PRIORITY_INLET, PETSC_TRUE, PETSC_TRUE},
125 {BC_HANDLER_OUTLET_CONSERVATION, BC_PRIORITY_OUTLET, PETSC_TRUE, PETSC_FALSE},
126 {BC_HANDLER_PERIODIC_GEOMETRIC, BC_PRIORITY_WALL, PETSC_FALSE, PETSC_FALSE},
128 };
129
130 PetscFunctionBeginUser;
131 for (size_t i = 0; i < sizeof(expectations) / sizeof(expectations[0]); ++i) {
132 BoundaryCondition *bc = NULL;
133 PetscCall(BoundaryCondition_Create(expectations[i].handler, &bc));
134 PetscCall(PicurvAssertBool((PetscBool)(bc != NULL), "factory should allocate a handler object"));
135 PetscCall(PicurvAssertIntEqual(expectations[i].priority, bc->priority, "handler priority should match expectation"));
136 PetscCall(PicurvAssertBool((PetscBool)((bc->Apply != NULL) == expectations[i].expect_apply), "Apply hook expectation mismatch"));
137 PetscCall(PicurvAssertBool((PetscBool)((bc->Initialize != NULL) == expectations[i].expect_initialize), "Initialize hook expectation mismatch"));
138 PetscCall(DestroyBoundaryHandler(&bc));
139 }
140 PetscFunctionReturn(0);
141}
142/**
143 * @brief Test-local routine.
144 */
145
147{
148 BoundaryCondition *bc = NULL;
149 PetscErrorCode ierr_create;
150
151 PetscFunctionBeginUser;
152 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
154 PetscCall(PetscPopErrorHandler());
155
156 PetscCall(PicurvAssertBool((PetscBool)(ierr_create != 0), "unsupported handler should return a non-zero error code"));
157 if (bc) {
158 PetscCall(PetscFree(bc));
159 }
160 PetscFunctionReturn(0);
161}
162/**
163 * @brief Test-local routine.
164 */
165
167{
168 SimCtx *simCtx = NULL;
169 UserCtx *user = NULL;
170
171 PetscFunctionBeginUser;
172 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 8, 8, 8));
173 simCtx->np = 128;
174
175 for (BCFace face = BC_FACE_NEG_X; face <= BC_FACE_POS_Z; ++face) {
176 PetscInt ci = -1, cj = -1, ck = -1;
177 PetscReal xi = -1.0, eta = -1.0, zta = -1.0;
178 PetscBool placed = PETSC_FALSE;
179
180 user->identifiedInletBCFace = face;
182 user, &user->info,
183 user->info.xs, user->info.ys, user->info.zs,
184 user->info.mx, user->info.my, user->info.mz,
185 0,
186 &ci, &cj, &ck, &xi, &eta, &zta, &placed));
187
188 PetscCall(PicurvAssertBool(placed, "single-rank deterministic face placement should succeed"));
189 PetscCall(PicurvAssertBool((PetscBool)(ci >= user->info.xs && ci < user->info.xs + user->info.xm), "ci must map to owned node window"));
190 PetscCall(PicurvAssertBool((PetscBool)(cj >= user->info.ys && cj < user->info.ys + user->info.ym), "cj must map to owned node window"));
191 PetscCall(PicurvAssertBool((PetscBool)(ck >= user->info.zs && ck < user->info.zs + user->info.zm), "ck must map to owned node window"));
192 PetscCall(PicurvAssertBool((PetscBool)(xi >= 0.0 && xi < 1.0), "xi should be in [0,1)"));
193 PetscCall(PicurvAssertBool((PetscBool)(eta >= 0.0 && eta < 1.0), "eta should be in [0,1)"));
194 PetscCall(PicurvAssertBool((PetscBool)(zta >= 0.0 && zta < 1.0), "zta should be in [0,1)"));
195
196 if (face == BC_FACE_NEG_X || face == BC_FACE_POS_X) {
197 PetscCall(PicurvAssertRealNear(0.5, xi, 1.0e-10, "deterministic x-face placement should sit halfway into boundary-adjacent cell"));
198 } else if (face == BC_FACE_NEG_Y || face == BC_FACE_POS_Y) {
199 PetscCall(PicurvAssertRealNear(0.5, eta, 1.0e-10, "deterministic y-face placement should sit halfway into boundary-adjacent cell"));
200 } else {
201 PetscCall(PicurvAssertRealNear(0.5, zta, 1.0e-10, "deterministic z-face placement should sit halfway into boundary-adjacent cell"));
202 }
203 }
204
205 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
206 PetscFunctionReturn(0);
207}
208/**
209 * @brief Test-local routine.
210 */
211
213{
214 SimCtx *simCtx = NULL;
215 UserCtx *user = NULL;
216 PetscRandom rand_i = NULL, rand_j = NULL, rand_k = NULL;
217
218 PetscFunctionBeginUser;
219 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 8, 8, 8));
220
221 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand_i));
222 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand_j));
223 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand_k));
224 PetscCall(PetscRandomSetInterval(rand_i, 0.0, 1.0));
225 PetscCall(PetscRandomSetInterval(rand_j, 0.0, 1.0));
226 PetscCall(PetscRandomSetInterval(rand_k, 0.0, 1.0));
227 PetscCall(PetscRandomSetFromOptions(rand_i));
228 PetscCall(PetscRandomSetFromOptions(rand_j));
229 PetscCall(PetscRandomSetFromOptions(rand_k));
230
231 for (BCFace face = BC_FACE_NEG_X; face <= BC_FACE_POS_Z; ++face) {
232 PetscInt ci = -1, cj = -1, ck = -1;
233 PetscReal xi = -1.0, eta = -1.0, zta = -1.0;
234 const PetscReal boundary_eps = 5.0e-4;
235
236 user->identifiedInletBCFace = face;
238 user, &user->info,
239 user->info.xs, user->info.ys, user->info.zs,
240 user->info.mx, user->info.my, user->info.mz,
241 &rand_i, &rand_j, &rand_k,
242 &ci, &cj, &ck,
243 &xi, &eta, &zta));
244
245 PetscCall(PicurvAssertBool((PetscBool)(ci >= user->info.xs && ci < user->info.xs + user->info.xm), "ci must map to owned node window"));
246 PetscCall(PicurvAssertBool((PetscBool)(cj >= user->info.ys && cj < user->info.ys + user->info.ym), "cj must map to owned node window"));
247 PetscCall(PicurvAssertBool((PetscBool)(ck >= user->info.zs && ck < user->info.zs + user->info.zm), "ck must map to owned node window"));
248 PetscCall(PicurvAssertBool((PetscBool)(xi >= 0.0 && xi <= 1.0), "xi should be in [0,1]"));
249 PetscCall(PicurvAssertBool((PetscBool)(eta >= 0.0 && eta <= 1.0), "eta should be in [0,1]"));
250 PetscCall(PicurvAssertBool((PetscBool)(zta >= 0.0 && zta <= 1.0), "zta should be in [0,1]"));
251
252 switch (face) {
253 case BC_FACE_NEG_X:
254 PetscCall(PicurvAssertBool((PetscBool)(xi <= boundary_eps), "NEG_X should pin xi near 0"));
255 break;
256 case BC_FACE_POS_X:
257 PetscCall(PicurvAssertBool((PetscBool)(xi >= 1.0 - boundary_eps), "POS_X should pin xi near 1"));
258 break;
259 case BC_FACE_NEG_Y:
260 PetscCall(PicurvAssertBool((PetscBool)(eta <= boundary_eps), "NEG_Y should pin eta near 0"));
261 break;
262 case BC_FACE_POS_Y:
263 PetscCall(PicurvAssertBool((PetscBool)(eta >= 1.0 - boundary_eps), "POS_Y should pin eta near 1"));
264 break;
265 case BC_FACE_NEG_Z:
266 PetscCall(PicurvAssertBool((PetscBool)(zta <= boundary_eps), "NEG_Z should pin zta near 0"));
267 break;
268 case BC_FACE_POS_Z:
269 PetscCall(PicurvAssertBool((PetscBool)(zta >= 1.0 - boundary_eps), "POS_Z should pin zta near 1"));
270 break;
271 }
272 }
273
274 PetscCall(PetscRandomDestroy(&rand_i));
275 PetscCall(PetscRandomDestroy(&rand_j));
276 PetscCall(PetscRandomDestroy(&rand_k));
277 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
278 PetscFunctionReturn(0);
279}
280/**
281 * @brief Entry point for this unit-test binary.
282 */
283
284int main(int argc, char **argv)
285{
286 PetscErrorCode ierr;
287 const PicurvTestCase cases[] = {
288 {"can-rank-service-face-matches-inlet-when-defined", TestCanRankServiceFaceMatchesInletWhenDefined},
289 {"can-rank-service-inlet-face-requires-definition", TestCanRankServiceInletFaceRequiresDefinition},
290 {"boundary-condition-factory-assignments", TestBoundaryConditionFactoryAssignments},
291 {"boundary-condition-factory-implemented-handler-matrix", TestBoundaryConditionFactoryImplementedHandlerMatrix},
292 {"boundary-condition-factory-rejects-unsupported-handler", TestBoundaryConditionFactoryRejectsUnsupportedHandler},
293 {"deterministic-face-grid-location-matrix", TestGetDeterministicFaceGridLocationFaceMatrix},
294 {"random-inlet-face-location-matrix", TestGetRandomCellAndLogicalCoordsOnInletFaceMatrix},
295 };
296
297 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv boundary tests");
298 if (ierr) {
299 return (int)ierr;
300 }
301
302 ierr = PicurvRunTests("unit-boundaries", cases, sizeof(cases) / sizeof(cases[0]));
303 if (ierr) {
304 PetscFinalize();
305 return (int)ierr;
306 }
307
308 ierr = PetscFinalize();
309 return (int)ierr;
310}
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:399
PetscErrorCode BoundaryCondition_Create(BCHandlerType handler_type, BoundaryCondition **new_bc_ptr)
(Private) Creates and configures a specific BoundaryCondition handler object.
Definition Boundaries.c:744
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:126
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:212
The "virtual table" struct for a boundary condition handler object.
Definition variables.h:280
PetscErrorCode(* PreStep)(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow, PetscReal *local_outflow)
Definition variables.h:285
PetscErrorCode(* Destroy)(BoundaryCondition *self)
Definition variables.h:289
PetscErrorCode(* Initialize)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:284
PetscErrorCode(* Apply)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:286
BCPriorityType priority
Definition variables.h:282
static PetscErrorCode TestCanRankServiceFaceMatchesInletWhenDefined(void)
Test-local routine.
static PetscErrorCode TestBoundaryConditionFactoryAssignments(void)
Test-local routine.
int main(int argc, char **argv)
Entry point for this unit-test binary.
static PetscErrorCode TestGetDeterministicFaceGridLocationFaceMatrix(void)
Test-local routine.
static PetscErrorCode TestBoundaryConditionFactoryRejectsUnsupportedHandler(void)
Test-local routine.
static PetscErrorCode TestGetRandomCellAndLogicalCoordsOnInletFaceMatrix(void)
Test-local routine.
static PetscErrorCode TestBoundaryConditionFactoryImplementedHandlerMatrix(void)
Test-local routine.
static PetscErrorCode TestCanRankServiceInletFaceRequiresDefinition(void)
Test-local routine.
static PetscErrorCode DestroyBoundaryHandler(BoundaryCondition **bc_ptr)
Test-local routine.
PetscErrorCode PicurvCreateMinimalContexts(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz)
Shared test-support routine.
PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Shared test-support routine.
PetscErrorCode PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
Shared test-support routine.
PetscErrorCode PicurvRunTests(const char *suite_name, const PicurvTestCase *cases, size_t case_count)
Shared test-support routine.
PetscErrorCode PicurvAssertIntEqual(PetscInt expected, PetscInt actual, const char *context)
Shared test-support routine.
PetscErrorCode PicurvAssertBool(PetscBool value, const char *context)
Shared test-support routine.
C test module for PICurv.
Named test case descriptor consumed by PicurvRunTests.
PetscBool inletFaceDefined
Definition variables.h:757
BCFace identifiedInletBCFace
Definition variables.h:758
PetscInt KM
Definition variables.h:747
BCHandlerType
Defines the specific computational "strategy" for a boundary handler.
Definition variables.h:230
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:243
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:236
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:235
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:245
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:232
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:241
@ BC_HANDLER_OUTLET_PRESSURE
Definition variables.h:242
PetscInt np
Definition variables.h:683
PetscInt JM
Definition variables.h:747
DMDALocalInfo info
Definition variables.h:745
BCPriorityType
Definition variables.h:250
@ BC_PRIORITY_OUTLET
Definition variables.h:255
@ BC_PRIORITY_WALL
Definition variables.h:254
@ BC_PRIORITY_INLET
Definition variables.h:252
PetscInt IM
Definition variables.h:747
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:203
@ BC_FACE_NEG_X
Definition variables.h:204
@ BC_FACE_POS_Z
Definition variables.h:206
@ BC_FACE_POS_Y
Definition variables.h:205
@ BC_FACE_NEG_Z
Definition variables.h:206
@ BC_FACE_POS_X
Definition variables.h:204
@ BC_FACE_NEG_Y
Definition variables.h:205
The master context for the entire simulation.
Definition variables.h:591
User-defined context containing data specific to a single computational grid level.
Definition variables.h:738