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

C unit tests for per-window accumulator storage and pointwise application. More...

#include "test_support.h"
#include "statistics_accumulator.h"
#include "statistics_moments.h"
#include "field_catalog.h"
#include "statistics_window.h"
#include "statistics_target.h"
Include dependency graph for test_statistics_accumulator.c:

Go to the source code of this file.

Macros

#define ACC_N   6 /* fixture size; cell domain is 5x5x5 */
 
#define ACC_CELLS   125
 

Functions

static PicurvWindowDefinition AccDefinition (PetscBool want_second)
 Builds a definition requesting Ucat and P with second moments.
 
static PetscErrorCode SetUniform (UserCtx *user, PetscReal x, PetscReal y, PetscReal z, PetscReal p)
 Sets Ucat to a uniform vector and P to a uniform scalar.
 
static PetscErrorCode ReadScalarAt (UserCtx *user, Vec v, PetscInt i, PetscInt j, PetscInt k, PetscReal *out)
 Reads one interior point of a scalar accumulator vector.
 
static PetscErrorCode ReadComponentAt (UserCtx *user, Vec v, PetscInt components, PetscInt component, PetscInt i, PetscInt j, PetscInt k, PetscReal *out)
 Reads one component of a multi-component accumulator vector at an interior point.
 
static PetscErrorCode TestComponentCounts (void)
 Component counts follow the documented product shapes.
 
static PetscErrorCode TestStorageShape (void)
 Storage allocates one mean per field and the right number of product components.
 
static PetscErrorCode TestKnownAccumulationAcrossField (void)
 Three accepted states reproduce the analytically known moments at every point.
 
static PetscErrorCode TestValidFractionRange (void)
 The valid-fraction range reports mask coverage, including a never-valid point.
 
static PetscErrorCode TestValidFractionDetectsNeverValidPoint (void)
 A point blanked from the start is reported as never valid.
 
static PetscErrorCode TestCovarianceAccumulation (void)
 A vector-scalar covariance reproduces its analytically known components.
 
static PetscErrorCode TestScalarSelfCovarianceMatchesSecondMoment (void)
 A field's covariance with itself reduces exactly to its own second moment.
 
static PetscErrorCode TestCovarianceRequiresFieldMembership (void)
 A covariance member missing from the field list is rejected, not silently skipped.
 
static PetscErrorCode TestDerivedQuantities (void)
 Derived quantities reproduce the analytic values the accumulated state implies.
 
static PetscErrorCode TestSpatialMeanExcludesUnsampledPoints (void)
 The spatial mean divides by sampled points, not by the whole vector.
 
static PetscErrorCode TestPayloadEnumeration (void)
 Payload enumeration covers every vector exactly once with stable names.
 
static PetscErrorCode TestWeightedAccumulation (void)
 Unequal weights reproduce the weighted result the kernels define.
 
static PetscErrorCode TestMaskedPointsAreExcluded (void)
 Masked points accumulate nothing, and remain distinguishable from unsampled ones.
 
static PetscErrorCode TestConstantFieldZeroProduct (void)
 A constant field yields exactly zero product at every point.
 
static PetscErrorCode TestRunloopDriverAppliesScheduledStates (void)
 The runloop entry point applies exactly the states the schedule accepts.
 
int main (int argc, char **argv)
 Entry point for the accumulator suite.
 

Detailed Description

C unit tests for per-window accumulator storage and pointwise application.

The field series reuses the values verified analytically by the moment-kernel suite, so a failure here isolates the field-to-accumulator wiring rather than the numerics: three samples of (1,2,3), (3,6,5), (5,4,7) have means (3,4,5) and symmetric centered products (8,4,8,8,4,8) in (xx,xy,xz,yy,yz,zz) order.

Definition in file test_statistics_accumulator.c.

Macro Definition Documentation

◆ ACC_N

#define ACC_N   6 /* fixture size; cell domain is 5x5x5 */

Definition at line 19 of file test_statistics_accumulator.c.

◆ ACC_CELLS

#define ACC_CELLS   125

Definition at line 20 of file test_statistics_accumulator.c.

Function Documentation

◆ AccDefinition()

static PicurvWindowDefinition AccDefinition ( PetscBool  want_second)
static

Builds a definition requesting Ucat and P with second moments.

Definition at line 23 of file test_statistics_accumulator.c.

24{
26 memset(&d, 0, sizeof(d));
27 strncpy(d.name, "acc", PICURV_WINDOW_NAME_LENGTH - 1);
30 d.step_cadence = 1;
31 d.field_count = 2;
32 d.fields[0].field_id = FIELD_ID_UCAT; d.fields[0].want_second = want_second;
33 d.fields[1].field_id = FIELD_ID_P; d.fields[1].want_second = want_second;
34 return d;
35}
@ FIELD_ID_UCAT
@ FIELD_ID_P
PicurvWindowFieldRequest fields[16]
PicurvCadenceKind cadence_kind
PetscInt step_cadence
Used when cadence_kind is step; must be positive.
#define PICURV_WINDOW_NAME_LENGTH
Maximum stored length of a window name, including the terminator.
PetscBool want_second
Also keep the centered second moment.
PetscInt field_id
Catalogued Eulerian field identity.
@ PICURV_WEIGHTING_SAMPLE
Equal weight per accepted state.
@ PICURV_CADENCE_STEP
Every n completed steps from activation.
The scientifically immutable definition of one window.
Here is the caller graph for this function:

◆ SetUniform()

static PetscErrorCode SetUniform ( UserCtx user,
PetscReal  x,
PetscReal  y,
PetscReal  z,
PetscReal  p 
)
static

Sets Ucat to a uniform vector and P to a uniform scalar.

Definition at line 38 of file test_statistics_accumulator.c.

39{
40 Cmpnts ***ucat = NULL;
41 PetscReal ***pp = NULL;
42 const DMDALocalInfo info = user->info;
43
44 PetscFunctionBeginUser;
45 PetscCall(DMDAVecGetArray(user->fda, user->Ucat, &ucat));
46 PetscCall(DMDAVecGetArray(user->da, user->P, &pp));
47 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k)
48 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j)
49 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
50 ucat[k][j][i].x = x; ucat[k][j][i].y = y; ucat[k][j][i].z = z;
51 pp[k][j][i] = p;
52 }
53 PetscCall(DMDAVecRestoreArray(user->da, user->P, &pp));
54 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucat, &ucat));
55 PetscFunctionReturn(0);
56}
PetscScalar x
Definition variables.h:103
PetscScalar z
Definition variables.h:103
Vec Ucat
Definition variables.h:929
DMDALocalInfo info
Definition variables.h:908
PetscScalar y
Definition variables.h:103
A 3D point or vector with PetscScalar components.
Definition variables.h:102
Here is the caller graph for this function:

◆ ReadScalarAt()

static PetscErrorCode ReadScalarAt ( UserCtx user,
Vec  v,
PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscReal *  out 
)
static

Reads one interior point of a scalar accumulator vector.

Definition at line 59 of file test_statistics_accumulator.c.

60{
61 PetscReal ***a = NULL;
62 PetscFunctionBeginUser;
63 PetscCall(DMDAVecGetArrayRead(user->da, v, &a));
64 *out = a[k][j][i];
65 PetscCall(DMDAVecRestoreArrayRead(user->da, v, &a));
66 PetscFunctionReturn(0);
67}
Here is the caller graph for this function:

◆ ReadComponentAt()

static PetscErrorCode ReadComponentAt ( UserCtx user,
Vec  v,
PetscInt  components,
PetscInt  component,
PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscReal *  out 
)
static

Reads one component of a multi-component accumulator vector at an interior point.

Definition at line 70 of file test_statistics_accumulator.c.

72{
73 PetscScalar ****a = NULL;
74 DM dm = NULL;
75 PetscFunctionBeginUser;
76 PetscCall(PicurvStatisticsComponentDM(user, components, &dm));
77 PetscCall(DMDAVecGetArrayDOFRead(dm, v, &a));
78 *out = a[k][j][i][component];
79 PetscCall(DMDAVecRestoreArrayDOFRead(dm, v, &a));
80 PetscFunctionReturn(0);
81}
PetscErrorCode PicurvStatisticsComponentDM(UserCtx *user, PetscInt components, DM *dm)
Resolves the DM carrying a given number of accumulator components.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestComponentCounts()

static PetscErrorCode TestComponentCounts ( void  )
static

Component counts follow the documented product shapes.

Definition at line 84 of file test_statistics_accumulator.c.

85{
86 PetscInt n = 0;
87 PetscErrorCode bad = 0;
88
89 PetscFunctionBeginUser;
90 PetscCall(PicurvProductComponentCount(1, &n));
91 PetscCall(PicurvAssertIntEqual(1, n, "a scalar self-product has one component"));
92 PetscCall(PicurvProductComponentCount(3, &n));
93 PetscCall(PicurvAssertIntEqual(6, n, "a three-vector self-product has six symmetric components"));
94 PetscCall(PicurvCovarianceComponentCount(3, 1, &n));
95 PetscCall(PicurvAssertIntEqual(3, n, "a vector-scalar covariance has three components"));
96 PetscCall(PicurvCovarianceComponentCount(1, 1, &n));
97 PetscCall(PicurvAssertIntEqual(1, n, "a scalar-scalar covariance has one component"));
98
99 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
100 bad = PicurvCovarianceComponentCount(3, 3, &n);
101 PetscCall(PetscPopErrorHandler());
102 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_OUTOFRANGE, bad,
103 "vector-vector cross products are an explicit non-goal"));
104 PetscFunctionReturn(0);
105}
PetscErrorCode PicurvProductComponentCount(PetscInt dof, PetscInt *count)
Reports how many symmetric product components a field's second moment needs.
PetscErrorCode PicurvCovarianceComponentCount(PetscInt dof_a, PetscInt dof_b, PetscInt *count)
Reports how many components a covariance between two fields needs.
PetscErrorCode PicurvAssertIntEqual(PetscInt expected, PetscInt actual, const char *context)
Asserts that two integer values are equal.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestStorageShape()

static PetscErrorCode TestStorageShape ( void  )
static

Storage allocates one mean per field and the right number of product components.

Definition at line 108 of file test_statistics_accumulator.c.

109{
110 SimCtx *simCtx = NULL;
111 UserCtx *user = NULL;
112 PicurvWindowStorage storage;
114 PetscInt bs = 0;
115
116 PetscFunctionBeginUser;
117 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
118 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
119
120 PetscCall(PicurvAssertIntEqual(2, storage.field_count, "two fields requested"));
121
122 /* Each product is one vector carrying all of its components, so the symmetric
123 * second-order tensor stays a single object rather than six scalars. */
124 PetscCall(VecGetBlockSize(storage.m2[0], &bs));
125 PetscCall(PicurvAssertIntEqual(6, bs, "the Ucat product is one six-component tensor"));
126 PetscCall(VecGetBlockSize(storage.m2[1], &bs));
127 PetscCall(PicurvAssertIntEqual(1, bs, "the P product is a single scalar"));
128
129 /* Means inherit their source field's layout. */
130 PetscCall(VecGetBlockSize(storage.mean[0], &bs));
131 PetscCall(PicurvAssertIntEqual(3, bs, "the Ucat mean is a three-vector"));
132 PetscCall(VecGetBlockSize(storage.mean[1], &bs));
133 PetscCall(PicurvAssertIntEqual(1, bs, "the P mean is a scalar"));
134
135 PetscCall(PicurvWindowStorageDestroy(&storage));
136 PetscCall(PicurvAssertIntEqual(0, storage.field_count, "destroy zeroes the storage"));
137 PetscCall(PicurvAssertBool((PetscBool)(storage.mean == NULL), "destroy releases the mean array"));
138 /* Destroying zeroed storage is safe. */
139 PetscCall(PicurvWindowStorageDestroy(&storage));
140
141 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
142 PetscFunctionReturn(0);
143}
PetscInt field_count
Fields accumulated.
Vec * mean
One per field, matching that field's layout.
PetscErrorCode PicurvWindowStorageCreate(UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage)
Allocates the accumulator state one window owns on one block.
Vec * m2
One per field; NULL when no second moment was requested.
PetscErrorCode PicurvWindowStorageDestroy(PicurvWindowStorage *storage)
Releases accumulator state previously created for one window.
Independent accumulator state for one window on one block.
static PicurvWindowDefinition AccDefinition(PetscBool want_second)
Builds a definition requesting Ucat and P with second moments.
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.
The master context for the entire simulation.
Definition variables.h:695
User-defined context containing data specific to a single computational grid level.
Definition variables.h:896
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestKnownAccumulationAcrossField()

static PetscErrorCode TestKnownAccumulationAcrossField ( void  )
static

Three accepted states reproduce the analytically known moments at every point.

Definition at line 146 of file test_statistics_accumulator.c.

147{
148 SimCtx *simCtx = NULL;
149 UserCtx *user = NULL;
150 PicurvWindowStorage storage;
152 const PetscReal series[3][3] = {{1.0, 2.0, 3.0}, {3.0, 6.0, 5.0}, {5.0, 4.0, 7.0}};
153 const PetscReal scalars[3] = {1.0, 2.0, 6.0};
154 const PetscReal expected_product[6] = {8.0, 4.0, 8.0, 8.0, 4.0, 8.0};
155 const char *labels[6] = {"xx", "xy", "xz", "yy", "yz", "zz"};
156 Cmpnts ***mean_vec = NULL;
157 PetscReal value = 0.0;
158 char context[128];
159
160 PetscFunctionBeginUser;
161 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
162 PetscCall(VecSet(user->Nvert, 0.0));
163 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
164
165 for (PetscInt s = 0; s < 3; ++s) {
166 PetscCall(SetUniform(user, series[s][0], series[s][1], series[s][2], scalars[s]));
167 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
168 }
169
170 /* Occupancy: every fluid point saw all three states. */
171 PetscCall(ReadScalarAt(user, storage.count, 2, 2, 2, &value));
172 PetscCall(PicurvAssertRealNear(3.0, value, 1.0e-12, "per-point sample count"));
173 PetscCall(ReadScalarAt(user, storage.weight, 2, 2, 2, &value));
174 PetscCall(PicurvAssertRealNear(3.0, value, 1.0e-12, "per-point total weight"));
175 PetscCall(ReadScalarAt(user, storage.weight_sq, 2, 2, 2, &value));
176 PetscCall(PicurvAssertRealNear(3.0, value, 1.0e-12, "per-point squared-weight sum"));
177
178 /* Vector mean. */
179 PetscCall(DMDAVecGetArrayRead(user->fda, storage.mean[0], &mean_vec));
180 PetscCall(PicurvAssertRealNear(3.0, mean_vec[2][2][2].x, 1.0e-12, "Ucat mean x"));
181 PetscCall(PicurvAssertRealNear(4.0, mean_vec[2][2][2].y, 1.0e-12, "Ucat mean y"));
182 PetscCall(PicurvAssertRealNear(5.0, mean_vec[2][2][2].z, 1.0e-12, "Ucat mean z"));
183 PetscCall(DMDAVecRestoreArrayRead(user->fda, storage.mean[0], &mean_vec));
184
185 /* All six symmetric centered products, in fixed catalog order. */
186 for (PetscInt c = 0; c < 6; ++c) {
187 PetscCall(ReadComponentAt(user, storage.m2[0], 6, c, 2, 2, 2, &value));
188 PetscCall(PetscSNPrintf(context, sizeof(context), "Ucat centered product %s", labels[c]));
189 PetscCall(PicurvAssertRealNear(expected_product[c], value, 1.0e-11, context));
190 }
191
192 /* Scalar mean and second moment. */
193 PetscCall(ReadScalarAt(user, storage.mean[1], 2, 2, 2, &value));
194 PetscCall(PicurvAssertRealNear(3.0, value, 1.0e-12, "P mean"));
195 PetscCall(ReadScalarAt(user, storage.m2[1], 2, 2, 2, &value));
196 PetscCall(PicurvAssertRealNear(14.0, value, 1.0e-11, "P centered second moment"));
197
198 PetscCall(PicurvWindowStorageDestroy(&storage));
199 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
200 PetscFunctionReturn(0);
201}
Vec weight
Per-point valid weight.
Vec weight_sq
Per-point squared-weight sum.
Vec count
Per-point accepted sample count.
PetscErrorCode PicurvWindowAccumulate(UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage, PetscReal weight)
Applies one accepted completed state to a window's accumulators.
static PetscErrorCode ReadComponentAt(UserCtx *user, Vec v, PetscInt components, PetscInt component, PetscInt i, PetscInt j, PetscInt k, PetscReal *out)
Reads one component of a multi-component accumulator vector at an interior point.
static PetscErrorCode SetUniform(UserCtx *user, PetscReal x, PetscReal y, PetscReal z, PetscReal p)
Sets Ucat to a uniform vector and P to a uniform scalar.
static PetscErrorCode ReadScalarAt(UserCtx *user, Vec v, PetscInt i, PetscInt j, PetscInt k, PetscReal *out)
Reads one interior point of a scalar accumulator vector.
PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
Vec Nvert
Definition variables.h:929
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestValidFractionRange()

static PetscErrorCode TestValidFractionRange ( void  )
static

The valid-fraction range reports mask coverage, including a never-valid point.

This is the mask-health indicator the console snapshot prints, and it is the only signal that distinguishes a mean built from every state from one built from none. A blanked point keeps a zero mean that looks like a legitimate value, so the range has to surface it.

Definition at line 211 of file test_statistics_accumulator.c.

212{
213 SimCtx *simCtx = NULL;
214 UserCtx *user = NULL;
215 PicurvWindowStorage storage;
216 PicurvWindowDefinition d = AccDefinition(PETSC_FALSE);
217 PetscReal ***nvert = NULL;
218 PetscReal lowest = 0.0, highest = 0.0;
219
220 PetscFunctionBeginUser;
221 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
222 PetscCall(VecSet(user->Nvert, 0.0));
223 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
224 PetscCall(SetUniform(user, 1.0, 2.0, 3.0, 7.0));
225
226 /* Before any sample the range is the documented degenerate, not a division. */
227 PetscCall(PicurvWindowValidFractionRange(user, &d, &storage, 0, &lowest, &highest));
228 PetscCall(PicurvAssertRealNear(1.0, lowest, 1.0e-12, "an unsampled window reports full coverage"));
229 PetscCall(PicurvAssertRealNear(0.0, highest, 1.0e-12, "an unsampled window reports no maximum"));
230
231 /* An unobstructed domain: every targeted point saw every state. */
232 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
233 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
234 PetscCall(PicurvWindowValidFractionRange(user, &d, &storage, 2, &lowest, &highest));
235 PetscCall(PicurvAssertRealNear(1.0, lowest, 1.0e-12, "a clear domain is fully covered"));
236 PetscCall(PicurvAssertRealNear(1.0, highest, 1.0e-12, "a clear domain has no over-counted point"));
237
238 /* Blank one interior cell and take a third state: that point now trails. */
239 PetscCall(DMDAVecGetArray(user->da, user->Nvert, &nvert));
240 nvert[3][3][3] = 1.0;
241 PetscCall(DMDAVecRestoreArray(user->da, user->Nvert, &nvert));
242 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
243 PetscCall(PicurvWindowValidFractionRange(user, &d, &storage, 3, &lowest, &highest));
244 PetscCall(PicurvAssertRealNear(2.0 / 3.0, lowest, 1.0e-12,
245 "a point blanked for one state trails the rest"));
246 PetscCall(PicurvAssertRealNear(1.0, highest, 1.0e-12, "unobstructed points stay fully covered"));
247
248 PetscCall(PicurvWindowStorageDestroy(&storage));
249 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
250 PetscFunctionReturn(0);
251}
PetscErrorCode PicurvWindowValidFractionRange(UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, PetscInt sample_count, PetscReal *minimum, PetscReal *maximum)
Reports the range of per-point valid fraction across a window's domain.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestValidFractionDetectsNeverValidPoint()

static PetscErrorCode TestValidFractionDetectsNeverValidPoint ( void  )
static

A point blanked from the start is reported as never valid.

Definition at line 256 of file test_statistics_accumulator.c.

257{
258 SimCtx *simCtx = NULL;
259 UserCtx *user = NULL;
260 PicurvWindowStorage storage;
261 PicurvWindowDefinition d = AccDefinition(PETSC_FALSE);
262 PetscReal ***nvert = NULL;
263 PetscReal lowest = 1.0, highest = 0.0;
264
265 PetscFunctionBeginUser;
266 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
267 PetscCall(VecSet(user->Nvert, 0.0));
268 PetscCall(DMDAVecGetArray(user->da, user->Nvert, &nvert));
269 nvert[3][3][3] = 1.0;
270 PetscCall(DMDAVecRestoreArray(user->da, user->Nvert, &nvert));
271
272 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
273 PetscCall(SetUniform(user, 1.0, 2.0, 3.0, 7.0));
274 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
275 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
276
277 PetscCall(PicurvWindowValidFractionRange(user, &d, &storage, 2, &lowest, &highest));
278 PetscCall(PicurvAssertRealNear(0.0, lowest, 1.0e-12,
279 "a permanently blanked point reports zero coverage"));
280 PetscCall(PicurvAssertRealNear(1.0, highest, 1.0e-12, "the rest of the domain is unaffected"));
281
282 PetscCall(PicurvWindowStorageDestroy(&storage));
283 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
284 PetscFunctionReturn(0);
285}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestCovarianceAccumulation()

static PetscErrorCode TestCovarianceAccumulation ( void  )
static

A vector-scalar covariance reproduces its analytically known components.

Definition at line 288 of file test_statistics_accumulator.c.

289{
290 SimCtx *simCtx = NULL;
291 UserCtx *user = NULL;
292 PicurvWindowStorage storage;
294 const PetscReal series[3][3] = {{1.0, 2.0, 3.0}, {3.0, 6.0, 5.0}, {5.0, 4.0, 7.0}};
295 const PetscReal scalars[3] = {1.0, 2.0, 6.0};
296 /* Means are (3,4,5) and 3, so the centered sums are
297 * x: (-2)(-2)+(0)(-1)+(2)(3) = 10, y: (-2)(-2)+(2)(-1)+(0)(3) = 2,
298 * z: (-2)(-2)+(0)(-1)+(2)(3) = 10. */
299 const PetscReal expected[3] = {10.0, 2.0, 10.0};
300 const char *labels[3] = {"x", "y", "z"};
301 PetscReal value = 0.0;
302 char context[128];
303
304 PetscFunctionBeginUser;
305 d.covariance_count = 1;
308
309 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
310 PetscCall(VecSet(user->Nvert, 0.0));
311 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
312 {
313 PetscInt bs = 0;
314 PetscCall(VecGetBlockSize(storage.cm[0], &bs));
315 PetscCall(PicurvAssertIntEqual(3, bs, "a vector-scalar pair is one three-component vector"));
316 }
317
318 for (PetscInt s = 0; s < 3; ++s) {
319 PetscCall(SetUniform(user, series[s][0], series[s][1], series[s][2], scalars[s]));
320 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
321 }
322
323 for (PetscInt c = 0; c < 3; ++c) {
324 PetscCall(ReadComponentAt(user, storage.cm[0], 3, c, 2, 2, 2, &value));
325 PetscCall(PetscSNPrintf(context, sizeof(context), "Ucat-P co-moment %s", labels[c]));
326 PetscCall(PicurvAssertRealNear(expected[c], value, 1.0e-11, context));
327 }
328
329 /* The co-moment pass must not disturb the moments computed alongside it. */
330 PetscCall(ReadScalarAt(user, storage.mean[1], 2, 2, 2, &value));
331 PetscCall(PicurvAssertRealNear(3.0, value, 1.0e-12, "P mean is unaffected by the covariance pass"));
332 PetscCall(ReadComponentAt(user, storage.m2[0], 6, 0, 2, 2, 2, &value));
333 PetscCall(PicurvAssertRealNear(8.0, value, 1.0e-11, "Ucat xx product is unaffected by the covariance pass"));
334
335 PetscCall(PicurvWindowStorageDestroy(&storage));
336 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
337 PetscFunctionReturn(0);
338}
Vec * cm
One per covariance pair.
PetscInt first
First member; must also appear in the field list.
PetscInt second
Second member; must also appear in the field list.
PicurvWindowCovarianceRequest covariances[16]
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestScalarSelfCovarianceMatchesSecondMoment()

static PetscErrorCode TestScalarSelfCovarianceMatchesSecondMoment ( void  )
static

A field's covariance with itself reduces exactly to its own second moment.

Definition at line 341 of file test_statistics_accumulator.c.

342{
343 SimCtx *simCtx = NULL;
344 UserCtx *user = NULL;
345 PicurvWindowStorage storage;
347 const PetscReal scalars[3] = {1.0, 2.0, 6.0};
348 PetscReal co_moment = 0.0;
349 PetscReal second_moment = 0.0;
350
351 PetscFunctionBeginUser;
352 /* Pairing P with itself makes the co-moment pass and the moment pass compute the
353 * same quantity by different routes, so any drift between them shows up here
354 * rather than only in a cross-field result no closed form covers. */
355 d.covariance_count = 1;
358
359 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
360 PetscCall(VecSet(user->Nvert, 0.0));
361 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
362
363 for (PetscInt s = 0; s < 3; ++s) {
364 PetscCall(SetUniform(user, 0.0, 0.0, 0.0, scalars[s]));
365 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
366 }
367
368 PetscCall(ReadScalarAt(user, storage.cm[0], 2, 2, 2, &co_moment));
369 PetscCall(ReadScalarAt(user, storage.m2[1], 2, 2, 2, &second_moment));
370 PetscCall(PicurvAssertRealNear(14.0, co_moment, 1.0e-11, "P self-covariance equals its centered sum"));
371 PetscCall(PicurvAssertBool((PetscBool)(co_moment == second_moment),
372 "self-covariance and second moment agree bit for bit"));
373
374 PetscCall(PicurvWindowStorageDestroy(&storage));
375 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
376 PetscFunctionReturn(0);
377}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestCovarianceRequiresFieldMembership()

static PetscErrorCode TestCovarianceRequiresFieldMembership ( void  )
static

A covariance member missing from the field list is rejected, not silently skipped.

Definition at line 380 of file test_statistics_accumulator.c.

381{
382 SimCtx *simCtx = NULL;
383 UserCtx *user = NULL;
384 PicurvWindowStorage storage;
385 PicurvWindowDefinition d = AccDefinition(PETSC_FALSE);
386 PetscErrorCode bad = 0;
387
388 PetscFunctionBeginUser;
389 /* Nvert is catalogued but is not in the window's field list, so no running mean
390 * exists for it and the co-moment update has nothing to center against. */
391 d.covariance_count = 1;
394
395 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
396 PetscCall(VecSet(user->Nvert, 0.0));
397 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
398 PetscCall(SetUniform(user, 1.0, 2.0, 3.0, 4.0));
399
400 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
401 bad = PicurvWindowAccumulate(user, &d, &storage, 1.0);
402 PetscCall(PetscPopErrorHandler());
403 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONGSTATE, bad,
404 "a covariance member outside the field list is rejected"));
405
406 PetscCall(PicurvWindowStorageDestroy(&storage));
407 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
408 PetscFunctionReturn(0);
409}
@ FIELD_ID_NVERT
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestDerivedQuantities()

static PetscErrorCode TestDerivedQuantities ( void  )
static

Derived quantities reproduce the analytic values the accumulated state implies.

The series is the one the moment suite verifies by hand, so a failure here isolates the normalization and component selection rather than the accumulation: with three unit-weight samples, R_ij = C_ij / 3, and the diagonal of the Ucat tensor is 8/3 in each direction, giving TKE = 4 and an RMS of sqrt(8/3) per component.

Definition at line 419 of file test_statistics_accumulator.c.

420{
421 SimCtx *simCtx = NULL;
422 UserCtx *user = NULL;
423 PicurvWindowStorage storage;
425 const PetscReal series[3][3] = {{1.0, 2.0, 3.0}, {3.0, 6.0, 5.0}, {5.0, 4.0, 7.0}};
426 const PetscReal scalars[3] = {1.0, 2.0, 6.0};
427 Vec scalar_target = NULL, vector_target = NULL;
428 PetscInt count = 0;
429 PetscReal value = 0.0;
430
431 PetscFunctionBeginUser;
432 d.covariance_count = 1;
435
436 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
437 PetscCall(VecSet(user->Nvert, 0.0));
438 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
439 for (PetscInt s = 0; s < 3; ++s) {
440 PetscCall(SetUniform(user, series[s][0], series[s][1], series[s][2], scalars[s]));
441 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
442 }
443 PetscCall(DMCreateGlobalVector(user->da, &scalar_target));
444 PetscCall(DMCreateGlobalVector(user->fda, &vector_target));
445
446 /* Requesting every output resolves each kind against what the window carries. */
447 PetscCall(PicurvWindowDerivedCount(&d, &storage, "mean,reynolds_stress,rms,tke,flux", &count));
448 /* 2 means + (6 Ucat + 1 P) stresses + (3 Ucat + 1 P) RMS + 1 TKE + 1 flux. */
449 PetscCall(PicurvAssertIntEqual(15, count, "every requested output is enumerated"));
450
451 for (PetscInt index = 0; index < count; ++index) {
452 PicurvDerivedField field;
453 PetscBool is_tke = PETSC_FALSE, is_rxx = PETSC_FALSE, is_rmsx = PETSC_FALSE;
454 PetscBool is_flux = PETSC_FALSE, is_pvar = PETSC_FALSE;
455
456 PetscCall(PicurvWindowDerive(user, &d, &storage, "mean,reynolds_stress,rms,tke,flux",
457 index, scalar_target, vector_target, &field));
458 PetscCall(PetscStrcmp(field.name, "acc_Ucat_tke", &is_tke));
459 PetscCall(PetscStrcmp(field.name, "acc_Ucat_R_xx", &is_rxx));
460 PetscCall(PetscStrcmp(field.name, "acc_Ucat_rmsx", &is_rmsx));
461 PetscCall(PetscStrcmp(field.name, "acc_Ucat_P_flux", &is_flux));
462 PetscCall(PetscStrcmp(field.name, "acc_P_variance", &is_pvar));
463
464 if (is_rxx) {
465 PetscCall(ReadScalarAt(user, scalar_target, 2, 2, 2, &value));
466 PetscCall(PicurvAssertRealNear(8.0 / 3.0, value, 1.0e-11, "R_xx is C_xx over the weight"));
467 } else if (is_rmsx) {
468 PetscCall(ReadScalarAt(user, scalar_target, 2, 2, 2, &value));
469 PetscCall(PicurvAssertRealNear(PetscSqrtReal(8.0 / 3.0), value, 1.0e-11,
470 "RMS is the root of the normal stress"));
471 } else if (is_tke) {
472 PetscCall(ReadScalarAt(user, scalar_target, 2, 2, 2, &value));
473 PetscCall(PicurvAssertRealNear(4.0, value, 1.0e-11, "TKE is half the trace"));
474 } else if (is_pvar) {
475 PetscCall(ReadScalarAt(user, scalar_target, 2, 2, 2, &value));
476 PetscCall(PicurvAssertRealNear(14.0 / 3.0, value, 1.0e-11, "a scalar product normalizes too"));
477 } else if (is_flux) {
478 Cmpnts ***flux = NULL;
479
480 PetscCall(DMDAVecGetArrayRead(user->fda, vector_target, &flux));
481 PetscCall(PicurvAssertRealNear(10.0 / 3.0, flux[2][2][2].x, 1.0e-11, "flux x"));
482 PetscCall(PicurvAssertRealNear(2.0 / 3.0, flux[2][2][2].y, 1.0e-11, "flux y"));
483 PetscCall(PicurvAssertRealNear(10.0 / 3.0, flux[2][2][2].z, 1.0e-11, "flux z"));
484 PetscCall(DMDAVecRestoreArrayRead(user->fda, vector_target, &flux));
485 }
486 }
487
488 /* A narrower request produces only what it asked for. */
489 PetscCall(PicurvWindowDerivedCount(&d, &storage, "tke", &count));
490 PetscCall(PicurvAssertIntEqual(1, count, "a single output kind enumerates once"));
491
492 /* An unsampled point is left at zero rather than divided by a zero weight. */
493 {
494 PicurvDerivedField field;
495 PetscReal ***nvert = NULL;
496
497 PetscCall(DMDAVecGetArray(user->da, user->Nvert, &nvert));
498 nvert[3][3][3] = 1.0;
499 PetscCall(DMDAVecRestoreArray(user->da, user->Nvert, &nvert));
500 PetscCall(PicurvWindowStorageDestroy(&storage));
501 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
502 PetscCall(SetUniform(user, 1.0, 2.0, 3.0, 4.0));
503 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
504 PetscCall(PicurvWindowDerive(user, &d, &storage, "tke", 0, scalar_target, vector_target, &field));
505 PetscCall(ReadScalarAt(user, scalar_target, 3, 3, 3, &value));
506 PetscCall(PicurvAssertRealNear(0.0, value, 1.0e-12,
507 "a never-sampled point derives to zero, not a division"));
508 }
509
510 /* An unknown output kind is refused rather than silently ignored. */
511 {
512 PetscErrorCode bad = 0;
513
514 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
515 bad = PicurvWindowDerivedCount(&d, &storage, "skewness", &count);
516 PetscCall(PetscPopErrorHandler());
517 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, bad, "an unknown output kind is refused"));
518 }
519
520 PetscCall(VecDestroy(&vector_target));
521 PetscCall(VecDestroy(&scalar_target));
522 PetscCall(PicurvWindowStorageDestroy(&storage));
523 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
524 PetscFunctionReturn(0);
525}
char name[96]
Output field name, window qualified.
PetscErrorCode PicurvWindowDerive(UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const char *outputs, PetscInt index, Vec scalar_target, Vec vector_target, PicurvDerivedField *field)
Derives one output field from centered accumulator state.
PetscErrorCode PicurvWindowDerivedCount(const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const char *outputs, PetscInt *count)
Reports how many derived fields a requested output set produces.
One derived output field, resolved by enumeration index.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestSpatialMeanExcludesUnsampledPoints()

static PetscErrorCode TestSpatialMeanExcludesUnsampledPoints ( void  )
static

The spatial mean divides by sampled points, not by the whole vector.

A derived field is zero outside the target domain and wherever the mask never admitted a point. Those zeros are absences, and averaging over them silently scales the answer down by the fraction of the vector the window never covered — a wrong number that still looks plausible.

Definition at line 535 of file test_statistics_accumulator.c.

536{
537 SimCtx *simCtx = NULL;
538 UserCtx *user = NULL;
539 PicurvWindowStorage storage;
540 PicurvWindowDefinition d = AccDefinition(PETSC_FALSE);
542 PetscReal ***nvert = NULL;
543 PetscReal mean = 0.0;
544 PetscReal whole_vector_mean = 0.0;
545 PetscReal sum = 0.0;
546 PetscInt vector_size = 0;
547 PetscInt targeted = 0;
548
549 PetscFunctionBeginUser;
550 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
551 PetscCall(VecSet(user->Nvert, 0.0));
552 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
553 PetscCall(SetUniform(user, 1.0, 2.0, 3.0, 4.0));
554 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
555
556 /* A field holding exactly one over every sampled point: its masked mean must be
557 * one, whatever fraction of the vector the target domain occupies. */
558 PetscCall(VecZeroEntries(user->PostScalar));
560 PetscCall(SpatialTargetPlanGlobalPointCount(&plan, PETSC_COMM_WORLD, &targeted));
561 {
562 PetscReal ***values = NULL;
563
564 PetscCall(DMDAVecGetArray(user->da, user->PostScalar, &values));
565 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k)
566 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j)
567 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) values[k][j][i] = 1.0;
568 PetscCall(DMDAVecRestoreArray(user->da, user->PostScalar, &values));
569 }
570
571 PetscCall(PicurvWindowSpatialMean(user, &d, &storage, user->PostScalar, &mean));
572 PetscCall(PicurvAssertRealNear(1.0, mean, 1.0e-12,
573 "the mean over sampled points ignores untargeted entries"));
574
575 /* The whole-vector average is genuinely different, which is what makes the
576 * distinction worth asserting rather than assuming. */
577 PetscCall(VecSum(user->PostScalar, &sum));
578 PetscCall(VecGetSize(user->PostScalar, &vector_size));
579 whole_vector_mean = sum / (PetscReal)vector_size;
580 PetscCall(PicurvAssertBool((PetscBool)(vector_size > targeted),
581 "the vector is larger than the targeted domain"));
582 PetscCall(PicurvAssertBool((PetscBool)(whole_vector_mean < 0.9),
583 "a whole-vector average would understate the result"));
584
585 /* Blanking a point removes it from both the sum and the count, so a uniform
586 * field still averages to its own value rather than being diluted. */
587 PetscCall(DMDAVecGetArray(user->da, user->Nvert, &nvert));
588 nvert[3][3][3] = 1.0;
589 PetscCall(DMDAVecRestoreArray(user->da, user->Nvert, &nvert));
590 PetscCall(PicurvWindowStorageDestroy(&storage));
591 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
592 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
593 PetscCall(PicurvWindowSpatialMean(user, &d, &storage, user->PostScalar, &mean));
594 PetscCall(PicurvAssertRealNear(1.0, mean, 1.0e-12,
595 "a never-sampled point is excluded from the average"));
596
597 PetscCall(PicurvWindowStorageDestroy(&storage));
598 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
599 PetscFunctionReturn(0);
600}
PetscErrorCode PicurvWindowSpatialMean(UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, Vec field, PetscReal *mean)
Reports the spatial mean of a derived field over the points a window sampled.
@ 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 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.
Resolved iteration domain for one field on one block.
Vec PostScalar
Definition variables.h:949
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPayloadEnumeration()

static PetscErrorCode TestPayloadEnumeration ( void  )
static

Payload enumeration covers every vector exactly once with stable names.

Definition at line 603 of file test_statistics_accumulator.c.

604{
605 SimCtx *simCtx = NULL;
606 UserCtx *user = NULL;
607 PicurvWindowStorage storage;
609 /* 3 occupancy + 2 means + 2 products + 1 co-moment. Each product and co-moment
610 * is one payload carrying all of its components. */
611 const char *expected[8] = {
612 "count", "weight", "weight_sq",
613 "Ucat_mean", "P_mean",
614 "Ucat_m2", "P_m2",
615 "Ucat_P_cm"
616 };
617 const PetscInt expected_components[8] = {1, 1, 1, 3, 1, 6, 1, 3};
618 PetscInt count = 0;
619 PetscErrorCode bad = 0;
620
621 PetscFunctionBeginUser;
622 d.covariance_count = 1;
625
626 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
627 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
628 PetscCall(PicurvWindowStoragePayloadCount(&storage, &count));
629 PetscCall(PicurvAssertIntEqual(8, count, "every accumulator vector is enumerated once"));
630
631 for (PetscInt index = 0; index < count; ++index) {
633 PetscBool matches = PETSC_FALSE;
634 PetscInt block_size = 0;
635 char context[160];
636
637 PetscCall(PicurvWindowStoragePayload(user, &d, &storage, index, &payload));
638 PetscCall(PetscStrcmp(payload.name, expected[index], &matches));
639 PetscCall(PetscSNPrintf(context, sizeof(context), "payload %d is named '%s', got '%s'",
640 (int)index, expected[index], payload.name));
641 PetscCall(PicurvAssertBool(matches, context));
642 PetscCall(PicurvAssertBool((PetscBool)(payload.vec != NULL), "an enumerated payload has a vector"));
643 PetscCall(PicurvAssertBool((PetscBool)(payload.role != NULL), "an enumerated payload has a role"));
644
645 /* The declared component count must match the vector actually enumerated,
646 * because the manifest inventory records it and the reader trusts it. */
647 PetscCall(VecGetBlockSize(payload.vec, &block_size));
648 PetscCall(PetscSNPrintf(context, sizeof(context), "payload '%s' declares %d components",
649 payload.name, (int)expected_components[index]));
650 PetscCall(PicurvAssertIntEqual(expected_components[index], payload.components, context));
651 PetscCall(PicurvAssertIntEqual(payload.components, block_size,
652 "the declared component count matches the vector"));
653 }
654
655 /* A field without a requested second moment contributes no product payload. */
656 {
657 PicurvWindowDefinition partial = AccDefinition(PETSC_FALSE);
659
660 partial.fields[0].want_second = PETSC_TRUE; /* Ucat keeps its product, P does not. */
661 PetscCall(PicurvWindowStorageCreate(user, &partial, &lean));
662 PetscCall(PicurvWindowStoragePayloadCount(&lean, &count));
663 PetscCall(PicurvAssertIntEqual(6, count, "a field without a second moment adds no payload"));
664 PetscCall(PicurvWindowStorageDestroy(&lean));
665 }
666
667 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
668 bad = PicurvWindowStoragePayload(user, &d, &storage, count, NULL);
669 PetscCall(PetscPopErrorHandler());
670 PetscCall(PicurvAssertBool((PetscBool)(bad != 0), "an out-of-range payload index is rejected"));
671
672 PetscCall(PicurvWindowStorageDestroy(&storage));
673 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
674 PetscFunctionReturn(0);
675}
PetscInt components
Degrees of freedom the vector carries.
Vec vec
Borrowed accumulator vector; never owned by the caller.
PetscErrorCode PicurvWindowStoragePayload(UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, PetscInt index, PicurvStatisticsPayload *payload)
Resolves one enumerated payload of a window's storage.
const char * role
Inventory role: occupancy, mean, second_moment, co_moment.
char name[96]
File basename, no extension.
PetscErrorCode PicurvWindowStoragePayloadCount(const PicurvWindowStorage *storage, PetscInt *count)
Reports how many checkpointable vectors one window's storage holds.
One checkpointable accumulator vector, resolved by enumeration index.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestWeightedAccumulation()

static PetscErrorCode TestWeightedAccumulation ( void  )
static

Unequal weights reproduce the weighted result the kernels define.

Definition at line 678 of file test_statistics_accumulator.c.

679{
680 SimCtx *simCtx = NULL;
681 UserCtx *user = NULL;
682 PicurvWindowStorage storage;
684 PetscReal value = 0.0;
685
686 PetscFunctionBeginUser;
687 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
688 PetscCall(VecSet(user->Nvert, 0.0));
689 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
690
691 /* P = 1 with weight 1, then P = 3 with weight 3: mean 2.5, M2 = 3. */
692 PetscCall(SetUniform(user, 0.0, 0.0, 0.0, 1.0));
693 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
694 PetscCall(SetUniform(user, 0.0, 0.0, 0.0, 3.0));
695 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 3.0));
696
697 PetscCall(ReadScalarAt(user, storage.weight, 2, 2, 2, &value));
698 PetscCall(PicurvAssertRealNear(4.0, value, 1.0e-12, "weighted total weight"));
699 PetscCall(ReadScalarAt(user, storage.weight_sq, 2, 2, 2, &value));
700 PetscCall(PicurvAssertRealNear(10.0, value, 1.0e-12, "weighted squared-weight sum"));
701 PetscCall(ReadScalarAt(user, storage.mean[1], 2, 2, 2, &value));
702 PetscCall(PicurvAssertRealNear(2.5, value, 1.0e-12, "weighted mean"));
703 PetscCall(ReadScalarAt(user, storage.m2[1], 2, 2, 2, &value));
704 PetscCall(PicurvAssertRealNear(3.0, value, 1.0e-11, "weighted centered second moment"));
705
706 PetscCall(PicurvWindowStorageDestroy(&storage));
707 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
708 PetscFunctionReturn(0);
709}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestMaskedPointsAreExcluded()

static PetscErrorCode TestMaskedPointsAreExcluded ( void  )
static

Masked points accumulate nothing, and remain distinguishable from unsampled ones.

Definition at line 712 of file test_statistics_accumulator.c.

713{
714 SimCtx *simCtx = NULL;
715 UserCtx *user = NULL;
716 PicurvWindowStorage storage;
717 PicurvWindowDefinition d = AccDefinition(PETSC_FALSE);
718 PetscReal ***nvert = NULL;
719 PetscReal value = 0.0;
720
721 PetscFunctionBeginUser;
722 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
723 PetscCall(VecSet(user->Nvert, 0.0));
724 /* Blank a single interior cell. */
725 PetscCall(DMDAVecGetArray(user->da, user->Nvert, &nvert));
726 nvert[3][3][3] = 1.0;
727 PetscCall(DMDAVecRestoreArray(user->da, user->Nvert, &nvert));
728
729 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
730 PetscCall(SetUniform(user, 1.0, 2.0, 3.0, 7.0));
731 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
732 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0));
733
734 PetscCall(ReadScalarAt(user, storage.count, 2, 2, 2, &value));
735 PetscCall(PicurvAssertRealNear(2.0, value, 1.0e-12, "a fluid point sees every state"));
736 PetscCall(ReadScalarAt(user, storage.count, 3, 3, 3, &value));
737 PetscCall(PicurvAssertRealNear(0.0, value, 1.0e-12, "a blanked point accumulates no sample"));
738 PetscCall(ReadScalarAt(user, storage.weight, 3, 3, 3, &value));
739 PetscCall(PicurvAssertRealNear(0.0, value, 1.0e-12, "a blanked point accumulates no weight"));
740 PetscCall(ReadScalarAt(user, storage.mean[1], 3, 3, 3, &value));
741 PetscCall(PicurvAssertRealNear(0.0, value, 1.0e-12, "a blanked point keeps an untouched mean"));
742
743 PetscCall(PicurvWindowStorageDestroy(&storage));
744 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
745 PetscFunctionReturn(0);
746}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestConstantFieldZeroProduct()

static PetscErrorCode TestConstantFieldZeroProduct ( void  )
static

A constant field yields exactly zero product at every point.

Definition at line 749 of file test_statistics_accumulator.c.

750{
751 SimCtx *simCtx = NULL;
752 UserCtx *user = NULL;
753 PicurvWindowStorage storage;
755 PetscReal value = 0.0;
756
757 PetscFunctionBeginUser;
758 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
759 PetscCall(VecSet(user->Nvert, 0.0));
760 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
761
762 for (PetscInt s = 0; s < 5; ++s) {
763 PetscCall(SetUniform(user, 2.5, -1.5, 4.0, 9.0));
764 PetscCall(PicurvWindowAccumulate(user, &d, &storage, 1.0 + 0.5 * (PetscReal)s));
765 }
766 for (PetscInt c = 0; c < 6; ++c) {
767 PetscCall(ReadComponentAt(user, storage.m2[0], 6, c, 2, 2, 2, &value));
768 PetscCall(PicurvAssertBool((PetscBool)(value == 0.0),
769 "a constant field gives bitwise-zero products"));
770 }
771 PetscCall(ReadScalarAt(user, storage.mean[1], 2, 2, 2, &value));
772 PetscCall(PicurvAssertRealNear(9.0, value, 1.0e-12, "a constant field keeps its value as the mean"));
773
774 PetscCall(PicurvWindowStorageDestroy(&storage));
775 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
776 PetscFunctionReturn(0);
777}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestRunloopDriverAppliesScheduledStates()

static PetscErrorCode TestRunloopDriverAppliesScheduledStates ( void  )
static

The runloop entry point applies exactly the states the schedule accepts.

Drives FieldStatisticsUpdateWindows directly, so the scheduling decision and the field accumulation are exercised through the same path the solver uses rather than being tested only in isolation.

Definition at line 786 of file test_statistics_accumulator.c.

787{
788 SimCtx *simCtx = NULL;
789 UserCtx *user = NULL;
790 PicurvWindow window;
792 PicurvWindowStorage storage;
793 PetscReal value = 0.0;
794 const PetscReal dt = 0.5;
795
796 PetscFunctionBeginUser;
797 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, ACC_N, ACC_N, ACC_N));
798 PetscCall(VecSet(user->Nvert, 0.0));
799
800 /* Stride two, physical-time weighting, starting at the origin. */
802 d.step_cadence = 2;
803 PetscCall(PicurvWindowInit(&window, &d));
804 PetscCall(PicurvWindowStorageCreate(user, &d, &storage));
805
806 simCtx->fieldStatisticsEnabled = PETSC_TRUE;
807 simCtx->fieldStatisticsWindowCount = 1;
808 simCtx->fieldStatisticsWindows = &window;
809 user->fieldStatisticsStorage = &storage;
810
811 /* Steps 0,2,4 are due. Step 0 anchors; steps 2 and 4 each represent 1.0 of time. */
812 for (PetscInt step = 0; step <= 4; ++step) {
813 PetscCall(SetUniform(user, 1.0, 2.0, 3.0, (PetscReal)(step + 1)));
814 PetscCall(FieldStatisticsUpdateWindows(simCtx, step, (PetscReal)step * dt));
815 }
816
817 PetscCall(PicurvAssertIntEqual(2, window.sample_count, "stride two over five steps accepts two samples"));
818 PetscCall(PicurvAssertRealNear(2.0, window.total_weight, 1.0e-12, "accepted weights span the elapsed time"));
819
820 /* Only the due states reached the field accumulators. */
821 PetscCall(ReadScalarAt(user, storage.count, 2, 2, 2, &value));
822 PetscCall(PicurvAssertRealNear(2.0, value, 1.0e-12, "per-point count matches the accepted samples"));
823 PetscCall(ReadScalarAt(user, storage.weight, 2, 2, 2, &value));
824 PetscCall(PicurvAssertRealNear(2.0, value, 1.0e-12, "per-point weight matches the accepted weights"));
825
826 /* P carried 3 at step 2 and 5 at step 4, each with weight 1: mean 4, M2 = 2. */
827 PetscCall(ReadScalarAt(user, storage.mean[1], 2, 2, 2, &value));
828 PetscCall(PicurvAssertRealNear(4.0, value, 1.0e-12, "only the scheduled samples contribute to the mean"));
829 PetscCall(ReadScalarAt(user, storage.m2[1], 2, 2, 2, &value));
830 PetscCall(PicurvAssertRealNear(2.0, value, 1.0e-11, "second moment reflects only the scheduled samples"));
831
832 /* A disabled subsystem is inert on the same path. */
833 simCtx->fieldStatisticsEnabled = PETSC_FALSE;
834 PetscCall(FieldStatisticsUpdateWindows(simCtx, 6, 3.0));
835 PetscCall(PicurvAssertIntEqual(2, window.sample_count, "a disabled subsystem accepts nothing"));
836
837 simCtx->fieldStatisticsWindows = NULL;
838 simCtx->fieldStatisticsWindowCount = 0;
839 user->fieldStatisticsStorage = NULL;
840 PetscCall(PicurvWindowStorageDestroy(&storage));
841 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
842 PetscFunctionReturn(0);
843}
PetscInt sample_count
PetscErrorCode FieldStatisticsUpdateWindows(struct SimCtx *simCtx, PetscInt step, PetscReal time)
Offers one completed state to every configured window.
PetscReal total_weight
PetscErrorCode PicurvWindowInit(PicurvWindow *window, const PicurvWindowDefinition *definition)
Validates a definition and initializes a window to the pending state.
@ PICURV_WEIGHTING_PHYSICAL_TIME
Weight is the represented interval.
Runtime state of one window.
PetscInt fieldStatisticsWindowCount
Definition variables.h:770
PetscBool fieldStatisticsEnabled
Definition variables.h:769
struct PicurvWindow * fieldStatisticsWindows
Definition variables.h:771
struct PicurvWindowStorage * fieldStatisticsStorage
Definition variables.h:952
Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char **  argv 
)

Entry point for the accumulator suite.

Definition at line 848 of file test_statistics_accumulator.c.

849{
850 PetscErrorCode ierr;
851 const PicurvTestCase cases[] = {
852 {"component-counts", TestComponentCounts},
853 {"storage-shape", TestStorageShape},
854 {"known-accumulation-across-field", TestKnownAccumulationAcrossField},
855 {"weighted-accumulation", TestWeightedAccumulation},
856 {"masked-points-excluded", TestMaskedPointsAreExcluded},
857 {"constant-field-zero-product", TestConstantFieldZeroProduct},
858 {"valid-fraction-range", TestValidFractionRange},
859 {"valid-fraction-detects-never-valid-point", TestValidFractionDetectsNeverValidPoint},
860 {"covariance-accumulation", TestCovarianceAccumulation},
861 {"scalar-self-covariance-matches-second-moment", TestScalarSelfCovarianceMatchesSecondMoment},
862 {"covariance-requires-field-membership", TestCovarianceRequiresFieldMembership},
863 {"payload-enumeration", TestPayloadEnumeration},
864 {"derived-quantities", TestDerivedQuantities},
865 {"spatial-mean-excludes-unsampled-points", TestSpatialMeanExcludesUnsampledPoints},
866 {"runloop-driver-applies-scheduled-states", TestRunloopDriverAppliesScheduledStates},
867 };
868
869 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv statistics accumulator tests");
870 if (ierr) return (int)ierr;
871 ierr = PicurvRunTests("unit-statistics-accumulator", cases, sizeof(cases) / sizeof(cases[0]));
872 if (ierr) { PetscFinalize(); return (int)ierr; }
873 ierr = PetscFinalize();
874 return (int)ierr;
875}
static PetscErrorCode TestCovarianceAccumulation(void)
A vector-scalar covariance reproduces its analytically known components.
static PetscErrorCode TestWeightedAccumulation(void)
Unequal weights reproduce the weighted result the kernels define.
static PetscErrorCode TestStorageShape(void)
Storage allocates one mean per field and the right number of product components.
static PetscErrorCode TestValidFractionRange(void)
The valid-fraction range reports mask coverage, including a never-valid point.
static PetscErrorCode TestValidFractionDetectsNeverValidPoint(void)
A point blanked from the start is reported as never valid.
static PetscErrorCode TestRunloopDriverAppliesScheduledStates(void)
The runloop entry point applies exactly the states the schedule accepts.
static PetscErrorCode TestDerivedQuantities(void)
Derived quantities reproduce the analytic values the accumulated state implies.
static PetscErrorCode TestMaskedPointsAreExcluded(void)
Masked points accumulate nothing, and remain distinguishable from unsampled ones.
static PetscErrorCode TestKnownAccumulationAcrossField(void)
Three accepted states reproduce the analytically known moments at every point.
static PetscErrorCode TestScalarSelfCovarianceMatchesSecondMoment(void)
A field's covariance with itself reduces exactly to its own second moment.
static PetscErrorCode TestConstantFieldZeroProduct(void)
A constant field yields exactly zero product at every point.
static PetscErrorCode TestSpatialMeanExcludesUnsampledPoints(void)
The spatial mean divides by sampled points, not by the whole vector.
static PetscErrorCode TestPayloadEnumeration(void)
Payload enumeration covers every vector exactly once with stable names.
static PetscErrorCode TestCovarianceRequiresFieldMembership(void)
A covariance member missing from the field list is rejected, not silently skipped.
static PetscErrorCode TestComponentCounts(void)
Component counts follow the documented product shapes.
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: