22 return (PetscBool)(PetscAbsReal(PetscRealPart(actual) - expected) <= 1.0e-12);
26static PetscBool
ReadBlock(FILE *file,
void *data,
size_t count,
size_t element_size)
28 uint32_t block_size = 0;
29 const size_t expected_size = count * element_size;
31 if (expected_size > UINT32_MAX)
return PETSC_FALSE;
32 if (fread(&block_size,
sizeof(block_size), 1, file) != 1 || block_size != (uint32_t)expected_size) {
35 return (PetscBool)(fread(data, element_size, count, file) == count);
52 PetscBool saw_piece = PETSC_FALSE;
53 PetscBool saw_velocity = PETSC_FALSE;
54 PetscBool saw_cell_id = PETSC_FALSE;
55 PetscBool saw_migration_status = PETSC_FALSE;
56 PetscBool saw_pid = PETSC_FALSE;
57 PetscBool saw_ske = PETSC_FALSE;
58 PetscBool valid = PETSC_FALSE;
59 size_t footer_size = 0;
61 file = fopen(path,
"rb");
62 if (!file)
return PETSC_FALSE;
64 while (fgets(line,
sizeof(line), file)) {
65 if (strstr(line,
"NumberOfPoints=\"4\"") && strstr(line,
"NumberOfVerts=\"4\"")) saw_piece = PETSC_TRUE;
66 if (strstr(line,
"Name=\"velocity\"") && strstr(line,
"NumberOfComponents=\"3\"")) saw_velocity = PETSC_TRUE;
67 if (strstr(line,
"Name=\"CellID\"") && strstr(line,
"NumberOfComponents=\"3\"")) saw_cell_id = PETSC_TRUE;
68 if (strstr(line,
"Name=\"Migration Status\"") && strstr(line,
"NumberOfComponents=\"1\"")) saw_migration_status = PETSC_TRUE;
69 if (strstr(line,
"Name=\"pid\"") && strstr(line,
"NumberOfComponents=\"1\"")) saw_pid = PETSC_TRUE;
70 if (strstr(line,
"Name=\"ske\"") && strstr(line,
"NumberOfComponents=\"1\"")) saw_ske = PETSC_TRUE;
71 if (strstr(line,
"<AppendedData encoding=\"raw\">"))
break;
73 if (!saw_piece || !saw_velocity || !saw_cell_id || !saw_migration_status || !saw_pid || !saw_ske ||
74 fgetc(file) !=
'_')
goto cleanup;
86 const PetscInt source = 2 * p;
88 if (!
ScalarMatches(coords[3 * p + 0], (PetscReal)source + 0.1) ||
91 !
ScalarMatches(velocity[3 * p + 0], (PetscReal)source + 10.0) ||
92 !
ScalarMatches(velocity[3 * p + 1], (PetscReal)source + 20.0) ||
93 !
ScalarMatches(velocity[3 * p + 2], (PetscReal)source + 30.0) ||
94 !
ScalarMatches(cell_id[3 * p + 0], (PetscReal)source + 100.0) ||
95 !
ScalarMatches(cell_id[3 * p + 1], (PetscReal)source + 200.0) ||
96 !
ScalarMatches(cell_id[3 * p + 2], (PetscReal)source + 300.0) ||
97 !
ScalarMatches(migration_status[p], (PetscReal)source + 400.0) ||
100 connectivity[p] != p || offsets[p] != p + 1)
goto cleanup;
103 footer_size = fread(footer, 1,
sizeof(footer) - 1, file);
104 footer[footer_size] =
'\0';
105 if (!strstr(footer,
"</AppendedData>") || !strstr(footer,
"</VTKFile>"))
goto cleanup;
117 PetscReal (*real3)[3] = NULL;
118 PetscInt (*int3)[3] = NULL;
119 PetscInt *int1 = NULL;
120 PetscInt64 *int64 = NULL;
121 PetscReal *real1 = NULL;
123 PetscFunctionBeginUser;
124 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void *)&real3));
125 for (PetscInt p = 0; p < nlocal; ++p) {
126 const PetscReal source = (PetscReal)(global_offset + p);
127 real3[p][0] = source + 0.1;
128 real3[p][1] = source + 0.2;
129 real3[p][2] = source + 0.3;
131 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void *)&real3));
133 PetscCall(DMSwarmGetField(user->
swarm,
"velocity", NULL, NULL, (
void *)&real3));
134 for (PetscInt p = 0; p < nlocal; ++p) {
135 const PetscReal source = (PetscReal)(global_offset + p);
136 real3[p][0] = source + 10.0;
137 real3[p][1] = source + 20.0;
138 real3[p][2] = source + 30.0;
140 PetscCall(DMSwarmRestoreField(user->
swarm,
"velocity", NULL, NULL, (
void *)&real3));
142 PetscCall(DMSwarmGetField(user->
swarm,
"DMSwarm_CellID", NULL, NULL, (
void *)&int3));
143 for (PetscInt p = 0; p < nlocal; ++p) {
144 const PetscInt source = global_offset + p;
145 int3[p][0] = source + 100;
146 int3[p][1] = source + 200;
147 int3[p][2] = source + 300;
149 PetscCall(DMSwarmRestoreField(user->
swarm,
"DMSwarm_CellID", NULL, NULL, (
void *)&int3));
151 PetscCall(DMSwarmGetField(user->
swarm,
"DMSwarm_location_status", NULL, NULL, (
void *)&int1));
152 for (PetscInt p = 0; p < nlocal; ++p) int1[p] = global_offset + p + 400;
153 PetscCall(DMSwarmRestoreField(user->
swarm,
"DMSwarm_location_status", NULL, NULL, (
void *)&int1));
155 PetscCall(DMSwarmGetField(user->
swarm,
"DMSwarm_pid", NULL, NULL, (
void *)&int64));
156 for (PetscInt p = 0; p < nlocal; ++p) int64[p] = (PetscInt64)(global_offset + p + 100000);
157 PetscCall(DMSwarmRestoreField(user->
swarm,
"DMSwarm_pid", NULL, NULL, (
void *)&int64));
159 PetscCall(DMSwarmGetField(user->
post_swarm,
"ske", NULL, NULL, (
void *)&real1));
160 for (PetscInt p = 0; p < nlocal; ++p) real1[p] = (PetscReal)(global_offset + p) + 0.5;
161 PetscCall(DMSwarmRestoreField(user->
post_swarm,
"ske", NULL, NULL, (
void *)&real1));
162 PetscFunctionReturn(0);
171 char vtk_path[PETSC_MAX_PATH_LEN];
172 char vtk_tmp_path[PETSC_MAX_PATH_LEN];
173 PetscMPIInt rank = 0, size = 1;
174 PetscInt nlocal = 0, global_offset = 0;
175 PetscMPIInt local_owner = -1, final_owner = -1;
176 PetscBool parsed = PETSC_FALSE;
177 PetscBool tmp_exists = PETSC_FALSE;
178 PetscBool prefix_set = PETSC_FALSE;
180 PetscFunctionBeginUser;
181 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
182 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
184 PetscCallMPI(MPI_Exscan(&nlocal, &global_offset, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
185 if (rank == 0) global_offset = 0;
187 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
191 PetscCall(PetscOptionsGetString(NULL, NULL,
"-vtk_test_output_prefix",
194 "the focused particle VTK test requires -vtk_test_output_prefix"));
196 PetscCall(PetscSNPrintf(vtk_tmp_path,
sizeof(vtk_tmp_path),
"%s.tmp", vtk_path));
199 PetscCallMPI(MPI_Allreduce(&local_owner, &final_owner, 1, MPI_INT, MPI_MAX, PETSC_COMM_WORLD));
201 "the final exported particle should have the expected owner"));
206 "position,velocity,CellID,Migration Status,pid,ske",
210 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
212 "multirank WriteParticleFile should emit one combined .vtp file"));
213 PetscCall(PetscTestFile(vtk_tmp_path,
'r', &tmp_exists));
215 "successful multirank particle VTK output should not leave a temporary file"));
218 PetscCallMPI(MPI_Bcast(&parsed, 1, MPIU_BOOL, 0, PETSC_COMM_WORLD));
220 "every coordinate, particle field, and vertex index in the generated VTK should match"));
223 PetscFunctionReturn(0);
234 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv multirank particle VTK tests");
235 if (ierr)
return (
int)ierr;
237 ierr =
PicurvRunTests(
"unit-post-particle-vtk-mpi", cases,
sizeof(cases) /
sizeof(cases[0]));
243 ierr = PetscFinalize();
PetscErrorCode WriteParticleFile(UserCtx *user, PostProcessParams *pps, PetscInt ti)
Writes particle data to a VTP file using the Prepare-Write-Cleanup pattern.
static PetscErrorCode TestWriteParticleFileCollectiveMultiRank(void)
Ensures particle fields are collectively gathered and written without corruption.
static PetscBool ScalarMatches(PetscScalar actual, PetscReal expected)
Compare one scalar from the raw VTK payload with its deterministic truth.
int main(int argc, char **argv)
Runs the focused serial/MPI particle VTK regression.
static PetscBool ReadBlock(FILE *file, void *data, size_t count, size_t element_size)
Read one length-prefixed raw appended-data block.
static PetscBool ValidateParticleVTKFile(const char *path)
Read and exhaustively validate every appended value in a generated VTP file.
static PetscErrorCode FillParticleFields(UserCtx *user, PetscInt nlocal, PetscInt global_offset)
Fill each local swarm entry from its rank-ordered global particle index.
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 PicurvAssertFileExists(const char *path, const char *context)
Asserts that a filesystem path exists as a readable file.
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.
char particle_output_prefix[256]
PetscInt particle_output_freq
char particle_fields[1024]
PetscBool outputParticles
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.