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

C unit tests for MPI collective helper routines. More...

#include "test_support.h"
#include "ParticleMotion.h"
#include "ParticleSwarm.h"
#include "Boundaries.h"
#include "grid.h"
#include "statistics_target.h"
#include "statistics_accumulator.h"
#include "statistics_window.h"
#include "io.h"
#include "field_catalog.h"
#include "les.h"
#include <stdlib.h>
Include dependency graph for test_mpi_kernels.c:

Go to the source code of this file.

Functions

static void MarkXPeriodic (UserCtx *user)
 Marks both x boundary faces as geometric periodic boundaries.
 
static PetscReal PeriodicScalarValue (PetscInt i, PetscInt j, PetscInt k, PetscReal offset)
 Returns a deterministic scalar value for periodic-transfer assertions.
 
static PetscErrorCode TestDistributeParticlesCollectiveConsistency (void)
 Tests collective particle distribution consistency across MPI ranks.
 
static PetscErrorCode TestBoundingBoxCollectivesMultiRank (void)
 Tests multi-rank bounding-box gather and broadcast helpers.
 
static PetscErrorCode TestPeriodicCellFieldSynchronizationMultiRank (void)
 Tests distributed synchronization of persistent cell-centered scalar fields.
 
static PetscErrorCode TestPeriodicGeometryValidationMultiRank (void)
 Tests periodic seam translation discovery when seam nodes are on different ranks.
 
static PetscErrorCode TestPeriodicFaceCenterCoordinatesMultiRank (void)
 Tests translated face-center ghosts when the periodic axis is partitioned.
 
static PetscErrorCode TestPeriodicFaceFieldSynchronizationMultiRank (void)
 Tests persistent I-face synchronization across an MPI-partitioned seam.
 
static PetscErrorCode TestPeriodicStaggeredFieldSynchronizationMultiRank (void)
 Tests persistent Ucont synchronization across an MPI-partitioned seam.
 
static PetscErrorCode TestRestartCellIdMigrationMovesParticleToOwner (void)
 Tests restart fast-path migration using preloaded cell ownership metadata.
 
static PetscErrorCode TestSpatialTargetIsDecompositionIndependent (void)
 Spatial target domains must not depend on how ranks divide the block.
 
static PetscErrorCode TestStatisticsCheckpointRoundTripMultiRank (void)
 Statistics payloads must survive a checkpoint round trip under decomposition.
 
static PetscErrorCode TestLESAveragingIsDecompositionIndependent (void)
 Tests that LES coefficient averaging does not depend on the decomposition.
 
int main (int argc, char **argv)
 Runs the unit-mpi PETSc test binary.
 

Detailed Description

C unit tests for MPI collective helper routines.

Definition in file test_mpi_kernels.c.

Function Documentation

◆ MarkXPeriodic()

static void MarkXPeriodic ( UserCtx *  user)
static

Marks both x boundary faces as geometric periodic boundaries.

Definition at line 24 of file test_mpi_kernels.c.

Here is the caller graph for this function:

◆ PeriodicScalarValue()

static PetscReal PeriodicScalarValue ( PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscReal  offset 
)
static

Returns a deterministic scalar value for periodic-transfer assertions.

Definition at line 37 of file test_mpi_kernels.c.

38{
39 return offset + (PetscReal)(i + 10 * j + 100 * k);
40}
Here is the caller graph for this function:

◆ TestDistributeParticlesCollectiveConsistency()

static PetscErrorCode TestDistributeParticlesCollectiveConsistency ( void  )
static

Tests collective particle distribution consistency across MPI ranks.

Definition at line 45 of file test_mpi_kernels.c.

46{
47 PetscMPIInt rank = 0, size = 1;
48 PetscInt local_particles = 0;
49 PetscInt remainder = 0;
50 PetscInt global_particles = 0;
51 PetscInt remainder_min = 0, remainder_max = 0;
52 const PetscInt total_particles = 137;
53
54 PetscFunctionBeginUser;
55 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
56 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
57 PetscCall(PicurvAssertBool((PetscBool)(size >= 2), "unit-mpi requires at least two MPI ranks"));
58
59 PetscCall(DistributeParticles(total_particles, rank, size, &local_particles, &remainder));
60
61 PetscCall(PicurvAssertIntEqual(
62 total_particles / size + (((PetscInt)rank < remainder) ? 1 : 0),
63 local_particles,
64 "local particle share should match quotient+remainder policy"));
65
66 PetscCallMPI(MPI_Allreduce(&local_particles, &global_particles, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
67 PetscCall(PicurvAssertIntEqual(total_particles, global_particles, "distributed particle count must conserve total particles"));
68
69 PetscCallMPI(MPI_Allreduce(&remainder, &remainder_min, 1, MPIU_INT, MPI_MIN, PETSC_COMM_WORLD));
70 PetscCallMPI(MPI_Allreduce(&remainder, &remainder_max, 1, MPIU_INT, MPI_MAX, PETSC_COMM_WORLD));
71 PetscCall(PicurvAssertIntEqual(remainder_min, remainder_max, "all ranks should report the same remainder"));
72 PetscFunctionReturn(0);
73}
PetscErrorCode DistributeParticles(PetscInt numParticles, PetscMPIInt rank, PetscMPIInt size, PetscInt *particlesPerProcess, PetscInt *remainder)
Distributes particles evenly across MPI processes, handling any remainders.
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.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestBoundingBoxCollectivesMultiRank()

static PetscErrorCode TestBoundingBoxCollectivesMultiRank ( void  )
static

Tests multi-rank bounding-box gather and broadcast helpers.

Definition at line 78 of file test_mpi_kernels.c.

79{
80 SimCtx *simCtx = NULL;
81 UserCtx *user = NULL;
82 BoundingBox local_bbox;
83 BoundingBox *boxes = NULL;
84 PetscMPIInt rank = 0, size = 1;
85 PetscReal global_min_x = 0.0;
86 PetscReal global_max_x = 0.0;
87 PetscReal expected_global_max_x = 0.0;
88
89 PetscFunctionBeginUser;
90 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
91 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
92 PetscCall(PicurvAssertBool((PetscBool)(size >= 2), "unit-mpi requires at least two MPI ranks"));
93
94 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 8, 6, 4));
95 expected_global_max_x = ((PetscReal)(user->IM - 1) / (PetscReal)user->IM) + 1.0e-6;
96 PetscCall(ComputeLocalBoundingBox(user, &local_bbox));
97 PetscCall(GatherAllBoundingBoxes(user, &boxes));
98 PetscCall(BroadcastAllBoundingBoxes(user, &boxes));
99 PetscCall(PicurvAssertBool((PetscBool)(boxes != NULL), "all ranks should hold the gathered bounding-box table"));
100
101 for (PetscMPIInt r = 0; r < size; ++r) {
102 PetscCall(PicurvAssertBool((PetscBool)(boxes[r].min_coords.x <= boxes[r].max_coords.x), "bbox x-range should be valid"));
103 PetscCall(PicurvAssertBool((PetscBool)(boxes[r].min_coords.y <= boxes[r].max_coords.y), "bbox y-range should be valid"));
104 PetscCall(PicurvAssertBool((PetscBool)(boxes[r].min_coords.z <= boxes[r].max_coords.z), "bbox z-range should be valid"));
105 }
106
107 PetscCall(PicurvAssertRealNear(local_bbox.min_coords.x, boxes[rank].min_coords.x, 1.0e-10, "local bbox min x preserved"));
108 PetscCall(PicurvAssertRealNear(local_bbox.max_coords.x, boxes[rank].max_coords.x, 1.0e-10, "local bbox max x preserved"));
109
110 PetscCallMPI(MPI_Allreduce(&local_bbox.min_coords.x, &global_min_x, 1, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD));
111 PetscCallMPI(MPI_Allreduce(&local_bbox.max_coords.x, &global_max_x, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD));
112 PetscCall(PicurvAssertBool((PetscBool)(global_min_x <= 0.0), "global min x should include domain start"));
113 PetscCall(PicurvAssertRealNear(expected_global_max_x, global_max_x, 1.0e-10, "global max x should match the normalized physical-node domain end"));
114
115 free(boxes);
116 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
117 PetscFunctionReturn(0);
118}
PetscErrorCode BroadcastAllBoundingBoxes(UserCtx *user, BoundingBox **bboxlist)
Broadcasts the bounding box information collected on rank 0 to all other ranks.
Definition grid.c:1061
PetscErrorCode ComputeLocalBoundingBox(UserCtx *user, BoundingBox *localBBox)
Computes the local bounding box of the grid on the current process.
Definition grid.c:850
PetscErrorCode GatherAllBoundingBoxes(UserCtx *user, BoundingBox **allBBoxes)
Gathers local bounding boxes from all MPI processes to rank 0.
Definition grid.c:999
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 PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
PetscErrorCode PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
Destroys minimal SimCtx/UserCtx fixtures and all owned PETSc objects.
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:199
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:198
PetscScalar x
Definition variables.h:122
PetscInt IM
Definition variables.h:1088
Defines a 3D axis-aligned bounding box.
Definition variables.h:197
The master context for the entire simulation.
Definition variables.h:859
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1074
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPeriodicCellFieldSynchronizationMultiRank()

static PetscErrorCode TestPeriodicCellFieldSynchronizationMultiRank ( void  )
static

Tests distributed synchronization of persistent cell-centered scalar fields.

Definition at line 123 of file test_mpi_kernels.c.

124{
125 SimCtx *simCtx = NULL;
126 UserCtx *user = NULL;
127 PetscReal ***p = NULL;
128 PetscReal ***cs = NULL;
129 PetscReal ***diffusivity = NULL;
130 Cmpnts ***ucat = NULL;
132 PetscInt xs, xe, ys, ye, zs, ze, mx;
133
134 PetscFunctionBeginUser;
135 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 8, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
136 MarkXPeriodic(user);
137 PetscCall(PicurvAssertBool((PetscBool)(user->info.xm < user->info.mx),
138 "periodic cell synchronization test requires the x axis to be partitioned"));
139 PetscCall(DMCreateGlobalVector(user->da, &user->CS));
140 PetscCall(DMCreateLocalVector(user->da, &user->lCs));
141
142 xs = user->info.xs;
143 xe = xs + user->info.xm;
144 ys = user->info.ys;
145 ye = ys + user->info.ym;
146 zs = user->info.zs;
147 ze = zs + user->info.zm;
148 mx = user->info.mx;
149
150 PetscCall(DMDAVecGetArray(user->da, user->P, &p));
151 PetscCall(DMDAVecGetArray(user->da, user->CS, &cs));
152 PetscCall(DMDAVecGetArray(user->da, user->Diffusivity, &diffusivity));
153 PetscCall(DMDAVecGetArray(user->fda, user->Ucat, &ucat));
154 for (PetscInt k = zs; k < ze; ++k) {
155 for (PetscInt j = ys; j < ye; ++j) {
156 for (PetscInt i = xs; i < xe; ++i) {
157 p[k][j][i] = PeriodicScalarValue(i, j, k, 0.0);
158 cs[k][j][i] = PeriodicScalarValue(i, j, k, 1000.0);
159 diffusivity[k][j][i] = PeriodicScalarValue(i, j, k, 2000.0);
160 ucat[k][j][i] = (Cmpnts){PeriodicScalarValue(i,j,k,3000.0),0.0,0.0};
161 }
162 }
163 }
164 PetscCall(DMDAVecRestoreArray(user->da, user->P, &p));
165 PetscCall(DMDAVecRestoreArray(user->da, user->CS, &cs));
166 PetscCall(DMDAVecRestoreArray(user->da, user->Diffusivity, &diffusivity));
167 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucat, &ucat));
168
169 PetscCall(SynchronizePeriodicCellFields(user, 4, fields));
170
171 PetscCall(DMDAVecGetArrayRead(user->da, user->P, &p));
172 PetscCall(DMDAVecGetArrayRead(user->da, user->CS, &cs));
173 PetscCall(DMDAVecGetArrayRead(user->da, user->Diffusivity, &diffusivity));
174 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucat, &ucat));
175 if (xs == 0) {
176 for (PetscInt k = zs; k < ze; ++k) {
177 for (PetscInt j = ys; j < ye; ++j) {
178 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(mx - 2, j, k, 0.0), p[k][j][0], 1.0e-12,
179 "distributed P negative endpoint should copy the opposite interior"));
180 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(mx - 2, j, k, 1000.0), cs[k][j][0], 1.0e-12,
181 "distributed CS negative endpoint should copy the opposite interior"));
182 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(mx - 2, j, k, 2000.0), diffusivity[k][j][0], 1.0e-12,
183 "distributed Diffusivity negative endpoint should copy the opposite interior"));
184 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(mx-2,j,k,3000.0),ucat[k][j][0].x,1e-12,
185 "distributed Ucat negative endpoint should copy the opposite interior"));
186 }
187 }
188 }
189 if (xe == mx) {
190 for (PetscInt k = zs; k < ze; ++k) {
191 for (PetscInt j = ys; j < ye; ++j) {
192 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(1, j, k, 0.0), p[k][j][mx - 1], 1.0e-12,
193 "distributed P positive endpoint should copy the opposite interior"));
194 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(1, j, k, 1000.0), cs[k][j][mx - 1], 1.0e-12,
195 "distributed CS positive endpoint should copy the opposite interior"));
196 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(1, j, k, 2000.0), diffusivity[k][j][mx - 1], 1.0e-12,
197 "distributed Diffusivity positive endpoint should copy the opposite interior"));
198 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(1,j,k,3000.0),ucat[k][j][mx-1].x,1e-12,
199 "distributed Ucat positive endpoint should copy the leading interior"));
200 }
201 }
202 }
203 PetscCall(DMDAVecRestoreArrayRead(user->da, user->P, &p));
204 PetscCall(DMDAVecRestoreArrayRead(user->da, user->CS, &cs));
205 PetscCall(DMDAVecRestoreArrayRead(user->da, user->Diffusivity, &diffusivity));
206 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucat, &ucat));
207 PetscCall(DMDAVecGetArrayRead(user->fda,user->lUcat,&ucat));
208 if(xs==0) PetscCall(PicurvAssertRealNear(PeriodicScalarValue(1,2,2,3000.0),ucat[2][2][-1].x,1e-12,
209 "distributed local Ucat negative ghost follows corrected global endpoint"));
210 if(xe==mx) PetscCall(PicurvAssertRealNear(PeriodicScalarValue(mx-2,2,2,3000.0),ucat[2][2][mx].x,1e-12,
211 "distributed local Ucat positive ghost follows corrected global endpoint"));
212 PetscCall(DMDAVecRestoreArrayRead(user->fda,user->lUcat,&ucat));
213
214 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
215 PetscFunctionReturn(0);
216}
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes periodic endpoint cells for a list of cell-centered fields.
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_UCAT
@ FIELD_ID_CS
@ FIELD_ID_P
@ FIELD_ID_DIFFUSIVITY
static void MarkXPeriodic(UserCtx *user)
Marks both x boundary faces as geometric periodic boundaries.
static PetscReal PeriodicScalarValue(PetscInt i, PetscInt j, PetscInt k, PetscReal offset)
Returns a deterministic scalar value for periodic-transfer assertions.
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.
Vec Diffusivity
Definition variables.h:1116
DMDALocalInfo info
Definition variables.h:1086
Vec lUcat
Definition variables.h:1113
A 3D point or vector with PetscScalar components.
Definition variables.h:121
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPeriodicGeometryValidationMultiRank()

static PetscErrorCode TestPeriodicGeometryValidationMultiRank ( void  )
static

Tests periodic seam translation discovery when seam nodes are on different ranks.

Definition at line 220 of file test_mpi_kernels.c.

221{
222 SimCtx *simCtx = NULL;
223 UserCtx *user = NULL;
224 PetscReal expected_translation = 0.0;
225
226 PetscFunctionBeginUser;
227 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 8, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
228 MarkXPeriodic(user);
229 PetscCall(PicurvAssertBool((PetscBool)(user->info.xm < user->info.mx),
230 "periodic geometry validation test requires the x axis to be partitioned"));
231
232 expected_translation = (PetscReal)(user->info.mx - 2) / (PetscReal)user->info.mx;
233 PetscCall(ValidatePeriodicGeometry(user));
234
236 "distributed X-periodic translation should be marked valid"));
237 PetscCall(PicurvAssertRealNear(expected_translation, user->periodic_translation[0].x, 1.0e-12,
238 "distributed X-periodic translation"));
239 PetscCall(PicurvAssertRealNear(0.0, user->periodic_translation[0].y, 1.0e-12,
240 "distributed X-periodic translation y component"));
241 PetscCall(PicurvAssertRealNear(0.0, user->periodic_translation[0].z, 1.0e-12,
242 "distributed X-periodic translation z component"));
243
244 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
245 PetscFunctionReturn(0);
246}
PetscErrorCode ValidatePeriodicGeometry(UserCtx *user)
Validates that configured geometric periodic seams match by translation.
Definition grid.c:421
PetscScalar z
Definition variables.h:122
PetscScalar y
Definition variables.h:122
Cmpnts periodic_translation[3]
Definition variables.h:1095
PetscBool periodic_translation_valid[3]
Definition variables.h:1096
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPeriodicFaceCenterCoordinatesMultiRank()

static PetscErrorCode TestPeriodicFaceCenterCoordinatesMultiRank ( void  )
static

Tests translated face-center ghosts when the periodic axis is partitioned.

Definition at line 251 of file test_mpi_kernels.c.

252{
253 SimCtx *simCtx = NULL;
254 UserCtx *user = NULL;
255 Cmpnts ***centx = NULL;
256 const Cmpnts ***lcentx = NULL;
257 const FieldId fields[] = {FIELD_ID_CENTX};
258 PetscReal translation, spacing;
259 PetscInt xs, xe, mx;
260
261 PetscFunctionBeginUser;
262 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 8, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
263 MarkXPeriodic(user);
264 PetscCall(ValidatePeriodicGeometry(user));
265 PetscCall(PicurvAssertBool((PetscBool)(user->info.xm < user->info.mx),
266 "face-center coordinate test requires the x axis to be partitioned"));
267 translation = user->periodic_translation[0].x;
268 spacing = translation / (PetscReal)(user->info.mx - 2);
269 xs = user->info.xs;
270 xe = xs + user->info.xm;
271 mx = user->info.mx;
272
273 PetscCall(DMDAVecGetArray(user->fda, user->Centx, &centx));
274 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; k++) {
275 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; j++) {
276 for (PetscInt i = xs; i < xe; i++) {
277 centx[k][j][i] = (Cmpnts){spacing * i, 2.0 + j, 3.0 + k};
278 }
279 }
280 }
281 PetscCall(DMDAVecRestoreArray(user->fda, user->Centx, &centx));
282
283 PetscCall(SynchronizePeriodicFaceFields(user, 'i', 1, fields));
284 PetscCall(DMDAVecGetArrayRead(user->fda, user->lCentx, &lcentx));
285 if (xs == 0) {
286 PetscCall(PicurvAssertRealNear(-spacing, lcentx[2][2][-1].x, 1.0e-12,
287 "distributed translated Centx negative adjacent ghost"));
288 PetscCall(PicurvAssertRealNear(0.0, lcentx[2][2][0].x, 1.0e-12,
289 "distributed translated Centx negative endpoint"));
290 }
291 if (xe == mx) {
292 PetscCall(PicurvAssertRealNear(translation + spacing, lcentx[2][2][mx - 1].x, 1.0e-12,
293 "distributed translated Centx positive endpoint"));
294 PetscCall(PicurvAssertRealNear(translation + 2.0 * spacing, lcentx[2][2][mx].x, 1.0e-12,
295 "distributed translated Centx positive adjacent ghost"));
296 }
297 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lCentx, &lcentx));
298
299 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
300 PetscFunctionReturn(0);
301}
PetscErrorCode SynchronizePeriodicFaceFields(UserCtx *user, char face_direction, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent fields belonging to one face family.
@ FIELD_ID_CENTX
Vec Centx
Definition variables.h:1149
Vec lCentx
Definition variables.h:1150
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPeriodicFaceFieldSynchronizationMultiRank()

static PetscErrorCode TestPeriodicFaceFieldSynchronizationMultiRank ( void  )
static

Tests persistent I-face synchronization across an MPI-partitioned seam.

Definition at line 305 of file test_mpi_kernels.c.

306{
307 SimCtx *simCtx = NULL;
308 UserCtx *user = NULL;
309 PetscReal ***iaj = NULL;
310 const FieldId fields[] = {FIELD_ID_IAJ};
311 PetscInt xs, xe, ys, ye, zs, ze, mx;
312
313 PetscFunctionBeginUser;
314 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 8, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
315 MarkXPeriodic(user);
316 PetscCall(PicurvAssertBool((PetscBool)(user->info.xm < user->info.mx),
317 "periodic face synchronization test requires the x axis to be partitioned"));
318
319 xs = user->info.xs; xe = xs + user->info.xm;
320 ys = user->info.ys; ye = ys + user->info.ym;
321 zs = user->info.zs; ze = zs + user->info.zm;
322 mx = user->info.mx;
323
324 PetscCall(DMDAVecGetArray(user->da, user->IAj, &iaj));
325 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) for (PetscInt i = xs; i < xe; i++) {
326 iaj[k][j][i] = PeriodicScalarValue(i, j, k, 3000.0);
327 }
328 PetscCall(DMDAVecRestoreArray(user->da, user->IAj, &iaj));
329
330 PetscCall(SynchronizePeriodicFaceFields(user, 'i', 1, fields));
331
332 PetscCall(DMDAVecGetArrayRead(user->da, user->IAj, &iaj));
333 if (xs == 0) {
334 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) {
335 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(mx - 2, j, k, 3000.0), iaj[k][j][0], 1.0e-12,
336 "distributed I-face negative seam should copy the opposite physical seam"));
337 }
338 }
339 if (xe == mx) {
340 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) {
341 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(1, j, k, 3000.0), iaj[k][j][mx - 1], 1.0e-12,
342 "distributed I-face positive dummy should copy the leading physical face"));
343 }
344 }
345 PetscCall(DMDAVecRestoreArrayRead(user->da, user->IAj, &iaj));
346
347 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
348 PetscFunctionReturn(0);
349}
@ FIELD_ID_IAJ
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestPeriodicStaggeredFieldSynchronizationMultiRank()

static PetscErrorCode TestPeriodicStaggeredFieldSynchronizationMultiRank ( void  )
static

Tests persistent Ucont synchronization across an MPI-partitioned seam.

Definition at line 353 of file test_mpi_kernels.c.

354{
355 SimCtx *simCtx = NULL;
356 UserCtx *user = NULL;
357 Cmpnts ***ucont = NULL;
358 const FieldId fields[] = {FIELD_ID_UCONT};
359 PetscInt xs, xe, ys, ye, zs, ze, mx;
360
361 PetscFunctionBeginUser;
362 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, 8, 4, 4, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE));
363 MarkXPeriodic(user);
364 PetscCall(PicurvAssertBool((PetscBool)(user->info.xm < user->info.mx),
365 "periodic staggered synchronization test requires the x axis to be partitioned"));
366
367 xs = user->info.xs; xe = xs + user->info.xm;
368 ys = user->info.ys; ye = ys + user->info.ym;
369 zs = user->info.zs; ze = zs + user->info.zm;
370 mx = user->info.mx;
371
372 PetscCall(DMDAVecGetArray(user->fda, user->Ucont, &ucont));
373 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) for (PetscInt i = xs; i < xe; i++) {
374 PetscReal value = PeriodicScalarValue(i, j, k, 0.0);
375 ucont[k][j][i] = (Cmpnts){value + 1000.0, value + 2000.0, value + 3000.0};
376 }
377 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucont, &ucont));
378
379 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, fields));
380
381 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucont, &ucont));
382 if (xs == 0) {
383 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) {
384 PetscReal expected = PeriodicScalarValue(mx - 2, j, k, 0.0);
385 PetscCall(PicurvAssertRealNear(expected + 1000.0, ucont[k][j][0].x, 1.0e-12,
386 "distributed Ucont.x negative seam"));
387 PetscCall(PicurvAssertRealNear(expected + 2000.0, ucont[k][j][0].y, 1.0e-12,
388 "distributed Ucont.y negative X endpoint"));
389 }
390 }
391 if (xe == mx) {
392 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) {
393 PetscReal expected = PeriodicScalarValue(1, j, k, 0.0);
394 PetscCall(PicurvAssertRealNear(expected + 2000.0, ucont[k][j][mx - 1].y, 1.0e-12,
395 "distributed Ucont.y positive X dummy"));
396 PetscCall(PicurvAssertRealNear(expected + 3000.0, ucont[k][j][mx - 1].z, 1.0e-12,
397 "distributed Ucont.z positive X dummy"));
398 }
399 }
400 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucont, &ucont));
401
402 PetscCall(DMDAVecGetArrayRead(user->fda, user->lUcont, &ucont));
403 if (xs == 0) {
404 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) {
405 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(mx - 3, j, k, 1000.0), ucont[k][j][-1].x, 1.0e-12,
406 "distributed Ucont.x negative adjacent ghost repair"));
407 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(1, j, k, 2000.0), ucont[k][j][-1].y, 1.0e-12,
408 "distributed Ucont.y tangential X wraparound"));
409 }
410 }
411 if (xe == mx) {
412 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) {
413 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(2, j, k, 1000.0), ucont[k][j][mx].x, 1.0e-12,
414 "distributed Ucont.x positive adjacent ghost repair"));
415 PetscCall(PicurvAssertRealNear(PeriodicScalarValue(mx - 2, j, k, 2000.0), ucont[k][j][mx].y, 1.0e-12,
416 "distributed Ucont.y positive tangential X wraparound"));
417 }
418 }
419 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lUcont, &ucont));
420
421 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
422 PetscFunctionReturn(0);
423}
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent component-staggered vector fields.
@ FIELD_ID_UCONT
Vec Ucont
Definition variables.h:1113
Vec lUcont
Definition variables.h:1113
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestRestartCellIdMigrationMovesParticleToOwner()

