PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Data Structures | Macros | Typedefs | Functions
statistics_accumulator.h File Reference

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

#include "variables.h"
#include "statistics_window.h"
Include dependency graph for statistics_accumulator.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  PicurvWindowStorage
 Independent accumulator state for one window on one block. More...
 
struct  PicurvStatisticsPayload
 One checkpointable accumulator vector, resolved by enumeration index. More...
 
struct  PicurvDerivedField
 One derived output field, resolved by enumeration index. More...
 

Macros

#define PICURV_STATISTICS_PAYLOAD_NAME_LENGTH   96
 Maximum stored length of a payload name, including the terminator.
 
#define PICURV_STATISTICS_VARIANCE_FLOOR   1.0e-12
 Tolerance within which a negative variance is treated as floating-point noise.
 

Typedefs

typedef struct PicurvWindowStorage PicurvWindowStorage
 Independent accumulator state for one window on one block.
 

Functions

PetscErrorCode PicurvWindowStoragePayloadCount (const PicurvWindowStorage *storage, PetscInt *count)
 Reports how many checkpointable vectors one window's storage holds.
 
PetscErrorCode PicurvWindowStoragePayload (UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, PetscInt index, PicurvStatisticsPayload *payload)
 Resolves one enumerated payload of a window's storage.
 
PetscErrorCode PicurvStatisticsComponentDM (UserCtx *user, PetscInt components, DM *dm)
 Resolves the DM carrying a given number of accumulator components.
 
PetscErrorCode PicurvProductComponentCount (PetscInt dof, PetscInt *count)
 Reports how many symmetric product components a field's second moment needs.
 
PetscErrorCode PicurvCovarianceComponentCount (PetscInt dof_a, PetscInt dof_b, PetscInt *count)
 Reports how many components a covariance between two fields needs.
 
PetscErrorCode PicurvWindowStorageCreate (UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage)
 Allocates the accumulator state one window owns on one block.
 
PetscErrorCode PicurvWindowStorageDestroy (PicurvWindowStorage *storage)
 Releases accumulator state previously created for one window.
 
PetscErrorCode PicurvWindowDerivedCount (const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, const char *outputs, PetscInt *count)
 Reports how many derived fields a requested output set produces.
 
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.
 
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.
 
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 PicurvWindowAccumulate (UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage, PetscReal weight)
 Applies one accepted completed state to a window's accumulators.
 

Detailed Description

Per-window PETSc accumulator storage and pointwise application.

Holds the independent state each named window owns, and applies one accepted completed state to it, per Field Statistics Phase 2 Implementation Specification sections 5 and 13.

Storage is allocated once by the vector factory and released once at teardown. Application is strictly pointwise: it reads a source field value at an owned point and updates that point's accumulator, so it performs no halo exchange and allocates nothing.

Definition in file statistics_accumulator.h.


Data Structure Documentation

◆ PicurvWindowStorage

struct PicurvWindowStorage

Independent accumulator state for one window on one block.

Per-point occupancy is tracked separately from the field moments because the fluid mask can move: a point contributes only to the states in which it was valid, so its own count and weight are what normalize its moments.

Every product is one vector carrying all of its components, not one vector per component. A symmetric second-order tensor is a single object: splitting it would cost six memory streams in the per-step accumulation loop instead of one cache line, and six collective gathers per checkpoint instead of one. Component counts that neither da nor fda provides are carried by a DM mirroring the block decomposition at that degree of freedom.

Definition at line 35 of file statistics_accumulator.h.

Data Fields
PetscInt field_count Fields accumulated.
PetscInt covariance_count Covariance pairs accumulated.
Vec count Per-point accepted sample count.
Vec weight Per-point valid weight.
Vec weight_sq Per-point squared-weight sum.
Vec * mean One per field, matching that field's layout.
Vec * m2 One per field; NULL when no second moment was requested.
Vec * cm One per covariance pair.

◆ PicurvStatisticsPayload

struct PicurvStatisticsPayload

One checkpointable accumulator vector, resolved by enumeration index.

The enumeration order is the persistence contract: the manifest inventory, the checkpoint writer, and the restart reader all walk it identically, so a payload lands in the vector it came from without a separate lookup table.

Definition at line 56 of file statistics_accumulator.h.

Data Fields
char name[96] File basename, no extension.
Vec vec Borrowed accumulator vector; never owned by the caller.
PetscInt components Degrees of freedom the vector carries.
const char * role Inventory role: occupancy, mean, second_moment, co_moment.
const char * layout Catalog layout name for the inventory entry.

◆ PicurvDerivedField

struct PicurvDerivedField

One derived output field, resolved by enumeration index.

Definition at line 146 of file statistics_accumulator.h.

Data Fields
char name[96] Output field name, window qualified.
PetscInt components One or three.

Macro Definition Documentation

◆ PICURV_STATISTICS_PAYLOAD_NAME_LENGTH

#define PICURV_STATISTICS_PAYLOAD_NAME_LENGTH   96

Maximum stored length of a payload name, including the terminator.

Definition at line 47 of file statistics_accumulator.h.

◆ PICURV_STATISTICS_VARIANCE_FLOOR

#define PICURV_STATISTICS_VARIANCE_FLOOR   1.0e-12

Tolerance within which a negative variance is treated as floating-point noise.

Definition at line 143 of file statistics_accumulator.h.

Typedef Documentation

◆ PicurvWindowStorage

Independent accumulator state for one window on one block.

Per-point occupancy is tracked separately from the field moments because the fluid mask can move: a point contributes only to the states in which it was valid, so its own count and weight are what normalize its moments.

Every product is one vector carrying all of its components, not one vector per component. A symmetric second-order tensor is a single object: splitting it would cost six memory streams in the per-step accumulation loop instead of one cache line, and six collective gathers per checkpoint instead of one. Component counts that neither da nor fda provides are carried by a DM mirroring the block decomposition at that degree of freedom.

Function Documentation

◆ PicurvWindowStoragePayloadCount()

PetscErrorCode PicurvWindowStoragePayloadCount ( const PicurvWindowStorage storage,
PetscInt *  count 
)

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

Parameters
[in]storageStorage to measure.
[out]countPayload count, including the three occupancy vectors.
Returns
Zero on success, or PETSC_ERR_ARG_NULL for a null argument.

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}
PetscInt field_count
Fields accumulated.
PetscInt covariance_count
Covariance pairs accumulated.
Vec * m2
One per field; NULL when no second moment was requested.
Here is the caller graph for this function:

◆ PicurvWindowStoragePayload()

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

Resolves one enumerated payload of a window's storage.

Names are derived from catalogued field names and fixed component suffixes, so they are stable across runs, rank counts, and configuration reorderings.

Parameters
[in]userBlock context the storage belongs to.
[in]definitionWindow definition naming the accumulated fields and pairs.
[in]storageStorage to enumerate.
[in]indexPayload index in [0, count).
[out]payloadResolved payload; the vector is borrowed, not duplicated.
Returns
Zero on success, or PETSC_ERR_ARG_OUTOFRANGE for an index outside the range.

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.
FieldId
Compile-time identity for a catalogued Eulerian field.
Immutable metadata for one field identity.
PetscErrorCode PicurvProductComponentCount(PetscInt dof, PetscInt *count)
Implementation of PicurvProductComponentCount().
PetscErrorCode PicurvCovarianceComponentCount(PetscInt dof_a, PetscInt dof_b, PetscInt *count)
Implementation of PicurvCovarianceComponentCount().
PetscErrorCode PicurvWindowStoragePayloadCount(const PicurvWindowStorage *storage, PetscInt *count)
Implementation of PicurvWindowStoragePayloadCount().
Vec weight
Per-point valid weight.
PetscInt components
Degrees of freedom the vector carries.
Vec weight_sq
Per-point squared-weight sum.
Vec * mean
One per field, matching that field's layout.
Vec vec
Borrowed accumulator vector; never owned by the caller.
const char * role
Inventory role: occupancy, mean, second_moment, co_moment.
Vec count
Per-point accepted sample count.
char name[96]
File basename, no extension.
const char * layout
Catalog layout name for the inventory entry.
Vec * cm
One per covariance pair.
PetscInt first
First member; must also appear in the field list.
PicurvWindowFieldRequest fields[16]
PetscInt second
Second member; must also appear in the field list.
PicurvWindowCovarianceRequest covariances[16]
PetscInt field_id
Catalogued Eulerian field identity.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvStatisticsComponentDM()

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

Resolves the DM carrying a given number of accumulator components.

Component counts of one and three reuse the DMs the block already owns; six is carried by the symmetric-tensor DM created alongside them. Every one of these mirrors the block decomposition exactly, so a pointwise loop can read a source field and write an accumulator at the same index.

Parameters
[in]userBlock context owning the DMs.
[in]componentsComponent count to place.
[out]dmResolved DM; borrowed, never destroyed by the caller.
Returns
Zero on success, or PETSC_ERR_ARG_OUTOFRANGE for an unsupported count.

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:

◆ PicurvProductComponentCount()

PetscErrorCode PicurvProductComponentCount ( PetscInt  dof,
PetscInt *  count 
)

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

Parameters
[in]dofDegree of freedom of the field.
[out]countComponent count: one for a scalar, six for a three-vector.
Returns
Zero on success, or PETSC_ERR_ARG_OUTOFRANGE for an unsupported dof.

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 
)

Reports how many components a covariance between two fields needs.

Parameters
[in]dof_aDegree of freedom of the first member.
[in]dof_bDegree of freedom of the second member.
[out]countComponent count.
Returns
Zero on success, or PETSC_ERR_ARG_OUTOFRANGE for an unsupported pairing.

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:

◆ PicurvWindowStorageCreate()

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

Allocates the accumulator state one window owns on one block.

Every vector is duplicated from one the factory already built, so no new DM or layout decision is introduced.

Parameters
[in]userBlock context supplying the source fields.
[in]definitionWindow definition naming the requested fields and pairs.
[out]storageStorage to populate; zeroed on entry.
Returns
Zero on success, or a PETSc error for an unknown field or unsupported layout.

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.
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 PicurvStatisticsComponentDM(UserCtx *user, PetscInt components, DM *dm)
Implementation of PicurvStatisticsComponentDM().
@ 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.
PetscBool want_second
Also keep the centered second moment.
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)

Releases accumulator state previously created for one window.

Parameters
[in,out]storageStorage to release; safe to call on zeroed storage.
Returns
Zero on success.

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:

◆ PicurvWindowDerivedCount()

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

Reports how many derived fields a requested output set produces.

Parameters
[in]definitionWindow definition naming the accumulated fields and pairs.
[in]storageAccumulator state the outputs are derived from.
[in]outputsComma-separated output kinds: mean, reynolds_stress, rms, tke, flux.
[out]countNumber of derived fields.
Returns
Zero on success, or PETSC_ERR_ARG_WRONG for an unknown output kind.

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.
DerivedKind
Output kinds a postprocessing recipe may request, in enumeration order.
@ DERIVED_KIND_COUNT
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:

◆ PicurvWindowDerive()

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.

Normalizes in exactly one place: R_ij = C_ij / W, RMS_i = sqrt(R_ii), k = (R_xx + R_yy + R_zz) / 2, and a flux is the co-moment over the same weight. Each uses the moment kernels rather than repeating the division, so the online and offline halves of the pipeline cannot disagree about what centered state means.

A variance that comes out slightly negative through floating-point cancellation is clamped only where a square root would otherwise fail, and only within PICURV_STATISTICS_VARIANCE_FLOOR. Stored state is never modified.

Points the window never sampled are left at zero rather than divided by a zero weight; the valid-fraction range reports how much of the domain that covers.

Parameters
[in]userBlock context supplying the target domain.
[in]definitionWindow definition.
[in]storageAccumulator state to read.
[in]outputsComma-separated output kinds.
[in]indexDerived field index in [0, count).
[out]scalar_targetScalar destination, used when the field has one component.
[out]vector_targetVector destination, used when the field has three.
[out]fieldResolved name and component count of the derived field.
Returns
Zero on success, or a PETSc error.

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 const char *const kAxisName[3]
Axis labels indexing the pair table above.
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 
)

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

The average is taken over targeted points that actually accumulated weight, not over the whole vector. A derived field is zero everywhere outside the target domain and at any point the mask never admitted, and those zeros are absences rather than measurements: including them would scale the answer down by the fraction of the vector the window never covered.

Performs a collective reduction, so callers use it for reporting rather than per point.

Parameters
[in]userBlock context supplying the target domain.
[in]definitionWindow definition naming the accumulated fields.
[in]storageAccumulator state supplying per-point occupancy.
[in]fieldDerived field to average, on the cell-centred scalar DM.
[out]meanSpatial mean; zero when the window sampled no point.
Returns
Zero on success, or a PETSc error.

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 
)

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

A point contributes only to the states in which the mask accepted it, so with a moving immersed body different points carry different sample counts. The ratio of a point's own count to the window's accepted-sample count is its valid fraction, and the range of that ratio is the window's mask-health indicator: a minimum of one means every point saw every state, and a minimum of zero means some point contributed nothing at all.

Performs a collective reduction, so callers gate it on an already-active reporting path rather than calling it every step.

Parameters
[in]userBlock context supplying the target domain and mask.
[in]definitionWindow definition naming the accumulated fields.
[in]storageAccumulator state to inspect.
[in]sample_countAccepted states the window has recorded.
[out]minimumSmallest valid fraction; one when no point is targeted.
[out]maximumLargest valid fraction; zero when no point is targeted.
Returns
Zero on success, or a PETSc error.

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 
)

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

Iterates the pointwise target domain, skipping points the mask rejects, and updates each point's occupancy and every requested moment and co-moment through the centered kernels.

Parameters
[in]userBlock context supplying the source fields.
[in]definitionWindow definition.
[in,out]storageAccumulator state to update.
[in]weightWeight the window assigned to this state; must be positive.
Returns
Zero on success, or a PETSc error.

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 const PetscInt kProductFirst[6]
Upper-triangular row-major component pairs for a three-vector self-product.
static PetscErrorCode FindFieldSlot(const PicurvWindowDefinition *definition, PetscInt field_id, PetscInt *slot)
Internal helper: locates the storage slot holding one field's running mean.
static const PetscInt kProductSecond[6]
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: