PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
postprocessor.c
Go to the documentation of this file.
1/**
2 * @file postprocessor.c
3 * @brief Offline post-processing tool driving the derived-output pipelines.
4 *
5 * Reads committed checkpoint bundles over a step range and dispatches the
6 * Eulerian, Lagrangian, particle-statistics, and field-statistics pipelines that
7 * a recipe selected, writing one output family per enabled pipeline.
8 */
9
10#include "postprocessor.h" // Use our new header
12#include "statistics_window.h"
13
14
15#undef __FUNCT__
16#define __FUNCT__ "SetupPostProcessSwarm"
17/**
18 * @brief Internal helper implementation: `SetupPostProcessSwarm()`.
19 * @details Local to this translation unit.
20 */
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}
97
98
99#undef __FUNCT__
100#define __FUNCT__ "EulerianDataProcessingPipeline"
101/**
102 * @brief Implementation of \ref EulerianDataProcessingPipeline().
103 * @details Full API contract (arguments, ownership, side effects) is documented with
104 * the header declaration in `include/postprocessor.h`.
105 * @see EulerianDataProcessingPipeline()
106 */
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}
191
192
193#undef __FUNCT__
194#define __FUNCT__ "WriteEulerianFile"
195/**
196 * @brief Implementation of \ref WriteEulerianFile().
197 * @details Full API contract (arguments, ownership, side effects) is documented with
198 * the header declaration in `include/postprocessor.h`.
199 * @see WriteEulerianFile()
200 */
201PetscErrorCode WriteEulerianFile(UserCtx* user, PostProcessParams* pps, PetscInt ti)
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}
475
476
477#undef __FUNCT__
478#define __FUNCT__ "FieldStatisticsPipeline"
479/**
480 * @brief Implementation of \ref FieldStatisticsPipeline().
481 * @details Full API contract (arguments, ownership, side effects) is documented with
482 * the header declaration in `include/postprocessor.h`.
483 * @see FieldStatisticsPipeline()
484 */
485PetscErrorCode FieldStatisticsPipeline(UserCtx *user, PostProcessParams *pps, PetscInt ti)
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}
617
618#undef __FUNCT__
619#define __FUNCT__ "ParticleDataProcessingPipeline"
620/**
621 * @brief Implementation of \ref ParticleDataProcessingPipeline().
622 * @details Full API contract (arguments, ownership, side effects) is documented with
623 * the header declaration in `include/postprocessor.h`.
624 * @see ParticleDataProcessingPipeline()
625 */
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}
691
692#undef __FUNCT__
693#define __FUNCT__ "GlobalStatisticsPipeline"
694/**
695 * @brief Internal helper implementation: `GlobalStatisticsPipeline()`.
696 * @details Local to this translation unit.
697 */
698PetscErrorCode GlobalStatisticsPipeline(UserCtx *user, PostProcessParams *pps, PetscInt ti)
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}
740
741#undef __FUNCT__
742#define __FUNCT__ "WriteParticleFile"
743/**
744 * @brief Implementation of \ref WriteParticleFile().
745 * @details Full API contract (arguments, ownership, side effects) is documented with
746 * the header declaration in `include/postprocessor.h`.
747 * @see WriteParticleFile()
748 */
749PetscErrorCode WriteParticleFile(UserCtx* user, PostProcessParams* pps, PetscInt ti)
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}
809
810#undef __FUNCT__
811#define __FUNCT__ "main"
812#ifndef PICURV_POSTPROCESSOR_NO_MAIN
813/**
814 * @brief Entry point for the postprocessor executable.
815 * @details Initializes PETSc, loads post-processing inputs, executes the
816 * requested pipelines, and finalizes runtime resources before exit.
817 */
818int main(int argc, char **argv)
819{
820 PetscErrorCode ierr;
821 SimCtx *simCtx = NULL;
822
823 // === I. INITIALIZE PETSC & MPI ===========================================
824 ierr = PetscInitialize(&argc, &argv, (char *)0, "Unified Post-Processing Tool"); CHKERRQ(ierr);
825
826 // === II. CONFIGURE SIMULATION & POST-PROCESSING CONTEXTS =================
827 ierr = CreateSimulationContext(argc, argv, &simCtx); CHKERRQ(ierr);
828 ierr = PetscPrintf(PETSC_COMM_WORLD, "Postprocessor MPI processes: %d\n", (int)simCtx->size); CHKERRQ(ierr);
829 // === IIB. SET EXECUTION MODE (SOLVER vs POST-PROCESSOR) =====
831 // == IIC. CONFIGURE SIMULATION ENVIRONMENT & DIRECTORIES =====
832 ierr = SetupSimulationEnvironment(simCtx); CHKERRQ(ierr);
833 // === III. SETUP GRID & DATA STRUCTURES ===================================
834 ierr = SetupGridAndSolvers(simCtx); CHKERRQ(ierr);
835 // === IV. SETUP DOMAIN DECOMPOSITION INFORMATION =========================
836 ierr = SetupDomainRankInfo(simCtx); CHKERRQ(ierr);
837 // === V. SETUP BOUNDARY CONDITIONS ====================================
838 ierr = SetupBoundaryConditions(simCtx); CHKERRQ(ierr);
839 // === VI. SETUP USER CONTEXT & DATA STRUCTURES ============================
840 // Get the finest-level user context, as this is where we'll load data
841 UserCtx *user = simCtx->usermg.mgctx[simCtx->usermg.mglevels-1].user;
842 PostProcessParams *pps = simCtx->pps;
843
844 // === VI. CAPABILITY DISPATCH ============================================
845 // Each stage declares what it needs rather than inferring it from another
846 // stage's configuration. Field statistics are Eulerian and must not require a
847 // swarm: a turbulence run normally carries no particles at all.
848 PetscBool needs_particle_stage = (pps->outputParticles || pps->particle_pipeline[0] != '\0' || pps->statistics_pipeline[0] != '\0') ? PETSC_TRUE : PETSC_FALSE;
849 if(needs_particle_stage) {
850 if(simCtx->np > 0){
851 ierr = InitializeParticleSwarm(simCtx); CHKERRQ(ierr);
852 // Create a post-processing specific DMSwarm
853 ierr = SetupPostProcessSwarm(user,pps); CHKERRQ(ierr);
854 }else{
855 SETERRQ(PETSC_COMM_SELF,1,
856 "Particle post-processing requested (particle output or particle statistics pipeline) "
857 "but np=0. Please set np>0 during solver run to enable particle post-processing.");
858 }
859 }
860
861 LOG_ALLOW(GLOBAL, LOG_INFO, "=============================================================\n");
862
863
864 // === VII. MAIN POST-PROCESSING LOOP ======================================
865 for (PetscInt ti = pps->startTime; ti <= pps->endTime; ti += pps->timeStep) {
866 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Processing Time Step %" PetscInt_FMT " ---\n", ti);
867
868 // 1. Load Data (UpdateLocalGhosts is called inside the kernels)
869 ierr = ReadSimulationFields(user, ti); CHKERRQ(ierr);
870
871 // 2. Transform Data
872 ierr = EulerianDataProcessingPipeline(user, pps); CHKERRQ(ierr);
873
874 // 3. Write Output
875 ierr = WriteEulerianFile(user, pps, ti); CHKERRQ(ierr);
876
877 if(needs_particle_stage) {
878 // 1. Resize swarm based on particle count in this timestep's file
879 ierr = PreCheckAndResizeSwarm(user, ti, pps->particleExt); CHKERRQ(ierr);
880
881 // 2. Load particle data into the correctly sized swarm
882 ierr = ReadAllSwarmFields(user, ti); CHKERRQ(ierr);
883
884 // 3. Transform particle data
885 ierr = ParticleDataProcessingPipeline(user, pps); CHKERRQ(ierr);
886
887 // 4. Write particle output (optional)
888 if (pps->outputParticles) {
889 ierr = WriteParticleFile(user, pps, ti); CHKERRQ(ierr);
890 }
891
892 // 5. Global statistical reductions (MSD, etc.) → CSV files
893 ierr = GlobalStatisticsPipeline(user, pps, ti); CHKERRQ(ierr);
894 }
895
896 // 4. Accumulated Eulerian window statistics → derived fields and history.
897 // Eulerian and independent of the swarm, so it sits outside the particle
898 // block: a turbulence run normally carries no particles at all.
899 ierr = FieldStatisticsPipeline(user, pps, ti); CHKERRQ(ierr);
900
901 if(simCtx->rank == 0){
902 PetscInt StepsToRun = pps->endTime - pps->startTime;
903 PetscReal currentTime = (PetscReal)ti*simCtx->dt;
904 PrintProgressBar(ti-1,pps->startTime,StepsToRun,currentTime);
905 if(get_log_level()>LOG_ERROR)PetscPrintf(PETSC_COMM_SELF,"\n");
906 }
907 ierr = RuntimeMemoryLogSample(simCtx, ti, "Post", "-"); CHKERRQ(ierr);
908 }
909
910 // After the loop, print the 100% complete bar on rank 0 and add a newline
911 // to ensure subsequent terminal output starts on a fresh line.
912 if (simCtx->rank == 0) {
913 PetscInt endTime = pps->endTime-1; // needs to be verified.
914 PetscInt StepsToRun = pps->endTime - pps->startTime;
915 PetscReal endTimeValue = (PetscReal)pps->endTime*simCtx->dt;
916 PrintProgressBar(endTime, pps->startTime, StepsToRun, endTimeValue);
917 PetscPrintf(PETSC_COMM_SELF, "\n");
918 fflush(stdout);
919 }
920
921 LOG_ALLOW(GLOBAL, LOG_INFO, "=============================================================\n");
922 LOG_ALLOW(GLOBAL, LOG_INFO, "Post-processing finished successfully.\n");
923
924
925 // === VIII. FINALIZE =========================================================
926 ierr = RuntimeMemoryLogSample(simCtx, pps->endTime, "Final", "Complete"); CHKERRQ(ierr);
927 ierr = ProfilingFinalize(simCtx); CHKERRQ(ierr);
928 ierr = FinalizeSimulation(simCtx); CHKERRQ(ierr);
929 ierr = PetscFinalize();
930 return ierr;
931}
932#endif
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.
PetscErrorCode RegisterSwarmField(DM swarm, const char *fieldName, PetscInt fieldDim, PetscDataType dtype)
Registers a swarm field without finalizing registration.
PetscErrorCode ReadSimulationFields(UserCtx *user, PetscInt ti)
Reads binary field data for velocity, pressure, and other required vectors.
Definition io.c:1463
void TrimWhitespace(char *str)
Removes leading and trailing ASCII whitespace from a mutable string.
Definition io.c:399
PetscErrorCode ReadAllSwarmFields(UserCtx *user, PetscInt ti)
Reads multiple fields (positions, velocity, CellID, and weight) into a DMSwarm.
Definition io.c:1864
PetscErrorCode RestoreFieldStatisticsState(SimCtx *simCtx, PetscInt ti)
Restores field-statistics window state and accumulators from a checkpoint.
Definition io.c:1667
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
#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
PetscErrorCode ProfilingFinalize(SimCtx *simCtx)
the profiling excercise and build a profiling summary which is then printed to a log file.
Definition logging.c:2196
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:84
void PrintProgressBar(PetscInt step, PetscInt startStep, PetscInt totalSteps, PetscReal currentTime)
Prints a progress bar to the console.
Definition logging.c:2302
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:2086
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
@ 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
PetscErrorCode ComputeParticleMSD(UserCtx *user, const char *stats_prefix, PetscInt ti)
Computes the mean-squared displacement (MSD) of a particle cloud.
PetscErrorCode ComputeQCriterion(UserCtx *user)
Computes the Q-criterion diagnostic from the local velocity-gradient tensor.
PetscErrorCode ComputeSpecificKE(UserCtx *user, const char *velocity_field, const char *ske_field)
Computes the specific kinetic energy (KE per unit mass) for each particle.
PetscErrorCode NormalizeRelativeField(UserCtx *user, const char *relative_field_name)
Normalizes pressure using the value at the configured logical grid point.
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 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.
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 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().
int main(int argc, char **argv)
Entry point for the postprocessor executable.
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:2576
PetscErrorCode SetupGridAndSolvers(SimCtx *simCtx)
The main orchestrator for setting up all grid-related components.
Definition setup.c:1364
PetscErrorCode SetupSimulationEnvironment(SimCtx *simCtx)
Verifies and prepares the complete I/O environment for a simulation run.
Definition setup.c:1063
PetscErrorCode CreateSimulationContext(int argc, char **argv, SimCtx **p_simCtx)
Allocates and populates the master SimulationContext object.
Definition setup.c:160
PetscErrorCode SetupBoundaryConditions(SimCtx *simCtx)
(Orchestrator) Sets up all boundary conditions for the simulation.
Definition setup.c:2027
PetscErrorCode FinalizeSimulation(SimCtx *simCtx)
Main cleanup function for the entire simulation context.
Definition setup.c:3721
Per-window PETSc accumulator storage and pointwise application.
PetscErrorCode PicurvWindowDerivedCount(const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const char *outputs, PetscInt *count)
Reports how many derived fields a requested output set produces.
Window lifecycle, scheduling, and weighting for the field-statistics pipeline.
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
char statistics_output_prefix[256]
basename for CSV output, e.g.
Definition variables.h:617
Vec P_nodal
Definition variables.h:1001
UserCtx * user
Definition variables.h:571
PetscInt npoints
Definition variables.h:656
PetscInt num_components
Definition variables.h:643
char particle_output_prefix[256]
Definition variables.h:612
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
UserMG usermg
Definition variables.h:852
#define MAX_FIELD_LIST_LENGTH
Definition variables.h:587
DM post_swarm
Definition variables.h:1000
PetscReal dt
Definition variables.h:710
PetscInt * connectivity
Definition variables.h:660
PetscInt * offsets
Definition variables.h:661
Vec Ucat_nodal
Definition variables.h:1002
PetscInt timeStep
Definition variables.h:603
PetscInt np
Definition variables.h:827
#define MAX_FILENAME_LENGTH
Definition variables.h:588
Vec Qcrit
Definition variables.h:1003
char statistics_pipeline[1024]
e.g.
Definition variables.h:616
char field_statistics_formats[1024]
Comma-separated formats: vtk for derived fields, csv for the convergence history.
Definition variables.h:625
char output_fields_instantaneous[1024]
Definition variables.h:608
PetscScalar * data
Definition variables.h:644
struct PicurvWindow * fieldStatisticsWindows
Definition variables.h:771
char particle_pipeline[1024]
Definition variables.h:610
char name[64]
Definition variables.h:642
PetscScalar * coords
Definition variables.h:657
PetscInt mglevels
Definition variables.h:578
char process_pipeline[1024]
Definition variables.h:607
char particle_fields[1024]
Definition variables.h:611
PetscBool outputParticles
Definition variables.h:604
VTKFieldInfo point_data_fields[20]
Definition variables.h:658
struct PicurvWindowStorage * fieldStatisticsStorage
Definition variables.h:962
Vec Psi_nodal
Definition variables.h:1004
PetscInt mz
Definition variables.h:655
PostProcessParams * pps
Definition variables.h:890
PetscMPIInt size
Definition variables.h:699
@ EXEC_MODE_POSTPROCESSOR
Definition variables.h:669
PetscInt field_statistics_source_step
Committed step supplying the state; negative means the step being processed.
Definition variables.h:627
char particleExt[8]
Definition variables.h:631
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
MGCtx * mgctx
Definition variables.h:581
ExecutionMode exec_mode
Definition variables.h:714
PetscInt startTime
Definition variables.h:601
PetscInt my
Definition variables.h:655
PetscInt mx
Definition variables.h:655
#define MAX_VTK_FIELD_NAME_LENGTH
Maximum length for VTK field names.
Definition variables.h:591
PetscBool fieldStatisticsContinue
Definition variables.h:776
Holds all configuration parameters for a post-processing run.
Definition variables.h:596
The master context for the entire simulation.
Definition variables.h:695
User-defined context containing data specific to a single computational grid level.
Definition variables.h:906
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
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