static PetscErrorCode TestRestartCellIdMigrationMovesParticleToOwner ( void  )
static

Tests restart fast-path migration using preloaded cell ownership metadata.

Definition at line 428 of file test_mpi_kernels.c.

429{
430 SimCtx *simCtx = NULL;
431 UserCtx *user = NULL;
432 RankCellInfo my_cell_info;
433 PetscMPIInt rank = 0, size = 1;
434 PetscInt *cell_ids = NULL;
435 PetscReal *positions = NULL;
436 PetscInt nlocal = 0;
437
438 PetscFunctionBeginUser;
439 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
440 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
441 PetscCall(PicurvAssertIntEqual(2, size, "restart migration unit test expects exactly two MPI ranks"));
442
443 PetscCall(PetscMemzero(&my_cell_info, sizeof(my_cell_info)));
444 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 8, 4, 4));
445 PetscCall(PicurvCreateSwarmPair(user, 1, "ske"));
446 PetscCall(GetOwnedCellRange(&user->info, 0, &my_cell_info.xs_cell, &my_cell_info.xm_cell));
447 PetscCall(GetOwnedCellRange(&user->info, 1, &my_cell_info.ys_cell, &my_cell_info.ym_cell));
448 PetscCall(GetOwnedCellRange(&user->info, 2, &my_cell_info.zs_cell, &my_cell_info.zm_cell));
449 PetscCall(PetscMalloc1(size, &user->RankCellInfoMap));
450 PetscCallMPI(MPI_Allgather(&my_cell_info, sizeof(RankCellInfo), MPI_BYTE,
451 user->RankCellInfoMap, sizeof(RankCellInfo), MPI_BYTE,
452 PETSC_COMM_WORLD));
453
454 PetscCall(DMSwarmGetField(user->swarm, "DMSwarm_CellID", NULL, NULL, (void **)&cell_ids));
455 PetscCall(DMSwarmGetField(user->swarm, "position", NULL, NULL, (void **)&positions));
456 if (rank == 0) {
457 cell_ids[0] = user->RankCellInfoMap[1].xs_cell;
458 cell_ids[1] = user->RankCellInfoMap[1].ys_cell;
459 cell_ids[2] = user->RankCellInfoMap[1].zs_cell;
460 positions[0] = 0.875;
461 positions[1] = 0.5;
462 positions[2] = 0.5;
463 } else {
464 cell_ids[0] = user->RankCellInfoMap[1].xs_cell;
465 cell_ids[1] = user->RankCellInfoMap[1].ys_cell;
466 cell_ids[2] = user->RankCellInfoMap[1].zs_cell;
467 positions[0] = 0.95;
468 positions[1] = 0.5;
469 positions[2] = 0.5;
470 }
471 PetscCall(DMSwarmRestoreField(user->swarm, "position", NULL, NULL, (void **)&positions));
472 PetscCall(DMSwarmRestoreField(user->swarm, "DMSwarm_CellID", NULL, NULL, (void **)&cell_ids));
473
474 PetscCall(MigrateRestartParticlesUsingCellID(user));
475 PetscCall(DMSwarmGetLocalSize(user->swarm, &nlocal));
476 PetscCall(PicurvAssertIntEqual((rank == 0) ? 0 : 2, nlocal,
477 "restart migration should move the foreign particle onto the owning rank"));
478
479 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
480 PetscFunctionReturn(0);
481}
PetscErrorCode MigrateRestartParticlesUsingCellID(UserCtx *user)
Fast-path migration for restart particles using preloaded Cell IDs.
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:2936
PetscErrorCode PicurvCreateSwarmPair(UserCtx *user, PetscInt nlocal, const char *post_field_name)
Creates matched solver and post-processing swarms for tests.
PetscInt ys_cell
Definition variables.h:230
PetscInt xs_cell
Definition variables.h:230
PetscInt zm_cell
Definition variables.h:231
PetscInt zs_cell
Definition variables.h:230
PetscInt xm_cell
Definition variables.h:231
RankCellInfo * RankCellInfoMap
Definition variables.h:1170
PetscInt ym_cell
Definition variables.h:231
A lean struct to hold the global cell ownership range for a single MPI rank.
Definition variables.h:229
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestSpatialTargetIsDecompositionIndependent()

static PetscErrorCode TestSpatialTargetIsDecompositionIndependent ( void  )
static

Spatial target domains must not depend on how ranks divide the block.

The global point count is a function of layout, global dimensions, and periodicity alone. Summing the per-rank counts must therefore reproduce the same analytic totals a single rank computes, with no point counted twice at a decomposition seam and none dropped.

Definition at line 490 of file test_mpi_kernels.c.

