22 PetscBool exists = PETSC_FALSE;
24 PetscFunctionBeginUser;
25 PetscCall(PetscTestFile(path,
'r', &exists));
27 PetscCall(PetscPrintf(PETSC_COMM_WORLD,
"[FAIL] %s | unexpected file: %s\n", context, path));
28 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Unexpected file is present.");
30 PetscFunctionReturn(0);
46 PetscFunctionBeginUser;
47 PetscCheck(count_out != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"count_out must not be NULL.");
48 file = fopen(path,
"r");
49 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to open %s.", path);
50 while (fgets(line,
sizeof(line), file)) {
51 if (strncmp(line, prefix, strlen(prefix)) == 0) count++;
55 PetscFunctionReturn(0);
67 void *field_ptr = NULL;
69 PetscFunctionBeginUser;
70 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
76 "SetupPostProcessSwarm should allocate a dedicated post swarm"));
78 PetscCall(DMSwarmSetLocalSizes(user->
post_swarm, 0, 0));
79 PetscCall(DMSwarmGetField(user->
post_swarm,
"ske", &bs, NULL, &field_ptr));
80 PetscCall(
PicurvAssertIntEqual(1, bs,
"SetupPostProcessSwarm should register scalar output field 'ske'"));
81 PetscCall(DMSwarmRestoreField(user->
post_swarm,
"ske", &bs, NULL, &field_ptr));
84 PetscFunctionReturn(0);
95 const PetscScalar ***p_nodal_arr = NULL;
97 PetscFunctionBeginUser;
98 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
102 PetscCall(VecSet(user->
P, 9.0));
103 PetscCall(VecSet(user->
P_nodal, -1.0));
106 PetscCall(DMDAVecGetArrayRead(user->
da, user->
P_nodal, (
void *)&p_nodal_arr));
108 "Eulerian pipeline should execute CellToNodeAverage for P->P_nodal"));
110 "Eulerian pipeline should leave the extra non-physical boundary node unchanged"));
111 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
P_nodal, (
void *)&p_nodal_arr));
114 PetscFunctionReturn(0);
125 PetscReal (*vel_arr)[3] = NULL;
126 PetscScalar *ske_arr = NULL;
128 PetscFunctionBeginUser;
129 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
134 PetscCall(DMSwarmGetField(user->
swarm,
"velocity", NULL, NULL, (
void *)&vel_arr));
141 PetscCall(DMSwarmRestoreField(user->
swarm,
"velocity", NULL, NULL, (
void *)&vel_arr));
144 PetscCall(DMSwarmGetField(user->
post_swarm,
"ske", NULL, NULL, (
void *)&ske_arr));
146 "Particle pipeline should write first specific kinetic energy value"));
148 "Particle pipeline should write second specific kinetic energy value"));
149 PetscCall(DMSwarmRestoreField(user->
post_swarm,
"ske", NULL, NULL, (
void *)&ske_arr));
152 PetscFunctionReturn(0);
163 char tmpdir[PETSC_MAX_PATH_LEN];
164 char csv_prefix[PETSC_MAX_PATH_LEN];
165 char csv_path[PETSC_MAX_PATH_LEN];
166 PetscReal (*pos_arr)[3] = NULL;
168 PetscFunctionBeginUser;
169 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
181 PetscCall(PetscSNPrintf(csv_prefix,
sizeof(csv_prefix),
"%s/stats", tmpdir));
182 PetscCall(PetscSNPrintf(csv_path,
sizeof(csv_path),
"%s_msd.csv", csv_prefix));
187 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
191 pos_arr[1][0] = -1.0;
194 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
197 PetscCall(
PicurvAssertFileExists(csv_path,
"GlobalStatisticsPipeline should dispatch ComputeMSD and emit CSV output"));
201 PetscFunctionReturn(0);
212 char tmpdir[PETSC_MAX_PATH_LEN];
213 char csv_prefix[PETSC_MAX_PATH_LEN];
214 char csv_path[PETSC_MAX_PATH_LEN];
215 char csv_tmp_path[PETSC_MAX_PATH_LEN];
216 PetscReal (*pos_arr)[3] = NULL;
217 PetscInt row_count = 0;
219 PetscFunctionBeginUser;
220 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
232 PetscCall(PetscSNPrintf(csv_prefix,
sizeof(csv_prefix),
"%s/stats", tmpdir));
233 PetscCall(PetscSNPrintf(csv_path,
sizeof(csv_path),
"%s_msd.csv", csv_prefix));
234 PetscCall(PetscSNPrintf(csv_tmp_path,
sizeof(csv_tmp_path),
"%s.tmp", csv_path));
239 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
243 pos_arr[1][0] = -1.0;
246 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
249 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
253 pos_arr[1][0] = -2.0;
256 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
261 "Reprocessing the same MSD step should keep one CSV row for that step"));
263 "Reprocessing the same MSD step should not leave a temporary CSV artifact behind"));
267 PetscFunctionReturn(0);
278 char tmpdir[PETSC_MAX_PATH_LEN];
279 char post_path[PETSC_MAX_PATH_LEN];
280 char source_dir[PETSC_MAX_PATH_LEN];
281 char stats_dir[PETSC_MAX_PATH_LEN];
282 char stats_prefix[PETSC_MAX_PATH_LEN];
283 char sentinel_path[PETSC_MAX_PATH_LEN];
284 PetscBool exists = PETSC_FALSE;
287 PetscFunctionBeginUser;
289 PetscCall(PetscNew(&simCtx->
pps));
291 PetscCall(PetscMemzero(pps,
sizeof(*pps)));
294 PetscCall(PetscSNPrintf(post_path,
sizeof(post_path),
"%s/post.run", tmpdir));
295 file = fopen(post_path,
"w");
296 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to create post config %s.", post_path);
297 fputs(
"startTime = 0\nendTime = 0\ntimeStep = 1\n", file);
301 PetscCall(PetscSNPrintf(source_dir,
sizeof(source_dir),
"%s/output", tmpdir));
303 PetscCall(PetscSNPrintf(stats_dir,
sizeof(stats_dir),
"%s/output/statistics", tmpdir));
304 PetscCall(PetscSNPrintf(stats_prefix,
sizeof(stats_prefix),
"%s/BrownianStats", stats_dir));
305 PetscCall(PetscSNPrintf(sentinel_path,
sizeof(sentinel_path),
"%s/sentinel.keep", stats_dir));
316 PetscCall(PetscTestDirectory(stats_dir,
'r', &exists));
318 "SetupSimulationEnvironment should create the statistics directory when it is missing"));
320 file = fopen(sentinel_path,
"w");
321 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to create statistics sentinel %s.", sentinel_path);
322 fputs(
"keep\n", file);
328 "SetupSimulationEnvironment should preserve existing statistics directory contents on repeat runs"));
332 PetscFunctionReturn(0);
343 char tmpdir[PETSC_MAX_PATH_LEN];
344 char vtk_path[PETSC_MAX_PATH_LEN];
346 PetscFunctionBeginUser;
347 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
350 PetscCall(PetscSNPrintf(vtk_path,
sizeof(vtk_path),
"%s/field_00003.vts", tmpdir));
354 PetscCall(VecSet(user->
P_nodal, 7.0));
357 PetscCall(
PicurvAssertFileExists(vtk_path,
"WriteEulerianFile should emit a .vts file for requested output fields"));
361 PetscFunctionReturn(0);
372 char tmpdir[PETSC_MAX_PATH_LEN];
373 char vtk_path[PETSC_MAX_PATH_LEN];
374 PetscReal (*pos_arr)[3] = NULL;
375 PetscReal (*vel_arr)[3] = NULL;
377 PetscFunctionBeginUser;
378 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
382 PetscCall(PetscSNPrintf(vtk_path,
sizeof(vtk_path),
"%s/particles_00004.vtp", tmpdir));
389 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
390 PetscCall(DMSwarmGetField(user->
swarm,
"velocity", NULL, NULL, (
void *)&vel_arr));
391 for (PetscInt p = 0; p < 3; ++p) {
392 pos_arr[p][0] = (PetscReal)p;
393 pos_arr[p][1] = (PetscReal)(p + 1);
394 pos_arr[p][2] = (PetscReal)(p + 2);
395 vel_arr[p][0] = 2.0 * (PetscReal)p;
396 vel_arr[p][1] = 3.0 * (PetscReal)p;
397 vel_arr[p][2] = 4.0 * (PetscReal)p;
399 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
400 PetscCall(DMSwarmRestoreField(user->
swarm,
"velocity", NULL, NULL, (
void *)&vel_arr));
403 PetscCall(
PicurvAssertFileExists(vtk_path,
"WriteParticleFile should emit a .vtp file for requested particle fields"));
407 PetscFunctionReturn(0);
417 char tmpdir[PETSC_MAX_PATH_LEN];
418 char vtk_path[PETSC_MAX_PATH_LEN];
419 char vtk_tmp_path[PETSC_MAX_PATH_LEN];
421 PetscFunctionBeginUser;
422 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
425 PetscCall(PetscSNPrintf(vtk_path,
sizeof(vtk_path),
"%s/field_00003.vts", tmpdir));
426 PetscCall(PetscSNPrintf(vtk_tmp_path,
sizeof(vtk_tmp_path),
"%s.tmp", vtk_path));
430 PetscCall(VecSet(user->
P_nodal, 7.0));
433 PetscCall(VecSet(user->
P_nodal, 9.0));
437 "Rewriting the same Eulerian step should leave the final .vts file present"));
439 "Rewriting the same Eulerian step should not leave a temporary .vts file behind"));
443 PetscFunctionReturn(0);
457 char tmpdir[PETSC_MAX_PATH_LEN];
458 char vtk_path[PETSC_MAX_PATH_LEN];
459 char vtk_tmp_path[PETSC_MAX_PATH_LEN];
460 PetscReal (*pos_arr)[3] = NULL;
461 PetscReal (*vel_arr)[3] = NULL;
463 PetscFunctionBeginUser;
464 PetscCall(PetscMemzero(&pps,
sizeof(pps)));
468 PetscCall(PetscSNPrintf(vtk_path,
sizeof(vtk_path),
"%s/particles_00004.vtp", tmpdir));
469 PetscCall(PetscSNPrintf(vtk_tmp_path,
sizeof(vtk_tmp_path),
"%s.tmp", vtk_path));
476 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
477 PetscCall(DMSwarmGetField(user->
swarm,
"velocity", NULL, NULL, (
void *)&vel_arr));
478 for (PetscInt p = 0; p < 3; ++p) {
479 pos_arr[p][0] = (PetscReal)p;
480 pos_arr[p][1] = (PetscReal)(p + 1);
481 pos_arr[p][2] = (PetscReal)(p + 2);
482 vel_arr[p][0] = 2.0 * (PetscReal)p;
483 vel_arr[p][1] = 3.0 * (PetscReal)p;
484 vel_arr[p][2] = 4.0 * (PetscReal)p;
486 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
487 PetscCall(DMSwarmRestoreField(user->
swarm,
"velocity", NULL, NULL, (
void *)&vel_arr));
493 "Rewriting the same particle step should leave the final .vtp file present"));
495 "Rewriting the same particle step should not leave a temporary .vtp file behind"));
499 PetscFunctionReturn(0);
522 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv postprocessor tests");
527 ierr =
PicurvRunTests(
"unit-postprocessor", cases,
sizeof(cases) /
sizeof(cases[0]));
533 ierr = PetscFinalize();
PetscErrorCode EulerianDataProcessingPipeline(UserCtx *user, PostProcessParams *pps)
Parses the processing pipeline string and executes the requested kernels.
PetscErrorCode WriteEulerianFile(UserCtx *user, PostProcessParams *pps, PetscInt ti)
Orchestrates the writing of a combined, multi-field VTK file for a single time step.
PetscErrorCode GlobalStatisticsPipeline(UserCtx *user, PostProcessParams *pps, PetscInt ti)
Executes the global statistics pipeline, computing aggregate reductions over all particles.
PetscErrorCode ParticleDataProcessingPipeline(UserCtx *user, PostProcessParams *pps)
Parses and executes the particle pipeline using a robust two-pass approach.
PetscErrorCode WriteParticleFile(UserCtx *user, PostProcessParams *pps, PetscInt ti)
Writes particle data to a VTP file using the Prepare-Write-Cleanup pattern.
PetscErrorCode SetupPostProcessSwarm(UserCtx *user, PostProcessParams *pps)
Creates a new, dedicated DMSwarm for post-processing tasks.
PetscErrorCode SetupSimulationEnvironment(SimCtx *simCtx)
Verifies and prepares the complete I/O environment for a simulation run.
static PetscErrorCode CountLinesWithPrefix(const char *path, const char *prefix, PetscInt *count_out)
Count file lines that begin with a given prefix.
static PetscErrorCode TestWriteParticleFileWritesVTP(void)
Tests VTP emission for particle post-processing output.
static PetscErrorCode TestSetupSimulationEnvironmentCreatesStatisticsDirectoryWithoutClobbering(void)
Tests that postprocessor setup creates nested statistics directories without clobbering existing cont...
int main(int argc, char **argv)
Runs the unit-postprocessor PETSc test binary.
static PetscErrorCode PicurvAssertFileMissing(const char *path, const char *context)
Assert that a file path does not exist.
static PetscErrorCode TestParticleDataProcessingPipelineComputesSpecificKE(void)
Tests particle post-processing pipeline dispatch for specific-KE output.
static PetscErrorCode TestGlobalStatisticsPipelineRewritesExistingMSDStep(void)
Tests that reprocessing the same MSD step rewrites the CSV row instead of duplicating it.
static PetscErrorCode TestWriteParticleFileRewritesSameStepCleanly(void)
Tests repeated same-step VTP emission leaves only the final artifact.
static PetscErrorCode TestGlobalStatisticsPipelineWritesMSDCSV(void)
Tests global statistics pipeline generation of MSD CSV output.
static PetscErrorCode TestWriteEulerianFileWritesVTS(void)
Tests VTS emission for Eulerian post-processing output.
static PetscErrorCode TestWriteEulerianFileRewritesSameStepCleanly(void)
Tests repeated same-step VTS emission leaves only the final artifact.
static PetscErrorCode TestEulerianDataProcessingPipelineRunsConfiguredKernels(void)
Tests Eulerian post-processing pipeline dispatch for configured kernels.
static PetscErrorCode TestSetupPostProcessSwarmRegistersPipelineFields(void)
Tests post-processing swarm setup and pipeline-field registration.
PetscErrorCode PicurvMakeTempDir(char *path, size_t path_len)
Creates a unique temporary directory for one test case.
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 PicurvEnsureDir(const char *path)
Ensures a directory exists for test output.
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 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 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.
PetscErrorCode PicurvRemoveTempDir(const char *path)
Recursively removes a temporary directory created by PicurvMakeTempDir.
Shared declarations for the PICurv C test fixture and assertion layer.
Named test case descriptor consumed by PicurvRunTests.
char statistics_output_prefix[256]
basename for CSV output, e.g.
char particle_output_prefix[256]
char statistics_pipeline[1024]
e.g.
PetscReal psrc_z
Point source location for PARTICLE_INIT_POINT_SOURCE.
char output_fields_instantaneous[1024]
char source_dir[PETSC_MAX_PATH_LEN]
char particle_pipeline[1024]
char process_pipeline[1024]
PetscInt particle_output_freq
char particle_fields[1024]
PetscBool outputParticles
@ EXEC_MODE_POSTPROCESSOR
char PostprocessingControlFile[PETSC_MAX_PATH_LEN]
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.