PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Macros | Functions
postprocessor.c File Reference

Offline post-processing tool driving the derived-output pipelines. More...

#include "postprocessor.h"
#include "statistics_accumulator.h"
#include "statistics_window.h"
Include dependency graph for postprocessor.c:

Go to the source code of this file.

Macros

#define __FUNCT__   "SetupPostProcessSwarm"
 
#define __FUNCT__   "EulerianDataProcessingPipeline"
 
#define __FUNCT__   "WriteEulerianFile"
 
#define __FUNCT__   "FieldStatisticsPipeline"
 
#define __FUNCT__   "ParticleDataProcessingPipeline"
 
#define __FUNCT__   "GlobalStatisticsPipeline"
 
#define __FUNCT__   "WriteParticleFile"
 
#define __FUNCT__   "ResolvePostProcessingSteps"
 
#define __FUNCT__   "main"
 

Functions

PetscErrorCode SetupPostProcessSwarm (UserCtx *user, PostProcessParams *pps)
 Internal helper implementation: SetupPostProcessSwarm().
 
PetscErrorCode EulerianDataProcessingPipeline (UserCtx *user, PostProcessParams *pps)
 Implementation of EulerianDataProcessingPipeline().
 
PetscErrorCode WriteEulerianFile (UserCtx *user, PostProcessParams *pps, PetscInt ti)
 Implementation of WriteEulerianFile().
 
PetscErrorCode FieldStatisticsPipeline (UserCtx *user, PostProcessParams *pps, PetscInt ti)
 Implementation of FieldStatisticsPipeline().
 
PetscErrorCode ParticleDataProcessingPipeline (UserCtx *user, PostProcessParams *pps)
 Implementation of ParticleDataProcessingPipeline().
 
PetscErrorCode GlobalStatisticsPipeline (UserCtx *user, PostProcessParams *pps, PetscInt ti)
 Internal helper implementation: GlobalStatisticsPipeline().
 
PetscErrorCode WriteParticleFile (UserCtx *user, PostProcessParams *pps, PetscInt ti)
 Implementation of WriteParticleFile().
 
PetscErrorCode ResolvePostProcessingSteps (PostProcessParams *pps, PetscInt **steps, PetscInt *count)
 Internal helper implementation: ResolvePostProcessingSteps().
 
int main (int argc, char **argv)
 Entry point for the postprocessor executable.
 

Detailed Description

Offline post-processing tool driving the derived-output pipelines.

Reads committed checkpoint bundles over a step range and dispatches the Eulerian, Lagrangian, particle-statistics, and field-statistics pipelines that a recipe selected, writing one output family per enabled pipeline.

Definition in file postprocessor.c.

Macro Definition Documentation

◆ __FUNCT__ [1/9]

#define __FUNCT__   "SetupPostProcessSwarm"

Definition at line 16 of file postprocessor.c.

◆ __FUNCT__ [2/9]

#define __FUNCT__   "EulerianDataProcessingPipeline"

Definition at line 16 of file postprocessor.c.

◆ __FUNCT__ [3/9]

#define __FUNCT__   "WriteEulerianFile"

Definition at line 16 of file postprocessor.c.

◆ __FUNCT__ [4/9]

#define __FUNCT__   "FieldStatisticsPipeline"

Definition at line 16 of file postprocessor.c.

◆ __FUNCT__ [5/9]

#define __FUNCT__   "ParticleDataProcessingPipeline"

Definition at line 16 of file postprocessor.c.

◆ __FUNCT__ [6/9]

#define __FUNCT__   "GlobalStatisticsPipeline"

Definition at line 16 of file postprocessor.c.

◆ __FUNCT__ [7/9]

#define __FUNCT__   "WriteParticleFile"

Definition at line 16 of file postprocessor.c.

◆ __FUNCT__ [8/9]

#define __FUNCT__   "ResolvePostProcessingSteps"

Definition at line 16 of file postprocessor.c.

◆ __FUNCT__ [9/9]

#define __FUNCT__   "main"

Definition at line 16 of file postprocessor.c.

Function Documentation

◆ SetupPostProcessSwarm()

PetscErrorCode SetupPostProcessSwarm ( UserCtx *  user,
PostProcessParams *  pps 
)

Internal helper implementation: SetupPostProcessSwarm().

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:393
#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:894
@ 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:885
DM post_swarm
Definition variables.h:1175
char particle_pipeline[1024]
Definition variables.h:770
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EulerianDataProcessingPipeline()

PetscErrorCode EulerianDataProcessingPipeline ( UserCtx *  user,
PostProcessParams *  pps 
)

Implementation of EulerianDataProcessingPipeline().

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 /* Retired stage. Loaded fields are now scaled as they are read, which is the
170 * only point that knows which fields a step actually loaded; scaling them here
171 * too would scale them twice. Recipes locked before the change still carry
172 * the token, so it is accepted and does nothing. */
173 }
174 else if (strcasecmp(keyword, "NormalizeRelativeField") == 0) {
175 if (!args_str) SETERRQ(PETSC_COMM_SELF, 1, "NormalizePressure requires the pressure field name (e.g., 'P') as an argument.");
176 ierr = NormalizeRelativeField(user, args_str); CHKERRQ(ierr);
177 }
178 // *** Add new kernels here in the future using 'else if' ***
179 // else if (strcasecmp(keyword, "ComputeVorticity") == 0) { ... }
180 else {
181 LOG_ALLOW(GLOBAL, LOG_WARNING, "Unknown transformation keyword '%s'. Skipping.\n", keyword);
182 }
183
184 step_token = strtok_r(NULL, ";", &step_saveptr);
185 }
186
187 ierr = PetscFree(pipeline_copy); CHKERRQ(ierr);
188 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Data Transformation Pipeline Complete ---\n");
190 PetscFunctionReturn(0);
191}
#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 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.
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1077
PetscInt np
Definition variables.h:990
char process_pipeline[1024]
Definition variables.h:767
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 
)

Implementation of WriteEulerianFile().

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 202 of file postprocessor.c.

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

◆ FieldStatisticsPipeline()

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

Implementation of FieldStatisticsPipeline().

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 492 of file postprocessor.c.

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

◆ ParticleDataProcessingPipeline()

PetscErrorCode ParticleDataProcessingPipeline ( UserCtx *  user,
PostProcessParams *  pps 
)

Implementation of ParticleDataProcessingPipeline().

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 634 of file postprocessor.c.

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

◆ GlobalStatisticsPipeline()

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

Internal helper implementation: GlobalStatisticsPipeline().

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

Local to this translation unit.

Definition at line 706 of file postprocessor.c.

707{
708 PetscErrorCode ierr;
709 char *pipeline_copy, *step_token, *step_saveptr;
710
711 PetscFunctionBeginUser;
713
714 if (pps->statistics_pipeline[0] == '\0') { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
715
716 PetscInt n_global;
717 ierr = DMSwarmGetSize(user->swarm, &n_global); CHKERRQ(ierr);
718 if (n_global == 0) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
719
720 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting Global Statistics Pipeline ---\n");
721
722 ierr = PetscStrallocpy(pps->statistics_pipeline, &pipeline_copy); CHKERRQ(ierr);
723 step_token = strtok_r(pipeline_copy, ";", &step_saveptr);
724 while (step_token) {
725 TrimWhitespace(step_token);
726 if (strlen(step_token) == 0) {
727 step_token = strtok_r(NULL, ";", &step_saveptr); continue;
728 }
729 char *keyword = strtok(step_token, ":");
730 TrimWhitespace(keyword);
731
732 if (strcasecmp(keyword, "ComputeMSD") == 0) {
733 ierr = ComputeParticleMSD(user, pps->statistics_output_prefix, ti); CHKERRQ(ierr);
734 } else {
736 "Unknown statistics keyword '%s'. Skipping.\n", keyword);
737 }
738 /* Additional kernels should add else-if branches here when implemented. */
739
740 step_token = strtok_r(NULL, ";", &step_saveptr);
741 }
742 ierr = PetscFree(pipeline_copy); CHKERRQ(ierr);
743
744 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Global Statistics Pipeline Complete ---\n");
746 PetscFunctionReturn(0);
747}
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:777
char statistics_pipeline[1024]
e.g.
Definition variables.h:776
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 
)

Implementation of WriteParticleFile().

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 757 of file postprocessor.c.

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

◆ ResolvePostProcessingSteps()

PetscErrorCode ResolvePostProcessingSteps ( PostProcessParams *  pps,
PetscInt **  steps,
PetscInt *  count 
)

