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

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

#include "ParticleInitialConditions.h"
#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "logging.h"
Include dependency graph for ParticleInitialConditions.c:

Go to the source code of this file.

Data Structures

struct  ExpressionNode
 One tree node; children index the node pool. More...
 
struct  ExpressionInstruction
 One postfix instruction. More...
 
struct  PicurvExpression
 
struct  ExpressionParser
 Parser state: the text, a cursor, the node pool, and the names in scope. More...
 
struct  ParticleFieldBinding
 One configured field: its identity and one expression per component. More...
 
struct  ParticleFieldPlan
 

Macros

#define PARTICLE_VARIABLE_COUNT   ((PetscInt)(sizeof(kParticleVariables) / sizeof(kParticleVariables[0])))
 
#define PARTICLE_FIELD_MAX_COMPONENTS   3
 
#define PARTICLE_FIELD_EXPRESSION_LENGTH   4096
 
#define PARTICLE_FIELD_HISTOGRAM_BINS   10
 

Enumerations

enum  ExpressionOp {
  OP_CONST = 0 , OP_VAR , OP_NEG , OP_POS ,
  OP_NOT , OP_ADD , OP_SUB , OP_MUL ,
  OP_DIV , OP_MOD , OP_POW , OP_EQ ,
  OP_NE , OP_LT , OP_LE , OP_GT ,
  OP_GE , OP_AND , OP_OR , OP_ABS ,
  OP_COS , OP_EXP , OP_SIN , OP_SQRT ,
  OP_TAN , OP_MAXIMUM , OP_MINIMUM , OP_WHERE ,
  OP_UNIFORM , OP_NORMAL
}
 

Functions

static void ParserFail (ExpressionParser *parser, const char *message)
 Record the first parse error with its position; later ones are consequences.
 
static void SkipSpace (ExpressionParser *parser)
 Skip whitespace before the next token.
 
static PetscBool Accept (ExpressionParser *parser, const char *token)
 Consume token if it is next, and report whether it was.
 
static PetscInt NewNode (ExpressionParser *parser, ExpressionOp op, PetscInt nchild, PetscInt a, PetscInt b, PetscInt c)
 Append a node to the pool and return its index, or -1 once an error is set.
 
static PetscInt ParseOr (ExpressionParser *parser)
 and ('or' and)*
 
static PetscInt ParseAtom (ExpressionParser *parser)
 number | name | name(args) | (expression)
 
static PetscInt ParseUnary (ExpressionParser *parser)
 Parses a sign prefix recursively and wraps the operand in a negate or identity node.
 
static PetscInt ParsePower (ExpressionParser *parser)
 Parses an atom and, when a power operator follows, appends a power node.
 
static PetscInt ParseTerm (ExpressionParser *parser)
 Parses a left-associative chain of *, /, %, folding each operator into a node.
 
static PetscInt ParseSum (ExpressionParser *parser)
 Parses a left-associative chain of + and -, folding each operator into a node.
 
static PetscInt ParseComparison (ExpressionParser *parser)
 sum (comparison sum)*; a chain a < b < c is (a < b) and (b < c).
 
static PetscInt ParseNot (ExpressionParser *parser)
 'not' not | comparison
 
static PetscInt ParseAnd (ExpressionParser *parser)
 not ('and' not)*
 
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 PetscInt CountCode (const ExpressionParser *parser, PetscInt index)
 Count the instructions a subtree emits (shared subtrees count per use).
 
PetscErrorCode PicurvExpressionCompile (const char *text, const char *const *names, PetscInt name_count, PetscBool allow_random, PicurvExpression **expression)
 Implementation of PicurvExpressionCompile().
 
static uint64_t Mix64 (uint64_t z)
 splitmix64 finalizer: a bijective mix of 64 bits.
 
static PetscReal UnitFromHash (uint64_t hash)
 Uniform on [0, 1) from the top 53 bits of a hash.
 
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.
 
PetscErrorCode PicurvExpressionEvaluate (const PicurvExpression *expression, const PetscReal *values, const PicurvExpressionDrawKey *key, PetscReal *result)
 Implementation of PicurvExpressionEvaluate().
 
PetscErrorCode PicurvExpressionDestroy (PicurvExpression **expression)
 Implementation of PicurvExpressionDestroy().
 
PetscErrorCode ParticleFieldPlanCreate (ParticleFieldPlan **plan)
 Implementation of ParticleFieldPlanCreate().
 
static PetscErrorCode DomainBounds (const SimCtx *simCtx, Cmpnts *lower, Cmpnts *upper)
 Global domain bounds, in solver units, from the replicated rank bounding boxes.
 
static PetscReal Normalized (PetscReal value, PetscReal lower, PetscReal upper)
 Position within the domain along one axis, in [0, 1]; 0 across a flat axis.
 
PetscErrorCode ParticleFieldPlanApply (UserCtx *user, const ParticleFieldPlan *plan, const ParticleFieldEvent *event, PetscInt first, PetscInt end)
 Implementation of ParticleFieldPlanApply().
 
PetscErrorCode ParticleFieldPlanSummarize (UserCtx *user, const ParticleFieldPlan *plan)
 Implementation of ParticleFieldPlanSummarize().
 
PetscErrorCode ParticleFieldPlanDestroy (ParticleFieldPlan **plan)
 Implementation of ParticleFieldPlanDestroy().
 

Variables

struct { 
 
const char * name
 
int min_args
 
int max_args
 
ExpressionOp op
 
PetscBool random
 
} kFunctions [] 
 Name, arity, and opcode of every callable function.
 
static const char *const kParticleVariables [] = {"x", "y", "z", "xn", "yn", "zn", "pid", "t"}
 Names a particle expression may use, in the order their values are supplied.
 

Detailed Description

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

Expressions are parsed into a small tree, because a chained comparison evaluates its middle operand twice, and compiled to postfix code that a stack evaluator runs once per particle. Evaluation is pure: random draws are hashed from the particle's identity, so a value does not depend on rank count, particle order, or how often it is evaluated.

Definition in file ParticleInitialConditions.c.


Data Structure Documentation

◆ ExpressionNode

struct ExpressionNode

One tree node; children index the node pool.

Definition at line 51 of file ParticleInitialConditions.c.

Data Fields
ExpressionOp op
PetscReal value
PetscInt var
PetscInt child[3]
PetscInt nchild

◆ ExpressionInstruction

struct ExpressionInstruction

One postfix instruction.

Definition at line 60 of file ParticleInitialConditions.c.

Data Fields
ExpressionOp op
PetscReal value
PetscInt arg

◆ PicurvExpression

struct PicurvExpression

Definition at line 66 of file ParticleInitialConditions.c.

Collaboration diagram for PicurvExpression:
[legend]
Data Fields
ExpressionInstruction * code
PetscInt length
PetscInt max_depth
PetscBool draws

◆ ExpressionParser

struct ExpressionParser

Parser state: the text, a cursor, the node pool, and the names in scope.

Definition at line 74 of file ParticleInitialConditions.c.

Collaboration diagram for ExpressionParser:
[legend]
Data Fields
const char * text
size_t pos
ExpressionNode * nodes
PetscInt count
PetscInt capacity
const char *const * names
PetscInt name_count
PetscBool allow_random
char error[256]

◆ ParticleFieldBinding

struct ParticleFieldBinding

One configured field: its identity and one expression per component.

Definition at line 548 of file ParticleInitialConditions.c.

Collaboration diagram for ParticleFieldBinding:
[legend]
Data Fields
ParticleFieldId field
PetscInt components
PicurvExpression * component[3]

◆ ParticleFieldPlan

struct ParticleFieldPlan

Definition at line 554 of file ParticleInitialConditions.c.

Collaboration diagram for ParticleFieldPlan:
[legend]
Data Fields
PetscInt count
ParticleFieldBinding * bindings

Macro Definition Documentation

◆ PARTICLE_VARIABLE_COUNT

#define PARTICLE_VARIABLE_COUNT   ((PetscInt)(sizeof(kParticleVariables) / sizeof(kParticleVariables[0])))

Definition at line 542 of file ParticleInitialConditions.c.

◆ PARTICLE_FIELD_MAX_COMPONENTS

#define PARTICLE_FIELD_MAX_COMPONENTS   3

Definition at line 543 of file ParticleInitialConditions.c.

◆ PARTICLE_FIELD_EXPRESSION_LENGTH

#define PARTICLE_FIELD_EXPRESSION_LENGTH   4096

Definition at line 544 of file ParticleInitialConditions.c.

◆ PARTICLE_FIELD_HISTOGRAM_BINS

#define PARTICLE_FIELD_HISTOGRAM_BINS   10

Definition at line 545 of file ParticleInitialConditions.c.

Enumeration Type Documentation

◆ ExpressionOp

Enumerator
OP_CONST 
OP_VAR 
OP_NEG 
OP_POS 
OP_NOT 
OP_ADD 
OP_SUB 
OP_MUL 
OP_DIV 
OP_MOD 
OP_POW 
OP_EQ 
OP_NE 
OP_LT 
OP_LE 
OP_GT 
OP_GE 
OP_AND 
OP_OR 
OP_ABS 
OP_COS 
OP_EXP 
OP_SIN 
OP_SQRT 
OP_TAN 
OP_MAXIMUM 
OP_MINIMUM 
OP_WHERE 
OP_UNIFORM 
OP_NORMAL 

Definition at line 26 of file ParticleInitialConditions.c.

26 {

Function Documentation

◆ ParserFail()

static void ParserFail ( ExpressionParser *  parser,
const char *  message 
)
static

Record the first parse error with its position; later ones are consequences.

Definition at line 87 of file ParticleInitialConditions.c.

88{
89 if (!parser->error[0]) {
90 snprintf(parser->error, sizeof(parser->error), "%s at position %zu of '%s'",
91 message, parser->pos + 1, parser->text);
92 }
93}
Here is the caller graph for this function:

◆ SkipSpace()

static void SkipSpace ( ExpressionParser *  parser)
static

Skip whitespace before the next token.

Definition at line 96 of file ParticleInitialConditions.c.

97{
98 while (parser->text[parser->pos] && isspace((unsigned char)parser->text[parser->pos])) parser->pos++;
99}
Here is the caller graph for this function:

◆ Accept()

static PetscBool Accept ( ExpressionParser *  parser,
const char *  token 
)
static

Consume token if it is next, and report whether it was.

Definition at line 102 of file ParticleInitialConditions.c.

103{
104 size_t length = strlen(token);
105
106 SkipSpace(parser);
107 if (strncmp(parser->text + parser->pos, token, length) != 0) return PETSC_FALSE;
108 if (isalpha((unsigned char)token[0])) {
109 const char next = parser->text[parser->pos + length];
110 if (isalnum((unsigned char)next) || next == '_') return PETSC_FALSE;
111 }
112 /* `*` must not match the first half of `**`, nor `<` the first half of `<=`. */
113 if ((!strcmp(token, "*") && parser->text[parser->pos + 1] == '*') ||
114 ((!strcmp(token, "<") || !strcmp(token, ">")) && parser->text[parser->pos + 1] == '=')) {
115 return PETSC_FALSE;
116 }
117 parser->pos += length;
118 return PETSC_TRUE;
119}
static void SkipSpace(ExpressionParser *parser)
Skip whitespace before the next token.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ NewNode()

static PetscInt NewNode ( ExpressionParser *  parser,
ExpressionOp  op,
PetscInt  nchild,
PetscInt  a,
PetscInt  b,
PetscInt  c 
)
static

Append a node to the pool and return its index, or -1 once an error is set.

Definition at line 122 of file ParticleInitialConditions.c.

124{
125 if (parser->error[0]) return -1;
126 if (parser->count == parser->capacity) {
127 ParserFail(parser, "expression is too long");
128 return -1;
129 }
130 ExpressionNode *node = &parser->nodes[parser->count];
131 memset(node, 0, sizeof(*node));
132 node->op = op;
133 node->nchild = nchild;
134 node->child[0] = a;
135 node->child[1] = b;
136 node->child[2] = c;
137 return parser->count++;
138}
static void ParserFail(ExpressionParser *parser, const char *message)
Record the first parse error with its position; later ones are consequences.
One tree node; children index the node pool.
A generic C-style linked list node for integers.
Definition variables.h:470
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseOr()

static PetscInt ParseOr ( ExpressionParser *  parser)
static

and ('or' and)*

Definition at line 348 of file ParticleInitialConditions.c.

349{
350 PetscInt left = ParseAnd(parser);
351 while (Accept(parser, "or")) left = NewNode(parser, OP_OR, 2, left, ParseAnd(parser), -1);
352 return left;
353}
static PetscBool Accept(ExpressionParser *parser, const char *token)
Consume token if it is next, and report whether it was.
static PetscInt NewNode(ExpressionParser *parser, ExpressionOp op, PetscInt nchild, PetscInt a, PetscInt b, PetscInt c)
Append a node to the pool and return its index, or -1 once an error is set.
static PetscInt ParseAnd(ExpressionParser *parser)
not ('and' not)*
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseAtom()

static PetscInt ParseAtom ( ExpressionParser *  parser)
static

number | name | name(args) | (expression)

Definition at line 143 of file ParticleInitialConditions.c.

144{
145 SkipSpace(parser);
146 const char *start = parser->text + parser->pos;
147
148 if (isdigit((unsigned char)*start) || (*start == '.' && isdigit((unsigned char)start[1]))) {
149 /* Decimal literals only, as the Python validator requires. */
150 size_t length = 0;
151 while (isdigit((unsigned char)start[length])) length++;
152 if (start[length] == '.') { length++; while (isdigit((unsigned char)start[length])) length++; }
153 if (start[length] == 'e' || start[length] == 'E') {
154 size_t exponent = length + 1;
155 if (start[exponent] == '+' || start[exponent] == '-') exponent++;
156 if (!isdigit((unsigned char)start[exponent])) { ParserFail(parser, "malformed number"); return -1; }
157 while (isdigit((unsigned char)start[exponent])) exponent++;
158 length = exponent;
159 }
160 if (isalnum((unsigned char)start[length]) || start[length] == '_' || start[length] == '.') {
161 ParserFail(parser, "malformed number");
162 return -1;
163 }
164 const PetscReal value = strtod(start, NULL);
165 parser->pos += length;
166 PetscInt node = NewNode(parser, OP_CONST, 0, -1, -1, -1);
167 if (node >= 0) parser->nodes[node].value = value;
168 return node;
169 }
170 if (isalpha((unsigned char)*start) || *start == '_') {
171 char name[64];
172 size_t length = 0;
173 while (isalnum((unsigned char)start[length]) || start[length] == '_') length++;
174 if (length >= sizeof(name)) { ParserFail(parser, "name is too long"); return -1; }
175 memcpy(name, start, length);
176 name[length] = '\0';
177 if (!strcmp(name, "and") || !strcmp(name, "or") || !strcmp(name, "not")) {
178 ParserFail(parser, "operator where an operand was expected");
179 return -1;
180 }
181 parser->pos += length;
182 if (Accept(parser, "(")) {
183 for (size_t f = 0; f < sizeof(kFunctions) / sizeof(kFunctions[0]); ++f) {
184 if (strcmp(name, kFunctions[f].name)) continue;
185 if (kFunctions[f].random && !parser->allow_random) {
186 ParserFail(parser, "random draws are not available in this expression");
187 return -1;
188 }
189 PetscInt args[3] = {-1, -1, -1}, nargs = 0;
190 if (!Accept(parser, ")")) {
191 do {
192 if (nargs == 3) { ParserFail(parser, "too many arguments"); return -1; }
193 args[nargs++] = ParseOr(parser);
194 if (parser->error[0]) return -1;
195 } while (Accept(parser, ","));
196 if (!Accept(parser, ")")) { ParserFail(parser, "expected ')'"); return -1; }
197 }
198 if (nargs < kFunctions[f].min_args || nargs > kFunctions[f].max_args) {
199 ParserFail(parser, "wrong number of arguments");
200 return -1;
201 }
202 if (kFunctions[f].random) {
203 /* A draw's stream is a constant so that two expressions naming the
204 same stream draw the same number for the same particle. */
205 PetscInt node = NewNode(parser, kFunctions[f].op, 0, -1, -1, -1);
206 if (node < 0) return -1;
207 if (nargs == 1) {
208 const ExpressionNode *stream = &parser->nodes[args[0]];
209 if (stream->op != OP_CONST || stream->value < 0.0 ||
210 stream->value != PetscFloorReal(stream->value)) {
211 ParserFail(parser, "a draw's stream must be a non-negative integer literal");
212 return -1;
213 }
214 parser->nodes[node].value = stream->value;
215 parser->nodes[node].var = 1;
216 }
217 return node;
218 }
219 return NewNode(parser, kFunctions[f].op, nargs, args[0], args[1], args[2]);
220 }
221 ParserFail(parser, "unknown function");
222 return -1;
223 }
224 if (!strcmp(name, "pi")) {
225 PetscInt node = NewNode(parser, OP_CONST, 0, -1, -1, -1);
226 if (node >= 0) parser->nodes[node].value = PETSC_PI;
227 return node;
228 }
229 for (PetscInt v = 0; v < parser->name_count; ++v) {
230 if (!strcmp(name, parser->names[v])) {
231 PetscInt node = NewNode(parser, OP_VAR, 0, -1, -1, -1);
232 if (node >= 0) parser->nodes[node].var = v;
233 return node;
234 }
235 }
236 ParserFail(parser, "unknown name");
237 return -1;
238 }
239 if (Accept(parser, "(")) {
240 PetscInt inner = ParseOr(parser);
241 if (!Accept(parser, ")")) { ParserFail(parser, "expected ')'"); return -1; }
242 return inner;
243 }
244 ParserFail(parser, "expected a number, name, or '('");
245 return -1;
246}
const char *const * names
static const struct @1 kFunctions[]
Name, arity, and opcode of every callable function.
static PetscInt ParseOr(ExpressionParser *parser)
and ('or' and)*
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseUnary()

static PetscInt ParseUnary ( ExpressionParser *  parser)
static

Parses a sign prefix recursively and wraps the operand in a negate or identity node.

Parameters
[in,out]parserParser state; its position advances past the operand.
Returns
Node index of the signed operand, or -1 after an error.

Definition at line 270 of file ParticleInitialConditions.c.

271{
272 if (Accept(parser, "-")) return NewNode(parser, OP_NEG, 1, ParseUnary(parser), -1, -1);
273 if (Accept(parser, "+")) return NewNode(parser, OP_POS, 1, ParseUnary(parser), -1, -1);
274 return ParsePower(parser);
275}
static PetscInt ParseUnary(ExpressionParser *parser)
Parses a sign prefix recursively and wraps the operand in a negate or identity node.
static PetscInt ParsePower(ExpressionParser *parser)
Parses an atom and, when a power operator follows, appends a power node.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParsePower()

static PetscInt ParsePower ( ExpressionParser *  parser)
static

Parses an atom and, when a power operator follows, appends a power node.

Grammar: ‘atom [’**' unary]. The exponent is parsed as a unary, so2**-1is valid anda**b**cgroups to the right; a unary minus on the left is parsed by the caller, so-2**2` is -4.

Parameters
[in,out]parserParser state; its position advances past the power.
Returns
Node index of the power (or of the bare atom), or -1 after an error.

Definition at line 258 of file ParticleInitialConditions.c.

259{
260 PetscInt base = ParseAtom(parser);
261 if (Accept(parser, "**")) return NewNode(parser, OP_POW, 2, base, ParseUnary(parser), -1);
262 return base;
263}
static PetscInt ParseAtom(ExpressionParser *parser)
number | name | name(args) | (expression)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseTerm()

static PetscInt ParseTerm ( ExpressionParser *  parser)
static

Parses a left-associative chain of *, /, %, folding each operator into a node.

// is refused explicitly, since the Python side does not accept floor division.

Parameters
[in,out]parserParser state; its position advances past the term.
Returns
Node index of the term, or -1 after an error.

Definition at line 283 of file ParticleInitialConditions.c.

284{
285 PetscInt left = ParseUnary(parser);
286 for (;;) {
287 if (Accept(parser, "*")) left = NewNode(parser, OP_MUL, 2, left, ParseUnary(parser), -1);
288 else if (Accept(parser, "/")) {
289 if (parser->text[parser->pos] == '/') { ParserFail(parser, "'//' is not supported"); return -1; }
290 left = NewNode(parser, OP_DIV, 2, left, ParseUnary(parser), -1);
291 } else if (Accept(parser, "%")) left = NewNode(parser, OP_MOD, 2, left, ParseUnary(parser), -1);
292 else return left;
293 }
294}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseSum()

static PetscInt ParseSum ( ExpressionParser *  parser)
static

Parses a left-associative chain of + and -, folding each operator into a node.

Parameters
[in,out]parserParser state; its position advances past the sum.
Returns
Node index of the sum, or -1 after an error.

Definition at line 301 of file ParticleInitialConditions.c.

302{
303 PetscInt left = ParseTerm(parser);
304 for (;;) {
305 if (Accept(parser, "+")) left = NewNode(parser, OP_ADD, 2, left, ParseTerm(parser), -1);
306 else if (Accept(parser, "-")) left = NewNode(parser, OP_SUB, 2, left, ParseTerm(parser), -1);
307 else return left;
308 }
309}
static PetscInt ParseTerm(ExpressionParser *parser)
Parses a left-associative chain of *, /, %, folding each operator into a node.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseComparison()

static PetscInt ParseComparison ( ExpressionParser *  parser)
static

sum (comparison sum)*; a chain a < b < c is (a < b) and (b < c).

Definition at line 312 of file ParticleInitialConditions.c.

313{
314 static const struct { const char *token; ExpressionOp op; } kComparisons[] = {
315 {"==", OP_EQ}, {"!=", OP_NE}, {"<=", OP_LE}, {">=", OP_GE}, {"<", OP_LT}, {">", OP_GT},
316 };
317 PetscInt left = ParseSum(parser), result = -1;
318
319 for (;;) {
321 for (size_t c = 0; c < sizeof(kComparisons) / sizeof(kComparisons[0]); ++c) {
322 if (Accept(parser, kComparisons[c].token)) { op = kComparisons[c].op; break; }
323 }
324 if (op == OP_CONST) return (result < 0) ? left : result;
325 const PetscInt right = ParseSum(parser);
326 const PetscInt pair = NewNode(parser, op, 2, left, right, -1);
327 result = (result < 0) ? pair : NewNode(parser, OP_AND, 2, result, pair, -1);
328 left = right;
329 }
330}
static PetscInt ParseSum(ExpressionParser *parser)
Parses a left-associative chain of + and -, folding each operator into a node.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseNot()

static PetscInt ParseNot ( ExpressionParser *  parser)
static

'not' not | comparison

Definition at line 333 of file ParticleInitialConditions.c.

334{
335 if (Accept(parser, "not")) return NewNode(parser, OP_NOT, 1, ParseNot(parser), -1, -1);
336 return ParseComparison(parser);
337}
static PetscInt ParseComparison(ExpressionParser *parser)
sum (comparison sum)*; a chain a < b < c is (a < b) and (b < c).
static PetscInt ParseNot(ExpressionParser *parser)
'not' not | comparison
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseAnd()

static PetscInt ParseAnd ( ExpressionParser *  parser)
static

not ('and' not)*

Definition at line 340 of file ParticleInitialConditions.c.

341{
342 PetscInt left = ParseNot(parser);
343 while (Accept(parser, "and")) left = NewNode(parser, OP_AND, 2, left, ParseNot(parser), -1);
344 return left;
345}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EmitNode()

static void EmitNode ( const ExpressionParser *  parser,
PetscInt  index,
PicurvExpression *  expression,
PetscInt *  depth 
)
static

Emit postfix code for a subtree and track the stack depth it needs.

Definition at line 356 of file ParticleInitialConditions.c.

358{
359 const ExpressionNode *node = &parser->nodes[index];
360
361 for (PetscInt c = 0; c < node->nchild; ++c) EmitNode(parser, node->child[c], expression, depth);
362 ExpressionInstruction *instruction = &expression->code[expression->length++];
363 instruction->op = node->op;
364 instruction->value = node->value;
365 instruction->arg = node->var;
366 *depth += 1 - node->nchild;
367 if (*depth > expression->max_depth) expression->max_depth = *depth;
368 if (node->op == OP_UNIFORM || node->op == OP_NORMAL) expression->draws = PETSC_TRUE;
369}
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.
ExpressionInstruction * code
One postfix instruction.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CountCode()

static PetscInt CountCode ( const ExpressionParser *  parser,
PetscInt  index 
)
static

Count the instructions a subtree emits (shared subtrees count per use).

Definition at line 372 of file ParticleInitialConditions.c.

373{
374 const ExpressionNode *node = &parser->nodes[index];
375 PetscInt total = 1;
376
377 for (PetscInt c = 0; c < node->nchild; ++c) total += CountCode(parser, node->child[c]);
378 return total;
379}
static PetscInt CountCode(const ExpressionParser *parser, PetscInt index)
Count the instructions a subtree emits (shared subtrees count per use).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvExpressionCompile()

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

Implementation of PicurvExpressionCompile().

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}
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:

◆ Mix64()

static uint64_t Mix64 ( uint64_t  z)
static

splitmix64 finalizer: a bijective mix of 64 bits.

Definition at line 418 of file ParticleInitialConditions.c.

419{
420 z += 0x9E3779B97F4A7C15ULL;
421 z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL;
422 z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL;
423 return z ^ (z >> 31);
424}
Here is the caller graph for this function:

◆ UnitFromHash()

static PetscReal UnitFromHash ( uint64_t  hash)
static

Uniform on [0, 1) from the top 53 bits of a hash.

Definition at line 427 of file ParticleInitialConditions.c.

428{
429 return (PetscReal)(hash >> 11) * (1.0 / 9007199254740992.0);
430}
Here is the caller graph for this function:

◆ Draw()

static PetscReal Draw ( const PicurvExpressionDrawKey *  key,
const ExpressionInstruction *  instruction 
)
static

A draw keyed on the particle, the event, the stream, and the function.

Definition at line 433 of file ParticleInitialConditions.c.

434{
435 /* A default stream is the drawing field's own, so two fields draw independently; an
436 explicit stream is shared by every expression that names it. */
437 const uint64_t space = instruction->arg ? 1u : 0u;
438 const uint64_t stream = instruction->arg ? (uint64_t)instruction->value : (uint64_t)key->field;
439 uint64_t hash = Mix64((uint64_t)key->seed);
440 hash = Mix64(hash ^ space);
441 hash = Mix64(hash ^ stream);
442 hash = Mix64(hash ^ (uint64_t)key->pid);
443 hash = Mix64(hash ^ (uint64_t)key->salt);
444 hash = Mix64(hash ^ (uint64_t)instruction->op);
445 if (instruction->op == OP_UNIFORM) return UnitFromHash(hash);
446 /* Box-Muller from two independent uniforms; u1 lies in (0, 1]. */
447 const PetscReal u1 = 1.0 - UnitFromHash(hash);
448 const PetscReal u2 = UnitFromHash(Mix64(hash ^ 0xD1B54A32D192ED03ULL));
449 return PetscSqrtReal(-2.0 * PetscLogReal(u1)) * PetscCosReal(2.0 * PETSC_PI * u2);
450}
static uint64_t Mix64(uint64_t z)
splitmix64 finalizer: a bijective mix of 64 bits.
static PetscReal UnitFromHash(uint64_t hash)
Uniform on [0, 1) from the top 53 bits of a hash.
PetscInt64 pid
Particle identity; draws are pure functions of it.
PetscInt64 salt
Event that produced the particle: 0 for the t=0 population.
PetscInt64 seed
Run seed (-particle_random_seed).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Extreme()

static PetscReal Extreme ( PetscReal  a,
PetscReal  b,
PetscBool  maximum 
)
static

Maximum or minimum that propagates NaN, as numpy does.

Definition at line 453 of file ParticleInitialConditions.c.

454{
455 if (PetscIsNanReal(a) || PetscIsNanReal(b)) return NAN;
456 return maximum ? PetscMax(a, b) : PetscMin(a, b);
457}
Here is the caller graph for this function:

◆ PicurvExpressionEvaluate()

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

Implementation of PicurvExpressionEvaluate().

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}
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.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvExpressionDestroy()

PetscErrorCode PicurvExpressionDestroy ( PicurvExpression **  expression)

Implementation of PicurvExpressionDestroy().

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)

Implementation of ParticleFieldPlanCreate().

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:

◆ DomainBounds()

static PetscErrorCode DomainBounds ( const SimCtx *  simCtx,
Cmpnts *  lower,
Cmpnts *  upper 
)
static

Global domain bounds, in solver units, from the replicated rank bounding boxes.

Definition at line 618 of file ParticleInitialConditions.c.

619{
620 PetscMPIInt size = 1;
621
622 PetscFunctionBeginUser;
623 PetscCheck(simCtx->bboxlist, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
624 "Rank bounding boxes are needed before a particle initial value is applied.");
625 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
626 *lower = simCtx->bboxlist[0].min_coords;
627 *upper = simCtx->bboxlist[0].max_coords;
628 for (PetscInt r = 1; r < (PetscInt)size * simCtx->block_number; ++r) {
629 lower->x = PetscMin(lower->x, simCtx->bboxlist[r].min_coords.x);
630 lower->y = PetscMin(lower->y, simCtx->bboxlist[r].min_coords.y);
631 lower->z = PetscMin(lower->z, simCtx->bboxlist[r].min_coords.z);
632 upper->x = PetscMax(upper->x, simCtx->bboxlist[r].max_coords.x);
633 upper->y = PetscMax(upper->y, simCtx->bboxlist[r].max_coords.y);
634 upper->z = PetscMax(upper->z, simCtx->bboxlist[r].max_coords.z);
635 }
636 PetscFunctionReturn(0);
637}
PetscInt block_number
Definition variables.h:958
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:204
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:203
PetscScalar x
Definition variables.h:122
BoundingBox * bboxlist
Definition variables.h:995
PetscScalar z
Definition variables.h:122
PetscScalar y
Definition variables.h:122
Here is the caller graph for this function:

◆ Normalized()

static PetscReal Normalized ( PetscReal  value,
PetscReal  lower,
PetscReal  upper 
)
static

Position within the domain along one axis, in [0, 1]; 0 across a flat axis.

Definition at line 640 of file ParticleInitialConditions.c.

641{
642 return (upper > lower) ? (value - lower) / (upper - lower) : 0.0;
643}
Here is the caller graph for this function:

◆ ParticleFieldPlanApply()

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

Implementation of ParticleFieldPlanApply().

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
ScalingCtx scaling
Definition variables.h:952
PetscInt particleRandomSeed
Base seed for every particle RNG stream (-particle_random_seed).
Definition variables.h:993
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 
)

Implementation of ParticleFieldPlanSummarize().

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)

Implementation of ParticleFieldPlanDestroy().

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:

Variable Documentation

◆ [struct]

const struct { ... } kFunctions[]
Initial value:
= {
{"abs", 1, 1, OP_ABS, PETSC_FALSE}, {"cos", 1, 1, OP_COS, PETSC_FALSE},
{"exp", 1, 1, OP_EXP, PETSC_FALSE}, {"sin", 1, 1, OP_SIN, PETSC_FALSE},
{"sqrt", 1, 1, OP_SQRT, PETSC_FALSE}, {"tan", 1, 1, OP_TAN, PETSC_FALSE},
{"maximum", 2, 2, OP_MAXIMUM, PETSC_FALSE}, {"minimum", 2, 2, OP_MINIMUM, PETSC_FALSE},
{"where", 3, 3, OP_WHERE, PETSC_FALSE},
{"uniform", 0, 1, OP_UNIFORM, PETSC_TRUE}, {"normal", 0, 1, OP_NORMAL, PETSC_TRUE},
}

Name, arity, and opcode of every callable function.

◆ kParticleVariables

const char* const kParticleVariables[] = {"x", "y", "z", "xn", "yn", "zn", "pid", "t"}
static

Names a particle expression may use, in the order their values are supplied.

Definition at line 541 of file ParticleInitialConditions.c.

541{"x", "y", "z", "xn", "yn", "zn", "pid", "t"};