491{
492 SimCtx *simCtx = NULL;
493 UserCtx *user = NULL;
495 PetscInt local = 0, global = 0, rank_count = 0;
496 PetscMPIInt size = 0;
497 const PetscInt n = 6; /* DMDA is n + 1 = 7 per dimension */
498 const PetscInt cell = 5; /* cell-like span [1, 6) */
499 const PetscInt node = 6; /* node-like nonperiodic span [0, 6) */
500
501 PetscFunctionBeginUser;
502 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
503 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, n, n, n));
504
506 PetscCall(SpatialTargetPlanLocalPointCount(&plan, &local));
507 PetscCall(SpatialTargetPlanGlobalPointCount(&plan, PETSC_COMM_WORLD, &global));
508 PetscCall(PicurvAssertIntEqual(cell * cell * cell, global,
509 "cell-centered global count must be decomposition independent"));
510
511 /* Every rank must contribute a non-negative count, and the ranks together
512 * must contribute the whole domain exactly once. */
513 PetscCall(PicurvAssertBool((PetscBool)(local >= 0), "local point count must be non-negative"));
514 PetscCallMPI(MPI_Allreduce(&local, &rank_count, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
515 PetscCall(PicurvAssertIntEqual(global, rank_count,
516 "summed local counts must equal the reduced global count"));
517
519 PetscCall(SpatialTargetPlanGlobalPointCount(&plan, PETSC_COMM_WORLD, &global));
520 PetscCall(PicurvAssertIntEqual(node * cell * cell, global,
521 "I-face global count must be decomposition independent"));
522
524 PetscCall(SpatialTargetPlanGlobalPointCount(&plan, PETSC_COMM_WORLD, &global));
525 PetscCall(PicurvAssertIntEqual(node * node * node, global,
526 "node-centered global count must be decomposition independent"));
527
528 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
529
530 /* Fully periodic: duplicate planes must be dropped once globally, not once per rank. */
531 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(&simCtx, &user, n, n, n,
532 PETSC_TRUE, PETSC_TRUE, PETSC_TRUE));
534 PetscCall(SpatialTargetPlanGlobalPointCount(&plan, PETSC_COMM_WORLD, &global));
535 PetscCall(PicurvAssertIntEqual(cell * cell * cell, global,
536 "fully periodic node-centered count must drop duplicates exactly once"));
537 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
538 PetscFunctionReturn(0);
539}
@ FIELD_ID_CSI
@ FIELD_ID_COORDINATES
@ PICURV_STATISTICS_MASK_FLUID
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.
Resolved iteration domain for one field on one block.
A generic C-style linked list node for integers.
Definition variables.h:463
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestStatisticsCheckpointRoundTripMultiRank()

static PetscErrorCode TestStatisticsCheckpointRoundTripMultiRank ( void  )
static

Statistics payloads must survive a checkpoint round trip under decomposition.

The write path gathers each accumulator into natural ordering before it reaches disk, and the read path scatters it back. That pairing is where a decomposition bug would live, so this drives the whole cycle on more than one rank and requires an exact match: the format stores IEEE doubles, and natural ordering is defined to be independent of how ranks divide the block.

Definition at line 550 of file test_mpi_kernels.c.

551{
552 SimCtx *simCtx = NULL;
553 UserCtx *user = NULL;
554 PicurvWindow window;
555 PicurvWindowStorage storage;
556 PicurvWindowDefinition definition;
557 Vec *reference = NULL;
558 char tmpdir[PETSC_MAX_PATH_LEN];
559 PetscInt payload_count = 0;
560 PetscMPIInt size = 0;
561
562 PetscFunctionBeginUser;
563 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
564 PetscCall(PicurvAssertBool((PetscBool)(size >= 2), "unit-mpi requires at least two MPI ranks"));
565
566 memset(&definition, 0, sizeof(definition));
567 strncpy(definition.name, "production", PICURV_WINDOW_NAME_LENGTH - 1);
570 definition.step_cadence = 1;
571 definition.field_count = 2;
572 definition.fields[0].field_id = FIELD_ID_UCAT; definition.fields[0].want_second = PETSC_TRUE;
573 definition.fields[1].field_id = FIELD_ID_P; definition.fields[1].want_second = PETSC_TRUE;
574 definition.covariance_count = 1;
575 definition.covariances[0].first = FIELD_ID_UCAT;
576 definition.covariances[0].second = FIELD_ID_P;
577
578 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 6, 6, 6));
579 tmpdir[0] = '\0';
580 if (simCtx->rank == 0) PetscCall(PicurvMakeTempDir(tmpdir, sizeof(tmpdir)));
581 PetscCallMPI(MPI_Bcast(tmpdir, sizeof(tmpdir), MPI_CHAR, 0, PETSC_COMM_WORLD));
582 PetscCall(PetscStrncpy(simCtx->output_dir, tmpdir, sizeof(simCtx->output_dir)));
583 PetscCall(PetscStrncpy(simCtx->restart_dir, tmpdir, sizeof(simCtx->restart_dir)));
584 PetscCall(PicurvPopulateIdentityMetrics(user));
585 PetscCall(VecSet(user->Nvert, 0.0));
586
587 PetscCall(PicurvWindowInit(&window, &definition));
588 PetscCall(PicurvWindowStorageCreate(user, &definition, &storage));
589 simCtx->fieldStatisticsEnabled = PETSC_TRUE;
590 simCtx->fieldStatisticsWindowCount = 1;
591 simCtx->fieldStatisticsWindows = &window;
592 user->fieldStatisticsStorage = &storage;
593
594 /* A spatially varying field makes the assertion meaningful: a payload gathered
595 * or scattered through the wrong owner comes back permuted, which a uniform
596 * field could not reveal. */
597 for (PetscInt s = 0; s < 4; ++s) {
598 PetscBool accepted = PETSC_FALSE;
599 PetscReal weight = 0.0;
600 PetscReal ***p = NULL;
601 Cmpnts ***ucat = NULL;
602 const DMDALocalInfo info = user->info;
603
604 PetscCall(DMDAVecGetArray(user->da, user->P, &p));
605 PetscCall(DMDAVecGetArray(user->fda, user->Ucat, &ucat));
606 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k)
607 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j)
608 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) {
609 const PetscReal tag = (PetscReal)(100 * k + 10 * j + i);
610
611 p[k][j][i] = tag + 0.25 * (PetscReal)s;
612 ucat[k][j][i].x = tag;
613 ucat[k][j][i].y = tag + 1.0 + (PetscReal)s;
614 ucat[k][j][i].z = tag - 1.0;
615 }
616 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucat, &ucat));
617 PetscCall(DMDAVecRestoreArray(user->da, user->P, &p));
618
619 PetscCall(PicurvWindowOfferState(&window, s, 0.5 * (PetscReal)s, &accepted, &weight));
620 if (accepted) PetscCall(PicurvWindowAccumulate(user, &definition, &storage, weight));
621 }
622 PetscCall(PicurvAssertIntEqual(3, window.sample_count, "three intervals are represented"));
623
624 PetscCall(WriteCheckpointBundle(simCtx, "test"));
625
626 PetscCall(PicurvWindowStoragePayloadCount(&storage, &payload_count));
627 PetscCall(PetscCalloc1((size_t)payload_count, &reference));
628 for (PetscInt index = 0; index < payload_count; ++index) {
630
631 PetscCall(PicurvWindowStoragePayload(user, &definition, &storage, index, &payload));
632 PetscCall(VecDuplicate(payload.vec, &reference[index]));
633 PetscCall(VecCopy(payload.vec, reference[index]));
634 PetscCall(VecZeroEntries(payload.vec));
635 }
636 PetscCall(PicurvWindowInit(&window, &definition));
637
638 simCtx->fieldStatisticsContinue = PETSC_TRUE;
639 PetscCall(RestoreFieldStatisticsState(simCtx, simCtx->step));
640 PetscCall(PicurvAssertIntEqual(3, window.sample_count, "window bookkeeping survives on every rank"));
641 PetscCall(PicurvAssertRealNear(1.5, window.total_weight, 1.0e-12,
642 "physical-time weights survive on every rank"));
643
644 for (PetscInt index = 0; index < payload_count; ++index) {
646 PetscReal difference = 0.0;
647 char context[192];
648
649 PetscCall(PicurvWindowStoragePayload(user, &definition, &storage, index, &payload));
650 PetscCall(VecAXPY(reference[index], -1.0, payload.vec));
651 PetscCall(VecNorm(reference[index], NORM_INFINITY, &difference));
652 PetscCall(PetscSNPrintf(context, sizeof(context),
653 "payload '%s' round trips exactly across %d ranks",
654 payload.name, (int)size));
655 PetscCall(PicurvAssertBool((PetscBool)(difference == 0.0), context));
656 PetscCall(VecDestroy(&reference[index]));
657 }
658 PetscCall(PetscFree(reference));
659
660 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
661 if (simCtx->rank == 0) PetscCall(PicurvRemoveTempDir(tmpdir));
662 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
663 simCtx->fieldStatisticsEnabled = PETSC_FALSE;
664 simCtx->fieldStatisticsWindowCount = 0;
665 simCtx->fieldStatisticsWindows = NULL;
666 user->fieldStatisticsStorage = NULL;
667 PetscCall(PicurvWindowStorageDestroy(&storage));
668 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
669 PetscFunctionReturn(0);
670}
PetscErrorCode RestoreFieldStatisticsState(SimCtx *simCtx, PetscInt ti)
Restores field-statistics window state and accumulators from a checkpoint.
Definition io.c:1686
PetscErrorCode WriteCheckpointBundle(SimCtx *simCtx, const char *reason)
Write and atomically publish one complete checkpoint bundle.
Definition io.c:2554
Vec vec
Borrowed accumulator vector; never owned by the caller.
PetscErrorCode PicurvWindowStorageCreate(UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage)
Allocates the accumulator state one window owns on one block.
PetscErrorCode PicurvWindowStoragePayload(UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, PetscInt index, PicurvStatisticsPayload *payload)
Resolves one enumerated payload of a window's storage.
char name[96]
File basename, no extension.
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.
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.
Independent accumulator state for one window on one block.
PetscInt sample_count
PetscInt first
First member; must also appear in the field list.
PicurvWindowFieldRequest fields[16]
PicurvCadenceKind cadence_kind
PetscReal total_weight
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.
PetscErrorCode PicurvWindowInit(PicurvWindow *window, const PicurvWindowDefinition *definition)
Validates a definition and initializes a window to the pending state.
PetscErrorCode PicurvWindowOfferState(PicurvWindow *window, PetscInt step, PetscReal time, PetscBool *accepted, PetscReal *weight)
Offers one completed state to a window and reports the decision.
PetscBool want_second
Also keep the centered second moment.
PetscInt second
Second member; must also appear in the field list.
PicurvWindowCovarianceRequest covariances[16]
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.
Runtime state of one window.
The scientifically immutable definition of one window.
PetscErrorCode PicurvMakeTempDir(char *path, size_t path_len)
Creates a unique temporary directory for one test case.
PetscErrorCode PicurvPopulateIdentityMetrics(UserCtx *user)
Populates identity metric vectors on the minimal grid fixture.
PetscErrorCode PicurvRemoveTempDir(const char *path)
Recursively removes a temporary directory created by PicurvMakeTempDir.
PetscInt fieldStatisticsWindowCount
Definition variables.h:932
PetscMPIInt rank
Definition variables.h:862
char output_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:881
PetscBool fieldStatisticsEnabled
Definition variables.h:931
struct PicurvWindow * fieldStatisticsWindows
Definition variables.h:933
struct PicurvWindowStorage * fieldStatisticsStorage
Definition variables.h:1136
PetscInt step
Definition variables.h:867
Vec Nvert
Definition variables.h:1113
char restart_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:880
PetscBool fieldStatisticsContinue
Definition variables.h:938
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestLESAveragingIsDecompositionIndependent()

static PetscErrorCode TestLESAveragingIsDecompositionIndependent ( void  )
static

Tests that LES coefficient averaging does not depend on the decomposition.

The averaged coefficient is a physical property of the block, so splitting the same field across more ranks must not change it. This case catches a wrong communicator, a halo entry counted as if it were owned, or a layout boundary plane included in one decomposition but not another. The reference is computed from the analytic definition over the interior index box rather than from a single-rank run, so the test does not merely compare one decomposition against itself.

Definition at line 682 of file test_mpi_kernels.c.

683{
684 SimCtx *simCtx = NULL;
685 UserCtx *user = NULL;
686 Vec numerator, denominator, ratio;
688 PetscReal ***num = NULL, ***den = NULL, ***out = NULL;
689 DMDALocalInfo info;
690 PetscInt xs, xe, ys, ye, zs, ze;
691 PetscReal sampled = 0.0;
692 PetscReal numerator_total = 0.0, denominator_total = 0.0;
693 const PetscBool all[3] = {PETSC_TRUE, PETSC_TRUE, PETSC_TRUE};
694
695 PetscFunctionBeginUser;
696 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 8, 8, 8));
697 PetscCall(DMDAGetLocalInfo(user->da, &info));
698 PetscCall(VecSet(user->lNvert, 0.0));
699 PetscCall(VecSet(user->lAj, 1.0));
700
701 xs = (info.xs == 0) ? 1 : info.xs;
702 ys = (info.ys == 0) ? 1 : info.ys;
703 zs = (info.zs == 0) ? 1 : info.zs;
704 xe = (info.xs + info.xm == info.mx) ? info.mx - 1 : info.xs + info.xm;
705 ye = (info.ys + info.ym == info.my) ? info.my - 1 : info.ys + info.ym;
706 ze = (info.zs + info.zm == info.mz) ? info.mz - 1 : info.zs + info.zm;
707
708 PetscCall(VecDuplicate(user->lNvert, &numerator));
709 PetscCall(VecDuplicate(user->lNvert, &denominator));
710 PetscCall(VecDuplicate(user->lNvert, &ratio));
711 PetscCall(VecSet(numerator, 0.0));
712 PetscCall(VecSet(denominator, 0.0));
713 PetscCall(VecSet(ratio, 0.0));
714
715 /* Each rank writes only the cells it owns, from a closed-form function of the
716 global indices, so the distributed sums are exactly the serial ones below. */
717 PetscCall(DMDAVecGetArray(user->da, numerator, &num));
718 PetscCall(DMDAVecGetArray(user->da, denominator, &den));
719 for (PetscInt k = zs; k < ze; ++k)
720 for (PetscInt j = ys; j < ye; ++j)
721 for (PetscInt i = xs; i < xe; ++i) {
722 num[k][j][i] = (PetscReal)(i * j + k);
723 den[k][j][i] = (PetscReal)(i + 1);
724 }
725 PetscCall(DMDAVecRestoreArray(user->da, numerator, &num));
726 PetscCall(DMDAVecRestoreArray(user->da, denominator, &den));
727
728 PetscCall(SpatialTargetPlanCreate(user, FIELD_ID_CS,
730 PetscCall(PicurvSpatialRatioAverage(user, &plan, numerator, denominator, NULL, all,
731 PETSC_COMM_WORLD, ratio, NULL));
732
733 for (PetscInt k = 1; k < info.mz - 1; ++k)
734 for (PetscInt j = 1; j < info.my - 1; ++j)
735 for (PetscInt i = 1; i < info.mx - 1; ++i) {
736 numerator_total += (PetscReal)(i * j + k);
737 denominator_total += (PetscReal)(i + 1);
738 }
739
740 PetscCall(DMDAVecGetArray(user->da, ratio, &out));
741 sampled = out[zs][ys][xs];
742 PetscCall(DMDAVecRestoreArray(user->da, ratio, &out));
743
744 PetscCall(PicurvAssertRealNear(numerator_total / denominator_total, sampled, 1.0e-10,
745 "the globally averaged ratio must match the serial sums "
746 "however the block is decomposed"));
747
748 PetscCall(VecDestroy(&numerator));
749 PetscCall(VecDestroy(&denominator));
750 PetscCall(VecDestroy(&ratio));
751 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
752 PetscFunctionReturn(0);
753}
PetscErrorCode PicurvSpatialRatioAverage(UserCtx *user, const SpatialTargetPlan *plan, Vec numerator, Vec denominator, Vec inclusion, const PetscBool average_direction[3], MPI_Comm comm, Vec ratio, PetscReal *scalar)
Averages two fields over a target domain and divides the results.
Vec lNvert
Definition variables.h:1113
Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char **  argv 
)

Runs the unit-mpi PETSc test binary.

Definition at line 758 of file test_mpi_kernels.c.

759{
760 PetscErrorCode ierr;
761 const PicurvTestCase cases[] = {
762 {"distribute-particles-collective-consistency", TestDistributeParticlesCollectiveConsistency},
763 {"bounding-box-collectives-multi-rank", TestBoundingBoxCollectivesMultiRank},
764 {"periodic-cell-field-synchronization-multi-rank", TestPeriodicCellFieldSynchronizationMultiRank},
765 {"periodic-geometry-validation-multi-rank", TestPeriodicGeometryValidationMultiRank},
766 {"periodic-face-center-coordinates-multi-rank", TestPeriodicFaceCenterCoordinatesMultiRank},
767 {"periodic-face-field-synchronization-multi-rank", TestPeriodicFaceFieldSynchronizationMultiRank},
768 {"periodic-staggered-field-synchronization-multi-rank", TestPeriodicStaggeredFieldSynchronizationMultiRank},
769 {"spatial-target-decomposition-independent", TestSpatialTargetIsDecompositionIndependent},
770 {"statistics-checkpoint-round-trip-multi-rank", TestStatisticsCheckpointRoundTripMultiRank},
771 {"les-averaging-decomposition-independent", TestLESAveragingIsDecompositionIndependent},
772 {"restart-cellid-migration-moves-particle-to-owner", TestRestartCellIdMigrationMovesParticleToOwner},
773 };
774
775 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv MPI-focused runtime tests");
776 if (ierr) {
777 return (int)ierr;
778 }
779
780 ierr = PicurvRunTests("unit-mpi", cases, sizeof(cases) / sizeof(cases[0]));
781 if (ierr) {
782 PetscFinalize();
783 return (int)ierr;
784 }
785
786 ierr = PetscFinalize();
787 return (int)ierr;
788}
static PetscErrorCode TestSpatialTargetIsDecompositionIndependent(void)
Spatial target domains must not depend on how ranks divide the block.
static PetscErrorCode TestPeriodicFaceCenterCoordinatesMultiRank(void)
Tests translated face-center ghosts when the periodic axis is partitioned.
static PetscErrorCode TestPeriodicFaceFieldSynchronizationMultiRank(void)
Tests persistent I-face synchronization across an MPI-partitioned seam.
static PetscErrorCode TestRestartCellIdMigrationMovesParticleToOwner(void)
Tests restart fast-path migration using preloaded cell ownership metadata.
static PetscErrorCode TestLESAveragingIsDecompositionIndependent(void)
Tests that LES coefficient averaging does not depend on the decomposition.
static PetscErrorCode TestPeriodicGeometryValidationMultiRank(void)
Tests periodic seam translation discovery when seam nodes are on different ranks.
static PetscErrorCode TestStatisticsCheckpointRoundTripMultiRank(void)
Statistics payloads must survive a checkpoint round trip under decomposition.
static PetscErrorCode TestPeriodicStaggeredFieldSynchronizationMultiRank(void)
Tests persistent Ucont synchronization across an MPI-partitioned seam.
static PetscErrorCode TestPeriodicCellFieldSynchronizationMultiRank(void)
Tests distributed synchronization of persistent cell-centered scalar fields.
static PetscErrorCode TestDistributeParticlesCollectiveConsistency(void)
Tests collective particle distribution consistency across MPI ranks.
static PetscErrorCode TestBoundingBoxCollectivesMultiRank(void)
Tests multi-rank bounding-box gather and broadcast helpers.
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: