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

Go to the source code of this file.

Functions

PetscErrorCode SetupPostProcessSwarm (UserCtx *user, PostProcessParams *pps)
 Creates a new, dedicated DMSwarm for post-processing tasks.
 
PetscErrorCode WriteEulerianFile (UserCtx *user, PostProcessParams *pps, PetscInt ti)
 Orchestrates the writing of a combined, multi-field VTK file for a single time step.
 
PetscErrorCode EulerianDataProcessingPipeline (UserCtx *user, PostProcessParams *pps)
 Parses the processing pipeline string and executes the requested kernels.
 
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.
 

Function Documentation

◆ SetupPostProcessSwarm()

PetscErrorCode SetupPostProcessSwarm ( UserCtx user,
PostProcessParams pps 
)

Creates a new, dedicated DMSwarm for post-processing tasks.

This function is called once at startup. It creates an empty DMSwarm and associates it with the same grid DM as the primary swarm and registers all the required fields.

Parameters
userThe UserCtx where user->post_swarm will be created.
ppsThe PostProcessParams containing the particle_pipeline string for field registration.
Returns
PetscErrorCode

Definition at line 20 of file postprocessor.c.

21{
22 PetscErrorCode ierr;
23 PetscFunctionBeginUser;
24 char *pipeline_copy, *step_token, *step_saveptr;
25 PetscBool finalize_needed = PETSC_FALSE;
26
27 ierr = DMCreate(PETSC_COMM_WORLD, &user->post_swarm); CHKERRQ(ierr);
28 ierr = DMSetType(user->post_swarm, DMSWARM); CHKERRQ(ierr);
29 ierr = DMSetDimension(user->post_swarm, 3); CHKERRQ(ierr);
30 ierr = DMSwarmSetType(user->post_swarm, DMSWARM_BASIC); CHKERRQ(ierr);
31 // Associate it with the same grid as the solver's swarm
32 if (user->da) {
33 ierr = DMSwarmSetCellDM(user->post_swarm, user->da); CHKERRQ(ierr);
34 LOG_ALLOW(LOCAL,LOG_INFO,"Associated DMSwarm with Cell DM (user->da).\n");
35 } else {
36 // If user->da is essential for your simulation logic with particles, this should be a fatal error.
37 LOG_ALLOW(GLOBAL, LOG_WARNING, "user->da (Cell DM for Swarm) is NULL. Cell-based swarm operations might fail.\n");
38 // SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "CreateParticleSwarm - user->da (Cell DM) is NULL but required.");
39 }
40
41 LOG_ALLOW(GLOBAL, LOG_INFO, "Created dedicated DMSwarm for post-processing.\n");
42
43 LOG_ALLOW(GLOBAL, LOG_DEBUG, " --- Setting up Post-Processing Pipeline fields-- \n");
44
45 ierr = PetscStrallocpy(pps->particle_pipeline, &pipeline_copy); CHKERRQ(ierr);
46 step_token = strtok_r(pipeline_copy, ";", &step_saveptr);
47 while (step_token) {
48 TrimWhitespace(step_token);
49 if (strlen(step_token) == 0) { step_token = strtok_r(NULL, ";", &step_saveptr); continue; }
50
51 char *keyword = strtok(step_token, ":");
52 char *args_str = strtok(NULL, "");
53 TrimWhitespace(keyword);
54 PetscInt output_field_dimensions = 1; // Default to scalar output fields
55
56 if (strcasecmp(keyword, "ComputeSpecificKE") == 0) {
57 if (!args_str) SETERRQ(PETSC_COMM_SELF, 1, "Error (ComputeSpecificKE): Missing arguments.");
58 char *inputs_str = strtok(args_str, ">");
59 char *output_field = strtok(NULL, ">");
60 output_field_dimensions = 1; // SKE is scalar
61 if (!output_field) SETERRQ(PETSC_COMM_SELF, 1, "Error (ComputeSpecificKE): Missing output field in 'in>out' syntax.");
62 TrimWhitespace(output_field);
63 // Register the output field
64 ierr = RegisterSwarmField(user->post_swarm, output_field, output_field_dimensions,PETSC_REAL); CHKERRQ(ierr);
65 LOG_ALLOW(GLOBAL, LOG_INFO, "Registered particle field '%s' (ComputeSpecificKE).\n", output_field);
66 finalize_needed = PETSC_TRUE;
67 } else {
68 LOG_ALLOW(GLOBAL, LOG_WARNING, "Warning: Unknown particle transformation keyword '%s'. Skipping.\n", keyword);
69 }
70
71 // Add other 'else if' blocks here for other kernels that create output fields
72
73 step_token = strtok_r(NULL, ";", &step_saveptr);
74 } // while step_token
75
76 ierr = PetscFree(pipeline_copy); CHKERRQ(ierr);
77
78 // --- FINALIZE STEP ---
79 if (finalize_needed) {
80 LOG_ALLOW(GLOBAL, LOG_INFO, "Finalizing new field registrations for particle pipeline.\n");
81 ierr = DMSwarmFinalizeFieldRegister(user->post_swarm); CHKERRQ(ierr);
82 }
83
84 LOG_ALLOW(GLOBAL, LOG_INFO, "Post-Processing DMSwarm setup complete.\n");
85
86 PetscFunctionReturn(0);
87}
PetscErrorCode RegisterSwarmField(DM swarm, const char *fieldName, PetscInt fieldDim, PetscDataType dtype)
Registers a swarm field without finalizing registration.
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
#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_INFO
Informational messages about program execution.
Definition logging.h:32
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:33
DM post_swarm
Definition variables.h:701
char particle_pipeline[1024]
Definition variables.h:455
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteEulerianFile()

PetscErrorCode WriteEulerianFile ( UserCtx user,
PostProcessParams pps,
PetscInt  ti 
)

Orchestrates the writing of a combined, multi-field VTK file for a single time step.

This function is the primary driver for generating output. It performs these steps:

  1. Prepares the subsampled coordinate array required for the legacy grid format.
  2. Parses the user-requested list of fields from the configuration.
  3. For each field, prepares a corresponding subsampled data array.
  4. Assembles all prepared arrays into a single VTKMetaData struct.
  5. Calls the low-level VTK writer to generate the final .vts file.
  6. Frees all temporary memory allocated during the preparation phase.
Parameters
userThe UserCtx for the finest grid level.
ppsThe post-processing configuration struct.
tiThe current time step index.
Returns
PetscErrorCode

Definition at line 173 of file postprocessor.c.

174{
175 PetscErrorCode ierr;
176 VTKMetaData meta;
177 char filename[MAX_FILENAME_LENGTH];
178
179 PetscFunctionBeginUser;
180
181 if (pps->output_fields_instantaneous[0] == '\0') {
182 LOG_ALLOW(GLOBAL, LOG_DEBUG, "No instantaneous fields requested for output at ti=%" PetscInt_FMT ". Skipping.\n", ti);
183 PetscFunctionReturn(0);
184 }
185
186 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting VTK File Writing for ti = %" PetscInt_FMT " ---\n", ti);
187
188 /* 1) Metadata init */
189 ierr = PetscMemzero(&meta, sizeof(VTKMetaData)); CHKERRQ(ierr);
191
192 /* 2) Coordinates (subsampled interior) */
193 ierr = PrepareOutputCoordinates(user, &meta.coords, &meta.mx, &meta.my, &meta.mz, &meta.npoints); CHKERRQ(ierr);
194 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Using coords linearization order: fast=i mid=j slow=k (sizes: %" PetscInt_FMT " x %" PetscInt_FMT " x %" PetscInt_FMT ")\n",
195 meta.mx, meta.my, meta.mz);
196
197 /* 3) Fields (rank 0) */
198 if (user->simCtx->rank == 0) {
199 char *fields_copy, *field_name;
200 ierr = PetscStrallocpy(pps->output_fields_instantaneous, &fields_copy); CHKERRQ(ierr);
201
202 field_name = strtok(fields_copy, ",");
203 while (field_name) {
204 TrimWhitespace(field_name);
205 if (!*field_name) { field_name = strtok(NULL, ","); continue; }
206
207 LOG_ALLOW(LOCAL, LOG_DEBUG, "Preparing field '%s' for output.\n", field_name);
208
209 Vec field_vec = NULL;
210 PetscInt num_components = 0;
211
212 if (!strcasecmp(field_name, "P_nodal")) {
213 field_vec = user->P_nodal; num_components = 1;
214 } else if (!strcasecmp(field_name, "Ucat_nodal")) {
215 field_vec = user->Ucat_nodal; num_components = 3;
216 } else if (!strcasecmp(field_name, "Qcrit")) {
217 field_vec = user->Qcrit; num_components = 1;
218 } else {
219 LOG_ALLOW(LOCAL, LOG_WARNING, "Field '%s' not recognized. Skipping.\n", field_name);
220 field_name = strtok(NULL, ",");
221 continue;
222 }
223
225 LOG_ALLOW(LOCAL, LOG_WARNING, "MAX_POINT_DATA_FIELDS reached. Cannot add '%s'.\n", field_name);
226 field_name = strtok(NULL, ",");
227 continue;
228 }
229
230 // --- Add field to metadata ---
231 VTKFieldInfo* current_field = &meta.point_data_fields[meta.num_point_data_fields];
232 strncpy(current_field->name, field_name, MAX_VTK_FIELD_NAME_LENGTH-1);
233 current_field->name[MAX_VTK_FIELD_NAME_LENGTH-1] = '\0';
234 current_field->num_components = num_components;
235
236 /* Build interior AoS from NATURAL gathered Vec */
237 ierr = PrepareOutputEulerianFieldData(user, field_vec, num_components, &current_field->data); CHKERRQ(ierr);
238
239 /*
240 // *** DEBUG: Dump Ucat_nodal details and add scalar companions Ux, Uy, Uz for easier visualization ***
241
242 // If this is Ucat_nodal, dump a few tuples and add scalar companions
243 if (!strcasecmp(field_name, "Ucat_nodal")) {
244 const PetscInt npts = meta.npoints;
245 const PetscScalar *a = (const PetscScalar*)current_field->data;
246
247 LOG_ALLOW(GLOBAL, LOG_INFO, "DBG Ucat_nodal: ptr=%p npoints=%" PetscInt_FMT " num_components=%" PetscInt_FMT "\n",
248 (void*)a, npts, current_field->num_components);
249
250 if (a && current_field->num_components == 3 && npts > 0) {
251 const PetscInt nshow = (npts < 5) ? npts : 5;
252 LOG_ALLOW(GLOBAL, LOG_INFO, "DBG Ucat_nodal: showing first %d of %" PetscInt_FMT " tuples (AoS x,y,z):\n",
253 (int)nshow, npts);
254 for (PetscInt t = 0; t < nshow; ++t) {
255 LOG_ALLOW(GLOBAL, LOG_INFO, " Ucat_nodal[%3" PetscInt_FMT "] = (%g, %g, %g)\n",
256 t, (double)a[3*t+0], (double)a[3*t+1], (double)a[3*t+2]);
257 }
258 if (npts > 10) {
259 PetscInt mid = npts / 2;
260 LOG_ALLOW(GLOBAL, LOG_INFO, " Ucat_nodal[mid=%" PetscInt_FMT "] = (%g, %g, %g)\n",
261 mid, (double)a[3*mid+0], (double)a[3*mid+1], (double)a[3*mid+2]);
262 }
263
264 // Add scalar companions from the AoS we just created
265 PetscScalar *Ux=NULL,*Uy=NULL,*Uz=NULL;
266 ierr = PetscMalloc1(meta.npoints, &Ux); CHKERRQ(ierr);
267 ierr = PetscMalloc1(meta.npoints, &Uy); CHKERRQ(ierr);
268 ierr = PetscMalloc1(meta.npoints, &Uz); CHKERRQ(ierr);
269 for (PetscInt i = 0; i < meta.npoints; ++i) {
270 Ux[i] = a[3*i+0]; Uy[i] = a[3*i+1]; Uz[i] = a[3*i+2];
271 }
272
273 if (meta.num_point_data_fields + 3 <= MAX_POINT_DATA_FIELDS) {
274 VTKFieldInfo *fx = &meta.point_data_fields[++meta.num_point_data_fields];
275 strncpy(fx->name, "Ux_debug", MAX_VTK_FIELD_NAME_LENGTH-1);
276 fx->name[MAX_VTK_FIELD_NAME_LENGTH-1] = '\0';
277 fx->num_components = 1; fx->data = Ux;
278
279 VTKFieldInfo *fy = &meta.point_data_fields[++meta.num_point_data_fields];
280 strncpy(fy->name, "Uy_debug", MAX_VTK_FIELD_NAME_LENGTH-1);
281 fy->name[MAX_VTK_FIELD_NAME_LENGTH-1] = '\0';
282 fy->num_components = 1; fy->data = Uy;
283
284 VTKFieldInfo *fz = &meta.point_data_fields[++meta.num_point_data_fields];
285 strncpy(fz->name, "Uz_debug", MAX_VTK_FIELD_NAME_LENGTH-1);
286 fz->name[MAX_VTK_FIELD_NAME_LENGTH-1] = '\0';
287 fz->num_components = 1; fz->data = Uz;
288
289 LOG_ALLOW(GLOBAL, LOG_INFO, "DBG: Added scalar companions Ux_debug, Uy_debug, Uz_debug.\n");
290 } else {
291 LOG_ALLOW(GLOBAL, LOG_WARNING, "DBG: Not enough slots to add Ux/Uy/Uz debug fields.\n");
292 PetscFree(Ux); PetscFree(Uy); PetscFree(Uz);
293 }
294
295 // Mid-plane CSV + AoS vs NATURAL compare (component X)
296
297 // Gather NATURAL again (small cost, but isolated and clear)
298 PetscInt Ng = 0;
299 double *nat_d = NULL;
300 DM dmU = NULL;
301 DMDALocalInfo infU;
302 ierr = VecGetDM(field_vec, &dmU); CHKERRQ(ierr);
303 ierr = DMDAGetLocalInfo(dmU, &infU); CHKERRQ(ierr);
304
305 const PetscInt M=infU.mx, N=infU.my, P=infU.mz;
306 const PetscInt mx = meta.mx, my = meta.my, mz = meta.mz;
307 const PetscInt iInnerMid = mx/2; // interior index [0..mx-1]
308 const PetscInt iGlob = iInnerMid;
309
310 ierr = VecToArrayOnRank0(field_vec, &Ng, &nat_d); CHKERRQ(ierr);
311
312 if (nat_d) {
313 const PetscScalar *nar = (const PetscScalar*)nat_d;
314 const char *base = pps->output_prefix;
315 char fn[512], fnc[512];
316 snprintf(fn, sizeof(fn), "%s_%05" PetscInt_FMT "_iMid.csv", base, ti);
317 snprintf(fnc, sizeof(fnc), "%s_%05" PetscInt_FMT "_iMid_compare.csv", base, ti);
318
319 FILE *fp = fopen(fn, "w");
320 FILE *fpc = fopen(fnc, "w");
321 if (fp) fprintf(fp, "jInner,kInner,Ux,Uy,Uz\n");
322 if (fpc) fprintf(fpc, "jInner,kInner,Ux_AoS,Ux_NAT,abs_diff\n");
323
324 double maxAbsDiff = 0.0, sumAbs = 0.0;
325 PetscInt count = 0;
326
327 for (PetscInt kInner = 0; kInner < mz; ++kInner) {
328 const PetscInt k = kInner;
329 for (PetscInt jInner = 0; jInner < my; ++jInner) {
330 const PetscInt j = jInner;
331
332 // AoS tuple index
333 const PetscInt t = iInnerMid + mx * (jInner + my * kInner);
334 const PetscScalar ux = a[3*t+0], uy = a[3*t+1], uz = a[3*t+2];
335
336 if (fp) fprintf(fp, "%d,%d,%.15e,%.15e,%.15e\n",
337 (int)jInner,(int)kInner,(double)ux,(double)uy,(double)uz);
338
339 // NATURAL base for (iGlob,j,k)
340 const PetscInt baseNat = 3 * (((k)*N + j)*M + iGlob);
341 const PetscScalar uxN = nar[baseNat + 0];
342
343 const double diff = fabs((double)ux - (double)uxN);
344 if (diff > maxAbsDiff) maxAbsDiff = diff;
345 sumAbs += diff; ++count;
346
347 if (fpc) fprintf(fpc, "%d,%d,%.15e,%.15e,%.15e\n",
348 (int)jInner,(int)kInner,(double)ux,(double)uxN,diff);
349 } // for jInner
350 } // for kInner
351 if (fp) fclose(fp);
352 if (fpc) fclose(fpc);
353
354 if (count > 0) {
355 const double meanAbs = sumAbs / (double)count;
356 LOG_ALLOW(GLOBAL, LOG_INFO,
357 "PETSc-Vec vs AoS (i-mid, Ux): max|Δ|=%.6e, mean|Δ|=%.6e -> CSV: %s\n",
358 maxAbsDiff, meanAbs, fnc);
359 LOG_ALLOW(GLOBAL, LOG_INFO, "Wrote i-mid plane CSV: %s\n", fn);
360 } // if count>0
361
362 ierr = PetscFree(nat_d); CHKERRQ(ierr);
363
364
365 } // if nat_d
366
367 } // if a && num_components==3 && npts>0
368 } // if Ucat_nodal
369 // --- END DEBUG BLOCK (Ucat_nodal) ---
370 */
371
372 meta.num_point_data_fields++; /* count the main field we just added */
373 field_name = strtok(NULL, ",");
374 }
375
376 ierr = PetscFree(fields_copy); CHKERRQ(ierr);
377
378 // --- DEBUG: Add sanity fields i_idx, j_idx, k_idx and x_pos, y_pos, z_pos ---
379 // These are the logical indices and physical coordinates of each point in the subsampled grid.
380 // They can be used to verify the grid structure and orientation in visualization tools.
381 // They are added as scalar fields with names "i_idx", "j_idx", "k_idx" and "x_pos", "y_pos", "z_pos".
382 // Note: these are only added if there is room in the MAX_POINT_DATA_FIELDS limit.
383 // They are allocated and owned here, and will be freed below.
384 // They are in the same linearization order as meta.coords (AoS x,y,z by point).
385 /*
386 // Append sanity fields i/j/k indices and coordinates
387
388 // Build i/j/k and x/y/z (length = npoints); these match the same linearization as coords
389 const PetscInt n = meta.npoints;
390 PetscScalar *i_idx=NULL,*j_idx=NULL,*k_idx=NULL,*x_pos=NULL,*y_pos=NULL,*z_pos=NULL;
391
392 ierr = PetscMalloc1(n, &i_idx); CHKERRQ(ierr);
393 ierr = PetscMalloc1(n, &j_idx); CHKERRQ(ierr);
394 ierr = PetscMalloc1(n, &k_idx); CHKERRQ(ierr);
395 ierr = PetscMalloc1(n, &x_pos); CHKERRQ(ierr);
396 ierr = PetscMalloc1(n, &y_pos); CHKERRQ(ierr);
397 ierr = PetscMalloc1(n, &z_pos); CHKERRQ(ierr);
398
399 // coords is length 3*n, AoS: (x,y,z) by point
400 const PetscScalar *c = (const PetscScalar*)meta.coords;
401 for (PetscInt k = 0; k < meta.mz; ++k) {
402 for (PetscInt j = 0; j < meta.my; ++j) {
403 for (PetscInt i = 0; i < meta.mx; ++i) {
404 const PetscInt t = i + meta.mx * (j + meta.my * k);
405 i_idx[t] = (PetscScalar)i;
406 j_idx[t] = (PetscScalar)j;
407 k_idx[t] = (PetscScalar)k;
408 x_pos[t] = c[3*t+0];
409 y_pos[t] = c[3*t+1];
410 z_pos[t] = c[3*t+2];
411 }
412 }
413 }
414
415 const char *nf[6] = {"i_idx","j_idx","k_idx","x_pos","y_pos","z_pos"};
416 PetscScalar *arrs[6] = {i_idx,j_idx,k_idx,x_pos,y_pos,z_pos};
417 for (int s=0; s<6; ++s) {
418 if (meta.num_point_data_fields < MAX_POINT_DATA_FIELDS) {
419 VTKFieldInfo *f = &meta.point_data_fields[meta.num_point_data_fields++];
420 strncpy(f->name, nf[s], MAX_VTK_FIELD_NAME_LENGTH-1);
421 f->name[MAX_VTK_FIELD_NAME_LENGTH-1] = '\0';
422 f->num_components = 1;
423 f->data = arrs[s];
424 } else {
425 LOG_ALLOW(GLOBAL, LOG_WARNING, "Sanity field '%s' dropped: MAX_POINT_DATA_FIELDS reached.\n", nf[s]);
426 PetscFree(arrs[s]);
427 }
428 }
429 LOG_ALLOW(GLOBAL, LOG_INFO, "DBG: Added sanity fields i_idx/j_idx/k_idx and x_pos/y_pos/z_pos.\n");
430 */
431 // --- END DEBUG BLOCK (sanity fields) ---
432
433 /* Field summary */
434 LOG_ALLOW(GLOBAL, LOG_INFO, "PointData fields to write: %d\n", (int)meta.num_point_data_fields);
435 for (PetscInt ii=0; ii<meta.num_point_data_fields; ++ii) {
436 LOG_ALLOW(GLOBAL, LOG_INFO, " # %2" PetscInt_FMT " Field Name = %s Components = %d\n",
437 ii, meta.point_data_fields[ii].name, (int)meta.point_data_fields[ii].num_components);
438 }
439 } // if rank 0
440
441 /* 4) Write the VTS */
442 snprintf(filename, sizeof(filename), "%s_%05" PetscInt_FMT ".vts", pps->output_prefix, ti);
443 ierr = CreateVTKFileFromMetadata(filename, &meta, PETSC_COMM_WORLD); CHKERRQ(ierr);
444
445 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Eulerian File Writing for ti = %" PetscInt_FMT " Complete ---\n", ti);
446 PetscFunctionReturn(0);
447}
PetscInt CreateVTKFileFromMetadata(const char *filename, const VTKMetaData *meta, MPI_Comm comm)
Definition vtk_io.c:128
Vec P_nodal
Definition variables.h:702
#define MAX_POINT_DATA_FIELDS
Defines the maximum number of data fields for VTK point data.
Definition variables.h:437
PetscInt npoints
Definition variables.h:484
PetscInt num_components
Definition variables.h:471
PetscMPIInt rank
Definition variables.h:516
PetscInt num_point_data_fields
Definition variables.h:487
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:633
char output_prefix[256]
Definition variables.h:454
Vec Ucat_nodal
Definition variables.h:703
#define MAX_FILENAME_LENGTH
Definition variables.h:435
Vec Qcrit
Definition variables.h:704
VTKFileType fileType
Definition variables.h:482
char output_fields_instantaneous[1024]
Definition variables.h:452
PetscScalar * data
Definition variables.h:472
char name[64]
Definition variables.h:470
PetscScalar * coords
Definition variables.h:485
VTKFieldInfo point_data_fields[20]
Definition variables.h:486
PetscInt mz
Definition variables.h:483
PetscInt my
Definition variables.h:483
PetscInt mx
Definition variables.h:483
#define MAX_VTK_FIELD_NAME_LENGTH
Maximum length for VTK field names.
Definition variables.h:438
@ VTK_STRUCTURED
Definition variables.h:477
Stores all necessary information for a single data array in a VTK file.
Definition variables.h:469
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:287
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:223
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EulerianDataProcessingPipeline()

PetscErrorCode EulerianDataProcessingPipeline ( UserCtx user,
PostProcessParams pps 
)

Parses the processing pipeline string and executes the requested kernels.

Parameters
userThe UserCtx containing the data to be transformed.
configThe PostProcessConfig containing the pipeline string.
Returns
PetscErrorCode

Parses the processing pipeline string and executes the requested kernels.

This function uses a general-purpose parser to handle a syntax of the form: "Keyword1:in1>out1; Keyword2:in1,in2>out2; Keyword3:arg1;"

It tokenizes the pipeline string and dispatches to the appropriate kernel function from processing_kernels.c with the specified field name arguments.

Parameters
userThe UserCtx containing the data to be transformed.
ppsThe PostProcessParams struct containing the pipeline string.
Returns
PetscErrorCode

Definition at line 102 of file postprocessor.c.

103{
104 PetscErrorCode ierr;
105 char *pipeline_copy, *step_token, *step_saveptr;
106
107 PetscFunctionBeginUser;
108 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting Data Transformation Pipeline ---\n");
109
110 // Do nothing if the pipeline string is empty
111 if (pps->process_pipeline[0] == '\0') {
112 LOG_ALLOW(GLOBAL, LOG_INFO, "Processing pipeline is empty. No transformations will be run.\n");
113 PetscFunctionReturn(0);
114 }
115
116 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pipeline string: [%s]\n", pps->process_pipeline);
117
118 // Make a writable copy for strtok_r, as it modifies the string
119 ierr = PetscStrallocpy(pps->process_pipeline, &pipeline_copy); CHKERRQ(ierr);
120
121 // --- Outer Loop: Tokenize by Semicolon (;) to get each processing step ---
122 step_token = strtok_r(pipeline_copy, ";", &step_saveptr);
123 while (step_token) {
124 TrimWhitespace(step_token);
125 if (strlen(step_token) == 0) {
126 step_token = strtok_r(NULL, ";", &step_saveptr);
127 continue;
128 }
129
130 char *keyword = strtok(step_token, ":");
131 char *args_str = strtok(NULL, ""); // Get the rest of the string as arguments
132
133 if (!keyword) { // Should not happen with TrimWhitespace, but is a safe check
134 step_token = strtok_r(NULL, ";", &step_saveptr);
135 continue;
136 }
137
138 TrimWhitespace(keyword);
139 if (args_str) TrimWhitespace(args_str);
140
141 LOG_ALLOW(GLOBAL, LOG_INFO, "Executing Transformation: '%s' on args: '%s'\n", keyword, args_str ? args_str : "None");
142
143 // --- DISPATCHER: Route to the correct kernel based on the keyword ---
144 if (strcasecmp(keyword, "CellToNodeAverage") == 0) {
145 if (!args_str) SETERRQ(PETSC_COMM_SELF, 1, "CellToNodeAverage requires arguments in 'in_field>out_field' format.");
146 char *in_field = strtok(args_str, ">");
147 char *out_field = strtok(NULL, ">");
148 if (!in_field || !out_field) SETERRQ(PETSC_COMM_SELF, 1, "CellToNodeAverage requires 'in>out' syntax (e.g., P>P_nodal).");
149 TrimWhitespace(in_field); TrimWhitespace(out_field);
150 ierr = ComputeNodalAverage(user, in_field, out_field); CHKERRQ(ierr);
151 }
152 else if (strcasecmp(keyword, "ComputeQCriterion") == 0) {
153 ierr = ComputeQCriterion(user); CHKERRQ(ierr);
154 }
155 else if (strcasecmp(keyword, "NormalizeRelativeField") == 0) {
156 if (!args_str) SETERRQ(PETSC_COMM_SELF, 1, "NormalizePressure requires the pressure field name (e.g., 'P') as an argument.");
157 ierr = NormalizeRelativeField(user, args_str); CHKERRQ(ierr);
158 }
159 // *** Add new kernels here in the future using 'else if' ***
160 // else if (strcasecmp(keyword, "ComputeVorticity") == 0) { ... }
161 else {
162 LOG_ALLOW(GLOBAL, LOG_WARNING, "Unknown transformation keyword '%s'. Skipping.\n", keyword);
163 }
164
165 step_token = strtok_r(NULL, ";", &step_saveptr);
166 }
167
168 ierr = PetscFree(pipeline_copy); CHKERRQ(ierr);
169 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Data Transformation Pipeline Complete ---\n");
170 PetscFunctionReturn(0);
171}
PetscErrorCode ComputeQCriterion(UserCtx *user)
Computes the Q-Criterion, a scalar value identifying vortex cores.
PetscErrorCode NormalizeRelativeField(UserCtx *user, const char *relative_field_name)
Normalizes a relative field by subtracting a reference value.
PetscErrorCode ComputeNodalAverage(UserCtx *user, const char *in_field_name, const char *out_field_name)
Computes node-centered data by averaging 8 surrounding cell-centered values, exactly replicating the ...
char process_pipeline[1024]
Definition variables.h:451
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParticleDataProcessingPipeline()

PetscErrorCode ParticleDataProcessingPipeline ( UserCtx user,
PostProcessParams pps 
)

Parses and executes the particle pipeline using a robust two-pass approach.

This function ensures correctness and efficiency by separating field registration from kernel execution.

PASS 1 (Registration): The pipeline string is parsed to identify all new fields that will be created. These fields are registered with the DMSwarm.

Finalize: After Pass 1, DMSwarmFinalizeFieldRegister is called exactly once if any new fields were added, preparing the swarm's memory layout.

PASS 2 (Execution): The pipeline string is parsed again, and this time the actual compute kernels are executed, filling the now-valid fields.

Parameters
userThe UserCtx containing the DMSwarm.
ppsThe PostProcessParams struct containing the particle_pipeline string.
Returns
PetscErrorCode

Definition at line 468 of file postprocessor.c.

469{
470 PetscErrorCode ierr;
471 char *pipeline_copy, *step_token, *step_saveptr;
472
473 PetscFunctionBeginUser;
474
475 // --- Timestep Setup: Synchronize post_swarm size ---
476 PetscInt n_global_source;
477 ierr = DMSwarmGetSize(user->swarm, &n_global_source); CHKERRQ(ierr);
478
479 // Resize post_swarm to match source swarm
480 ierr = ResizeSwarmGlobally(user->post_swarm, n_global_source); CHKERRQ(ierr);
481
482 if (pps->particle_pipeline[0] == '\0') {
483 PetscFunctionReturn(0);
484 }
485
486 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting Particle Data Transformation Pipeline ---\n");
487 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Particle Pipeline string: [%s]\n", pps->particle_pipeline);
488
489 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Executing compute kernels...\n");
490 ierr = PetscStrallocpy(pps->particle_pipeline, &pipeline_copy); CHKERRQ(ierr);
491 step_token = strtok_r(pipeline_copy, ";", &step_saveptr);
492 while (step_token) {
493 TrimWhitespace(step_token);
494 if (strlen(step_token) == 0) { step_token = strtok_r(NULL, ";", &step_saveptr); continue; }
495
496 char *keyword = strtok(step_token, ":");
497 char *args_str = strtok(NULL, "");
498 TrimWhitespace(keyword);
499 if (args_str) TrimWhitespace(args_str);
500
501 LOG_ALLOW(GLOBAL, LOG_INFO, "Executing Particle Transformation: '%s' on args: '%s'\n", keyword, args_str ? args_str : "None");
502
503 if (strcasecmp(keyword, "ComputeSpecificKE") == 0) {
504 char *velocity_field = strtok(args_str, ">");
505 char *ske_field = strtok(NULL, ">");
506 TrimWhitespace(velocity_field); TrimWhitespace(ske_field);
507
508 ierr = ComputeSpecificKE(user, velocity_field, ske_field); CHKERRQ(ierr);
509 }
510 else {
511 LOG_ALLOW(GLOBAL, LOG_WARNING, "Unknown particle transformation keyword '%s'. Skipping.\n", keyword);
512 }
513
514 step_token = strtok_r(NULL, ";", &step_saveptr);
515 }
516 ierr = PetscFree(pipeline_copy); CHKERRQ(ierr);
517
518 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Particle Data Transformation Pipeline Complete ---\n");
519 PetscFunctionReturn(0);
520}
PetscErrorCode ResizeSwarmGlobally(DM swarm, PetscInt N_target)
PetscErrorCode ComputeSpecificKE(UserCtx *user, const char *velocity_field, const char *ske_field)
Computes the specific kinetic energy (KE per unit mass) for each particle.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteParticleFile()

PetscErrorCode WriteParticleFile ( UserCtx user,
PostProcessParams pps,
PetscInt  ti 
)

Writes particle data to a VTP file using the Prepare-Write-Cleanup pattern.

Definition at line 526 of file postprocessor.c.

527{
528 PetscErrorCode ierr;
529 VTKMetaData part_meta;
530 char filename[MAX_FILENAME_LENGTH];
531 PetscInt n_total_particles_before_subsample;
532
533 PetscFunctionBeginUser;
534
535 // These checks can be done on all ranks
536 if (!pps->outputParticles || pps->particle_fields[0] == '\0') {
537 PetscFunctionReturn(0);
538 }
539 PetscInt n_global;
540 ierr = DMSwarmGetSize(user->swarm, &n_global); CHKERRQ(ierr);
541 if (n_global == 0) {
542 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Swarm is empty for ti=%" PetscInt_FMT ". Skipping particle file write.\n", ti);
543 PetscFunctionReturn(0);
544 }
545
546 ierr = PetscMemzero(&part_meta, sizeof(VTKMetaData)); CHKERRQ(ierr);
547
548 // --- 1. PREPARE (Collective Call) ---
549 ierr = PrepareOutputParticleData(user, pps, &part_meta, &n_total_particles_before_subsample); CHKERRQ(ierr);
550
551 // --- 2. WRITE and CLEANUP (Rank 0 only) ---
552 if (user->simCtx->rank == 0) {
553 if (part_meta.npoints > 0) {
554 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting VTP Particle File Writing for ti = %" PetscInt_FMT " (writing %" PetscInt_FMT " of %" PetscInt_FMT " particles) ---\n",
555 ti, part_meta.npoints, n_total_particles_before_subsample);
556
557 /* Field summary */
558 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Data fields to write: %d\n", (int)part_meta.num_point_data_fields);
559 for (PetscInt ii=0; ii<part_meta.num_point_data_fields; ++ii) {
560 LOG_ALLOW(GLOBAL, LOG_INFO, " # %2" PetscInt_FMT " Field Name = %s Components = %d\n",
561 ii, part_meta.point_data_fields[ii].name, (int)part_meta.point_data_fields[ii].num_components);
562 }
563
564 snprintf(filename, sizeof(filename), "%s_%05" PetscInt_FMT ".vtp", pps->particle_output_prefix, ti);
565 ierr = CreateVTKFileFromMetadata(filename, &part_meta, PETSC_COMM_WORLD); CHKERRQ(ierr);
566
567 } else {
568 LOG_ALLOW(GLOBAL, LOG_DEBUG, "No particles to write at ti=%" PetscInt_FMT " after subsampling. Skipping.\n", ti);
569 }
570
571 }
572
573 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Particle File Writing for ti = %" PetscInt_FMT " Complete ---\n", ti);
574 PetscFunctionReturn(0);
575}
char particle_output_prefix[256]
Definition variables.h:457
char particle_fields[1024]
Definition variables.h:456
PetscBool outputParticles
Definition variables.h:448
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:421
Here is the call graph for this function:
Here is the caller graph for this function: