15static PetscReal
ScalarValue(PetscInt i, PetscInt j, PetscInt k)
17 return (PetscReal)(i + 10 * j + 100 * k);
24 PetscReal global_error = 0.0;
26 PetscFunctionBeginUser;
27 PetscCallMPI(MPI_Allreduce(&local_error, &global_error, 1, MPIU_REAL, MPI_MAX,
29 PetscCall(
PicurvAssertBool((PetscBool)(global_error <= tolerance), context));
30 PetscFunctionReturn(0);
39 PetscReal ***pressure = NULL;
41 const PetscReal ***pressure_result = NULL;
42 const PetscReal ***pressure_nodal = NULL;
43 const PetscReal ***qcrit = NULL;
44 const Cmpnts ***velocity_nodal = NULL;
45 PetscReal pressure_error = 0.0;
46 PetscReal pressure_nodal_error = 0.0;
47 PetscReal velocity_nodal_error = 0.0;
48 PetscReal qcrit_error = 0.0;
49 PetscInt local_physical_nodes = 0;
50 PetscInt global_physical_nodes = 0;
51 PetscInt local_q_points = 0;
52 PetscInt global_q_points = 0;
54 PetscFunctionBeginUser;
56 PetscCall(PetscCalloc1(1, &simCtx->
pps));
65 PetscCall(DMDAVecGetArray(user->
da, user->
P, &pressure));
66 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &velocity));
67 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k) {
68 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j) {
69 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
71 velocity[k][j][i] = (
Cmpnts){1.0, 2.0, 3.0};
75 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &velocity));
76 PetscCall(DMDAVecRestoreArray(user->
da, user->
P, &pressure));
81 PetscCall(PetscStrncpy(
83 "NormalizeRelativeField:P;DimensionalizeAllLoadedFields;"
84 "CellToNodeAverage:P>P_nodal;CellToNodeAverage:Ucat>Ucat_nodal;"
89 PetscCall(DMDAVecGetArrayRead(user->
da, user->
P, &pressure_result));
90 PetscCall(DMDAVecGetArrayRead(user->
da, user->
P_nodal, &pressure_nodal));
91 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat_nodal, &velocity_nodal));
92 PetscCall(DMDAVecGetArrayRead(user->
da, user->
Qcrit, &qcrit));
93 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k) {
94 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j) {
95 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
96 const PetscBool physical_node =
97 (PetscBool)(i < user->info.mx - 1 && j < user->info.my - 1 &&
98 k < user->info.mz - 1);
99 const PetscBool q_point =
100 (PetscBool)(i >= 1 && i < user->info.mx - 1 &&
101 j >= 1 && j < user->info.my - 1 &&
102 k >= 1 && k < user->info.mz - 1);
103 const PetscReal expected_pressure = 2.0 * (
ScalarValue(i, j, k) - 111.0);
105 pressure_error = PetscMax(
107 PetscAbsReal(pressure_result[k][j][i] - expected_pressure));
109 const PetscReal expected_nodal =
112 pressure_nodal_error = PetscMax(
113 pressure_nodal_error,
114 PetscAbsReal(pressure_nodal[k][j][i] - expected_nodal));
115 velocity_nodal_error = PetscMax(
116 velocity_nodal_error,
117 PetscAbsReal(velocity_nodal[k][j][i].x - 1.0));
118 velocity_nodal_error = PetscMax(
119 velocity_nodal_error,
120 PetscAbsReal(velocity_nodal[k][j][i].y - 2.0));
121 velocity_nodal_error = PetscMax(
122 velocity_nodal_error,
123 PetscAbsReal(velocity_nodal[k][j][i].z - 3.0));
124 ++local_physical_nodes;
126 pressure_nodal_error = PetscMax(
127 pressure_nodal_error,
128 PetscAbsReal(pressure_nodal[k][j][i] -
kSentinel));
129 velocity_nodal_error = PetscMax(
130 velocity_nodal_error,
131 PetscAbsReal(velocity_nodal[k][j][i].x -
kSentinel));
132 velocity_nodal_error = PetscMax(
133 velocity_nodal_error,
134 PetscAbsReal(velocity_nodal[k][j][i].y -
kSentinel));
135 velocity_nodal_error = PetscMax(
136 velocity_nodal_error,
137 PetscAbsReal(velocity_nodal[k][j][i].z -
kSentinel));
139 qcrit_error = PetscMax(
141 PetscAbsReal(qcrit[k][j][i] - (q_point ? 0.0 :
kSentinel)));
142 if (q_point) ++local_q_points;
146 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
Qcrit, &qcrit));
147 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat_nodal, &velocity_nodal));
148 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
P_nodal, &pressure_nodal));
149 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
P, &pressure_result));
151 PetscCallMPI(MPI_Allreduce(&local_physical_nodes, &global_physical_nodes, 1,
152 MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
153 PetscCallMPI(MPI_Allreduce(&local_q_points, &global_q_points, 1,
154 MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
156 global_physical_nodes,
157 "the Eulerian pipeline must visit every physical node exactly once"));
160 "Q-criterion must visit every interior cell exactly once"));
162 "pressure normalization and dimensionalization are decomposition independent"));
164 "scalar nodal averaging covers rank interfaces and physical boundaries"));
166 "vector nodal averaging covers rank interfaces and physical boundaries"));
168 "Q-criterion covers the complete distributed interior"));
171 PetscFunctionReturn(0);
180 PetscMPIInt rank = 0, size = 1;
181 PetscInt source_local = 0;
182 PetscInt source_global = 0;
183 PetscInt post_local = 0;
184 PetscInt post_global = 0;
186 PetscScalar (*velocity)[3] = NULL;
187 const PetscScalar *ske = NULL;
188 PetscReal local_error = 0.0;
189 const PetscInt total_particles = 7;
191 PetscFunctionBeginUser;
192 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
193 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
194 source_local = total_particles / size + (rank < total_particles % size ? 1 : 0);
195 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
204 PetscCallMPI(MPI_Exscan(&source_local, &offset, 1, MPIU_INT, MPI_SUM,
206 if (rank == 0) offset = 0;
208 PetscCall(DMSwarmGetField(user->
swarm,
"velocity", NULL, NULL, (
void **)&velocity));
209 for (PetscInt p = 0; p < source_local; ++p) {
210 const PetscReal tag = (PetscReal)(offset + p + 1);
212 velocity[p][0] = tag;
213 velocity[p][1] = 2.0;
214 velocity[p][2] = -1.0;
216 PetscCall(DMSwarmRestoreField(user->
swarm,
"velocity", NULL, NULL,
217 (
void **)&velocity));
220 PetscCall(DMSwarmGetLocalSize(user->
swarm, &source_local));
221 PetscCall(DMSwarmGetSize(user->
swarm, &source_global));
222 PetscCall(DMSwarmGetLocalSize(user->
post_swarm, &post_local));
223 PetscCall(DMSwarmGetSize(user->
post_swarm, &post_global));
225 "derived particle fields must share source local ownership"));
227 "derived and source swarms must have the same global size"));
229 PetscCall(DMSwarmGetField(user->
post_swarm,
"ske", NULL, NULL, (
void **)&ske));
230 for (PetscInt p = 0; p < source_local; ++p) {
231 const PetscReal tag = (PetscReal)(offset + p + 1);
232 const PetscReal expected = 0.5 * (tag * tag + 5.0);
234 local_error = PetscMax(local_error,
235 PetscAbsReal(PetscRealPart(ske[p]) - expected));
237 PetscCall(DMSwarmRestoreField(user->
post_swarm,
"ske", NULL, NULL, (
void **)&ske));
239 "specific kinetic energy must match every source particle"));
242 PetscFunctionReturn(0);
250 PetscMPIInt rank = 0, size = 1;
251 const PetscInt targets[] = {7, 3, 9, 0};
253 PetscFunctionBeginUser;
254 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
255 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
259 for (
size_t target_index = 0;
260 target_index <
sizeof(targets) /
sizeof(targets[0]); ++target_index) {
263 const PetscInt target = targets[target_index];
264 const PetscInt expected_local =
265 target / size + (rank < target % size ? 1 : 0);
268 PetscCall(DMSwarmGetLocalSize(user->
swarm, &local));
269 PetscCall(DMSwarmGetSize(user->
swarm, &global));
271 "global swarm resize must use balanced local sizes"));
273 "global swarm resize must conserve the requested total"));
277 PetscFunctionReturn(0);
288 PetscReal ***pressure = NULL;
289 const PetscReal ***nodal = NULL;
291 PetscInt components = 0;
292 PetscInt local_checked = 0;
293 PetscInt global_checked = 0;
294 PetscReal local_error = 0.0;
297 PetscFunctionBeginUser;
298 PetscCall(PetscMemzero(&definition,
sizeof(definition)));
299 PetscCall(PetscStrncpy(definition.
name,
"analytic",
sizeof(definition.
name)));
307 PetscCall(VecSet(user->
Nvert, 0.0));
315 PetscCall(DMDAVecGetArray(user->
da, user->
P, &pressure));
316 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k)
317 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j)
318 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i)
320 PetscCall(DMDAVecRestoreArray(user->
da, user->
P, &pressure));
323 &result, &components));
325 "the pressure mean must derive as a scalar field"));
327 "the pressure mean must use the scalar nodal staging vector"));
329 PetscCall(DMDAVecGetArrayRead(user->
da, result, &nodal));
330 for (PetscInt k = PetscMax(user->
info.zs, 1);
331 k < PetscMin(user->
info.zs + user->
info.zm, user->
info.mz - 2); ++k) {
332 for (PetscInt j = PetscMax(user->
info.ys, 1);
333 j < PetscMin(user->
info.ys + user->
info.ym, user->
info.my - 2); ++j) {
334 for (PetscInt i = PetscMax(user->
info.xs, 1);
335 i < PetscMin(user->
info.xs + user->
info.xm, user->
info.mx - 2); ++i) {
336 local_error = PetscMax(
338 PetscAbsReal(nodal[k][j][i] - (
ScalarValue(i, j, k) + 55.5)));
343 PetscCall(DMDAVecRestoreArrayRead(user->
da, result, &nodal));
344 PetscCallMPI(MPI_Allreduce(&local_checked, &global_checked, 1, MPIU_INT, MPI_SUM,
348 "the derived mean must cover every fully resolved nodal point"));
350 "field-statistics nodal derivation must cross rank interfaces"));
358 PetscFunctionReturn(0);
372 ierr = PetscInitialize(&argc, &argv, NULL,
373 "PICurv serial/MPI post-processing compute tests");
374 if (ierr)
return (
int)ierr;
376 sizeof(cases) /
sizeof(cases[0]));
381 ierr = PetscFinalize();
PetscErrorCode ResizeSwarmGlobally(DM swarm, PetscInt N_target)
Resizes a swarm collectively to a target global particle count.
PetscErrorCode ComputeWindowStatisticNodal(UserCtx *user, PetscInt window_index, const char *outputs, PetscInt output_index, char *out_name, size_t name_size, Vec *out_vec, PetscInt *out_components)
Derives one accumulated statistic and converts it to nodal values.
PetscErrorCode EulerianDataProcessingPipeline(UserCtx *user, PostProcessParams *pps)
Parses the processing pipeline string and executes the requested kernels.
PetscErrorCode ParticleDataProcessingPipeline(UserCtx *user, PostProcessParams *pps)
Parses and executes the particle pipeline using a robust two-pass approach.
PetscErrorCode SetupPostProcessSwarm(UserCtx *user, PostProcessParams *pps)
Creates a new, dedicated DMSwarm for post-processing tasks.
Per-window PETSc accumulator storage and pointwise application.
#define PICURV_STATISTICS_PAYLOAD_NAME_LENGTH
Maximum stored length of a payload name, including the terminator.
PetscErrorCode PicurvWindowStorageCreate(UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage)
Allocates the accumulator state one window owns on one block.
PetscErrorCode PicurvWindowAccumulate(UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage, PetscReal weight)
Applies one accepted completed state to a window's accumulators.
PetscErrorCode PicurvWindowStorageDestroy(PicurvWindowStorage *storage)
Releases accumulator state previously created for one window.
Independent accumulator state for one window on one block.
Window lifecycle, scheduling, and weighting for the field-statistics pipeline.
PicurvWindowFieldRequest fields[16]
PicurvCadenceKind cadence_kind
PetscInt step_cadence
Used when cadence_kind is step; must be positive.
PetscErrorCode PicurvWindowInit(PicurvWindow *window, const PicurvWindowDefinition *definition)
Validates a definition and initializes a window to the pending state.
PetscInt field_id
Catalogued Eulerian field identity.
@ PICURV_WEIGHTING_SAMPLE
Equal weight per accepted state.
@ PICURV_CADENCE_STEP
Every n completed steps from activation.
PicurvWeighting weighting
Runtime state of one window.
The scientifically immutable definition of one window.
static PetscErrorCode TestResizeSwarmGloballyBalanced(void)
Checks that global resizing produces one balanced global population.
static PetscErrorCode TestFieldStatisticsDerivedNodalMultiRank(void)
Exercises accumulator-to-derived-nodal field statistics across ownership boundaries.
static PetscErrorCode TestParticlePipelineMatchesSourceOwnership(void)
Verifies the production-created derived swarm mirrors source ownership.
static const PetscReal kSentinel
static PetscReal ScalarValue(PetscInt i, PetscInt j, PetscInt k)
Analytic scalar field used to expose misplaced or missing grid points.
int main(int argc, char **argv)
Runs the serial/MPI post-processing compute regression suite.
static PetscErrorCode AssertGlobalError(PetscReal local_error, PetscReal tolerance, const char *context)
Requires every rank's maximum error to be within tolerance.
static PetscErrorCode TestEulerianPipelineDecompositionIndependent(void)
Exercises every Eulerian transformation currently dispatched by the pipeline.
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 PicurvCreateSwarmPair(UserCtx *user, PetscInt nlocal, const char *post_field_name)
Creates matched solver and post-processing swarms for tests.
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
PetscBool fieldStatisticsEnabled
struct PicurvWindow * fieldStatisticsWindows
char particle_pipeline[1024]
char process_pipeline[1024]
struct PicurvWindowStorage * fieldStatisticsStorage
A 3D point or vector with PetscScalar components.
Holds all configuration parameters for a post-processing run.
The master context for the entire simulation.
User-defined context containing data specific to a single computational grid level.