PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
test_periodic_dev.c
Go to the documentation of this file.
1/**
2 * @file test_periodic_dev.c
3 * @brief Focused geometric-periodic boundary tests.
4 */
5
6#include "test_support.h"
7
8#include "Boundaries.h"
9
10/**
11 * @brief Appends one key/value pair to a linked list of boundary-condition parameters.
12 */
13static PetscErrorCode AppendBCParam(BC_Param **head, const char *key, const char *value)
14{
15 BC_Param *node = NULL;
16 BC_Param **cursor = head;
17
18 PetscFunctionBeginUser;
19 PetscCheck(head != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "BC param list head cannot be NULL.");
20 PetscCall(PetscCalloc1(1, &node));
21 PetscCall(PetscStrallocpy(key, &node->key));
22 PetscCall(PetscStrallocpy(value, &node->value));
23 while (*cursor) {
24 cursor = &(*cursor)->next;
25 }
26 *cursor = node;
27 PetscFunctionReturn(0);
28}
29/**
30 * @brief Destroys one boundary-condition handler allocated by a periodic test.
31 */
32static PetscErrorCode DestroyBoundaryHandler(BoundaryCondition **bc_ptr)
33{
34 BoundaryCondition *bc = NULL;
35
36 PetscFunctionBeginUser;
37 if (!bc_ptr || !*bc_ptr) PetscFunctionReturn(0);
38
39 bc = *bc_ptr;
40 if (bc->Destroy) {
41 PetscCall(bc->Destroy(bc));
42 }
43 PetscCall(PetscFree(bc));
44 *bc_ptr = NULL;
45 PetscFunctionReturn(0);
46}
47/**
48 * @brief Marks the x faces as periodic for periodic-transfer harnesses.
49 */
59/**
60 * @brief Marks the y faces as periodic for periodic-transfer harnesses.
61 */
71/**
72 * @brief Tests periodic configuration rejects an unpaired geometric periodic face.
73 */
75{
76 SimCtx *simCtx = NULL;
77 UserCtx *user = NULL;
78 PetscErrorCode ierr_validate = 0;
79
80 PetscFunctionBeginUser;
81 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 4, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
84
85 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
86 ierr_validate = BoundarySystem_Validate(user);
87 PetscCall(PetscPopErrorHandler());
88
89 PetscCall(PicurvAssertBool((PetscBool)(ierr_validate != 0), "unpaired periodic faces should be rejected"));
90 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
91 PetscFunctionReturn(0);
92}
93/**
94 * @brief Tests constant translational periodic geometry is accepted and stored.
95 */
96static PetscErrorCode TestPeriodicGeometryStoresTranslation(void)
97{
98 SimCtx *simCtx = NULL;
99 UserCtx *user = NULL;
100 Vec gcoor = NULL;
101 const Cmpnts ***coor = NULL;
102 PetscReal expected_x = 0.0;
103
104 PetscFunctionBeginUser;
105 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 4, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
106 MarkXPeriodic(user);
107
108 PetscCall(DMGetCoordinates(user->da, &gcoor));
109 PetscCall(DMDAVecGetArrayRead(user->fda, gcoor, &coor));
110 expected_x = coor[0][0][user->info.mx - 2].x - coor[0][0][0].x;
111 PetscCall(DMDAVecRestoreArrayRead(user->fda, gcoor, &coor));
112
113 PetscCall(ValidatePeriodicGeometry(user));
114 PetscCall(PicurvAssertBool(user->periodic_translation_valid[0], "X-periodic translation should be marked valid"));
115 PetscCall(PicurvAssertBool((PetscBool)!user->periodic_translation_valid[1], "Y translation should remain invalid"));
116 PetscCall(PicurvAssertBool((PetscBool)!user->periodic_translation_valid[2], "Z translation should remain invalid"));
117 PetscCall(PicurvAssertRealNear(expected_x, user->periodic_translation[0].x, 1.0e-12, "stored X translation"));
118 PetscCall(PicurvAssertRealNear(0.0, user->periodic_translation[0].y, 1.0e-12, "stored X translation y component"));
119 PetscCall(PicurvAssertRealNear(0.0, user->periodic_translation[0].z, 1.0e-12, "stored X translation z component"));
120
121 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
122 PetscFunctionReturn(0);
123}
124/**
125 * @brief Tests mixed periodic directions are validated and stored independently.
126 */
128{
129 SimCtx *simCtx = NULL;
130 UserCtx *user = NULL;
131
132 PetscFunctionBeginUser;
133 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 4, 6, 4, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE));
134 MarkXPeriodic(user);
135 MarkYPeriodic(user);
136
137 PetscCall(ValidatePeriodicGeometry(user));
138 PetscCall(PicurvAssertBool(user->periodic_translation_valid[0], "mixed-periodic X translation should be valid"));
139 PetscCall(PicurvAssertBool(user->periodic_translation_valid[1], "mixed-periodic Y translation should be valid"));
140 PetscCall(PicurvAssertBool((PetscBool)!user->periodic_translation_valid[2], "mixed-periodic Z translation should remain invalid"));
141 PetscCall(PicurvAssertBool((PetscBool)(PetscAbsReal(user->periodic_translation[0].x) > 0.0),
142 "mixed-periodic X translation should have nonzero x component"));
143 PetscCall(PicurvAssertBool((PetscBool)(PetscAbsReal(user->periodic_translation[1].y) > 0.0),
144 "mixed-periodic Y translation should have nonzero y component"));
145
146 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
147 PetscFunctionReturn(0);
148}
149/**
150 * @brief Tests non-translational seam geometry fails before metric construction.
151 */
153{
154 SimCtx *simCtx = NULL;
155 UserCtx *user = NULL;
156 Vec gcoor = NULL;
157 Cmpnts ***coor = NULL;
158 PetscErrorCode ierr_validate = 0;
159
160 PetscFunctionBeginUser;
161 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 4, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
162 MarkXPeriodic(user);
163
164 PetscCall(DMGetCoordinates(user->da, &gcoor));
165 PetscCall(DMDAVecGetArray(user->fda, gcoor, &coor));
166 coor[1][1][user->info.mx - 2].y += 0.25;
167 PetscCall(DMDAVecRestoreArray(user->fda, gcoor, &coor));
168 PetscCall(UpdateLocalGhosts(user, FIELD_ID_COORDINATES));
169
170 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
171 ierr_validate = ValidatePeriodicGeometry(user);
172 PetscCall(PetscPopErrorHandler());
173
174 PetscCall(PicurvAssertBool((PetscBool)(ierr_validate != 0), "varying seam translation should be rejected"));
175 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
176 PetscFunctionReturn(0);
177}
178
179/**
180 * @brief Tests face-center coordinates remain geometrically continuous across a periodic seam.
181 */
183{
184 SimCtx *simCtx = NULL;
185 UserCtx *user = NULL;
186 Cmpnts ***centx = NULL;
187 const Cmpnts ***lcentx = NULL;
188 const FieldId fields[] = {FIELD_ID_CENTX};
189 PetscReal translation, spacing;
190
191 PetscFunctionBeginUser;
192 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 6, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
193 MarkXPeriodic(user);
194 PetscCall(ValidatePeriodicGeometry(user));
195 translation = user->periodic_translation[0].x;
196 spacing = translation / (PetscReal)(user->info.mx - 2);
197
198 PetscCall(DMDAVecGetArray(user->fda, user->Centx, &centx));
199 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; k++) {
200 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; j++) {
201 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; i++) {
202 centx[k][j][i] = (Cmpnts){spacing * i, 2.0 + j, 3.0 + k};
203 }
204 }
205 }
206 PetscCall(DMDAVecRestoreArray(user->fda, user->Centx, &centx));
207
208 PetscCall(SynchronizePeriodicFaceFields(user, 'i', 1, fields));
209 PetscCall(DMDAVecGetArrayRead(user->fda, user->lCentx, &lcentx));
210 PetscCall(PicurvAssertRealNear(-spacing, lcentx[2][2][-1].x, 1.0e-12,
211 "translated Centx negative adjacent ghost"));
212 PetscCall(PicurvAssertRealNear(0.0, lcentx[2][2][0].x, 1.0e-12,
213 "translated Centx negative endpoint"));
214 PetscCall(PicurvAssertRealNear(translation + spacing, lcentx[2][2][user->info.mx - 1].x, 1.0e-12,
215 "translated Centx positive endpoint"));
216 PetscCall(PicurvAssertRealNear(translation + 2.0 * spacing, lcentx[2][2][user->info.mx].x, 1.0e-12,
217 "translated Centx positive adjacent ghost"));
218 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lCentx, &lcentx));
219
220 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
221 PetscFunctionReturn(0);
222}
223
224/**
225 * @brief Tests the dedicated QUICK outer-ghost repair for cell-centered inputs.
226 */
227static PetscErrorCode TestPeriodicQuickStencilPreparation(void)
228{
229 SimCtx *simCtx = NULL;
230 UserCtx *user = NULL;
231 Cmpnts ***ucat = NULL;
232 PetscReal ***nvert = NULL;
233
234 PetscFunctionBeginUser;
235 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 6, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
236 MarkXPeriodic(user);
237
238 PetscCall(DMDAVecGetArray(user->fda, user->Ucat, &ucat));
239 PetscCall(DMDAVecGetArray(user->da, user->Nvert, &nvert));
240 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; k++) {
241 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; j++) {
242 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; i++) {
243 ucat[k][j][i] = (Cmpnts){100.0 + i, 200.0 + i, 300.0 + i};
244 nvert[k][j][i] = 400.0 + i;
245 }
246 }
247 }
248 PetscCall(DMDAVecRestoreArray(user->da, user->Nvert, &nvert));
249 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucat, &ucat));
250 PetscCall(UpdateLocalGhosts(user, FIELD_ID_UCAT));
251 PetscCall(UpdateLocalGhosts(user, FIELD_ID_NVERT));
252 PetscCall(PreparePeriodicQuickStencilFields(user, user->lUcat, user->lNvert));
253
254 PetscCall(DMDAVecGetArrayRead(user->fda, user->lUcat, &ucat));
255 PetscCall(DMDAVecGetArrayRead(user->da, user->lNvert, &nvert));
256 PetscCall(PicurvAssertRealNear(100.0 + user->info.mx - 3, ucat[2][2][-1].x, 1.0e-12,
257 "QUICK Ucat negative outer ghost"));
258 PetscCall(PicurvAssertRealNear(102.0, ucat[2][2][user->info.mx].x, 1.0e-12,
259 "QUICK Ucat positive outer ghost"));
260 PetscCall(PicurvAssertRealNear(400.0 + user->info.mx - 3, nvert[2][2][-1], 1.0e-12,
261 "QUICK Nvert negative outer ghost"));
262 PetscCall(PicurvAssertRealNear(402.0, nvert[2][2][user->info.mx], 1.0e-12,
263 "QUICK Nvert positive outer ghost"));
264 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lNvert, &nvert));
265 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lUcat, &ucat));
266
267 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
268 PetscFunctionReturn(0);
269}
270/**
271 * @brief Tests periodic geometric factory construction.
272 */
273static PetscErrorCode TestPeriodicGeometricFactoryAssignment(void)
274{
275 BoundaryCondition *bc = NULL;
276
277 PetscFunctionBeginUser;
279 PetscCall(PicurvAssertBool((PetscBool)(bc != NULL), "periodic geometric factory should allocate a handler"));
280 PetscCall(PicurvAssertIntEqual(BC_PRIORITY_WALL, bc->priority, "periodic geometric handler priority"));
281 PetscCall(PicurvAssertBool((PetscBool)(bc->Apply == NULL), "periodic geometric handler should not expose Apply"));
282 PetscCall(PicurvAssertBool((PetscBool)(bc->Initialize == NULL), "periodic geometric handler should not expose Initialize"));
283 PetscCall(DestroyBoundaryHandler(&bc));
284 PetscFunctionReturn(0);
285}
286/**
287 * @brief Tests periodic metric transfer through the aggregate periodic-metrics helper.
288 */
290{
291 SimCtx *simCtx = NULL;
292 UserCtx *user = NULL;
293 PetscReal ***aj = NULL;
294 PetscReal expected_neg_face = 0.0;
295 PetscReal expected_pos_face = 0.0;
296 PetscReal expected_neg_ghost = 0.0;
297 PetscReal expected_pos_ghost = 0.0;
298
299 PetscFunctionBeginUser;
300 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 4, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
301 MarkXPeriodic(user);
302 PetscCall(ValidatePeriodicGeometry(user));
303
304 PetscCall(DMDAVecGetArray(user->da, user->Aj, &aj));
305 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; ++k) {
306 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; ++j) {
307 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; ++i) {
308 aj[k][j][i] = 100.0 + (PetscReal)i;
309 }
310 }
311 }
312 PetscCall(DMDAVecRestoreArray(user->da, user->Aj, &aj));
313 PetscCall(DMGlobalToLocalBegin(user->da, user->Aj, INSERT_VALUES, user->lAj));
314 PetscCall(DMGlobalToLocalEnd(user->da, user->Aj, INSERT_VALUES, user->lAj));
315 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, &aj));
316 expected_neg_face = aj[2][2][user->info.mx - 2];
317 expected_pos_face = aj[2][2][1];
318 expected_neg_ghost = expected_pos_face;
319 expected_pos_ghost = expected_neg_face;
320 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, &aj));
321
322 PetscCall(ApplyMetricsPeriodicBCs(user));
323
324 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, &aj));
325 PetscCall(PicurvAssertRealNear(expected_neg_face, aj[2][2][0], 1.0e-12, "NEG_X periodic metric face should copy from the opposite interior face"));
326 PetscCall(PicurvAssertRealNear(expected_pos_face, aj[2][2][user->info.mx - 1], 1.0e-12, "POS_X periodic metric face should copy from the leading interior face"));
327 PetscCall(PicurvAssertRealNear(expected_neg_ghost, aj[2][2][-1], 1.0e-12, "cell-centered Aj negative ghost should retain PETSc wraparound"));
328 PetscCall(PicurvAssertRealNear(expected_pos_ghost, aj[2][2][user->info.mx], 1.0e-12, "cell-centered Aj positive ghost should retain PETSc wraparound"));
329 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, &aj));
330
331 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
332 PetscFunctionReturn(0);
333}
334/**
335 * @brief Tests ordered cell-field synchronization across two periodic directions.
336 */
338{
339 SimCtx *simCtx = NULL;
340 UserCtx *user = NULL;
341 PetscReal ***p = NULL;
342 PetscReal ***cs = NULL;
343 PetscReal ***diffusivity = NULL;
344 Cmpnts ***ucat = NULL;
345 PetscInt mx, my;
347
348 PetscFunctionBeginUser;
349 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 4, 4, 4, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE));
350 MarkXPeriodic(user);
351 MarkYPeriodic(user);
352
353 PetscCall(DMCreateGlobalVector(user->da, &user->Nu_t));
354 PetscCall(DMCreateLocalVector(user->da, &user->lNu_t));
355 PetscCall(VecSet(user->Nu_t, 5.0));
356 PetscCall(VecSet(user->lNu_t, 0.0));
357 PetscCall(DMCreateGlobalVector(user->da, &user->CS));
358 PetscCall(DMCreateLocalVector(user->da, &user->lCs));
359 PetscCall(VecSet(user->Phi, 3.0));
360 PetscCall(VecSet(user->Nvert, 4.0));
361
362 PetscCall(DMDAVecGetArray(user->da, user->P, &p));
363 PetscCall(DMDAVecGetArray(user->da, user->CS, &cs));
364 PetscCall(DMDAVecGetArray(user->da, user->Diffusivity, &diffusivity));
365 PetscCall(DMDAVecGetArray(user->fda, user->Ucat, &ucat));
366 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; ++k) {
367 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; ++j) {
368 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; ++i) {
369 PetscReal value = (PetscReal)(i + 10 * j + 100 * k);
370 p[k][j][i] = value;
371 cs[k][j][i] = value + 4000.0;
372 diffusivity[k][j][i] = value + 5000.0;
373 ucat[k][j][i].x = value + 1000.0;
374 ucat[k][j][i].y = value + 2000.0;
375 ucat[k][j][i].z = value + 3000.0;
376 }
377 }
378 }
379 PetscCall(DMDAVecRestoreArray(user->da, user->P, &p));
380 PetscCall(DMDAVecRestoreArray(user->da, user->CS, &cs));
381 PetscCall(DMDAVecRestoreArray(user->da, user->Diffusivity, &diffusivity));
382 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucat, &ucat));
383
384 PetscCall(SynchronizePeriodicCellFields(user, 7, fields));
385
386 mx = user->info.mx;
387 my = user->info.my;
388 PetscCall(DMDAVecGetArrayRead(user->da, user->P, &p));
389 PetscCall(DMDAVecGetArrayRead(user->da, user->CS, &cs));
390 PetscCall(DMDAVecGetArrayRead(user->da, user->Diffusivity, &diffusivity));
391 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucat, &ucat));
392 PetscCall(PicurvAssertRealNear((PetscReal)((mx - 2) + 20 + 200), p[2][2][0], 1.0e-12,
393 "x-periodic negative endpoint should copy the opposite interior value"));
394 PetscCall(PicurvAssertRealNear((PetscReal)(1 + 20 + 200), p[2][2][mx - 1], 1.0e-12,
395 "x-periodic positive endpoint should copy the leading interior value"));
396 PetscCall(PicurvAssertRealNear((PetscReal)(2 + 10 * (my - 2) + 200), p[2][0][2], 1.0e-12,
397 "y-periodic negative endpoint should copy the opposite interior value"));
398 PetscCall(PicurvAssertRealNear((PetscReal)((mx - 2) + 10 * (my - 2) + 200), p[2][0][0], 1.0e-12,
399 "ordered synchronization should propagate the opposite periodic corner"));
400 PetscCall(PicurvAssertRealNear((PetscReal)((mx - 2) + 10 * (my - 2) + 1200), ucat[2][0][0].x, 1.0e-12,
401 "vector fields should use the same ordered periodic-corner protocol"));
402 PetscCall(PicurvAssertRealNear((PetscReal)((mx - 2) + 10 * (my - 2) + 4200), cs[2][0][0], 1.0e-12,
403 "CS should use the ordered periodic-corner protocol"));
404 PetscCall(PicurvAssertRealNear((PetscReal)((mx - 2) + 10 * (my - 2) + 5200), diffusivity[2][0][0], 1.0e-12,
405 "Diffusivity should use the ordered periodic-corner protocol"));
406 PetscCall(DMDAVecRestoreArrayRead(user->da, user->P, &p));
407 PetscCall(DMDAVecRestoreArrayRead(user->da, user->CS, &cs));
408 PetscCall(DMDAVecRestoreArrayRead(user->da, user->Diffusivity, &diffusivity));
409 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucat, &ucat));
410 PetscCall(PicurvAssertVecConstant(user->Nu_t, 5.0, 1.0e-12, "Nu_t should be accepted by periodic cell synchronization"));
411
412 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
413 PetscFunctionReturn(0);
414}
415
416/**
417 * @brief Tests ordered persistent synchronization for an I-face scalar metric.
418 */
420{
421 SimCtx *simCtx = NULL;
422 UserCtx *user = NULL;
423 PetscReal ***iaj = NULL;
424 Cmpnts ***csi = NULL;
425 const FieldId fields[] = {FIELD_ID_CSI, FIELD_ID_IAJ};
426 PetscInt mx, my;
427
428 PetscFunctionBeginUser;
429 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 4, 4, 4, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE));
430 MarkXPeriodic(user);
431 MarkYPeriodic(user);
432 mx = user->info.mx;
433 my = user->info.my;
434
435 PetscCall(DMDAVecGetArray(user->da, user->IAj, &iaj));
436 PetscCall(DMDAVecGetArray(user->fda, user->Csi, &csi));
437 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; k++) {
438 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; j++) {
439 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; i++) {
440 PetscReal value = (PetscReal)(i + 10 * j + 100 * k);
441 iaj[k][j][i] = value;
442 csi[k][j][i] = (Cmpnts){value + 1000.0, value + 2000.0, value + 3000.0};
443 }
444 }
445 }
446 PetscCall(DMDAVecRestoreArray(user->da, user->IAj, &iaj));
447 PetscCall(DMDAVecRestoreArray(user->fda, user->Csi, &csi));
448
449 PetscCall(SynchronizePeriodicFaceFields(user, 'i', 2, fields));
450
451 PetscCall(DMDAVecGetArrayRead(user->da, user->IAj, &iaj));
452 PetscCall(DMDAVecGetArrayRead(user->fda, user->Csi, &csi));
453 PetscCall(PicurvAssertRealNear((PetscReal)(mx - 2 + 20 + 200), iaj[2][2][0], 1.0e-12,
454 "I-face negative seam should copy the opposite physical seam face"));
455 PetscCall(PicurvAssertRealNear((PetscReal)(1 + 20 + 200), iaj[2][2][mx - 1], 1.0e-12,
456 "I-face positive dummy should copy the leading physical face"));
457 PetscCall(PicurvAssertRealNear((PetscReal)(2 + 10 * (my - 2) + 200), iaj[2][0][2], 1.0e-12,
458 "I-face field should use cell-style synchronization tangentially"));
459 PetscCall(PicurvAssertRealNear((PetscReal)(mx - 2 + 10 * (my - 2) + 200), iaj[2][0][0], 1.0e-12,
460 "ordered face synchronization should propagate mixed-axis corners"));
461 PetscCall(PicurvAssertRealNear((PetscReal)(mx - 2 + 10 * (my - 2) + 1200), csi[2][0][0].x, 1.0e-12,
462 "vector I-face fields should use the same ordered synchronization"));
463 PetscCall(DMDAVecRestoreArrayRead(user->da, user->IAj, &iaj));
464 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Csi, &csi));
465
466 PetscCall(DMDAVecGetArrayRead(user->da, user->lIAj, &iaj));
467 PetscCall(PicurvAssertRealNear((PetscReal)(mx - 3 + 20 + 200), iaj[2][2][-1], 1.0e-12,
468 "I-face negative adjacent ghost should be shifted to the prior face"));
469 PetscCall(PicurvAssertRealNear((PetscReal)(2 + 20 + 200), iaj[2][2][mx], 1.0e-12,
470 "I-face positive adjacent ghost should be shifted to the next face"));
471 PetscCall(PicurvAssertRealNear((PetscReal)(2 + 10 + 200), iaj[2][-1][2], 1.0e-12,
472 "I-face tangential ghosts should retain cell-style wraparound"));
473 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lIAj, &iaj));
474
475 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
476 PetscFunctionReturn(0);
477}
478
479/**
480 * @brief Tests ordered persistent synchronization for component-staggered Ucont.
481 */
483{
484 SimCtx *simCtx = NULL;
485 UserCtx *user = NULL;
486 Cmpnts ***ucont = NULL;
487 const FieldId fields[] = {FIELD_ID_UCONT};
488 PetscInt mx, my;
489
490 PetscFunctionBeginUser;
491 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 4, 4, 4, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE));
492 MarkXPeriodic(user);
493 MarkYPeriodic(user);
494 mx = user->info.mx;
495 my = user->info.my;
496
497 PetscCall(DMDAVecGetArray(user->fda, user->Ucont, &ucont));
498 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; k++) {
499 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; j++) {
500 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; i++) {
501 PetscReal value = (PetscReal)(i + 10 * j + 100 * k);
502 ucont[k][j][i] = (Cmpnts){value + 1000.0, value + 2000.0, value + 3000.0};
503 }
504 }
505 }
506 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucont, &ucont));
507
508 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
509
510 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &ucont));
511 PetscCall(PicurvAssertRealNear((PetscReal)(mx - 2 + 20 + 1200), ucont[2][2][0].x, 1.0e-12,
512 "Ucont.x negative X seam should copy the opposite physical seam"));
513 PetscCall(PicurvAssertRealNear((PetscReal)(1 + 20 + 2200), ucont[2][2][mx - 1].y, 1.0e-12,
514 "Ucont.y positive X dummy should copy the leading physical value"));
515 PetscCall(PicurvAssertRealNear((PetscReal)(2 + 10 * (my - 2) + 3200), ucont[2][0][2].z, 1.0e-12,
516 "Ucont.z should synchronize tangentially in Y"));
517 PetscCall(PicurvAssertRealNear((PetscReal)(mx - 2 + 10 * (my - 2) + 2200), ucont[2][0][0].y, 1.0e-12,
518 "ordered staggered synchronization should propagate mixed-axis corners"));
519 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucont, &ucont));
520
521 PetscCall(DMDAVecGetArrayRead(user->fda, user->lUcont, &ucont));
522 PetscCall(PicurvAssertRealNear((PetscReal)(mx - 3 + 20 + 1200), ucont[2][2][-1].x, 1.0e-12,
523 "Ucont.x should use face-normal X ghost repair"));
524 PetscCall(PicurvAssertRealNear((PetscReal)(1 + 20 + 2200), ucont[2][2][-1].y, 1.0e-12,
525 "Ucont.y should retain cell-style wraparound tangentially in X"));
526 PetscCall(PicurvAssertRealNear((PetscReal)(2 + 20 + 1200), ucont[2][2][mx].x, 1.0e-12,
527 "Ucont.x positive adjacent ghost should use the next face"));
528 PetscCall(PicurvAssertRealNear((PetscReal)(mx - 2 + 20 + 2200), ucont[2][2][mx].y, 1.0e-12,
529 "Ucont.y positive X ghost should retain cell-style wraparound"));
530 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lUcont, &ucont));
531
532 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
533 PetscFunctionReturn(0);
534}
535
536/**
537 * @brief Tests mixed-boundary post-projection cell finalization and Ucont preservation.
538 */
540{
541 SimCtx *simCtx = NULL;
542 UserCtx *user = NULL;
543 Vec ucont_before = NULL;
544 Vec ucont_difference = NULL;
545 Cmpnts ***ucat = NULL;
546 Cmpnts ***ucont = NULL;
547 PetscReal ***p = NULL;
548 PetscReal difference_norm = 0.0;
549 PetscInt mx;
550
551 PetscFunctionBeginUser;
552 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 4, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
553 MarkXPeriodic(user);
554 PetscCall(VecSet(user->Bcs.Ubcs, 0.0));
555
556 PetscCall(DMDAVecGetArray(user->fda, user->Ucat, &ucat));
557 PetscCall(DMDAVecGetArray(user->fda, user->Ucont, &ucont));
558 PetscCall(DMDAVecGetArray(user->da, user->P, &p));
559 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; ++k) {
560 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; ++j) {
561 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; ++i) {
562 PetscReal value = (PetscReal)(i + 10 * j + 100 * k);
563 ucat[k][j][i].x = value + 1000.0;
564 ucat[k][j][i].y = value + 2000.0;
565 ucat[k][j][i].z = value + 3000.0;
566 ucont[k][j][i].x = value + 4000.0;
567 ucont[k][j][i].y = value + 5000.0;
568 ucont[k][j][i].z = value + 6000.0;
569 p[k][j][i] = value;
570 }
571 }
572 }
573 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucat, &ucat));
574 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucont, &ucont));
575 PetscCall(DMDAVecRestoreArray(user->da, user->P, &p));
576
577 PetscCall(VecDuplicate(user->Ucont, &ucont_before));
578 PetscCall(VecDuplicate(user->Ucont, &ucont_difference));
579 PetscCall(VecCopy(user->Ucont, ucont_before));
580
581 PetscCall(FinalizePostProjectionCellFields(user));
582
583 PetscCall(VecWAXPY(ucont_difference, -1.0, user->Ucont, ucont_before));
584 PetscCall(VecNorm(ucont_difference, NORM_INFINITY, &difference_norm));
585 PetscCall(PicurvAssertRealNear(0.0, difference_norm, 1.0e-12,
586 "post-projection cell finalization must preserve mixed-boundary Ucont"));
587
588 mx = user->info.mx;
589 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucat, &ucat));
590 PetscCall(DMDAVecGetArrayRead(user->da, user->P, &p));
591 PetscCall(PicurvAssertRealNear((PetscReal)((mx - 2) + 20 + 200 + 1000), ucat[2][2][0].x, 1.0e-12,
592 "mixed finalization should restore the negative x-periodic Ucat endpoint"));
593 PetscCall(PicurvAssertRealNear((PetscReal)(-(2 + 10 + 200 + 1000)), ucat[2][0][2].x, 1.0e-12,
594 "mixed finalization should extrapolate the non-periodic y dummy face"));
595 PetscCall(PicurvAssertRealNear((PetscReal)(-((mx - 2) + 10 + 200 + 1000)), ucat[2][0][0].x, 1.0e-12,
596 "periodic synchronization should win at a mixed x-periodic/y-physical edge"));
597 PetscCall(PicurvAssertRealNear((PetscReal)((mx - 2) + 20 + 200), p[2][2][0], 1.0e-12,
598 "mixed finalization should restore the negative x-periodic pressure endpoint"));
599 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucat, &ucat));
600 PetscCall(DMDAVecRestoreArrayRead(user->da, user->P, &p));
601
602 PetscCall(VecDestroy(&ucont_difference));
603 PetscCall(VecDestroy(&ucont_before));
604 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
605 PetscFunctionReturn(0);
606}
607
608/** @brief Verifies reconstructed Ucat periodic duplicates and local values are finalized. */
610{
611 SimCtx *simCtx=NULL; UserCtx *user=NULL; Cmpnts ***ucont=NULL,***ucat=NULL,***lucat=NULL;
612 const FieldId staggered_fields[] = {FIELD_ID_UCONT}, cell_fields[] = {FIELD_ID_UCAT};
613 PetscInt mx;
614 PetscFunctionBeginUser;
615 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx,&user,4,4,4,PETSC_TRUE,PETSC_FALSE,PETSC_FALSE));
616 MarkXPeriodic(user); mx=user->info.mx;
617 PetscCall(VecSet(user->Ucont,0.0));
618 PetscCall(DMDAVecGetArray(user->fda,user->Ucont,&ucont));
619 for(PetscInt k=1;k<user->info.mz-1;k++) for(PetscInt j=1;j<user->info.my-1;j++)
620 for(PetscInt i=1;i<mx-1;i++) ucont[k][j][i].x=(PetscReal)i;
621 PetscCall(DMDAVecRestoreArray(user->fda,user->Ucont,&ucont));
622 PetscCall(SynchronizePeriodicStaggeredFields(user,1,staggered_fields));
623 PetscCall(VecSet(user->Ucat,-99.0));
624 PetscCall(Contra2Cart(user));
625 PetscCall(SynchronizePeriodicCellFields(user,1,cell_fields));
626 PetscCall(UpdateLocalGhosts(user, FIELD_ID_UCAT));
627
628 PetscCall(DMDAVecGetArrayRead(user->fda,user->Ucat,&ucat));
629 PetscCall(PicurvAssertRealNear(ucat[2][2][mx-2].x,ucat[2][2][0].x,1e-12,"negative Ucat duplicate follows opposite independent cell"));
630 PetscCall(PicurvAssertRealNear(ucat[2][2][1].x,ucat[2][2][mx-1].x,1e-12,"positive Ucat duplicate follows leading independent cell"));
631 PetscCall(DMDAVecGetArrayRead(user->fda,user->lUcat,&lucat));
632 PetscCall(PicurvAssertRealNear(ucat[2][2][0].x,lucat[2][2][0].x,1e-12,"local negative Ucat endpoint matches corrected global"));
633 PetscCall(PicurvAssertRealNear(ucat[2][2][mx-1].x,lucat[2][2][mx-1].x,1e-12,"local positive Ucat endpoint matches corrected global"));
634 PetscCall(DMDAVecRestoreArrayRead(user->fda,user->lUcat,&lucat));
635 PetscCall(DMDAVecRestoreArrayRead(user->fda,user->Ucat,&ucat));
636 PetscCall(PicurvDestroyMinimalContexts(&simCtx,&user));
637 PetscFunctionReturn(0);
638}
639/**
640 * @brief Runs the focused geometric-periodic PETSc test binary.
641 */
642int main(int argc, char **argv)
643{
644 PetscErrorCode ierr;
645 const PicurvTestCase cases[] = {
646 {"periodic-geometric-factory-assignment", TestPeriodicGeometricFactoryAssignment},
647 {"periodic-configuration-requires-paired-faces", TestPeriodicConfigurationRequiresPairedFaces},
648 {"periodic-geometry-stores-translation", TestPeriodicGeometryStoresTranslation},
649 {"periodic-geometry-stores-mixed-translations", TestPeriodicGeometryStoresMixedTranslations},
650 {"periodic-geometry-rejects-varying-translation", TestPeriodicGeometryRejectsVaryingTranslation},
651 {"periodic-face-center-coordinate-synchronization", TestPeriodicFaceCenterCoordinateSynchronization},
652 {"periodic-quick-stencil-preparation", TestPeriodicQuickStencilPreparation},
653 {"apply-metrics-periodic-bcs-synchronizes-aj", TestApplyMetricsPeriodicBCsSynchronizesAj},
654 {"synchronize-periodic-cell-fields-copies-mixed-axes", TestSynchronizePeriodicCellFieldsCopiesMixedAxes},
655 {"synchronize-periodic-face-fields-copies-mixed-axes", TestSynchronizePeriodicFaceFieldsCopiesMixedAxes},
656 {"synchronize-periodic-staggered-fields-copies-mixed-axes", TestSynchronizePeriodicStaggeredFieldsCopiesMixedAxes},
657 {"finalize-post-projection-cell-fields-mixed-boundaries", TestFinalizePostProjectionCellFieldsMixedBoundaries},
658 {"contra2cart-periodic-ucat-finalization", TestContra2CartPeriodicUcatFinalization},
659 };
660
661 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv geometric-periodic tests");
662 if (ierr) {
663 return (int)ierr;
664 }
665
666 ierr = PicurvRunTests("unit-periodic", cases, sizeof(cases) / sizeof(cases[0]));
667 if (ierr) {
668 PetscFinalize();
669 return (int)ierr;
670 }
671
672 ierr = PetscFinalize();
673 return (int)ierr;
674}
PetscErrorCode PreparePeriodicQuickStencilFields(UserCtx *user, Vec local_vector_field, Vec local_scalar_field)
Repairs the outer adjacent periodic ghosts used by QUICK cell stencils.
PetscErrorCode ApplyMetricsPeriodicBCs(UserCtx *user)
(Orchestrator) Updates all metric-related fields in the local ghost cell regions for periodic boundar...
PetscErrorCode BoundarySystem_Validate(UserCtx *user)
(Public) Validates the consistency and compatibility of the parsed boundary condition system.
Definition Boundaries.c:815
PetscErrorCode BoundaryCondition_Create(BCHandlerType handler_type, BoundaryCondition **new_bc_ptr)
(Private) Creates and configures a specific BoundaryCondition handler object.
Definition Boundaries.c:729
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent component-staggered vector fields.
PetscErrorCode FinalizePostProjectionCellFields(UserCtx *user)
Finalizes cell-centered fields after the projection step.
PetscErrorCode SynchronizePeriodicFaceFields(UserCtx *user, char face_direction, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent fields belonging to one face family.
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes periodic endpoint cells for a list of cell-centered fields.
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_CSI
@ FIELD_ID_IAJ
@ FIELD_ID_NVERT
@ FIELD_ID_UCAT
@ FIELD_ID_COORDINATES
@ FIELD_ID_NU_T
@ FIELD_ID_UCONT
@ FIELD_ID_PHI
@ FIELD_ID_CS
@ FIELD_ID_P
@ FIELD_ID_CENTX
@ FIELD_ID_DIFFUSIVITY
PetscErrorCode ValidatePeriodicGeometry(UserCtx *user)
Validates that configured geometric periodic seams match by translation.
Definition grid.c:421
PetscErrorCode Contra2Cart(UserCtx *user)
Reconstructs Cartesian velocity (Ucat) at cell centers from contravariant velocity (Ucont) defined on...
Definition setup.c:3262
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:2451
The "virtual table" struct for a boundary condition handler object.
Definition variables.h:383
PetscErrorCode(* Destroy)(BoundaryCondition *self)
Definition variables.h:392
PetscErrorCode(* Initialize)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:387
PetscErrorCode(* Apply)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:389
BCPriorityType priority
Definition variables.h:385
static PetscErrorCode TestContra2CartPeriodicUcatFinalization(void)
Verifies reconstructed Ucat periodic duplicates and local values are finalized.
static void MarkXPeriodic(UserCtx *user)
Marks the x faces as periodic for periodic-transfer harnesses.
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 TestPeriodicGeometricFactoryAssignment(void)
Tests periodic geometric factory construction.
static PetscErrorCode TestPeriodicFaceCenterCoordinateSynchronization(void)
Tests face-center coordinates remain geometrically continuous across a periodic seam.
int main(int argc, char **argv)
Runs the focused geometric-periodic PETSc test binary.
static PetscErrorCode TestPeriodicGeometryStoresTranslation(void)
Tests constant translational periodic geometry is accepted and stored.
static PetscErrorCode TestSynchronizePeriodicFaceFieldsCopiesMixedAxes(void)
Tests ordered persistent synchronization for an I-face scalar metric.
static PetscErrorCode TestFinalizePostProjectionCellFieldsMixedBoundaries(void)
Tests mixed-boundary post-projection cell finalization and Ucont preservation.
static PetscErrorCode TestPeriodicConfigurationRequiresPairedFaces(void)
Tests periodic configuration rejects an unpaired geometric periodic face.
static PetscErrorCode TestPeriodicQuickStencilPreparation(void)
Tests the dedicated QUICK outer-ghost repair for cell-centered inputs.
static PetscErrorCode TestSynchronizePeriodicCellFieldsCopiesMixedAxes(void)
Tests ordered cell-field synchronization across two periodic directions.
static PetscErrorCode TestPeriodicGeometryRejectsVaryingTranslation(void)
Tests non-translational seam geometry fails before metric construction.
static PetscErrorCode TestSynchronizePeriodicStaggeredFieldsCopiesMixedAxes(void)
Tests ordered persistent synchronization for component-staggered Ucont.
static PetscErrorCode DestroyBoundaryHandler(BoundaryCondition **bc_ptr)
Destroys one boundary-condition handler allocated by a periodic test.
static PetscErrorCode TestPeriodicGeometryStoresMixedTranslations(void)
Tests mixed periodic directions are validated and stored independently.
static PetscErrorCode TestApplyMetricsPeriodicBCsSynchronizesAj(void)
Tests periodic metric transfer through the aggregate periodic-metrics helper.
static void MarkYPeriodic(UserCtx *user)
Marks the y faces as periodic for periodic-transfer harnesses.
PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
PetscErrorCode PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
Destroys minimal SimCtx/UserCtx fixtures and all owned PETSc objects.
PetscErrorCode PicurvCreateMinimalContextsWithPeriodicity(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz, PetscBool x_periodic, PetscBool y_periodic, PetscBool z_periodic)
Builds minimal SimCtx and UserCtx fixtures for C unit tests with configurable periodicity.
PetscErrorCode PicurvRunTests(const char *suite_name, const PicurvTestCase *cases, size_t case_count)
Runs a named C test suite and prints pass/fail progress markers.
PetscErrorCode PicurvAssertVecConstant(Vec vec, PetscScalar expected, PetscReal tol, const char *context)
Asserts that a PETSc vector is spatially constant within tolerance.
PetscErrorCode PicurvAssertIntEqual(PetscInt expected, PetscInt actual, const char *context)
Asserts that two integer values are equal.
PetscErrorCode PicurvAssertBool(PetscBool value, const char *context)
Asserts that one boolean condition is true.
Shared declarations for the PICurv C test fixture and assertion layer.
Named test case descriptor consumed by PicurvRunTests.
@ PERIODIC
Definition variables.h:322
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:1096
Vec lNvert
Definition variables.h:1111
struct BC_Param_s * next
Definition variables.h:369
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:346
BCHandlerType handler_type
Definition variables.h:399
Vec Ucont
Definition variables.h:1111
Vec Ubcs
Physical Cartesian velocity at boundary faces. Full 3D array but only boundary-face entries are meani...
Definition variables.h:153
PetscScalar x
Definition variables.h:121
Vec Centx
Definition variables.h:1147
Vec lNu_t
Definition variables.h:1154
PetscScalar z
Definition variables.h:121
Vec lCentx
Definition variables.h:1148
Vec lUcont
Definition variables.h:1111
Vec Diffusivity
Definition variables.h:1114
DMDALocalInfo info
Definition variables.h:1083
@ BC_PRIORITY_WALL
Definition variables.h:357
Vec lUcat
Definition variables.h:1111
PetscScalar y
Definition variables.h:121
Cmpnts periodic_translation[3]
Definition variables.h:1092
Vec Nvert
Definition variables.h:1111
PetscBool periodic_translation_valid[3]
Definition variables.h:1093
BCType mathematical_type
Definition variables.h:398
@ BC_FACE_NEG_X
Definition variables.h:292
@ BC_FACE_POS_Y
Definition variables.h:293
@ BC_FACE_POS_X
Definition variables.h:292
@ BC_FACE_NEG_Y
Definition variables.h:293
A node in a linked list for storing key-value parameters from the bcs.dat file.
Definition variables.h:366
A 3D point or vector with PetscScalar components.
Definition variables.h:120
The master context for the entire simulation.
Definition variables.h:858
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1071
A generic C-style linked list node for integers.
Definition variables.h:469