PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
test_statistics_config.c
Go to the documentation of this file.
1/**
2 * @file test_statistics_config.c
3 * @brief C unit tests for field-statistics control ingress.
4 *
5 * These exercise the parse site directly against the PETSc options database, which
6 * is the same path a generated `control` file takes. Python rejects malformed
7 * configuration earlier and with better messages, so the cases here are the ones a
8 * hand-written or hand-edited control file can still reach.
9 */
10
11#include "test_support.h"
12
13#include "statistics_config.h"
14#include "statistics_window.h"
15#include "field_catalog.h"
16
17/** @brief Clears any statistics options left by a previous case. */
18static PetscErrorCode ClearStatisticsOptions(void)
19{
20 PetscFunctionBeginUser;
21 PetscCall(PetscOptionsClear(NULL));
22 PetscFunctionReturn(0);
23}
24
25/** @brief Installs one option as the control file would. */
26static PetscErrorCode SetOption(const char *name, const char *value)
27{
28 PetscFunctionBeginUser;
29 PetscCall(PetscOptionsSetValue(NULL, name, value));
30 PetscFunctionReturn(0);
31}
32
33/** @brief Installs a complete, valid single-window configuration. */
34static PetscErrorCode SetValidWindow(void)
35{
36 PetscFunctionBeginUser;
37 PetscCall(ClearStatisticsOptions());
38 PetscCall(SetOption("-field_statistics_enabled", "true"));
39 PetscCall(SetOption("-field_statistics_window_count", "1"));
40 PetscCall(SetOption("-field_statistics_window_0_name", "production"));
41 PetscCall(SetOption("-field_statistics_window_0_start_time", "50.0"));
42 PetscCall(SetOption("-field_statistics_window_0_end_time", "250.0"));
43 PetscCall(SetOption("-field_statistics_window_0_weighting", "physical_time"));
44 PetscCall(SetOption("-field_statistics_window_0_step_cadence", "5"));
45 PetscCall(SetOption("-field_statistics_window_0_field_count", "2"));
46 PetscCall(SetOption("-field_statistics_window_0_field_0_name", "Ucat"));
47 PetscCall(SetOption("-field_statistics_window_0_field_0_moments", "first,second"));
48 PetscCall(SetOption("-field_statistics_window_0_field_1_name", "P"));
49 PetscCall(SetOption("-field_statistics_window_0_field_1_moments", "first"));
50 PetscCall(SetOption("-field_statistics_window_0_covariance_count", "1"));
51 PetscCall(SetOption("-field_statistics_window_0_covariance_0", "Ucat,P"));
52 PetscFunctionReturn(0);
53}
54
55/** @brief Runs the parse site against a zeroed context and reports the error code. */
56static PetscErrorCode ParseIntoFreshContext(SimCtx *simCtx, PetscErrorCode *result)
57{
58 PetscFunctionBeginUser;
59 PetscCall(PetscMemzero(simCtx, sizeof(*simCtx)));
60 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
61 *result = ParseFieldStatisticsConfig(simCtx);
62 PetscCall(PetscPopErrorHandler());
63 PetscFunctionReturn(0);
64}
65
66/** @brief A complete configuration resolves into the definition the pipeline consumes. */
67static PetscErrorCode TestResolvesCompleteWindow(void)
68{
69 SimCtx simCtx;
70 PetscBool name_matches = PETSC_FALSE;
71
72 PetscFunctionBeginUser;
73 PetscCall(SetValidWindow());
74 PetscCall(PetscMemzero(&simCtx, sizeof(simCtx)));
75 PetscCall(ParseFieldStatisticsConfig(&simCtx));
76
77 PetscCall(PicurvAssertBool(simCtx.fieldStatisticsEnabled, "the enabled flag is read"));
78 PetscCall(PicurvAssertIntEqual(1, simCtx.fieldStatisticsWindowCount, "one window is resolved"));
79 {
81
82 PetscCall(PetscStrcmp(d->name, "production", &name_matches));
83 PetscCall(PicurvAssertBool(name_matches, "the window name is read"));
84 PetscCall(PicurvAssertRealNear(50.0, d->start_time, 1.0e-12, "the start time is read"));
85 PetscCall(PicurvAssertBool(d->bounded, "a present end time bounds the window"));
86 PetscCall(PicurvAssertRealNear(250.0, d->end_time, 1.0e-12, "the end time is read"));
88 "the weighting is read"));
89 PetscCall(PicurvAssertBool((PetscBool)(d->cadence_kind == PICURV_CADENCE_STEP),
90 "a step cadence selects the step schedule"));
91 PetscCall(PicurvAssertIntEqual(5, d->step_cadence, "the step cadence is read"));
92 PetscCall(PicurvAssertIntEqual(2, d->field_count, "both fields are resolved"));
94 "field names resolve through the typed catalog"));
95 PetscCall(PicurvAssertBool(d->fields[0].want_second, "a requested second moment is recorded"));
96 PetscCall(PicurvAssertIntEqual(FIELD_ID_P, d->fields[1].field_id, "the second field resolves"));
97 PetscCall(PicurvAssertBool((PetscBool)!d->fields[1].want_second,
98 "a field asking only for the first moment keeps no product"));
99 PetscCall(PicurvAssertIntEqual(1, d->covariance_count, "the covariance is resolved"));
100 PetscCall(PicurvAssertIntEqual(FIELD_ID_UCAT, d->covariances[0].first, "the first member resolves"));
101 PetscCall(PicurvAssertIntEqual(FIELD_ID_P, d->covariances[0].second, "the second member resolves"));
102 }
103 /* The window starts pending: resolution configures, it does not activate. */
104 PetscCall(PicurvAssertBool((PetscBool)(simCtx.fieldStatisticsWindows[0].state == PICURV_WINDOW_PENDING),
105 "a resolved window begins pending"));
106
107 PetscCall(DestroyFieldStatisticsConfig(&simCtx));
108 PetscCall(PicurvAssertIntEqual(0, simCtx.fieldStatisticsWindowCount, "teardown clears the count"));
109 PetscCall(ClearStatisticsOptions());
110 PetscFunctionReturn(0);
111}
112
113/** @brief An omitted end time leaves the window open rather than defaulting one. */
114static PetscErrorCode TestOmittedEndTimeLeavesWindowOpen(void)
115{
116 SimCtx simCtx;
117
118 PetscFunctionBeginUser;
119 PetscCall(SetValidWindow());
120 PetscCall(PetscOptionsClearValue(NULL, "-field_statistics_window_0_end_time"));
121 PetscCall(PetscMemzero(&simCtx, sizeof(simCtx)));
122 PetscCall(ParseFieldStatisticsConfig(&simCtx));
123
124 PetscCall(PicurvAssertBool((PetscBool)!simCtx.fieldStatisticsWindows[0].definition.bounded,
125 "an absent end time leaves the window open ended"));
126 PetscCall(DestroyFieldStatisticsConfig(&simCtx));
127 PetscCall(ClearStatisticsOptions());
128 PetscFunctionReturn(0);
129}
130
131/** @brief A disabled subsystem resolves nothing and allocates nothing. */
132static PetscErrorCode TestDisabledResolvesNothing(void)
133{
134 SimCtx simCtx;
135
136 PetscFunctionBeginUser;
137 PetscCall(SetValidWindow());
138 PetscCall(SetOption("-field_statistics_enabled", "false"));
139 PetscCall(PetscMemzero(&simCtx, sizeof(simCtx)));
140 PetscCall(ParseFieldStatisticsConfig(&simCtx));
141
143 "a disabled subsystem resolves no window"));
144 PetscCall(PicurvAssertBool((PetscBool)(simCtx.fieldStatisticsWindows == NULL),
145 "a disabled subsystem allocates nothing"));
146
147 /* Absent entirely is the same as disabled, and must not error. */
148 PetscCall(ClearStatisticsOptions());
149 PetscCall(PetscMemzero(&simCtx, sizeof(simCtx)));
150 PetscCall(ParseFieldStatisticsConfig(&simCtx));
152 "an absent configuration resolves no window"));
153 PetscCall(ClearStatisticsOptions());
154 PetscFunctionReturn(0);
155}
156
157/** @brief The console cadence rides the same chain and is independent of the windows. */
158static PetscErrorCode TestConsoleCadenceIsIndependent(void)
159{
160 SimCtx simCtx;
161
162 PetscFunctionBeginUser;
163 PetscCall(ClearStatisticsOptions());
164 PetscCall(SetOption("-statistics_console_output_freq", "25"));
165 PetscCall(PetscMemzero(&simCtx, sizeof(simCtx)));
166 PetscCall(ParseFieldStatisticsConfig(&simCtx));
167 /* Read even when no window exists, because it is reporting configuration rather
168 * than science and the banner reports it either way. */
170 "the console cadence is read without any window"));
171 PetscCall(ClearStatisticsOptions());
172 PetscFunctionReturn(0);
173}
174
175/** @brief Malformed configuration is rejected rather than silently partially applied. */
176static PetscErrorCode TestMalformedConfigurationRejected(void)
177{
178 SimCtx simCtx;
179 PetscErrorCode bad = 0;
180
181 PetscFunctionBeginUser;
182
183 /* Enabled with no window at all. */
184 PetscCall(ClearStatisticsOptions());
185 PetscCall(SetOption("-field_statistics_enabled", "true"));
186 PetscCall(ParseIntoFreshContext(&simCtx, &bad));
187 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, bad, "enabled with no window is rejected"));
188
189 /* Both cadences: the schedule would be ambiguous. */
190 PetscCall(SetValidWindow());
191 PetscCall(SetOption("-field_statistics_window_0_time_cadence", "0.5"));
192 PetscCall(ParseIntoFreshContext(&simCtx, &bad));
193 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, bad, "two cadences are rejected"));
194
195 /* Neither cadence. */
196 PetscCall(SetValidWindow());
197 PetscCall(PetscOptionsClearValue(NULL, "-field_statistics_window_0_step_cadence"));
198 PetscCall(ParseIntoFreshContext(&simCtx, &bad));
199 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, bad, "no cadence is rejected"));
200
201 /* An unknown weighting. */
202 PetscCall(SetValidWindow());
203 PetscCall(SetOption("-field_statistics_window_0_weighting", "inverse_variance"));
204 PetscCall(ParseIntoFreshContext(&simCtx, &bad));
205 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, bad, "an unknown weighting is rejected"));
206
207 /* An unknown moment. */
208 PetscCall(SetValidWindow());
209 PetscCall(SetOption("-field_statistics_window_0_field_0_moments", "first,third"));
210 PetscCall(ParseIntoFreshContext(&simCtx, &bad));
211 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, bad, "an unknown moment is rejected"));
212
213 /* A covariance member missing from the field list has no mean to center against. */
214 PetscCall(SetValidWindow());
215 PetscCall(SetOption("-field_statistics_window_0_covariance_0", "Ucat,Nvert"));
216 PetscCall(ParseIntoFreshContext(&simCtx, &bad));
217 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, bad,
218 "a covariance member outside the field list is rejected"));
219
220 /* A field paired with itself is the self-product, requested another way. */
221 PetscCall(SetValidWindow());
222 PetscCall(SetOption("-field_statistics_window_0_covariance_0", "Ucat,Ucat"));
223 PetscCall(ParseIntoFreshContext(&simCtx, &bad));
224 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, bad, "a self-pairing covariance is rejected"));
225
226 /* Two windows sharing a name cannot be told apart in a checkpoint. */
227 PetscCall(SetValidWindow());
228 PetscCall(SetOption("-field_statistics_window_count", "2"));
229 PetscCall(SetOption("-field_statistics_window_1_name", "production"));
230 PetscCall(SetOption("-field_statistics_window_1_start_time", "0.0"));
231 PetscCall(SetOption("-field_statistics_window_1_weighting", "sample"));
232 PetscCall(SetOption("-field_statistics_window_1_step_cadence", "1"));
233 PetscCall(SetOption("-field_statistics_window_1_field_count", "1"));
234 PetscCall(SetOption("-field_statistics_window_1_field_0_name", "P"));
235 PetscCall(SetOption("-field_statistics_window_1_field_0_moments", "first"));
236 PetscCall(ParseIntoFreshContext(&simCtx, &bad));
237 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_WRONG, bad, "a duplicate window name is rejected"));
238
239 /* A bounded window that ends before it starts. */
240 PetscCall(SetValidWindow());
241 PetscCall(SetOption("-field_statistics_window_0_end_time", "10.0"));
242 PetscCall(ParseIntoFreshContext(&simCtx, &bad));
243 PetscCall(PicurvAssertIntEqual(PETSC_ERR_ARG_OUTOFRANGE, bad,
244 "an end before the start is rejected by window validation"));
245
246 PetscCall(ClearStatisticsOptions());
247 PetscFunctionReturn(0);
248}
249
250/**
251 * @brief Entry point for the statistics configuration suite.
252 */
253int main(int argc, char **argv)
254{
255 PetscErrorCode ierr;
256 const PicurvTestCase cases[] = {
257 {"resolves-complete-window", TestResolvesCompleteWindow},
258 {"omitted-end-time-leaves-window-open", TestOmittedEndTimeLeavesWindowOpen},
259 {"disabled-resolves-nothing", TestDisabledResolvesNothing},
260 {"console-cadence-is-independent", TestConsoleCadenceIsIndependent},
261 {"malformed-configuration-rejected", TestMalformedConfigurationRejected},
262 };
263
264 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv statistics configuration tests");
265 if (ierr) return (int)ierr;
266 ierr = PicurvRunTests("unit-statistics-config", cases, sizeof(cases) / sizeof(cases[0]));
267 if (ierr) { PetscFinalize(); return (int)ierr; }
268 ierr = PetscFinalize();
269 return (int)ierr;
270}
Authoritative identities and storage metadata for persistent Eulerian fields.
@ FIELD_ID_UCAT
@ FIELD_ID_P
Control ingress for the field-statistics pipeline.
PetscErrorCode DestroyFieldStatisticsConfig(SimCtx *simCtx)
Releases the window definitions resolved by ParseFieldStatisticsConfig().
PetscErrorCode ParseFieldStatisticsConfig(SimCtx *simCtx)
Resolves field-statistics configuration from the control file.
Window lifecycle, scheduling, and weighting for the field-statistics pipeline.
PicurvWindowState state
PetscInt first
First member; must also appear in the field list.
PicurvWindowFieldRequest fields[16]
PetscReal end_time
Requested end; ignored when bounded is false.
PicurvCadenceKind cadence_kind
PetscInt step_cadence
Used when cadence_kind is step; must be positive.
@ PICURV_WINDOW_PENDING
Requested start not yet reached.
PetscBool want_second
Also keep the centered second moment.
PetscInt second
Second member; must also appear in the field list.
PicurvWindowCovarianceRequest covariances[16]
PetscBool bounded
False for an open-ended window.
PicurvWindowDefinition definition
PetscReal start_time
Requested start.
PetscInt field_id
Catalogued Eulerian field identity.
@ PICURV_WEIGHTING_PHYSICAL_TIME
Weight is the represented interval.
@ PICURV_CADENCE_STEP
Every n completed steps from activation.
The scientifically immutable definition of one window.
static PetscErrorCode TestDisabledResolvesNothing(void)
A disabled subsystem resolves nothing and allocates nothing.
int main(int argc, char **argv)
Entry point for the statistics configuration suite.
static PetscErrorCode TestMalformedConfigurationRejected(void)
Malformed configuration is rejected rather than silently partially applied.
static PetscErrorCode ParseIntoFreshContext(SimCtx *simCtx, PetscErrorCode *result)
Runs the parse site against a zeroed context and reports the error code.
static PetscErrorCode TestResolvesCompleteWindow(void)
A complete configuration resolves into the definition the pipeline consumes.
static PetscErrorCode TestOmittedEndTimeLeavesWindowOpen(void)
An omitted end time leaves the window open rather than defaulting one.
static PetscErrorCode SetOption(const char *name, const char *value)
Installs one option as the control file would.
static PetscErrorCode SetValidWindow(void)
Installs a complete, valid single-window configuration.
static PetscErrorCode ClearStatisticsOptions(void)
Clears any statistics options left by a previous case.
static PetscErrorCode TestConsoleCadenceIsIndependent(void)
The console cadence rides the same chain and is independent of the windows.
PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
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.
PetscInt fieldStatisticsWindowCount
Definition variables.h:770
PetscInt statisticsConsoleOutputFreq
Definition variables.h:772
PetscBool fieldStatisticsEnabled
Definition variables.h:769
struct PicurvWindow * fieldStatisticsWindows
Definition variables.h:771
The master context for the entire simulation.
Definition variables.h:695