PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
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 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.

Definition at line 128 of file vtk_io.c.

129{
130 PetscMPIInt rank;
131 MPI_Comm_rank(comm, &rank);
132 PetscErrorCode ierr = 0;
133
134 if (!rank) {
135 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank 0 writing combined VTK file '%s'.\n", filename);
136 FILE *fp = fopen(filename, "wb");
137 if (!fp) {
138 LOG_ALLOW(GLOBAL, LOG_ERROR, "fopen failed for %s.\n", filename);
139 return PETSC_ERR_FILE_OPEN;
140 }
141
142 PetscInt boffset = 0;
143
144 ierr = WriteVTKFileHeader(fp, meta, &boffset);
145 if(ierr) { fclose(fp); return ierr; }
146
147 if (meta->coords) {
148 ierr = WriteVTKAppendedBlock(fp, meta->coords, 3 * meta->npoints, sizeof(PetscScalar));
149 if(ierr) { fclose(fp); return ierr; }
150 }
151 /*
152 // ======== DEBUG: Dump first few values of Ucat_nodal if present ========
153 if (!rank) {
154 for (PetscInt i = 0; i < meta->num_point_data_fields; i++) {
155 const VTKFieldInfo* f = &meta->point_data_fields[i];
156 if (strcasecmp(f->name, "Ucat_nodal") == 0) {
157 const PetscScalar *a = f->data;
158 const PetscInt npts = meta->npoints;
159 const PetscInt nshow = (npts < 5) ? npts : 5;
160
161 LOG_ALLOW(GLOBAL, LOG_INFO,
162 "DBG (writer) Ucat_nodal: first %d of %" PetscInt_FMT " tuples:\n",
163 (int)nshow, npts);
164 for (PetscInt t = 0; t < nshow; ++t) {
165 LOG_ALLOW(GLOBAL, LOG_INFO,
166 " Ucat_nodal[%3" PetscInt_FMT "] = (%g, %g, %g)\n",
167 t, (double)a[3*t+0], (double)a[3*t+1], (double)a[3*t+2]);
168 }
169 }
170 }
171 }
172 */
173 // ======== END DEBUG ========
174
175
176 for (PetscInt i = 0; i < meta->num_point_data_fields; i++) {
177 const VTKFieldInfo* field = &meta->point_data_fields[i];
178 if (field->data) {
179 ierr = WriteVTKAppendedBlock(fp, field->data, field->num_components * meta->npoints, sizeof(PetscScalar));
180 if(ierr) { fclose(fp); return ierr; }
181 }
182 }
183 if (meta->fileType == VTK_POLYDATA) {
184 if (meta->connectivity) {
185 ierr = WriteVTKAppendedBlock(fp, meta->connectivity, meta->npoints, sizeof(PetscInt));
186 if(ierr) { fclose(fp); return ierr; }
187 }
188 if (meta->offsets) {
189 ierr = WriteVTKAppendedBlock(fp, meta->offsets, meta->npoints, sizeof(PetscInt));
190 if(ierr) { fclose(fp); return ierr; }
191 }
192 }
193 ierr = WriteVTKFileFooter(fp, meta);
194 if(ierr) { fclose(fp); return ierr; }
195
196 fclose(fp);
197 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank 0 finished writing VTK file '%s'.\n", filename);
198 }
199 return 0;
200}
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:45
#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:207
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:29
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:32
PetscInt npoints
Definition variables.h:484
PetscInt num_components
Definition variables.h:471
PetscInt num_point_data_fields
Definition variables.h:487
PetscInt * connectivity
Definition variables.h:488
PetscInt * offsets
Definition variables.h:489
VTKFileType fileType
Definition variables.h:482
PetscScalar * data
Definition variables.h:472
PetscScalar * coords
Definition variables.h:485
VTKFieldInfo point_data_fields[20]
Definition variables.h:486
@ VTK_POLYDATA
Definition variables.h:478
Stores all necessary information for a single data array in a VTK file.
Definition variables.h:469
static PetscErrorCode WriteVTKFileFooter(FILE *fp, const VTKMetaData *meta)
Definition vtk_io.c:116
static PetscErrorCode WriteVTKAppendedBlock(FILE *fp, const void *data, PetscInt num_elements, size_t element_size)
Definition vtk_io.c:17
static PetscErrorCode WriteVTKFileHeader(FILE *fp, const VTKMetaData *meta, PetscInt *boffset)
Definition vtk_io.c:106

◆ 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

Definition at line 223 of file vtk_io.c.

224{
225 PetscErrorCode ierr;
226 Vec coords_global;
227 PetscInt IM, JM, KM;
228 PetscInt N_full_coords;
229 PetscScalar *full_coords_arr = NULL;
230
231 PetscFunctionBeginUser;
232 ierr = DMDAGetInfo(user->da, NULL, &IM, &JM, &KM, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); CHKERRQ(ierr);
233
234 // Set the dimensions of the output grid
235 *out_nx = IM - 1;
236 *out_ny = JM - 1;
237 *out_nz = KM - 1;
238 *out_npoints = (*out_nx) * (*out_ny) * (*out_nz);
239
240 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);
241
242 // --- Step 1: Gather the full coordinate vector to rank 0 ---
243 ierr = DMGetCoordinates(user->da, &coords_global); CHKERRQ(ierr);
244 // Reuse of your existing, proven utility function's logic
245 ierr = VecToArrayOnRank0(coords_global, &N_full_coords, &full_coords_arr); CHKERRQ(ierr);
246
247 // --- Step 2: On rank 0, subsample the gathered array ---
248 if (user->simCtx->rank == 0) {
249 // We expect N_full_coords to be IM * JM * KM * 3
250 if (N_full_coords != IM * JM * KM * 3) {
251 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);
252 }
253
254 // Allocate the smaller output C array
255 ierr = PetscMalloc1(3 * (*out_npoints), out_coords); CHKERRQ(ierr);
256
257 // Loop over the smaller grid dimensions and copy data
258 PetscInt p_out = 0; // Index for the small output array
259 for (PetscInt k = 0; k < *out_nz; k++) {
260 for (PetscInt j = 0; j < *out_ny; j++) {
261 for (PetscInt i = 0; i < *out_nx; i++) {
262 // Calculate the index in the full, 1D source array
263 PetscInt p_in = 3 * (k * (JM * IM) + j * IM + i);
264
265 (*out_coords)[p_out++] = full_coords_arr[p_in + 0]; // x
266 (*out_coords)[p_out++] = full_coords_arr[p_in + 1]; // y
267 (*out_coords)[p_out++] = full_coords_arr[p_in + 2]; // z
268 }
269 }
270 }
271 // Free the temporary full array that was allocated by VecToArrayOnRank0
272 ierr = PetscFree(full_coords_arr); CHKERRQ(ierr);
273 } else {
274 // On other ranks, the output pointer is NULL
275 *out_coords = NULL;
276 }
277
278 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Subsampled coordinates prepared on rank 0 with total %" PetscInt_FMT " points.\n", *out_npoints);
279
280 PetscFunctionReturn(0);
281}
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:1619
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:33
PetscMPIInt rank
Definition variables.h:516
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:633
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

Definition at line 287 of file vtk_io.c.

288{
289 PetscErrorCode ierr;
290 PetscInt IM, JM, KM, nx, ny, nz, npoints;
291 PetscInt N_full_field = 0;
292 PetscScalar *full_field_arr = NULL;
293
294 PetscFunctionBeginUser;
295 ierr = DMDAGetInfo(user->da, NULL, &IM, &JM, &KM, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); CHKERRQ(ierr);
296 nx = IM - 1; ny = JM - 1; nz = KM - 1;
297 npoints = nx * ny * nz;
298
300 "Preparing subsampled field data (dof=%" PetscInt_FMT ") for a %" PetscInt_FMT "x%" PetscInt_FMT "x%" PetscInt_FMT " grid.\n",
301 num_components, nx, ny, nz);
302
303 // --- Step 1: Gather the full field vector to rank 0 ---
304 ierr = VecToArrayOnRank0(field_vec, &N_full_field, &full_field_arr); CHKERRQ(ierr);
305
306 // --- Step 2: On rank 0, subsample the gathered array ---
307 if (user->simCtx->rank == 0) {
308 // Sanity check the size of the gathered array
309 if (N_full_field != IM * JM * KM * num_components) {
310 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED,
311 "Gathered field array has wrong size. Expected %" PetscInt_FMT ", got %" PetscInt_FMT,
312 IM * JM * KM * num_components, N_full_field);
313 }
314
315 // Allocate the smaller output C array (AoS layout expected by your writer)
316 ierr = PetscMalloc1(num_components * npoints, out_data); CHKERRQ(ierr);
317
318 /*
319 // ======== Layout diagnostics (rank 0 only, read-only) ========
320 if (num_components > 1 && full_field_arr) {
321 const PetscInt n_full = IM * JM * KM;
322
323 // Choose a safe interior probe point and its +i neighbor
324 const PetscInt ic = (IM > 4) ? IM/2 : 1;
325 const PetscInt jc = (JM > 4) ? JM/2 : 1;
326 const PetscInt kc = (KM > 4) ? KM/2 : 1;
327 const PetscInt ic1 = (ic + 1 < IM-1) ? (ic + 1) : ic; // stay interior
328
329 const PetscInt t_full_c = (kc * JM + jc) * IM + ic;
330 const PetscInt t_full_c1 = (kc * JM + jc) * IM + ic1;
331
332 // If source were AoS: full[num_components * t + comp]
333 PetscScalar aos_c0 = full_field_arr[num_components * t_full_c + 0];
334 PetscScalar aos_c1 = full_field_arr[num_components * t_full_c1 + 0];
335 PetscScalar aos_c0_y = (num_components > 1) ? full_field_arr[num_components * t_full_c + 1] : 0.0;
336 PetscScalar aos_c0_z = (num_components > 2) ? full_field_arr[num_components * t_full_c + 2] : 0.0;
337
338 // If source were SoA: full[comp * n_full + t]
339 PetscScalar soa_c0 = full_field_arr[0 * n_full + t_full_c];
340 PetscScalar soa_c1 = full_field_arr[0 * n_full + t_full_c1];
341 PetscScalar soa_c0_y = (num_components > 1) ? full_field_arr[1 * n_full + t_full_c] : 0.0;
342 PetscScalar soa_c0_z = (num_components > 2) ? full_field_arr[2 * n_full + t_full_c] : 0.0;
343
344 double dAOS = fabs((double)(aos_c1 - aos_c0)); // spatial delta along +i
345 double dSOA = fabs((double)(soa_c1 - soa_c0));
346
347 const char *verdict = (dSOA < dAOS) ? "LIKELY SoA (component-major)" : "LIKELY AoS (interleaved)";
348
349 LOG_ALLOW(GLOBAL, LOG_INFO,
350 "Layout probe @ (i=%" PetscInt_FMT ", j=%" PetscInt_FMT ", k=%" PetscInt_FMT ") vs +i neighbor:\n"
351 " AoS-candidate: Ux(center)=%.6e Ux(+i)=%.6e |Δ|=%.6e ; Uy(center)=%.6e Uz(center)=%.6e\n"
352 " SoA-candidate: Ux(center)=%.6e Ux(+i)=%.6e |Δ|=%.6e ; Uy(center)=%.6e Uz(center)=%.6e\n"
353 " Verdict: %s\n",
354 ic, jc, kc,
355 (double)aos_c0, (double)aos_c1, dAOS, (double)aos_c0_y, (double)aos_c0_z,
356 (double)soa_c0, (double)soa_c1, dSOA, (double)soa_c0_y, (double)soa_c0_z,
357 verdict
358 );
359 } else if (num_components == 1) {
360 LOG_ALLOW(GLOBAL, LOG_INFO, "Layout probe: scalar field (dof=1) — no component interleave to detect.\n");
361 }
362 // ======== END diagnostics ========
363 */
364
365 // --- PACK: assumes source is already AoS ---
366 {
367 PetscInt p_out = 0;
368 for (PetscInt k = 0; k < nz; k++) {
369 for (PetscInt j = 0; j < ny; j++) {
370 for (PetscInt i = 0; i < nx; i++) {
371 const PetscInt p_in_start = num_components * (k * (JM * IM) + j * IM + i);
372 for (PetscInt c = 0; c < num_components; c++) {
373 (*out_data)[p_out++] = full_field_arr[p_in_start + c];
374 }
375 }
376 }
377 }
378 }
379
380 // Free temporary gathered array
381 ierr = PetscFree(full_field_arr); CHKERRQ(ierr);
382 } else {
383 // Other ranks: no allocation
384 *out_data = NULL;
385 }
386
387 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Subsampled field data prepared on rank 0 with total %" PetscInt_FMT " points.\n", npoints);
388
389 PetscFunctionReturn(0);
390}
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.

This function orchestrates the preparation of particle data for writing. It is a COLLECTIVE operation that all MPI ranks must enter, though the primary work of data aggregation and preparation is performed on rank 0.

The function follows a "merge-and-prepare" strategy:

  1. It treats user->swarm as the read-only source of primary data (e.g., positions, velocity) that was loaded from disk.
  2. It treats user->pp_swarm as the source of derived data (e.g., specific_ke) that was computed by the post-processing pipeline.
  3. On Rank 0 only: a. It gathers the full particle coordinates from the source swarm. This determines the total number of particles before any subsampling. b. It parses the pps->particle_fields list. For each requested field, it determines whether to source it from user->swarm or user->pp_swarm. c. It gathers the full data for each requested field from its appropriate source into a generic buffer. d. It performs strided subsampling. During this step, any integer fields (like pid, CellID) are CAST to PetscScalar, so that all output arrays are of the same floating-point type. e. It populates the VTKMetaData struct with the final, subsampled, scalar-only data arrays, making it ready for the file writer.
Parameters
[in]userThe UserCtx, containing both user->swarm and user->pp_swarm.
[in]ppsThe PostProcessParams struct for configuration (field list, frequency).
[out]metaA pointer to the VTKMetaData struct to be populated (on rank 0).
[out]p_n_totalA pointer to store the total number of particles before subsampling (on rank 0).
Returns
PetscErrorCode

Definition at line 421 of file vtk_io.c.

