PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Functions
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 "particle_statistics.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.
 
PetscErrorCode GlobalStatisticsPipeline (UserCtx *user, PostProcessParams *pps, PetscInt ti)
 Executes the global statistics pipeline, computing aggregate reductions over all particles.
 
PetscErrorCode FieldStatisticsPipeline (UserCtx *user, PostProcessParams *pps, PetscInt ti)
 Derives and writes field statistics for the windows a recipe requests.
 

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

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

Local to this translation unit.

Definition at line 21 of file postprocessor.c.

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

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

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

See also
WriteEulerianFile()

Definition at line 201 of file postprocessor.c.

202{
203 PetscErrorCode ierr;
204 VTKMetaData meta;
205 char filename[MAX_FILENAME_LENGTH];
206
207 PetscFunctionBeginUser;
209
210 if (pps->output_fields_instantaneous[0] == '\0') {
211 LOG_ALLOW(GLOBAL, LOG_DEBUG, "No instantaneous fields requested for output at ti=%" PetscInt_FMT ". Skipping.\n", ti);
213 PetscFunctionReturn(0);
214 }
215
216 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting VTK File Writing for ti = %" PetscInt_FMT " ---\n", ti);
217
218 /* 1) Metadata init */
219 /* 2) Metadata and coordinates, through the shared assembly the statistics
220 * stage also uses, so both producers build a file the same way. */
221 ierr = BeginStructuredVTKOutput(user, &meta); CHKERRQ(ierr);
222 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",
223 meta.mx, meta.my, meta.mz);
224
225 /* 3) Field preparation is collective because each append gathers a
226 * distributed Vec. Rank zero alone owns the packed output buffers, but
227 * every rank must resolve the same field list and enter each append. */
228 {
229 char *fields_copy, *field_name;
230 ierr = PetscStrallocpy(pps->output_fields_instantaneous, &fields_copy); CHKERRQ(ierr);
231
232 field_name = strtok(fields_copy, ",");
233 while (field_name) {
234 TrimWhitespace(field_name);
235 if (!*field_name) { field_name = strtok(NULL, ","); continue; }
236
237 LOG_ALLOW(LOCAL, LOG_DEBUG, "Preparing field '%s' for output.\n", field_name);
238
239 Vec field_vec = NULL;
240 PetscInt num_components = 0;
241
242 if (!strcasecmp(field_name, "P_nodal")) {
243 field_vec = user->P_nodal; num_components = 1;
244 } else if (!strcasecmp(field_name, "Ucat_nodal")) {
245 field_vec = user->Ucat_nodal; num_components = 3;
246 } else if (!strcasecmp(field_name, "Qcrit")) {
247 field_vec = user->Qcrit; num_components = 1;
248 } else if (!strcasecmp(field_name, "Psi_nodal")){
249 if(user->simCtx->np==0){
250 LOG_ALLOW(LOCAL, LOG_WARNING, "Field 'Psi_nodal' requested but no particles are present. Skipping.\n");
251 field_name = strtok(NULL, ",");
252 continue;
253 }
254 field_vec = user->Psi_nodal; num_components = 1;
255 } else {
256 LOG_ALLOW(LOCAL, LOG_WARNING, "Field '%s' not recognized. Skipping.\n", field_name);
257 field_name = strtok(NULL, ",");
258 continue;
259 }
260
261 // --- Add field to metadata ---
262 ierr = AppendStructuredVTKField(user, &meta, field_name, field_vec, num_components); CHKERRQ(ierr);
263
264 /*
265 // *** DEBUG: Dump Ucat_nodal details and add scalar companions Ux, Uy, Uz for easier visualization ***
266
267 // If this is Ucat_nodal, dump a few tuples and add scalar companions
268 if (!strcasecmp(field_name, "Ucat_nodal")) {
269 const PetscInt npts = meta.npoints;
270 const PetscScalar *a = (const PetscScalar*)current_field->data;
271
272 LOG_ALLOW(GLOBAL, LOG_INFO, "DBG Ucat_nodal: ptr=%p npoints=%" PetscInt_FMT " num_components=%" PetscInt_FMT "\n",
273 (void*)a, npts, current_field->num_components);
274
275 if (a && current_field->num_components == 3 && npts > 0) {
276 const PetscInt nshow = (npts < 5) ? npts : 5;
277 LOG_ALLOW(GLOBAL, LOG_INFO, "DBG Ucat_nodal: showing first %d of %" PetscInt_FMT " tuples (AoS x,y,z):\n",
278 (int)nshow, npts);
279 for (PetscInt t = 0; t < nshow; ++t) {
280 LOG_ALLOW(GLOBAL, LOG_INFO, " Ucat_nodal[%3" PetscInt_FMT "] = (%g, %g, %g)\n",
281 t, (double)a[3*t+0], (double)a[3*t+1], (double)a[3*t+2]);
282 }
283 if (npts > 10) {
284 PetscInt mid = npts / 2;
285 LOG_ALLOW(GLOBAL, LOG_INFO, " Ucat_nodal[mid=%" PetscInt_FMT "] = (%g, %g, %g)\n",
286 mid, (double)a[3*mid+0], (double)a[3*mid+1], (double)a[3*mid+2]);
287 }
288
289 // Add scalar companions from the AoS we just created
290 PetscScalar *Ux=NULL,*Uy=NULL,*Uz=NULL;
291 ierr = PetscMalloc1(meta.npoints, &Ux); CHKERRQ(ierr);
292 ierr = PetscMalloc1(meta.npoints, &Uy); CHKERRQ(ierr);
293 ierr = PetscMalloc1(meta.npoints, &Uz); CHKERRQ(ierr);
294 for (PetscInt i = 0; i < meta.npoints; ++i) {
295 Ux[i] = a[3*i+0]; Uy[i] = a[3*i+1]; Uz[i] = a[3*i+2];
296 }
297
298 if (meta.num_point_data_fields + 3 <= MAX_POINT_DATA_FIELDS) {
299 VTKFieldInfo *fx = &meta.point_data_fields[++meta.num_point_data_fields];
300 strncpy(fx->name, "Ux_debug", MAX_VTK_FIELD_NAME_LENGTH-1);
301 fx->name[MAX_VTK_FIELD_NAME_LENGTH-1] = '\0';
302 fx->num_components = 1; fx->data = Ux;
303
304 VTKFieldInfo *fy = &meta.point_data_fields[++meta.num_point_data_fields];
305 strncpy(fy->name, "Uy_debug", MAX_VTK_FIELD_NAME_LENGTH-1);
306 fy->name[MAX_VTK_FIELD_NAME_LENGTH-1] = '\0';
307 fy->num_components = 1; fy->data = Uy;
308
309 VTKFieldInfo *fz = &meta.point_data_fields[++meta.num_point_data_fields];
310 strncpy(fz->name, "Uz_debug", MAX_VTK_FIELD_NAME_LENGTH-1);
311 fz->name[MAX_VTK_FIELD_NAME_LENGTH-1] = '\0';
312 fz->num_components = 1; fz->data = Uz;
313
314 LOG_ALLOW(GLOBAL, LOG_INFO, "DBG: Added scalar companions Ux_debug, Uy_debug, Uz_debug.\n");
315 } else {
316 LOG_ALLOW(GLOBAL, LOG_WARNING, "DBG: Not enough slots to add Ux/Uy/Uz debug fields.\n");
317 PetscFree(Ux); PetscFree(Uy); PetscFree(Uz);
318 }
319
320 // Mid-plane CSV + AoS vs NATURAL compare (component X)
321
322 // Gather NATURAL again (small cost, but isolated and clear)
323 PetscInt Ng = 0;
324 double *nat_d = NULL;
325 DM dmU = NULL;
326 DMDALocalInfo infU;
327 ierr = VecGetDM(field_vec, &dmU); CHKERRQ(ierr);
328 ierr = DMDAGetLocalInfo(dmU, &infU); CHKERRQ(ierr);
329
330 const PetscInt M=infU.mx, N=infU.my, P=infU.mz;
331 const PetscInt mx = meta.mx, my = meta.my, mz = meta.mz;
332 const PetscInt iInnerMid = mx/2; // interior index [0..mx-1]
333 const PetscInt iGlob = iInnerMid;
334
335 ierr = VecToArrayOnRank0(field_vec, &Ng, &nat_d); CHKERRQ(ierr);
336
337 if (nat_d) {
338 const PetscScalar *nar = (const PetscScalar*)nat_d;
339 const char *base = pps->output_prefix;
340 char fn[512], fnc[512];
341 snprintf(fn, sizeof(fn), "%s_%05" PetscInt_FMT "_iMid.csv", base, ti);
342 snprintf(fnc, sizeof(fnc), "%s_%05" PetscInt_FMT "_iMid_compare.csv", base, ti);
343
344 FILE *fp = fopen(fn, "w");
345 FILE *fpc = fopen(fnc, "w");
346 if (fp) fprintf(fp, "jInner,kInner,Ux,Uy,Uz\n");
347 if (fpc) fprintf(fpc, "jInner,kInner,Ux_AoS,Ux_NAT,abs_diff\n");
348
349 double maxAbsDiff = 0.0, sumAbs = 0.0;
350 PetscInt count = 0;
351
352 for (PetscInt kInner = 0; kInner < mz; ++kInner) {
353 const PetscInt k = kInner;
354 for (PetscInt jInner = 0; jInner < my; ++jInner) {
355 const PetscInt j = jInner;
356
357 // AoS tuple index
358 const PetscInt t = iInnerMid + mx * (jInner + my * kInner);
359 const PetscScalar ux = a[3*t+0], uy = a[3*t+1], uz = a[3*t+2];
360
361 if (fp) fprintf(fp, "%d,%d,%.15e,%.15e,%.15e\n",
362 (int)jInner,(int)kInner,(double)ux,(double)uy,(double)uz);
363
364 // NATURAL base for (iGlob,j,k)
365 const PetscInt baseNat = 3 * (((k)*N + j)*M + iGlob);
366 const PetscScalar uxN = nar[baseNat + 0];
367
368 const double diff = fabs((double)ux - (double)uxN);
369 if (diff > maxAbsDiff) maxAbsDiff = diff;
370 sumAbs += diff; ++count;
371
372 if (fpc) fprintf(fpc, "%d,%d,%.15e,%.15e,%.15e\n",
373 (int)jInner,(int)kInner,(double)ux,(double)uxN,diff);
374 } // for jInner
375 } // for kInner
376 if (fp) fclose(fp);
377 if (fpc) fclose(fpc);
378
379 if (count > 0) {
380 const double meanAbs = sumAbs / (double)count;
381 LOG_ALLOW(GLOBAL, LOG_INFO,
382 "PETSc-Vec vs AoS (i-mid, Ux): max|Δ|=%.6e, mean|Δ|=%.6e -> CSV: %s\n",
383 maxAbsDiff, meanAbs, fnc);
384 LOG_ALLOW(GLOBAL, LOG_INFO, "Wrote i-mid plane CSV: %s\n", fn);
385 } // if count>0
386
387 ierr = PetscFree(nat_d); CHKERRQ(ierr);
388
389
390 } // if nat_d
391
392 } // if a && num_components==3 && npts>0
393 } // if Ucat_nodal
394 // --- END DEBUG BLOCK (Ucat_nodal) ---
395 */
396
397 field_name = strtok(NULL, ",");
398 }
399
400 ierr = PetscFree(fields_copy); CHKERRQ(ierr);
401
402 // --- DEBUG: Add sanity fields i_idx, j_idx, k_idx and x_pos, y_pos, z_pos ---
403 // These are the logical indices and physical coordinates of each point in the subsampled grid.
404 // They can be used to verify the grid structure and orientation in visualization tools.
405 // They are added as scalar fields with names "i_idx", "j_idx", "k_idx" and "x_pos", "y_pos", "z_pos".
406 // Note: these are only added if there is room in the MAX_POINT_DATA_FIELDS limit.
407 // They are allocated and owned here, and will be freed below.
408 // They are in the same linearization order as meta.coords (AoS x,y,z by point).
409 /*
410 // Append sanity fields i/j/k indices and coordinates
411
412 // Build i/j/k and x/y/z (length = npoints); these match the same linearization as coords
413 const PetscInt n = meta.npoints;
414 PetscScalar *i_idx=NULL,*j_idx=NULL,*k_idx=NULL,*x_pos=NULL,*y_pos=NULL,*z_pos=NULL;
415
416 ierr = PetscMalloc1(n, &i_idx); CHKERRQ(ierr);
417 ierr = PetscMalloc1(n, &j_idx); CHKERRQ(ierr);
418 ierr = PetscMalloc1(n, &k_idx); CHKERRQ(ierr);
419 ierr = PetscMalloc1(n, &x_pos); CHKERRQ(ierr);
420 ierr = PetscMalloc1(n, &y_pos); CHKERRQ(ierr);
421 ierr = PetscMalloc1(n, &z_pos); CHKERRQ(ierr);
422
423 // coords is length 3*n, AoS: (x,y,z) by point
424 const PetscScalar *c = (const PetscScalar*)meta.coords;
425 for (PetscInt k = 0; k < meta.mz; ++k) {
426 for (PetscInt j = 0; j < meta.my; ++j) {
427 for (PetscInt i = 0; i < meta.mx; ++i) {
428 const PetscInt t = i + meta.mx * (j + meta.my * k);
429 i_idx[t] = (PetscScalar)i;
430 j_idx[t] = (PetscScalar)j;
431 k_idx[t] = (PetscScalar)k;
432 x_pos[t] = c[3*t+0];
433 y_pos[t] = c[3*t+1];
434 z_pos[t] = c[3*t+2];
435 }
436 }
437 }
438
439 const char *nf[6] = {"i_idx","j_idx","k_idx","x_pos","y_pos","z_pos"};
440 PetscScalar *arrs[6] = {i_idx,j_idx,k_idx,x_pos,y_pos,z_pos};
441 for (int s=0; s<6; ++s) {
442 if (meta.num_point_data_fields < MAX_POINT_DATA_FIELDS) {
443 VTKFieldInfo *f = &meta.point_data_fields[meta.num_point_data_fields++];
444 strncpy(f->name, nf[s], MAX_VTK_FIELD_NAME_LENGTH-1);
445 f->name[MAX_VTK_FIELD_NAME_LENGTH-1] = '\0';
446 f->num_components = 1;
447 f->data = arrs[s];
448 } else {
449 LOG_ALLOW(GLOBAL, LOG_WARNING, "Sanity field '%s' dropped: MAX_POINT_DATA_FIELDS reached.\n", nf[s]);
450 PetscFree(arrs[s]);
451 }
452 }
453 LOG_ALLOW(GLOBAL, LOG_INFO, "DBG: Added sanity fields i_idx/j_idx/k_idx and x_pos/y_pos/z_pos.\n");
454 */
455 // --- END DEBUG BLOCK (sanity fields) ---
456
457 if (user->simCtx->rank == 0) {
458 /* Field summary */
459 LOG_ALLOW(GLOBAL, LOG_INFO, "PointData fields to write: %d\n", (int)meta.num_point_data_fields);
460 for (PetscInt ii=0; ii<meta.num_point_data_fields; ++ii) {
461 LOG_ALLOW(GLOBAL, LOG_INFO, " # %2" PetscInt_FMT " Field Name = %s Components = %d\n",
462 ii, meta.point_data_fields[ii].name, (int)meta.point_data_fields[ii].num_components);
463 }
464 }
465 }
466
467 /* 4) Write the VTS */
468 ierr = PetscSNPrintf(filename, sizeof(filename), "%s_%05" PetscInt_FMT ".vts", pps->output_prefix, ti); CHKERRQ(ierr);
469 ierr = FinishStructuredVTKOutput(&meta, filename); CHKERRQ(ierr);
470
471 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Eulerian File Writing for ti = %" PetscInt_FMT " Complete ---\n", ti);
473 PetscFunctionReturn(0);
474}
Vec P_nodal
Definition variables.h:1001
PetscInt num_components
Definition variables.h:643
PetscMPIInt rank
Definition variables.h:698
PetscInt num_point_data_fields
Definition variables.h:659
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:909
char output_prefix[256]
Definition variables.h:609
Vec Ucat_nodal
Definition variables.h:1002
PetscInt np
Definition variables.h:827
#define MAX_FILENAME_LENGTH
Definition variables.h:588
Vec Qcrit
Definition variables.h:1003
char output_fields_instantaneous[1024]
Definition variables.h:608
char name[64]
Definition variables.h:642
VTKFieldInfo point_data_fields[20]
Definition variables.h:658
Vec Psi_nodal
Definition variables.h:1004
PetscInt mz
Definition variables.h:655
PetscInt my
Definition variables.h:655
PetscInt mx
Definition variables.h:655
PetscErrorCode FinishStructuredVTKOutput(VTKMetaData *meta, const char *filename)
Writes an assembled structured VTK file and releases its buffers.
Definition vtk_io.c:354
PetscErrorCode BeginStructuredVTKOutput(UserCtx *user, VTKMetaData *meta)
Begins one structured VTK file: clears the metadata and builds its coordinates.
Definition vtk_io.c:308
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.
Definition vtk_io.c:326
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.
ppsThe PostProcessParams containing the pipeline string.
Returns
PetscErrorCode

Parses the processing pipeline string and executes the requested kernels.

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

See also
EulerianDataProcessingPipeline()

Definition at line 107 of file postprocessor.c.

108{
109 PetscErrorCode ierr;
110 char *pipeline_copy, *step_token, *step_saveptr;
111
112 PetscFunctionBeginUser;
114 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting Data Transformation Pipeline ---\n");
115
116 // Do nothing if the pipeline string is empty
117 if (pps->process_pipeline[0] == '\0') {
118 LOG_ALLOW(GLOBAL, LOG_INFO, "Processing pipeline is empty. No transformations will be run.\n");
120 PetscFunctionReturn(0);
121 }
122
123 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pipeline string: [%s]\n", pps->process_pipeline);
124
125 // Make a writable copy for strtok_r, as it modifies the string
126 ierr = PetscStrallocpy(pps->process_pipeline, &pipeline_copy); CHKERRQ(ierr);
127
128 // --- Outer Loop: Tokenize by Semicolon (;) to get each processing step ---
129 step_token = strtok_r(pipeline_copy, ";", &step_saveptr);
130 while (step_token) {
131 TrimWhitespace(step_token);
132 if (strlen(step_token) == 0) {
133 step_token = strtok_r(NULL, ";", &step_saveptr);
134 continue;
135 }
136
137 char *keyword = strtok(step_token, ":");
138 char *args_str = strtok(NULL, ""); // Get the rest of the string as arguments
139
140 if (!keyword) { // Should not happen with TrimWhitespace, but is a safe check
141 step_token = strtok_r(NULL, ";", &step_saveptr);
142 continue;
143 }
144
145 TrimWhitespace(keyword);
146 if (args_str) TrimWhitespace(args_str);
147
148 LOG_ALLOW(GLOBAL, LOG_INFO, "Executing Transformation: '%s' on args: '%s'\n", keyword, args_str ? args_str : "None");
149
150 // --- DISPATCHER: Route to the correct kernel based on the keyword ---
151 if (strcasecmp(keyword, "CellToNodeAverage") == 0) {
152 if (!args_str) SETERRQ(PETSC_COMM_SELF, 1, "CellToNodeAverage requires arguments in 'in_field>out_field' format.");
153 char *in_field = strtok(args_str, ">");
154 char *out_field = strtok(NULL, ">");
155 if (!in_field || !out_field) SETERRQ(PETSC_COMM_SELF, 1, "CellToNodeAverage requires 'in>out' syntax (e.g., P>P_nodal).");
156 if(strcmp(in_field,out_field)==0) SETERRQ(PETSC_COMM_SELF, 1, "CellToNodeAverage input and output fields must be different.");
157 if(user->simCtx->np == 0 && (strcmp(out_field,"Psi_nodal")==0 || strcmp(in_field,"Psi_nodal")==0)){
158 LOG(GLOBAL,LOG_WARNING,"CellToNodeAverage cannot process 'Psi_nodal' when no particles are present in the simulation.\n");
159 step_token = strtok_r(NULL, ";", &step_saveptr);
160 continue;
161 }
162 TrimWhitespace(in_field); TrimWhitespace(out_field);
163 ierr = ComputeNodalAverage(user, in_field, out_field); CHKERRQ(ierr);
164 }
165 else if (strcasecmp(keyword, "ComputeQCriterion") == 0) {
166 ierr = ComputeQCriterion(user); CHKERRQ(ierr);
167 }
168 else if (strcasecmp(keyword, "DimensionalizeAllLoadedFields") == 0) {
169 /* Emitted by global_operations.dimensionalize. It had no dispatch branch,
170 * so the option was accepted, serialized, and then silently skipped. */
171 ierr = DimensionalizeAllLoadedFields(user); CHKERRQ(ierr);
172 }
173 else if (strcasecmp(keyword, "NormalizeRelativeField") == 0) {
174 if (!args_str) SETERRQ(PETSC_COMM_SELF, 1, "NormalizePressure requires the pressure field name (e.g., 'P') as an argument.");
175 ierr = NormalizeRelativeField(user, args_str); CHKERRQ(ierr);
176 }
177 // *** Add new kernels here in the future using 'else if' ***
178 // else if (strcasecmp(keyword, "ComputeVorticity") == 0) { ... }
179 else {
180 LOG_ALLOW(GLOBAL, LOG_WARNING, "Unknown transformation keyword '%s'. Skipping.\n", keyword);
181 }
182
183 step_token = strtok_r(NULL, ";", &step_saveptr);
184 }
185
186 ierr = PetscFree(pipeline_copy); CHKERRQ(ierr);
187 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Data Transformation Pipeline Complete ---\n");
189 PetscFunctionReturn(0);
190}
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:84
PetscErrorCode ComputeQCriterion(UserCtx *user)
Computes the Q-criterion diagnostic from the local velocity-gradient tensor.
PetscErrorCode NormalizeRelativeField(UserCtx *user, const char *relative_field_name)
Normalizes pressure using the value at the configured logical grid point.
PetscErrorCode DimensionalizeAllLoadedFields(UserCtx *user)
Orchestrates the dimensionalization of all relevant fields loaded from a file.
PetscErrorCode ComputeNodalAverage(UserCtx *user, const char *in_field_name, const char *out_field_name)
Interpolates a cell-centered field to nodal locations using local stencil averaging.
char process_pipeline[1024]
Definition variables.h:607
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

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

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

See also
ParticleDataProcessingPipeline()

Definition at line 626 of file postprocessor.c.

627{
628 PetscErrorCode ierr;
629 char *pipeline_copy, *step_token, *step_saveptr;
630
631 PetscFunctionBeginUser;
632
634
635 if (pps->particle_pipeline[0] == '\0') {
637 PetscFunctionReturn(0);
638 }
639
640 // --- Timestep Setup: Synchronize post_swarm size ---
641 PetscInt n_local_source;
642 ierr = DMSwarmGetLocalSize(user->swarm, &n_local_source); CHKERRQ(ierr);
643
644 // Derived entries use the same local index as their source particle.
645 ierr = DMSwarmSetLocalSizes(user->post_swarm, n_local_source, -1); CHKERRQ(ierr);
646
647 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting Particle Data Transformation Pipeline ---\n");
648 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Particle Pipeline string: [%s]\n", pps->particle_pipeline);
649
650 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Executing compute kernels...\n");
651 ierr = PetscStrallocpy(pps->particle_pipeline, &pipeline_copy); CHKERRQ(ierr);
652 step_token = strtok_r(pipeline_copy, ";", &step_saveptr);
653 while (step_token) {
654 TrimWhitespace(step_token);
655 if (strlen(step_token) == 0) { step_token = strtok_r(NULL, ";", &step_saveptr); continue; }
656
657 char *keyword = strtok(step_token, ":");
658 char *args_str = strtok(NULL, "");
659 TrimWhitespace(keyword);
660 if (args_str) TrimWhitespace(args_str);
661
662 LOG_ALLOW(GLOBAL, LOG_INFO, "Executing Particle Transformation: '%s' on args: '%s'\n", keyword, args_str ? args_str : "None");
663
664 if (strcasecmp(keyword, "ComputeSpecificKE") == 0) {
665 if (!args_str) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "ComputeSpecificKE requires 'input_field>output_field' arguments.");
666 char *velocity_field = strtok(args_str, ">");
667 char *ske_field = strtok(NULL, ">");
668 if (!velocity_field || !ske_field) {
669 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "ComputeSpecificKE requires 'input_field>output_field' arguments.");
670 }
671 TrimWhitespace(velocity_field); TrimWhitespace(ske_field);
672 if (strlen(velocity_field) == 0 || strlen(ske_field) == 0) {
673 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "ComputeSpecificKE does not allow empty input/output field names.");
674 }
675
676 ierr = ComputeSpecificKE(user, velocity_field, ske_field); CHKERRQ(ierr);
677 }
678 else {
679 LOG_ALLOW(GLOBAL, LOG_WARNING, "Unknown particle transformation keyword '%s'. Skipping.\n", keyword);
680 }
681
682 step_token = strtok_r(NULL, ";", &step_saveptr);
683 }
684 ierr = PetscFree(pipeline_copy); CHKERRQ(ierr);
685
686 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Particle Data Transformation Pipeline Complete ---\n");
687
689 PetscFunctionReturn(0);
690}
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.

Parameters
userPrimary UserCtx input for the operation.
ppsPost-processing configuration for the operation.
tiSource timestep written into the VTP filename and metadata.
Returns
PetscErrorCode 0 on success.

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

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

See also
WriteParticleFile()

Definition at line 749 of file postprocessor.c.

750{
751 PetscErrorCode ierr;
752 VTKMetaData part_meta;
753 char filename[MAX_FILENAME_LENGTH];
754 PetscInt n_total_particles_before_subsample;
755
756 PetscFunctionBeginUser;
758
759 // These checks can be done on all ranks
760 if (!pps->outputParticles || pps->particle_fields[0] == '\0') {
762 PetscFunctionReturn(0);
763 }
764 PetscInt n_global;
765 ierr = DMSwarmGetSize(user->swarm, &n_global); CHKERRQ(ierr);
766 if (n_global == 0) {
767 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Swarm is empty for ti=%" PetscInt_FMT ". Skipping particle file write.\n", ti);
769 PetscFunctionReturn(0);
770 }
771
772 ierr = PetscMemzero(&part_meta, sizeof(VTKMetaData)); CHKERRQ(ierr);
773
774 // --- 1. PREPARE (Collective Call) ---
775 ierr = PrepareOutputParticleData(user, pps, &part_meta, &n_total_particles_before_subsample); CHKERRQ(ierr);
776
777 // --- 2. WRITE and CLEANUP (Rank 0 only) ---
778 if (user->simCtx->rank == 0) {
779 if (part_meta.npoints > 0) {
780 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting VTP Particle File Writing for ti = %" PetscInt_FMT " (writing %" PetscInt_FMT " of %" PetscInt_FMT " particles) ---\n",
781 ti, part_meta.npoints, n_total_particles_before_subsample);
782
783 /* Field summary */
784 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Data fields to write: %d\n", (int)part_meta.num_point_data_fields);
785 for (PetscInt ii=0; ii<part_meta.num_point_data_fields; ++ii) {
786 LOG_ALLOW(GLOBAL, LOG_INFO, " # %2" PetscInt_FMT " Field Name = %s Components = %d\n",
787 ii, part_meta.point_data_fields[ii].name, (int)part_meta.point_data_fields[ii].num_components);
788 }
789
790 ierr = PetscSNPrintf(filename, sizeof(filename), "%s_%05" PetscInt_FMT ".vtp", pps->particle_output_prefix, ti); CHKERRQ(ierr);
791 ierr = CreateVTKFileFromMetadata(filename, &part_meta, PETSC_COMM_WORLD); CHKERRQ(ierr);
792
793 } else {
794 LOG_ALLOW(GLOBAL, LOG_DEBUG, "No particles to write at ti=%" PetscInt_FMT " after subsampling. Skipping.\n", ti);
795 }
796
797 for (PetscInt field = 0; field < part_meta.num_point_data_fields; ++field) {
798 ierr = PetscFree(part_meta.point_data_fields[field].data); CHKERRQ(ierr);
799 }
800 ierr = PetscFree(part_meta.coords); CHKERRQ(ierr);
801 ierr = PetscFree(part_meta.connectivity); CHKERRQ(ierr);
802 ierr = PetscFree(part_meta.offsets); CHKERRQ(ierr);
803 }
804
805 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Particle File Writing for ti = %" PetscInt_FMT " Complete ---\n", ti);
807 PetscFunctionReturn(0);
808}
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:149
PetscInt npoints
Definition variables.h:656
char particle_output_prefix[256]
Definition variables.h:612
PetscInt * connectivity
Definition variables.h:660
PetscInt * offsets
Definition variables.h:661
PetscScalar * data
Definition variables.h:644
PetscScalar * coords
Definition variables.h:657
char particle_fields[1024]
Definition variables.h:611
PetscBool outputParticles
Definition variables.h:604
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:486
Here is the call graph for this function:
Here is the caller graph for this function:

◆ GlobalStatisticsPipeline()

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

Executes the global statistics pipeline, computing aggregate reductions over all particles.

Parses the semicolon-delimited pps->statistics_pipeline string and dispatches to the appropriate kernel (e.g. ComputeParticleMSD). Each kernel appends one row to its own CSV file and logs a summary via LOG_INFO. All MPI reductions happen inside each kernel. This pipeline is independent of the per-particle VTK pipeline; it produces no .vtp output.

Parameters
userThe UserCtx containing the primary DMSwarm (user->swarm).
ppsThe PostProcessParams containing statistics_pipeline and statistics_output_prefix.
tiCurrent time-step index (passed through to kernels for time computation).
Returns
PetscErrorCode

Executes the global statistics pipeline, computing aggregate reductions over all particles.

Local to this translation unit.

Definition at line 698 of file postprocessor.c.

699{
700 PetscErrorCode ierr;
701 char *pipeline_copy, *step_token, *step_saveptr;
702
703 PetscFunctionBeginUser;
705
706 if (pps->statistics_pipeline[0] == '\0') { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
707
708 PetscInt n_global;
709 ierr = DMSwarmGetSize(user->swarm, &n_global); CHKERRQ(ierr);
710 if (n_global == 0) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
711
712 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting Global Statistics Pipeline ---\n");
713
714 ierr = PetscStrallocpy(pps->statistics_pipeline, &pipeline_copy); CHKERRQ(ierr);
715 step_token = strtok_r(pipeline_copy, ";", &step_saveptr);
716 while (step_token) {
717 TrimWhitespace(step_token);
718 if (strlen(step_token) == 0) {
719 step_token = strtok_r(NULL, ";", &step_saveptr); continue;
720 }
721 char *keyword = strtok(step_token, ":");
722 TrimWhitespace(keyword);
723
724 if (strcasecmp(keyword, "ComputeMSD") == 0) {
725 ierr = ComputeParticleMSD(user, pps->statistics_output_prefix, ti); CHKERRQ(ierr);
726 } else {
728 "Unknown statistics keyword '%s'. Skipping.\n", keyword);
729 }
730 /* Additional kernels should add else-if branches here when implemented. */
731
732 step_token = strtok_r(NULL, ";", &step_saveptr);
733 }
734 ierr = PetscFree(pipeline_copy); CHKERRQ(ierr);
735
736 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Global Statistics Pipeline Complete ---\n");
738 PetscFunctionReturn(0);
739}
PetscErrorCode ComputeParticleMSD(UserCtx *user, const char *stats_prefix, PetscInt ti)
Computes the mean-squared displacement (MSD) of a particle cloud.
char statistics_output_prefix[256]
basename for CSV output, e.g.
Definition variables.h:617
char statistics_pipeline[1024]
e.g.
Definition variables.h:616
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FieldStatisticsPipeline()

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

Derives and writes field statistics for the windows a recipe requests.

Runs once per processed step, beside the particle statistics pipeline, because a window's accumulated state is a property of the bundle at that step. A recipe spanning several steps therefore produces a convergence history rather than the same picture repeated; pinning field_statistics_source_step collapses it back to a single bundle.

Each window is written to its own file. The VTK point-data cap is per file, and one window carrying every output already fills most of it.

Does nothing when no window is requested. A window the run does not configure is fatal; a window that has not yet accumulated a sample is skipped with a note, since a recipe covering a whole run legitimately reaches bundles from before it began.

Parameters
[in]userFinest-level block context holding accumulators and staging fields.
[in]ppsPost-processing recipe naming the windows, outputs, and formats.
[in]tiStep being processed; names the output and selects the bundle.
Returns
Zero on success, or a PETSc error.

Derives and writes field statistics for the windows a recipe requests.

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

See also
FieldStatisticsPipeline()

Definition at line 485 of file postprocessor.c.

486{
487 PetscErrorCode ierr;
488 SimCtx *simCtx = NULL;
489 char *windows_copy = NULL;
490 char *window_name = NULL;
491 PetscInt source_step = 0;
492 PetscBool want_vtk = PETSC_FALSE, want_csv = PETSC_FALSE;
493
494 PetscFunctionBeginUser;
496 if (pps->field_statistics_windows[0] == '\0') { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
497 simCtx = user->simCtx;
498
499 PetscCheck(FieldStatisticsIsActive(simCtx), PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
500 "Field-statistics post-processing was requested, but the run's control configures "
501 "no statistics window.");
502
503 {
504 char formats[MAX_FIELD_LIST_LENGTH];
505 char *token = NULL;
506
507 ierr = PetscStrncpy(formats, pps->field_statistics_formats, sizeof(formats)); CHKERRQ(ierr);
508 token = strtok(formats, ",");
509 while (token) {
510 TrimWhitespace(token);
511 if (!strcasecmp(token, "vtk")) want_vtk = PETSC_TRUE;
512 else if (!strcasecmp(token, "csv")) want_csv = PETSC_TRUE;
514 "Unknown field-statistics format '%s'. Known formats are vtk and csv.\n", token);
515 token = strtok(NULL, ",");
516 }
517 }
518 if (!want_vtk && !want_csv) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
519
520 /* An explicit source step pins every processed step to one bundle; otherwise each
521 * step derives from its own, which is what turns a multi-step recipe into a
522 * convergence history rather than the same picture repeated. */
523 source_step = (pps->field_statistics_source_step >= 0) ? pps->field_statistics_source_step : ti;
524
525 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting Field Statistics Pipeline (step %" PetscInt_FMT ") ---\n",
526 source_step);
527 simCtx->fieldStatisticsContinue = PETSC_TRUE;
528 ierr = RestoreFieldStatisticsState(simCtx, source_step); CHKERRQ(ierr);
529
530 ierr = PetscStrallocpy(pps->field_statistics_windows, &windows_copy); CHKERRQ(ierr);
531 window_name = strtok(windows_copy, ",");
532 while (window_name) {
533 PetscInt window_index = -1;
534 const PicurvWindow *window = NULL;
535
536 TrimWhitespace(window_name);
537 if (!*window_name) { window_name = strtok(NULL, ","); continue; }
538
539 for (PetscInt w = 0; w < simCtx->fieldStatisticsWindowCount; ++w) {
540 PetscBool matches = PETSC_FALSE;
541
542 ierr = PetscStrcmp(simCtx->fieldStatisticsWindows[w].definition.name,
543 window_name, &matches); CHKERRQ(ierr);
544 if (matches) { window_index = w; break; }
545 }
546 if (window_index < 0) {
547 ierr = PetscFree(windows_copy); CHKERRQ(ierr);
548 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
549 "Field-statistics post-processing requested window '%s', which this run does "
550 "not configure.", window_name);
551 }
552 window = &simCtx->fieldStatisticsWindows[window_index];
553
554 /* A window that has not started by this step is not an error: a recipe
555 * spanning a whole run legitimately reaches bundles from before it began. */
556 if (window->sample_count <= 0) {
558 "Statistics window '%s' had accumulated no sample by step %" PetscInt_FMT
559 "; nothing to derive yet.\n", window->definition.name, source_step);
560 window_name = strtok(NULL, ",");
561 continue;
562 }
563
564 if (want_vtk) {
565 VTKMetaData meta;
566 PetscInt derived_count = 0;
567 char filename[PETSC_MAX_PATH_LEN];
568
569 ierr = PicurvWindowDerivedCount(&window->definition,
570 &user->fieldStatisticsStorage[window_index],
571 pps->field_statistics_outputs, &derived_count); CHKERRQ(ierr);
572 /* An output kind resolves against what the window accumulated, so asking
573 * for stresses from a means-only window yields nothing. Report it the way
574 * an unrecognized Eulerian output field is reported, rather than writing a
575 * quietly short file. */
576 if (derived_count == 0) {
578 "Outputs '%s' produce no field for window '%s'; it accumulates none of "
579 "the state they need. Skipping.\n",
581 }
582 /* One file per window. The point-data cap is per file, and a single
583 * window with every output already fills most of it. */
584 ierr = BeginStructuredVTKOutput(user, &meta); CHKERRQ(ierr);
585 for (PetscInt index = 0; index < derived_count; ++index) {
586 char name[MAX_VTK_FIELD_NAME_LENGTH];
587 Vec nodal = NULL;
588 PetscInt components = 0;
589
590 ierr = ComputeWindowStatisticNodal(user, window_index,
591 pps->field_statistics_outputs, index,
592 name, sizeof(name), &nodal, &components); CHKERRQ(ierr);
593 /* The staging vector is reused for the next field, which is safe
594 * because appending copies the values out. */
595 ierr = AppendStructuredVTKField(user, &meta, name, nodal, components); CHKERRQ(ierr);
596 }
597 ierr = PetscSNPrintf(filename, sizeof(filename), "%s_statistics_%s_%05" PetscInt_FMT ".vts",
598 pps->output_prefix, window->definition.name, ti); CHKERRQ(ierr);
599 LOG_ALLOW(GLOBAL, LOG_INFO, "Wrote %d derived field(s) for window '%s' to %s\n",
600 (int)meta.num_point_data_fields, window->definition.name, filename);
601 ierr = FinishStructuredVTKOutput(&meta, filename); CHKERRQ(ierr);
602 }
603
604 if (want_csv) {
605 ierr = ComputeWindowStatisticsSummary(user, window_index, pps->output_prefix,
606 ti); CHKERRQ(ierr);
607 }
608
609 window_name = strtok(NULL, ",");
610 }
611 ierr = PetscFree(windows_copy); CHKERRQ(ierr);
612
613 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Field Statistics Pipeline Complete ---\n");
615 PetscFunctionReturn(0);
616}
PetscErrorCode RestoreFieldStatisticsState(SimCtx *simCtx, PetscInt ti)
Restores field-statistics window state and accumulators from a checkpoint.
Definition io.c:1667
PetscErrorCode ComputeWindowStatisticsSummary(UserCtx *user, PetscInt window_index, const char *output_prefix, PetscInt ti)
Appends one convergence row for an accumulated window to its CSV history.
PetscErrorCode ComputeWindowStatisticNodal(UserCtx *user, PetscInt window_index, const char *outputs, PetscInt output_index, char *out_name, size_t name_size, Vec *out_vec, PetscInt *out_components)
Derives one accumulated statistic and converts it to nodal values.
PetscErrorCode PicurvWindowDerivedCount(const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const char *outputs, PetscInt *count)
Reports how many derived fields a requested output set produces.
PetscInt sample_count
PicurvWindowDefinition definition
PetscBool FieldStatisticsIsActive(const struct SimCtx *simCtx)
Reports whether this run has live field-statistics state.
Runtime state of one window.
PetscInt fieldStatisticsWindowCount
Definition variables.h:770
#define MAX_FIELD_LIST_LENGTH
Definition variables.h:587
char field_statistics_formats[1024]
Comma-separated formats: vtk for derived fields, csv for the convergence history.
Definition variables.h:625
struct PicurvWindow * fieldStatisticsWindows
Definition variables.h:771
struct PicurvWindowStorage * fieldStatisticsStorage
Definition variables.h:962
PetscInt field_statistics_source_step
Committed step supplying the state; negative means the step being processed.
Definition variables.h:627
char field_statistics_windows[1024]
Comma-separated window names to derive; empty disables the pipeline.
Definition variables.h:621
char field_statistics_outputs[1024]
Comma-separated outputs: mean, reynolds_stress, rms, tke, flux.
Definition variables.h:623
#define MAX_VTK_FIELD_NAME_LENGTH
Maximum length for VTK field names.
Definition variables.h:591
PetscBool fieldStatisticsContinue
Definition variables.h:776
The master context for the entire simulation.
Definition variables.h:695
Here is the call graph for this function:
Here is the caller graph for this function: