33 return offset + (PetscReal)(i + 10 * j + 100 * k);
41 PetscMPIInt rank = 0, size = 1;
42 PetscInt local_particles = 0;
43 PetscInt remainder = 0;
44 PetscInt global_particles = 0;
45 PetscInt remainder_min = 0, remainder_max = 0;
46 const PetscInt total_particles = 137;
48 PetscFunctionBeginUser;
49 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
50 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
51 PetscCall(
PicurvAssertBool((PetscBool)(size >= 2),
"unit-mpi requires at least two MPI ranks"));
56 total_particles / size + (((PetscInt)rank < remainder) ? 1 : 0),
58 "local particle share should match quotient+remainder policy"));
60 PetscCallMPI(MPI_Allreduce(&local_particles, &global_particles, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
61 PetscCall(
PicurvAssertIntEqual(total_particles, global_particles,
"distributed particle count must conserve total particles"));
63 PetscCallMPI(MPI_Allreduce(&remainder, &remainder_min, 1, MPIU_INT, MPI_MIN, PETSC_COMM_WORLD));
64 PetscCallMPI(MPI_Allreduce(&remainder, &remainder_max, 1, MPIU_INT, MPI_MAX, PETSC_COMM_WORLD));
65 PetscCall(
PicurvAssertIntEqual(remainder_min, remainder_max,
"all ranks should report the same remainder"));
66 PetscFunctionReturn(0);
78 PetscMPIInt rank = 0, size = 1;
79 PetscReal global_min_x = 0.0;
80 PetscReal global_max_x = 0.0;
81 PetscReal expected_global_max_x = 0.0;
83 PetscFunctionBeginUser;
84 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
85 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
86 PetscCall(
PicurvAssertBool((PetscBool)(size >= 2),
"unit-mpi requires at least two MPI ranks"));
89 expected_global_max_x = ((PetscReal)(user->
IM - 1) / (PetscReal)user->
IM) + 1.0e-6;
93 PetscCall(
PicurvAssertBool((PetscBool)(boxes != NULL),
"all ranks should hold the gathered bounding-box table"));
95 for (PetscMPIInt r = 0; r < size; ++r) {
104 PetscCallMPI(MPI_Allreduce(&local_bbox.
min_coords.
x, &global_min_x, 1, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD));
105 PetscCallMPI(MPI_Allreduce(&local_bbox.
max_coords.
x, &global_max_x, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD));
106 PetscCall(
PicurvAssertBool((PetscBool)(global_min_x <= 0.0),
"global min x should include domain start"));
107 PetscCall(
PicurvAssertRealNear(expected_global_max_x, global_max_x, 1.0e-10,
"global max x should match the normalized physical-node domain end"));
111 PetscFunctionReturn(0);
121 PetscReal ***p = NULL;
122 PetscReal ***cs = NULL;
123 PetscReal ***diffusivity = NULL;
125 const char *fields[] = {
"P",
"CS",
"Diffusivity",
"Ucat"};
126 PetscInt xs, xe, ys, ye, zs, ze, mx;
128 PetscFunctionBeginUser;
132 "periodic cell synchronization test requires the x axis to be partitioned"));
133 PetscCall(DMCreateGlobalVector(user->
da, &user->
CS));
134 PetscCall(DMCreateLocalVector(user->
da, &user->
lCs));
137 xe = xs + user->
info.xm;
139 ye = ys + user->
info.ym;
141 ze = zs + user->
info.zm;
144 PetscCall(DMDAVecGetArray(user->
da, user->
P, &p));
145 PetscCall(DMDAVecGetArray(user->
da, user->
CS, &cs));
146 PetscCall(DMDAVecGetArray(user->
da, user->
Diffusivity, &diffusivity));
147 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
148 for (PetscInt k = zs; k < ze; ++k) {
149 for (PetscInt j = ys; j < ye; ++j) {
150 for (PetscInt i = xs; i < xe; ++i) {
158 PetscCall(DMDAVecRestoreArray(user->
da, user->
P, &p));
159 PetscCall(DMDAVecRestoreArray(user->
da, user->
CS, &cs));
160 PetscCall(DMDAVecRestoreArray(user->
da, user->
Diffusivity, &diffusivity));
161 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
165 PetscCall(DMDAVecGetArrayRead(user->
da, user->
P, &p));
166 PetscCall(DMDAVecGetArrayRead(user->
da, user->
CS, &cs));
167 PetscCall(DMDAVecGetArrayRead(user->
da, user->
Diffusivity, &diffusivity));
168 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucat, &ucat));
170 for (PetscInt k = zs; k < ze; ++k) {
171 for (PetscInt j = ys; j < ye; ++j) {
173 "distributed P negative endpoint should copy the opposite interior"));
175 "distributed CS negative endpoint should copy the opposite interior"));
177 "distributed Diffusivity negative endpoint should copy the opposite interior"));
179 "distributed Ucat negative endpoint should copy the opposite interior"));
184 for (PetscInt k = zs; k < ze; ++k) {
185 for (PetscInt j = ys; j < ye; ++j) {
187 "distributed P positive endpoint should copy the opposite interior"));
189 "distributed CS positive endpoint should copy the opposite interior"));
191 "distributed Diffusivity positive endpoint should copy the opposite interior"));
193 "distributed Ucat positive endpoint should copy the leading interior"));
197 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
P, &p));
198 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
CS, &cs));
199 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
Diffusivity, &diffusivity));
200 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucat, &ucat));
201 PetscCall(DMDAVecGetArrayRead(user->
fda,user->
lUcat,&ucat));
203 "distributed local Ucat negative ghost follows corrected global endpoint"));
205 "distributed local Ucat positive ghost follows corrected global endpoint"));
206 PetscCall(DMDAVecRestoreArrayRead(user->
fda,user->
lUcat,&ucat));
209 PetscFunctionReturn(0);
218 PetscReal expected_translation = 0.0;
220 PetscFunctionBeginUser;
224 "periodic geometry validation test requires the x axis to be partitioned"));
226 expected_translation = (PetscReal)(user->
info.mx - 2) / (PetscReal)user->
info.mx;
230 "distributed X-periodic translation should be marked valid"));
232 "distributed X-periodic translation"));
234 "distributed X-periodic translation y component"));
236 "distributed X-periodic translation z component"));
239 PetscFunctionReturn(0);
250 const Cmpnts ***lcentx = NULL;
251 const char *fields[] = {
"Centx"};
252 PetscReal translation, spacing;
255 PetscFunctionBeginUser;
260 "face-center coordinate test requires the x axis to be partitioned"));
262 spacing = translation / (PetscReal)(user->
info.mx - 2);
264 xe = xs + user->
info.xm;
267 PetscCall(DMDAVecGetArray(user->
fda, user->
Centx, ¢x));
268 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; k++) {
269 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; j++) {
270 for (PetscInt i = xs; i < xe; i++) {
271 centx[k][j][i] = (
Cmpnts){spacing * i, 2.0 + j, 3.0 + k};
275 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Centx, ¢x));
278 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
lCentx, &lcentx));
281 "distributed translated Centx negative adjacent ghost"));
283 "distributed translated Centx negative endpoint"));
287 "distributed translated Centx positive endpoint"));
289 "distributed translated Centx positive adjacent ghost"));
291 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
lCentx, &lcentx));
294 PetscFunctionReturn(0);
303 PetscReal ***iaj = NULL;
304 const char *fields[] = {
"IAj"};
305 PetscInt xs, xe, ys, ye, zs, ze, mx;
307 PetscFunctionBeginUser;
311 "periodic face synchronization test requires the x axis to be partitioned"));
313 xs = user->
info.xs; xe = xs + user->
info.xm;
314 ys = user->
info.ys; ye = ys + user->
info.ym;
315 zs = user->
info.zs; ze = zs + user->
info.zm;
318 PetscCall(DMDAVecGetArray(user->
da, user->
IAj, &iaj));
319 for (PetscInt k = zs; k < ze; k++)
for (PetscInt j = ys; j < ye; j++)
for (PetscInt i = xs; i < xe; i++) {
322 PetscCall(DMDAVecRestoreArray(user->
da, user->
IAj, &iaj));
326 PetscCall(DMDAVecGetArrayRead(user->
da, user->
IAj, &iaj));
328 for (PetscInt k = zs; k < ze; k++)
for (PetscInt j = ys; j < ye; j++) {
330 "distributed I-face negative seam should copy the opposite physical seam"));
334 for (PetscInt k = zs; k < ze; k++)
for (PetscInt j = ys; j < ye; j++) {
336 "distributed I-face positive dummy should copy the leading physical face"));
339 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
IAj, &iaj));
342 PetscFunctionReturn(0);
352 const char *fields[] = {
"Ucont"};
353 PetscInt xs, xe, ys, ye, zs, ze, mx;
355 PetscFunctionBeginUser;
359 "periodic staggered synchronization test requires the x axis to be partitioned"));
361 xs = user->
info.xs; xe = xs + user->
info.xm;
362 ys = user->
info.ys; ye = ys + user->
info.ym;
363 zs = user->
info.zs; ze = zs + user->
info.zm;
366 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucont, &ucont));
367 for (PetscInt k = zs; k < ze; k++)
for (PetscInt j = ys; j < ye; j++)
for (PetscInt i = xs; i < xe; i++) {
369 ucont[k][j][i] = (
Cmpnts){value + 1000.0, value + 2000.0, value + 3000.0};
371 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucont, &ucont));
375 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
377 for (PetscInt k = zs; k < ze; k++)
for (PetscInt j = ys; j < ye; j++) {
380 "distributed Ucont.x negative seam"));
382 "distributed Ucont.y negative X endpoint"));
386 for (PetscInt k = zs; k < ze; k++)
for (PetscInt j = ys; j < ye; j++) {
389 "distributed Ucont.y positive X dummy"));
391 "distributed Ucont.z positive X dummy"));
394 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
396 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
lUcont, &ucont));
398 for (PetscInt k = zs; k < ze; k++)
for (PetscInt j = ys; j < ye; j++) {
400 "distributed Ucont.x negative adjacent ghost repair"));
402 "distributed Ucont.y tangential X wraparound"));
406 for (PetscInt k = zs; k < ze; k++)
for (PetscInt j = ys; j < ye; j++) {
408 "distributed Ucont.x positive adjacent ghost repair"));
410 "distributed Ucont.y positive tangential X wraparound"));
413 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
lUcont, &ucont));
416 PetscFunctionReturn(0);
427 PetscMPIInt rank = 0, size = 1;
428 PetscInt *cell_ids = NULL;
429 PetscReal *positions = NULL;
432 PetscFunctionBeginUser;
433 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
434 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
435 PetscCall(
PicurvAssertIntEqual(2, size,
"restart migration unit test expects exactly two MPI ranks"));
437 PetscCall(PetscMemzero(&my_cell_info,
sizeof(my_cell_info)));
444 PetscCallMPI(MPI_Allgather(&my_cell_info,
sizeof(
RankCellInfo), MPI_BYTE,
448 PetscCall(DMSwarmGetField(user->
swarm,
"DMSwarm_CellID", NULL, NULL, (
void **)&cell_ids));
449 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void **)&positions));
454 positions[0] = 0.875;
465 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void **)&positions));
466 PetscCall(DMSwarmRestoreField(user->
swarm,
"DMSwarm_CellID", NULL, NULL, (
void **)&cell_ids));
469 PetscCall(DMSwarmGetLocalSize(user->
swarm, &nlocal));
471 "restart migration should move the foreign particle onto the owning rank"));
474 PetscFunctionReturn(0);
494 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv MPI-focused runtime tests");
499 ierr =
PicurvRunTests(
"unit-mpi", cases,
sizeof(cases) /
sizeof(cases[0]));
505 ierr = PetscFinalize();
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const char *field_names[])
Synchronizes persistent component-staggered vector fields.
PetscErrorCode SynchronizePeriodicFaceFields(UserCtx *user, char face_direction, PetscInt num_fields, const char *field_names[])
Synchronizes persistent fields belonging to one face family.
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const char *field_names[])
Synchronizes periodic endpoint cells for a list of cell-centered fields.
Header file for Particle Motion and migration related functions.
PetscErrorCode MigrateRestartParticlesUsingCellID(UserCtx *user)
Fast-path migration for restart particles using preloaded Cell IDs.
Header file for Particle Swarm management functions.
PetscErrorCode DistributeParticles(PetscInt numParticles, PetscMPIInt rank, PetscMPIInt size, PetscInt *particlesPerProcess, PetscInt *remainder)
Distributes particles evenly across MPI processes, handling any remainders.
Public interface for grid, solver, and metric setup routines.
PetscErrorCode BroadcastAllBoundingBoxes(UserCtx *user, BoundingBox **bboxlist)
Broadcasts the bounding box information collected on rank 0 to all other ranks.
PetscErrorCode ValidatePeriodicGeometry(UserCtx *user)
Validates that configured geometric periodic seams match by translation.
PetscErrorCode ComputeLocalBoundingBox(UserCtx *user, BoundingBox *localBBox)
Computes the local bounding box of the grid on the current process.
PetscErrorCode GatherAllBoundingBoxes(UserCtx *user, BoundingBox **allBBoxes)
Gathers local bounding boxes from all MPI processes to rank 0.
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...
static void MarkXPeriodic(UserCtx *user)
Marks both x boundary faces as geometric periodic boundaries.
static PetscErrorCode TestPeriodicFaceCenterCoordinatesMultiRank(void)
Tests translated face-center ghosts when the periodic axis is partitioned.
int main(int argc, char **argv)
Runs the unit-mpi PETSc test binary.
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 TestPeriodicGeometryValidationMultiRank(void)
Tests periodic seam translation discovery when seam nodes are on different ranks.
static PetscErrorCode TestPeriodicStaggeredFieldSynchronizationMultiRank(void)
Tests persistent Ucont synchronization across an MPI-partitioned seam.
static PetscReal PeriodicScalarValue(PetscInt i, PetscInt j, PetscInt k, PetscReal offset)
Returns a deterministic scalar value for periodic-transfer assertions.
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 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.
PetscErrorCode PicurvCreateMinimalContextsWithPeriodicity(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz, PetscBool x_periodic, PetscBool y_periodic, PetscBool z_periodic)
Builds minimal SimCtx and UserCtx fixtures for C unit tests with configurable periodicity.
PetscErrorCode 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.
BoundaryFaceConfig boundary_faces[6]
@ BC_HANDLER_PERIODIC_GEOMETRIC
BCHandlerType handler_type
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
RankCellInfo * RankCellInfoMap
Cmpnts periodic_translation[3]
PetscBool periodic_translation_valid[3]
Defines a 3D axis-aligned bounding box.
A 3D point or vector with PetscScalar components.
A lean struct to hold the global cell ownership range for a single MPI rank.
The master context for the entire simulation.
User-defined context containing data specific to a single computational grid level.