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

Go to the source code of this file.

Functions

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 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 DimensionalizeField (UserCtx *user, const char *field_name)
 Scales a specified field from non-dimensional to dimensional units in-place.
 
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 ComputeDisplacement (UserCtx *user, const char *disp_field)
 Computes the displacement magnitude |r_i - r_0| for each particle (per-particle VTK kernel).
 
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 ComputeWindowStatisticsSummary (UserCtx *user, PetscInt window_index, const char *output_prefix, PetscInt ti)
 Appends one convergence row for an accumulated window to its CSV history.
 

Function Documentation

◆ ComputeNodalAverage()

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.

The kernel reads the input field by name, computes nodal values, and stores the output in the named destination field. Both fields must already exist in the current UserCtx.

Parameters
[in,out]userBlock-level context that owns the source and destination vectors.
[in]in_field_nameName of the input field to sample.
[in]out_field_nameName of the output field to populate.
Returns
PetscErrorCode 0 on success.

Interpolates a cell-centered field to nodal locations using local stencil averaging.

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

See also
ComputeNodalAverage()

Definition at line 99 of file postprocessing_kernels.c.

100{
101 PetscErrorCode ierr;
102 FieldId in_field_id;
103 Vec in_vec_local = NULL, out_vec_global = NULL;
104 DM dm_in = NULL, dm_out = NULL;
105 PetscInt dof = 0;
106
107 PetscFunctionBeginUser;
109 LOG_ALLOW(GLOBAL, LOG_INFO, "-> KERNEL: Running ComputeNodalAverage on '%s' -> '%s'.\n", in_field_name, out_field_name);
110
111 // --- 1. Map string names to PETSc objects ---
112 if (strcasecmp(in_field_name, "P") == 0) { in_vec_local = user->lP; dm_in = user->da; dof = 1; }
113 else if (strcasecmp(in_field_name, "Ucat") == 0) { in_vec_local = user->lUcat; dm_in = user->fda; dof = 3; }
114 else if (strcasecmp(in_field_name, "Psi") == 0) { in_vec_local = user->lPsi; dm_in = user->da; dof = 1; }
115 else if (strcasecmp(in_field_name, "Qcrit") == 0) { in_vec_local = user->lQcrit; dm_in = user->da; dof = 1; }
116 /* The staging pair carries derived statistics, which are config-counted and so
117 * cannot be named by a compile-time member of their own. */
118 else if (strcasecmp(in_field_name, "PostScalar") == 0) { in_vec_local = user->lPostScalar; dm_in = user->da; dof = 1; }
119 else if (strcasecmp(in_field_name, "PostVector") == 0) { in_vec_local = user->lPostVector; dm_in = user->fda; dof = 3; }
120 // ... (add other fields as needed) ...
121 else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Unknown input field name for nodal averaging: %s", in_field_name);
122
123 if (strcasecmp(out_field_name, "P_nodal") == 0) { out_vec_global = user->P_nodal; dm_out = user->da; }
124 else if (strcasecmp(out_field_name, "Ucat_nodal") == 0) { out_vec_global = user->Ucat_nodal; dm_out = user->fda; }
125 else if (strcasecmp(out_field_name, "Psi_nodal") == 0) { out_vec_global = user->Psi_nodal; dm_out = user->da; }
126 else if (strcasecmp(out_field_name, "Qcrit_nodal") == 0) { out_vec_global = user->Qcrit_nodal; dm_out = user->da; }
127 else if (strcasecmp(out_field_name, "PostScalarNodal") == 0) { out_vec_global = user->PostScalarNodal; dm_out = user->da; }
128 else if (strcasecmp(out_field_name, "PostVectorNodal") == 0) { out_vec_global = user->PostVectorNodal; dm_out = user->fda; }
129 // ... (add other fields as needed) ...
130 else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Unknown output field name for nodal averaging: %s", out_field_name);
131
132 // --- 2. Ensure Input Data Ghosts are Up-to-Date ---
133 ierr = FieldIdFromName(in_field_name, &in_field_id); CHKERRQ(ierr);
134 ierr = UpdateLocalGhosts(user, in_field_id); CHKERRQ(ierr);
135 /* The boundary node average reads the dummy cells, so show them before averaging. */
137 ierr = LOG_FIELD_ANATOMY(user, in_field_id, "PreNodalAverage"); CHKERRQ(ierr);
138 }
139
140 // --- 3. Get DMDA info and array pointers ---
141 DMDALocalInfo info;
142 ierr = DMDAGetLocalInfo(dm_out, &info); CHKERRQ(ierr);
143 /* Every owned output point is valid except the global high layout plane.
144 * A rank interface is not a boundary: its +1 source value is in the halo. */
145 const PetscInt i_end = PetscMin(info.xs + info.xm, info.mx - 1);
146 const PetscInt j_end = PetscMin(info.ys + info.ym, info.my - 1);
147 const PetscInt k_end = PetscMin(info.zs + info.zm, info.mz - 1);
148
149 if (dof == 1) { // --- Scalar Field Averaging ---
150 const PetscReal ***l_in_arr;
151 PetscReal ***g_out_arr;
152 ierr = DMDAVecGetArrayRead(dm_in,in_vec_local, (void*)&l_in_arr); CHKERRQ(ierr);
153 ierr = DMDAVecGetArray(dm_out,out_vec_global, (void*)&g_out_arr); CHKERRQ(ierr);
154
155 // Loop over the output NODE locations. The loop bounds match the required
156 // size of the final subsampled grid.
157 for (PetscInt k = info.zs; k < k_end; k++) {
158 for (PetscInt j = info.ys; j < j_end; j++) {
159 for (PetscInt i = info.xs; i < i_end; i++) {
160 g_out_arr[k][j][i] = 0.125 * (l_in_arr[k][j][i] + l_in_arr[k][j][i+1] +
161 l_in_arr[k][j+1][i] + l_in_arr[k][j+1][i+1] +
162 l_in_arr[k+1][j][i] + l_in_arr[k+1][j][i+1] +
163 l_in_arr[k+1][j+1][i] + l_in_arr[k+1][j+1][i+1]);
164 }
165 }
166 }
167 ierr = DMDAVecRestoreArrayRead(dm_in,in_vec_local, (void*)&l_in_arr); CHKERRQ(ierr);
168 ierr = DMDAVecRestoreArray(dm_out,out_vec_global, (void*)&g_out_arr); CHKERRQ(ierr);
169
170 } else if (dof == 3) { // --- Vector Field Averaging ---
171 const Cmpnts ***l_in_arr;
172 Cmpnts ***g_out_arr;
173 ierr = DMDAVecGetArrayRead(dm_in,in_vec_local, (void*)&l_in_arr); CHKERRQ(ierr);
174 ierr = DMDAVecGetArray(dm_out,out_vec_global, (void*)&g_out_arr); CHKERRQ(ierr);
175
176 for (PetscInt k = info.zs; k < k_end; k++) {
177 for (PetscInt j = info.ys; j < j_end; j++) {
178 for (PetscInt i = info.xs; i < i_end; i++) {
179 g_out_arr[k][j][i].x = 0.125 * (l_in_arr[k][j][i].x + l_in_arr[k][j][i+1].x +
180 l_in_arr[k][j+1][i].x + l_in_arr[k][j+1][i+1].x +
181 l_in_arr[k+1][j][i].x + l_in_arr[k+1][j][i+1].x +
182 l_in_arr[k+1][j+1][i].x + l_in_arr[k+1][j+1][i+1].x);
183
184 g_out_arr[k][j][i].y = 0.125 * (l_in_arr[k][j][i].y + l_in_arr[k][j][i+1].y +
185 l_in_arr[k][j+1][i].y + l_in_arr[k][j+1][i+1].y +
186 l_in_arr[k+1][j][i].y + l_in_arr[k+1][j][i+1].y +
187 l_in_arr[k+1][j+1][i].y + l_in_arr[k+1][j+1][i+1].y);
188
189 g_out_arr[k][j][i].z = 0.125 * (l_in_arr[k][j][i].z + l_in_arr[k][j][i+1].z +
190 l_in_arr[k][j+1][i].z + l_in_arr[k][j+1][i+1].z +
191 l_in_arr[k+1][j][i].z + l_in_arr[k+1][j][i+1].z +
192 l_in_arr[k+1][j+1][i].z + l_in_arr[k+1][j+1][i+1].z);
193 }
194 }
195 }
196 ierr = DMDAVecRestoreArrayRead(dm_in,in_vec_local, (void*)&l_in_arr); CHKERRQ(ierr);
197 ierr = DMDAVecRestoreArray(dm_out,out_vec_global, (void*)&g_out_arr); CHKERRQ(ierr);
198 }
200 PetscFunctionReturn(0);
201}
PetscErrorCode FieldIdFromName(const char *field_name, FieldId *field_id)
Resolve a user-facing field name once into its typed identity.
FieldId
Compile-time identity for a catalogued Eulerian field.
PetscBool is_function_allowed(const char *functionName)
Checks if a given function is in the allow-list.
Definition logging.c:186
#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
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:87
PetscErrorCode LOG_FIELD_ANATOMY(UserCtx *user, FieldId field_id, const char *stage_name)
Logs the anatomy of a specified field at key boundary locations, respecting the solver's specific gri...
Definition logging.c:2861
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:34
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:885
#define __FUNCT__
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:2489
Vec Qcrit_nodal
Q-criterion averaged to grid nodes; the field a .vts can place correctly.
Definition variables.h:1179
Vec lPostScalar
Definition variables.h:1133
Vec P_nodal
Definition variables.h:1176
Vec Ucat_nodal
Definition variables.h:1177
Vec PostScalarNodal
Definition variables.h:1133
PetscScalar x
Definition variables.h:122
PetscScalar z
Definition variables.h:122
Vec PostVectorNodal
Definition variables.h:1134
Vec Psi_nodal
Definition variables.h:1180
Vec lPostVector
Definition variables.h:1134
Vec lUcat
Definition variables.h:1113
PetscScalar y
Definition variables.h:122
Vec lQcrit
Cell-centred Q-criterion and its ghosted copy for nodal averaging.
Definition variables.h:1178
A 3D point or vector with PetscScalar components.
Definition variables.h:121
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ComputeQCriterion()

PetscErrorCode ComputeQCriterion ( UserCtx *  user)

Computes the Q-criterion diagnostic from the local velocity-gradient tensor.

This kernel evaluates rotational versus strain-rate dominance and writes the result into the configured Q-criterion output vector for visualization and flow feature identification.

Parameters
[in,out]userBlock-level context containing velocity fields and target output storage.
Returns
PetscErrorCode 0 on success.

Computes the Q-criterion diagnostic from the local velocity-gradient tensor.

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

See also
ComputeQCriterion()

Definition at line 366 of file postprocessing_kernels.c.

367{
368 PetscErrorCode ierr;
369 DMDALocalInfo info;
370 const Cmpnts ***lucat, ***lcsi, ***leta, ***lzet;
371 const PetscReal***laj, ***lnvert;
372 PetscReal ***gq;
373
374 PetscFunctionBeginUser;
376 LOG_ALLOW(GLOBAL, LOG_INFO, "-> KERNEL: Running ComputeQCriterion.\n");
377
378 // --- 1. Ensure all required ghost values are up-to-date ---
379 ierr = UpdateLocalGhosts(user, FIELD_ID_UCAT); CHKERRQ(ierr);
380 ierr = UpdateLocalGhosts(user, FIELD_ID_CSI); CHKERRQ(ierr);
381 ierr = UpdateLocalGhosts(user, FIELD_ID_ETA); CHKERRQ(ierr);
382 ierr = UpdateLocalGhosts(user, FIELD_ID_ZET); CHKERRQ(ierr);
383 ierr = UpdateLocalGhosts(user, FIELD_ID_AJ); CHKERRQ(ierr);
384 ierr = UpdateLocalGhosts(user, FIELD_ID_NVERT); CHKERRQ(ierr);
385
386 // --- 2. Get DMDA info and array pointers ---
387 ierr = DMDAGetLocalInfo(user->da, &info); CHKERRQ(ierr);
388
389 ierr = DMDAVecGetArrayRead(user->fda, user->lUcat, (void*)&lucat); CHKERRQ(ierr);
390 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (void*)&lcsi); CHKERRQ(ierr);
391 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (void*)&leta); CHKERRQ(ierr);
392 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (void*)&lzet); CHKERRQ(ierr);
393 ierr = DMDAVecGetArrayRead(user->da, user->lAj, (void*)&laj); CHKERRQ(ierr);
394 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (void*)&lnvert); CHKERRQ(ierr);
395 ierr = DMDAVecGetArray(user->da, user->Qcrit, (void*)&gq); CHKERRQ(ierr);
396
397 // --- 3. Define Loop Bounds for INTERIOR Cells ---
398 PetscInt i_start = (info.xs == 0) ? 1 : info.xs;
399 PetscInt i_end = (info.xs + info.xm == info.mx) ? info.mx - 1 : info.xs + info.xm;
400 PetscInt j_start = (info.ys == 0) ? 1 : info.ys;
401 PetscInt j_end = (info.ys + info.ym == info.my) ? info.my - 1 : info.ys + info.ym;
402 PetscInt k_start = (info.zs == 0) ? 1 : info.zs;
403 PetscInt k_end = (info.zs + info.zm == info.mz) ? info.mz - 1 : info.zs + info.zm;
404
405 // --- 4. Main Computation Loop ---
406 for (PetscInt k = k_start; k < k_end; k++) {
407 for (PetscInt j = j_start; j < j_end; j++) {
408 for (PetscInt i = i_start; i < i_end; i++) {
409
410 // Calculate velocity derivatives in computational space (central differences)
411 PetscReal uc = 0.5 * (lucat[k][j][i+1].x - lucat[k][j][i-1].x);
412 PetscReal vc = 0.5 * (lucat[k][j][i+1].y - lucat[k][j][i-1].y);
413 PetscReal wc = 0.5 * (lucat[k][j][i+1].z - lucat[k][j][i-1].z);
414
415 PetscReal ue = 0.5 * (lucat[k][j+1][i].x - lucat[k][j-1][i].x);
416 PetscReal ve = 0.5 * (lucat[k][j+1][i].y - lucat[k][j-1][i].y);
417 PetscReal we = 0.5 * (lucat[k][j+1][i].z - lucat[k][j-1][i].z);
418
419 PetscReal uz = 0.5 * (lucat[k+1][j][i].x - lucat[k-1][j][i].x);
420 PetscReal vz = 0.5 * (lucat[k+1][j][i].y - lucat[k-1][j][i].y);
421 PetscReal wz = 0.5 * (lucat[k+1][j][i].z - lucat[k-1][j][i].z);
422
423 // Average metrics to the cell center
424 PetscReal csi1 = 0.5 * (lcsi[k][j][i].x + lcsi[k][j][i-1].x) * laj[k][j][i];
425 PetscReal csi2 = 0.5 * (lcsi[k][j][i].y + lcsi[k][j][i-1].y) * laj[k][j][i];
426 PetscReal csi3 = 0.5 * (lcsi[k][j][i].z + lcsi[k][j][i-1].z) * laj[k][j][i];
427
428 PetscReal eta1 = 0.5 * (leta[k][j][i].x + leta[k][j-1][i].x) * laj[k][j][i];
429 PetscReal eta2 = 0.5 * (leta[k][j][i].y + leta[k][j-1][i].y) * laj[k][j][i];
430 PetscReal eta3 = 0.5 * (leta[k][j][i].z + leta[k][j-1][i].z) * laj[k][j][i];
431
432 PetscReal zet1 = 0.5 * (lzet[k][j][i].x + lzet[k-1][j][i].x) * laj[k][j][i];
433 PetscReal zet2 = 0.5 * (lzet[k][j][i].y + lzet[k-1][j][i].y) * laj[k][j][i];
434 PetscReal zet3 = 0.5 * (lzet[k][j][i].z + lzet[k-1][j][i].z) * laj[k][j][i];
435
436 // Calculate velocity gradient tensor components d_ij = du_i/dx_j
437 PetscReal d11 = uc * csi1 + ue * eta1 + uz * zet1;
438 PetscReal d12 = uc * csi2 + ue * eta2 + uz * zet2;
439 PetscReal d13 = uc * csi3 + ue * eta3 + uz * zet3;
440
441 PetscReal d21 = vc * csi1 + ve * eta1 + vz * zet1;
442 PetscReal d22 = vc * csi2 + ve * eta2 + vz * zet2;
443 PetscReal d23 = vc * csi3 + ve * eta3 + vz * zet3;
444
445 PetscReal d31 = wc * csi1 + we * eta1 + wz * zet1;
446 PetscReal d32 = wc * csi2 + we * eta2 + wz * zet2;
447 PetscReal d33 = wc * csi3 + we * eta3 + wz * zet3;
448
449 // Strain-Rate Tensor S_ij = 0.5 * (d_ij + d_ji)
450 PetscReal s11 = d11;
451 PetscReal s12 = 0.5 * (d12 + d21);
452 PetscReal s13 = 0.5 * (d13 + d31);
453 PetscReal s22 = d22;
454 PetscReal s23 = 0.5 * (d23 + d32);
455 PetscReal s33 = d33;
456
457 // Vorticity Tensor Omega_ij = 0.5 * (d_ij - d_ji)
458 PetscReal w12 = 0.5 * (d12 - d21);
459 PetscReal w13 = 0.5 * (d13 - d31);
460 PetscReal w23 = 0.5 * (d23 - d32);
461
462 // Squared norms of the tensors
463 PetscReal s_norm_sq = s11*s11 + s22*s22 + s33*s33 + 2.0*(s12*s12 + s13*s13 + s23*s23);
464 PetscReal w_norm_sq = 2.0 * (w12*w12 + w13*w13 + w23*w23);
465
466 gq[k][j][i] = 0.5 * (w_norm_sq - s_norm_sq);
467
468 if (lnvert[k][j][i] > 0.1) {
469 gq[k][j][i] = 0.0;
470 }
471 }
472 }
473 }
474
475 // --- 5. Restore arrays ---
476 ierr = DMDAVecRestoreArrayRead(user->fda, user->lUcat, (void*)&lucat); CHKERRQ(ierr);
477 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (void*)&lcsi); CHKERRQ(ierr);
478 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (void*)&leta); CHKERRQ(ierr);
479 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (void*)&lzet); CHKERRQ(ierr);
480 ierr = DMDAVecRestoreArrayRead(user->da, user->lAj, (void*)&laj); CHKERRQ(ierr);
481 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (void*)&lnvert); CHKERRQ(ierr);
482 ierr = DMDAVecRestoreArray(user->da, user->Qcrit, (void*)&gq); CHKERRQ(ierr);
483
484 /* With dimensionalize on, Ucat already carries U_ref but the metrics are still
485 * nondimensional, so the gradients above are U_ref/L_ref short of physical by one
486 * factor of L_ref each: Q came out U_ref^2 Q* instead of (U_ref/L_ref)^2 Q*. */
487 if (user->simCtx->pps && user->simCtx->pps->dimensionalize) {
488 PetscReal length_scale = 1.0;
489 if (!PicurvFieldReferenceScale(user->simCtx, "Coordinates", &length_scale, NULL, 0) &&
490 length_scale > 0.0) {
491 ierr = VecScale(user->Qcrit, 1.0 / (length_scale * length_scale)); CHKERRQ(ierr);
492 }
493 }
494
495 /* The loop above skips the layout boundary, so fill it before anything reads
496 * Qcrit with a stencil. Q is not a moment: it does not vanish at a wall, so only
497 * the periodic case is defined, and the cell synchronizer writes only that case. */
498 {
499 const FieldId qcrit[] = {FIELD_ID_QCRIT};
500 ierr = SynchronizePeriodicCellFields(user, 1, qcrit); CHKERRQ(ierr);
501 }
502
504 PetscFunctionReturn(0);
505}
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes periodic endpoint cells for a list of cell-centered fields.
@ FIELD_ID_CSI
@ FIELD_ID_NVERT
@ FIELD_ID_UCAT
@ FIELD_ID_AJ
@ FIELD_ID_ETA
@ FIELD_ID_QCRIT
@ FIELD_ID_ZET
PetscErrorCode PicurvFieldReferenceScale(SimCtx *simCtx, const char *field_name, PetscReal *scale, char *description, size_t description_length)
Physical scale one field is multiplied by to leave non-dimensional form.
Definition io.c:3197
Vec lNvert
Definition variables.h:1113
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1077
PetscBool dimensionalize
Whether derived output leaves non-dimensional form, from global_operations.dimensionalize.
Definition variables.h:789
Vec Qcrit
Definition variables.h:1178
PostProcessParams * pps
Definition variables.h:1058
Here is the call graph for this function:
Here is the caller graph for this function:

◆ NormalizeRelativeField()

PetscErrorCode NormalizeRelativeField ( UserCtx *  user,
const char *  relative_field_name 
)

Normalizes pressure using the value at the configured logical grid point.

The owning rank reads the reference value, shares it collectively, and every rank subtracts it in-place from its distributed portion of the field.

Parameters
[in,out]userBlock-level context containing pressure and reference configuration.
[in]relative_field_nameName of the field to normalize.
Returns
PetscErrorCode 0 on success.

Normalizes pressure using the value at the configured logical grid point.

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

See also
NormalizeRelativeField()

Definition at line 515 of file postprocessing_kernels.c.

516{
517 PetscErrorCode ierr;
518 Vec P_vec = NULL;
519 DMDALocalInfo info;
520 PetscInt ip=1, jp=1, kp=1; // Default reference point
521 PetscReal p_ref = 0.0;
522 PetscReal p_ref_local = 0.0;
523 PetscInt found_local = 0, found_global = 0;
524 PostProcessParams *pps = user->simCtx->pps;
525
526 // Fetch the logical reference point from pps.
527 ip = pps->reference[0];
528 jp = pps->reference[1];
529 kp = pps->reference[2];
530
531 PetscFunctionBeginUser;
533 LOG_ALLOW(GLOBAL, LOG_INFO, "-> KERNEL: Running NormalizeRelativeField on '%s'.\n", relative_field_name);
534
535 // --- 1. Map string argument to the PETSc Vec ---
536 if (strcasecmp(relative_field_name, "P") == 0) {
537 P_vec = user->P;
538 } else {
539 SETERRQ(PETSC_COMM_SELF, 1, "NormalizeRelativeField only supports the primary 'P' field , not '%s' currently.", relative_field_name);
540 }
541
542 // --- 2. Read the logical reference point from whichever rank owns it ---
543 ierr = DMDAGetLocalInfo(user->da, &info); CHKERRQ(ierr);
544 PetscCheck(ip >= 0 && ip < info.mx && jp >= 0 && jp < info.my &&
545 kp >= 0 && kp < info.mz,
546 PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
547 "Reference point (%" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT
548 ") lies outside the %" PetscInt_FMT "x%" PetscInt_FMT "x%" PetscInt_FMT
549 " pressure layout.", ip, jp, kp, info.mx, info.my, info.mz);
550 if (ip >= info.xs && ip < info.xs + info.xm &&
551 jp >= info.ys && jp < info.ys + info.ym &&
552 kp >= info.zs && kp < info.zs + info.zm) {
553 const PetscReal ***pressure = NULL;
554
555 ierr = DMDAVecGetArrayRead(user->da, P_vec, &pressure); CHKERRQ(ierr);
556 p_ref_local = pressure[kp][jp][ip];
557 ierr = DMDAVecRestoreArrayRead(user->da, P_vec, &pressure); CHKERRQ(ierr);
558 found_local = 1;
559 }
560 ierr = MPI_Allreduce(&p_ref_local, &p_ref, 1, MPIU_REAL, MPI_SUM,
561 PETSC_COMM_WORLD); CHKERRQ(ierr);
562 ierr = MPI_Allreduce(&found_local, &found_global, 1, MPIU_INT, MPI_SUM,
563 PETSC_COMM_WORLD); CHKERRQ(ierr);
564 PetscCheck(found_global == 1, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
565 "Reference pressure point must have exactly one owner; found %" PetscInt_FMT ".",
566 found_global);
568 "%s reference point (%" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT
569 ") has value %g.\n", relative_field_name, ip, jp, kp, (double)p_ref);
570
571 // --- 3. Perform the normalization (in-place shift) on the full distributed vector ---
572 ierr = VecShift(P_vec, -p_ref); CHKERRQ(ierr);
573 LOG_ALLOW(GLOBAL, LOG_DEBUG, "%s field normalized by subtracting %g.\n", relative_field_name, p_ref);
574
576 PetscFunctionReturn(0);
577}
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
PetscInt reference[3]
Definition variables.h:798
Holds all configuration parameters for a post-processing run.
Definition variables.h:754
Here is the caller graph for this function:

◆ DimensionalizeField()

PetscErrorCode DimensionalizeField ( UserCtx *  user,
const char *  field_name 
)

Scales a specified field from non-dimensional to dimensional units in-place.

Resolves the name in the Eulerian field catalog, then the particle field catalog, and scales the field's storage (its global Vec, the DM coordinates, or its DMSwarm field) by the reference scale of the dimension recorded on its catalog entry.

Parameters
[in,out]userThe UserCtx whose storage is modified.
[in]field_nameCase-insensitive catalogued name or alias, e.g. "Ucat", "Nu_t", "Coordinates", "ParticlePosition".
Returns
Zero on success; PETSC_ERR_ARG_UNKNOWN_TYPE for a name in neither catalog, and PETSC_ERR_ARG_WRONGSTATE for a field without a fixed dimension or without storage in this run.

Scales a specified field from non-dimensional to dimensional units in-place.

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

See also
DimensionalizeField()

Definition at line 15 of file postprocessing_kernels.c.

16{
17 PetscErrorCode ierr;
18 SimCtx *simCtx = NULL;
19 Vec target_vec = NULL;
20 PetscReal scale_factor = 1.0;
21 char field_type[64] = "Unknown";
22 PetscBool is_swarm_field = PETSC_FALSE; // Flag for special swarm handling
23 const char *swarm_field_name = NULL; // Name of the field within the swarm
24 FieldId field_id = FIELD_ID_INVALID;
26 PetscBool found = PETSC_FALSE;
27
28 PetscFunctionBeginUser;
30 if (!user) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx is NULL.");
31 if (!field_name) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "field_name is NULL.");
32 simCtx = user->simCtx;
33
34 // --- 1. Resolve the field in either catalog; its entry records its dimension ---
35 ierr = FieldTryIdFromName(field_name, &field_id, &found); CHKERRQ(ierr);
36 if (found && field_id == FIELD_ID_COORDINATES) {
37 ierr = DMGetCoordinates(user->da, &target_vec); CHKERRQ(ierr);
38 } else if (found) {
39 FieldView view;
40
41 ierr = FieldGetView(user, field_id, &view); CHKERRQ(ierr);
42 target_vec = view.global_vec;
43 } else {
44 const ParticleFieldDescriptor *descriptor = NULL;
45
46 ierr = ParticleFieldTryIdFromName(field_name, &particle_id, &found); CHKERRQ(ierr);
47 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE,
48 "DimensionalizeField: '%s' is in neither field catalog.", field_name);
49 ierr = ParticleFieldGetDescriptor(particle_id, &descriptor); CHKERRQ(ierr);
50 is_swarm_field = PETSC_TRUE;
51 swarm_field_name = descriptor->canonical_name;
52 }
53 ierr = PicurvFieldReferenceScale(simCtx, field_name, &scale_factor,
54 field_type, sizeof(field_type)); CHKERRQ(ierr);
55
56 // --- 2. Check for trivial scaling ---
57 if (PetscAbsReal(scale_factor - 1.0) < PETSC_MACHINE_EPSILON) {
58 LOG(GLOBAL, LOG_DEBUG, "DimensionalizeField: Scaling factor for '%s' is 1.0. Skipping operation.\n", field_name);
60 PetscFunctionReturn(0);
61 }
62
63 // --- 3. Perform the in-place scaling operation ---
64 LOG(GLOBAL, LOG_INFO, "Scaling '%s' field (%s) by factor %.4e.\n", field_name, field_type, scale_factor);
65
66 if (is_swarm_field) {
67 // Special handling for DMSwarm fields
68 ierr = DMSwarmCreateGlobalVectorFromField(user->swarm, swarm_field_name, &target_vec); CHKERRQ(ierr);
69 ierr = VecScale(target_vec, scale_factor); CHKERRQ(ierr);
70 ierr = DMSwarmDestroyGlobalVectorFromField(user->swarm, swarm_field_name, &target_vec); CHKERRQ(ierr);
71 } else {
72 // Standard handling for PETSc Vecs
73 if (target_vec) {
74 ierr = VecScale(target_vec, scale_factor); CHKERRQ(ierr);
75 } else {
76 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "Target vector for field '%s' was not found or is NULL.", field_name);
77 }
78 }
79
80 // --- 4. Post-scaling updates for special cases ---
81 if (strcasecmp(field_name, "Coordinates") == 0) {
82 ierr = UpdateLocalGhosts(user, FIELD_ID_COORDINATES); CHKERRQ(ierr);
83 }
84
86 PetscFunctionReturn(0);
87}
PetscErrorCode FieldGetView(UserCtx *user, FieldId field_id, FieldView *view)
Resolve the existing DM and global/local vectors for one field.
PetscErrorCode FieldTryIdFromName(const char *field_name, FieldId *field_id, PetscBool *found)
Look a name up without treating an unknown name as an error.
@ FIELD_ID_COORDINATES
@ FIELD_ID_INVALID
Non-owning runtime objects resolved for one field and UserCtx.
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:84
ParticleFieldId
Compile-time identity for a persistent solver-particle field.
@ PARTICLE_FIELD_ID_INVALID
PetscErrorCode ParticleFieldGetDescriptor(ParticleFieldId field_id, const ParticleFieldDescriptor **descriptor)
Return immutable metadata for a valid particle field ID.
PetscErrorCode ParticleFieldTryIdFromName(const char *field_name, ParticleFieldId *field_id, PetscBool *found)
Look a name up without treating an unknown name as an error.
Immutable metadata for one persistent particle field.
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:

◆ ComputeSpecificKE()

PetscErrorCode ComputeSpecificKE ( UserCtx *  user,
const char *  velocity_field,
const char *  ske_field 
)

Computes the specific kinetic energy (KE per unit mass) for each particle.

This kernel calculates SKE = 0.5 * |velocity|^2. It requires that the velocity field exists and will populate the specific kinetic energy field. The output field must be registered before this kernel is called.

Parameters
userThe UserCtx containing the DMSwarm.
velocity_fieldThe name of the input vector field for particle velocity.
ske_fieldThe name of the output scalar field to store specific KE.
Returns
PetscErrorCode

Computes the specific kinetic energy (KE per unit mass) for each particle.

Local to this translation unit.

Definition at line 588 of file postprocessing_kernels.c.

589{
590 PetscErrorCode ierr;
591 PetscInt n_local;
592 const PetscScalar (*vel_arr)[3]; // Access velocity as array of 3-component vectors
593 PetscScalar *ske_arr;
594
595 PetscFunctionBeginUser;
597 LOG_ALLOW(GLOBAL, LOG_INFO, "-> KERNEL: Running ComputeSpecificKE ('%s' -> '%s').\n", velocity_field, ske_field);
598
599 // Get local data arrays from the DMSwarm
600 ierr = DMSwarmGetLocalSize(user->swarm, &n_local); CHKERRQ(ierr);
601 if (n_local == 0) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
602
603 // Get read-only access to velocity and write access to the output field
604 ierr = DMSwarmGetField(user->swarm, velocity_field, NULL, NULL, (void**)&vel_arr); CHKERRQ(ierr);
605 ierr = DMSwarmGetField(user->post_swarm, ske_field, NULL, NULL, (void**)&ske_arr); CHKERRQ(ierr);
606
607 // Main computation loop
608 for (PetscInt p = 0; p < n_local; p++) {
609 const PetscScalar u = vel_arr[p][0];
610 const PetscScalar v = vel_arr[p][1];
611 const PetscScalar w = vel_arr[p][2];
612 const PetscScalar vel_sq = u*u + v*v + w*w;
613 ske_arr[p] = 0.5 * vel_sq;
614 }
615
616 // Restore arrays
617 ierr = DMSwarmRestoreField(user->swarm, velocity_field, NULL, NULL, (void**)&vel_arr); CHKERRQ(ierr);
618 ierr = DMSwarmRestoreField(user->post_swarm, ske_field, NULL, NULL, (void**)&ske_arr); CHKERRQ(ierr);
619
621 PetscFunctionReturn(0);
622}
DM post_swarm
Definition variables.h:1175
Here is the caller graph for this function:

◆ ComputeDisplacement()

PetscErrorCode ComputeDisplacement ( UserCtx *  user,
const char *  disp_field 
)

Computes the displacement magnitude |r_i - r_0| for each particle (per-particle VTK kernel).

Reference point r_0 = (simCtx->psrc_x, psrc_y, psrc_z). Writes the scalar displacement to post_swarm[disp_field]. This is a visualisation kernel only — use ComputeParticleMSD from particle_statistics.h for quantitative global statistics.

Parameters
userThe UserCtx containing the DMSwarms.
disp_fieldName of the output scalar field in post_swarm.
Returns
PetscErrorCode

Computes the displacement magnitude |r_i - r_0| for each particle (per-particle VTK kernel).

Local to this translation unit.

Definition at line 630 of file postprocessing_kernels.c.

631{
632 PetscErrorCode ierr;
633 PetscInt n_local;
634 const PetscReal (*pos_arr)[3];
635 PetscScalar *disp_out;
636 SimCtx *simCtx = user->simCtx;
637
638 PetscFunctionBeginUser;
640 LOG_ALLOW(GLOBAL, LOG_INFO, "-> KERNEL: Running ComputeDisplacement (-> '%s').\n", disp_field);
641
642 ierr = DMSwarmGetLocalSize(user->swarm, &n_local); CHKERRQ(ierr);
643 if (n_local == 0) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
644
645 const PetscReal x0 = simCtx->psrc_x;
646 const PetscReal y0 = simCtx->psrc_y;
647 const PetscReal z0 = simCtx->psrc_z;
648
649 ierr = DMSwarmGetField(user->swarm, ParticleFieldName(PARTICLE_FIELD_ID_POSITION), NULL, NULL, (void**)&pos_arr); CHKERRQ(ierr);
650 ierr = DMSwarmGetField(user->post_swarm, disp_field, NULL, NULL, (void**)&disp_out); CHKERRQ(ierr);
651
652 for (PetscInt p = 0; p < n_local; p++) {
653 const PetscReal dx = pos_arr[p][0] - x0;
654 const PetscReal dy = pos_arr[p][1] - y0;
655 const PetscReal dz = pos_arr[p][2] - z0;
656 disp_out[p] = PetscSqrtReal(dx*dx + dy*dy + dz*dz);
657 }
658
659 ierr = DMSwarmRestoreField(user->swarm, ParticleFieldName(PARTICLE_FIELD_ID_POSITION), NULL, NULL, (void**)&pos_arr); CHKERRQ(ierr);
660 ierr = DMSwarmRestoreField(user->post_swarm, disp_field, NULL, NULL, (void**)&disp_out); CHKERRQ(ierr);
661
663 PetscFunctionReturn(0);
664}
const char * ParticleFieldName(ParticleFieldId field_id)
Return the canonical PETSc DMSwarm name for an ID.
@ PARTICLE_FIELD_ID_POSITION
PetscReal psrc_x
Definition variables.h:945
PetscReal psrc_z
Point source location for PARTICLE_INIT_POINT_SOURCE.
Definition variables.h:945
PetscReal psrc_y
Definition variables.h:945
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ComputeWindowStatisticNodal()

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.

The counterpart of ComputeNodalAverage() for accumulated window state: it takes an index into a window's requested output set, normalizes the centered state behind it, and leaves the result in the shared post-processing staging field ready for output. The staging vectors are reused between calls, so a caller must consume the result before requesting the next one.

With global_operations.dimensionalize set, the result carries physical units: the source field's reference scale raised to the power the derived kind carries: a mean and an RMS are linear in it, a Reynolds stress and a turbulent kinetic energy quadratic. A co-moment flux relates two possibly different fields, so its factor is the product of their scales rather than one squared. That is what the per-field scaling table alone cannot express, and why one blanket velocity factor would have been wrong for three of the five kinds.

Parameters
[in]userBlock context holding the accumulators and staging fields.
[in]window_indexWindow whose state is derived.
[in]outputsComma-separated output kinds the recipe requested.
[in]output_indexIndex into that output set.
[out]out_nameName of the derived field, window qualified.
[in]name_sizeCapacity of out_name.
[out]out_vecNodal vector holding the result; borrowed, not owned.
[out]out_componentsComponent count of the result.
Returns
Zero on success, or a PETSc error.

Derives one accumulated statistic and converts it to nodal values.

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

See also
ComputeWindowStatisticNodal()

Definition at line 212 of file postprocessing_kernels.c.

216{
217 PetscErrorCode ierr;
218 SimCtx *simCtx = NULL;
219 PicurvDerivedField derived;
220 const PicurvWindowDefinition *definition = NULL;
221
222 PetscFunctionBeginUser;
224 PetscCheck(user != NULL && out_name != NULL && out_vec != NULL && out_components != NULL,
225 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Context and outputs are required.");
226 simCtx = user->simCtx;
227 PetscCheck(FieldStatisticsIsActive(simCtx) && user->fieldStatisticsStorage != NULL,
228 PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
229 "No accumulated window state exists to derive.");
230 definition = &simCtx->fieldStatisticsWindows[window_index].definition;
231
232 /* Derive into the cell-centred staging pair, then reach the nodal path by
233 * catalogued name. A per-window accumulator has no compile-time offset, so
234 * staging is what lets the shared ghost and nodal kernels address it at all. */
235 ierr = PicurvWindowDerive(user, definition, &user->fieldStatisticsStorage[window_index],
236 outputs, output_index, user->PostScalar, user->PostVector,
237 &derived); CHKERRQ(ierr);
238 /* The derivation writes the physical interior only, so the layout boundary would
239 * otherwise reach the nodal average as structural zeros. On periodic axes the
240 * shared cell synchronizer fills the dummy planes from the wrapped physical
241 * planes; the ghost update then carries the written values outward. */
242 if (derived.components == 1) {
243 const FieldId staged[] = {FIELD_ID_POST_SCALAR};
244 ierr = SynchronizePeriodicCellFields(user, 1, staged); CHKERRQ(ierr);
245 ierr = UpdateLocalGhosts(user, FIELD_ID_POST_SCALAR); CHKERRQ(ierr);
246 ierr = ComputeNodalAverage(user, "PostScalar", "PostScalarNodal"); CHKERRQ(ierr);
247 *out_vec = user->PostScalarNodal;
248 } else {
249 const FieldId staged[] = {FIELD_ID_POST_VECTOR};
250 ierr = SynchronizePeriodicCellFields(user, 1, staged); CHKERRQ(ierr);
251 ierr = UpdateLocalGhosts(user, FIELD_ID_POST_VECTOR); CHKERRQ(ierr);
252 ierr = ComputeNodalAverage(user, "PostVector", "PostVectorNodal"); CHKERRQ(ierr);
253 *out_vec = user->PostVectorNodal;
254 }
255 *out_components = derived.components;
256 ierr = PetscStrncpy(out_name, derived.name, name_size); CHKERRQ(ierr);
257
258 LOG_ALLOW(GLOBAL, LOG_DEBUG, "-> KERNEL: Derived '%s' (%d component(s)).\n",
259 out_name, (int)derived.components);
261 PetscFunctionReturn(0);
262}
@ FIELD_ID_POST_VECTOR
@ FIELD_ID_POST_SCALAR
PetscErrorCode ComputeNodalAverage(UserCtx *user, const char *in_field_name, const char *out_field_name)
Implementation of ComputeNodalAverage().
PetscInt components
One or three.
char name[96]
Output field name, window qualified.
PetscErrorCode PicurvWindowDerive(UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const char *outputs, PetscInt index, Vec scalar_target, Vec vector_target, PicurvDerivedField *field)
Derives one output field from centered accumulator state.
One derived output field, resolved by enumeration index.
PicurvWindowDefinition definition
PetscBool FieldStatisticsIsActive(const struct SimCtx *simCtx)
Reports whether this run has live field-statistics state.
The scientifically immutable definition of one window.
Vec PostScalar
Definition variables.h:1133
struct PicurvWindow * fieldStatisticsWindows
Definition variables.h:933
struct PicurvWindowStorage * fieldStatisticsStorage
Definition variables.h:1136
Vec PostVector
Definition variables.h:1134
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ComputeWindowStatisticsSummary()

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.

The counterpart of ComputeParticleMSD() for Eulerian window state: it reduces the window to the few numbers that answer whether it has run long enough — sample count, total weight, represented time, the per-point valid-fraction range, and the mean turbulent kinetic energy — and appends them as one row per processed step. No single field snapshot can answer that question, which is why the history exists beside the field output rather than instead of it.

The mean is taken over the fluid cells the window actually sampled. Cells outside the target domain, and cells a moving mask excluded, hold zeros that mean "never measured"; averaging over them would scale the result down by the fraction of the grid the window never covered.

Parameters
[in]userBlock context holding the accumulators and staging fields.
[in]window_indexWindow to summarize.
[in]output_prefixOutput path prefix; the window name and .csv are appended.
[in]tiStep being processed, recorded as the row's key.
Returns
Zero on success, or a PETSc error.

Appends one convergence row for an accumulated window to its CSV history.

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

See also
ComputeWindowStatisticsSummary()

Definition at line 272 of file postprocessing_kernels.c.

274{
275 PetscErrorCode ierr;
276 SimCtx *simCtx = NULL;
277 const PicurvWindow *window = NULL;
278 const PicurvWindowStorage *storage = NULL;
279 PetscReal lowest = 1.0, highest = 0.0;
280 PetscReal mean_tke = 0.0;
281 PetscBool has_tke = PETSC_FALSE;
282 PetscInt derived_count = 0;
283 char path[PETSC_MAX_PATH_LEN];
284
285 PetscFunctionBeginUser;
287 PetscCheck(user != NULL && output_prefix != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
288 "Context and output prefix are required.");
289 simCtx = user->simCtx;
290 PetscCheck(FieldStatisticsIsActive(simCtx) && user->fieldStatisticsStorage != NULL,
291 PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
292 "No accumulated window state exists to summarize.");
293 window = &simCtx->fieldStatisticsWindows[window_index];
294 storage = &user->fieldStatisticsStorage[window_index];
295
296 ierr = PicurvWindowValidFractionRange(user, &window->definition, storage,
297 window->sample_count, &lowest, &highest); CHKERRQ(ierr);
298
299 /* A single domain number is what makes the row plottable against step. It is
300 * produced by the same derivation the field output uses, so the scalar and the
301 * field can never disagree about what the window holds. */
302 ierr = PicurvWindowDerivedCount(&window->definition, storage, "tke", &derived_count); CHKERRQ(ierr);
303 if (derived_count > 0) {
304 PicurvDerivedField derived;
305
306 ierr = PicurvWindowDerive(user, &window->definition, storage, "tke", 0,
307 user->PostScalar, user->PostVector, &derived); CHKERRQ(ierr);
308 /* Averaged over the fluid cells the window actually sampled. A whole-vector
309 * mean would divide by boundary and dummy entries the derivation never
310 * writes, scaling the answer down by the fraction it never covered. */
311 ierr = PicurvWindowSpatialMean(user, &window->definition, storage,
312 user->PostScalar, &mean_tke); CHKERRQ(ierr);
313 has_tke = PETSC_TRUE;
314 }
315
316 /* The window's clock is solver time. A dimensionalized summary reports its derived
317 * statistics physically, so the times beside them are reported in seconds too; a
318 * time-weighted total weight is a time, a sample-weighted one is a count. */
319 PetscReal time_scale = 1.0;
320 if (simCtx->pps && simCtx->pps->dimensionalize) {
321 const FieldDimension time_dimension = FIELD_DIM_TIME;
322
323 ierr = FieldDimensionReferenceScale(&simCtx->scaling, time_dimension, &time_scale); CHKERRQ(ierr);
324 }
325 const PetscReal weight_scale =
326 (window->definition.weighting == PICURV_WEIGHTING_PHYSICAL_TIME) ? time_scale : 1.0;
327
328 if (simCtx->rank == 0) {
329 FILE *csv = NULL;
330 PetscBool exists = PETSC_FALSE;
331
332 ierr = PetscSNPrintf(path, sizeof(path), "%s_statistics_%s.csv",
333 output_prefix, window->definition.name); CHKERRQ(ierr);
334 ierr = PetscTestFile(path, 'r', &exists); CHKERRQ(ierr);
335 csv = fopen(path, exists ? "a" : "w");
336 PetscCheck(csv != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
337 "Unable to open statistics summary '%s'.", path);
338 if (!exists) {
339 fprintf(csv, "step,state,samples,total_weight,represented_time,"
340 "valid_fraction_min,valid_fraction_max,mean_tke\n");
341 }
342 fprintf(csv, "%" PetscInt_FMT ",%s,%d,%.10e,%.10e,%.6f,%.6f,",
343 ti, PicurvWindowStateName(window->state), window->sample_count,
344 (double)(window->total_weight * weight_scale),
345 (double)(window->represented_time * time_scale),
346 (double)lowest, (double)highest);
347 if (has_tke) fprintf(csv, "%.10e\n", (double)mean_tke);
348 else fprintf(csv, "\n");
349 PetscCheck(fclose(csv) == 0, PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
350 "Unable to close statistics summary '%s'.", path);
351 }
352 LOG_ALLOW(GLOBAL, LOG_DEBUG, "-> KERNEL: Summarized window '%s' at step %" PetscInt_FMT ".\n",
353 window->definition.name, ti);
355 PetscFunctionReturn(0);
356}
PetscErrorCode FieldDimensionReferenceScale(const ScalingCtx *scaling, FieldDimension dimension, PetscReal *scale)
Return the factor that turns a solver value of one dimension into physical units.
#define FIELD_DIM_TIME
Physical dimension as exponents of the reference length, velocity, and density.
PetscErrorCode PicurvWindowDerivedCount(const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const char *outputs, PetscInt *count)
Reports how many derived fields a requested output set produces.
PetscErrorCode PicurvWindowValidFractionRange(UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, PetscInt sample_count, PetscReal *minimum, PetscReal *maximum)
Reports the range of per-point valid fraction across a window's domain.
PetscErrorCode PicurvWindowSpatialMean(UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, Vec field, PetscReal *mean)
Reports the spatial mean of a derived field over the points a window sampled.
Independent accumulator state for one window on one block.
PetscInt sample_count
PicurvWindowState state
PetscReal total_weight
const char * PicurvWindowStateName(PicurvWindowState state)
Returns a stable human-readable name for a window state.
@ PICURV_WEIGHTING_PHYSICAL_TIME
Weight is the represented interval.
PetscReal represented_time
Physical time the window covers.
Runtime state of one window.
PetscMPIInt rank
Definition variables.h:862
ScalingCtx scaling
Definition variables.h:946
Here is the call graph for this function:
Here is the caller graph for this function: