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

The expression language shared by every configured field: a parser, a postfix compiler, and a pure stack evaluator. More...

#include "expression.h"
#include "Metric.h"
#include "Boundaries.h"
#include "grid.h"
#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "logging.h"
Include dependency graph for expression.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...
 

Macros

#define EXPRESSION_MAX_SERIES_DEPTH   4
 Series that may nest inside one another, and terms one series may add.
 
#define EXPRESSION_MAX_SERIES_TERMS   1000000
 
#define PICURV_SOURCE_EXPRESSION_LENGTH   4096
 

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_ERF , OP_EXP , OP_SIN ,
  OP_SQRT , OP_TAN , OP_TANH , OP_COSH ,
  OP_SINH , OP_LOG , OP_FLOOR , OP_ATAN2 ,
  OP_MAXIMUM , OP_MINIMUM , OP_WHERE , OP_UNIFORM ,
  OP_NORMAL , OP_BESSELJ , OP_BESSELY , OP_BER ,
  OP_BEI , OP_SUM , OP_SUM_START , OP_SUM_HI ,
  OP_SUM_ACC , OP_LOCAL
}
 

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 PetscBool SeriesBound (const ExpressionParser *parser, PetscInt index, PetscReal *bound)
 The value of a series bound: an integer literal, possibly negated or parenthesized.
 
static PetscInt ParseSeries (ExpressionParser *parser)
 sum( has been read: index name, integer bounds, then the body with the index in scope.
 
static PetscInt ParseAtom (ExpressionParser *parser)
 number | name | name(args) | sum(index, lo, hi, body) | (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 PetscInt CountCode (const ExpressionParser *parser, PetscInt index)
 Count the instructions a subtree emits (shared subtrees count per use).
 
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.
 
PetscErrorCode PicurvExpressionCompile (const char *text, const char *const *names, PetscInt name_count, PetscBool allow_random, PicurvExpression **expression)
 Implementation of PicurvExpressionCompile().
 
uint64_t PicurvMix64 (uint64_t z)
 Implementation of PicurvMix64().
 
PetscReal PicurvUnitFromHash (uint64_t hash)
 Implementation of PicurvUnitFromHash().
 
uint64_t PicurvParticleDrawHash (PetscInt64 seed, PicurvDrawSpace space, uint64_t stream, PetscInt64 pid, PetscInt64 salt)
 Implementation of PicurvParticleDrawHash().
 
static PetscReal Draw (const PicurvExpressionDrawKey *key, const ExpressionInstruction *instruction)
 A draw keyed on the particle, the event, the stream, and the function.
 
static PetscReal Kelvin (PetscReal x, PetscBool imaginary)
 Kelvin functions of order zero, ber(x) + i bei(x) = J0(x e^{3 pi i / 4}).
 
static PetscReal Bessel (PetscReal order, PetscReal x, PetscBool second)
 Bessel function of the first or second kind of integer order; NaN for another order.
 
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 PicurvExpressionUsesVariable (const PicurvExpression *expression, PetscInt index, PetscBool *uses)
 Implementation of PicurvExpressionUsesVariable().
 
PetscErrorCode PicurvVelocitySourceCreate (const char *prefix, PetscBool on_face, PetscBool has_data, PicurvVelocitySource *source)
 Implementation of PicurvVelocitySourceCreate().
 
PetscErrorCode PicurvVelocitySourceBulkFactor (const PicurvVelocitySource *source, PetscReal physical_time, const PetscReal local[3], const char *where, PetscReal *factor)
 Implementation of PicurvVelocitySourceBulkFactor().
 
PetscErrorCode PicurvVelocitySourceEvaluate (const PicurvVelocitySource *source, const PetscReal *values, const Cmpnts edges[3], Cmpnts *value)
 Implementation of PicurvVelocitySourceEvaluate().
 
PetscErrorCode PicurvSourceCellBulkFactor (UserCtx *user, const PicurvVelocitySource *source, Vec fraction, Vec length, PetscReal physical_time, PetscReal *factor)
 Implementation of PicurvSourceCellBulkFactor().
 
PetscErrorCode PicurvSourceAddFluxes (UserCtx *user, const PicurvVelocitySource *source, Vec fraction, Vec length, PetscReal physical_time, PetscReal factor, PetscBool boundary_faces)
 Implementation of PicurvSourceAddFluxes().
 
PetscErrorCode PicurvSourceSetCellPressure (UserCtx *user, const PicurvExpression *pressure, Vec fraction, Vec length, PetscReal physical_time)
 Implementation of PicurvSourceSetCellPressure().
 
PetscErrorCode PicurvSourceFaceSlotsCreate (UserCtx *user, BCFace face_id, Vec fraction, Vec length, PicurvSourceFaceSlots *slots)
 Implementation of PicurvSourceFaceSlotsCreate().
 
PetscErrorCode PicurvSourceFaceSlotsDestroy (PicurvSourceFaceSlots *slots)
 Implementation of PicurvSourceFaceSlotsDestroy().
 
PetscErrorCode PicurvVelocitySourceDestroy (PicurvVelocitySource *source)
 Implementation of PicurvVelocitySourceDestroy().
 

Variables

struct { 
 
const char * name
 
int min_args
 
int max_args
 
ExpressionOp op
 
PetscBool random
 
} kFunctions [] 
 Name, arity, and opcode of every callable function.
 
const char *const kPicurvSourceVariableNames [PICURV_SOURCE_VAR_COUNT]
 Names of PicurvSourceVariable, in order.
 

Detailed Description

The expression language shared by every configured field: a parser, a postfix compiler, and a pure stack evaluator.

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 point. A series sum(k, lo, hi, body) compiles its body once and loops over it, with the index in a slot of its nesting level. Evaluation is pure: random draws are hashed from a caller-supplied key, so a value does not depend on rank count, evaluation order, or how often it is evaluated.

Definition in file expression.c.


Data Structure Documentation

◆ ExpressionNode

struct ExpressionNode

One tree node; children index the node pool.

Definition at line 69 of file expression.c.

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

◆ ExpressionInstruction

struct ExpressionInstruction

One postfix instruction.

Definition at line 79 of file expression.c.

Data Fields
ExpressionOp op
PetscReal value
PetscInt arg

◆ PicurvExpression

struct PicurvExpression

Definition at line 85 of file expression.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 93 of file expression.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 series_index[4][64]
PetscInt series_depth
char error[256]

Macro Definition Documentation

◆ EXPRESSION_MAX_SERIES_DEPTH

#define EXPRESSION_MAX_SERIES_DEPTH   4

Series that may nest inside one another, and terms one series may add.

Definition at line 65 of file expression.c.

◆ EXPRESSION_MAX_SERIES_TERMS

#define EXPRESSION_MAX_SERIES_TERMS   1000000

Definition at line 66 of file expression.c.

◆ PICURV_SOURCE_EXPRESSION_LENGTH

#define PICURV_SOURCE_EXPRESSION_LENGTH   4096

Definition at line 824 of file expression.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_ERF 
OP_EXP 
OP_SIN 
OP_SQRT 
OP_TAN 
OP_TANH 
OP_COSH 
OP_SINH 
OP_LOG 
OP_FLOOR 
OP_ATAN2 
OP_MAXIMUM 
OP_MINIMUM 
OP_WHERE 
OP_UNIFORM 
OP_NORMAL 
OP_BESSELJ 
OP_BESSELY 
OP_BER 
OP_BEI 
OP_SUM 
OP_SUM_START 
OP_SUM_HI 
OP_SUM_ACC 
OP_LOCAL 

Definition at line 29 of file expression.c.

29 {
37 /* A series: START pushes the running total and sets the index, HI carries the upper bound
38 and the body's length, ACC adds the body's value and loops; LOCAL reads an index. */
ExpressionOp
Definition expression.c:29
@ OP_SUM
Definition expression.c:39
@ OP_ATAN2
Definition expression.c:34
@ OP_TAN
Definition expression.c:33
@ OP_NE
Definition expression.c:32
@ OP_MINIMUM
Definition expression.c:34
@ OP_WHERE
Definition expression.c:34
@ OP_BER
Definition expression.c:36
@ OP_EQ
Definition expression.c:32
@ OP_SUM_ACC
Definition expression.c:39
@ OP_MAXIMUM
Definition expression.c:34
@ OP_CONST
Definition expression.c:30
@ OP_GE
Definition expression.c:32
@ OP_POW
Definition expression.c:31
@ OP_POS
Definition expression.c:30
@ OP_NOT
Definition expression.c:30
@ OP_SQRT
Definition expression.c:33
@ OP_MOD
Definition expression.c:31
@ OP_BESSELJ
Definition expression.c:36
@ OP_COS
Definition expression.c:33
@ OP_VAR
Definition expression.c:30
@ OP_SUB
Definition expression.c:31
@ OP_DIV
Definition expression.c:31
@ OP_LOCAL
Definition expression.c:39
@ OP_LT
Definition expression.c:32
@ OP_TANH
Definition expression.c:33
@ OP_BEI
Definition expression.c:36
@ OP_ADD
Definition expression.c:31
@ OP_MUL
Definition expression.c:31
@ OP_COSH
Definition expression.c:33
@ OP_SIN
Definition expression.c:33
@ OP_UNIFORM
Definition expression.c:35
@ OP_NEG
Definition expression.c:30
@ OP_GT
Definition expression.c:32
@ OP_BESSELY
Definition expression.c:36
@ OP_ERF
Definition expression.c:33
@ OP_SUM_START
Definition expression.c:39
@ OP_LE
Definition expression.c:32
@ OP_ABS
Definition expression.c:33
@ OP_AND
Definition expression.c:32
@ OP_SUM_HI
Definition expression.c:39
@ OP_LOG
Definition expression.c:33
@ OP_OR
Definition expression.c:32
@ OP_FLOOR
Definition expression.c:33
@ OP_SINH
Definition expression.c:33
@ OP_EXP
Definition expression.c:33
@ OP_NORMAL
Definition expression.c:35

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 108 of file expression.c.

109{
111 if (!parser->error[0]) {
112 snprintf(parser->error, sizeof(parser->error), "%s at position %zu of '%s'",
113 message, parser->pos + 1, parser->text);
114 }
116}
const char * text
Definition expression.c:94
char error[256]
Definition expression.c:104
#define PROFILE_HELPER_BEGIN
Marks the beginning of a profiled helper: timed only when enabled by name.
Definition logging.h:1018
#define PROFILE_HELPER_END
Alias of PROFILE_FUNCTION_END for a function that began with PROFILE_HELPER_BEGIN.
Definition logging.h:1037
Here is the caller graph for this function:

◆ SkipSpace()

static void SkipSpace ( ExpressionParser *  parser)
static

Skip whitespace before the next token.

Definition at line 119 of file expression.c.

120{
122 while (parser->text[parser->pos] && isspace((unsigned char)parser->text[parser->pos])) parser->pos++;
124}
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:1033
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled function: an always-on aggregate timer.
Definition logging.h:1004
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 127 of file expression.c.

128{
130 size_t length = strlen(token);
131
132 SkipSpace(parser);
133 if (strncmp(parser->text + parser->pos, token, length) != 0) { PROFILE_FUNCTION_END; return PETSC_FALSE; }
134 if (isalpha((unsigned char)token[0])) {
135 const char next = parser->text[parser->pos + length];
136 if (isalnum((unsigned char)next) || next == '_') { PROFILE_FUNCTION_END; return PETSC_FALSE; }
137 }
138 /* `*` must not match the first half of `**`, nor `<` the first half of `<=`. */
139 if ((!strcmp(token, "*") && parser->text[parser->pos + 1] == '*') ||
140 ((!strcmp(token, "<") || !strcmp(token, ">")) && parser->text[parser->pos + 1] == '=')) {
142 return PETSC_FALSE;
143 }
144 parser->pos += length;
146 return PETSC_TRUE;
147}
static void SkipSpace(ExpressionParser *parser)
Skip whitespace before the next token.
Definition expression.c:119
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 150 of file expression.c.

152{
154 if (parser->error[0]) { PROFILE_FUNCTION_END; return -1; }
155 if (parser->count == parser->capacity) {
156 ParserFail(parser, "expression is too long");
158 return -1;
159 }
160 ExpressionNode *node = &parser->nodes[parser->count];
161 memset(node, 0, sizeof(*node));
162 node->op = op;
163 node->nchild = nchild;
164 node->child[0] = a;
165 node->child[1] = b;
166 node->child[2] = c;
168 return parser->count++;
169}
static void ParserFail(ExpressionParser *parser, const char *message)
Record the first parse error with its position; later ones are consequences.
Definition expression.c:108
ExpressionNode * nodes
Definition expression.c:96
PetscInt capacity
Definition expression.c:98
One tree node; children index the node pool.
Definition expression.c:69
A generic C-style linked list node for integers.
Definition variables.h:472
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 489 of file expression.c.

490{
492 PetscInt left = ParseAnd(parser);
493 while (Accept(parser, "or")) left = NewNode(parser, OP_OR, 2, left, ParseAnd(parser), -1);
495 return left;
496}
static PetscBool Accept(ExpressionParser *parser, const char *token)
Consume token if it is next, and report whether it was.
Definition expression.c:127
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.
Definition expression.c:150
static PetscInt ParseAnd(ExpressionParser *parser)
not ('and' not)*
Definition expression.c:479
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SeriesBound()

static PetscBool SeriesBound ( const ExpressionParser *  parser,
PetscInt  index,
PetscReal *  bound 
)
static

The value of a series bound: an integer literal, possibly negated or parenthesized.

Definition at line 174 of file expression.c.

175{
177 const ExpressionNode *node = &parser->nodes[index];
178 PetscReal sign = 1.0;
179 if (node->op == OP_NEG || node->op == OP_POS) {
180 if (node->op == OP_NEG) sign = -1.0;
181 node = &parser->nodes[node->child[0]];
182 }
183 *bound = sign * node->value;
185 return (PetscBool)(node->op == OP_CONST && node->value == PetscFloorReal(node->value));
186}
Here is the caller graph for this function:

◆ ParseSeries()

static PetscInt ParseSeries ( ExpressionParser *  parser)
static

sum( has been read: index name, integer bounds, then the body with the index in scope.

Definition at line 189 of file expression.c.

190{
192 char name[64];
193 size_t length = 0;
194 PetscReal lower = 0.0, upper = 0.0;
195
196 SkipSpace(parser);
197 const char *start = parser->text + parser->pos;
198 while (isalnum((unsigned char)start[length]) || start[length] == '_') length++;
199 if (!length || isdigit((unsigned char)start[0]) || length >= sizeof(name)) {
200 ParserFail(parser, "a series needs an index name first");
202 return -1;
203 }
204 memcpy(name, start, length);
205 name[length] = '\0';
206 parser->pos += length;
207 for (PetscInt v = 0; v < parser->name_count; ++v) {
208 if (!strcmp(name, parser->names[v])) { ParserFail(parser, "a series index cannot reuse a variable's name"); PROFILE_HELPER_END; return -1; }
209 }
210 for (PetscInt level = 0; level < parser->series_depth; ++level) {
211 if (!strcmp(name, parser->series_index[level])) { ParserFail(parser, "a series index cannot reuse an enclosing index"); PROFILE_HELPER_END; return -1; }
212 }
213 if (!strcmp(name, "pi")) { ParserFail(parser, "a series index cannot be pi"); PROFILE_HELPER_END; return -1; }
214 if (parser->series_depth == EXPRESSION_MAX_SERIES_DEPTH) { ParserFail(parser, "series nest too deeply"); PROFILE_HELPER_END; return -1; }
215 if (!Accept(parser, ",")) { ParserFail(parser, "expected ',' after the series index"); PROFILE_HELPER_END; return -1; }
216 const PetscInt lo = ParseOr(parser);
217 if (parser->error[0] || !Accept(parser, ",")) { ParserFail(parser, "expected ',' after the lower bound"); PROFILE_HELPER_END; return -1; }
218 const PetscInt hi = ParseOr(parser);
219 if (parser->error[0] || !Accept(parser, ",")) { ParserFail(parser, "expected ',' after the upper bound"); PROFILE_HELPER_END; return -1; }
220 if (!SeriesBound(parser, lo, &lower) || !SeriesBound(parser, hi, &upper)) {
221 ParserFail(parser, "series bounds must be integer numbers");
223 return -1;
224 }
225 if (upper - lower + 1.0 > (PetscReal)EXPRESSION_MAX_SERIES_TERMS) {
226 ParserFail(parser, "a series may add at most 1000000 terms");
228 return -1;
229 }
230 memcpy(parser->series_index[parser->series_depth], name, length + 1);
231 parser->series_depth++;
232 const PetscInt body = ParseOr(parser);
233 parser->series_depth--;
234 if (parser->error[0]) { PROFILE_HELPER_END; return -1; }
235 if (!Accept(parser, ")")) { ParserFail(parser, "expected ')' after the series body"); PROFILE_HELPER_END; return -1; }
236 PetscInt node = NewNode(parser, OP_SUM, 1, body, -1, -1);
237 if (node >= 0) {
238 parser->nodes[node].value = lower;
239 parser->nodes[node].upper = upper;
240 parser->nodes[node].var = parser->series_depth;
241 }
243 return node;
244}
#define EXPRESSION_MAX_SERIES_TERMS
Definition expression.c:66
PetscReal value
Definition expression.c:71
#define EXPRESSION_MAX_SERIES_DEPTH
Series that may nest inside one another, and terms one series may add.
Definition expression.c:65
PetscInt var
Definition expression.c:73
PetscInt series_depth
Definition expression.c:103
const char *const * names
Definition expression.c:99
char series_index[4][64]
Definition expression.c:102
static PetscBool SeriesBound(const ExpressionParser *parser, PetscInt index, PetscReal *bound)
The value of a series bound: an integer literal, possibly negated or parenthesized.
Definition expression.c:174
static PetscInt ParseOr(ExpressionParser *parser)
and ('or' and)*
Definition expression.c:489
PetscReal upper
Definition expression.c:72
PetscInt name_count
Definition expression.c:100
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) | sum(index, lo, hi, body) | (expression)

Definition at line 247 of file expression.c.

248{
250 SkipSpace(parser);
251 const char *start = parser->text + parser->pos;
252
253 if (isdigit((unsigned char)*start) || (*start == '.' && isdigit((unsigned char)start[1]))) {
254 /* Decimal literals only, as the Python validator requires. */
255 size_t length = 0;
256 while (isdigit((unsigned char)start[length])) length++;
257 if (start[length] == '.') { length++; while (isdigit((unsigned char)start[length])) length++; }
258 if (start[length] == 'e' || start[length] == 'E') {
259 size_t exponent = length + 1;
260 if (start[exponent] == '+' || start[exponent] == '-') exponent++;
261 if (!isdigit((unsigned char)start[exponent])) { ParserFail(parser, "malformed number"); PROFILE_HELPER_END; return -1; }
262 while (isdigit((unsigned char)start[exponent])) exponent++;
263 length = exponent;
264 }
265 if (isalnum((unsigned char)start[length]) || start[length] == '_' || start[length] == '.') {
266 ParserFail(parser, "malformed number");
268 return -1;
269 }
270 const PetscReal value = strtod(start, NULL);
271 parser->pos += length;
272 PetscInt node = NewNode(parser, OP_CONST, 0, -1, -1, -1);
273 if (node >= 0) parser->nodes[node].value = value;
275 return node;
276 }
277 if (isalpha((unsigned char)*start) || *start == '_') {
278 char name[64];
279 size_t length = 0;
280 while (isalnum((unsigned char)start[length]) || start[length] == '_') length++;
281 if (length >= sizeof(name)) { ParserFail(parser, "name is too long"); PROFILE_HELPER_END; return -1; }
282 memcpy(name, start, length);
283 name[length] = '\0';
284 if (!strcmp(name, "and") || !strcmp(name, "or") || !strcmp(name, "not")) {
285 ParserFail(parser, "operator where an operand was expected");
287 return -1;
288 }
289 parser->pos += length;
290 if (!strcmp(name, "sum") && Accept(parser, "(")) { PROFILE_HELPER_END; return ParseSeries(parser); }
291 if (Accept(parser, "(")) {
292 for (size_t f = 0; f < sizeof(kFunctions) / sizeof(kFunctions[0]); ++f) {
293 if (strcmp(name, kFunctions[f].name)) continue;
294 if (kFunctions[f].random && !parser->allow_random) {
295 ParserFail(parser, "random draws are not available in this expression");
297 return -1;
298 }
299 PetscInt args[3] = {-1, -1, -1}, nargs = 0;
300 if (!Accept(parser, ")")) {
301 do {
302 if (nargs == 3) { ParserFail(parser, "too many arguments"); PROFILE_HELPER_END; return -1; }
303 args[nargs++] = ParseOr(parser);
304 if (parser->error[0]) { PROFILE_HELPER_END; return -1; }
305 } while (Accept(parser, ","));
306 if (!Accept(parser, ")")) { ParserFail(parser, "expected ')'"); PROFILE_HELPER_END; return -1; }
307 }
308 if (nargs < kFunctions[f].min_args || nargs > kFunctions[f].max_args) {
309 ParserFail(parser, "wrong number of arguments");
311 return -1;
312 }
313 if (kFunctions[f].random) {
314 /* A draw's stream is a constant so that two expressions naming the
315 same stream draw the same number for the same particle. */
316 PetscInt node = NewNode(parser, kFunctions[f].op, 0, -1, -1, -1);
317 if (node < 0) { PROFILE_HELPER_END; return -1; }
318 if (nargs == 1) {
319 const ExpressionNode *stream = &parser->nodes[args[0]];
320 if (stream->op != OP_CONST || stream->value < 0.0 ||
321 stream->value != PetscFloorReal(stream->value)) {
322 ParserFail(parser, "a draw's stream must be a non-negative integer literal");
324 return -1;
325 }
326 parser->nodes[node].value = stream->value;
327 parser->nodes[node].var = 1;
328 }
330 return node;
331 }
333 return NewNode(parser, kFunctions[f].op, nargs, args[0], args[1], args[2]);
334 }
335 ParserFail(parser, "unknown function");
337 return -1;
338 }
339 for (PetscInt level = parser->series_depth - 1; level >= 0; --level) {
340 if (strcmp(name, parser->series_index[level])) continue;
341 PetscInt node = NewNode(parser, OP_LOCAL, 0, -1, -1, -1);
342 if (node >= 0) parser->nodes[node].var = level;
344 return node;
345 }
346 if (!strcmp(name, "pi")) {
347 PetscInt node = NewNode(parser, OP_CONST, 0, -1, -1, -1);
348 if (node >= 0) parser->nodes[node].value = PETSC_PI;
350 return node;
351 }
352 for (PetscInt v = 0; v < parser->name_count; ++v) {
353 if (!strcmp(name, parser->names[v])) {
354 PetscInt node = NewNode(parser, OP_VAR, 0, -1, -1, -1);
355 if (node >= 0) parser->nodes[node].var = v;
357 return node;
358 }
359 }
360 ParserFail(parser, "unknown name");
362 return -1;
363 }
364 if (Accept(parser, "(")) {
365 PetscInt inner = ParseOr(parser);
366 if (!Accept(parser, ")")) { ParserFail(parser, "expected ')'"); PROFILE_HELPER_END; return -1; }
368 return inner;
369 }
370 ParserFail(parser, "expected a number, name, or '('");
372 return -1;
373}
PetscBool allow_random
Definition expression.c:101
static const struct @0 kFunctions[]
Name, arity, and opcode of every callable function.
static PetscInt ParseSeries(ExpressionParser *parser)
sum( has been read: index name, integer bounds, then the body with the index in scope.
Definition expression.c:189
ExpressionOp op
Definition expression.c:70
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 399 of file expression.c.

400{
402 if (Accept(parser, "-")) { PROFILE_FUNCTION_END; return NewNode(parser, OP_NEG, 1, ParseUnary(parser), -1, -1); }
403 if (Accept(parser, "+")) { PROFILE_FUNCTION_END; return NewNode(parser, OP_POS, 1, ParseUnary(parser), -1, -1); }
405 return ParsePower(parser);
406}
static PetscInt ParseUnary(ExpressionParser *parser)
Parses a sign prefix recursively and wraps the operand in a negate or identity node.
Definition expression.c:399
static PetscInt ParsePower(ExpressionParser *parser)
Parses an atom and, when a power operator follows, appends a power node.
Definition expression.c:385
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 385 of file expression.c.

386{
388 PetscInt base = ParseAtom(parser);
389 if (Accept(parser, "**")) { PROFILE_HELPER_END; return NewNode(parser, OP_POW, 2, base, ParseUnary(parser), -1); }
391 return base;
392}
static PetscInt ParseAtom(ExpressionParser *parser)
number | name | name(args) | sum(index, lo, hi, body) | (expression)
Definition expression.c:247
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 414 of file expression.c.

415{
417 PetscInt left = ParseUnary(parser);
418 for (;;) {
419 if (Accept(parser, "*")) left = NewNode(parser, OP_MUL, 2, left, ParseUnary(parser), -1);
420 else if (Accept(parser, "/")) {
421 if (parser->text[parser->pos] == '/') { ParserFail(parser, "'//' is not supported"); PROFILE_FUNCTION_END; return -1; }
422 left = NewNode(parser, OP_DIV, 2, left, ParseUnary(parser), -1);
423 } else if (Accept(parser, "%")) left = NewNode(parser, OP_MOD, 2, left, ParseUnary(parser), -1);
424 else { PROFILE_FUNCTION_END; return left; }
425 }
427}
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 434 of file expression.c.

435{
437 PetscInt left = ParseTerm(parser);
438 for (;;) {
439 if (Accept(parser, "+")) left = NewNode(parser, OP_ADD, 2, left, ParseTerm(parser), -1);
440 else if (Accept(parser, "-")) left = NewNode(parser, OP_SUB, 2, left, ParseTerm(parser), -1);
441 else { PROFILE_FUNCTION_END; return left; }
442 }
444}
static PetscInt ParseTerm(ExpressionParser *parser)
Parses a left-associative chain of *, /, %, folding each operator into a node.
Definition expression.c:414
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 447 of file expression.c.

448{
450 static const struct { const char *token; ExpressionOp op; } kComparisons[] = {
451 {"==", OP_EQ}, {"!=", OP_NE}, {"<=", OP_LE}, {">=", OP_GE}, {"<", OP_LT}, {">", OP_GT},
452 };
453 PetscInt left = ParseSum(parser), result = -1;
454
455 for (;;) {
457 for (size_t c = 0; c < sizeof(kComparisons) / sizeof(kComparisons[0]); ++c) {
458 if (Accept(parser, kComparisons[c].token)) { op = kComparisons[c].op; break; }
459 }
460 if (op == OP_CONST) { PROFILE_FUNCTION_END; return (result < 0) ? left : result; }
461 const PetscInt right = ParseSum(parser);
462 const PetscInt pair = NewNode(parser, op, 2, left, right, -1);
463 result = (result < 0) ? pair : NewNode(parser, OP_AND, 2, result, pair, -1);
464 left = right;
465 }
467}
static PetscInt ParseSum(ExpressionParser *parser)
Parses a left-associative chain of + and -, folding each operator into a node.
Definition expression.c:434
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 470 of file expression.c.

471{
473 if (Accept(parser, "not")) { PROFILE_FUNCTION_END; return NewNode(parser, OP_NOT, 1, ParseNot(parser), -1, -1); }
475 return ParseComparison(parser);
476}
static PetscInt ParseComparison(ExpressionParser *parser)
sum (comparison sum)*; a chain a < b < c is (a < b) and (b < c).
Definition expression.c:447
static PetscInt ParseNot(ExpressionParser *parser)
'not' not | comparison
Definition expression.c:470
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 479 of file expression.c.

480{
482 PetscInt left = ParseNot(parser);
483 while (Accept(parser, "and")) left = NewNode(parser, OP_AND, 2, left, ParseNot(parser), -1);
485 return left;
486}
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 534 of file expression.c.

535{
537 const ExpressionNode *node = &parser->nodes[index];
538 PetscInt total = (node->op == OP_SUM) ? 3 : 1; /* a series: START, HI and ACC around its body */
539
540 for (PetscInt c = 0; c < node->nchild; ++c) total += CountCode(parser, node->child[c]);
542 return total;
543}
static PetscInt CountCode(const ExpressionParser *parser, PetscInt index)
Count the instructions a subtree emits (shared subtrees count per use).
Definition expression.c:534
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 501 of file expression.c.

503{
505 const ExpressionNode *node = &parser->nodes[index];
506
507 if (node->op == OP_SUM) {
508 const PetscInt body = CountCode(parser, node->child[0]);
509 ExpressionInstruction *start = &expression->code[expression->length++];
510 start->op = OP_SUM_START; start->value = node->value; start->arg = node->var;
511 ExpressionInstruction *hi = &expression->code[expression->length++];
512 hi->op = OP_SUM_HI; hi->value = node->upper; hi->arg = body;
513 *depth += 1; /* the running total */
514 if (*depth > expression->max_depth) expression->max_depth = *depth;
515 EmitNode(parser, node->child[0], expression, depth);
516 ExpressionInstruction *acc = &expression->code[expression->length++];
517 acc->op = OP_SUM_ACC; acc->value = (PetscReal)body; acc->arg = node->var;
518 *depth -= 1;
520 return;
521 }
522 for (PetscInt c = 0; c < node->nchild; ++c) EmitNode(parser, node->child[c], expression, depth);
523 ExpressionInstruction *instruction = &expression->code[expression->length++];
524 instruction->op = node->op;
525 instruction->value = node->value;
526 instruction->arg = node->var;
527 *depth += 1 - node->nchild;
528 if (*depth > expression->max_depth) expression->max_depth = *depth;
529 if (node->op == OP_UNIFORM || node->op == OP_NORMAL) expression->draws = PETSC_TRUE;
531}
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.
Definition expression.c:501
PetscBool draws
Definition expression.c:89
PetscInt max_depth
Definition expression.c:88
ExpressionInstruction * code
Definition expression.c:86
PetscInt length
Definition expression.c:87
One postfix instruction.
Definition expression.c:79
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 549 of file expression.c.

551{
552 ExpressionParser parser;
553 PetscInt root, depth = 0;
554
555 PetscFunctionBeginUser;
557 PetscCheck(text && expression, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Expression text and output are required.");
558 PetscCall(PetscMemzero(&parser, sizeof(parser)));
559 parser.text = text;
560 parser.names = names;
561 parser.name_count = name_count;
562 parser.allow_random = allow_random;
563 parser.capacity = 4 * (PetscInt)strlen(text) + 8;
564 PetscCall(PetscMalloc1(parser.capacity, &parser.nodes));
565
566 root = ParseOr(&parser);
567 SkipSpace(&parser);
568 if (!parser.error[0] && parser.text[parser.pos]) ParserFail(&parser, "unexpected text");
569 if (parser.error[0]) {
570 PetscCall(PetscFree(parser.nodes));
571 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid expression: %s.", parser.error);
572 }
573
574 PetscCall(PetscNew(expression));
575 (*expression)->length = 0;
576 PetscCall(PetscMalloc1(CountCode(&parser, root), &(*expression)->code));
577 EmitNode(&parser, root, *expression, &depth);
578 PetscCall(PetscFree(parser.nodes));
580 PetscFunctionReturn(0);
581}
Parser state: the text, a cursor, the node pool, and the names in scope.
Definition expression.c:93
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvMix64()

uint64_t PicurvMix64 ( uint64_t  z)

Implementation of PicurvMix64().

splitmix64 finalizer: a bijective mix of 64 bits.

See also
PicurvMix64()

Definition at line 587 of file expression.c.

588{
589 z += 0x9E3779B97F4A7C15ULL;
590 z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL;
591 z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL;
592 return z ^ (z >> 31);
593}
Here is the caller graph for this function:

◆ PicurvUnitFromHash()

PetscReal PicurvUnitFromHash ( uint64_t  hash)

Implementation of PicurvUnitFromHash().

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

See also
PicurvUnitFromHash()

Definition at line 599 of file expression.c.

600{
601 return (PetscReal)(hash >> 11) * (1.0 / 9007199254740992.0);
602}
Here is the caller graph for this function:

◆ PicurvParticleDrawHash()

uint64_t PicurvParticleDrawHash ( PetscInt64  seed,
PicurvDrawSpace  space,
uint64_t  stream,
PetscInt64  pid,
PetscInt64  salt 
)

Implementation of PicurvParticleDrawHash().

Hash that keys a particle draw: a pure function of its arguments, so a draw does not depend on the rank a particle lives on, the order particles are visited, or what was drawn before it.

See also
PicurvParticleDrawHash()

Definition at line 608 of file expression.c.

610{
611 uint64_t hash = PicurvMix64((uint64_t)seed);
612 hash = PicurvMix64(hash ^ (uint64_t)space);
613 hash = PicurvMix64(hash ^ stream);
614 hash = PicurvMix64(hash ^ (uint64_t)pid);
615 return PicurvMix64(hash ^ (uint64_t)salt);
616}
uint64_t PicurvMix64(uint64_t z)
Implementation of PicurvMix64().
Definition expression.c:587
Here is the call graph for this function:
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 619 of file expression.c.

620{
622 /* A default stream is the drawing field's own, so two fields draw independently; an
623 explicit stream is shared by every expression that names it. */
625 const uint64_t stream = instruction->arg ? (uint64_t)instruction->value : (uint64_t)key->field;
626 const uint64_t hash = PicurvMix64(PicurvParticleDrawHash(key->seed, space, stream, key->pid, key->salt)
627 ^ (uint64_t)instruction->op);
628 if (instruction->op == OP_UNIFORM) { PROFILE_HELPER_END; return PicurvUnitFromHash(hash); }
629 /* Box-Muller from two independent uniforms; u1 lies in (0, 1]. */
630 const PetscReal u1 = 1.0 - PicurvUnitFromHash(hash);
631 const PetscReal u2 = PicurvUnitFromHash(PicurvMix64(hash ^ 0xD1B54A32D192ED03ULL));
633 return PetscSqrtReal(-2.0 * PetscLogReal(u1)) * PetscCosReal(2.0 * PETSC_PI * u2);
634}
PetscReal PicurvUnitFromHash(uint64_t hash)
Implementation of PicurvUnitFromHash().
Definition expression.c:599
uint64_t PicurvParticleDrawHash(PetscInt64 seed, PicurvDrawSpace space, uint64_t stream, PetscInt64 pid, PetscInt64 salt)
Implementation of PicurvParticleDrawHash().
Definition expression.c:608
PetscInt64 pid
Particle identity; draws are pure functions of it.
Definition expression.h:69
PicurvDrawSpace
Disjoint key spaces of the keyed particle draws, so draws made for different purposes never coincide ...
Definition expression.h:27
@ PICURV_DRAW_SPACE_FIELD
An expression's default, per-field stream.
Definition expression.h:28
@ PICURV_DRAW_SPACE_NAMED_STREAM
uniform(k)/normal(k): a stream shared across fields.
Definition expression.h:29
PetscInt64 salt
Event that produced the particle: 0 for the t=0 population.
Definition expression.h:70
PetscInt64 seed
Run seed (-particle_random_seed).
Definition expression.h:68
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Kelvin()

static PetscReal Kelvin ( PetscReal  x,
PetscBool  imaginary 
)
static

Kelvin functions of order zero, ber(x) + i bei(x) = J0(x e^{3 pi i / 4}).

From their power series below |x| = 30. Its terms grow to about exp(|x| / sqrt(2)) before it converges, losing digits to cancellation: 1e-13 relative at 30, 1e-9 at 60, and none left by 150. From 30 up, the asymptotic expansion in 1/|x|, whose error at 30 is 7e-15 and falls from there. Both functions are even.

Definition at line 643 of file expression.c.

644{
646 const PetscReal abs_x = PetscAbsReal(x);
647 if (abs_x >= 30.0) {
648 const PetscReal argument = abs_x / PetscSqrtReal(2.0);
649 PetscReal coefficient = 1.0, reciprocal = 1.0, total = 0.0;
650 for (PetscInt k = 0; k < 14; ++k) {
651 const PetscReal phase = argument + (0.75 * k - 0.125) * PETSC_PI;
652 total += coefficient * reciprocal * (imaginary ? PetscSinReal(phase) : PetscCosReal(phase));
653 coefficient *= -(2.0 * k + 1.0) * (2.0 * k + 1.0) / (8.0 * (k + 1.0));
654 reciprocal /= abs_x;
655 }
657 return PetscExpReal(argument) / PetscSqrtReal(2.0 * PETSC_PI * abs_x) * total;
658 }
659 const PetscReal q = 0.0625 * x * x * x * x; /* (x/2)^4 */
660 PetscReal term = imaginary ? 0.25 * x * x : 1.0, total = term;
661 /* ber: (-1)^m (x/2)^(4m) / ((2m)!)^2; bei: (-1)^m (x/2)^(4m+2) / ((2m+1)!)^2. */
662 for (PetscInt m = 1; m < 200; ++m) {
663 const PetscReal a = imaginary ? 2.0 * m : 2.0 * m - 1.0, b = a + 1.0;
664 term *= -q / (a * a * b * b);
665 total += term;
666 if (PetscAbsReal(term) <= 1.0e-17 * PetscAbsReal(total)) break;
667 }
669 return total;
670}
Here is the caller graph for this function:

◆ Bessel()

static PetscReal Bessel ( PetscReal  order,
PetscReal  x,
PetscBool  second 
)
static

Bessel function of the first or second kind of integer order; NaN for another order.

Definition at line 673 of file expression.c.

674{
676 if (order != PetscFloorReal(order) || PetscAbsReal(order) > 1.0e6) { PROFILE_HELPER_END; return NAN; }
677 const int n = (int)order;
679 return second ? (PetscReal)yn(n, (double)x) : (PetscReal)jn(n, (double)x);
680}
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 683 of file expression.c.

684{
686 if (PetscIsNanReal(a) || PetscIsNanReal(b)) { PROFILE_HELPER_END; return NAN; }
688 return maximum ? PetscMax(a, b) : PetscMin(a, b);
689}
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 695 of file expression.c.

697{
698 PetscReal stack[expression->max_depth + 1];
699 PetscReal index[EXPRESSION_MAX_SERIES_DEPTH];
700 PetscInt top = 0;
701
702 PetscFunctionBeginUser;
704 PetscCheck(!expression->draws || key, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
705 "An expression that draws random numbers needs a draw key.");
706 for (PetscInt i = 0; i < expression->length; ++i) {
707 const ExpressionInstruction *instruction = &expression->code[i];
708 PetscReal a, b, c;
709
710 switch (instruction->op) {
711 case OP_CONST: stack[top++] = instruction->value; break;
712 case OP_VAR: stack[top++] = values[instruction->arg]; break;
713 case OP_UNIFORM:
714 case OP_NORMAL: stack[top++] = Draw(key, instruction); break;
715 case OP_NEG: stack[top - 1] = -stack[top - 1]; break;
716 case OP_POS: break;
717 case OP_NOT: stack[top - 1] = (stack[top - 1] == 0.0) ? 1.0 : 0.0; break;
718 case OP_ABS: stack[top - 1] = PetscAbsReal(stack[top - 1]); break;
719 case OP_COS: stack[top - 1] = PetscCosReal(stack[top - 1]); break;
720 case OP_EXP: stack[top - 1] = PetscExpReal(stack[top - 1]); break;
721 case OP_SIN: stack[top - 1] = PetscSinReal(stack[top - 1]); break;
722 case OP_SQRT: stack[top - 1] = PetscSqrtReal(stack[top - 1]); break;
723 case OP_TAN: stack[top - 1] = PetscTanReal(stack[top - 1]); break;
724 case OP_ERF: stack[top - 1] = (PetscReal)erf((double)stack[top - 1]); break;
725 case OP_TANH: stack[top - 1] = PetscTanhReal(stack[top - 1]); break;
726 case OP_COSH: stack[top - 1] = PetscCoshReal(stack[top - 1]); break;
727 case OP_SINH: stack[top - 1] = PetscSinhReal(stack[top - 1]); break;
728 case OP_LOG: stack[top - 1] = PetscLogReal(stack[top - 1]); break;
729 case OP_FLOOR: stack[top - 1] = PetscFloorReal(stack[top - 1]); break;
730 case OP_BER: stack[top - 1] = Kelvin(stack[top - 1], PETSC_FALSE); break;
731 case OP_BEI: stack[top - 1] = Kelvin(stack[top - 1], PETSC_TRUE); break;
732 case OP_LOCAL: stack[top++] = index[instruction->arg]; break;
733 case OP_SUM_START:
734 stack[top++] = 0.0;
735 index[instruction->arg] = instruction->value;
736 /* An empty range skips the body and its ACC. */
737 if (instruction->value > expression->code[i + 1].value) i += 2 + expression->code[i + 1].arg;
738 else i += 1;
739 break;
740 case OP_SUM_ACC: {
741 const PetscInt body = (PetscInt)instruction->value;
742 const PetscReal upper = expression->code[i - body - 1].value;
743 const PetscReal term = stack[--top];
744 stack[top - 1] += term;
745 index[instruction->arg] += 1.0;
746 if (index[instruction->arg] <= upper) i -= body + 1;
747 } break;
748 case OP_WHERE:
749 c = stack[--top]; b = stack[--top]; a = stack[top - 1];
750 stack[top - 1] = (a != 0.0) ? b : c;
751 break;
752 default:
753 b = stack[--top]; a = stack[top - 1];
754 switch (instruction->op) {
755 case OP_ADD: a = a + b; break;
756 case OP_SUB: a = a - b; break;
757 case OP_MUL: a = a * b; break;
758 case OP_DIV: a = a / b; break;
759 /* Python's modulus takes the sign of the divisor. */
760 case OP_MOD: a = (b == 0.0) ? NAN : a - b * PetscFloorReal(a / b); break;
761 case OP_POW: a = PetscPowReal(a, b); break;
762 case OP_EQ: a = (a == b) ? 1.0 : 0.0; break;
763 case OP_NE: a = (a != b) ? 1.0 : 0.0; break;
764 case OP_LT: a = (a < b) ? 1.0 : 0.0; break;
765 case OP_LE: a = (a <= b) ? 1.0 : 0.0; break;
766 case OP_GT: a = (a > b) ? 1.0 : 0.0; break;
767 case OP_GE: a = (a >= b) ? 1.0 : 0.0; break;
768 case OP_AND: a = (a != 0.0 && b != 0.0) ? 1.0 : 0.0; break;
769 case OP_OR: a = (a != 0.0 || b != 0.0) ? 1.0 : 0.0; break;
770 case OP_ATAN2: a = PetscAtan2Real(a, b); break;
771 case OP_MAXIMUM: a = Extreme(a, b, PETSC_TRUE); break;
772 case OP_MINIMUM: a = Extreme(a, b, PETSC_FALSE); break;
773 case OP_BESSELJ: a = Bessel(a, b, PETSC_FALSE); break;
774 case OP_BESSELY: a = Bessel(a, b, PETSC_TRUE); break;
775 default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unknown expression opcode %d.", (int)instruction->op);
776 }
777 stack[top - 1] = a;
778 }
779 }
780 *result = stack[0];
782 PetscFunctionReturn(0);
783}
static PetscReal Bessel(PetscReal order, PetscReal x, PetscBool second)
Bessel function of the first or second kind of integer order; NaN for another order.
Definition expression.c:673
static PetscReal Kelvin(PetscReal x, PetscBool imaginary)
Kelvin functions of order zero, ber(x) + i bei(x) = J0(x e^{3 pi i / 4}).
Definition expression.c:643
static PetscReal Draw(const PicurvExpressionDrawKey *key, const ExpressionInstruction *instruction)
A draw keyed on the particle, the event, the stream, and the function.
Definition expression.c:619
static PetscReal Extreme(PetscReal a, PetscReal b, PetscBool maximum)
Maximum or minimum that propagates NaN, as numpy does.
Definition expression.c:683
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 789 of file expression.c.

790{
791 PetscFunctionBeginUser;
793 if (!expression || !*expression) { PROFILE_HELPER_END; PetscFunctionReturn(0); }
794 PetscCall(PetscFree((*expression)->code));
795 PetscCall(PetscFree(*expression));
797 PetscFunctionReturn(0);
798}
Here is the caller graph for this function:

◆ PicurvExpressionUsesVariable()

PetscErrorCode PicurvExpressionUsesVariable ( const PicurvExpression *  expression,
PetscInt  index,
PetscBool *  uses 
)

Implementation of PicurvExpressionUsesVariable().

Whether a compiled expression reads one of its variables.

See also
PicurvExpressionUsesVariable()

Definition at line 805 of file expression.c.

806{
807 PetscFunctionBeginUser;
809 *uses = PETSC_FALSE;
810 for (PetscInt i = 0; i < expression->length && !*uses; ++i) {
811 if (expression->code[i].op == OP_VAR && expression->code[i].arg == index) *uses = PETSC_TRUE;
812 }
814 PetscFunctionReturn(0);
815}
Here is the caller graph for this function:

◆ PicurvVelocitySourceCreate()

PetscErrorCode PicurvVelocitySourceCreate ( const char *  prefix,
PetscBool  on_face,
PetscBool  has_data,
PicurvVelocitySource *  source 
)

Implementation of PicurvVelocitySourceCreate().

Read and compile a velocity source from the options database.

See also
PicurvVelocitySourceCreate()

Definition at line 830 of file expression.c.

832{
833 static const char *const kTime[] = {"t"};
834 char option[PETSC_MAX_PATH_LEN], frame[32], text[PICURV_SOURCE_EXPRESSION_LENGTH];
835 PetscBool found = PETSC_FALSE;
836
837 PetscFunctionBeginUser;
839 PetscCall(PetscMemzero(source, sizeof(*source)));
840 PetscCall(PetscSNPrintf(option, sizeof(option), "-%s_frame", prefix));
841 PetscCall(PetscOptionsGetString(NULL, NULL, option, frame, sizeof(frame), &found));
842 PetscCheck(found || has_data, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT, "Velocity source option %s is required.", option);
843 if (!found) PetscCall(PetscStrncpy(frame, on_face ? "normal" : "cartesian", sizeof(frame)));
844 if (!strcmp(frame, "cartesian")) source->frame = PICURV_SOURCE_FRAME_CARTESIAN;
845 else if (!strcmp(frame, "curvilinear")) source->frame = PICURV_SOURCE_FRAME_CURVILINEAR;
846 else if (!strcmp(frame, "normal") && on_face) source->frame = PICURV_SOURCE_FRAME_NORMAL;
847 else if (!strcmp(frame, "contravariant") && !on_face) source->frame = PICURV_SOURCE_FRAME_CONTRAVARIANT;
848 else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT, "%s is '%s'; expected cartesian, curvilinear, or %s.",
849 option, frame, on_face ? "normal" : "contravariant");
850 source->count = (source->frame == PICURV_SOURCE_FRAME_NORMAL) ? 1 : 3;
851 for (PetscInt c = 0; c < source->count; ++c) {
852 PetscBool uses_t = PETSC_FALSE;
853 PetscCall(PetscSNPrintf(option, sizeof(option), "-%s_expr_%d", prefix, (int)c));
854 PetscCall(PetscOptionsGetString(NULL, NULL, option, text, sizeof(text), &found));
855 if (!found && has_data && c == 0) { source->count = 0; break; }
856 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT, "Velocity source option %s is required.", option);
859 &source->component[c]));
860 PetscCall(PicurvExpressionUsesVariable(source->component[c], PICURV_SOURCE_VAR_T, &uses_t));
861 source->time_dependent = (PetscBool)(source->time_dependent || uses_t);
862 }
863 PetscCall(PetscSNPrintf(option, sizeof(option), "-%s_bulk_velocity", prefix));
864 PetscCall(PetscOptionsGetString(NULL, NULL, option, text, sizeof(text), &found));
865 if (!found && on_face) {
866 PetscCall(PetscSNPrintf(option, sizeof(option), "-%s_flow_rate", prefix));
867 PetscCall(PetscOptionsGetString(NULL, NULL, option, text, sizeof(text), &source->bulk_is_flow_rate));
868 found = source->bulk_is_flow_rate;
869 }
870 if (found) {
871 PetscCall(PicurvExpressionCompile(text, kTime, 1, PETSC_FALSE, &source->bulk));
872 PetscCall(PicurvExpressionUsesVariable(source->bulk, 0, &source->bulk_time_dependent));
873 }
875 PetscFunctionReturn(0);
876}
PetscErrorCode PicurvExpressionUsesVariable(const PicurvExpression *expression, PetscInt index, PetscBool *uses)
Implementation of PicurvExpressionUsesVariable().
Definition expression.c:805
const char *const kPicurvSourceVariableNames[PICURV_SOURCE_VAR_COUNT]
Names of PicurvSourceVariable, in order.
Definition expression.c:821
PetscErrorCode PicurvExpressionCompile(const char *text, const char *const *names, PetscInt name_count, PetscBool allow_random, PicurvExpression **expression)
Implementation of PicurvExpressionCompile().
Definition expression.c:549
#define PICURV_SOURCE_EXPRESSION_LENGTH
Definition expression.c:824
PicurvExpression * bulk
Target mean speed [m/s] in t, or NULL.
Definition expression.h:160
PetscBool bulk_time_dependent
Definition expression.h:161
@ PICURV_SOURCE_VAR_T
Physical time [s].
Definition expression.h:130
@ PICURV_SOURCE_VAR_COUNT
Definition expression.h:139
@ PICURV_SOURCE_VAR_CELL_COUNT
Variables a source reads on every target.
Definition expression.h:137
PicurvExpression * component[3]
Definition expression.h:158
PetscBool time_dependent
Some component reads t.
Definition expression.h:159
PicurvSourceFrame frame
Definition expression.h:156
PetscBool bulk_is_flow_rate
bulk is a volumetric flow rate [m^3/s] through a face instead.
Definition expression.h:162
@ PICURV_SOURCE_FRAME_NORMAL
One value: the speed into the domain through a face.
Definition expression.h:146
@ PICURV_SOURCE_FRAME_CONTRAVARIANT
Three values: the volume flux through the xi, eta, zeta faces; cells only.
Definition expression.h:147
@ PICURV_SOURCE_FRAME_CARTESIAN
Three values: x, y, z velocity components.
Definition expression.h:144
@ PICURV_SOURCE_FRAME_CURVILINEAR
Three values along the unit xi, eta, zeta grid-line directions.
Definition expression.h:145
PetscInt count
1 for the normal frame, otherwise 3; 0 for staged data alone.
Definition expression.h:157
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvVelocitySourceBulkFactor()

PetscErrorCode PicurvVelocitySourceBulkFactor ( const PicurvVelocitySource *  source,
PetscReal  physical_time,
const PetscReal  local[3],
const char *  where,
PetscReal *  factor 
)

Implementation of PicurvVelocitySourceBulkFactor().

Factor on a source's expressions that makes a target's whole mean equal its bulk velocity.

See also
PicurvVelocitySourceBulkFactor()

Definition at line 882 of file expression.c.

884{
885 PetscReal global[3], target;
886
887 PetscFunctionBeginUser;
889 PetscCallMPI(MPI_Allreduce(local, global, 3, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD));
890 PetscCheck(global[2] > 0.0, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
891 "%s has no open area or volume to set a bulk velocity on.", where);
892 PetscCheck(global[0] != 0.0, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
893 "%s: the source's mean speed is zero, so it cannot be scaled to a bulk velocity.", where);
894 PetscCall(PicurvExpressionEvaluate(source->bulk, &physical_time, NULL, &target));
895 if (source->bulk_is_flow_rate) target /= global[2];
896 *factor = (target * global[2] - global[1]) / global[0];
897 LOG_ALLOW(GLOBAL, LOG_DEBUG, "%s: bulk velocity %g, source mean %g, data mean %g, factor %g.\n", where,
898 (double)target, (double)(global[0] / global[2]), (double)(global[1] / global[2]), (double)*factor);
900 PetscFunctionReturn(0);
901}
PetscErrorCode PicurvExpressionEvaluate(const PicurvExpression *expression, const PetscReal *values, const PicurvExpressionDrawKey *key, PetscReal *result)
Implementation of PicurvExpressionEvaluate().
Definition expression.c:695
#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:214
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvVelocitySourceEvaluate()

PetscErrorCode PicurvVelocitySourceEvaluate ( const PicurvVelocitySource *  source,
const PetscReal *  values,
const Cmpnts  edges[3],
Cmpnts *  value 
)

Implementation of PicurvVelocitySourceEvaluate().

Evaluate a source at one point.

See also
PicurvVelocitySourceEvaluate()

Definition at line 907 of file expression.c.

909{
910 PetscReal v[3] = {0.0, 0.0, 0.0};
911
912 PetscFunctionBeginUser;
914 for (PetscInt c = 0; c < source->count; ++c) {
915 PetscCall(PicurvExpressionEvaluate(source->component[c], values, NULL, &v[c]));
916 PetscCheck(!PetscIsInfOrNanReal(v[c]), PETSC_COMM_SELF, PETSC_ERR_FP,
917 "Velocity source value %" PetscInt_FMT " is %g at (x, y, z) = (%g, %g, %g), t = %g.",
918 c, (double)v[c], (double)values[PICURV_SOURCE_VAR_X], (double)values[PICURV_SOURCE_VAR_Y],
919 (double)values[PICURV_SOURCE_VAR_Z], (double)values[PICURV_SOURCE_VAR_T]);
920 }
921 if (source->frame == PICURV_SOURCE_FRAME_CURVILINEAR) {
922 /* Values are components along the unit grid-line directions. */
923 value->x = value->y = value->z = 0.0;
924 for (PetscInt a = 0; a < 3; ++a) {
925 const PetscReal norm = PetscSqrtReal(edges[a].x * edges[a].x + edges[a].y * edges[a].y + edges[a].z * edges[a].z);
926 PetscCheck(norm > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "A cell edge has zero length.");
927 value->x += v[a] * edges[a].x / norm;
928 value->y += v[a] * edges[a].y / norm;
929 value->z += v[a] * edges[a].z / norm;
930 }
931 } else {
932 value->x = v[0];
933 value->y = v[1];
934 value->z = v[2];
935 }
937 PetscFunctionReturn(0);
938}
@ PICURV_SOURCE_VAR_Y
Physical y [m].
Definition expression.h:128
@ PICURV_SOURCE_VAR_X
Physical x of the point [m].
Definition expression.h:127
@ PICURV_SOURCE_VAR_Z
Physical z [m].
Definition expression.h:129
PetscScalar x
Definition variables.h:142
PetscScalar z
Definition variables.h:142
PetscScalar y
Definition variables.h:142
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvSourceCellBulkFactor()

PetscErrorCode PicurvSourceCellBulkFactor ( UserCtx *  user,
const PicurvVelocitySource *  source,
Vec  fraction,
Vec  length,
PetscReal  physical_time,
PetscReal *  factor 
)

Implementation of PicurvSourceCellBulkFactor().

Factor on a source's expressions that makes the volume mean of the whole field, along the source's own direction at each cell, equal its bulk velocity.

See also
PicurvSourceCellBulkFactor()

Definition at line 944 of file expression.c.

946{
947 const DMDALocalInfo *info = &user->info;
948 const PetscReal L_ref = user->simCtx->scaling.L_ref, U_ref = user->simCtx->scaling.U_ref;
949 const PetscInt xs = info->xs, xe = info->xs + info->xm, mx = info->mx;
950 const PetscInt ys = info->ys, ye = info->ys + info->ym, my = info->my;
951 const PetscInt zs = info->zs, ze = info->zs + info->zm, mz = info->mz;
952 PetscReal values[PICURV_SOURCE_VAR_COUNT], local[3] = {0.0, 0.0, 0.0};
953 const Cmpnts ***cent, ***frac, ***len, ***csi, ***eta, ***zet, ***ucat;
954 const PetscReal ***aj;
955
956 PetscFunctionBeginUser;
958 PetscCall(DMDAVecGetArrayRead(user->fda, user->Cent, &cent));
959 PetscCall(DMDAVecGetArrayRead(user->fda, fraction, &frac));
960 PetscCall(DMDAVecGetArrayRead(user->fda, length, &len));
961 PetscCall(DMDAVecGetArrayRead(user->fda, user->lCsi, &csi));
962 PetscCall(DMDAVecGetArrayRead(user->fda, user->lEta, &eta));
963 PetscCall(DMDAVecGetArrayRead(user->fda, user->lZet, &zet));
964 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, &aj));
965 PetscCall(DMDAVecGetArrayRead(user->fda, user->Ucat, &ucat));
966 values[PICURV_SOURCE_VAR_T] = physical_time;
967 for (PetscInt k = (zs == 0) ? 1 : zs; k < ((ze == mz) ? mz - 1 : ze); ++k)
968 for (PetscInt j = (ys == 0) ? 1 : ys; j < ((ye == my) ? my - 1 : ye); ++j)
969 for (PetscInt i = (xs == 0) ? 1 : xs; i < ((xe == mx) ? mx - 1 : xe); ++i) {
970 const PetscReal volume = 1.0 / PetscAbsReal(aj[k][j][i]);
971 Cmpnts edges[3], v;
972 PicurvSourceSetVariables(values, cent[k][j][i], frac[k][j][i], len[k][j][i], L_ref);
973 PetscCall(ComputeCellEdgeVectors(aj[k][j][i], csi[k][j][i], eta[k][j][i], zet[k][j][i], edges));
974 PetscCall(PicurvVelocitySourceEvaluate(source, values, edges, &v));
975 const PetscReal speed = PetscSqrtReal(v.x * v.x + v.y * v.y + v.z * v.z);
976 local[0] += speed * volume;
977 if (speed > 0.0) local[1] += U_ref * (ucat[k][j][i].x * v.x + ucat[k][j][i].y * v.y +
978 ucat[k][j][i].z * v.z) / speed * volume;
979 local[2] += volume;
980 }
981 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Ucat, &ucat));
982 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, &aj));
983 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet));
984 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta));
985 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi));
986 PetscCall(DMDAVecRestoreArrayRead(user->fda, length, &len));
987 PetscCall(DMDAVecRestoreArrayRead(user->fda, fraction, &frac));
988 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Cent, &cent));
989 PetscCall(PicurvVelocitySourceBulkFactor(source, physical_time, local, "The field", factor));
991 PetscFunctionReturn(0);
992}
PetscErrorCode ComputeCellEdgeVectors(PetscReal ajc, Cmpnts csi, Cmpnts eta, Cmpnts zet, Cmpnts edges[3])
Computes a cell's edge vectors along its three grid directions.
Definition Metric.c:710
PetscErrorCode PicurvVelocitySourceBulkFactor(const PicurvVelocitySource *source, PetscReal physical_time, const PetscReal local[3], const char *where, PetscReal *factor)
Implementation of PicurvVelocitySourceBulkFactor().
Definition expression.c:882
PetscErrorCode PicurvVelocitySourceEvaluate(const PicurvVelocitySource *source, const PetscReal *values, const Cmpnts edges[3], Cmpnts *value)
Implementation of PicurvVelocitySourceEvaluate().
Definition expression.c:907
static void PicurvSourceSetVariables(PetscReal *values, Cmpnts point, Cmpnts fraction, Cmpnts length, PetscReal L_ref)
Fill a source's position and grid-line variables at one point.
Definition expression.h:206
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1126
PetscReal L_ref
Definition variables.h:870
ScalingCtx scaling
Definition variables.h:973
DMDALocalInfo info
Definition variables.h:1135
PetscReal U_ref
Definition variables.h:871
A 3D point or vector with PetscScalar components.
Definition variables.h:141
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvSourceAddFluxes()

PetscErrorCode PicurvSourceAddFluxes ( UserCtx *  user,
const PicurvVelocitySource *  source,
Vec  fraction,
Vec  length,
PetscReal  physical_time,
PetscReal  factor,
PetscBool  boundary_faces 
)

Implementation of PicurvSourceAddFluxes().

Add a source's flux to every owned face of Ucont, evaluating it at each face centre.

See also
PicurvSourceAddFluxes()

Definition at line 998 of file expression.c.

1000{
1001 const DMDALocalInfo *info = &user->info;
1002 const PetscReal L_ref = user->simCtx->scaling.L_ref, U_ref = user->simCtx->scaling.U_ref;
1003 const PetscInt start[3] = {info->xs, info->ys, info->zs};
1004 const PetscInt end[3] = {info->xs + info->xm, info->ys + info->ym, info->zs + info->zm};
1005 const PetscInt nodes[3] = {info->mx, info->my, info->mz};
1006 const PetscInt lo[3] = {(start[0] == 0) ? 1 : start[0], (start[1] == 0) ? 1 : start[1], (start[2] == 0) ? 1 : start[2]};
1007 const PetscInt hi[3] = {(end[0] == nodes[0]) ? nodes[0] - 1 : end[0], (end[1] == nodes[1]) ? nodes[1] - 1 : end[1],
1008 (end[2] == nodes[2]) ? nodes[2] - 1 : end[2]};
1009 const Cmpnts ***frac, ***len, ***csi, ***eta, ***zet, ***centx, ***centy, ***centz;
1010 const PetscReal ***aj;
1011 Cmpnts ***ucont;
1012 PetscReal values[PICURV_SOURCE_VAR_COUNT];
1013
1014 PetscFunctionBeginUser;
1016 PetscCall(DMDAVecGetArrayRead(user->fda, fraction, &frac));
1017 PetscCall(DMDAVecGetArrayRead(user->fda, length, &len));
1018 PetscCall(DMDAVecGetArrayRead(user->fda, user->lCsi, &csi));
1019 PetscCall(DMDAVecGetArrayRead(user->fda, user->lEta, &eta));
1020 PetscCall(DMDAVecGetArrayRead(user->fda, user->lZet, &zet));
1021 PetscCall(DMDAVecGetArrayRead(user->fda, user->lCentx, &centx));
1022 PetscCall(DMDAVecGetArrayRead(user->fda, user->lCenty, &centy));
1023 PetscCall(DMDAVecGetArrayRead(user->fda, user->lCentz, &centz));
1024 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, &aj));
1025 PetscCall(DMDAVecGetArray(user->fda, user->Ucont, &ucont));
1026 values[PICURV_SOURCE_VAR_T] = physical_time;
1027 for (PetscInt k = lo[2]; k < hi[2]; ++k)
1028 for (PetscInt j = lo[1]; j < hi[1]; ++j)
1029 for (PetscInt i = lo[0]; i < hi[0]; ++i) {
1030 const PetscInt cell[3] = {i, j, k};
1031 const PetscReal lengths[3] = {len[k][j][i].x, len[k][j][i].y, len[k][j][i].z};
1032 Cmpnts edges[3];
1033 PetscCall(ComputeCellEdgeVectors(aj[k][j][i], csi[k][j][i], eta[k][j][i], zet[k][j][i], edges));
1034 for (PetscInt a = 0; a < 3; ++a) {
1035 const PetscReal half = 0.5 * PetscSqrtReal(edges[a].x * edges[a].x + edges[a].y * edges[a].y +
1036 edges[a].z * edges[a].z) / lengths[a];
1037 /* Each cell owns its + face; the first cell of a line also owns the boundary - face. */
1038 const PetscInt sides = (boundary_faces && start[a] == 0 && cell[a] == 1) ? 2 : 1;
1039 for (PetscInt side = 0; side < sides; ++side) {
1040 const PetscInt offset = side ? -1 : 0;
1041 const PetscInt fi = i + (a == 0 ? offset : 0), fj = j + (a == 1 ? offset : 0), fk = k + (a == 2 ? offset : 0);
1042 const Cmpnts metric = (a == 0) ? csi[fk][fj][fi] : (a == 1) ? eta[fk][fj][fi] : zet[fk][fj][fi];
1043 const Cmpnts centre = (a == 0) ? centx[fk][fj][fi] : (a == 1) ? centy[fk][fj][fi] : centz[fk][fj][fi];
1044 PetscReal flux;
1045 PicurvSourceSetVariables(values, centre, frac[k][j][i], len[k][j][i], L_ref);
1046 values[PICURV_SOURCE_VAR_XI + a] += side ? -half : half;
1048 PetscReal value;
1049 PetscCall(PicurvExpressionEvaluate(source->component[a], values, NULL, &value));
1050 flux = factor * value / (U_ref * L_ref * L_ref);
1051 } else if (source->frame == PICURV_SOURCE_FRAME_CURVILINEAR) {
1052 const PetscReal area = PetscSqrtReal(metric.x * metric.x + metric.y * metric.y + metric.z * metric.z);
1053 PetscReal value;
1054 PetscCall(PicurvExpressionEvaluate(source->component[a], values, NULL, &value));
1055 flux = factor * (value / U_ref) * user->GridOrientation * area;
1056 } else {
1057 Cmpnts v;
1058 PetscCall(PicurvVelocitySourceEvaluate(source, values, edges, &v));
1059 flux = (factor * v.x / U_ref) * metric.x + (factor * v.y / U_ref) * metric.y +
1060 (factor * v.z / U_ref) * metric.z;
1061 }
1062 if (a == 0) ucont[fk][fj][fi].x += flux;
1063 if (a == 1) ucont[fk][fj][fi].y += flux;
1064 if (a == 2) ucont[fk][fj][fi].z += flux;
1065 }
1066 }
1067 }
1068 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucont, &ucont));
1069 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, &aj));
1070 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lCentz, &centz));
1071 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lCenty, &centy));
1072 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lCentx, &centx));
1073 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet));
1074 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta));
1075 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi));
1076 PetscCall(DMDAVecRestoreArrayRead(user->fda, length, &len));
1077 PetscCall(DMDAVecRestoreArrayRead(user->fda, fraction, &frac));
1079 PetscFunctionReturn(0);
1080}
@ PICURV_SOURCE_VAR_XI
Arc-length fraction along the xi grid line, 0 at the - face.
Definition expression.h:131
Vec Ucont
Definition variables.h:1162
Vec lCenty
Definition variables.h:1218
Vec lCentx
Definition variables.h:1218
PetscInt GridOrientation
Definition variables.h:1141
Vec lCentz
Definition variables.h:1218
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvSourceSetCellPressure()

PetscErrorCode PicurvSourceSetCellPressure ( UserCtx *  user,
const PicurvExpression *  pressure,
Vec  fraction,
Vec  length,
PetscReal  physical_time 
)

Implementation of PicurvSourceSetCellPressure().

Set a block's pressure at every interior cell centre from an expression in Pa.

See also
PicurvSourceSetCellPressure()

Definition at line 1086 of file expression.c.

1088{
1089 const DMDALocalInfo *info = &user->info;
1090 const ScalingCtx *scaling = &user->simCtx->scaling;
1091 const PetscInt xs = info->xs, xe = info->xs + info->xm, mx = info->mx;
1092 const PetscInt ys = info->ys, ye = info->ys + info->ym, my = info->my;
1093 const PetscInt zs = info->zs, ze = info->zs + info->zm, mz = info->mz;
1094 const Cmpnts ***cent, ***frac, ***len;
1095 PetscReal ***p, values[PICURV_SOURCE_VAR_COUNT];
1096
1097 PetscFunctionBeginUser;
1099 PetscCall(DMDAVecGetArrayRead(user->fda, user->Cent, &cent));
1100 PetscCall(DMDAVecGetArrayRead(user->fda, fraction, &frac));
1101 PetscCall(DMDAVecGetArrayRead(user->fda, length, &len));
1102 PetscCall(DMDAVecGetArray(user->da, user->P, &p));
1103 values[PICURV_SOURCE_VAR_T] = physical_time;
1104 for (PetscInt k = (zs == 0) ? 1 : zs; k < ((ze == mz) ? mz - 1 : ze); ++k)
1105 for (PetscInt j = (ys == 0) ? 1 : ys; j < ((ye == my) ? my - 1 : ye); ++j)
1106 for (PetscInt i = (xs == 0) ? 1 : xs; i < ((xe == mx) ? mx - 1 : xe); ++i) {
1107 PetscReal value;
1108 PicurvSourceSetVariables(values, cent[k][j][i], frac[k][j][i], len[k][j][i], scaling->L_ref);
1109 PetscCall(PicurvExpressionEvaluate(pressure, values, NULL, &value));
1110 p[k][j][i] = value / scaling->P_ref;
1111 }
1112 PetscCall(DMDAVecRestoreArray(user->da, user->P, &p));
1113 PetscCall(DMDAVecRestoreArrayRead(user->fda, length, &len));
1114 PetscCall(DMDAVecRestoreArrayRead(user->fda, fraction, &frac));
1115 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->Cent, &cent));
1117 PetscFunctionReturn(0);
1118}
PetscReal P_ref
Definition variables.h:873
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvSourceFaceSlotsCreate()

PetscErrorCode PicurvSourceFaceSlotsCreate ( UserCtx *  user,
BCFace  face_id,
Vec  fraction,
Vec  length,
PicurvSourceFaceSlots *  slots 
)

Implementation of PicurvSourceFaceSlotsCreate().

Gather each owned slot of a boundary face: face-centre position, the grid-line fractions and lengths of the interior cell behind it, and its directions.

See also
PicurvSourceFaceSlotsCreate()

Definition at line 1124 of file expression.c.

1126{
1127 const DMDALocalInfo *info = &user->info;
1128 const PetscInt start[3] = {info->xs, info->ys, info->zs};
1129 const PetscInt end[3] = {info->xs + info->xm, info->ys + info->ym, info->zs + info->zm};
1130 const PetscInt nodes[3] = {info->mx, info->my, info->mz};
1131 const PetscReal L_ref = user->simCtx->scaling.L_ref;
1132 const PetscBool negative = (PetscBool)((PetscInt)face_id % 2 == 0);
1133 PetscBool can_service = PETSC_FALSE;
1134 PetscInt n_slots;
1135 Cmpnts face_centre;
1136 PetscReal face_area;
1137
1138 PetscFunctionBeginUser;
1140 PetscCall(CalculateFaceCenterAndArea(user, face_id, &face_centre, &face_area));
1141 slots->normal = (PetscInt)face_id / 2;
1142 slots->a = (slots->normal == 0) ? 1 : 0;
1143 slots->b = (slots->normal == 2) ? 1 : 2;
1144 const PetscInt normal = slots->normal, a = slots->a, b = slots->b;
1145 PetscCall(CanRankServiceFace(info, user->IM, user->JM, user->KM, face_id, &can_service));
1146 slots->layer = negative ? start[normal] : nodes[normal] - 2;
1147 slots->lo[0] = (start[a] == 0) ? 1 : start[a];
1148 slots->lo[1] = (start[b] == 0) ? 1 : start[b];
1149 slots->n[0] = can_service ? ((end[a] == nodes[a]) ? nodes[a] - 1 : end[a]) - slots->lo[0] : 0;
1150 slots->n[1] = can_service ? ((end[b] == nodes[b]) ? nodes[b] - 1 : end[b]) - slots->lo[1] : 0;
1151 n_slots = slots->n[0] * slots->n[1];
1152 PetscCall(PetscMalloc2(n_slots, &slots->values, n_slots, &slots->edges));
1153
1154 if (n_slots > 0) {
1155 /* The interior cell behind the slot layer. */
1156 const PetscInt cell = negative ? slots->layer + 1 : slots->layer;
1157 const Vec centres = (normal == 0) ? user->lCentx : (normal == 1) ? user->lCenty : user->lCentz;
1158 const Cmpnts ***cent, ***frac, ***len, ***csi, ***eta, ***zet;
1159 const PetscReal ***aj;
1160 PetscCall(DMDAVecGetArrayRead(user->fda, centres, &cent));
1161 PetscCall(DMDAVecGetArrayRead(user->fda, fraction, &frac));
1162 PetscCall(DMDAVecGetArrayRead(user->fda, length, &len));
1163 PetscCall(DMDAVecGetArrayRead(user->fda, user->lCsi, &csi));
1164 PetscCall(DMDAVecGetArrayRead(user->fda, user->lEta, &eta));
1165 PetscCall(DMDAVecGetArrayRead(user->fda, user->lZet, &zet));
1166 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, &aj));
1167 for (PetscInt q = 0; q < slots->n[1]; ++q)
1168 for (PetscInt p = 0; p < slots->n[0]; ++p) {
1169 const PetscInt s = p + slots->n[0] * q;
1170 PetscInt f[3], c[3];
1171 f[normal] = slots->layer; f[a] = slots->lo[0] + p; f[b] = slots->lo[1] + q;
1172 c[normal] = cell; c[a] = f[a]; c[b] = f[b];
1173 PicurvSourceSetVariables(slots->values[s], cent[f[2]][f[1]][f[0]], frac[c[2]][c[1]][c[0]], len[c[2]][c[1]][c[0]], L_ref);
1174 slots->values[s][PICURV_SOURCE_VAR_T] = 0.0;
1175 /* On the face itself the fraction along the normal line is exactly 0 or 1. */
1176 slots->values[s][PICURV_SOURCE_VAR_XI + normal] = negative ? 0.0 : 1.0;
1177 {
1178 const Cmpnts X = cent[f[2]][f[1]][f[0]];
1179 const PetscReal dx = X.x - face_centre.x, dy = X.y - face_centre.y, dz = X.z - face_centre.z;
1180 slots->values[s][PICURV_SOURCE_VAR_R] = PetscSqrtReal(dx * dx + dy * dy + dz * dz) * L_ref;
1181 }
1182 PetscCall(ComputeCellEdgeVectors(aj[c[2]][c[1]][c[0]], csi[c[2]][c[1]][c[0]], eta[c[2]][c[1]][c[0]], zet[c[2]][c[1]][c[0]], slots->edges[s]));
1183 const Cmpnts m = (normal == 0) ? csi[f[2]][f[1]][f[0]] : (normal == 1) ? eta[f[2]][f[1]][f[0]] : zet[f[2]][f[1]][f[0]];
1184 const Cmpnts through = {user->GridOrientation * m.x, user->GridOrientation * m.y, user->GridOrientation * m.z};
1185 slots->edges[s][normal] = through;
1186 }
1187 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, &aj));
1188 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet));
1189 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta));
1190 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi));
1191 PetscCall(DMDAVecRestoreArrayRead(user->fda, length, &len));
1192 PetscCall(DMDAVecRestoreArrayRead(user->fda, fraction, &frac));
1193 PetscCall(DMDAVecRestoreArrayRead(user->fda, centres, &cent));
1194 }
1196 PetscFunctionReturn(0);
1197}
PetscErrorCode CanRankServiceFace(const DMDALocalInfo *info, PetscInt IM_nodes_global, PetscInt JM_nodes_global, PetscInt KM_nodes_global, BCFace face_id, PetscBool *can_service_out)
Determines if the current MPI rank owns any part of a specified global face.
Definition Boundaries.c:22
PetscInt normal
Logical axis of the face normal.
Definition expression.h:222
PetscInt n[2]
Owned slots along the two tangential axes; 0 off the face.
Definition expression.h:226
@ PICURV_SOURCE_VAR_R
On a face, distance from the face's centre [m].
Definition expression.h:138
PetscInt b
The two tangential axes.
Definition expression.h:223
PetscInt layer
Index along the normal of the face's metric and flux.
Definition expression.h:224
PetscReal(* values)[PICURV_SOURCE_VAR_COUNT]
Variables per slot; t is the caller's.
Definition expression.h:227
Cmpnts(* edges)[3]
Directions per slot, for the curvilinear frame.
Definition expression.h:228
PetscInt lo[2]
First tangential indices of the owned slots.
Definition expression.h:225
PetscErrorCode CalculateFaceCenterAndArea(UserCtx *user, BCFace face_id, Cmpnts *face_center, PetscReal *face_area)
Calculates the geometric center and total area of a specified boundary face.
Definition grid.c:1211
PetscInt KM
Definition variables.h:1137
PetscInt JM
Definition variables.h:1137
PetscInt IM
Definition variables.h:1137
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvSourceFaceSlotsDestroy()

PetscErrorCode PicurvSourceFaceSlotsDestroy ( PicurvSourceFaceSlots *  slots)

Implementation of PicurvSourceFaceSlotsDestroy().

Release what PicurvSourceFaceSlotsCreate() allocated.

See also
PicurvSourceFaceSlotsDestroy()

Definition at line 1203 of file expression.c.

1204{
1205 PetscFunctionBeginUser;
1207 PetscCall(PetscFree2(slots->values, slots->edges));
1209 PetscFunctionReturn(0);
1210}
Here is the caller graph for this function:

◆ PicurvVelocitySourceDestroy()

PetscErrorCode PicurvVelocitySourceDestroy ( PicurvVelocitySource *  source)

Implementation of PicurvVelocitySourceDestroy().

Free a source's compiled expressions and bulk velocity.

See also
PicurvVelocitySourceDestroy()

Definition at line 1216 of file expression.c.

1217{
1218 PetscFunctionBeginUser;
1220 for (PetscInt c = 0; c < 3; ++c) PetscCall(PicurvExpressionDestroy(&source->component[c]));
1221 PetscCall(PicurvExpressionDestroy(&source->bulk));
1222 source->count = 0;
1224 PetscFunctionReturn(0);
1225}
PetscErrorCode PicurvExpressionDestroy(PicurvExpression **expression)
Implementation of PicurvExpressionDestroy().
Definition expression.c:789
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},
{"erf", 1, 1, OP_ERF, PETSC_FALSE}, {"tanh", 1, 1, OP_TANH, PETSC_FALSE},
{"cosh", 1, 1, OP_COSH, PETSC_FALSE}, {"sinh", 1, 1, OP_SINH, PETSC_FALSE},
{"log", 1, 1, OP_LOG, PETSC_FALSE}, {"floor", 1, 1, OP_FLOOR, PETSC_FALSE},
{"atan2", 2, 2, OP_ATAN2, PETSC_FALSE},
{"maximum", 2, 2, OP_MAXIMUM, PETSC_FALSE}, {"minimum", 2, 2, OP_MINIMUM, PETSC_FALSE},
{"where", 3, 3, OP_WHERE, PETSC_FALSE},
{"besselj", 2, 2, OP_BESSELJ, PETSC_FALSE}, {"bessely", 2, 2, OP_BESSELY, PETSC_FALSE},
{"ber", 1, 1, OP_BER, PETSC_FALSE}, {"bei", 1, 1, OP_BEI, PETSC_FALSE},
{"uniform", 0, 1, OP_UNIFORM, PETSC_TRUE}, {"normal", 0, 1, OP_NORMAL, PETSC_TRUE},
}

Name, arity, and opcode of every callable function.

◆ kPicurvSourceVariableNames

const char* const kPicurvSourceVariableNames[PICURV_SOURCE_VAR_COUNT]
Initial value:
= {
"x", "y", "z", "t", "xi", "eta", "zeta", "L_xi", "L_eta", "L_zeta", "r"}

Names of PicurvSourceVariable, in order.

Definition at line 821 of file expression.c.

821 {
822 "x", "y", "z", "t", "xi", "eta", "zeta", "L_xi", "L_eta", "L_zeta", "r"};