422{
423 PetscErrorCode ierr;
424 PetscInt n_total_particles, n_components;
425
426 PetscFunctionBeginUser;
427
428 // Initialize output parameters on all ranks
429 *p_n_total = 0;
430
431 // --- The entire preparation process is a series of collective gathers followed by rank-0 processing ---
432
433 // --- Step 1: Gather Full Coordinates from SOURCE Swarm (Collective) ---
434 // This establishes the ground truth for particle positions and total count.
435 PetscScalar *full_coords_arr = NULL;
436 // NOTE: This assumes SwarmFieldToArrayOnRank0 exists and works for PetscScalar fields.
437 // If not, it may need to be generalized like the logic below.
438 ierr = SwarmFieldToArrayOnRank0(user->swarm, "position", &n_total_particles, &n_components, &full_coords_arr); CHKERRQ(ierr);
439
440 // --- Step 2: Prepare and Subsample Data (Rank 0 Only) ---
441 if (user->simCtx->rank == 0) {
442 *p_n_total = n_total_particles; // Report original count back to the caller.
443
444 if (n_total_particles == 0) {
445 if (full_coords_arr) {
446 ierr = PetscFree(full_coords_arr); CHKERRQ(ierr); // Cleanup the gathered coords array
447 }
448 PetscFunctionReturn(0); // Nothing to prepare.
449 }
450 if (n_components != 3) {
451 SETERRQ(PETSC_COMM_SELF, 1, "Coordinate field position must have 3 components, but has %" PetscInt_FMT, n_components);
452 }
453
454 // --- Subsampling Calculation ---
455 PetscInt stride = pps->particle_output_freq > 0 ? pps->particle_output_freq : 1;
456 meta->npoints = (n_total_particles > 0) ? (n_total_particles - 1) / stride + 1 : 0;
457
458 LOG_ALLOW(LOCAL, LOG_DEBUG, "Subsampling %" PetscInt_FMT " total particles with stride %" PetscInt_FMT " -> %" PetscInt_FMT " output particles.\n",
459 n_total_particles, stride, meta->npoints);
460
461 // --- Prepare Final Coordinates Array ---
462 ierr = PetscMalloc1(3 * meta->npoints, &meta->coords); CHKERRQ(ierr);
463 for (PetscInt i = 0; i < meta->npoints; i++) {
464 PetscInt source_idx = i * stride;
465 for (int d = 0; d < 3; d++) meta->coords[3 * i + d] = full_coords_arr[3 * source_idx + d];
466 }
467 ierr = PetscFree(full_coords_arr); // Free the temporary full array immediately after use.
468
469
470 // --- Prepare Final Field Arrays (Gather -> Subsample -> Store) ---
471 char *fields_copy, *field_name;
472 ierr = PetscStrallocpy(pps->particle_fields, &fields_copy); CHKERRQ(ierr);
473 field_name = strtok(fields_copy, ",");
474 while (field_name && meta->num_point_data_fields < MAX_POINT_DATA_FIELDS) {
475 TrimWhitespace(field_name);
476 if (!*field_name || strcasecmp(field_name, "position") == 0) {
477 field_name = strtok(NULL, ","); continue;
478 }
479
480 // A. Determine which swarm is the source for this field.
481 DM swarm_to_use = user->swarm; // Default to the main solver swarm.
482 const char* internal_name = field_name; // Default internal name to user-facing name.
483
484 PetscErrorCode check_ierr;
485 ierr = PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL); CHKERRQ(ierr);
486 check_ierr = DMSwarmGetField(user->post_swarm, field_name, NULL, NULL, NULL);
487 ierr = PetscPopErrorHandler(); CHKERRQ(ierr);
488 if (!check_ierr) {
489 swarm_to_use = user->post_swarm;
490 ierr = DMSwarmRestoreField(user->post_swarm, field_name, NULL, NULL, NULL); CHKERRQ(ierr);
491 LOG_ALLOW(LOCAL, LOG_DEBUG, "Field '%s' will be sourced from the post-processing swarm.\n", field_name);
492 } else {
493 LOG_ALLOW(LOCAL, LOG_DEBUG, "Field '%s' will be sourced from the main solver swarm.\n", field_name);
494 if (strcasecmp(field_name, "pid") == 0) internal_name = "DMSwarm_pid";
495 else if (strcasecmp(field_name, "CellID") == 0) internal_name = "DMSwarm_CellID";
496 else if (strcasecmp(field_name, "Migration Status") == 0) internal_name = "DMSwarm_location_status";
497 }
498
499 // B. Gather the full data for this field from its determined source.
500 // NOTE: This assumes a generic gather function exists.
501 // A simplified placeholder is used here; you may need to implement this.
502 void* full_field_arr_void = NULL;
503 PetscInt field_total_particles, field_num_components;
504
505 ierr = SwarmFieldToArrayOnRank0(swarm_to_use, internal_name, &field_total_particles, &field_num_components, &full_field_arr_void); CHKERRQ(ierr);
506
507 if (field_total_particles != n_total_particles) {
508 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);
509 ierr = PetscFree(full_field_arr_void); CHKERRQ(ierr);
510 field_name = strtok(NULL, ","); continue;
511 }
512
513 // C. Allocate final array (ALWAYS as PetscScalar) and copy/cast subsampled data.
514 VTKFieldInfo* current_field = &meta->point_data_fields[meta->num_point_data_fields];
515 strncpy(current_field->name, field_name, MAX_VTK_FIELD_NAME_LENGTH - 1);
516 current_field->name[MAX_VTK_FIELD_NAME_LENGTH - 1] = '\0';
517 current_field->num_components = field_num_components;
518 ierr = PetscMalloc1(current_field->num_components * meta->npoints, (PetscScalar**)&current_field->data); CHKERRQ(ierr);
519
520 PetscScalar* final_data_arr = (PetscScalar*)current_field->data;
521
522 // D. Perform the subsampling and potential casting
523 if (strcasecmp(internal_name, "DMSwarm_pid") == 0) {
524 PetscInt64* source_arr = (PetscInt64*)full_field_arr_void;
525 for (PetscInt i = 0; i < meta->npoints; i++) {
526 final_data_arr[i] = (PetscScalar)source_arr[i * stride];
527 }
528 } else if (strcasecmp(internal_name, "DMSwarm_CellID") == 0 || strcasecmp(internal_name, "DMSwarm_location_status") == 0) {
529 PetscInt* source_arr = (PetscInt*)full_field_arr_void;
530 for (PetscInt i = 0; i < meta->npoints; i++) {
531 PetscInt source_idx = i * stride;
532 for (PetscInt c = 0; c < current_field->num_components; c++) {
533 final_data_arr[current_field->num_components * i + c] = (PetscScalar)source_arr[current_field->num_components * source_idx + c];
534 }
535 }
536 } else { // Default case: The field is already PetscScalar
537 PetscScalar* source_arr = (PetscScalar*)full_field_arr_void;
538 for (PetscInt i = 0; i < meta->npoints; i++) {
539 PetscInt source_idx = i * stride;
540 for (PetscInt c = 0; c < current_field->num_components; c++) {
541 final_data_arr[current_field->num_components * i + c] = source_arr[current_field->num_components * source_idx + c];
542 }
543 }
544 }
545
546 ierr = PetscFree(full_field_arr_void); // Free the temporary full array.
547 meta->num_point_data_fields++;
548 field_name = strtok(NULL, ",");
549 }
550 ierr = PetscFree(fields_copy); CHKERRQ(ierr);
551
552 // --- Finalize VTK MetaData for PolyData ---
553 if (meta->npoints > 0) {
554 meta->fileType = VTK_POLYDATA;
555 ierr = PetscMalloc1(meta->npoints, &meta->connectivity); CHKERRQ(ierr);
556 ierr = PetscMalloc1(meta->npoints, &meta->offsets); CHKERRQ(ierr);
557 for (PetscInt i = 0; i < meta->npoints; i++) {
558 meta->connectivity[i] = i;
559 meta->offsets[i] = i + 1;
560 }
561 }
562 } // End of rank 0 block
563
564 ierr = MPI_Barrier(PETSC_COMM_WORLD); CHKERRQ(ierr);
565
566 PetscFunctionReturn(0);
567}
PetscErrorCode SwarmFieldToArrayOnRank0(DM swarm, const char *field_name, PetscInt *n_total_particles, PetscInt *n_components, void **gathered_array)
Gathers any DMSwarm field from all ranks to a single, contiguous array on rank 0.
Definition io.c:1714
void TrimWhitespace(char *str)
Helper function to trim leading/trailing whitespace from a string.
Definition io.c:1910
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:44
@ 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:437
DM post_swarm
Definition variables.h:701
char name[64]
Definition variables.h:470
PetscInt particle_output_freq
Definition variables.h:458
char particle_fields[1024]
Definition variables.h:456
#define MAX_VTK_FIELD_NAME_LENGTH
Maximum length for VTK field names.
Definition variables.h:438
Here is the call graph for this function:
Here is the caller graph for this function: