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

Per-window PETSc accumulator storage and pointwise application. More...

#include "statistics_accumulator.h"
#include "statistics_moments.h"
#include "statistics_target.h"
#include "field_catalog.h"
#include "io.h"
#include "logging.h"
Include dependency graph for statistics_accumulator.c:

Go to the source code of this file.

Macros

#define STATISTICS_DERIVED_OUTPUT_LENGTH   256
 Longest output list a recipe may request.
 

Enumerations

enum  DerivedKind {
  DERIVED_MEAN = 0 , DERIVED_REYNOLDS_STRESS , DERIVED_RMS , DERIVED_TKE ,
  DERIVED_FLUX , DERIVED_KIND_COUNT
}
 Output kinds a postprocessing recipe may request, in enumeration order. More...
 

Functions

static PetscInt ProductDiagonalIndex (PetscInt component)
 Internal helper: the product index carrying one component's own variance.
 
static PetscErrorCode ProductComponentLabel (PetscInt index, char *out, size_t size)
 Internal helper: writes the two-axis label of one product component.
 
PetscErrorCode PicurvProductComponentCount (PetscInt dof, PetscInt *count)
 Implementation of PicurvProductComponentCount().
 
PetscErrorCode PicurvCovarianceComponentCount (PetscInt dof_a, PetscInt dof_b, PetscInt *count)
 Implementation of PicurvCovarianceComponentCount().
 
PetscErrorCode PicurvStatisticsComponentDM (UserCtx *user, PetscInt components, DM *dm)
 Implementation of PicurvStatisticsComponentDM().
 
PetscErrorCode PicurvWindowStorageCreate (UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage)
 Implementation of PicurvWindowStorageCreate().
 
PetscErrorCode PicurvWindowStorageDestroy (PicurvWindowStorage *storage)
 Implementation of PicurvWindowStorageDestroy().
 
static PetscErrorCode FindFieldSlot (const PicurvWindowDefinition *definition, PetscInt field_id, PetscInt *slot)
 Internal helper: locates the storage slot holding one field's running mean.
 
PetscErrorCode PicurvWindowStoragePayloadCount (const PicurvWindowStorage *storage, PetscInt *count)
 Implementation of PicurvWindowStoragePayloadCount().
 
PetscErrorCode PicurvWindowStoragePayload (UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, PetscInt index, PicurvStatisticsPayload *payload)
 Implementation of PicurvWindowStoragePayload().
 
static PetscErrorCode ParseDerivedKinds (const char *outputs, PetscBool wanted[DERIVED_KIND_COUNT])
 Internal helper: reports which output kinds a recipe requested.
 
static PetscErrorCode DerivedKindExtent (const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, DerivedKind kind, PetscInt *count)
 Internal helper: counts the derived fields each kind contributes.
 
PetscErrorCode PicurvWindowDerivedCount (const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const char *outputs, PetscInt *count)
 Implementation of PicurvWindowDerivedCount().
 
static PetscErrorCode SafeStandardDeviation (PetscReal variance, const char *label, PetscReal *result)
 Internal helper: takes a square root of a variance that may be barely negative.
 
static PetscErrorCode ResolveDerivedIndex (const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const PetscBool wanted[DERIVED_KIND_COUNT], PetscInt index, DerivedKind *kind, PetscInt *offset)
 Internal helper: resolves which kind and member one derived index selects.
 
PetscErrorCode PicurvWindowDerive (UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const char *outputs, PetscInt index, Vec scalar_target, Vec vector_target, PicurvDerivedField *field)
 Implementation of PicurvWindowDerive().
 
PetscErrorCode PicurvWindowSpatialMean (UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, Vec field, PetscReal *mean)
 Implementation of PicurvWindowSpatialMean().
 
PetscErrorCode PicurvWindowValidFractionRange (UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, PetscInt sample_count, PetscReal *minimum, PetscReal *maximum)
 Implementation of PicurvWindowValidFractionRange().
 
PetscErrorCode PicurvWindowAccumulate (UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage, PetscReal weight)
 Implementation of PicurvWindowAccumulate().
 

Variables

static const PetscInt kProductFirst [6] = {0, 0, 0, 1, 1, 2}
 Upper-triangular row-major component pairs for a three-vector self-product.
 
static const PetscInt kProductSecond [6] = {0, 1, 2, 1, 2, 2}
 
static const char *const kAxisName [3] = {"x", "y", "z"}
 Axis labels indexing the pair table above.
 
static const char *const kDerivedKindName [DERIVED_KIND_COUNT]
 Recipe spellings of the output kinds.
 

Detailed Description

Per-window PETSc accumulator storage and pointwise application.

Full API contract is documented with the declarations in include/statistics_accumulator.h.

Definition in file statistics_accumulator.c.

Macro Definition Documentation

◆ STATISTICS_DERIVED_OUTPUT_LENGTH

#define STATISTICS_DERIVED_OUTPUT_LENGTH   256

Longest output list a recipe may request.

Definition at line 17 of file statistics_accumulator.c.

Enumeration Type Documentation

◆ DerivedKind

Output kinds a postprocessing recipe may request, in enumeration order.

Enumerator
DERIVED_MEAN 
DERIVED_REYNOLDS_STRESS 
DERIVED_RMS 
DERIVED_TKE 
DERIVED_FLUX 
DERIVED_KIND_COUNT 

Definition at line 360 of file statistics_accumulator.c.

360 {
361 DERIVED_MEAN = 0,
DerivedKind
Output kinds a postprocessing recipe may request, in enumeration order.
@ DERIVED_REYNOLDS_STRESS
@ DERIVED_KIND_COUNT

Function Documentation

◆ ProductDiagonalIndex()

static PetscInt ProductDiagonalIndex ( PetscInt  component)
static

Internal helper: the product index carrying one component's own variance.

Local to this translation unit. Found by searching the pair table for the entry pairing a component with itself, so the diagonal cannot be stated separately from the order it belongs to.

Definition at line 40 of file statistics_accumulator.c.

41{
42 for (PetscInt c = 0; c < 6; ++c) {
43 if (kProductFirst[c] == component && kProductSecond[c] == component) return c;
44 }
45 return -1;
46}
static const PetscInt kProductFirst[6]
Upper-triangular row-major component pairs for a three-vector self-product.
static const PetscInt kProductSecond[6]
Here is the caller graph for this function:

◆ ProductComponentLabel()

static PetscErrorCode ProductComponentLabel ( PetscInt  index,
char *  out,
size_t  size 
)
static

Internal helper: writes the two-axis label of one product component.

Local to this translation unit. Built from the pair table, so "xy" and the slot it names can never refer to different pairs.

Definition at line 53 of file statistics_accumulator.c.

54{
55 PetscFunctionBeginUser;
56 PetscCheck(index >= 0 && index < 6, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
57 "Product component %" PetscInt_FMT " is outside the symmetric set.", index);
58 PetscCall(PetscSNPrintf(out, size, "%s%s", kAxisName[kProductFirst[index]],
59 kAxisName[kProductSecond[index]]));
60 PetscFunctionReturn(0);
61}
static const char *const kAxisName[3]
Axis labels indexing the pair table above.
Here is the caller graph for this function:

◆ PicurvProductComponentCount()

PetscErrorCode PicurvProductComponentCount ( PetscInt  dof,
PetscInt *  count 
)

Implementation of PicurvProductComponentCount().

Reports how many symmetric product components a field's second moment needs.

See also
PicurvProductComponentCount()

Definition at line 67 of file statistics_accumulator.c.

68{
69 PetscFunctionBeginUser;
70 PetscCheck(count != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Count output is required.");
71 PetscCheck(dof == 1 || dof == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
72 "Self-products are supported for scalar and three-vector fields, got dof %" PetscInt_FMT ".", dof);
73 *count = (dof == 1) ? 1 : 6;
74 PetscFunctionReturn(0);
75}
Here is the caller graph for this function:

◆ PicurvCovarianceComponentCount()

PetscErrorCode PicurvCovarianceComponentCount ( PetscInt  dof_a,
PetscInt  dof_b,
PetscInt *  count 
)

Implementation of PicurvCovarianceComponentCount().

Reports how many components a covariance between two fields needs.

See also
PicurvCovarianceComponentCount()

Definition at line 81 of file statistics_accumulator.c.

82{
83 PetscFunctionBeginUser;
84 PetscCheck(count != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Count output is required.");
85 /* Phase 2 accepts scalar-scalar and vector-scalar pairs; vector-vector cross
86 * products are an explicit non-goal. */
87 PetscCheck((dof_a == 1 && dof_b == 1) || (dof_a == 3 && dof_b == 1) || (dof_a == 1 && dof_b == 3),
88 PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
89 "Covariance is supported for scalar-scalar and vector-scalar pairs, got dof %" PetscInt_FMT
90 " and %" PetscInt_FMT ".", dof_a, dof_b);
91 *count = (dof_a == 3 || dof_b == 3) ? 3 : 1;
92 PetscFunctionReturn(0);
93}
Here is the caller graph for this function:

◆ PicurvStatisticsComponentDM()

PetscErrorCode PicurvStatisticsComponentDM ( UserCtx user,
PetscInt  components,
DM *  dm 
)

Implementation of PicurvStatisticsComponentDM().

Resolves the DM carrying a given number of accumulator components.

See also
PicurvStatisticsComponentDM()

Definition at line 99 of file statistics_accumulator.c.

100{
101 PetscFunctionBeginUser;
102 PetscCheck(user != NULL && dm != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
103 "Context and DM output are required.");
104 switch (components) {
105 case 1: *dm = user->da; break;
106 case 3: *dm = user->fda; break;
107 case 6: *dm = user->fda6; break;
108 default:
109 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
110 "No block DM carries %" PetscInt_FMT " accumulator components.", components);
111 }
112 PetscCheck(*dm != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
113 "The block DM for %" PetscInt_FMT " accumulator components was never created.",
114 components);
115 PetscFunctionReturn(0);
116}
Here is the caller graph for this function:

◆ PicurvWindowStorageCreate()

PetscErrorCode PicurvWindowStorageCreate ( UserCtx user,
const PicurvWindowDefinition definition,
PicurvWindowStorage storage 
)

Implementation of PicurvWindowStorageCreate().

Allocates the accumulator state one window owns on one block.

See also
PicurvWindowStorageCreate()

Definition at line 122 of file statistics_accumulator.c.

124{
125 PetscFunctionBeginUser;
126 PetscCheck(user != NULL && definition != NULL && storage != NULL,
127 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Context, definition, and storage are required.");
128 PetscCall(PetscMemzero(storage, sizeof(*storage)));
129 storage->field_count = definition->field_count;
130 storage->covariance_count = definition->covariance_count;
131
132 /* Occupancy is per point and scalar regardless of what is accumulated. */
133 PetscCall(DMCreateGlobalVector(user->da, &storage->count));
134 PetscCall(VecSet(storage->count, 0.0));
135 PetscCall(DMCreateGlobalVector(user->da, &storage->weight));
136 PetscCall(VecSet(storage->weight, 0.0));
137 PetscCall(DMCreateGlobalVector(user->da, &storage->weight_sq));
138 PetscCall(VecSet(storage->weight_sq, 0.0));
139
140 if (definition->field_count > 0) {
141 PetscCall(PetscCalloc1((size_t)definition->field_count, &storage->mean));
142 PetscCall(PetscCalloc1((size_t)definition->field_count, &storage->m2));
143 }
144 for (PetscInt field_index = 0; field_index < definition->field_count; ++field_index) {
145 FieldView view;
146 PetscInt components = 0;
147 DM product_dm = NULL;
148
149 PetscCall(FieldGetView(user, (FieldId)definition->fields[field_index].field_id, &view));
150 /* The mean matches the source field's own layout, so it comes from that
151 * field's DM and inherits its decomposition. */
152 PetscCall(DMCreateGlobalVector(view.dm, &storage->mean[field_index]));
153 PetscCall(VecSet(storage->mean[field_index], 0.0));
154 if (!definition->fields[field_index].want_second) continue;
155 PetscCall(PicurvProductComponentCount(view.descriptor->dof, &components));
156 PetscCall(PicurvStatisticsComponentDM(user, components, &product_dm));
157 PetscCall(DMCreateGlobalVector(product_dm, &storage->m2[field_index]));
158 PetscCall(VecSet(storage->m2[field_index], 0.0));
159 }
160
161 if (definition->covariance_count > 0) {
162 PetscCall(PetscCalloc1((size_t)definition->covariance_count, &storage->cm));
163 }
164 for (PetscInt pair_index = 0; pair_index < definition->covariance_count; ++pair_index) {
165 FieldView view_a, view_b;
166 PetscInt components = 0;
167 DM pair_dm = NULL;
168
169 PetscCall(FieldGetView(user, (FieldId)definition->covariances[pair_index].first, &view_a));
170 PetscCall(FieldGetView(user, (FieldId)definition->covariances[pair_index].second, &view_b));
171 PetscCall(PicurvCovarianceComponentCount(view_a.descriptor->dof, view_b.descriptor->dof, &components));
172 PetscCall(PicurvStatisticsComponentDM(user, components, &pair_dm));
173 PetscCall(DMCreateGlobalVector(pair_dm, &storage->cm[pair_index]));
174 PetscCall(VecSet(storage->cm[pair_index], 0.0));
175 }
176
177 /* Report what this window costs and what it covers, once per block at setup.
178 * The point count is a function of layout, dimensions, and periodicity alone, so
179 * it tells an operator what "per point" will mean before any sample is taken. */
180 {
182 PetscInt payloads = 0;
183 PetscInt points = 0;
184
185 PetscCall(PicurvWindowStoragePayloadCount(storage, &payloads));
186 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
188 PetscCall(SpatialTargetPlanGlobalPointCount(&plan, PETSC_COMM_WORLD, &points));
190 "Statistics window '%s' block %d: %d accumulator vector(s) over %d point(s).\n",
191 definition->name, (int)user->_this, (int)payloads, (int)points);
192 }
193 PetscFunctionReturn(0);
194}
const FieldDescriptor * descriptor
PetscErrorCode FieldGetView(UserCtx *user, FieldId field_id, FieldView *view)
Resolve the existing DM and global/local vectors for one field.
FieldId
Compile-time identity for a catalogued Eulerian field.
Non-owning runtime objects resolved for one field and UserCtx.
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:45
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
PetscErrorCode PicurvProductComponentCount(PetscInt dof, PetscInt *count)
Implementation of PicurvProductComponentCount().
PetscErrorCode PicurvCovarianceComponentCount(PetscInt dof_a, PetscInt dof_b, PetscInt *count)
Implementation of PicurvCovarianceComponentCount().
PetscErrorCode PicurvStatisticsComponentDM(UserCtx *user, PetscInt components, DM *dm)
Implementation of PicurvStatisticsComponentDM().
PetscErrorCode PicurvWindowStoragePayloadCount(const PicurvWindowStorage *storage, PetscInt *count)
Implementation of PicurvWindowStoragePayloadCount().
Vec weight
Per-point valid weight.
Vec weight_sq
Per-point squared-weight sum.
PetscInt field_count
Fields accumulated.
PetscInt covariance_count
Covariance pairs accumulated.
Vec * mean
One per field, matching that field's layout.
Vec * m2
One per field; NULL when no second moment was requested.
Vec count
Per-point accepted sample count.
Vec * cm
One per covariance pair.
@ PICURV_STATISTICS_MASK_FLUID
PetscErrorCode SpatialTargetPlanGlobalPointCount(const SpatialTargetPlan *plan, MPI_Comm comm, PetscInt *count)
Counts the points contributed across a communicator.
PetscErrorCode SpatialTargetPlanCreate(UserCtx *user, FieldId field_id, PicurvStatisticsMask mask, SpatialTargetPlan *plan)
Resolves the iteration domain for one field on one block.
Resolved iteration domain for one field on one block.
PetscInt first
First member; must also appear in the field list.
PicurvWindowFieldRequest fields[16]
PetscBool want_second
Also keep the centered second moment.
PetscInt second
Second member; must also appear in the field list.
PicurvWindowCovarianceRequest covariances[16]
PetscInt field_id
Catalogued Eulerian field identity.
PetscInt _this
Definition variables.h:914
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvWindowStorageDestroy()

PetscErrorCode PicurvWindowStorageDestroy ( PicurvWindowStorage storage)

Implementation of PicurvWindowStorageDestroy().

Releases accumulator state previously created for one window.

See also
PicurvWindowStorageDestroy()

Definition at line 200 of file statistics_accumulator.c.

201{
202 PetscFunctionBeginUser;
203 if (storage == NULL) PetscFunctionReturn(0);
204 if (storage->count) PetscCall(VecDestroy(&storage->count));
205 if (storage->weight) PetscCall(VecDestroy(&storage->weight));
206 if (storage->weight_sq) PetscCall(VecDestroy(&storage->weight_sq));
207 for (PetscInt field_index = 0; storage->mean && field_index < storage->field_count; ++field_index) {
208 if (storage->mean[field_index]) PetscCall(VecDestroy(&storage->mean[field_index]));
209 }
210 for (PetscInt field_index = 0; storage->m2 && field_index < storage->field_count; ++field_index) {
211 if (storage->m2[field_index]) PetscCall(VecDestroy(&storage->m2[field_index]));
212 }
213 for (PetscInt pair_index = 0; storage->cm && pair_index < storage->covariance_count; ++pair_index) {
214 if (storage->cm[pair_index]) PetscCall(VecDestroy(&storage->cm[pair_index]));
215 }
216 if (storage->mean) PetscCall(PetscFree(storage->mean));
217 if (storage->m2) PetscCall(PetscFree(storage->m2));
218 if (storage->cm) PetscCall(PetscFree(storage->cm));
219 PetscCall(PetscMemzero(storage, sizeof(*storage)));
220 PetscFunctionReturn(0);
221}
Here is the caller graph for this function:

◆ FindFieldSlot()

static PetscErrorCode FindFieldSlot ( const PicurvWindowDefinition definition,
PetscInt  field_id,
PetscInt *  slot 
)
static

Internal helper: locates the storage slot holding one field's running mean.

Local to this translation unit. A covariance member must also appear in the window's field list, because the co-moment update needs that field's running mean; this is where that requirement is enforced.

Definition at line 229 of file statistics_accumulator.c.

231{
232 PetscFunctionBeginUser;
233 for (PetscInt field_index = 0; field_index < definition->field_count; ++field_index) {
234 if (definition->fields[field_index].field_id == field_id) {
235 *slot = field_index;
236 PetscFunctionReturn(0);
237 }
238 }
239 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
240 "Window '%s' requests a covariance over field '%s', which is not in its field list.",
241 definition->name, FieldCanonicalName((FieldId)field_id));
242}
const char * FieldCanonicalName(FieldId field_id)
Return the canonical printable name for an ID.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvWindowStoragePayloadCount()

PetscErrorCode PicurvWindowStoragePayloadCount ( const PicurvWindowStorage storage,
PetscInt *  count 
)

Implementation of PicurvWindowStoragePayloadCount().

Reports how many checkpointable vectors one window's storage holds.

See also
PicurvWindowStoragePayloadCount()

Definition at line 248 of file statistics_accumulator.c.

249{
250 PetscFunctionBeginUser;
251 PetscCheck(storage != NULL && count != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
252 "Storage and count output are required.");
253 *count = 3 + storage->field_count + storage->covariance_count;
254 /* A field without a requested second moment holds no product vector, so the
255 * enumeration skips it rather than emitting an empty payload. */
256 for (PetscInt field_index = 0; storage->m2 && field_index < storage->field_count; ++field_index) {
257 if (storage->m2[field_index]) *count += 1;
258 }
259 PetscFunctionReturn(0);
260}
Here is the caller graph for this function:

◆ PicurvWindowStoragePayload()

PetscErrorCode PicurvWindowStoragePayload ( UserCtx user,
const PicurvWindowDefinition definition,
const PicurvWindowStorage storage,
PetscInt  index,
PicurvStatisticsPayload payload 
)

Implementation of PicurvWindowStoragePayload().

Resolves one enumerated payload of a window's storage.

See also
PicurvWindowStoragePayload()

Definition at line 266 of file statistics_accumulator.c.

269{
270 PetscInt total = 0;
271 PetscInt cursor = index;
272
273 PetscFunctionBeginUser;
274 PetscCheck(user != NULL && definition != NULL && storage != NULL && payload != NULL,
275 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
276 "Context, definition, storage, and payload output are required.");
277 PetscCall(PicurvWindowStoragePayloadCount(storage, &total));
278 PetscCheck(index >= 0 && index < total, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
279 "Payload index %" PetscInt_FMT " is outside [0, %" PetscInt_FMT ").", index, total);
280 PetscCall(PetscMemzero(payload, sizeof(*payload)));
281
282 /* Occupancy first, then means, then products, then co-moments. The order is the
283 * persistence contract: the manifest inventory, the writer, and the reader all
284 * walk it identically. */
285 if (cursor < 3) {
286 static const char *const occupancy_name[3] = {"count", "weight", "weight_sq"};
287 Vec occupancy[3];
288
289 occupancy[0] = storage->count;
290 occupancy[1] = storage->weight;
291 occupancy[2] = storage->weight_sq;
292 PetscCall(PetscStrncpy(payload->name, occupancy_name[cursor], sizeof(payload->name)));
293 payload->vec = occupancy[cursor];
294 payload->components = 1;
295 payload->role = "occupancy";
297 PetscFunctionReturn(0);
298 }
299 cursor -= 3;
300
301 if (cursor < storage->field_count) {
302 const FieldDescriptor *descriptor = NULL;
303
304 PetscCall(FieldGetDescriptor((FieldId)definition->fields[cursor].field_id, &descriptor));
305 PetscCall(PetscSNPrintf(payload->name, sizeof(payload->name), "%s_mean",
306 descriptor->canonical_name));
307 payload->vec = storage->mean[cursor];
308 payload->components = descriptor->dof;
309 payload->role = "mean";
310 payload->layout = FieldLayoutName(descriptor->layout);
311 PetscFunctionReturn(0);
312 }
313 cursor -= storage->field_count;
314
315 {
316 PetscInt product_count = 0;
317 PetscInt seen = 0;
318
319 for (PetscInt field_index = 0; storage->m2 && field_index < storage->field_count; ++field_index) {
320 if (storage->m2[field_index]) ++product_count;
321 }
322 if (cursor < product_count) {
323 for (PetscInt field_index = 0; field_index < storage->field_count; ++field_index) {
324 const FieldDescriptor *descriptor = NULL;
325
326 if (!storage->m2[field_index]) continue;
327 if (seen++ != cursor) continue;
328 PetscCall(FieldGetDescriptor((FieldId)definition->fields[field_index].field_id, &descriptor));
329 PetscCall(PetscSNPrintf(payload->name, sizeof(payload->name), "%s_m2",
330 descriptor->canonical_name));
331 payload->vec = storage->m2[field_index];
332 PetscCall(PicurvProductComponentCount(descriptor->dof, &payload->components));
333 payload->role = "second_moment";
334 payload->layout = FieldLayoutName(descriptor->layout);
335 PetscFunctionReturn(0);
336 }
337 }
338 cursor -= product_count;
339 }
340
341 {
342 const FieldDescriptor *first = NULL;
343 const FieldDescriptor *second = NULL;
344
345 PetscCheck(cursor >= 0 && cursor < storage->covariance_count, PETSC_COMM_SELF, PETSC_ERR_PLIB,
346 "Payload index %" PetscInt_FMT " fell through the storage enumeration.", index);
347 PetscCall(FieldGetDescriptor((FieldId)definition->covariances[cursor].first, &first));
348 PetscCall(FieldGetDescriptor((FieldId)definition->covariances[cursor].second, &second));
349 PetscCall(PetscSNPrintf(payload->name, sizeof(payload->name), "%s_%s_cm",
350 first->canonical_name, second->canonical_name));
351 payload->vec = storage->cm[cursor];
352 PetscCall(PicurvCovarianceComponentCount(first->dof, second->dof, &payload->components));
353 payload->role = "co_moment";
354 payload->layout = FieldLayoutName(first->layout);
355 }
356 PetscFunctionReturn(0);
357}
FieldLayout layout
@ FIELD_LAYOUT_CELL_CENTERED
const char * canonical_name
const char * FieldLayoutName(FieldLayout layout)
Return a stable printable label for a field layout.
PetscErrorCode FieldGetDescriptor(FieldId field_id, const FieldDescriptor **descriptor)
Return immutable metadata for a valid field identifier.
Immutable metadata for one field identity.
PetscInt components
Degrees of freedom the vector carries.
Vec vec
Borrowed accumulator vector; never owned by the caller.
const char * role
Inventory role: occupancy, mean, second_moment, co_moment.
char name[96]
File basename, no extension.
const char * layout
Catalog layout name for the inventory entry.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseDerivedKinds()

static PetscErrorCode ParseDerivedKinds ( const char *  outputs,
PetscBool  wanted[DERIVED_KIND_COUNT] 
)
static

Internal helper: reports which output kinds a recipe requested.

Local to this translation unit.

Definition at line 378 of file statistics_accumulator.c.

379{
381 char *cursor = buffer;
382
383 PetscFunctionBeginUser;
384 for (PetscInt k = 0; k < DERIVED_KIND_COUNT; ++k) wanted[k] = PETSC_FALSE;
385 PetscCheck(outputs != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output list is required.");
386 PetscCall(PetscStrncpy(buffer, outputs, sizeof(buffer)));
387
388 while (cursor && *cursor) {
389 char *comma = strchr(cursor, ',');
390 PetscBool matched = PETSC_FALSE;
391
392 if (comma) *comma = '\0';
393 TrimWhitespace(cursor);
394 if (cursor[0] != '\0') {
395 for (PetscInt k = 0; k < DERIVED_KIND_COUNT; ++k) {
396 if (strcmp(cursor, kDerivedKindName[k])) continue;
397 wanted[k] = PETSC_TRUE;
398 matched = PETSC_TRUE;
399 break;
400 }
401 PetscCheck(matched, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
402 "Unknown field-statistics output '%s'. Available outputs are "
403 "mean, reynolds_stress, rms, tke, and flux.", cursor);
404 }
405 cursor = comma ? comma + 1 : NULL;
406 }
407 PetscFunctionReturn(0);
408}
void TrimWhitespace(char *str)
Removes leading and trailing ASCII whitespace from a mutable string.
Definition io.c:399
static const char *const kDerivedKindName[DERIVED_KIND_COUNT]
Recipe spellings of the output kinds.
#define STATISTICS_DERIVED_OUTPUT_LENGTH
Longest output list a recipe may request.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ DerivedKindExtent()

static PetscErrorCode DerivedKindExtent ( const PicurvWindowDefinition definition,
const PicurvWindowStorage storage,
DerivedKind  kind,
PetscInt *  count 
)
static

Internal helper: counts the derived fields each kind contributes.

Local to this translation unit. A kind contributes nothing when the state it needs was never accumulated, so a recipe may name every output without having to know which window carries which moment.

Definition at line 416 of file statistics_accumulator.c.

419{
420 PetscFunctionBeginUser;
421 *count = 0;
422 switch (kind) {
423 case DERIVED_MEAN:
424 *count = storage->field_count;
425 break;
427 case DERIVED_RMS:
428 for (PetscInt field_index = 0; storage->m2 && field_index < storage->field_count; ++field_index) {
429 const FieldDescriptor *descriptor = NULL;
430 PetscInt components = 0;
431
432 if (!storage->m2[field_index]) continue;
433 PetscCall(FieldGetDescriptor((FieldId)definition->fields[field_index].field_id, &descriptor));
434 PetscCall(PicurvProductComponentCount(descriptor->dof, &components));
435 /* A stress tensor emits every component; an RMS emits only the
436 * diagonal, because an off-diagonal has no square root to take. */
437 *count += (kind == DERIVED_REYNOLDS_STRESS) ? components : descriptor->dof;
438 }
439 break;
440 case DERIVED_TKE:
441 /* Turbulent kinetic energy is the trace of a three-vector's stress tensor,
442 * so it exists only for a vector field carrying a second moment. */
443 for (PetscInt field_index = 0; storage->m2 && field_index < storage->field_count; ++field_index) {
444 const FieldDescriptor *descriptor = NULL;
445
446 if (!storage->m2[field_index]) continue;
447 PetscCall(FieldGetDescriptor((FieldId)definition->fields[field_index].field_id, &descriptor));
448 if (descriptor->dof == 3) *count += 1;
449 }
450 break;
451 default:
452 *count = storage->covariance_count;
453 break;
454 }
455 PetscFunctionReturn(0);
456}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvWindowDerivedCount()

PetscErrorCode PicurvWindowDerivedCount ( const PicurvWindowDefinition definition,
const PicurvWindowStorage storage,
const char *  outputs,
PetscInt *  count 
)

Implementation of PicurvWindowDerivedCount().

Reports how many derived fields a requested output set produces.

See also
PicurvWindowDerivedCount()

Definition at line 462 of file statistics_accumulator.c.

465{
466 PetscBool wanted[DERIVED_KIND_COUNT];
467
468 PetscFunctionBeginUser;
469 PetscCheck(definition != NULL && storage != NULL && count != NULL,
470 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Definition, storage, and count are required.");
471 PetscCall(ParseDerivedKinds(outputs, wanted));
472 *count = 0;
473 for (PetscInt k = 0; k < DERIVED_KIND_COUNT; ++k) {
474 PetscInt extent = 0;
475
476 if (!wanted[k]) continue;
477 PetscCall(DerivedKindExtent(definition, storage, (DerivedKind)k, &extent));
478 *count += extent;
479 }
480 PetscFunctionReturn(0);
481}
static PetscErrorCode ParseDerivedKinds(const char *outputs, PetscBool wanted[DERIVED_KIND_COUNT])
Internal helper: reports which output kinds a recipe requested.
static PetscErrorCode DerivedKindExtent(const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, DerivedKind kind, PetscInt *count)
Internal helper: counts the derived fields each kind contributes.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SafeStandardDeviation()

static PetscErrorCode SafeStandardDeviation ( PetscReal  variance,
const char *  label,
PetscReal *  result 
)
static

Internal helper: takes a square root of a variance that may be barely negative.

Local to this translation unit. Centered accumulation can leave a variance a few ulps below zero when the signal is nearly constant. Clamping is confined to this one place, applies only under a root, and never touches stored state; a genuinely negative variance is a defect and is reported.

Definition at line 490 of file statistics_accumulator.c.

491{
492 PetscFunctionBeginUser;
493 if (variance >= 0.0) {
494 *result = PetscSqrtReal(variance);
495 PetscFunctionReturn(0);
496 }
497 PetscCheck(variance >= -PICURV_STATISTICS_VARIANCE_FLOOR, PETSC_COMM_SELF, PETSC_ERR_FP,
498 "Derived variance for '%s' is %g, which is too negative to be floating-point "
499 "cancellation; the accumulated state is inconsistent.", label, (double)variance);
500 *result = 0.0;
501 PetscFunctionReturn(0);
502}
#define PICURV_STATISTICS_VARIANCE_FLOOR
Tolerance within which a negative variance is treated as floating-point noise.
Here is the caller graph for this function:

◆ ResolveDerivedIndex()

static PetscErrorCode ResolveDerivedIndex ( const PicurvWindowDefinition definition,
const PicurvWindowStorage storage,
const PetscBool  wanted[DERIVED_KIND_COUNT],
PetscInt  index,
DerivedKind kind,
PetscInt *  offset 
)
static

Internal helper: resolves which kind and member one derived index selects.

Local to this translation unit. Walks the same order DerivedKindExtent counts, so the enumeration and the count cannot disagree.

Definition at line 509 of file statistics_accumulator.c.

513{
514 PetscFunctionBeginUser;
515 for (PetscInt k = 0; k < DERIVED_KIND_COUNT; ++k) {
516 PetscInt extent = 0;
517
518 if (!wanted[k]) continue;
519 PetscCall(DerivedKindExtent(definition, storage, (DerivedKind)k, &extent));
520 if (index < extent) {
521 *kind = (DerivedKind)k;
522 *offset = index;
523 PetscFunctionReturn(0);
524 }
525 index -= extent;
526 }
527 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
528 "Derived index is past the end of the requested output set.");
529}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvWindowDerive()

PetscErrorCode PicurvWindowDerive ( UserCtx user,
const PicurvWindowDefinition definition,
const PicurvWindowStorage storage,
const char *  outputs,
PetscInt  index,
Vec  scalar_target,
Vec  vector_target,
PicurvDerivedField field 
)

Implementation of PicurvWindowDerive().

Derives one output field from centered accumulator state.

See also
PicurvWindowDerive()

Definition at line 535 of file statistics_accumulator.c.

539{
540 PetscBool wanted[DERIVED_KIND_COUNT];
543 PetscReal ***weight_arr = NULL, ***weight_sq_arr = NULL, ***count_arr = NULL;
544 PetscScalar ****source = NULL, ****target = NULL;
545 const FieldDescriptor *descriptor = NULL;
546 DM source_dm = NULL;
547 Vec source_vec = NULL;
548 PetscInt offset = 0, slot = 0, member = 0;
549 /* The source's component count is not the output's: a scalar RMS reads a
550 * six-component tensor, so each needs its own DM. */
551 PetscInt source_components = 0;
552
553 PetscFunctionBeginUser;
554 PetscCheck(user != NULL && definition != NULL && storage != NULL && field != NULL,
555 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
556 "Context, definition, storage, and output are required.");
557 PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
558 "Derived index %" PetscInt_FMT " is negative.", index);
559 PetscCall(PetscMemzero(field, sizeof(*field)));
560 /* The output list is parsed once and the extents walked once. Resolving the
561 * index is what bounds-checks it, so counting first would repeat both. */
562 PetscCall(ParseDerivedKinds(outputs, wanted));
563 PetscCall(ResolveDerivedIndex(definition, storage, wanted, index, &kind, &offset));
564
565 /* Locate the accumulator slot and component this index selects. */
566 switch (kind) {
567 case DERIVED_MEAN:
568 slot = offset;
569 PetscCall(FieldGetDescriptor((FieldId)definition->fields[slot].field_id, &descriptor));
570 source_vec = storage->mean[slot];
571 field->components = descriptor->dof;
572 source_components = descriptor->dof;
573 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_mean",
574 definition->name, descriptor->canonical_name));
575 break;
576 case DERIVED_TKE:
577 for (slot = 0; slot < storage->field_count; ++slot) {
578 if (!storage->m2[slot]) continue;
579 PetscCall(FieldGetDescriptor((FieldId)definition->fields[slot].field_id, &descriptor));
580 if (descriptor->dof != 3) continue;
581 if (offset-- == 0) break;
582 }
583 source_vec = storage->m2[slot];
584 field->components = 1;
585 PetscCall(PicurvProductComponentCount(descriptor->dof, &source_components));
586 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_tke",
587 definition->name, descriptor->canonical_name));
588 break;
589 case DERIVED_FLUX:
590 slot = offset;
591 {
592 const FieldDescriptor *first = NULL;
593 const FieldDescriptor *second = NULL;
594
595 PetscCall(FieldGetDescriptor((FieldId)definition->covariances[slot].first, &first));
596 PetscCall(FieldGetDescriptor((FieldId)definition->covariances[slot].second, &second));
597 PetscCall(PicurvCovarianceComponentCount(first->dof, second->dof, &field->components));
598 source_components = field->components;
599 source_vec = storage->cm[slot];
600 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_%s_flux",
601 definition->name, first->canonical_name, second->canonical_name));
602 }
603 break;
604 default:
605 /* Stress and RMS both walk the fields carrying a product; stress emits every
606 * symmetric component, RMS only the diagonal. */
607 for (slot = 0; slot < storage->field_count; ++slot) {
608 PetscInt extent = 0;
609
610 if (!storage->m2[slot]) continue;
611 PetscCall(FieldGetDescriptor((FieldId)definition->fields[slot].field_id, &descriptor));
612 PetscCall(PicurvProductComponentCount(descriptor->dof, &extent));
613 if (kind == DERIVED_RMS) extent = descriptor->dof;
614 if (offset < extent) { member = offset; break; }
615 offset -= extent;
616 }
617 source_vec = storage->m2[slot];
618 field->components = 1;
619 PetscCall(PicurvProductComponentCount(descriptor->dof, &source_components));
620 if (kind == DERIVED_RMS) {
621 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_rms%s",
622 definition->name, descriptor->canonical_name,
623 descriptor->dof == 1 ? "" : kAxisName[member]));
624 } else if (descriptor->dof == 1) {
625 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_variance",
626 definition->name, descriptor->canonical_name));
627 } else {
628 char label[8];
629
630 PetscCall(ProductComponentLabel(member, label, sizeof(label)));
631 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_R_%s",
632 definition->name, descriptor->canonical_name, label));
633 }
634 break;
635 }
636
637 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
639 PetscCall(PicurvStatisticsComponentDM(user, source_components, &source_dm));
640 {
641 Vec destination = (field->components == 1) ? scalar_target : vector_target;
642 DM destination_dm = NULL;
643
644 PetscCheck(destination != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
645 "Derived field '%s' needs a %d-component destination.",
646 field->name, (int)field->components);
647 PetscCall(PicurvStatisticsComponentDM(user, field->components, &destination_dm));
648 PetscCall(VecZeroEntries(destination));
649
650 PetscCall(DMDAVecGetArrayRead(user->da, storage->weight, &weight_arr));
651 PetscCall(DMDAVecGetArrayRead(user->da, storage->weight_sq, &weight_sq_arr));
652 PetscCall(DMDAVecGetArrayRead(user->da, storage->count, &count_arr));
653 PetscCall(DMDAVecGetArrayDOFRead(source_dm, source_vec, &source));
654 PetscCall(DMDAVecGetArrayDOF(destination_dm, destination, &target));
655
656 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
657 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
658 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
660
661 /* A point the window never sampled has no average at all, so it
662 * is left at zero rather than divided by a zero weight. */
663 if (weight_arr[k][j][i] <= 0.0) continue;
664 pair.count = count_arr[k][j][i];
665 pair.weight = weight_arr[k][j][i];
666 pair.weight_sq = weight_sq_arr[k][j][i];
667 pair.mean_x = 0.0;
668 pair.mean_y = 0.0;
669
670 if (kind == DERIVED_MEAN) {
671 for (PetscInt c = 0; c < field->components; ++c) {
672 target[k][j][i][c] = source[k][j][i][c];
673 }
674 } else if (kind == DERIVED_TKE) {
675 /* The trace of the stress tensor, halved: components 0, 3
676 * and 5 are xx, yy and zz in the stored symmetric order. */
677 PetscReal trace = 0.0;
678
679 for (PetscInt c = 0; c < 3; ++c) {
680 pair.cm = source[k][j][i][ProductDiagonalIndex(c)];
681 trace += PicurvCoMomentStateCovariance(&pair);
682 }
683 target[k][j][i][0] = 0.5 * trace;
684 } else if (kind == DERIVED_RMS) {
685 PetscReal deviation = 0.0;
686
687 pair.cm = source[k][j][i][(descriptor->dof == 1)
688 ? 0 : ProductDiagonalIndex(member)];
690 field->name, &deviation));
691 target[k][j][i][0] = deviation;
692 } else if (kind == DERIVED_FLUX) {
693 for (PetscInt c = 0; c < field->components; ++c) {
694 pair.cm = source[k][j][i][c];
695 target[k][j][i][c] = PicurvCoMomentStateCovariance(&pair);
696 }
697 } else {
698 pair.cm = source[k][j][i][member];
699 target[k][j][i][0] = PicurvCoMomentStateCovariance(&pair);
700 }
701 }
702 }
703 }
704
705 PetscCall(DMDAVecRestoreArrayDOF(destination_dm, destination, &target));
706 PetscCall(DMDAVecRestoreArrayDOFRead(source_dm, source_vec, &source));
707 PetscCall(DMDAVecRestoreArrayRead(user->da, storage->count, &count_arr));
708 PetscCall(DMDAVecRestoreArrayRead(user->da, storage->weight_sq, &weight_sq_arr));
709 PetscCall(DMDAVecRestoreArrayRead(user->da, storage->weight, &weight_arr));
710 }
711 PetscFunctionReturn(0);
712}
static PetscInt ProductDiagonalIndex(PetscInt component)
Internal helper: the product index carrying one component's own variance.
static PetscErrorCode ResolveDerivedIndex(const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const PetscBool wanted[DERIVED_KIND_COUNT], PetscInt index, DerivedKind *kind, PetscInt *offset)
Internal helper: resolves which kind and member one derived index selects.
static PetscErrorCode ProductComponentLabel(PetscInt index, char *out, size_t size)
Internal helper: writes the two-axis label of one product component.
static PetscErrorCode SafeStandardDeviation(PetscReal variance, const char *label, PetscReal *result)
Internal helper: takes a square root of a variance that may be barely negative.
PetscInt components
One or three.
char name[96]
Output field name, window qualified.
PetscReal weight_sq
Sum of squared weights W2.
PetscReal mean_y
Weighted mean of the second member.
PetscReal count
Number of accepted samples.
PetscReal cm
Centered co-moment sum C.
PetscReal PicurvCoMomentStateCovariance(const PicurvCoMomentState *state)
Returns the weighted covariance C/W, or zero when no weight accumulated.
PetscReal mean_x
Weighted mean of the first member.
PetscReal weight
Total weight W.
Weighted centered co-moment state for one ordered pair of quantities.
PetscInt end[3]
Exclusive end per dimension (i, j, k).
PetscInt start[3]
Inclusive start per dimension (i, j, k).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvWindowSpatialMean()

PetscErrorCode PicurvWindowSpatialMean ( UserCtx user,
const PicurvWindowDefinition definition,
const PicurvWindowStorage storage,
Vec  field,
PetscReal *  mean 
)

Implementation of PicurvWindowSpatialMean().

Reports the spatial mean of a derived field over the points a window sampled.

See also
PicurvWindowSpatialMean()

Definition at line 718 of file statistics_accumulator.c.

721{
723 const PetscReal ***values = NULL;
724 PetscReal ***weight_arr = NULL;
725 PetscReal local_sum = 0.0;
726 PetscReal local_count = 0.0;
727 PetscReal totals[2] = {0.0, 0.0};
728 PetscReal reduced[2] = {0.0, 0.0};
729
730 PetscFunctionBeginUser;
731 PetscCheck(user != NULL && definition != NULL && storage != NULL && field != NULL && mean != NULL,
732 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
733 "Context, definition, storage, field, and output are required.");
734 *mean = 0.0;
735 if (definition->field_count == 0) PetscFunctionReturn(0);
736
737 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
739 PetscCall(DMDAVecGetArrayRead(user->da, field, &values));
740 PetscCall(DMDAVecGetArray(user->da, storage->weight, &weight_arr));
741 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
742 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
743 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
744 /* A point with no accumulated weight holds a zero that means "never
745 * measured", so it is excluded from both the sum and the count. */
746 if (weight_arr[k][j][i] <= 0.0) continue;
747 local_sum += values[k][j][i];
748 local_count += 1.0;
749 }
750 }
751 }
752 PetscCall(DMDAVecRestoreArray(user->da, storage->weight, &weight_arr));
753 PetscCall(DMDAVecRestoreArrayRead(user->da, field, &values));
754
755 totals[0] = local_sum;
756 totals[1] = local_count;
757 PetscCallMPI(MPI_Allreduce(totals, reduced, 2, MPIU_REAL, MPIU_SUM, PETSC_COMM_WORLD));
758 if (reduced[1] > 0.0) *mean = reduced[0] / reduced[1];
759 PetscFunctionReturn(0);
760}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvWindowValidFractionRange()

PetscErrorCode PicurvWindowValidFractionRange ( UserCtx user,
const PicurvWindowDefinition definition,
const PicurvWindowStorage storage,
PetscInt  sample_count,
PetscReal *  minimum,
PetscReal *  maximum 
)

Implementation of PicurvWindowValidFractionRange().

Reports the range of per-point valid fraction across a window's domain.

See also
PicurvWindowValidFractionRange()

Definition at line 766 of file statistics_accumulator.c.

769{
771 PetscReal ***nvert = NULL;
772 PetscReal ***count_arr = NULL;
773 PetscReal local_min = PETSC_MAX_REAL;
774 PetscReal local_max = 0.0;
775 PetscReal reduced_min = 0.0, reduced_max = 0.0;
776
777 PetscFunctionBeginUser;
778 PetscCheck(user != NULL && definition != NULL && storage != NULL &&
779 minimum != NULL && maximum != NULL,
780 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Context, definition, storage, and outputs are required.");
781 *minimum = 1.0;
782 *maximum = 0.0;
783 if (definition->field_count == 0 || sample_count <= 0) PetscFunctionReturn(0);
784
785 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
787 PetscCall(DMDAVecGetArrayRead(user->da, user->Nvert, &nvert));
788 PetscCall(DMDAVecGetArrayRead(user->da, storage->count, &count_arr));
789 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
790 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
791 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
792 /* The mask is evaluated at the current state, but a point excluded
793 * now may have contributed earlier, so its stored count is what
794 * decides its fraction rather than its present eligibility. */
795 const PetscReal fraction = count_arr[k][j][i] / (PetscReal)sample_count;
796
797 local_min = PetscMin(local_min, fraction);
798 local_max = PetscMax(local_max, fraction);
799 }
800 }
801 }
802 PetscCall(DMDAVecRestoreArrayRead(user->da, storage->count, &count_arr));
803 PetscCall(DMDAVecRestoreArrayRead(user->da, user->Nvert, &nvert));
804
805 /* A rank owning no targeted point must not drag the minimum down, so its
806 * sentinel is neutral under the reduction rather than zero. */
807 PetscCallMPI(MPI_Allreduce(&local_min, &reduced_min, 1, MPIU_REAL, MPIU_MIN, PETSC_COMM_WORLD));
808 PetscCallMPI(MPI_Allreduce(&local_max, &reduced_max, 1, MPIU_REAL, MPIU_MAX, PETSC_COMM_WORLD));
809 *minimum = (reduced_min == PETSC_MAX_REAL) ? 1.0 : reduced_min;
810 *maximum = reduced_max;
811 PetscFunctionReturn(0);
812}
Vec Nvert
Definition variables.h:929
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvWindowAccumulate()

PetscErrorCode PicurvWindowAccumulate ( UserCtx user,
const PicurvWindowDefinition definition,
PicurvWindowStorage storage,
PetscReal  weight 
)

Implementation of PicurvWindowAccumulate().

Applies one accepted completed state to a window's accumulators.

See also
PicurvWindowAccumulate()

Definition at line 818 of file statistics_accumulator.c.

820{
822 PetscReal ***nvert = NULL;
823 PetscReal ***count_arr = NULL, ***weight_arr = NULL, ***weight_sq_arr = NULL;
824
825 PetscFunctionBeginUser;
826 PetscCheck(user != NULL && definition != NULL && storage != NULL,
827 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Context, definition, and storage are required.");
828 PetscCheck(weight > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
829 "Accepted states carry a positive weight, got %g.", (double)weight);
830 if (definition->field_count == 0) PetscFunctionReturn(0);
831
832 /* Every requested field shares the pointwise cell-centered domain in Phase 2,
833 * so the plan is resolved once rather than per field. */
834 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
836
837 PetscCall(DMDAVecGetArrayRead(user->da, user->Nvert, &nvert));
838 PetscCall(DMDAVecGetArray(user->da, storage->count, &count_arr));
839 PetscCall(DMDAVecGetArray(user->da, storage->weight, &weight_arr));
840 PetscCall(DMDAVecGetArray(user->da, storage->weight_sq, &weight_sq_arr));
841
842 /* Pass one advances per-point occupancy, so every field pass afterwards can
843 * recover the pre-state weight uniformly as (stored weight - this weight).
844 * Occupancy is per point and per accepted state, never per field. */
845 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
846 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
847 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
848 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) continue;
849 count_arr[k][j][i] += 1.0;
850 weight_arr[k][j][i] += weight;
851 weight_sq_arr[k][j][i] += weight * weight;
852 }
853 }
854 }
855
856 /* Pass two updates every cross-field co-moment. It runs before any mean is
857 * written back, because a co-moment needs the pre-update mean of *both*
858 * members, and pass three overwrites them field by field. */
859 for (PetscInt pair = 0; pair < definition->covariance_count; ++pair) {
860 FieldView view_a, view_b;
861 PetscScalar ****src_a = NULL, ****mean_a = NULL;
862 PetscScalar ****src_b = NULL, ****mean_b = NULL;
863 PetscScalar ****co_moment = NULL;
864 DM pair_dm = NULL;
865 PetscInt slot_a = 0, slot_b = 0, dof_a = 0, dof_b = 0, components = 0;
866
867 PetscCall(FindFieldSlot(definition, definition->covariances[pair].first, &slot_a));
868 PetscCall(FindFieldSlot(definition, definition->covariances[pair].second, &slot_b));
869 PetscCall(FieldGetView(user, (FieldId)definition->covariances[pair].first, &view_a));
870 PetscCall(FieldGetView(user, (FieldId)definition->covariances[pair].second, &view_b));
871 dof_a = view_a.descriptor->dof;
872 dof_b = view_b.descriptor->dof;
873 PetscCall(PicurvCovarianceComponentCount(dof_a, dof_b, &components));
874 PetscCall(PicurvStatisticsComponentDM(user, components, &pair_dm));
875
876 /* A component-indexed view serves every degree of freedom, so the loop
877 * below needs no scalar-versus-vector branching. */
878 PetscCall(DMDAVecGetArrayDOFRead(view_a.dm, view_a.global_vec, &src_a));
879 PetscCall(DMDAVecGetArrayDOFRead(view_a.dm, storage->mean[slot_a], &mean_a));
880 PetscCall(DMDAVecGetArrayDOFRead(view_b.dm, view_b.global_vec, &src_b));
881 PetscCall(DMDAVecGetArrayDOFRead(view_b.dm, storage->mean[slot_b], &mean_b));
882 PetscCall(DMDAVecGetArrayDOF(pair_dm, storage->cm[pair], &co_moment));
883
884 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
885 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
886 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
887 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) continue;
888 for (PetscInt c = 0; c < components; ++c) {
889 /* A scalar member contributes its single value against every
890 * component of a vector member, which is what makes a
891 * vector-scalar covariance a three-component object. */
892 const PetscInt component_a = (dof_a == 3) ? c : 0;
893 const PetscInt component_b = (dof_b == 3) ? c : 0;
895
896 state.count = count_arr[k][j][i] - 1.0;
897 state.weight = weight_arr[k][j][i] - weight;
898 state.weight_sq = weight_sq_arr[k][j][i] - weight * weight;
899 state.mean_x = mean_a[k][j][i][component_a];
900 state.mean_y = mean_b[k][j][i][component_b];
901 state.cm = co_moment[k][j][i][c];
902 PetscCall(PicurvCoMomentStateUpdate(&state,
903 src_a[k][j][i][component_a],
904 src_b[k][j][i][component_b], weight));
905 co_moment[k][j][i][c] = state.cm;
906 }
907 }
908 }
909 }
910
911 PetscCall(DMDAVecRestoreArrayDOF(pair_dm, storage->cm[pair], &co_moment));
912 PetscCall(DMDAVecRestoreArrayDOFRead(view_b.dm, storage->mean[slot_b], &mean_b));
913 PetscCall(DMDAVecRestoreArrayDOFRead(view_b.dm, view_b.global_vec, &src_b));
914 PetscCall(DMDAVecRestoreArrayDOFRead(view_a.dm, storage->mean[slot_a], &mean_a));
915 PetscCall(DMDAVecRestoreArrayDOFRead(view_a.dm, view_a.global_vec, &src_a));
916 }
917
918 /* Pass three updates each requested field's mean and, when asked, its centered
919 * self-product. A three-vector's self-product is the six symmetric co-moments
920 * between component pairs, not three per-component variances, so the co-moment
921 * kernel drives every product component including the diagonal. */
922 for (PetscInt field_index = 0; field_index < definition->field_count; ++field_index) {
923 FieldView view;
924 PetscScalar ****src = NULL, ****mean = NULL, ****product = NULL;
925 DM product_dm = NULL;
926 PetscInt dof = 0, components = 0;
927 const PetscBool second = definition->fields[field_index].want_second;
928
929 PetscCall(FieldGetView(user, (FieldId)definition->fields[field_index].field_id, &view));
930 dof = view.descriptor->dof;
931 PetscCall(DMDAVecGetArrayDOFRead(view.dm, view.global_vec, &src));
932 PetscCall(DMDAVecGetArrayDOF(view.dm, storage->mean[field_index], &mean));
933 if (second) {
934 PetscCall(PicurvProductComponentCount(dof, &components));
935 PetscCall(PicurvStatisticsComponentDM(user, components, &product_dm));
936 PetscCall(DMDAVecGetArrayDOF(product_dm, storage->m2[field_index], &product));
937 }
938
939 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
940 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
941 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
942 PetscReal prior_weight = 0.0;
943 PetscReal prior_weight_sq = 0.0;
944 PetscReal prior_count = 0.0;
945
946 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) continue;
947
948 prior_weight = weight_arr[k][j][i] - weight;
949 prior_weight_sq = weight_sq_arr[k][j][i] - weight * weight;
950 prior_count = count_arr[k][j][i] - 1.0;
951
952 /* Products are updated before the means are written back,
953 * because the co-moment kernel needs the pre-update means. */
954 for (PetscInt c = 0; c < components; ++c) {
955 const PetscInt a = (dof == 1) ? 0 : kProductFirst[c];
956 const PetscInt b = (dof == 1) ? 0 : kProductSecond[c];
958
959 state.count = prior_count;
960 state.weight = prior_weight;
961 state.weight_sq = prior_weight_sq;
962 state.mean_x = mean[k][j][i][a];
963 state.mean_y = mean[k][j][i][b];
964 state.cm = product[k][j][i][c];
965 PetscCall(PicurvCoMomentStateUpdate(&state, src[k][j][i][a],
966 src[k][j][i][b], weight));
967 product[k][j][i][c] = state.cm;
968 }
969
970 for (PetscInt c = 0; c < dof; ++c) {
971 PicurvMomentState moment;
972
973 moment.count = prior_count;
974 moment.weight = prior_weight;
975 moment.weight_sq = prior_weight_sq;
976 moment.mean = mean[k][j][i][c];
977 moment.m2 = 0.0;
978 PetscCall(PicurvMomentStateUpdate(&moment, src[k][j][i][c], weight));
979 mean[k][j][i][c] = moment.mean;
980 }
981 }
982 }
983 }
984
985 if (second) PetscCall(DMDAVecRestoreArrayDOF(product_dm, storage->m2[field_index], &product));
986 PetscCall(DMDAVecRestoreArrayDOF(view.dm, storage->mean[field_index], &mean));
987 PetscCall(DMDAVecRestoreArrayDOFRead(view.dm, view.global_vec, &src));
988 }
989
990 PetscCall(DMDAVecRestoreArray(user->da, storage->weight_sq, &weight_sq_arr));
991 PetscCall(DMDAVecRestoreArray(user->da, storage->weight, &weight_arr));
992 PetscCall(DMDAVecRestoreArray(user->da, storage->count, &count_arr));
993 PetscCall(DMDAVecRestoreArrayRead(user->da, user->Nvert, &nvert));
994 PetscFunctionReturn(0);
995}
static PetscErrorCode FindFieldSlot(const PicurvWindowDefinition *definition, PetscInt field_id, PetscInt *slot)
Internal helper: locates the storage slot holding one field's running mean.
PetscReal weight_sq
Sum of squared weights W2.
PetscErrorCode PicurvCoMomentStateUpdate(PicurvCoMomentState *state, PetscReal value_x, PetscReal value_y, PetscReal weight)
Applies one weighted paired sample to a co-moment accumulator.
PetscReal weight
Total weight W.
PetscReal m2
Centered second-moment sum M2.
PetscReal mean
Weighted mean mu.
PetscErrorCode PicurvMomentStateUpdate(PicurvMomentState *state, PetscReal value, PetscReal weight)
Applies one weighted sample to a scalar moment accumulator.
PetscReal count
Number of accepted samples.
Weighted centered state for one scalar quantity at one point.
PetscBool SpatialTargetPlanMaskAllows(const SpatialTargetPlan *plan, PetscReal nvert_value)
Reports whether a point passes the plan's mask.
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ kProductFirst

const PetscInt kProductFirst[6] = {0, 0, 0, 1, 1, 2}
static

Upper-triangular row-major component pairs for a three-vector self-product.

This pair table is the single definition of the symmetric component order. The diagonal positions and the two-axis component labels are both derived from it rather than restated, because accumulation reads the table while derivation reads what follows from it: a hand-written copy that drifted would mislabel every Reynolds stress and take the root of the wrong component, with nothing failing.

Definition at line 28 of file statistics_accumulator.c.

28{0, 0, 0, 1, 1, 2};

◆ kProductSecond

const PetscInt kProductSecond[6] = {0, 1, 2, 1, 2, 2}
static

Definition at line 29 of file statistics_accumulator.c.

29{0, 1, 2, 1, 2, 2};

◆ kAxisName

const char* const kAxisName[3] = {"x", "y", "z"}
static

Axis labels indexing the pair table above.

Definition at line 32 of file statistics_accumulator.c.

32{"x", "y", "z"};

◆ kDerivedKindName

const char* const kDerivedKindName[DERIVED_KIND_COUNT]
static
Initial value:
= {
"mean", "reynolds_stress", "rms", "tke", "flux"
}

Recipe spellings of the output kinds.

Definition at line 370 of file statistics_accumulator.c.

370 {
371 "mean", "reynolds_stress", "rms", "tke", "flux"
372};