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

Configured initial values of particle-carried fields, and the expression language that defines them. More...

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

Go to the source code of this file.

Data Structures

struct  PicurvExpressionDrawKey
 What a random draw is keyed on, besides the particle and its stream. More...
 
struct  ParticleFieldEvent
 When and why a plan is applied. More...
 

Typedefs

typedef struct PicurvExpression PicurvExpression
 A compiled expression: postfix code for a small stack evaluator.
 
typedef struct ParticleFieldPlan ParticleFieldPlan
 Compiled initial values for every configured particle field.
 

Functions

PetscErrorCode PicurvExpressionCompile (const char *text, const char *const *names, PetscInt name_count, PetscBool allow_random, PicurvExpression **expression)
 Compile one expression against a list of variable names.
 
PetscErrorCode PicurvExpressionEvaluate (const PicurvExpression *expression, const PetscReal *values, const PicurvExpressionDrawKey *key, PetscReal *result)
 Evaluate a compiled expression.
 
PetscErrorCode PicurvExpressionDestroy (PicurvExpression **expression)
 Free a compiled expression.
 
PetscErrorCode ParticleFieldPlanCreate (ParticleFieldPlan **plan)
 Read a plan from the options database.
 
PetscErrorCode ParticleFieldPlanApply (UserCtx *user, const ParticleFieldPlan *plan, const ParticleFieldEvent *event, PetscInt first, PetscInt end)
 Write the plan's values into swarm entries [first, end) of this rank.
 
PetscErrorCode ParticleFieldPlanSummarize (UserCtx *user, const ParticleFieldPlan *plan)
 Log and record the realized initial values of every planned field.
 
PetscErrorCode ParticleFieldPlanDestroy (ParticleFieldPlan **plan)
 Free a plan and its compiled expressions.
 

Detailed Description

Configured initial values of particle-carried fields, and the expression language that defines them.

A plan binds each configured particle field to one compiled expression per component. Expressions are written in physical units: x, y, z are physical coordinates, t is physical time, and a value is divided by its field's catalog reference scale when written. The plan is applied to a contiguous local range of swarm entries, so the t=0 population and, later, particles injected at a boundary share one path.

The language is the one generators/ic.gen validates and evaluates for Eulerian expressions; tests/fixtures/expression_conformance.txt holds cases both implementations must agree on.

Definition in file ParticleInitialConditions.h.


Data Structure Documentation

◆ PicurvExpressionDrawKey

struct PicurvExpressionDrawKey

What a random draw is keyed on, besides the particle and its stream.

Definition at line 31 of file ParticleInitialConditions.h.

Data Fields
PetscInt64 seed Run seed (-particle_random_seed).
PetscInt64 pid Particle identity; draws are pure functions of it.
PetscInt64 salt Event that produced the particle: 0 for the t=0 population.
PetscInt64 field Field whose expression draws, the default stream's namespace.

◆ ParticleFieldEvent

struct ParticleFieldEvent

When and why a plan is applied.

Definition at line 79 of file ParticleInitialConditions.h.

Data Fields
PetscReal physical_time Time, in seconds, the evaluated particles appear at.
PetscInt64 salt Event identity keying random draws: 0 for the t=0 population.

Typedef Documentation

◆ PicurvExpression

A compiled expression: postfix code for a small stack evaluator.

Definition at line 28 of file ParticleInitialConditions.h.

◆ ParticleFieldPlan

Compiled initial values for every configured particle field.

Definition at line 76 of file ParticleInitialConditions.h.

Function Documentation

◆ PicurvExpressionCompile()

PetscErrorCode PicurvExpressionCompile ( const char *  text,
const char *const *  names,
PetscInt  name_count,
PetscBool  allow_random,
PicurvExpression **  expression 
)

Compile one expression against a list of variable names.

The grammar is Python's expression syntax restricted to numbers, the given names, pi, arithmetic (+ - * / % **), comparisons (chained ones included), and/or/not, and the functions abs cos exp maximum minimum sin sqrt tan where, plus uniform and normal when allow_random is true. Comparisons and logical operators yield 1 or 0; % takes the sign of the divisor; ** binds tighter than a unary minus on its left and groups to the right.

Parameters
[in]textExpression text.
[in]namesVariable names, in the order evaluation supplies their values.
[in]name_countNumber of names.
[in]allow_randomWhether uniform and normal may be called.
[out]expressionCompiled expression, owned by the caller.
Returns
Zero on success; PETSC_ERR_ARG_WRONG for a syntax error or an unknown name or function, with the offending position in the message.

Compile one expression against a list of variable names.

See also
PicurvExpressionCompile()

Definition at line 385 of file ParticleInitialConditions.c.

387{
388 ExpressionParser parser;
389 PetscInt root, depth = 0;
390
391 PetscFunctionBeginUser;
392 PetscCheck(text && expression, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Expression text and output are required.");
393 PetscCall(PetscMemzero(&parser, sizeof(parser)));
394 parser.text = text;
395 parser.names = names;
396 parser.name_count = name_count;
397 parser.allow_random = allow_random;
398 parser.capacity = 4 * (PetscInt)strlen(text) + 8;
399 PetscCall(PetscMalloc1(parser.capacity, &parser.nodes));
400
401 root = ParseOr(&parser);
402 SkipSpace(&parser);
403 if (!parser.error[0] && parser.text[parser.pos]) ParserFail(&parser, "unexpected text");
404 if (parser.error[0]) {
405 PetscCall(PetscFree(parser.nodes));
406 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid expression: %s.", parser.error);
407 }
408
409 PetscCall(PetscNew(expression));
410 (*expression)->length = 0;
411 PetscCall(PetscMalloc1(CountCode(&parser, root), &(*expression)->code));
412 EmitNode(&parser, root, *expression, &depth);
413 PetscCall(PetscFree(parser.nodes));
414 PetscFunctionReturn(0);
415}
static void EmitNode(const ExpressionParser *parser, PetscInt index, PicurvExpression *expression, PetscInt *depth)
Emit postfix code for a subtree and track the stack depth it needs.
static void SkipSpace(ExpressionParser *parser)
Skip whitespace before the next token.
static PetscInt CountCode(const ExpressionParser *parser, PetscInt index)
Count the instructions a subtree emits (shared subtrees count per use).
const char *const * names
static void ParserFail(ExpressionParser *parser, const char *message)
Record the first parse error with its position; later ones are consequences.
static PetscInt ParseOr(ExpressionParser *parser)
and ('or' and)*
Parser state: the text, a cursor, the node pool, and the names in scope.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvExpressionEvaluate()

PetscErrorCode PicurvExpressionEvaluate ( const PicurvExpression *  expression,
const PetscReal *  values,
const PicurvExpressionDrawKey *  key,
PetscReal *  result 
)

Evaluate a compiled expression.

Parameters
[in]expressionCompiled expression.
[in]valuesOne value per compiled name, in compile order.
[in]keyDraw key, required when the expression draws; may be NULL otherwise.
[out]resultValue of the expression; may be non-finite, which the caller judges.
Returns
Zero on success.

Evaluate a compiled expression.

See also
PicurvExpressionEvaluate()

Definition at line 463 of file ParticleInitialConditions.c.

465{
466 PetscReal stack[expression->max_depth + 1];
467 PetscInt top = 0;
468
469 PetscFunctionBeginUser;
470 PetscCheck(!expression->draws || key, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
471 "An expression that draws random numbers needs a draw key.");
472 for (PetscInt i = 0; i < expression->length; ++i) {
473 const ExpressionInstruction *instruction = &expression->code[i];
474 PetscReal a, b, c;
475
476 switch (instruction->op) {
477 case OP_CONST: stack[top++] = instruction->value; break;
478 case OP_VAR: stack[top++] = values[instruction->arg]; break;
479 case OP_UNIFORM:
480 case OP_NORMAL: stack[top++] = Draw(key, instruction); break;
481 case OP_NEG: stack[top - 1] = -stack[top - 1]; break;
482 case OP_POS: break;
483 case OP_NOT: stack[top - 1] = (stack[top - 1] == 0.0) ? 1.0 : 0.0; break;
484 case OP_ABS: stack[top - 1] = PetscAbsReal(stack[top - 1]); break;
485 case OP_COS: stack[top - 1] = PetscCosReal(stack[top - 1]); break;
486 case OP_EXP: stack[top - 1] = PetscExpReal(stack[top - 1]); break;
487 case OP_SIN: stack[top - 1] = PetscSinReal(stack[top - 1]); break;
488 case OP_SQRT: stack[top - 1] = PetscSqrtReal(stack[top - 1]); break;
489 case OP_TAN: stack[top - 1] = PetscTanReal(stack[top - 1]); break;
490 case OP_WHERE:
491 c = stack[--top]; b = stack[--top]; a = stack[top - 1];
492 stack[top - 1] = (a != 0.0) ? b : c;
493 break;
494 default:
495 b = stack[--top]; a = stack[top - 1];
496 switch (instruction->op) {
497 case OP_ADD: a = a + b; break;
498 case OP_SUB: a = a - b; break;
499 case OP_MUL: a = a * b; break;
500 case OP_DIV: a = a / b; break;
501 /* Python's modulus takes the sign of the divisor. */
502 case OP_MOD: a = (b == 0.0) ? NAN : a - b * PetscFloorReal(a / b); break;
503 case OP_POW: a = PetscPowReal(a, b); break;
504 case OP_EQ: a = (a == b) ? 1.0 : 0.0; break;
505 case OP_NE: a = (a != b) ? 1.0 : 0.0; break;
506 case OP_LT: a = (a < b) ? 1.0 : 0.0; break;
507 case OP_LE: a = (a <= b) ? 1.0 : 0.0; break;
508 case OP_GT: a = (a > b) ? 1.0 : 0.0; break;
509 case OP_GE: a = (a >= b) ? 1.0 : 0.0; break;
510 case OP_AND: a = (a != 0.0 && b != 0.0) ? 1.0 : 0.0; break;
511 case OP_OR: a = (a != 0.0 || b != 0.0) ? 1.0 : 0.0; break;
512 case OP_MAXIMUM: a = Extreme(a, b, PETSC_TRUE); break;
513 case OP_MINIMUM: a = Extreme(a, b, PETSC_FALSE); break;
514 default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unknown expression opcode %d.", (int)instruction->op);
515 }
516 stack[top - 1] = a;
517 }
518 }
519 *result = stack[0];
520 PetscFunctionReturn(0);
521}
ExpressionInstruction * code
static PetscReal Draw(const PicurvExpressionDrawKey *key, const ExpressionInstruction *instruction)
A draw keyed on the particle, the event, the stream, and the function.
static PetscReal Extreme(PetscReal a, PetscReal b, PetscBool maximum)
Maximum or minimum that propagates NaN, as numpy does.
One postfix instruction.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvExpressionDestroy()

PetscErrorCode PicurvExpressionDestroy ( PicurvExpression **  expression)

Free a compiled expression.

Parameters
[in,out]expressionExpression to free; set to NULL.
Returns
Zero on success.

Free a compiled expression.

See also
PicurvExpressionDestroy()

Definition at line 527 of file ParticleInitialConditions.c.

528{
529 PetscFunctionBeginUser;
530 if (!expression || !*expression) PetscFunctionReturn(0);
531 PetscCall(PetscFree((*expression)->code));
532 PetscCall(PetscFree(*expression));
533 PetscFunctionReturn(0);
534}
Here is the caller graph for this function:

◆ ParticleFieldPlanCreate()

PetscErrorCode ParticleFieldPlanCreate ( ParticleFieldPlan **  plan)

Read a plan from the options database.

Reads -particle_fields_count, and for each field i, -particle_fields_<i>_name and one expression per component, -particle_fields_<i>_expr_<c>. Every named field must be a particle-carried field of the catalog (capability USER_INITIALIZE).

Parameters
[out]planCompiled plan, or NULL when no field is configured.
Returns
Zero on success; PETSC_ERR_ARG_WRONG for an unknown or non-settable field or a malformed expression.

Read a plan from the options database.

See also
ParticleFieldPlanCreate()

Definition at line 563 of file ParticleInitialConditions.c.

564{
565 PetscInt count = 0;
566 PetscBool found = PETSC_FALSE;
567 char option[256];
568
569 PetscFunctionBeginUser;
570 PetscCheck(plan, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Plan output is required.");
571 *plan = NULL;
572 PetscCall(PetscSNPrintf(option, sizeof(option), "-particle_fields_count"));
573 PetscCall(PetscOptionsGetInt(NULL, NULL, option, &count, &found));
574 if (!found || count == 0) PetscFunctionReturn(0);
575 PetscCheck(count > 0 && count <= PARTICLE_FIELD_ID_COUNT, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
576 "%s must be between 0 and %d (got %" PetscInt_FMT ").", option, (int)PARTICLE_FIELD_ID_COUNT, count);
577
578 PetscCall(PetscNew(plan));
579 PetscCall(PetscCalloc1(count, &(*plan)->bindings));
580 (*plan)->count = count;
581 for (PetscInt i = 0; i < count; ++i) {
582 ParticleFieldBinding *binding = &(*plan)->bindings[i];
583 const ParticleFieldDescriptor *descriptor = NULL;
584 char name[64];
585
586 PetscCall(PetscSNPrintf(option, sizeof(option), "-particle_fields_%" PetscInt_FMT "_name", i));
587 PetscCall(PetscOptionsGetString(NULL, NULL, option, name, sizeof(name), &found));
588 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "%s is missing.", option);
589 PetscCall(ParticleFieldIdFromName(name, &binding->field));
590 PetscCall(ParticleFieldGetDescriptor(binding->field, &descriptor));
591 PetscCheck(descriptor->capabilities & PARTICLE_FIELD_CAPABILITY_USER_INITIALIZE, PETSC_COMM_WORLD,
592 PETSC_ERR_ARG_WRONG,
593 "Particle field '%s' cannot be given an initial value: the runtime sets it from the "
594 "Eulerian fields or from particle location.", name);
595 PetscCheck(descriptor->dimension.kind == FIELD_DIMENSION_FIXED && descriptor->data_type == PETSC_REAL,
596 PETSC_COMM_WORLD, PETSC_ERR_PLIB, "Settable particle field '%s' must be a real quantity.", name);
597 for (PetscInt j = 0; j < i; ++j) {
598 PetscCheck((*plan)->bindings[j].field != binding->field, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
599 "Particle field '%s' is configured twice.", name);
600 }
601 binding->components = descriptor->components;
602 PetscCheck(binding->components <= PARTICLE_FIELD_MAX_COMPONENTS, PETSC_COMM_WORLD, PETSC_ERR_PLIB,
603 "Particle field '%s' has more components than an initial value supports.", name);
604 for (PetscInt c = 0; c < binding->components; ++c) {
606
607 PetscCall(PetscSNPrintf(option, sizeof(option), "-particle_fields_%" PetscInt_FMT "_expr_%" PetscInt_FMT, i, c));
608 PetscCall(PetscOptionsGetString(NULL, NULL, option, text, sizeof(text), &found));
609 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "%s is missing.", option);
611 &binding->component[c]));
612 }
613 }
614 PetscFunctionReturn(0);
615}
static const char *const kParticleVariables[]
Names a particle expression may use, in the order their values are supplied.
#define PARTICLE_VARIABLE_COUNT
#define PARTICLE_FIELD_MAX_COMPONENTS
PicurvExpression * component[3]
PetscErrorCode PicurvExpressionCompile(const char *text, const char *const *names, PetscInt name_count, PetscBool allow_random, PicurvExpression **expression)
Implementation of PicurvExpressionCompile().
#define PARTICLE_FIELD_EXPRESSION_LENGTH
One configured field: its identity and one expression per component.
@ FIELD_DIMENSION_FIXED
The exponents are the field's dimension.
FieldDimensionKind kind
@ PARTICLE_FIELD_ID_COUNT
PetscErrorCode ParticleFieldIdFromName(const char *field_name, ParticleFieldId *field_id)
Resolve a canonical name or registered alias at a text-ingress boundary.
@ PARTICLE_FIELD_CAPABILITY_USER_INITIALIZE
The particle carries the value, so a configured initial value survives: nothing re-derives it from th...
PetscErrorCode ParticleFieldGetDescriptor(ParticleFieldId field_id, const ParticleFieldDescriptor **descriptor)
Return immutable metadata for a valid particle field ID.
Immutable metadata for one persistent particle field.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParticleFieldPlanApply()

PetscErrorCode ParticleFieldPlanApply ( UserCtx *  user,
const ParticleFieldPlan *  plan,
const ParticleFieldEvent *  event,
PetscInt  first,
PetscInt  end 
)

Write the plan's values into swarm entries [first, end) of this rank.

Not collective: each rank evaluates its own entries, and the domain bounds for xn, yn, zn come from the replicated rank bounding boxes. A value that is not finite is an error naming the particle and its position.

Parameters
[in,out]userBlock owning the swarm.
[in]planPlan to apply; NULL does nothing.
[in]eventTime and identity of the event producing these particles.
[in]firstFirst local entry.
[in]endOne past the last local entry.
Returns
Zero on success.

Write the plan's values into swarm entries [first, end) of this rank.

See also
ParticleFieldPlanApply()

Definition at line 649 of file ParticleInitialConditions.c.

651{
652 SimCtx *simCtx = NULL;
653 const PetscReal *positions = NULL;
654 const PetscInt64 *pids = NULL;
655 Cmpnts lower, upper;
656
657 PetscFunctionBeginUser;
658 if (!plan || end <= first) PetscFunctionReturn(0);
659 PetscCheck(user && user->swarm && event, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Swarm and event are required.");
660 simCtx = user->simCtx;
661 PetscCall(DomainBounds(simCtx, &lower, &upper));
662
663 PetscCall(DMSwarmGetField(user->swarm, ParticleFieldName(PARTICLE_FIELD_ID_POSITION), NULL, NULL, (void **)&positions));
664 PetscCall(DMSwarmGetField(user->swarm, ParticleFieldName(PARTICLE_FIELD_ID_PID), NULL, NULL, (void **)&pids));
665 for (PetscInt b = 0; b < plan->count; ++b) {
666 const ParticleFieldBinding *binding = &plan->bindings[b];
667 const ParticleFieldDescriptor *descriptor = NULL;
668 PetscReal *field = NULL, scale = 1.0;
669
670 PetscCall(ParticleFieldGetDescriptor(binding->field, &descriptor));
671 /* A value is physical; the swarm stores solver units. */
672 PetscCall(FieldDimensionReferenceScale(&simCtx->scaling, descriptor->dimension, &scale));
673 PetscCall(DMSwarmGetField(user->swarm, descriptor->canonical_name, NULL, NULL, (void **)&field));
674 for (PetscInt p = first; p < end; ++p) {
675 const PetscReal *position = &positions[3 * p];
676 const PetscReal values[] = {
677 position[0] * simCtx->scaling.L_ref, position[1] * simCtx->scaling.L_ref,
678 position[2] * simCtx->scaling.L_ref,
679 Normalized(position[0], lower.x, upper.x), Normalized(position[1], lower.y, upper.y),
680 Normalized(position[2], lower.z, upper.z),
681 (PetscReal)pids[p], event->physical_time,
682 };
683 const PicurvExpressionDrawKey key = {
684 (PetscInt64)simCtx->particleRandomSeed, pids[p], event->salt, (PetscInt64)binding->field,
685 };
686 for (PetscInt c = 0; c < binding->components; ++c) {
687 PetscReal value = 0.0;
688
689 PetscCall(PicurvExpressionEvaluate(binding->component[c], values, &key, &value));
690 PetscCheck(!PetscIsInfOrNanReal(value), PETSC_COMM_SELF, PETSC_ERR_FP,
691 "The initial value of '%s' is not finite for particle %lld at (%g, %g, %g).",
692 descriptor->canonical_name, (long long)pids[p], (double)values[0],
693 (double)values[1], (double)values[2]);
694 field[binding->components * p + c] = value / scale;
695 }
696 }
697 PetscCall(DMSwarmRestoreField(user->swarm, descriptor->canonical_name, NULL, NULL, (void **)&field));
698 }
699 PetscCall(DMSwarmRestoreField(user->swarm, ParticleFieldName(PARTICLE_FIELD_ID_PID), NULL, NULL, (void **)&pids));
700 PetscCall(DMSwarmRestoreField(user->swarm, ParticleFieldName(PARTICLE_FIELD_ID_POSITION), NULL, NULL, (void **)&positions));
701 PetscFunctionReturn(0);
702}
static PetscErrorCode DomainBounds(const SimCtx *simCtx, Cmpnts *lower, Cmpnts *upper)
Global domain bounds, in solver units, from the replicated rank bounding boxes.
PetscErrorCode PicurvExpressionEvaluate(const PicurvExpression *expression, const PetscReal *values, const PicurvExpressionDrawKey *key, PetscReal *result)
Implementation of PicurvExpressionEvaluate().
ParticleFieldBinding * bindings
static PetscReal Normalized(PetscReal value, PetscReal lower, PetscReal upper)
Position within the domain along one axis, in [0, 1]; 0 across a flat axis.
PetscInt64 salt
Event identity keying random draws: 0 for the t=0 population.
PetscReal physical_time
Time, in seconds, the evaluated particles appear at.
What a random draw is keyed on, besides the particle and its stream.
PetscErrorCode FieldDimensionReferenceScale(const ScalingCtx *scaling, FieldDimension dimension, PetscReal *scale)
Return the factor that turns a solver value of one dimension into physical units.
const char * ParticleFieldName(ParticleFieldId field_id)
Return the canonical PETSc DMSwarm name for an ID.
@ PARTICLE_FIELD_ID_POSITION
@ PARTICLE_FIELD_ID_PID
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1074
PetscReal L_ref
Definition variables.h:846
PetscScalar x
Definition variables.h:122
PetscScalar z
Definition variables.h:122
ScalingCtx scaling
Definition variables.h:952
PetscInt particleRandomSeed
Base seed for every particle RNG stream (-particle_random_seed).
Definition variables.h:993
PetscScalar y
Definition variables.h:122
A 3D point or vector with PetscScalar components.
Definition variables.h:121
The master context for the entire simulation.
Definition variables.h:864
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParticleFieldPlanSummarize()

PetscErrorCode ParticleFieldPlanSummarize ( UserCtx *  user,
const ParticleFieldPlan *  plan 
)

Log and record the realized initial values of every planned field.

Collective. Reports count, mean, variance, minimum, maximum, and a ten-bin histogram per field component, in physical units, to the log and to particle_initial_fields.csv in the run's metrics directory.

Parameters
[in]userBlock owning the swarm.
[in]planPlan whose fields are summarized; NULL does nothing.
Returns
Zero on success.

Log and record the realized initial values of every planned field.

See also
ParticleFieldPlanSummarize()

Definition at line 708 of file ParticleInitialConditions.c.

709{
710 SimCtx *simCtx = NULL;
711 FILE *csv = NULL;
712 PetscInt nlocal = 0;
713
714 PetscFunctionBeginUser;
715 if (!plan) PetscFunctionReturn(0);
716 simCtx = user->simCtx;
717 PetscCall(DMSwarmGetLocalSize(user->swarm, &nlocal));
718 if (simCtx->rank == 0) {
719 char path[PETSC_MAX_PATH_LEN];
720
721 PetscCall(PetscSNPrintf(path, sizeof(path), "%s/particle_initial_fields.csv", simCtx->analysis_dir));
722 csv = fopen(path, "w");
723 if (!csv) {
724 LOG_ALLOW(GLOBAL, LOG_WARNING, "Could not write '%s'.\n", path);
725 } else {
726 fprintf(csv, "field,component,count,mean,variance,min,max");
727 for (int bin = 0; bin < PARTICLE_FIELD_HISTOGRAM_BINS; ++bin) fprintf(csv, ",bin_%d", bin);
728 fprintf(csv, "\n");
729 }
730 }
731
732 for (PetscInt b = 0; b < plan->count; ++b) {
733 const ParticleFieldBinding *binding = &plan->bindings[b];
734 const ParticleFieldDescriptor *descriptor = NULL;
735 const PetscReal *field = NULL;
736 PetscReal scale = 1.0;
737
738 PetscCall(ParticleFieldGetDescriptor(binding->field, &descriptor));
739 PetscCall(FieldDimensionReferenceScale(&simCtx->scaling, descriptor->dimension, &scale));
740 PetscCall(DMSwarmGetField(user->swarm, descriptor->canonical_name, NULL, NULL, (void **)&field));
741 for (PetscInt c = 0; c < binding->components; ++c) {
742 /* Sums and extremes in one reduction, then a histogram over the global range. */
743 PetscReal sums[3] = {0.0, 0.0, 0.0}, extremes[2] = {PETSC_MAX_REAL, PETSC_MAX_REAL};
744 PetscReal bins_local[PARTICLE_FIELD_HISTOGRAM_BINS] = {0.0}, bins[PARTICLE_FIELD_HISTOGRAM_BINS];
745
746 for (PetscInt p = 0; p < nlocal; ++p) {
747 const PetscReal value = field[binding->components * p + c] * scale;
748 sums[0] += 1.0;
749 sums[1] += value;
750 sums[2] += value * value;
751 extremes[0] = PetscMin(extremes[0], value);
752 extremes[1] = PetscMin(extremes[1], -value);
753 }
754 PetscCallMPI(MPI_Allreduce(MPI_IN_PLACE, sums, 3, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD));
755 PetscCallMPI(MPI_Allreduce(MPI_IN_PLACE, extremes, 2, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD));
756 const PetscReal count = sums[0], lowest = extremes[0], highest = -extremes[1];
757 const PetscReal mean = (count > 0.0) ? sums[1] / count : 0.0;
758 const PetscReal variance = (count > 0.0) ? PetscMax(sums[2] / count - mean * mean, 0.0) : 0.0;
759 const PetscReal width = (highest > lowest) ? (highest - lowest) / PARTICLE_FIELD_HISTOGRAM_BINS : 1.0;
760
761 for (PetscInt p = 0; p < nlocal; ++p) {
762 const PetscReal value = field[binding->components * p + c] * scale;
763 PetscInt bin = (PetscInt)((value - lowest) / width);
764 bins_local[PetscMax(0, PetscMin(bin, PARTICLE_FIELD_HISTOGRAM_BINS - 1))] += 1.0;
765 }
766 PetscCallMPI(MPI_Allreduce(bins_local, bins, PARTICLE_FIELD_HISTOGRAM_BINS, MPIU_REAL, MPI_SUM,
767 PETSC_COMM_WORLD));
769 "[Particle IC] %s[%" PetscInt_FMT "]: n=%.0f mean=%.6g var=%.6g min=%.6g max=%.6g\n",
770 descriptor->canonical_name, c, (double)count, (double)mean, (double)variance,
771 (double)(count > 0.0 ? lowest : 0.0), (double)(count > 0.0 ? highest : 0.0));
772 if (csv) {
773 fprintf(csv, "%s,%d,%.0f,%.10e,%.10e,%.10e,%.10e", descriptor->canonical_name, (int)c,
774 (double)count, (double)mean, (double)variance,
775 (double)(count > 0.0 ? lowest : 0.0), (double)(count > 0.0 ? highest : 0.0));
776 for (int bin = 0; bin < PARTICLE_FIELD_HISTOGRAM_BINS; ++bin) fprintf(csv, ",%.0f", (double)bins[bin]);
777 fprintf(csv, "\n");
778 }
779 }
780 PetscCall(DMSwarmRestoreField(user->swarm, descriptor->canonical_name, NULL, NULL, (void **)&field));
781 }
782 if (csv) fclose(csv);
783 PetscFunctionReturn(0);
784}
#define PARTICLE_FIELD_HISTOGRAM_BINS
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
PetscMPIInt rank
Definition variables.h:867
char analysis_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:888
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParticleFieldPlanDestroy()

PetscErrorCode ParticleFieldPlanDestroy ( ParticleFieldPlan **  plan)

Free a plan and its compiled expressions.

Parameters
[in,out]planPlan to free; set to NULL.
Returns
Zero on success.

Free a plan and its compiled expressions.

See also
ParticleFieldPlanDestroy()

Definition at line 790 of file ParticleInitialConditions.c.

791{
792 PetscFunctionBeginUser;
793 if (!plan || !*plan) PetscFunctionReturn(0);
794 for (PetscInt b = 0; b < (*plan)->count; ++b) {
795 for (PetscInt c = 0; c < PARTICLE_FIELD_MAX_COMPONENTS; ++c) {
796 PetscCall(PicurvExpressionDestroy(&(*plan)->bindings[b].component[c]));
797 }
798 }
799 PetscCall(PetscFree((*plan)->bindings));
800 PetscCall(PetscFree(*plan));
801 PetscFunctionReturn(0);
802}
PetscErrorCode PicurvExpressionDestroy(PicurvExpression **expression)
Implementation of PicurvExpressionDestroy().
Here is the call graph for this function:
Here is the caller graph for this function: