PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Functions
vtk_io.h File Reference
#include "variables.h"
#include "logging.h"
#include "io.h"
Include dependency graph for vtk_io.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

PetscInt CreateVTKFileFromMetadata (const char *filename, const VTKMetaData *meta, MPI_Comm comm)
 Creates and writes a VTK file (either .vts or .vtp) from a populated metadata struct.
 
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.
 
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.
 
PetscErrorCode BeginStructuredVTKOutput (UserCtx *user, VTKMetaData *meta)
 Begins one structured VTK file: clears the metadata and builds its coordinates.
 
PetscErrorCode AppendStructuredVTKField (UserCtx *user, VTKMetaData *meta, const char *name, Vec field_vec, PetscInt components)
 Adds one point-data field to a structured VTK file being assembled.
 
PetscErrorCode FinishStructuredVTKOutput (VTKMetaData *meta, const char *filename)
 Writes an assembled structured VTK file and releases its buffers.
 
PetscErrorCode PrepareOutputParticleData (UserCtx *user, PostProcessParams *pps, VTKMetaData *meta, PetscInt *p_n_total)
 Gathers, subsamples, and prepares all particle data for VTK output.
 

Function Documentation

◆ CreateVTKFileFromMetadata()

PetscInt CreateVTKFileFromMetadata ( const char *  filename,
const VTKMetaData meta,
MPI_Comm  comm 
)

Creates and writes a VTK file (either .vts or .vtp) from a populated metadata struct.

Parameters
[in]filenameThe output file name.
[in]metaPointer to a VTKMetaData structure containing all necessary fields.
[in]commThe MPI communicator.
Returns
0 on success, non-zero on failure.

Creates and writes a VTK file (either .vts or .vtp) from a populated metadata struct.

Creates a VTK file from prepared metadata and field payloads.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/vtk_io.h.

See also
CreateVTKFileFromMetadata()

Definition at line 149 of file vtk_io.c.

150{
151 PetscMPIInt rank;
152 MPI_Comm_rank(comm, &rank);
153 PetscErrorCode ierr = 0;
154
155 if (!rank) {
156 char tmp_filename[PETSC_MAX_PATH_LEN];
157 FILE *fp = NULL;
158
159 ierr = PetscSNPrintf(tmp_filename, sizeof(tmp_filename), "%s.tmp", filename);CHKERRQ(ierr);
160 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank 0 writing combined VTK file '%s'.\n", filename);
161 fp = fopen(tmp_filename, "wb");
162 if (!fp) {
163 LOG_ALLOW(GLOBAL, LOG_ERROR, "fopen failed for %s.\n", tmp_filename);
164 return PETSC_ERR_FILE_OPEN;
165 }
166
167 PetscInt boffset = 0;
168
169 ierr = WriteVTKFileHeader(fp, meta, &boffset);
170 if (ierr) {
171 fclose(fp);
172 remove(tmp_filename);
173 return ierr;
174 }
175
176 if (meta->coords) {
177 ierr = WriteVTKAppendedBlock(fp, meta->coords, 3 * meta->npoints, sizeof(PetscScalar));
178 if (ierr) {
179 fclose(fp);
180 remove(tmp_filename);
181 return ierr;
182 }
183 }
184
185 for (PetscInt i = 0; i < meta->num_point_data_fields; i++) {
186 const VTKFieldInfo *field = &meta->point_data_fields[i];
187 if (field->data) {
188 ierr = WriteVTKAppendedBlock(fp, field->data, field->num_components * meta->npoints, sizeof(PetscScalar));
189 if (ierr) {
190 fclose(fp);
191 remove(tmp_filename);
192 return ierr;
193 }
194 }
195 }
196 if (meta->fileType == VTK_POLYDATA) {
197 if (meta->connectivity) {
198 ierr = WriteVTKAppendedBlock(fp, meta->connectivity, meta->npoints, sizeof(PetscInt));
199 if (ierr) {
200 fclose(fp);
201 remove(tmp_filename);
202 return ierr;
203 }
204 }
205 if (meta->offsets) {
206 ierr = WriteVTKAppendedBlock(fp, meta->offsets, meta->npoints, sizeof(PetscInt));
207 if (ierr) {
208 fclose(fp);
209 remove(tmp_filename);
210 return ierr;
211 }
212 }
213 }
214 ierr = WriteVTKFileFooter(fp, meta);
215 if (ierr) {
216 fclose(fp);
217 remove(tmp_filename);
218 return ierr;
219 }
220 if (fclose(fp) != 0) {
221 remove(tmp_filename);
222 return PETSC_ERR_FILE_WRITE;
223 }
224 if (rename(tmp_filename, filename) != 0) {
225 remove(tmp_filename);
226 return PETSC_ERR_FILE_WRITE;
227 }
228 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank 0 finished writing VTK file '%s'.\n", filename);
229 }
230 return 0;
231}
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:29
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
PetscInt npoints
Definition variables.h:656
PetscInt num_components
Definition variables.h:643
PetscInt num_point_data_fields
Definition variables.h:659
PetscInt * connectivity
Definition variables.h:660
PetscInt * offsets
Definition variables.h:661
VTKFileType fileType
Definition variables.h:654
PetscScalar * data
Definition variables.h:644
PetscScalar * coords
Definition variables.h:657
VTKFieldInfo point_data_fields[20]
Definition variables.h:658
@ VTK_POLYDATA
Definition variables.h:650
Stores all necessary information for a single data array in a VTK file.
Definition variables.h:641
static PetscErrorCode WriteVTKFileFooter(FILE *fp, const VTKMetaData *meta)
Close a VTK XML document after all appended data have been written.
Definition vtk_io.c:132
static PetscErrorCode WriteVTKAppendedBlock(FILE *fp, const void *data, PetscInt num_elements, size_t element_size)
Write one binary data block in VTK appended-data format.
Definition vtk_io.c:21
static PetscErrorCode WriteVTKFileHeader(FILE *fp, const VTKMetaData *meta, PetscInt *boffset)
Open a VTK XML document and write its file-level header.
Definition vtk_io.c:119
Here is the caller graph for this function:

◆ PrepareOutputCoordinates()

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.

This function gathers the full, distributed grid coordinates onto rank 0. On rank 0, it then allocates a new, smaller C array and copies only the coordinates for the nodes within the range [0..IM-2, 0..JM-2, 0..KM-2]. This produces a contiguous array of points for a grid of size (IM-1)x(JM-1)x(KM-1), matching the legacy output. The output arrays are only allocated and valid on rank 0.

Parameters
[in]userThe UserCtx containing the grid information (DM, IM/JM/KM).
[out]out_coordsOn rank 0, a pointer to the newly allocated C array for coordinate data. NULL on other ranks.
[out]out_nxThe number of points in the x-dimension for the new grid (IM-1).
[out]out_nyThe number of points in the y-dimension for the new grid (JM-1).
[out]out_nzThe number of points in the z-dimension for the new grid (KM-1).
[out]out_npointsThe total number of points in the new grid.
Returns
PetscErrorCode

Creates a C array of coordinates corresponding to a subsampled (legacy-style) grid.

Local to this translation unit.

Definition at line 241 of file vtk_io.c.

242{
243 PetscErrorCode ierr;
244 Vec coords_global;
245 PetscInt IM, JM, KM;
246 PetscInt N_full_coords;
247 PetscScalar *full_coords_arr = NULL;
248
249 PetscFunctionBeginUser;
250 ierr = DMDAGetInfo(user->da, NULL, &IM, &JM, &KM, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); CHKERRQ(ierr);
251
252 // Set the dimensions of the output grid
253 *out_nx = IM - 1;
254 *out_ny = JM - 1;
255 *out_nz = KM - 1;
256 *out_npoints = (*out_nx) * (*out_ny) * (*out_nz);
257
258 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Preparing subsampled coordinates for a %" PetscInt_FMT "x%" PetscInt_FMT "x%" PetscInt_FMT " grid.\n", *out_nx, *out_ny, *out_nz);
259
260 // --- Step 1: Gather the full coordinate vector to rank 0 ---
261 ierr = DMGetCoordinates(user->da, &coords_global); CHKERRQ(ierr);
262 // Reuse of your existing, proven utility function's logic
263 ierr = VecToArrayOnRank0(coords_global, &N_full_coords, &full_coords_arr); CHKERRQ(ierr);
264
265 // --- Step 2: On rank 0, subsample the gathered array ---
266 if (user->simCtx->rank == 0) {
267 // We expect N_full_coords to be IM * JM * KM * 3
268 if (N_full_coords != IM * JM * KM * 3) {
269 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED, "Gathered coordinate array has wrong size. Expected %" PetscInt_FMT ", got %" PetscInt_FMT, IM * JM * KM * 3, N_full_coords);
270 }
271
272 // Allocate the smaller output C array
273 ierr = PetscMalloc1(3 * (*out_npoints), out_coords); CHKERRQ(ierr);
274
275 // Loop over the smaller grid dimensions and copy data
276 PetscInt p_out = 0; // Index for the small output array
277 for (PetscInt k = 0; k < *out_nz; k++) {
278 for (PetscInt j = 0; j < *out_ny; j++) {
279 for (PetscInt i = 0; i < *out_nx; i++) {
280 // Calculate the index in the full, 1D source array
281 PetscInt p_in = 3 * (k * (JM * IM) + j * IM + i);
282
283 (*out_coords)[p_out++] = full_coords_arr[p_in + 0]; // x
284 (*out_coords)[p_out++] = full_coords_arr[p_in + 1]; // y
285 (*out_coords)[p_out++] = full_coords_arr[p_in + 2]; // z
286 }
287 }
288 }
289 // Free the temporary full array that was allocated by VecToArrayOnRank0
290 ierr = PetscFree(full_coords_arr); CHKERRQ(ierr);
291 } else {
292 // On other ranks, the output pointer is NULL
293 *out_coords = NULL;
294 }
295
296 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Subsampled coordinates prepared on rank 0 with total %" PetscInt_FMT " points.\n", *out_npoints);
297
298 PetscFunctionReturn(0);
299}
PetscErrorCode VecToArrayOnRank0(Vec inVec, PetscInt *N, double **arrayOut)
Gathers the contents of a distributed PETSc Vec into a single array on rank 0.
Definition io.c:2650
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
PetscMPIInt rank
Definition variables.h:698
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:909
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PrepareOutputEulerianFieldData()

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.

This function gathers a full, distributed PETSc vector to rank 0. On rank 0, it then allocates a new, smaller C array and copies only the data components for nodes within the range [0..IM-2, 0..JM-2, 0..KM-2]. This produces a contiguous data array that perfectly matches the point ordering of the subsampled coordinates. The output array is only allocated and valid on rank 0.

Parameters
[in]userThe UserCtx for grid information.
[in]field_vecThe full-sized PETSc vector containing the field data (e.g., user->P_nodal).
[in]num_componentsThe number of components for this field (1 for scalar, 3 for vector).
[out]out_dataOn rank 0, a pointer to the newly allocated C array for the field data. NULL on other ranks.
Returns
PetscErrorCode

Creates a C array of field data corresponding to a subsampled (legacy-style) grid.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/vtk_io.h.

See also
PrepareOutputEulerianFieldData()

Definition at line 377 of file vtk_io.c.

378{
379 PetscErrorCode ierr;
380 PetscInt IM, JM, KM, nx, ny, nz, npoints;
381 PetscInt N_full_field = 0;
382 PetscScalar *full_field_arr = NULL;
383
384 PetscFunctionBeginUser;
385 ierr = DMDAGetInfo(user->da, NULL, &IM, &JM, &KM, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); CHKERRQ(ierr);
386 nx = IM - 1; ny = JM - 1; nz = KM - 1;
387 npoints = nx * ny * nz;
388
390 "Preparing subsampled field data (dof=%" PetscInt_FMT ") for a %" PetscInt_FMT "x%" PetscInt_FMT "x%" PetscInt_FMT " grid.\n",
391 num_components, nx, ny, nz);
392
393 // --- Step 1: Gather the full field vector to rank 0 ---
394 ierr = VecToArrayOnRank0(field_vec, &N_full_field, &full_field_arr); CHKERRQ(ierr);
395
396 // --- Step 2: On rank 0, subsample the gathered array ---
397 if (user->simCtx->rank == 0) {
398 // Sanity check the size of the gathered array
399 if (N_full_field != IM * JM * KM * num_components) {
400 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED,
401 "Gathered field array has wrong size. Expected %" PetscInt_FMT ", got %" PetscInt_FMT,
402 IM * JM * KM * num_components, N_full_field);
403 }
404
405 // Allocate the smaller output C array (AoS layout expected by your writer)
406 ierr = PetscMalloc1(num_components * npoints, out_data); CHKERRQ(ierr);
407
408 /*
409 // ======== Layout diagnostics (rank 0 only, read-only) ========
410 if (num_components > 1 && full_field_arr) {
411 const PetscInt n_full = IM * JM * KM;
412
413 // Choose a safe interior probe point and its +i neighbor
414 const PetscInt ic = (IM > 4) ? IM/2 : 1;
415 const PetscInt jc = (JM > 4) ? JM/2 : 1;
416 const PetscInt kc = (KM > 4) ? KM/2 : 1;
417 const PetscInt ic1 = (ic + 1 < IM-1) ? (ic + 1) : ic; // stay interior
418
419 const PetscInt t_full_c = (kc * JM + jc) * IM + ic;
420 const PetscInt t_full_c1 = (kc * JM + jc) * IM + ic1;
421
422 // If source were AoS: full[num_components * t + comp]
423 PetscScalar aos_c0 = full_field_arr[num_components * t_full_c + 0];
424 PetscScalar aos_c1 = full_field_arr[num_components * t_full_c1 + 0];
425 PetscScalar aos_c0_y = (num_components > 1) ? full_field_arr[num_components * t_full_c + 1] : 0.0;
426 PetscScalar aos_c0_z = (num_components > 2) ? full_field_arr[num_components * t_full_c + 2] : 0.0;
427
428 // If source were SoA: full[comp * n_full + t]
429 PetscScalar soa_c0 = full_field_arr[0 * n_full + t_full_c];
430 PetscScalar soa_c1 = full_field_arr[0 * n_full + t_full_c1];
431 PetscScalar soa_c0_y = (num_components > 1) ? full_field_arr[1 * n_full + t_full_c] : 0.0;
432 PetscScalar soa_c0_z = (num_components > 2) ? full_field_arr[2 * n_full + t_full_c] : 0.0;
433
434 double dAOS = fabs((double)(aos_c1 - aos_c0)); // spatial delta along +i
435 double dSOA = fabs((double)(soa_c1 - soa_c0));
436
437 const char *verdict = (dSOA < dAOS) ? "LIKELY SoA (component-major)" : "LIKELY AoS (interleaved)";
438
439 LOG_ALLOW(GLOBAL, LOG_INFO,
440 "Layout probe @ (i=%" PetscInt_FMT ", j=%" PetscInt_FMT ", k=%" PetscInt_FMT ") vs +i neighbor:\n"
441 " AoS-candidate: Ux(center)=%.6e Ux(+i)=%.6e |Δ|=%.6e ; Uy(center)=%.6e Uz(center)=%.6e\n"
442 " SoA-candidate: Ux(center)=%.6e Ux(+i)=%.6e |Δ|=%.6e ; Uy(center)=%.6e Uz(center)=%.6e\n"
443 " Verdict: %s\n",
444 ic, jc, kc,
445 (double)aos_c0, (double)aos_c1, dAOS, (double)aos_c0_y, (double)aos_c0_z,
446 (double)soa_c0, (double)soa_c1, dSOA, (double)soa_c0_y, (double)soa_c0_z,
447 verdict
448 );
449 } else if (num_components == 1) {
450 LOG_ALLOW(GLOBAL, LOG_INFO, "Layout probe: scalar field (dof=1) — no component interleave to detect.\n");
451 }
452 // ======== END diagnostics ========
453 */
454
455 // --- PACK: assumes source is already AoS ---
456 {
457 PetscInt p_out = 0;
458 for (PetscInt k = 0; k < nz; k++) {
459 for (PetscInt j = 0; j < ny; j++) {
460 for (PetscInt i = 0; i < nx; i++) {
461 const PetscInt p_in_start = num_components * (k * (JM * IM) + j * IM + i);
462 for (PetscInt c = 0; c < num_components; c++) {
463 (*out_data)[p_out++] = full_field_arr[p_in_start + c];
464 }
465 }
466 }
467 }
468 }
469
470 // Free temporary gathered array
471 ierr = PetscFree(full_field_arr); CHKERRQ(ierr);
472 } else {
473 // Other ranks: no allocation
474 *out_data = NULL;
475 }
476
477 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Subsampled field data prepared on rank 0 with total %" PetscInt_FMT " points.\n", npoints);
478
479 PetscFunctionReturn(0);
480}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ BeginStructuredVTKOutput()

PetscErrorCode BeginStructuredVTKOutput ( UserCtx user,
VTKMetaData meta 
)

Begins one structured VTK file: clears the metadata and builds its coordinates.

Every Eulerian VTK file is assembled the same way — coordinates, then a set of point-data fields, then the write. This trio exists so the two producers share that shape instead of restating it, and so the buffers each producer allocates are released in one place rather than in neither.

Parameters
[in]userBlock context supplying the grid.
[out]metaMetadata to initialize.
Returns
Zero on success, or a PETSc error.

Begins one structured VTK file: clears the metadata and builds its coordinates.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/vtk_io.h.

See also
BeginStructuredVTKOutput()

Definition at line 308 of file vtk_io.c.

309{
310 PetscFunctionBeginUser;
311 PetscCheck(user != NULL && meta != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
312 "Context and metadata are required.");
313 PetscCall(PetscMemzero(meta, sizeof(*meta)));
314 meta->fileType = VTK_STRUCTURED;
315 PetscCall(PrepareOutputCoordinates(user, &meta->coords, &meta->mx, &meta->my, &meta->mz,
316 &meta->npoints));
317 PetscFunctionReturn(0);
318}
PetscInt mz
Definition variables.h:655
PetscInt my
Definition variables.h:655
PetscInt mx
Definition variables.h:655
@ VTK_STRUCTURED
Definition variables.h:649
PetscErrorCode PrepareOutputCoordinates(UserCtx *user, PetscScalar **out_coords, PetscInt *out_nx, PetscInt *out_ny, PetscInt *out_nz, PetscInt *out_npoints)
Internal helper implementation: PrepareOutputCoordinates().
Definition vtk_io.c:241
Here is the call graph for this function:
Here is the caller graph for this function:

◆ AppendStructuredVTKField()

PetscErrorCode AppendStructuredVTKField ( UserCtx user,
VTKMetaData meta,
const char *  name,
Vec  field_vec,
PetscInt  components 
)

Adds one point-data field to a structured VTK file being assembled.

Skips the field with a warning when the file is already at its field limit, so a long output list degrades rather than corrupting the metadata.

Parameters
[in]userBlock context supplying the grid.
[in,out]metaMetadata to append to.
[in]nameField name as it will appear in the file.
[in]field_vecNodal vector holding the values.
[in]componentsOne for a scalar, three for a vector.
Returns
Zero on success, or a PETSc error.

Adds one point-data field to a structured VTK file being assembled.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/vtk_io.h.

See also
AppendStructuredVTKField()

Definition at line 326 of file vtk_io.c.

328{
329 VTKFieldInfo *entry = NULL;
330
331 PetscFunctionBeginUser;
332 PetscCheck(user != NULL && meta != NULL && name != NULL && field_vec != NULL,
333 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Context, metadata, name, and vector are required.");
336 "Reached the %d point-data field limit; '%s' is not written.\n",
338 PetscFunctionReturn(0);
339 }
340 entry = &meta->point_data_fields[meta->num_point_data_fields];
341 PetscCall(PetscStrncpy(entry->name, name, MAX_VTK_FIELD_NAME_LENGTH));
342 entry->num_components = components;
343 PetscCall(PrepareOutputEulerianFieldData(user, field_vec, components, &entry->data));
344 meta->num_point_data_fields++;
345 PetscFunctionReturn(0);
346}
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
#define MAX_POINT_DATA_FIELDS
Defines the maximum number of data fields for VTK point data.
Definition variables.h:590
char name[64]
Definition variables.h:642
#define MAX_VTK_FIELD_NAME_LENGTH
Maximum length for VTK field names.
Definition variables.h:591
PetscErrorCode PrepareOutputEulerianFieldData(UserCtx *user, Vec field_vec, PetscInt num_components, PetscScalar **out_data)
Implementation of PrepareOutputEulerianFieldData().
Definition vtk_io.c:377
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FinishStructuredVTKOutput()

PetscErrorCode FinishStructuredVTKOutput ( VTKMetaData meta,
const char *  filename 
)

Writes an assembled structured VTK file and releases its buffers.

Parameters
[in,out]metaMetadata to write and then release.
[in]filenameDestination path.
Returns
Zero on success, or a PETSc error.

Writes an assembled structured VTK file and releases its buffers.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/vtk_io.h.

See also
FinishStructuredVTKOutput()

Definition at line 354 of file vtk_io.c.

355{
356 PetscFunctionBeginUser;
357 PetscCheck(meta != NULL && filename != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
358 "Metadata and filename are required.");
359 PetscCall(CreateVTKFileFromMetadata(filename, meta, PETSC_COMM_WORLD));
360 for (PetscInt index = 0; index < meta->num_point_data_fields; ++index) {
361 if (meta->point_data_fields[index].data) {
362 PetscCall(PetscFree(meta->point_data_fields[index].data));
363 }
364 }
365 if (meta->coords) PetscCall(PetscFree(meta->coords));
366 meta->num_point_data_fields = 0;
367 PetscFunctionReturn(0);
368}
PetscErrorCode CreateVTKFileFromMetadata(const char *filename, const VTKMetaData *meta, MPI_Comm comm)
Implementation of CreateVTKFileFromMetadata().
Definition vtk_io.c:149
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PrepareOutputParticleData()

PetscErrorCode PrepareOutputParticleData ( UserCtx user,
PostProcessParams pps,
VTKMetaData meta,
PetscInt *  p_n_total 
)

Gathers, subsamples, and prepares all particle data for VTK output.

This function is a COLLECTIVE operation. All ranks must enter it. The heavy lifting (memory allocation, subsampling) is performed only on rank 0.

  1. Gathers the full coordinate and field data from the distributed DMSwarm to rank 0.
  2. On rank 0, subsamples the data based on pps->particle_output_freq.
  3. On rank 0, populates the VTKMetaData struct with the new, smaller, subsampled data arrays.
Parameters
[in]userThe UserCtx containing the DMSwarm.
[in]ppsThe PostProcessParams struct for configuration.
[out]metaA pointer to the VTKMetaData struct to be populated (on rank 0).
[out]p_n_totalOn rank 0, the total number of particles before subsampling.
Returns
PetscErrorCode

Gathers, subsamples, and prepares all particle data for VTK output.

Local to this translation unit.

Definition at line 486 of file vtk_io.c.

487{
488 PetscErrorCode ierr;
489 PetscInt n_total_particles, n_components;
490 PetscInt stride = pps->particle_output_freq > 0 ? pps->particle_output_freq : 1;
491
492 PetscFunctionBeginUser;
493
494 // Initialize output parameters on all ranks
495 *p_n_total = 0;
496
497 // --- The entire preparation process is a series of collective gathers followed by rank-0 processing ---
498
499 // --- Step 1: Gather Full Coordinates from SOURCE Swarm (Collective) ---
500 // This establishes the ground truth for particle positions and total count.
501 PetscReal *full_coords_arr = NULL;
502 PetscDataType coordinate_type;
503 // NOTE: This assumes SwarmFieldToArrayOnRank0 exists and works for PetscScalar fields.
504 // If not, it may need to be generalized like the logic below.
506 &n_total_particles, &n_components, &coordinate_type,
507 (void**)&full_coords_arr); CHKERRQ(ierr);
508 PetscCheck(coordinate_type == PETSC_REAL,
509 PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
510 "Particle position field must use PETSC_REAL storage, not %s.",
511 PetscDataTypes[coordinate_type]);
512
513 *p_n_total = n_total_particles;
514 if (n_total_particles == 0) {
515 ierr = PetscFree(full_coords_arr); CHKERRQ(ierr);
516 PetscFunctionReturn(0);
517 }
518 PetscCheck(n_components == 3,
519 PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
520 "Coordinate field position must have 3 components, but has %" PetscInt_FMT,
521 n_components);
522
523 // --- Step 2: Prepare and Subsample Coordinates (Rank 0 Only) ---
524 if (user->simCtx->rank == 0) {
525 // --- Subsampling Calculation ---
526 meta->npoints = (n_total_particles > 0) ? (n_total_particles - 1) / stride + 1 : 0;
527
528 LOG_ALLOW(LOCAL, LOG_DEBUG, "Subsampling %" PetscInt_FMT " total particles with stride %" PetscInt_FMT " -> %" PetscInt_FMT " output particles.\n",
529 n_total_particles, stride, meta->npoints);
530
531 // --- Prepare Final Coordinates Array ---
532 ierr = PetscMalloc1(3 * meta->npoints, &meta->coords); CHKERRQ(ierr);
533 for (PetscInt i = 0; i < meta->npoints; i++) {
534 PetscInt source_idx = i * stride;
535 for (int d = 0; d < 3; d++) meta->coords[3 * i + d] = full_coords_arr[3 * source_idx + d];
536 }
537 }
538 ierr = PetscFree(full_coords_arr); CHKERRQ(ierr);
539
540 // --- Step 3: Gather Fields (Collective), then Subsample and Store (Rank 0 Only) ---
541 char *fields_copy, *field_name;
542 PetscInt num_fields = 0;
543 ierr = PetscStrallocpy(pps->particle_fields, &fields_copy); CHKERRQ(ierr);
544 field_name = strtok(fields_copy, ",");
545 while (field_name && num_fields < MAX_POINT_DATA_FIELDS) {
546 TrimWhitespace(field_name);
547 if (!*field_name || strcasecmp(field_name, "position") == 0) {
548 field_name = strtok(NULL, ","); continue;
549 }
550
551 // A. Determine which swarm is the source for this field on every rank.
552 DM swarm_to_use = user->swarm; // Default to the main solver swarm.
553 const char* internal_name = field_name; // Default internal name to user-facing name.
554
555 PetscErrorCode check_ierr;
556 ierr = PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL); CHKERRQ(ierr);
557 check_ierr = DMSwarmGetField(user->post_swarm, field_name, NULL, NULL, NULL);
558 ierr = PetscPopErrorHandler(); CHKERRQ(ierr);
559 if (!check_ierr) {
560 swarm_to_use = user->post_swarm;
561 ierr = DMSwarmRestoreField(user->post_swarm, field_name, NULL, NULL, NULL); CHKERRQ(ierr);
562 if (user->simCtx->rank == 0) {
563 LOG_ALLOW(LOCAL, LOG_DEBUG, "Field '%s' will be sourced from the post-processing swarm.\n", field_name);
564 }
565 } else {
566 if (user->simCtx->rank == 0) {
567 LOG_ALLOW(LOCAL, LOG_DEBUG, "Field '%s' will be sourced from the main solver swarm.\n", field_name);
568 }
569 if (strcasecmp(field_name, "pid") == 0) internal_name = "DMSwarm_pid";
570 else if (strcasecmp(field_name, "CellID") == 0) internal_name = "DMSwarm_CellID";
571 else if (strcasecmp(field_name, "Migration Status") == 0) internal_name = "DMSwarm_location_status";
572 }
573
574 // B. Gather the complete field on rank 0 before applying the output stride.
575 // SwarmFieldToArrayOnRank0 handles both scalar and vector field layouts.
576 void* full_field_arr_void = NULL;
577 PetscInt field_total_particles, field_num_components;
578 PetscDataType field_data_type;
579
580 ierr = SwarmFieldToArrayOnRank0(swarm_to_use, internal_name,
581 &field_total_particles, &field_num_components,
582 &field_data_type, &full_field_arr_void); CHKERRQ(ierr);
583
584 if (field_total_particles != n_total_particles) {
585 if (user->simCtx->rank == 0) {
586 LOG_ALLOW(LOCAL, LOG_WARNING, "Field '%s' has %" PetscInt_FMT " particles, but expected %" PetscInt_FMT ". Skipping.\n", field_name, field_total_particles, n_total_particles);
587 }
588 ierr = PetscFree(full_field_arr_void); CHKERRQ(ierr);
589 field_name = strtok(NULL, ","); continue;
590 }
591
592 if (user->simCtx->rank == 0) {
593 // C. Allocate final array (ALWAYS as PetscScalar) and copy/cast subsampled data.
594 VTKFieldInfo* current_field = &meta->point_data_fields[meta->num_point_data_fields];
595 strncpy(current_field->name, field_name, MAX_VTK_FIELD_NAME_LENGTH - 1);
596 current_field->name[MAX_VTK_FIELD_NAME_LENGTH - 1] = '\0';
597 current_field->num_components = field_num_components;
598 ierr = PetscMalloc1(current_field->num_components * meta->npoints, (PetscScalar**)&current_field->data); CHKERRQ(ierr);
599
600 PetscScalar* final_data_arr = (PetscScalar*)current_field->data;
601
602 // D. Perform the subsampling and potential casting
603 if (field_data_type == PETSC_INT64) {
604 PetscInt64* source_arr = (PetscInt64*)full_field_arr_void;
605 for (PetscInt i = 0; i < meta->npoints; i++) {
606 final_data_arr[i] = (PetscScalar)source_arr[i * stride];
607 }
608 } else if (field_data_type == PETSC_INT) {
609 PetscInt* source_arr = (PetscInt*)full_field_arr_void;
610 for (PetscInt i = 0; i < meta->npoints; i++) {
611 PetscInt source_idx = i * stride;
612 for (PetscInt c = 0; c < current_field->num_components; c++) {
613 final_data_arr[current_field->num_components * i + c] = (PetscScalar)source_arr[current_field->num_components * source_idx + c];
614 }
615 }
616 } else if (field_data_type == PETSC_REAL) {
617 PetscReal* source_arr = (PetscReal*)full_field_arr_void;
618 for (PetscInt i = 0; i < meta->npoints; i++) {
619 PetscInt source_idx = i * stride;
620 for (PetscInt c = 0; c < current_field->num_components; c++) {
621 final_data_arr[current_field->num_components * i + c] = source_arr[current_field->num_components * source_idx + c];
622 }
623 }
624#if defined(PETSC_USE_COMPLEX)
625 } else if (field_data_type == PETSC_SCALAR) {
626 PetscScalar* source_arr = (PetscScalar*)full_field_arr_void;
627 for (PetscInt i = 0; i < meta->npoints; i++) {
628 PetscInt source_idx = i * stride;
629 for (PetscInt c = 0; c < current_field->num_components; c++) {
630 final_data_arr[current_field->num_components * i + c] = source_arr[current_field->num_components * source_idx + c];
631 }
632 }
633#endif
634 } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP,
635 "VTK particle output does not support field '%s' with PETSc type %s.",
636 field_name, PetscDataTypes[field_data_type]);
637
638 meta->num_point_data_fields++;
639 }
640 ierr = PetscFree(full_field_arr_void); CHKERRQ(ierr);
641 num_fields++;
642 field_name = strtok(NULL, ",");
643 }
644 ierr = PetscFree(fields_copy); CHKERRQ(ierr);
645
646 // --- Step 4: Finalize VTK MetaData for PolyData (Rank 0 Only) ---
647 if (user->simCtx->rank == 0) {
648 if (meta->npoints > 0) {
649 meta->fileType = VTK_POLYDATA;
650 ierr = PetscMalloc1(meta->npoints, &meta->connectivity); CHKERRQ(ierr);
651 ierr = PetscMalloc1(meta->npoints, &meta->offsets); CHKERRQ(ierr);
652 for (PetscInt i = 0; i < meta->npoints; i++) {
653 meta->connectivity[i] = i;
654 meta->offsets[i] = i + 1;
655 }
656 }
657 }
658
659 ierr = MPI_Barrier(PETSC_COMM_WORLD); CHKERRQ(ierr);
660
661 PetscFunctionReturn(0);
662}
PetscErrorCode SwarmFieldToArrayOnRank0(DM swarm, const char *field_name, PetscInt *n_total_particles, PetscInt *n_components, PetscDataType *field_type_out, void **gathered_array)
Gathers any DMSwarm field from all ranks to a single, contiguous array on rank 0.
Definition io.c:2686
void TrimWhitespace(char *str)
Removes leading and trailing ASCII whitespace from a mutable string.
Definition io.c:399
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:45
const char * ParticleFieldName(ParticleFieldId field_id)
Return the canonical PETSc DMSwarm name for an ID.
@ PARTICLE_FIELD_ID_POSITION
DM post_swarm
Definition variables.h:1000
PetscInt particle_output_freq
Definition variables.h:613
char particle_fields[1024]
Definition variables.h:611
Here is the call graph for this function:
Here is the caller graph for this function: