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

Focused multirank regression for instantaneous Eulerian VTK output. More...

#include "test_support.h"
#include "postprocessor.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Include dependency graph for test_post_eulerian_vtk_mpi.c:

Go to the source code of this file.

Functions

static PetscBool ScalarMatches (PetscScalar actual, PetscReal expected)
 Compare one scalar from the raw VTK payload with its deterministic truth.
 
static PetscBool ValidateEulerianVTKFile (const char *path, PetscInt mx, PetscInt my, PetscInt mz)
 Read and exhaustively validate every appended value in a generated VTS file.
 
static PetscErrorCode TestWriteEulerianFileCollectiveMultiRank (void)
 Ensures every Eulerian field and coordinate is correct after collective output.
 
int main (int argc, char **argv)
 Runs the focused serial/MPI Eulerian VTK regression.
 

Detailed Description

Focused multirank regression for instantaneous Eulerian VTK output.

Definition in file test_post_eulerian_vtk_mpi.c.

Function Documentation

◆ ScalarMatches()

static PetscBool ScalarMatches ( PetscScalar  actual,
PetscReal  expected 
)
static

Compare one scalar from the raw VTK payload with its deterministic truth.

Definition at line 16 of file test_post_eulerian_vtk_mpi.c.

17{
18 return (PetscBool)(PetscAbsReal(PetscRealPart(actual) - expected) <= 1.0e-12);
19}
Here is the caller graph for this function:

◆ ValidateEulerianVTKFile()

static PetscBool ValidateEulerianVTKFile ( const char *  path,
PetscInt  mx,
PetscInt  my,
PetscInt  mz 
)
static

Read and exhaustively validate every appended value in a generated VTS file.

Definition at line 22 of file test_post_eulerian_vtk_mpi.c.

23{
24 FILE *file = NULL;
25 char line[1024];
26 char footer[128];
27 char expected_extent[128];
28 PetscBool saw_pressure = PETSC_FALSE;
29 PetscBool saw_velocity = PETSC_FALSE;
30 PetscBool saw_qcrit = PETSC_FALSE;
31 PetscBool saw_extent = PETSC_FALSE;
32 uint32_t block_size = 0;
33 PetscInt npoints = mx * my * mz;
34 PetscScalar *coords = NULL;
35 PetscScalar *pressure = NULL;
36 PetscScalar *velocity = NULL;
37 PetscScalar *qcrit = NULL;
38 size_t footer_size = 0;
39 PetscBool valid = PETSC_FALSE;
40
41 file = fopen(path, "rb");
42 if (!file) return PETSC_FALSE;
43 snprintf(expected_extent, sizeof(expected_extent),
44 "WholeExtent=\"0 %d 0 %d 0 %d\"", (int)(mx - 1), (int)(my - 1), (int)(mz - 1));
45
46 while (fgets(line, sizeof(line), file)) {
47 if (strstr(line, "Name=\"P_nodal\"")) saw_pressure = PETSC_TRUE;
48 if (strstr(line, "Name=\"Ucat_nodal\"")) saw_velocity = PETSC_TRUE;
49 if (strstr(line, "Name=\"Qcrit\"")) saw_qcrit = PETSC_TRUE;
50 if (strstr(line, expected_extent)) saw_extent = PETSC_TRUE;
51 if (strstr(line, "<AppendedData encoding=\"raw\">")) break;
52 }
53 if (!saw_pressure || !saw_velocity || !saw_qcrit || !saw_extent || fgetc(file) != '_') goto cleanup;
54
55 /* Coordinates. */
56 if (fread(&block_size, sizeof(block_size), 1, file) != 1 ||
57 block_size != (uint32_t)(3 * npoints * (PetscInt)sizeof(PetscScalar))) goto cleanup;
58 coords = malloc((size_t)(3 * npoints) * sizeof(*coords));
59 if (!coords ||
60 fread(coords, sizeof(PetscScalar), (size_t)(3 * npoints), file) != (size_t)(3 * npoints)) goto cleanup;
61
62 /* P_nodal. */
63 if (fread(&block_size, sizeof(block_size), 1, file) != 1 ||
64 block_size != (uint32_t)(npoints * (PetscInt)sizeof(PetscScalar))) goto cleanup;
65 pressure = malloc((size_t)npoints * sizeof(*pressure));
66 if (!pressure ||
67 fread(pressure, sizeof(PetscScalar), (size_t)npoints, file) != (size_t)npoints) goto cleanup;
68
69 /* Ucat_nodal. */
70 if (fread(&block_size, sizeof(block_size), 1, file) != 1 ||
71 block_size != (uint32_t)(3 * npoints * (PetscInt)sizeof(PetscScalar))) goto cleanup;
72 velocity = malloc((size_t)(3 * npoints) * sizeof(*velocity));
73 if (!velocity ||
74 fread(velocity, sizeof(PetscScalar), (size_t)(3 * npoints), file) != (size_t)(3 * npoints)) goto cleanup;
75
76 /* Qcrit. */
77 if (fread(&block_size, sizeof(block_size), 1, file) != 1 ||
78 block_size != (uint32_t)(npoints * (PetscInt)sizeof(PetscScalar))) goto cleanup;
79 qcrit = malloc((size_t)npoints * sizeof(*qcrit));
80 if (!qcrit ||
81 fread(qcrit, sizeof(PetscScalar), (size_t)npoints, file) != (size_t)npoints) goto cleanup;
82
83 for (PetscInt k = 0; k < mz; ++k) {
84 for (PetscInt j = 0; j < my; ++j) {
85 for (PetscInt i = 0; i < mx; ++i) {
86 const PetscInt point = (k * my + j) * mx + i;
87 const PetscReal base = (PetscReal)(10000 * k + 100 * j + i);
88
89 if (!ScalarMatches(coords[3 * point + 0], (PetscReal)i / (PetscReal)mx) ||
90 !ScalarMatches(coords[3 * point + 1], (PetscReal)j / (PetscReal)my) ||
91 !ScalarMatches(coords[3 * point + 2], (PetscReal)k / (PetscReal)mz) ||
92 !ScalarMatches(pressure[point], base + 0.25) ||
93 !ScalarMatches(velocity[3 * point + 0], base + 1.0) ||
94 !ScalarMatches(velocity[3 * point + 1], base + 2.0) ||
95 !ScalarMatches(velocity[3 * point + 2], base + 3.0) ||
96 !ScalarMatches(qcrit[point], -base - 0.5)) goto cleanup;
97 }
98 }
99 }
100
101 footer_size = fread(footer, 1, sizeof(footer) - 1, file);
102 footer[footer_size] = '\0';
103 if (!strstr(footer, "</AppendedData>") || !strstr(footer, "</VTKFile>")) goto cleanup;
104
105 valid = PETSC_TRUE;
106
107cleanup:
108 fclose(file);
109 free(coords);
110 free(pressure);
111 free(velocity);
112 free(qcrit);
113 return valid;
114}
static PetscBool ScalarMatches(PetscScalar actual, PetscReal expected)
Compare one scalar from the raw VTK payload with its deterministic truth.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TestWriteEulerianFileCollectiveMultiRank()

static PetscErrorCode TestWriteEulerianFileCollectiveMultiRank ( void  )
static

Ensures every Eulerian field and coordinate is correct after collective output.

Definition at line 119 of file test_post_eulerian_vtk_mpi.c.

120{
121 SimCtx *simCtx = NULL;
122 UserCtx *user = NULL;
124 char vtk_path[PETSC_MAX_PATH_LEN];
125 char vtk_tmp_path[PETSC_MAX_PATH_LEN];
126 PetscReal ***pressure = NULL;
127 PetscReal ***qcrit = NULL;
128 Cmpnts ***velocity = NULL;
129 PetscMPIInt rank = 0, size = 1;
130 PetscMPIInt local_owner = -1, probe_owner = -1;
131 PetscBool parsed = PETSC_FALSE;
132 PetscBool tmp_exists = PETSC_FALSE;
133 PetscBool prefix_set = PETSC_FALSE;
134
135 PetscFunctionBeginUser;
136 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
137 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
138
139 PetscCall(PetscMemzero(&pps, sizeof(pps)));
140 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 8, 6, 4));
141 PetscCall(PetscOptionsGetString(NULL, NULL, "-vtk_test_output_prefix",
142 pps.output_prefix, sizeof(pps.output_prefix), &prefix_set));
143 PetscCall(PicurvAssertBool(prefix_set,
144 "the focused Eulerian VTK test requires -vtk_test_output_prefix"));
145 PetscCall(PetscSNPrintf(vtk_path, sizeof(vtk_path), "%s_00003.vts", pps.output_prefix));
146 PetscCall(PetscSNPrintf(vtk_tmp_path, sizeof(vtk_tmp_path), "%s.tmp", vtk_path));
147
148 PetscCall(DMDAVecGetArray(user->da, user->P_nodal, &pressure));
149 PetscCall(DMDAVecGetArray(user->fda, user->Ucat_nodal, &velocity));
150 PetscCall(DMDAVecGetArray(user->da, user->Qcrit, &qcrit));
151 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; ++k) {
152 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; ++j) {
153 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; ++i) {
154 const PetscReal base = (PetscReal)(10000 * k + 100 * j + i);
155
156 pressure[k][j][i] = base + 0.25;
157 velocity[k][j][i] = (Cmpnts){base + 1.0, base + 2.0, base + 3.0};
158 qcrit[k][j][i] = -base - 0.5;
159 }
160 }
161 }
162 PetscCall(DMDAVecRestoreArray(user->da, user->P_nodal, &pressure));
163 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucat_nodal, &velocity));
164 PetscCall(DMDAVecRestoreArray(user->da, user->Qcrit, &qcrit));
165
166 if (user->IM - 1 >= user->info.xs && user->IM - 1 < user->info.xs + user->info.xm &&
167 user->JM - 1 >= user->info.ys && user->JM - 1 < user->info.ys + user->info.ym &&
168 user->KM - 1 >= user->info.zs && user->KM - 1 < user->info.zs + user->info.zm) {
169 local_owner = rank;
170 }
171 PetscCallMPI(MPI_Allreduce(&local_owner, &probe_owner, 1, MPI_INT, MPI_MAX, PETSC_COMM_WORLD));
172 PetscCall(PicurvAssertBool((PetscBool)(probe_owner == ((size > 1) ? size - 1 : 0)),
173 "the final physical VTK point should have the expected owner"));
174
175 PetscCall(PetscStrncpy(pps.output_fields_instantaneous,
176 "P_nodal,Ucat_nodal,Qcrit",
177 sizeof(pps.output_fields_instantaneous)));
178
179 PetscCall(WriteEulerianFile(user, &pps, 3));
180 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
181 PetscCall(PicurvAssertFileExists(vtk_path,
182 "multirank WriteEulerianFile should emit one combined .vts file"));
183 PetscCall(PetscTestFile(vtk_tmp_path, 'r', &tmp_exists));
184 PetscCall(PicurvAssertBool((PetscBool)!tmp_exists,
185 "successful multirank Eulerian VTK output should not leave a temporary file"));
186
187 if (rank == 0) {
188 parsed = ValidateEulerianVTKFile(vtk_path, user->IM, user->JM, user->KM);
189 }
190 PetscCallMPI(MPI_Bcast(&parsed, 1, MPIU_BOOL, 0, PETSC_COMM_WORLD));
191 PetscCall(PicurvAssertBool(parsed,
192 "every coordinate and Eulerian field value in the generated VTK should match"));
193
194 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
195 PetscFunctionReturn(0);
196}
PetscErrorCode WriteEulerianFile(UserCtx *user, PostProcessParams *pps, PetscInt ti)
Orchestrates the writing of a combined, multi-field VTK file for a single time step.
static PetscBool ValidateEulerianVTKFile(const char *path, PetscInt mx, PetscInt my, PetscInt mz)
Read and exhaustively validate every appended value in a generated VTS file.
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 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.
Vec P_nodal
Definition variables.h:991
char output_prefix[256]
Definition variables.h:609
PetscInt KM
Definition variables.h:910
Vec Ucat_nodal
Definition variables.h:992
Vec Qcrit
Definition variables.h:993
char output_fields_instantaneous[1024]
Definition variables.h:608
PetscInt JM
Definition variables.h:910
DMDALocalInfo info
Definition variables.h:908
PetscInt IM
Definition variables.h:910
A 3D point or vector with PetscScalar components.
Definition variables.h:102
Holds all configuration parameters for a post-processing run.
Definition variables.h:596
The master context for the entire simulation.
Definition variables.h:695
User-defined context containing data specific to a single computational grid level.
Definition variables.h:896
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 focused serial/MPI Eulerian VTK regression.

Definition at line 199 of file test_post_eulerian_vtk_mpi.c.

200{
201 PetscErrorCode ierr;
202 const PicurvTestCase cases[] = {
203 {"write-eulerian-file-collective-multi-rank", TestWriteEulerianFileCollectiveMultiRank},
204 };
205
206 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv multirank Eulerian VTK tests");
207 if (ierr) return (int)ierr;
208
209 ierr = PicurvRunTests("unit-post-eulerian-vtk-mpi", cases, sizeof(cases) / sizeof(cases[0]));
210 if (ierr) {
211 PetscFinalize();
212 return (int)ierr;
213 }
214
215 ierr = PetscFinalize();
216 return (int)ierr;
217}
static PetscErrorCode TestWriteEulerianFileCollectiveMultiRank(void)
Ensures every Eulerian field and coordinate is correct after collective output.
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: