PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
test_statistics_target.c
Go to the documentation of this file.
1/**
2 * @file test_statistics_target.c
3 * @brief C unit tests for pointwise spatial target resolution.
4 *
5 * Covers the layout acceptance items in @ref p58_validation_sec — cell, node, and
6 * I/J/K-face layouts, and nonperiodic, mixed, and fully periodic domains with no
7 * duplicate planes.
8 *
9 * The fixture builds a DMDA of size `n + 1` per dimension, so with `n = 6` each
10 * dimension has seven slots: one extra non-physical high-side slot, and under the
11 * solver's shifted convention a boundary/dummy slot at index zero.
12 */
13
14#include "test_support.h"
15
16#include "statistics_target.h"
17#include "setup.h"
18
19#define TARGET_TEST_N 6 /* fixture size; DMDA is TARGET_TEST_N + 1 per dimension */
20#define TARGET_CELL_EXTENT 5 /* cell-like span: indices [1, 6) */
21#define TARGET_NODE_EXTENT 6 /* node-like nonperiodic span: indices [0, 6) */
22
23/** @brief Builds a plan and returns its global point count. */
24static PetscErrorCode PlanCount(UserCtx *user, FieldId field_id, PetscInt *count)
25{
27
28 PetscFunctionBeginUser;
29 PetscCall(SpatialTargetPlanCreate(user, field_id, PICURV_STATISTICS_MASK_FLUID, &plan));
30 PetscCall(SpatialTargetPlanGlobalPointCount(&plan, PETSC_COMM_WORLD, count));
31 PetscFunctionReturn(0);
32}
33
34/** @brief Cell-centered fields must exclude the dummy slot and the extra high slot. */
36{
37 SimCtx *simCtx = NULL;
38 UserCtx *user = NULL;
40 PetscInt local = 0, global = 0;
41
42 PetscFunctionBeginUser;
44
46 PetscCall(PicurvAssertIntEqual(PICURV_TARGET_POINTWISE, plan.kind, "plan kind should be pointwise"));
48 "P should resolve as cell-centered"));
49 /* On one rank the owned range spans the block, so the plan is the layout span. */
50 if (simCtx->size == 1) {
51 for (PetscInt dim = 0; dim < 3; ++dim) {
52 PetscCall(PicurvAssertIntEqual(1, plan.start[dim],
53 "cell-centered span must skip the index-zero dummy slot"));
54 PetscCall(PicurvAssertIntEqual(TARGET_TEST_N, plan.end[dim],
55 "cell-centered span must skip the extra high-side slot"));
56 }
57 }
58 PetscCall(SpatialTargetPlanLocalPointCount(&plan, &local));
59 PetscCall(SpatialTargetPlanGlobalPointCount(&plan, PETSC_COMM_WORLD, &global));
61 "cell-centered global point count"));
62 PetscCall(PicurvAssertBool((PetscBool)(local <= global), "local count cannot exceed global count"));
63
64 /* Vector and scalar cell-centered fields share one domain. */
65 PetscCall(PlanCount(user, FIELD_ID_UCAT, &global));
67 "Ucat shares the cell-centered domain regardless of dof"));
68
69 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
70 PetscFunctionReturn(0);
71}
72
73/** @brief Each face family is node-like in exactly its own direction. */
75{
76 SimCtx *simCtx = NULL;
77 UserCtx *user = NULL;
78 PetscInt global = 0;
79 const PetscInt cell = TARGET_CELL_EXTENT;
80 const PetscInt node = TARGET_NODE_EXTENT;
81
82 PetscFunctionBeginUser;
84
85 PetscCall(PlanCount(user, FIELD_ID_CSI, &global));
86 PetscCall(PicurvAssertIntEqual(node * cell * cell, global, "I-face domain is node-like in i only"));
87 PetscCall(PlanCount(user, FIELD_ID_ETA, &global));
88 PetscCall(PicurvAssertIntEqual(cell * node * cell, global, "J-face domain is node-like in j only"));
89 PetscCall(PlanCount(user, FIELD_ID_ZET, &global));
90 PetscCall(PicurvAssertIntEqual(cell * cell * node, global, "K-face domain is node-like in k only"));
91 PetscCall(PlanCount(user, FIELD_ID_COORDINATES, &global));
92 PetscCall(PicurvAssertIntEqual(node * node * node, global, "node-centered domain is node-like in all directions"));
93
94 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
95 PetscFunctionReturn(0);
96}
97
98/**
99 * @brief Periodic directions must drop the wrapped duplicate plane.
100 *
101 * The periodic repair algorithms write index zero and the final slot from the
102 * opposite side, so both are dependent duplicates. A node-like direction
103 * therefore loses one entry under periodicity, while a cell-like direction is
104 * unchanged because its duplicates were already outside the span.
105 */
107{
108 SimCtx *simCtx = NULL;
109 UserCtx *user = NULL;
110 PetscInt global = 0;
111 const PetscInt cell = TARGET_CELL_EXTENT;
112 const PetscInt node = TARGET_NODE_EXTENT;
113
114 PetscFunctionBeginUser;
115 /* Mixed: periodic in i only. */
116 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user,
118 PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
119 PetscCall(PlanCount(user, FIELD_ID_P, &global));
120 PetscCall(PicurvAssertIntEqual(cell * cell * cell, global,
121 "cell-centered count is unchanged by periodicity"));
122 PetscCall(PlanCount(user, FIELD_ID_CSI, &global));
123 PetscCall(PicurvAssertIntEqual(cell * cell * cell, global,
124 "I-face loses its duplicate plane when i is periodic"));
125 PetscCall(PlanCount(user, FIELD_ID_ETA, &global));
126 PetscCall(PicurvAssertIntEqual(cell * node * cell, global,
127 "J-face keeps its node-like span when only i is periodic"));
128 PetscCall(PlanCount(user, FIELD_ID_COORDINATES, &global));
129 PetscCall(PicurvAssertIntEqual(cell * node * node, global,
130 "node-centered loses only the periodic direction"));
131 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
132
133 /* Fully periodic: every node-like direction collapses to the cell extent. */
134 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user,
136 PETSC_TRUE, PETSC_TRUE, PETSC_TRUE));
137 PetscCall(PlanCount(user, FIELD_ID_COORDINATES, &global));
138 PetscCall(PicurvAssertIntEqual(cell * cell * cell, global,
139 "fully periodic node-centered domain has no duplicate planes"));
140 PetscCall(PlanCount(user, FIELD_ID_CSI, &global));
141 PetscCall(PicurvAssertIntEqual(cell * cell * cell, global,
142 "fully periodic I-face domain has no duplicate planes"));
143 PetscCall(PlanCount(user, FIELD_ID_P, &global));
144 PetscCall(PicurvAssertIntEqual(cell * cell * cell, global,
145 "fully periodic cell-centered domain is unchanged"));
146 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
147 PetscFunctionReturn(0);
148}
149
150/** @brief Component-staggered fields cannot share one pointwise domain and are rejected. */
151static PetscErrorCode TestComponentStaggeredRejected(void)
152{
153 SimCtx *simCtx = NULL;
154 UserCtx *user = NULL;
156 PetscErrorCode staggered_ierr = 0;
157 PetscErrorCode classify_ierr = 0;
158 PetscBool node_like = PETSC_FALSE;
159
160 PetscFunctionBeginUser;
162
163 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
165 PetscCall(PetscPopErrorHandler());
166 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONGSTATE, staggered_ierr,
167 "Ucont is component-staggered and must not resolve a pointwise domain"));
168
169 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
171 PetscCall(PetscPopErrorHandler());
172 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONGSTATE, classify_ierr,
173 "component-staggered layout has no single per-dimension classification"));
174
175 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
176 classify_ierr = PicurvLayoutDimensionIsNodeLike(FIELD_LAYOUT_CELL_CENTERED, 3, &node_like);
177 PetscCall(PetscPopErrorHandler());
178 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_OUTOFRANGE, classify_ierr,
179 "dimension index outside 0..2 must be rejected"));
180
181 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
182 PetscFunctionReturn(0);
183}
184
185/** @brief The fluid mask admits fluid cells and rejects blanked ones at the documented threshold. */
186static PetscErrorCode TestFluidMaskThreshold(void)
187{
188 SimCtx *simCtx = NULL;
189 UserCtx *user = NULL;
191
192 PetscFunctionBeginUser;
195
196 PetscCall(PicurvAssertBool(SpatialTargetPlanMaskAllows(&plan, 0.0),
197 "open fluid must pass the fluid mask"));
198 PetscCall(PicurvAssertBool(SpatialTargetPlanMaskAllows(&plan, 0.05),
199 "values below the threshold must pass the fluid mask"));
200 PetscCall(PicurvAssertBool((PetscBool)!SpatialTargetPlanMaskAllows(&plan, 1.0),
201 "solid cells must fail the fluid mask"));
203 "the threshold itself is excluded, matching the existing Nvert < 0.1 rule"));
204 PetscCall(PicurvAssertBool((PetscBool)!SpatialTargetPlanMaskAllows(NULL, 0.0),
205 "a null plan must not admit points"));
206
207 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
208 PetscFunctionReturn(0);
209}
210
211/**
212 * @brief The cell-centered span must agree with the existing owned-cell helper.
213 *
214 * `GetOwnedCellRange` reports cells by their origin node index, while field
215 * storage uses the solver's shifted convention where the cell with origin `j`
216 * lives at array index `j + 1`. The two therefore describe the same cells in
217 * different index spaces. This pins that relationship so the target plan and the
218 * established helper cannot drift apart.
219 */
220static PetscErrorCode TestCellSpanAgreesWithOwnedCellRange(void)
221{
222 SimCtx *simCtx = NULL;
223 UserCtx *user = NULL;
225
226 PetscFunctionBeginUser;
229
230 for (PetscInt dim = 0; dim < 3; ++dim) {
231 PetscInt origin_start = 0;
232 PetscInt origin_count = 0;
233
234 PetscCall(GetOwnedCellRange(&user->info, dim, &origin_start, &origin_count));
235 /* Shift from origin-node indexing into field-storage indexing. */
236 PetscCall(PicurvAssertIntEqual(origin_start + 1, plan.start[dim],
237 "cell span start must be the owned-cell origin shifted by one"));
238 PetscCall(PicurvAssertIntEqual(origin_start + origin_count + 1, plan.end[dim],
239 "cell span end must be the owned-cell extent shifted by one"));
240 }
241
242 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
243 PetscFunctionReturn(0);
244}
245
246/**
247 * @brief Entry point for the spatial target suite.
248 */
249int main(int argc, char **argv)
250{
251 PetscErrorCode ierr;
252 const PicurvTestCase cases[] = {
253 {"cell-centered-excludes-boundary-and-dummy", TestCellCenteredExcludesBoundaryAndDummy},
254 {"face-layouts-node-like-in-own-direction", TestFaceLayoutsAreNodeLikeInOwnDirection},
255 {"periodic-directions-drop-duplicate-plane", TestPeriodicDirectionsDropDuplicatePlane},
256 {"component-staggered-rejected", TestComponentStaggeredRejected},
257 {"fluid-mask-threshold", TestFluidMaskThreshold},
258 {"cell-span-agrees-with-owned-cell-range", TestCellSpanAgreesWithOwnedCellRange},
259 };
260
261 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv spatial target tests");
262 if (ierr) {
263 return (int)ierr;
264 }
265
266 ierr = PicurvRunTests("unit-statistics-target", cases, sizeof(cases) / sizeof(cases[0]));
267 if (ierr) {
268 PetscFinalize();
269 return (int)ierr;
270 }
271
272 ierr = PetscFinalize();
273 return (int)ierr;
274}
FieldLayout layout
@ FIELD_LAYOUT_CELL_CENTERED
@ FIELD_LAYOUT_COMPONENT_STAGGERED
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_CSI
@ FIELD_ID_UCAT
@ FIELD_ID_COORDINATES
@ FIELD_ID_UCONT
@ FIELD_ID_ETA
@ FIELD_ID_P
@ FIELD_ID_ZET
PetscErrorCode GetOwnedCellRange(const DMDALocalInfo *info_nodes, PetscInt dim, PetscInt *xs_cell_global_out, PetscInt *xm_cell_local_out)
Determines the global starting index and number of CELLS owned by the current processor in a specifie...
Definition setup.c:2976
Spatial target resolution for the field-statistics pipeline.
#define PICURV_STATISTICS_FLUID_THRESHOLD
Threshold below which a cell counts as fluid for the default mask.
PetscBool SpatialTargetPlanMaskAllows(const SpatialTargetPlan *plan, PetscReal nvert_value)
Reports whether a point passes the plan's mask.
PicurvTargetKind kind
Spatial mapping; always pointwise.
@ PICURV_TARGET_POINTWISE
@ PICURV_STATISTICS_MASK_FLUID
PetscInt end[3]
Exclusive end per dimension (i, j, k).
PetscInt start[3]
Inclusive start per dimension (i, j, k).
PetscErrorCode SpatialTargetPlanLocalPointCount(const SpatialTargetPlan *plan, PetscInt *count)
Counts the points this rank contributes.
PetscErrorCode SpatialTargetPlanGlobalPointCount(const SpatialTargetPlan *plan, MPI_Comm comm, PetscInt *count)
Counts the points contributed across a communicator.
PetscErrorCode SpatialTargetPlanCreate(UserCtx *user, FieldId field_id, PicurvStatisticsMask mask, SpatialTargetPlan *plan)
Resolves the iteration domain for one field on one block.
PetscErrorCode PicurvLayoutDimensionIsNodeLike(FieldLayout layout, PetscInt dim, PetscBool *node_like)
Reports whether a layout is node-like in one dimension.
const FieldDescriptor * descriptor
Catalog metadata for the targeted field.
Resolved iteration domain for one field on one block.
static PetscErrorCode TestComponentStaggeredRejected(void)
Component-staggered fields cannot share one pointwise domain and are rejected.
static PetscErrorCode PlanCount(UserCtx *user, FieldId field_id, PetscInt *count)
Builds a plan and returns its global point count.
static PetscErrorCode TestPeriodicDirectionsDropDuplicatePlane(void)
Periodic directions must drop the wrapped duplicate plane.
int main(int argc, char **argv)
Entry point for the spatial target suite.
static PetscErrorCode TestCellCenteredExcludesBoundaryAndDummy(void)
Cell-centered fields must exclude the dummy slot and the extra high slot.
static PetscErrorCode TestFaceLayoutsAreNodeLikeInOwnDirection(void)
Each face family is node-like in exactly its own direction.
static PetscErrorCode TestFluidMaskThreshold(void)
The fluid mask admits fluid cells and rejects blanked ones at the documented threshold.
#define TARGET_CELL_EXTENT
#define TARGET_NODE_EXTENT
#define TARGET_TEST_N
static PetscErrorCode TestCellSpanAgreesWithOwnedCellRange(void)
The cell-centered span must agree with the existing owned-cell helper.
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 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 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.
DMDALocalInfo info
Definition variables.h:1085
PetscMPIInt size
Definition variables.h:870
The master context for the entire simulation.
Definition variables.h:866
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1073
A generic C-style linked list node for integers.
Definition variables.h:470