Internal helper implementation: ResolvePostProcessingSteps().

Resolve the ordered steps one post-processing run processes.

Local to this translation unit.

Definition at line 824 of file postprocessor.c.

825{
826 PetscMPIInt rank;
827
828 PetscFunctionBeginUser;
829 *steps = NULL;
830 *count = 0;
831 if (pps->step_list_file[0] == '\0') {
832 PetscInt n = (pps->endTime >= pps->startTime) ? (pps->endTime - pps->startTime) / pps->timeStep + 1 : 0;
833 PetscCall(PetscMalloc1(n > 0 ? n : 1, steps));
834 for (PetscInt k = 0; k < n; ++k) (*steps)[k] = pps->startTime + k * pps->timeStep;
835 *count = n;
836 PetscFunctionReturn(0);
837 }
838
839 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
840 /* Rank 0 reads; a failure is broadcast as a negative count so every rank stops
841 together instead of the others waiting in the broadcast. */
842 PetscInt n = 0, capacity = 0;
843 PetscInt *list = NULL;
844 char bad_line[256] = "";
845 if (rank == 0) {
846 char line[256];
847 FILE *file = fopen(pps->step_list_file, "r");
848 if (!file) n = -1;
849 while (file && n >= 0 && fgets(line, sizeof(line), file)) {
850 char *end = NULL;
851 long value;
852 TrimWhitespace(line);
853 if (line[0] == '\0' || line[0] == '#') continue;
854 value = strtol(line, &end, 10);
855 if (end == line || *end != '\0') {
856 PetscCall(PetscStrncpy(bad_line, line, sizeof(bad_line)));
857 n = -2;
858 break;
859 }
860 if (n == capacity) {
861 PetscInt *grown = NULL;
862 capacity = capacity ? 2 * capacity : 64;
863 PetscCall(PetscMalloc1(capacity, &grown));
864 if (n) PetscCall(PetscArraycpy(grown, list, n));
865 PetscCall(PetscFree(list));
866 list = grown;
867 }
868 list[n++] = (PetscInt)value;
869 }
870 if (file) fclose(file);
871 }
872 PetscCallMPI(MPI_Bcast(&n, 1, MPIU_INT, 0, PETSC_COMM_WORLD));
873 if (n < 0) {
874 PetscCall(PetscFree(list));
875 PetscCheck(n != -1, PETSC_COMM_WORLD, PETSC_ERR_FILE_OPEN,
876 "Step list file '%s' is missing. It is written when picurv launches the post "
877 "stage; launch post-processing through picurv.", pps->step_list_file);
878 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
879 "Step list file '%s' has a non-integer line '%s'.", pps->step_list_file, bad_line);
880 }
881 if (rank != 0) PetscCall(PetscMalloc1(n > 0 ? n : 1, &list));
882 if (n > 0) PetscCallMPI(MPI_Bcast(list, (PetscMPIInt)n, MPIU_INT, 0, PETSC_COMM_WORLD));
883 *steps = list;
884 *count = n;
885 PetscFunctionReturn(0);
886}
char step_list_file[PETSC_MAX_PATH_LEN]
File listing the exact steps to process, one per line; empty uses the time controls.
Definition variables.h:763
PetscInt timeStep
Definition variables.h:761
PetscInt startTime
Definition variables.h:759
Head of a generic C-style linked list.
Definition variables.h:469
Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char **  argv 
)

Entry point for the postprocessor executable.

Initializes PETSc, loads post-processing inputs, executes the requested pipelines, and finalizes runtime resources before exit.

Definition at line 896 of file postprocessor.c.

897{
898 PetscErrorCode ierr;
899 SimCtx *simCtx = NULL;
900
901 if (PicurvHandleVersionArgument(argc, argv, "postprocessor")) return 0;
902 // === I. INITIALIZE PETSC & MPI ===========================================
903 ierr = PetscInitialize(&argc, &argv, (char *)0, "Unified Post-Processing Tool"); CHKERRQ(ierr);
904
905 // === II. CONFIGURE SIMULATION & POST-PROCESSING CONTEXTS =================
906 ierr = CreateSimulationContext(argc, argv, &simCtx); CHKERRQ(ierr);
907 ierr = PetscPrintf(PETSC_COMM_WORLD, "Postprocessor MPI processes: %d\n", (int)simCtx->size); CHKERRQ(ierr);
908 // === IIB. SET EXECUTION MODE (SOLVER vs POST-PROCESSOR) =====
910 // == IIC. CONFIGURE SIMULATION ENVIRONMENT & DIRECTORIES =====
911 ierr = SetupSimulationEnvironment(simCtx); CHKERRQ(ierr);
912 // === III. SETUP GRID & DATA STRUCTURES ===================================
913 ierr = SetupGridAndSolvers(simCtx); CHKERRQ(ierr);
914 // === IV. SETUP DOMAIN DECOMPOSITION INFORMATION =========================
915 ierr = SetupDomainRankInfo(simCtx); CHKERRQ(ierr);
916 // === V. SETUP BOUNDARY CONDITIONS ====================================
917 ierr = SetupBoundaryConditions(simCtx); CHKERRQ(ierr);
918 // === VI. SETUP USER CONTEXT & DATA STRUCTURES ============================
919 // Get the finest-level user context, as this is where we'll load data
920 UserCtx *user = simCtx->usermg.mgctx[simCtx->usermg.mglevels-1].user;
921 PostProcessParams *pps = simCtx->pps;
922
923 // === VI. CAPABILITY DISPATCH ============================================
924 // Each stage declares what it needs rather than inferring it from another
925 // stage's configuration. Field statistics are Eulerian and must not require a
926 // swarm: a turbulence run normally carries no particles at all.
927 PetscBool needs_particle_stage = (pps->outputParticles || pps->particle_pipeline[0] != '\0' || pps->statistics_pipeline[0] != '\0') ? PETSC_TRUE : PETSC_FALSE;
928 if(needs_particle_stage) {
929 if(simCtx->np > 0){
930 ierr = InitializeParticleSwarm(simCtx); CHKERRQ(ierr);
931 // Create a post-processing specific DMSwarm
932 ierr = SetupPostProcessSwarm(user,pps); CHKERRQ(ierr);
933 }else{
934 SETERRQ(PETSC_COMM_SELF,1,
935 "Particle post-processing requested (particle output or particle statistics pipeline) "
936 "but np=0. Please set np>0 during solver run to enable particle post-processing.");
937 }
938 }
939
940 LOG_ALLOW(GLOBAL, LOG_INFO, "=============================================================\n");
941
942
943 // The grid is loaded once, so it is dimensionalized once; the per-step pipeline
944 // scales only the fields it reloads.
945 PetscBool coordinates_dimensionalized = PETSC_FALSE;
946
947 // === VII. MAIN POST-PROCESSING LOOP ======================================
948 PetscInt *steps = NULL, step_count = 0;
949 ierr = ResolvePostProcessingSteps(pps, &steps, &step_count); CHKERRQ(ierr);
950 LOG_ALLOW(GLOBAL, LOG_INFO, "Processing %" PetscInt_FMT " step(s).\n", step_count);
951 for (PetscInt k = 0; k < step_count; ++k) {
952 const PetscInt ti = steps[k];
953 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Processing Time Step %" PetscInt_FMT " ---\n", ti);
954
955 // 1. Load Data (UpdateLocalGhosts is called inside the kernels)
956 ierr = ReadSimulationFields(user, ti); CHKERRQ(ierr);
957
958 // After the first read, which validates the checkpoint against the grid in
959 // nondimensional units and caches that geometry digest.
960 if (pps->dimensionalize && !coordinates_dimensionalized) {
961 ierr = DimensionalizeField(user, "Coordinates"); CHKERRQ(ierr);
962 coordinates_dimensionalized = PETSC_TRUE;
963 }
964
965 // 2. Transform Data
966 ierr = EulerianDataProcessingPipeline(user, pps); CHKERRQ(ierr);
967
968 // 3. Write Output
969 ierr = WriteEulerianFile(user, pps, ti); CHKERRQ(ierr);
970
971 if(needs_particle_stage) {
972 // 1. Resize swarm based on particle count in this timestep's file
973 ierr = PreCheckAndResizeSwarm(user, ti, pps->particleExt); CHKERRQ(ierr);
974
975 // 2. Load particle data into the correctly sized swarm
976 ierr = ReadAllSwarmFields(user, ti); CHKERRQ(ierr);
977
978 // 3. Global statistical reductions (MSD, etc.) → CSV files. These compare
979 // against nondimensional theory, so they read the positions as loaded.
980 ierr = GlobalStatisticsPipeline(user, pps, ti); CHKERRQ(ierr);
981
982 // 4. Dimensionalize the loaded particle fields before anything derives from
983 // or writes them; they are loaded after the Eulerian pipeline has run.
984 if (pps->dimensionalize) {
985 // Every loaded particle field with a physical dimension, each once.
986 for (PetscInt raw_id = 0; raw_id < PARTICLE_FIELD_ID_COUNT; ++raw_id) {
987 const ParticleFieldDescriptor *descriptor = NULL;
988
989 ierr = ParticleFieldGetDescriptor((ParticleFieldId)raw_id, &descriptor); CHKERRQ(ierr);
990 if (!(descriptor->capabilities & PARTICLE_FIELD_CAPABILITY_CHECKPOINT)) continue;
991 if (descriptor->dimension.kind != FIELD_DIMENSION_FIXED) continue;
992 ierr = DimensionalizeField(user, descriptor->canonical_name); CHKERRQ(ierr);
993 }
994 }
995
996 // 5. Transform particle data
997 ierr = ParticleDataProcessingPipeline(user, pps); CHKERRQ(ierr);
998
999 // 6. Write particle output (optional)
1000 if (pps->outputParticles) {
1001 ierr = WriteParticleFile(user, pps, ti); CHKERRQ(ierr);
1002 }
1003 }
1004
1005 // 4. Accumulated Eulerian window statistics → derived fields and history.
1006 // Eulerian and independent of the swarm, so it sits outside the particle
1007 // block: a turbulence run normally carries no particles at all.
1008 ierr = FieldStatisticsPipeline(user, pps, ti); CHKERRQ(ierr);
1009
1010 if(simCtx->rank == 0){
1011 PetscReal currentTime = (PetscReal)ti*simCtx->dt;
1012 PrintProgressBar(k, 0, step_count, currentTime);
1013 if(get_log_level()>LOG_ERROR)PetscPrintf(PETSC_COMM_SELF,"\n");
1014 }
1015 ierr = RuntimeMemoryLogSample(simCtx, ti, "Post", "-"); CHKERRQ(ierr);
1016 }
1017
1018 // End the progress bar's line so subsequent terminal output starts on a fresh line.
1019 if (simCtx->rank == 0 && step_count > 0) {
1020 PetscPrintf(PETSC_COMM_SELF, "\n");
1021 fflush(stdout);
1022 }
1023 const PetscInt last_step = step_count > 0 ? steps[step_count - 1] : pps->endTime;
1024 ierr = PetscFree(steps); CHKERRQ(ierr);
1025
1026 LOG_ALLOW(GLOBAL, LOG_INFO, "=============================================================\n");
1027 LOG_ALLOW(GLOBAL, LOG_INFO, "Post-processing finished successfully.\n");
1028
1029
1030 // === VIII. FINALIZE =========================================================
1031 ierr = RuntimeMemoryLogSample(simCtx, last_step, "Final", "Complete"); CHKERRQ(ierr);
1032 ierr = ProfilingFinalize(simCtx); CHKERRQ(ierr);
1033 ierr = FinalizeSimulation(simCtx); CHKERRQ(ierr);
1034 ierr = PetscFinalize();
1035 return ierr;
1036}
PetscErrorCode PreCheckAndResizeSwarm(UserCtx *user, PetscInt ti, const char *ext)
Checks particle count in the reference file and resizes the swarm if needed.
PetscErrorCode InitializeParticleSwarm(SimCtx *simCtx)
High-level particle initialization orchestrator for a simulation run.
@ FIELD_DIMENSION_FIXED
The exponents are the field's dimension.
FieldDimensionKind kind
PetscErrorCode ReadSimulationFields(UserCtx *user, PetscInt ti)
Reads binary field data for velocity, pressure, and other required vectors.
Definition io.c:1471
PetscErrorCode ReadAllSwarmFields(UserCtx *user, PetscInt ti)
Reads multiple fields (positions, velocity, CellID, and weight) into a DMSwarm.
Definition io.c:1883
PetscErrorCode ProfilingFinalize(SimCtx *simCtx)
the profiling excercise and build a profiling summary which is then printed to a log file.
Definition logging.c:2305
void PrintProgressBar(PetscInt step, PetscInt startStep, PetscInt totalSteps, PetscReal currentTime)
Prints a progress bar to the console.
Definition logging.c:2411
PetscErrorCode RuntimeMemoryLogSample(SimCtx *simCtx, PetscInt step, const char *event, const char *reason)
Append a reduced runtime memory sample to the configured memory log.
Definition logging.c:2195
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:87
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:29
ParticleFieldId
Compile-time identity for a persistent solver-particle field.
@ PARTICLE_FIELD_ID_COUNT
@ PARTICLE_FIELD_CAPABILITY_CHECKPOINT
PetscErrorCode ParticleFieldGetDescriptor(ParticleFieldId field_id, const ParticleFieldDescriptor **descriptor)
Return immutable metadata for a valid particle field ID.
Immutable metadata for one persistent particle field.
PetscErrorCode DimensionalizeField(UserCtx *user, const char *field_name)
Scales a specified field from non-dimensional to dimensional units in-place.
PetscErrorCode EulerianDataProcessingPipeline(UserCtx *user, PostProcessParams *pps)
Implementation of EulerianDataProcessingPipeline().
PetscErrorCode WriteEulerianFile(UserCtx *user, PostProcessParams *pps, PetscInt ti)
Implementation of WriteEulerianFile().
PetscErrorCode GlobalStatisticsPipeline(UserCtx *user, PostProcessParams *pps, PetscInt ti)
Internal helper implementation: GlobalStatisticsPipeline().
PetscErrorCode ResolvePostProcessingSteps(PostProcessParams *pps, PetscInt **steps, PetscInt *count)
Internal helper implementation: ResolvePostProcessingSteps().
PetscErrorCode ParticleDataProcessingPipeline(UserCtx *user, PostProcessParams *pps)
Implementation of ParticleDataProcessingPipeline().
PetscErrorCode WriteParticleFile(UserCtx *user, PostProcessParams *pps, PetscInt ti)
Implementation of WriteParticleFile().
PetscErrorCode FieldStatisticsPipeline(UserCtx *user, PostProcessParams *pps, PetscInt ti)
Implementation of FieldStatisticsPipeline().
PetscErrorCode SetupPostProcessSwarm(UserCtx *user, PostProcessParams *pps)
Internal helper implementation: SetupPostProcessSwarm().
PetscErrorCode SetupDomainRankInfo(SimCtx *simCtx)
Sets up the full rank communication infrastructure, including neighbor ranks and bounding box exchang...
Definition setup.c:3227
PetscErrorCode SetupGridAndSolvers(SimCtx *simCtx)
The main orchestrator for setting up all grid-related components.
Definition setup.c:2001
PetscErrorCode SetupSimulationEnvironment(SimCtx *simCtx)
Verifies and prepares the complete I/O environment for a simulation run.
Definition setup.c:1685
int PicurvHandleVersionArgument(int argc, char **argv, const char *executable_name)
Print the shared native build identity when a version flag is present.
Definition setup.c:47
PetscErrorCode CreateSimulationContext(int argc, char **argv, SimCtx **p_simCtx)
Allocates and populates the master SimulationContext object.
Definition setup.c:371
PetscErrorCode SetupBoundaryConditions(SimCtx *simCtx)
(Orchestrator) Sets up all boundary conditions for the simulation.
Definition setup.c:2678
PetscErrorCode FinalizeSimulation(SimCtx *simCtx)
Main cleanup function for the entire simulation context.
Definition setup.c:4368
UserCtx * user
Definition variables.h:729
PetscBool dimensionalize
Whether derived output leaves non-dimensional form, from global_operations.dimensionalize.
Definition variables.h:789
UserMG usermg
Definition variables.h:1015
PetscReal dt
Definition variables.h:874
PetscInt mglevels
Definition variables.h:736
PostProcessParams * pps
Definition variables.h:1058
PetscMPIInt size
Definition variables.h:863
@ EXEC_MODE_POSTPROCESSOR
Definition variables.h:833
char particleExt[8]
Definition variables.h:795
MGCtx * mgctx
Definition variables.h:739
ExecutionMode exec_mode
Definition variables.h:878
Holds all configuration parameters for a post-processing run.
Definition variables.h:754
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1074
Here is the call graph for this function: