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 4. Accumulated State and Component Order.

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 34 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 55 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 145 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 46 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 142 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 268 of file statistics_accumulator.c.

269{
270 PetscFunctionBeginUser;
271 PetscCheck(storage != NULL && count != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
272 "Storage and count output are required.");
273 *count = 3 + storage->field_count + storage->covariance_count;
274 /* A field without a requested second moment holds no product vector, so the
275 * enumeration skips it rather than emitting an empty payload. */
276 for (PetscInt field_index = 0; storage->m2 && field_index < storage->field_count; ++field_index) {
277 if (storage->m2[field_index]) *count += 1;
278 }
279 PetscFunctionReturn(0);
280}
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 286 of file statistics_accumulator.c.

289{
290 PetscInt total = 0;
291 PetscInt cursor = index;
292
293 PetscFunctionBeginUser;
294 PetscCheck(user != NULL && definition != NULL && storage != NULL && payload != NULL,
295 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
296 "Context, definition, storage, and payload output are required.");
297 PetscCall(PicurvWindowStoragePayloadCount(storage, &total));
298 PetscCheck(index >= 0 && index < total, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
299 "Payload index %" PetscInt_FMT " is outside [0, %" PetscInt_FMT ").", index, total);
300 PetscCall(PetscMemzero(payload, sizeof(*payload)));
301
302 /* Occupancy first, then means, then products, then co-moments. The order is the
303 * persistence contract: the manifest inventory, the writer, and the reader all
304 * walk it identically. */
305 if (cursor < 3) {
306 static const char *const occupancy_name[3] = {"count", "weight", "weight_sq"};
307 Vec occupancy[3];
308
309 occupancy[0] = storage->count;
310 occupancy[1] = storage->weight;
311 occupancy[2] = storage->weight_sq;
312 PetscCall(PetscStrncpy(payload->name, occupancy_name[cursor], sizeof(payload->name)));
313 payload->vec = occupancy[cursor];
314 payload->components = 1;
315 payload->role = "occupancy";
317 PetscFunctionReturn(0);
318 }
319 cursor -= 3;
320
321 if (cursor < storage->field_count) {
322 const FieldDescriptor *descriptor = NULL;
323
324 PetscCall(FieldGetDescriptor((FieldId)definition->fields[cursor].field_id, &descriptor));
325 PetscCall(PetscSNPrintf(payload->name, sizeof(payload->name), "%s_mean",
326 descriptor->canonical_name));
327 payload->vec = storage->mean[cursor];
328 payload->components = descriptor->dof;
329 payload->role = "mean";
330 payload->layout = FieldLayoutName(descriptor->layout);
331 PetscFunctionReturn(0);
332 }
333 cursor -= storage->field_count;
334
335 {
336 PetscInt product_count = 0;
337 PetscInt seen = 0;
338
339 for (PetscInt field_index = 0; storage->m2 && field_index < storage->field_count; ++field_index) {
340 if (storage->m2[field_index]) ++product_count;
341 }
342 if (cursor < product_count) {
343 for (PetscInt field_index = 0; field_index < storage->field_count; ++field_index) {
344 const FieldDescriptor *descriptor = NULL;
345
346 if (!storage->m2[field_index]) continue;
347 if (seen++ != cursor) continue;
348 PetscCall(FieldGetDescriptor((FieldId)definition->fields[field_index].field_id, &descriptor));
349 PetscCall(PetscSNPrintf(payload->name, sizeof(payload->name), "%s_m2",
350 descriptor->canonical_name));
351 payload->vec = storage->m2[field_index];
352 PetscCall(PicurvProductComponentCount(descriptor->dof, &payload->components));
353 payload->role = "second_moment";
354 payload->layout = FieldLayoutName(descriptor->layout);
355 PetscFunctionReturn(0);
356 }
357 }
358 cursor -= product_count;
359 }
360
361 {
362 const FieldDescriptor *first = NULL;
363 const FieldDescriptor *second = NULL;
364
365 PetscCheck(cursor >= 0 && cursor < storage->covariance_count, PETSC_COMM_SELF, PETSC_ERR_PLIB,
366 "Payload index %" PetscInt_FMT " fell through the storage enumeration.", index);
367 PetscCall(FieldGetDescriptor((FieldId)definition->covariances[cursor].first, &first));
368 PetscCall(FieldGetDescriptor((FieldId)definition->covariances[cursor].second, &second));
369 PetscCall(PetscSNPrintf(payload->name, sizeof(payload->name), "%s_%s_cm",
370 first->canonical_name, second->canonical_name));
371 payload->vec = storage->cm[cursor];
372 PetscCall(PicurvCovarianceComponentCount(first->dof, second->dof, &payload->components));
373 payload->role = "co_moment";
374 payload->layout = FieldLayoutName(first->layout);
375 }
376 PetscFunctionReturn(0);
377}
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 115 of file statistics_accumulator.c.

116{
117 PetscFunctionBeginUser;
118 PetscCheck(user != NULL && dm != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
119 "Context and DM output are required.");
120 switch (components) {
121 case 1: *dm = user->da; break;
122 case 3: *dm = user->fda; break;
123 case 6: *dm = user->fda6; break;
124 default:
125 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
126 "No block DM carries %" PetscInt_FMT " accumulator components.", components);
127 }
128 PetscCheck(*dm != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
129 "The block DM for %" PetscInt_FMT " accumulator components was never created.",
130 components);
131 PetscFunctionReturn(0);
132}
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 83 of file statistics_accumulator.c.

84{
85 PetscFunctionBeginUser;
86 PetscCheck(count != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Count output is required.");
87 PetscCheck(dof == 1 || dof == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
88 "Self-products are supported for scalar and three-vector fields, got dof %" PetscInt_FMT ".", dof);
89 *count = (dof == 1) ? 1 : 6;
90 PetscFunctionReturn(0);
91}
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 97 of file statistics_accumulator.c.

98{
99 PetscFunctionBeginUser;
100 PetscCheck(count != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Count output is required.");
101 /* Scalar-scalar and vector-scalar pairs only; a vector-vector cross product
102 * would need a nine-component carrier that nothing allocates. */
103 PetscCheck((dof_a == 1 && dof_b == 1) || (dof_a == 3 && dof_b == 1) || (dof_a == 1 && dof_b == 3),
104 PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
105 "Covariance is supported for scalar-scalar and vector-scalar pairs, got dof %" PetscInt_FMT
106 " and %" PetscInt_FMT ".", dof_a, dof_b);
107 *count = (dof_a == 3 || dof_b == 3) ? 3 : 1;
108 PetscFunctionReturn(0);
109}
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 140 of file statistics_accumulator.c.

142{
143 PetscFunctionBeginUser;
145 PetscCheck(user != NULL && definition != NULL && storage != NULL,
146 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Context, definition, and storage are required.");
147 PetscCall(PetscMemzero(storage, sizeof(*storage)));
148 storage->field_count = definition->field_count;
149 storage->covariance_count = definition->covariance_count;
150
151 /* Occupancy is per point and scalar regardless of what is accumulated. */
152 PetscCall(DMCreateGlobalVector(user->da, &storage->count));
153 PetscCall(VecSet(storage->count, 0.0));
154 PetscCall(DMCreateGlobalVector(user->da, &storage->weight));
155 PetscCall(VecSet(storage->weight, 0.0));
156 PetscCall(DMCreateGlobalVector(user->da, &storage->weight_sq));
157 PetscCall(VecSet(storage->weight_sq, 0.0));
158
159 if (definition->field_count > 0) {
160 PetscCall(PetscCalloc1((size_t)definition->field_count, &storage->mean));
161 PetscCall(PetscCalloc1((size_t)definition->field_count, &storage->m2));
162 }
163 for (PetscInt field_index = 0; field_index < definition->field_count; ++field_index) {
164 FieldView view;
165 PetscInt components = 0;
166 DM product_dm = NULL;
167
168 PetscCall(FieldGetView(user, (FieldId)definition->fields[field_index].field_id, &view));
169 /* The mean matches the source field's own layout, so it comes from that
170 * field's DM and inherits its decomposition. */
171 PetscCall(DMCreateGlobalVector(view.dm, &storage->mean[field_index]));
172 PetscCall(VecSet(storage->mean[field_index], 0.0));
173 if (!definition->fields[field_index].want_second) continue;
174 PetscCall(PicurvProductComponentCount(view.descriptor->dof, &components));
175 PetscCall(PicurvStatisticsComponentDM(user, components, &product_dm));
176 PetscCall(DMCreateGlobalVector(product_dm, &storage->m2[field_index]));
177 PetscCall(VecSet(storage->m2[field_index], 0.0));
178 }
179
180 if (definition->covariance_count > 0) {
181 PetscCall(PetscCalloc1((size_t)definition->covariance_count, &storage->cm));
182 }
183 for (PetscInt pair_index = 0; pair_index < definition->covariance_count; ++pair_index) {
184 FieldView view_a, view_b;
185 PetscInt components = 0;
186 DM pair_dm = NULL;
187
188 PetscCall(FieldGetView(user, (FieldId)definition->covariances[pair_index].first, &view_a));
189 PetscCall(FieldGetView(user, (FieldId)definition->covariances[pair_index].second, &view_b));
190 PetscCall(PicurvCovarianceComponentCount(view_a.descriptor->dof, view_b.descriptor->dof, &components));
191 PetscCall(PicurvStatisticsComponentDM(user, components, &pair_dm));
192 PetscCall(DMCreateGlobalVector(pair_dm, &storage->cm[pair_index]));
193 PetscCall(VecSet(storage->cm[pair_index], 0.0));
194 }
195
196 /* Report what this window costs and what it covers, once per block at setup.
197 * The point count is a function of layout, dimensions, and periodicity alone, so
198 * it tells an operator what "per point" will mean before any sample is taken. */
199 {
201 PetscInt payloads = 0;
202 PetscInt points = 0;
203
204 PetscCall(PicurvWindowStoragePayloadCount(storage, &payloads));
205 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
207 PetscCall(SpatialTargetPlanGlobalPointCount(&plan, PETSC_COMM_WORLD, &points));
209 "Statistics window '%s' block %d: %d accumulator vector(s) over %d point(s).\n",
210 definition->name, (int)user->_this, (int)payloads, (int)points);
211 }
213 PetscFunctionReturn(0);
214}
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
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:894
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:885
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:1091
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 220 of file statistics_accumulator.c.

221{
222 PetscFunctionBeginUser;
223 if (storage == NULL) PetscFunctionReturn(0);
224 if (storage->count) PetscCall(VecDestroy(&storage->count));
225 if (storage->weight) PetscCall(VecDestroy(&storage->weight));
226 if (storage->weight_sq) PetscCall(VecDestroy(&storage->weight_sq));
227 for (PetscInt field_index = 0; storage->mean && field_index < storage->field_count; ++field_index) {
228 if (storage->mean[field_index]) PetscCall(VecDestroy(&storage->mean[field_index]));
229 }
230 for (PetscInt field_index = 0; storage->m2 && field_index < storage->field_count; ++field_index) {
231 if (storage->m2[field_index]) PetscCall(VecDestroy(&storage->m2[field_index]));
232 }
233 for (PetscInt pair_index = 0; storage->cm && pair_index < storage->covariance_count; ++pair_index) {
234 if (storage->cm[pair_index]) PetscCall(VecDestroy(&storage->cm[pair_index]));
235 }
236 if (storage->mean) PetscCall(PetscFree(storage->mean));
237 if (storage->m2) PetscCall(PetscFree(storage->m2));
238 if (storage->cm) PetscCall(PetscFree(storage->cm));
239 PetscCall(PetscMemzero(storage, sizeof(*storage)));
240 PetscFunctionReturn(0);
241}
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 501 of file statistics_accumulator.c.

504{
505 PetscBool wanted[DERIVED_KIND_COUNT];
506
507 PetscFunctionBeginUser;
508 PetscCheck(definition != NULL && storage != NULL && count != NULL,
509 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Definition, storage, and count are required.");
510 PetscCall(ParseDerivedKinds(outputs, wanted));
511 *count = 0;
512 for (PetscInt k = 0; k < DERIVED_KIND_COUNT; ++k) {
513 PetscInt extent = 0;
514
515 if (!wanted[k]) continue;
516 PetscCall(DerivedKindExtent(definition, storage, (DerivedKind)k, &extent));
517 *count += extent;
518 }
519 PetscFunctionReturn(0);
520}
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 576 of file statistics_accumulator.c.

580{
581 PetscBool wanted[DERIVED_KIND_COUNT];
584 PetscReal ***weight_arr = NULL, ***weight_sq_arr = NULL, ***count_arr = NULL;
585 PetscScalar ****source = NULL, ****target = NULL;
586 const FieldDescriptor *descriptor = NULL;
587 DM source_dm = NULL;
588 Vec source_vec = NULL;
589 PetscInt offset = 0, slot = 0, member = 0;
590 /* Resolved where the branch knows which fields it read; 1.0 leaves the result in
591 * solver units, which is also what an undeclared scale falls back to. */
592 PetscReal dimensional_factor = 1.0;
593 /* The source's component count is not the output's: a scalar RMS reads a
594 * six-component tensor, so each needs its own DM. */
595 PetscInt source_components = 0;
596
597 PetscFunctionBeginUser;
599 PetscCheck(user != NULL && definition != NULL && storage != NULL && field != NULL,
600 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
601 "Context, definition, storage, and output are required.");
602 PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
603 "Derived index %" PetscInt_FMT " is negative.", index);
604 PetscCall(PetscMemzero(field, sizeof(*field)));
605 /* The output list is parsed once and the extents walked once. Resolving the
606 * index is what bounds-checks it, so counting first would repeat both. */
607 PetscCall(ParseDerivedKinds(outputs, wanted));
608 PetscCall(ResolveDerivedIndex(definition, storage, wanted, index, &kind, &offset));
609
610 /* Locate the accumulator slot and component this index selects. */
611 switch (kind) {
612 case DERIVED_MEAN:
613 slot = offset;
614 PetscCall(FieldGetDescriptor((FieldId)definition->fields[slot].field_id, &descriptor));
615 source_vec = storage->mean[slot];
616 field->components = descriptor->dof;
617 source_components = descriptor->dof;
618 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_mean",
619 definition->name, descriptor->canonical_name));
620 break;
621 case DERIVED_TKE:
622 for (slot = 0; slot < storage->field_count; ++slot) {
623 if (!storage->m2[slot]) continue;
624 PetscCall(FieldGetDescriptor((FieldId)definition->fields[slot].field_id, &descriptor));
625 if (descriptor->dof != 3) continue;
626 if (offset-- == 0) break;
627 }
628 source_vec = storage->m2[slot];
629 field->components = 1;
630 PetscCall(PicurvProductComponentCount(descriptor->dof, &source_components));
631 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_tke",
632 definition->name, descriptor->canonical_name));
633 break;
634 case DERIVED_FLUX:
635 slot = offset;
636 {
637 const FieldDescriptor *first = NULL;
638 const FieldDescriptor *second = NULL;
639
640 PetscCall(FieldGetDescriptor((FieldId)definition->covariances[slot].first, &first));
641 PetscCall(FieldGetDescriptor((FieldId)definition->covariances[slot].second, &second));
642 PetscCall(PicurvCovarianceComponentCount(first->dof, second->dof, &field->components));
643 source_components = field->components;
644 source_vec = storage->cm[slot];
645 if (user->simCtx->pps && user->simCtx->pps->dimensionalize) {
646 PetscReal first_scale = 1.0, second_scale = 1.0;
647
648 /* A co-moment carries the product of its two fields' dimensions. */
649 PetscCall(PicurvFieldReferenceScale(user->simCtx, first->canonical_name,
650 &first_scale, NULL, 0));
651 PetscCall(PicurvFieldReferenceScale(user->simCtx, second->canonical_name,
652 &second_scale, NULL, 0));
653 dimensional_factor = first_scale * second_scale;
654 }
655 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_%s_flux",
656 definition->name, first->canonical_name, second->canonical_name));
657 }
658 break;
659 default:
660 /* Stress and RMS both walk the fields carrying a product; stress emits every
661 * symmetric component, RMS only the diagonal. */
662 for (slot = 0; slot < storage->field_count; ++slot) {
663 PetscInt extent = 0;
664
665 if (!storage->m2[slot]) continue;
666 PetscCall(FieldGetDescriptor((FieldId)definition->fields[slot].field_id, &descriptor));
667 PetscCall(PicurvProductComponentCount(descriptor->dof, &extent));
668 if (kind == DERIVED_RMS) extent = descriptor->dof;
669 if (offset < extent) { member = offset; break; }
670 offset -= extent;
671 }
672 source_vec = storage->m2[slot];
673 field->components = 1;
674 PetscCall(PicurvProductComponentCount(descriptor->dof, &source_components));
675 if (kind == DERIVED_RMS) {
676 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_rms%s",
677 definition->name, descriptor->canonical_name,
678 descriptor->dof == 1 ? "" : kAxisName[member]));
679 } else if (descriptor->dof == 1) {
680 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_variance",
681 definition->name, descriptor->canonical_name));
682 } else {
683 char label[8];
684
685 PetscCall(ProductComponentLabel(member, label, sizeof(label)));
686 PetscCall(PetscSNPrintf(field->name, sizeof(field->name), "%s_%s_R_%s",
687 definition->name, descriptor->canonical_name, label));
688 }
689 break;
690 }
691
692 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
694 PetscCall(PicurvStatisticsComponentDM(user, source_components, &source_dm));
695 {
696 Vec destination = (field->components == 1) ? scalar_target : vector_target;
697 DM destination_dm = NULL;
698
699 PetscCheck(destination != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
700 "Derived field '%s' needs a %d-component destination.",
701 field->name, (int)field->components);
702 PetscCall(PicurvStatisticsComponentDM(user, field->components, &destination_dm));
703 PetscCall(VecZeroEntries(destination));
704
705 PetscCall(DMDAVecGetArrayRead(user->da, storage->weight, &weight_arr));
706 PetscCall(DMDAVecGetArrayRead(user->da, storage->weight_sq, &weight_sq_arr));
707 PetscCall(DMDAVecGetArrayRead(user->da, storage->count, &count_arr));
708 PetscCall(DMDAVecGetArrayDOFRead(source_dm, source_vec, &source));
709 PetscCall(DMDAVecGetArrayDOF(destination_dm, destination, &target));
710
711 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
712 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
713 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
715
716 /* A point the window never sampled has no average at all, so it
717 * is left at zero rather than divided by a zero weight. */
718 if (weight_arr[k][j][i] <= 0.0) continue;
719 pair.count = count_arr[k][j][i];
720 pair.weight = weight_arr[k][j][i];
721 pair.weight_sq = weight_sq_arr[k][j][i];
722 pair.mean_x = 0.0;
723 pair.mean_y = 0.0;
724
725 if (kind == DERIVED_MEAN) {
726 for (PetscInt c = 0; c < field->components; ++c) {
727 target[k][j][i][c] = source[k][j][i][c];
728 }
729 } else if (kind == DERIVED_TKE) {
730 /* The trace of the stress tensor, halved: components 0, 3
731 * and 5 are xx, yy and zz in the stored symmetric order. */
732 PetscReal trace = 0.0;
733
734 for (PetscInt c = 0; c < 3; ++c) {
735 pair.cm = source[k][j][i][ProductDiagonalIndex(c)];
736 trace += PicurvCoMomentStateCovariance(&pair);
737 }
738 target[k][j][i][0] = 0.5 * trace;
739 } else if (kind == DERIVED_RMS) {
740 PetscReal deviation = 0.0;
741
742 pair.cm = source[k][j][i][(descriptor->dof == 1)
743 ? 0 : ProductDiagonalIndex(member)];
745 field->name, &deviation));
746 target[k][j][i][0] = deviation;
747 } else if (kind == DERIVED_FLUX) {
748 for (PetscInt c = 0; c < field->components; ++c) {
749 pair.cm = source[k][j][i][c];
750 target[k][j][i][c] = PicurvCoMomentStateCovariance(&pair);
751 }
752 } else {
753 pair.cm = source[k][j][i][member];
754 target[k][j][i][0] = PicurvCoMomentStateCovariance(&pair);
755 }
756 }
757 }
758 }
759
760 PetscCall(DMDAVecRestoreArrayDOF(destination_dm, destination, &target));
761 PetscCall(DMDAVecRestoreArrayDOFRead(source_dm, source_vec, &source));
762 PetscCall(DMDAVecRestoreArrayRead(user->da, storage->count, &count_arr));
763 PetscCall(DMDAVecRestoreArrayRead(user->da, storage->weight_sq, &weight_sq_arr));
764 PetscCall(DMDAVecRestoreArrayRead(user->da, storage->weight, &weight_arr));
765
766 /* Applied once, here, so the VTK field and the convergence-history CSV - which
767 * both read this staging vector - cannot end up in different unit systems. */
768 if (user->simCtx->pps && user->simCtx->pps->dimensionalize) {
769 if (kind != DERIVED_FLUX && descriptor) {
770 PetscReal base = 1.0;
771
772 PetscCall(PicurvFieldReferenceScale(user->simCtx, descriptor->canonical_name,
773 &base, NULL, 0));
774 dimensional_factor = PetscPowRealInt(base, kDerivedKindScaleExponent[kind]);
775 }
776 if (PetscAbsReal(dimensional_factor - 1.0) > PETSC_MACHINE_EPSILON) {
777 PetscCall(VecScale(destination, dimensional_factor));
778 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Scaled derived '%s' by %.4e.\n",
779 kDerivedKindName[kind], (double)dimensional_factor);
780 }
781 }
782 }
784 PetscFunctionReturn(0);
785}
PetscErrorCode PicurvFieldReferenceScale(SimCtx *simCtx, const char *field_name, PetscReal *scale, char *description, size_t description_length)
Physical scale one field is multiplied by to leave non-dimensional form.
Definition io.c:3197
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
static const char *const kDerivedKindName[DERIVED_KIND_COUNT]
Recipe spellings of the output kinds.
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 const PetscInt kDerivedKindScaleExponent[DERIVED_KIND_COUNT]
Power of the source field's reference scale each derived kind carries.
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).
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1076
PetscBool dimensionalize
Whether derived output leaves non-dimensional form, from global_operations.dimensionalize.
Definition variables.h:796
PostProcessParams * pps
Definition variables.h:1057
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 793 of file statistics_accumulator.c.

796{
798 /* Every direction collapses: a window mean is one number for the domain. */
799 const PetscBool whole_domain[3] = {PETSC_TRUE, PETSC_TRUE, PETSC_TRUE};
800
801 PetscFunctionBeginUser;
803 PetscCheck(user != NULL && definition != NULL && storage != NULL && field != NULL && mean != NULL,
804 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
805 "Context, definition, storage, field, and output are required.");
806 *mean = 0.0;
807 if (definition->field_count == 0) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
808
809 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
811 /* No denominator means each admitted point counts once, so the result is the mean
812 * of the field over those points rather than a volume-weighted average. The
813 * accumulated weight is passed as the inclusion mask: a point the window never
814 * sampled holds a zero that means "never measured", and averaging it in would
815 * scale the answer down by the fraction of the domain the window never covered. */
816 PetscCall(PicurvSpatialRatioAverage(user, &plan, field, NULL, storage->weight,
817 whole_domain, PETSC_COMM_WORLD, NULL, mean));
819 PetscFunctionReturn(0);
820}
PetscErrorCode PicurvSpatialRatioAverage(UserCtx *user, const SpatialTargetPlan *plan, Vec numerator, Vec denominator, Vec inclusion, const PetscBool average_direction[3], MPI_Comm comm, Vec ratio, PetscReal *scalar)
Averages two fields over a target domain and divides the results.
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 828 of file statistics_accumulator.c.

831{
833 PetscReal ***nvert = NULL;
834 PetscReal ***count_arr = NULL;
835 PetscReal local_min = PETSC_MAX_REAL;
836 PetscReal local_max = 0.0;
837 PetscReal reduced_min = 0.0, reduced_max = 0.0;
838
839 PetscFunctionBeginUser;
841 PetscCheck(user != NULL && definition != NULL && storage != NULL &&
842 minimum != NULL && maximum != NULL,
843 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Context, definition, storage, and outputs are required.");
844 *minimum = 1.0;
845 *maximum = 0.0;
846 if (definition->field_count == 0 || sample_count <= 0) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
847
848 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
850 PetscCall(DMDAVecGetArrayRead(user->da, user->Nvert, &nvert));
851 PetscCall(DMDAVecGetArrayRead(user->da, storage->count, &count_arr));
852 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
853 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
854 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
855 /* The mask is evaluated at the current state, but a point excluded
856 * now may have contributed earlier, so its stored count is what
857 * decides its fraction rather than its present eligibility. */
858 const PetscReal fraction = count_arr[k][j][i] / (PetscReal)sample_count;
859
860 local_min = PetscMin(local_min, fraction);
861 local_max = PetscMax(local_max, fraction);
862 }
863 }
864 }
865 PetscCall(DMDAVecRestoreArrayRead(user->da, storage->count, &count_arr));
866 PetscCall(DMDAVecRestoreArrayRead(user->da, user->Nvert, &nvert));
867
868 /* A rank owning no targeted point must not drag the minimum down, so its
869 * sentinel is neutral under the reduction rather than zero. */
870 PetscCallMPI(MPI_Allreduce(&local_min, &reduced_min, 1, MPIU_REAL, MPIU_MIN, PETSC_COMM_WORLD));
871 PetscCallMPI(MPI_Allreduce(&local_max, &reduced_max, 1, MPIU_REAL, MPIU_MAX, PETSC_COMM_WORLD));
872 *minimum = (reduced_min == PETSC_MAX_REAL) ? 1.0 : reduced_min;
873 *maximum = reduced_max;
875 PetscFunctionReturn(0);
876}
Vec Nvert
Definition variables.h:1113
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 884 of file statistics_accumulator.c.

886{
888 PetscReal ***nvert = NULL;
889 PetscReal ***count_arr = NULL, ***weight_arr = NULL, ***weight_sq_arr = NULL;
890
891 PetscFunctionBeginUser;
893 PetscCheck(user != NULL && definition != NULL && storage != NULL,
894 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Context, definition, and storage are required.");
895 PetscCheck(weight > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
896 "Accepted states carry a positive weight, got %g.", (double)weight);
897 if (definition->field_count == 0) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
898
899 /* Every requested field shares the pointwise cell-centered domain, so the plan
900 * is resolved once rather than per field. */
901 PetscCall(SpatialTargetPlanCreate(user, (FieldId)definition->fields[0].field_id,
903
904 PetscCall(DMDAVecGetArrayRead(user->da, user->Nvert, &nvert));
905 PetscCall(DMDAVecGetArray(user->da, storage->count, &count_arr));
906 PetscCall(DMDAVecGetArray(user->da, storage->weight, &weight_arr));
907 PetscCall(DMDAVecGetArray(user->da, storage->weight_sq, &weight_sq_arr));
908
909 /* Pass one advances per-point occupancy, so every field pass afterwards can
910 * recover the pre-state weight uniformly as (stored weight - this weight).
911 * Occupancy is per point and per accepted state, never per field. */
912 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
913 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
914 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
915 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) continue;
916 count_arr[k][j][i] += 1.0;
917 weight_arr[k][j][i] += weight;
918 weight_sq_arr[k][j][i] += weight * weight;
919 }
920 }
921 }
922
923 /* Pass two updates every cross-field co-moment. It runs before any mean is
924 * written back, because a co-moment needs the pre-update mean of *both*
925 * members, and pass three overwrites them field by field. */
926 for (PetscInt pair = 0; pair < definition->covariance_count; ++pair) {
927 FieldView view_a, view_b;
928 PetscScalar ****src_a = NULL, ****mean_a = NULL;
929 PetscScalar ****src_b = NULL, ****mean_b = NULL;
930 PetscScalar ****co_moment = NULL;
931 DM pair_dm = NULL;
932 PetscInt slot_a = 0, slot_b = 0, dof_a = 0, dof_b = 0, components = 0;
933
934 PetscCall(FindFieldSlot(definition, definition->covariances[pair].first, &slot_a));
935 PetscCall(FindFieldSlot(definition, definition->covariances[pair].second, &slot_b));
936 PetscCall(FieldGetView(user, (FieldId)definition->covariances[pair].first, &view_a));
937 PetscCall(FieldGetView(user, (FieldId)definition->covariances[pair].second, &view_b));
938 dof_a = view_a.descriptor->dof;
939 dof_b = view_b.descriptor->dof;
940 PetscCall(PicurvCovarianceComponentCount(dof_a, dof_b, &components));
941 PetscCall(PicurvStatisticsComponentDM(user, components, &pair_dm));
942
943 /* A component-indexed view serves every degree of freedom, so the loop
944 * below needs no scalar-versus-vector branching. */
945 PetscCall(DMDAVecGetArrayDOFRead(view_a.dm, view_a.global_vec, &src_a));
946 PetscCall(DMDAVecGetArrayDOFRead(view_a.dm, storage->mean[slot_a], &mean_a));
947 PetscCall(DMDAVecGetArrayDOFRead(view_b.dm, view_b.global_vec, &src_b));
948 PetscCall(DMDAVecGetArrayDOFRead(view_b.dm, storage->mean[slot_b], &mean_b));
949 PetscCall(DMDAVecGetArrayDOF(pair_dm, storage->cm[pair], &co_moment));
950
951 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
952 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
953 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
954 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) continue;
955 for (PetscInt c = 0; c < components; ++c) {
956 /* A scalar member contributes its single value against every
957 * component of a vector member, which is what makes a
958 * vector-scalar covariance a three-component object. */
959 const PetscInt component_a = (dof_a == 3) ? c : 0;
960 const PetscInt component_b = (dof_b == 3) ? c : 0;
962
963 state.count = count_arr[k][j][i] - 1.0;
964 state.weight = weight_arr[k][j][i] - weight;
965 state.weight_sq = weight_sq_arr[k][j][i] - weight * weight;
966 state.mean_x = mean_a[k][j][i][component_a];
967 state.mean_y = mean_b[k][j][i][component_b];
968 state.cm = co_moment[k][j][i][c];
969 PetscCall(PicurvCoMomentStateUpdate(&state,
970 src_a[k][j][i][component_a],
971 src_b[k][j][i][component_b], weight));
972 co_moment[k][j][i][c] = state.cm;
973 }
974 }
975 }
976 }
977
978 PetscCall(DMDAVecRestoreArrayDOF(pair_dm, storage->cm[pair], &co_moment));
979 PetscCall(DMDAVecRestoreArrayDOFRead(view_b.dm, storage->mean[slot_b], &mean_b));
980 PetscCall(DMDAVecRestoreArrayDOFRead(view_b.dm, view_b.global_vec, &src_b));
981 PetscCall(DMDAVecRestoreArrayDOFRead(view_a.dm, storage->mean[slot_a], &mean_a));
982 PetscCall(DMDAVecRestoreArrayDOFRead(view_a.dm, view_a.global_vec, &src_a));
983 }
984
985 /* Pass three updates each requested field's mean and, when asked, its centered
986 * self-product. A three-vector's self-product is the six symmetric co-moments
987 * between component pairs, not three per-component variances, so the co-moment
988 * kernel drives every product component including the diagonal. */
989 for (PetscInt field_index = 0; field_index < definition->field_count; ++field_index) {
990 FieldView view;
991 PetscScalar ****src = NULL, ****mean = NULL, ****product = NULL;
992 DM product_dm = NULL;
993 PetscInt dof = 0, components = 0;
994 const PetscBool second = definition->fields[field_index].want_second;
995
996 PetscCall(FieldGetView(user, (FieldId)definition->fields[field_index].field_id, &view));
997 dof = view.descriptor->dof;
998 PetscCall(DMDAVecGetArrayDOFRead(view.dm, view.global_vec, &src));
999 PetscCall(DMDAVecGetArrayDOF(view.dm, storage->mean[field_index], &mean));
1000 if (second) {
1001 PetscCall(PicurvProductComponentCount(dof, &components));
1002 PetscCall(PicurvStatisticsComponentDM(user, components, &product_dm));
1003 PetscCall(DMDAVecGetArrayDOF(product_dm, storage->m2[field_index], &product));
1004 }
1005
1006 for (PetscInt k = plan.start[2]; k < plan.end[2]; ++k) {
1007 for (PetscInt j = plan.start[1]; j < plan.end[1]; ++j) {
1008 for (PetscInt i = plan.start[0]; i < plan.end[0]; ++i) {
1009 PetscReal prior_weight = 0.0;
1010 PetscReal prior_weight_sq = 0.0;
1011 PetscReal prior_count = 0.0;
1012
1013 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) continue;
1014
1015 prior_weight = weight_arr[k][j][i] - weight;
1016 prior_weight_sq = weight_sq_arr[k][j][i] - weight * weight;
1017 prior_count = count_arr[k][j][i] - 1.0;
1018
1019 /* Products are updated before the means are written back,
1020 * because the co-moment kernel needs the pre-update means. */
1021 for (PetscInt c = 0; c < components; ++c) {
1022 const PetscInt a = (dof == 1) ? 0 : kProductFirst[c];
1023 const PetscInt b = (dof == 1) ? 0 : kProductSecond[c];
1024 PicurvCoMomentState state;
1025
1026 state.count = prior_count;
1027 state.weight = prior_weight;
1028 state.weight_sq = prior_weight_sq;
1029 state.mean_x = mean[k][j][i][a];
1030 state.mean_y = mean[k][j][i][b];
1031 state.cm = product[k][j][i][c];
1032 PetscCall(PicurvCoMomentStateUpdate(&state, src[k][j][i][a],
1033 src[k][j][i][b], weight));
1034 product[k][j][i][c] = state.cm;
1035 }
1036
1037 for (PetscInt c = 0; c < dof; ++c) {
1038 PicurvMomentState moment;
1039
1040 moment.count = prior_count;
1041 moment.weight = prior_weight;
1042 moment.weight_sq = prior_weight_sq;
1043 moment.mean = mean[k][j][i][c];
1044 moment.m2 = 0.0;
1045 PetscCall(PicurvMomentStateUpdate(&moment, src[k][j][i][c], weight));
1046 mean[k][j][i][c] = moment.mean;
1047 }
1048 }
1049 }
1050 }
1051
1052 if (second) PetscCall(DMDAVecRestoreArrayDOF(product_dm, storage->m2[field_index], &product));
1053 PetscCall(DMDAVecRestoreArrayDOF(view.dm, storage->mean[field_index], &mean));
1054 PetscCall(DMDAVecRestoreArrayDOFRead(view.dm, view.global_vec, &src));
1055 }
1056
1057 PetscCall(DMDAVecRestoreArray(user->da, storage->weight_sq, &weight_sq_arr));
1058 PetscCall(DMDAVecRestoreArray(user->da, storage->weight, &weight_arr));
1059 PetscCall(DMDAVecRestoreArray(user->da, storage->count, &count_arr));
1060 PetscCall(DMDAVecRestoreArrayRead(user->da, user->Nvert, &nvert));
1062 PetscFunctionReturn(0);
1063}
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: