PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
test_vtk_io.c
Go to the documentation of this file.
1/**
2 * @file test_vtk_io.c
3 * @brief C unit tests for VTK output preparation and file writing helpers.
4 */
5
6#include "test_support.h"
7
8#include "vtk_io.h"
9
10#include <stdio.h>
11#include <string.h>
12/**
13 * @brief Tests coordinate extraction with interior-grid subsampling.
14 */
15
17{
18 SimCtx *simCtx = NULL;
19 UserCtx *user = NULL;
20 PetscScalar *coords = NULL;
21 PetscInt nx = 0, ny = 0, nz = 0, npoints = 0;
22 PetscReal expected_last_coord = 0.0;
23
24 PetscFunctionBeginUser;
25 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 4, 4, 4));
26 expected_last_coord = (PetscReal)(user->IM - 1) / (PetscReal)user->IM;
27
28 PetscCall(PrepareOutputCoordinates(user, &coords, &nx, &ny, &nz, &npoints));
29 PetscCall(PicurvAssertIntEqual(4, nx, "PrepareOutputCoordinates should output nx based on the production IM+1 DA"));
30 PetscCall(PicurvAssertIntEqual(4, ny, "PrepareOutputCoordinates should output ny based on the production JM+1 DA"));
31 PetscCall(PicurvAssertIntEqual(4, nz, "PrepareOutputCoordinates should output nz based on the production KM+1 DA"));
32 PetscCall(PicurvAssertIntEqual(64, npoints, "PrepareOutputCoordinates should output the full physical-node lattice"));
33
34 if (simCtx->rank == 0) {
35 PetscCall(PicurvAssertBool((PetscBool)(coords != NULL), "PrepareOutputCoordinates should allocate coordinates on rank 0"));
36 PetscCall(PicurvAssertRealNear(0.0, PetscRealPart(coords[0]), 1.0e-12, "First coordinate x should be 0"));
37 PetscCall(PicurvAssertRealNear(0.0, PetscRealPart(coords[1]), 1.0e-12, "First coordinate y should be 0"));
38 PetscCall(PicurvAssertRealNear(0.0, PetscRealPart(coords[2]), 1.0e-12, "First coordinate z should be 0"));
39
40 PetscCall(PicurvAssertRealNear(expected_last_coord, PetscRealPart(coords[3 * (npoints - 1) + 0]), 1.0e-12, "Last output coordinate x should be the normalized physical-node domain end"));
41 PetscCall(PicurvAssertRealNear(expected_last_coord, PetscRealPart(coords[3 * (npoints - 1) + 1]), 1.0e-12, "Last output coordinate y should be the normalized physical-node domain end"));
42 PetscCall(PicurvAssertRealNear(expected_last_coord, PetscRealPart(coords[3 * (npoints - 1) + 2]), 1.0e-12, "Last output coordinate z should be the normalized physical-node domain end"));
43
44 PetscCall(PetscFree(coords));
45 }
46
47 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
48 PetscFunctionReturn(0);
49}
50/**
51 * @brief Tests Eulerian scalar field extraction with subsampling.
52 */
53
55{
56 SimCtx *simCtx = NULL;
57 UserCtx *user = NULL;
58 PetscScalar *field_out = NULL;
59 PetscInt start = 0, end = 0;
60
61 PetscFunctionBeginUser;
62 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 4, 4, 4));
63
64 PetscCall(VecGetOwnershipRange(user->P_nodal, &start, &end));
65 for (PetscInt idx = start; idx < end; ++idx) {
66 PetscCall(VecSetValue(user->P_nodal, idx, (PetscScalar)idx, INSERT_VALUES));
67 }
68 PetscCall(VecAssemblyBegin(user->P_nodal));
69 PetscCall(VecAssemblyEnd(user->P_nodal));
70
71 PetscCall(PrepareOutputEulerianFieldData(user, user->P_nodal, 1, &field_out));
72 if (simCtx->rank == 0) {
73 PetscCall(PicurvAssertBool((PetscBool)(field_out != NULL),
74 "PrepareOutputEulerianFieldData should allocate subsampled data on rank 0"));
75 PetscCall(PicurvAssertRealNear(0.0, PetscRealPart(field_out[0]), 1.0e-12, "Subsampled first value should map to source index 0"));
76 PetscCall(PicurvAssertRealNear(93.0, PetscRealPart(field_out[63]), 1.0e-12,
77 "Subsampled last value should map to source index (3,3,3)=93 in the production-sized DA"));
78 PetscCall(PetscFree(field_out));
79 }
80
81 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
82 PetscFunctionReturn(0);
83}
84/**
85 * @brief Tests particle-data extraction with output subsampling.
86 */
87
89{
90 SimCtx *simCtx = NULL;
91 UserCtx *user = NULL;
93 VTKMetaData meta;
94 PetscInt n_total = 0;
95 PetscReal (*position)[3] = NULL;
96 PetscReal (*velocity)[3] = NULL;
97
98 PetscFunctionBeginUser;
99 PetscCall(PetscMemzero(&pps, sizeof(pps)));
100 PetscCall(PetscMemzero(&meta, sizeof(meta)));
101 PetscCall(PicurvCreateMinimalContexts(&simCtx, &user, 4, 4, 4));
102 PetscCall(PicurvCreateSwarmPair(user, 5, "ske"));
103
104 pps.particle_output_freq = 2;
105 PetscCall(PetscStrncpy(pps.particle_fields, "position,velocity", sizeof(pps.particle_fields)));
106
107 PetscCall(DMSwarmGetField(user->swarm, "position", NULL, NULL, (void *)&position));
108 PetscCall(DMSwarmGetField(user->swarm, "velocity", NULL, NULL, (void *)&velocity));
109 for (PetscInt p = 0; p < 5; ++p) {
110 position[p][0] = (PetscReal)p;
111 position[p][1] = 0.0;
112 position[p][2] = 0.0;
113 velocity[p][0] = 10.0 + (PetscReal)p;
114 velocity[p][1] = 20.0 + (PetscReal)p;
115 velocity[p][2] = 30.0 + (PetscReal)p;
116 }
117 PetscCall(DMSwarmRestoreField(user->swarm, "position", NULL, NULL, (void *)&position));
118 PetscCall(DMSwarmRestoreField(user->swarm, "velocity", NULL, NULL, (void *)&velocity));
119
120 PetscCall(PrepareOutputParticleData(user, &pps, &meta, &n_total));
121 if (simCtx->rank == 0) {
122 PetscCall(PicurvAssertIntEqual(5, n_total, "PrepareOutputParticleData should report total particles before subsampling"));
123 PetscCall(PicurvAssertIntEqual(3, meta.npoints, "Stride-2 subsampling over 5 particles should keep 3 output particles"));
124 PetscCall(PicurvAssertIntEqual(VTK_POLYDATA, meta.fileType, "Particle output metadata should be PolyData"));
125 PetscCall(PicurvAssertIntEqual(1, meta.num_point_data_fields, "Velocity field should be exported as one point-data field"));
126 PetscCall(PicurvAssertBool((PetscBool)(strcmp(meta.point_data_fields[0].name, "velocity") == 0),
127 "Particle field name should propagate to VTK metadata"));
128
129 PetscCall(PicurvAssertRealNear(0.0, PetscRealPart(meta.coords[0]), 1.0e-12, "First exported particle should be index 0"));
130 PetscCall(PicurvAssertRealNear(2.0, PetscRealPart(meta.coords[3]), 1.0e-12, "Second exported particle should be index 2"));
131 PetscCall(PicurvAssertRealNear(4.0, PetscRealPart(meta.coords[6]), 1.0e-12, "Third exported particle should be index 4"));
132
133 PetscCall(PetscFree(meta.coords));
134 for (PetscInt f = 0; f < meta.num_point_data_fields; ++f) {
135 PetscCall(PetscFree(meta.point_data_fields[f].data));
136 }
137 PetscCall(PetscFree(meta.connectivity));
138 PetscCall(PetscFree(meta.offsets));
139 }
140
141 PetscCall(PicurvDestroyMinimalContexts(&simCtx, &user));
142 PetscFunctionReturn(0);
143}
144/**
145 * @brief Tests structured-grid VTK file creation from assembled metadata.
146 */
147
149{
150 char tmpdir[PETSC_MAX_PATH_LEN];
151 char vtk_path[PETSC_MAX_PATH_LEN];
152 VTKMetaData meta;
153 FILE *file = NULL;
154 char probe[512];
155 size_t nread = 0;
156
157 PetscFunctionBeginUser;
158 PetscCall(PetscMemzero(&meta, sizeof(meta)));
159 PetscCall(PicurvMakeTempDir(tmpdir, sizeof(tmpdir)));
160 PetscCall(PetscSNPrintf(vtk_path, sizeof(vtk_path), "%s/field_00001.vts", tmpdir));
161
163 meta.mx = 2;
164 meta.my = 2;
165 meta.mz = 2;
166 meta.npoints = 8;
167 meta.num_point_data_fields = 1;
168
169 PetscCall(PetscMalloc1(3 * meta.npoints, &meta.coords));
170 PetscCall(PetscMalloc1(meta.npoints, &meta.point_data_fields[0].data));
171 PetscCall(PetscStrncpy(meta.point_data_fields[0].name, "P_nodal", sizeof(meta.point_data_fields[0].name)));
173
174 for (PetscInt p = 0; p < meta.npoints; ++p) {
175 meta.coords[3 * p + 0] = (PetscScalar)(p % 2);
176 meta.coords[3 * p + 1] = (PetscScalar)((p / 2) % 2);
177 meta.coords[3 * p + 2] = (PetscScalar)(p / 4);
178 meta.point_data_fields[0].data[p] = (PetscScalar)p;
179 }
180
181 PetscCall(CreateVTKFileFromMetadata(vtk_path, &meta, PETSC_COMM_WORLD));
182 PetscCall(PicurvAssertFileExists(vtk_path, "CreateVTKFileFromMetadata should emit a .vts file"));
183
184 file = fopen(vtk_path, "rb");
185 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Failed to open generated VTK file '%s'.", vtk_path);
186 nread = fread(probe, 1, sizeof(probe) - 1, file);
187 fclose(file);
188 probe[nread] = '\0';
189 PetscCall(PicurvAssertBool((PetscBool)(strstr(probe, "StructuredGrid") != NULL),
190 "Generated VTK header should declare StructuredGrid type"));
191
192 PetscCall(PetscFree(meta.coords));
193 PetscCall(PetscFree(meta.point_data_fields[0].data));
194 PetscCall(PicurvRemoveTempDir(tmpdir));
195 PetscFunctionReturn(0);
196}
197/**
198 * @brief Runs the unit-post-vtk PETSc test binary.
199 */
200
201int main(int argc, char **argv)
202{
203 PetscErrorCode ierr;
204 const PicurvTestCase cases[] = {
205 {"prepare-output-coordinates-subsamples-interior-grid", TestPrepareOutputCoordinatesSubsamplesInteriorGrid},
206 {"prepare-output-eulerian-field-data-subsamples-scalar", TestPrepareOutputEulerianFieldDataSubsamplesScalar},
207 {"prepare-output-particle-data-subsampling", TestPrepareOutputParticleDataSubsampling},
208 {"create-vtk-file-from-metadata-writes-structured-grid", TestCreateVTKFileFromMetadataWritesStructuredGrid},
209 };
210
211 ierr = PetscInitialize(&argc, &argv, NULL, "PICurv VTK I/O tests");
212 if (ierr) {
213 return (int)ierr;
214 }
215
216 ierr = PicurvRunTests("unit-post-vtk", cases, sizeof(cases) / sizeof(cases[0]));
217 if (ierr) {
218 PetscFinalize();
219 return (int)ierr;
220 }
221
222 ierr = PetscFinalize();
223 return (int)ierr;
224}
PetscInt CreateVTKFileFromMetadata(const char *filename, const VTKMetaData *meta, MPI_Comm comm)
Creates a VTK file from prepared metadata and field payloads.
Definition vtk_io.c:153
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 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.
static PetscErrorCode TestPrepareOutputEulerianFieldDataSubsamplesScalar(void)
Tests Eulerian scalar field extraction with subsampling.
Definition test_vtk_io.c:54
static PetscErrorCode TestPrepareOutputParticleDataSubsampling(void)
Tests particle-data extraction with output subsampling.
Definition test_vtk_io.c:88
int main(int argc, char **argv)
Runs the unit-post-vtk PETSc test binary.
static PetscErrorCode TestCreateVTKFileFromMetadataWritesStructuredGrid(void)
Tests structured-grid VTK file creation from assembled metadata.
static PetscErrorCode TestPrepareOutputCoordinatesSubsamplesInteriorGrid(void)
Tests coordinate extraction with interior-grid subsampling.
Definition test_vtk_io.c:16
Vec P_nodal
Definition variables.h:887
PetscInt npoints
Definition variables.h:604
PetscInt num_components
Definition variables.h:591
PetscMPIInt rank
Definition variables.h:646
PetscInt num_point_data_fields
Definition variables.h:607
PetscInt * connectivity
Definition variables.h:608
PetscInt * offsets
Definition variables.h:609
VTKFileType fileType
Definition variables.h:602
PetscScalar * data
Definition variables.h:592
char name[64]
Definition variables.h:590
PetscScalar * coords
Definition variables.h:605
PetscInt particle_output_freq
Definition variables.h:571
char particle_fields[1024]
Definition variables.h:569
VTKFieldInfo point_data_fields[20]
Definition variables.h:606
PetscInt mz
Definition variables.h:603
PetscInt IM
Definition variables.h:820
PetscInt my
Definition variables.h:603
PetscInt mx
Definition variables.h:603
@ VTK_POLYDATA
Definition variables.h:598
@ VTK_STRUCTURED
Definition variables.h:597
Holds all configuration parameters for a post-processing run.
Definition variables.h:553
The master context for the entire simulation.
Definition variables.h:643
User-defined context containing data specific to a single computational grid level.
Definition variables.h:811
PetscErrorCode PrepareOutputEulerianFieldData(UserCtx *user, Vec field_vec, PetscInt num_components, PetscScalar **out_data)
Creates a C array of field data corresponding to a subsampled (legacy-style) grid.
Definition vtk_io.c:313
PetscErrorCode PrepareOutputCoordinates(UserCtx *user, PetscScalar **out_coords, PetscInt *out_nx, PetscInt *out_ny, PetscInt *out_nz, PetscInt *out_npoints)
Creates a C array of coordinates corresponding to a subsampled (legacy-style) grid.
Definition vtk_io.c:245
PetscErrorCode PrepareOutputParticleData(UserCtx *user, PostProcessParams *pps, VTKMetaData *meta, PetscInt *p_n_total)
Gathers, subsamples, and prepares all particle data for VTK output.
Definition vtk_io.c:422