PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
expression.c
Go to the documentation of this file.
1/**
2 * @file expression.c
3 * @brief The expression language shared by every configured field: a parser, a postfix
4 * compiler, and a pure stack evaluator.
5 *
6 * Expressions are parsed into a small tree, because a chained comparison evaluates its
7 * middle operand twice, and compiled to postfix code that a stack evaluator runs once per
8 * point. A series `sum(k, lo, hi, body)` compiles its body once and loops over it, with the
9 * index in a slot of its nesting level. Evaluation is pure: random draws are hashed from a caller-supplied key, so a value
10 * does not depend on rank count, evaluation order, or how often it is evaluated.
11 */
12
13#include "expression.h"
14#include "Metric.h"
15#include "Boundaries.h"
16#include "grid.h"
17
18#include <ctype.h>
19#include <math.h>
20#include <stdint.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include "logging.h"
25/* ------------------------------------------------------------------------- */
26/* Expression tree */
27/* ------------------------------------------------------------------------- */
28
29typedef enum {
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. */
41
42/** @brief Name, arity, and opcode of every callable function. */
43static const struct {
44 const char *name;
45 int min_args;
46 int max_args;
47 ExpressionOp op;
48 PetscBool random;
49} kFunctions[] = {
50 {"abs", 1, 1, OP_ABS, PETSC_FALSE}, {"cos", 1, 1, OP_COS, PETSC_FALSE},
51 {"exp", 1, 1, OP_EXP, PETSC_FALSE}, {"sin", 1, 1, OP_SIN, PETSC_FALSE},
52 {"sqrt", 1, 1, OP_SQRT, PETSC_FALSE}, {"tan", 1, 1, OP_TAN, PETSC_FALSE},
53 {"erf", 1, 1, OP_ERF, PETSC_FALSE}, {"tanh", 1, 1, OP_TANH, PETSC_FALSE},
54 {"cosh", 1, 1, OP_COSH, PETSC_FALSE}, {"sinh", 1, 1, OP_SINH, PETSC_FALSE},
55 {"log", 1, 1, OP_LOG, PETSC_FALSE}, {"floor", 1, 1, OP_FLOOR, PETSC_FALSE},
56 {"atan2", 2, 2, OP_ATAN2, PETSC_FALSE},
57 {"maximum", 2, 2, OP_MAXIMUM, PETSC_FALSE}, {"minimum", 2, 2, OP_MINIMUM, PETSC_FALSE},
58 {"where", 3, 3, OP_WHERE, PETSC_FALSE},
59 {"besselj", 2, 2, OP_BESSELJ, PETSC_FALSE}, {"bessely", 2, 2, OP_BESSELY, PETSC_FALSE},
60 {"ber", 1, 1, OP_BER, PETSC_FALSE}, {"bei", 1, 1, OP_BEI, PETSC_FALSE},
61 {"uniform", 0, 1, OP_UNIFORM, PETSC_TRUE}, {"normal", 0, 1, OP_NORMAL, PETSC_TRUE},
62};
63
64/** @brief Series that may nest inside one another, and terms one series may add. */
65#define EXPRESSION_MAX_SERIES_DEPTH 4
66#define EXPRESSION_MAX_SERIES_TERMS 1000000
67
68/** @brief One tree node; children index the node pool. */
69typedef struct {
71 PetscReal value; /* OP_CONST value; explicit stream of a draw; a series' lower bound */
72 PetscReal upper; /* A series' upper bound */
73 PetscInt var; /* OP_VAR index; 1 for a draw on an explicit stream; an index slot */
74 PetscInt child[3];
75 PetscInt nchild;
77
78/** @brief One postfix instruction. */
79typedef struct {
81 PetscReal value;
82 PetscInt arg;
84
87 PetscInt length;
88 PetscInt max_depth;
89 PetscBool draws;
90};
91
92/** @brief Parser state: the text, a cursor, the node pool, and the names in scope. */
93typedef struct {
94 const char *text;
95 size_t pos;
97 PetscInt count;
98 PetscInt capacity;
99 const char *const *names;
100 PetscInt name_count;
101 PetscBool allow_random;
102 char series_index[EXPRESSION_MAX_SERIES_DEPTH][64]; /* Indices in scope, outermost first */
103 PetscInt series_depth;
104 char error[256];
106
107/** @brief Record the first parse error with its position; later ones are consequences. */
108static void ParserFail(ExpressionParser *parser, const char *message)
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}
117
118/** @brief Skip whitespace before the next token. */
119static void SkipSpace(ExpressionParser *parser)
120{
122 while (parser->text[parser->pos] && isspace((unsigned char)parser->text[parser->pos])) parser->pos++;
124}
125
126/** @brief Consume @p token if it is next, and report whether it was. */
127static PetscBool Accept(ExpressionParser *parser, const char *token)
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}
148
149/** @brief Append a node to the pool and return its index, or -1 once an error is set. */
150static PetscInt NewNode(ExpressionParser *parser, ExpressionOp op, PetscInt nchild,
151 PetscInt a, PetscInt b, PetscInt 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}
170
171static PetscInt ParseOr(ExpressionParser *parser);
172
173/** @brief The value of a series bound: an integer literal, possibly negated or parenthesized. */
174static PetscBool SeriesBound(const ExpressionParser *parser, PetscInt index, PetscReal *bound)
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}
187
188/** @brief `sum(` has been read: index name, integer bounds, then the body with the index in scope. */
189static PetscInt ParseSeries(ExpressionParser *parser)
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}
245
246/** @brief number | name | name(args) | sum(index, lo, hi, body) | (expression) */
247static PetscInt ParseAtom(ExpressionParser *parser)
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}
374
375static PetscInt ParseUnary(ExpressionParser *parser);
376
377/**
378 * @brief Parses an atom and, when a power operator follows, appends a power node.
379 * @details Grammar: `atom ['**' unary]`.
380 * The exponent is parsed as a unary, so `2**-1` is valid and `a**b**c` groups to
381 * the right; a unary minus on the left is parsed by the caller, so `-2**2` is -4.
382 * @param[in,out] parser Parser state; its position advances past the power.
383 * @return Node index of the power (or of the bare atom), or -1 after an error.
384 */
385static PetscInt ParsePower(ExpressionParser *parser)
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}
393
394/**
395 * @brief Parses a sign prefix recursively and wraps the operand in a negate or identity node.
396 * @param[in,out] parser Parser state; its position advances past the operand.
397 * @return Node index of the signed operand, or -1 after an error.
398 */
399static PetscInt ParseUnary(ExpressionParser *parser)
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}
407
408/**
409 * @brief Parses a left-associative chain of `*`, `/`, `%`, folding each operator into a node.
410 * @details `//` is refused explicitly, since the Python side does not accept floor division.
411 * @param[in,out] parser Parser state; its position advances past the term.
412 * @return Node index of the term, or -1 after an error.
413 */
414static PetscInt ParseTerm(ExpressionParser *parser)
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}
428
429/**
430 * @brief Parses a left-associative chain of `+` and `-`, folding each operator into a node.
431 * @param[in,out] parser Parser state; its position advances past the sum.
432 * @return Node index of the sum, or -1 after an error.
433 */
434static PetscInt ParseSum(ExpressionParser *parser)
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}
445
446/** @brief sum (comparison sum)*; a chain `a < b < c` is `(a < b) and (b < c)`. */
447static PetscInt ParseComparison(ExpressionParser *parser)
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}
468
469/** @brief 'not' not | comparison */
470static PetscInt ParseNot(ExpressionParser *parser)
471{
473 if (Accept(parser, "not")) { PROFILE_FUNCTION_END; return NewNode(parser, OP_NOT, 1, ParseNot(parser), -1, -1); }
475 return ParseComparison(parser);
476}
477
478/** @brief not ('and' not)* */
479static PetscInt ParseAnd(ExpressionParser *parser)
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}
487
488/** @brief and ('or' and)* */
489static PetscInt ParseOr(ExpressionParser *parser)
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}
497
498static PetscInt CountCode(const ExpressionParser *parser, PetscInt index);
499
500/** @brief Emit postfix code for a subtree and track the stack depth it needs. */
501static void EmitNode(const ExpressionParser *parser, PetscInt index, PicurvExpression *expression,
502 PetscInt *depth)
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}
532
533/** @brief Count the instructions a subtree emits (shared subtrees count per use). */
534static PetscInt CountCode(const ExpressionParser *parser, PetscInt index)
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}
544
545/**
546 * @brief Implementation of \ref PicurvExpressionCompile().
547 * @see PicurvExpressionCompile()
548 */
549PetscErrorCode PicurvExpressionCompile(const char *text, const char *const *names, PetscInt name_count,
550 PetscBool allow_random, PicurvExpression **expression)
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}
582
583/**
584 * @brief Implementation of \ref PicurvMix64().
585 * @see PicurvMix64()
586 */
587uint64_t PicurvMix64(uint64_t z)
588{
589 z += 0x9E3779B97F4A7C15ULL;
590 z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL;
591 z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL;
592 return z ^ (z >> 31);
593}
594
595/**
596 * @brief Implementation of \ref PicurvUnitFromHash().
597 * @see PicurvUnitFromHash()
598 */
599PetscReal PicurvUnitFromHash(uint64_t hash)
600{
601 return (PetscReal)(hash >> 11) * (1.0 / 9007199254740992.0);
602}
603
604/**
605 * @brief Implementation of \ref PicurvParticleDrawHash().
606 * @see PicurvParticleDrawHash()
607 */
608uint64_t PicurvParticleDrawHash(PetscInt64 seed, PicurvDrawSpace space, uint64_t stream,
609 PetscInt64 pid, PetscInt64 salt)
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}
617
618/** @brief A draw keyed on the particle, the event, the stream, and the function. */
619static PetscReal Draw(const PicurvExpressionDrawKey *key, const ExpressionInstruction *instruction)
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}
635
636/**
637 * @brief Kelvin functions of order zero, `ber(x) + i bei(x) = J0(x e^{3 pi i / 4})`.
638 * @details From their power series below |x| = 30. Its terms grow to about `exp(|x| / sqrt(2))`
639 * before it converges, losing digits to cancellation: 1e-13 relative at 30, 1e-9 at 60, and
640 * none left by 150. From 30 up, the asymptotic expansion in `1/|x|`, whose error at 30 is
641 * 7e-15 and falls from there. Both functions are even.
642 */
643static PetscReal Kelvin(PetscReal x, PetscBool imaginary)
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}
671
672/** @brief Bessel function of the first or second kind of integer order; NaN for another order. */
673static PetscReal Bessel(PetscReal order, PetscReal x, PetscBool second)
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}
681
682/** @brief Maximum or minimum that propagates NaN, as numpy does. */
683static PetscReal Extreme(PetscReal a, PetscReal b, PetscBool maximum)
684{
686 if (PetscIsNanReal(a) || PetscIsNanReal(b)) { PROFILE_HELPER_END; return NAN; }
688 return maximum ? PetscMax(a, b) : PetscMin(a, b);
689}
690
691/**
692 * @brief Implementation of \ref PicurvExpressionEvaluate().
693 * @see PicurvExpressionEvaluate()
694 */
695PetscErrorCode PicurvExpressionEvaluate(const PicurvExpression *expression, const PetscReal *values,
696 const PicurvExpressionDrawKey *key, PetscReal *result)
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}
784
785/**
786 * @brief Implementation of \ref PicurvExpressionDestroy().
787 * @see PicurvExpressionDestroy()
788 */
789PetscErrorCode PicurvExpressionDestroy(PicurvExpression **expression)
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}
799
800
801/**
802 * @brief Implementation of \ref PicurvExpressionUsesVariable().
803 * @see PicurvExpressionUsesVariable()
804 */
805PetscErrorCode PicurvExpressionUsesVariable(const PicurvExpression *expression, PetscInt index, PetscBool *uses)
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}
816
817/* ------------------------------------------------------------------------- */
818/* Prescribed velocity sources */
819/* ------------------------------------------------------------------------- */
820
822 "x", "y", "z", "t", "xi", "eta", "zeta", "L_xi", "L_eta", "L_zeta", "r"};
823
824#define PICURV_SOURCE_EXPRESSION_LENGTH 4096
825
826/**
827 * @brief Implementation of \ref PicurvVelocitySourceCreate().
828 * @see PicurvVelocitySourceCreate()
829 */
830PetscErrorCode PicurvVelocitySourceCreate(const char *prefix, PetscBool on_face, PetscBool has_data,
831 PicurvVelocitySource *source)
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}
877
878/**
879 * @brief Implementation of \ref PicurvVelocitySourceBulkFactor().
880 * @see PicurvVelocitySourceBulkFactor()
881 */
882PetscErrorCode PicurvVelocitySourceBulkFactor(const PicurvVelocitySource *source, PetscReal physical_time,
883 const PetscReal local[3], const char *where, PetscReal *factor)
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}
902
903/**
904 * @brief Implementation of \ref PicurvVelocitySourceEvaluate().
905 * @see PicurvVelocitySourceEvaluate()
906 */
907PetscErrorCode PicurvVelocitySourceEvaluate(const PicurvVelocitySource *source, const PetscReal *values,
908 const Cmpnts edges[3], Cmpnts *value)
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}
939
940/**
941 * @brief Implementation of \ref PicurvSourceCellBulkFactor().
942 * @see PicurvSourceCellBulkFactor()
943 */
944PetscErrorCode PicurvSourceCellBulkFactor(UserCtx *user, const PicurvVelocitySource *source, Vec fraction, Vec length,
945 PetscReal physical_time, PetscReal *factor)
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}
993
994/**
995 * @brief Implementation of \ref PicurvSourceAddFluxes().
996 * @see PicurvSourceAddFluxes()
997 */
998PetscErrorCode PicurvSourceAddFluxes(UserCtx *user, const PicurvVelocitySource *source, Vec fraction, Vec length,
999 PetscReal physical_time, PetscReal factor, PetscBool boundary_faces)
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}
1081
1082/**
1083 * @brief Implementation of \ref PicurvSourceSetCellPressure().
1084 * @see PicurvSourceSetCellPressure()
1085 */
1086PetscErrorCode PicurvSourceSetCellPressure(UserCtx *user, const PicurvExpression *pressure, Vec fraction, Vec length,
1087 PetscReal physical_time)
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}
1119
1120/**
1121 * @brief Implementation of \ref PicurvSourceFaceSlotsCreate().
1122 * @see PicurvSourceFaceSlotsCreate()
1123 */
1124PetscErrorCode PicurvSourceFaceSlotsCreate(UserCtx *user, BCFace face_id, Vec fraction, Vec length,
1125 PicurvSourceFaceSlots *slots)
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}
1198
1199/**
1200 * @brief Implementation of \ref PicurvSourceFaceSlotsDestroy().
1201 * @see PicurvSourceFaceSlotsDestroy()
1202 */
1204{
1205 PetscFunctionBeginUser;
1207 PetscCall(PetscFree2(slots->values, slots->edges));
1209 PetscFunctionReturn(0);
1210}
1211
1212/**
1213 * @brief Implementation of \ref PicurvVelocitySourceDestroy().
1214 * @see PicurvVelocitySourceDestroy()
1215 */
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 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
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
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
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 ParseAtom(ExpressionParser *parser)
number | name | name(args) | sum(index, lo, hi, body) | (expression)
Definition expression.c:247
#define EXPRESSION_MAX_SERIES_TERMS
Definition expression.c:66
static void SkipSpace(ExpressionParser *parser)
Skip whitespace before the next token.
Definition expression.c:119
PetscReal value
Definition expression.c:71
PetscBool draws
Definition expression.c:89
PetscErrorCode PicurvExpressionEvaluate(const PicurvExpression *expression, const PetscReal *values, const PicurvExpressionDrawKey *key, PetscReal *result)
Implementation of PicurvExpressionEvaluate().
Definition expression.c:695
uint64_t PicurvMix64(uint64_t z)
Implementation of PicurvMix64().
Definition expression.c:587
#define EXPRESSION_MAX_SERIES_DEPTH
Series that may nest inside one another, and terms one series may add.
Definition expression.c:65
PetscErrorCode PicurvExpressionDestroy(PicurvExpression **expression)
Implementation of PicurvExpressionDestroy().
Definition expression.c:789
PetscBool allow_random
Definition expression.c:101
PetscErrorCode PicurvSourceSetCellPressure(UserCtx *user, const PicurvExpression *pressure, Vec fraction, Vec length, PetscReal physical_time)
Implementation of PicurvSourceSetCellPressure().
PetscErrorCode PicurvVelocitySourceCreate(const char *prefix, PetscBool on_face, PetscBool has_data, PicurvVelocitySource *source)
Implementation of PicurvVelocitySourceCreate().
Definition expression.c:830
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 const struct @0 kFunctions[]
Name, arity, and opcode of every callable function.
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 PetscInt ParseNot(ExpressionParser *parser)
'not' not | comparison
Definition expression.c:470
static PetscInt CountCode(const ExpressionParser *parser, PetscInt index)
Count the instructions a subtree emits (shared subtrees count per use).
Definition expression.c:534
PetscInt var
Definition expression.c:73
PetscErrorCode PicurvSourceAddFluxes(UserCtx *user, const PicurvVelocitySource *source, Vec fraction, Vec length, PetscReal physical_time, PetscReal factor, PetscBool boundary_faces)
Implementation of PicurvSourceAddFluxes().
Definition expression.c:998
PetscInt series_depth
Definition expression.c:103
PetscInt max_depth
Definition expression.c:88
const char *const * names
Definition expression.c:99
PetscInt nchild
Definition expression.c:75
PetscErrorCode PicurvSourceCellBulkFactor(UserCtx *user, const PicurvVelocitySource *source, Vec fraction, Vec length, PetscReal physical_time, PetscReal *factor)
Implementation of PicurvSourceCellBulkFactor().
Definition expression.c:944
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
ExpressionInstruction * code
Definition expression.c:86
static PetscBool Accept(ExpressionParser *parser, const char *token)
Consume token if it is next, and report whether it was.
Definition expression.c:127
PetscErrorCode PicurvExpressionUsesVariable(const PicurvExpression *expression, PetscInt index, PetscBool *uses)
Implementation of PicurvExpressionUsesVariable().
Definition expression.c:805
PetscErrorCode PicurvVelocitySourceDestroy(PicurvVelocitySource *source)
Implementation of PicurvVelocitySourceDestroy().
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
const char * text
Definition expression.c:94
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
const char *const kPicurvSourceVariableNames[PICURV_SOURCE_VAR_COUNT]
Names of PicurvSourceVariable, in order.
Definition expression.c:821
PetscErrorCode PicurvVelocitySourceBulkFactor(const PicurvVelocitySource *source, PetscReal physical_time, const PetscReal local[3], const char *where, PetscReal *factor)
Implementation of PicurvVelocitySourceBulkFactor().
Definition expression.c:882
static void ParserFail(ExpressionParser *parser, const char *message)
Record the first parse error with its position; later ones are consequences.
Definition expression.c:108
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
char series_index[4][64]
Definition expression.c:102
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
PetscReal PicurvUnitFromHash(uint64_t hash)
Implementation of PicurvUnitFromHash().
Definition expression.c:599
PetscErrorCode PicurvVelocitySourceEvaluate(const PicurvVelocitySource *source, const PetscReal *values, const Cmpnts edges[3], Cmpnts *value)
Implementation of PicurvVelocitySourceEvaluate().
Definition expression.c:907
PetscErrorCode PicurvSourceFaceSlotsCreate(UserCtx *user, BCFace face_id, Vec fraction, Vec length, PicurvSourceFaceSlots *slots)
Implementation of PicurvSourceFaceSlotsCreate().
PetscErrorCode PicurvExpressionCompile(const char *text, const char *const *names, PetscInt name_count, PetscBool allow_random, PicurvExpression **expression)
Implementation of PicurvExpressionCompile().
Definition expression.c:549
uint64_t PicurvParticleDrawHash(PetscInt64 seed, PicurvDrawSpace space, uint64_t stream, PetscInt64 pid, PetscInt64 salt)
Implementation of PicurvParticleDrawHash().
Definition expression.c:608
ExpressionNode * nodes
Definition expression.c:96
ExpressionOp op
Definition expression.c:70
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
#define PICURV_SOURCE_EXPRESSION_LENGTH
Definition expression.c:824
static PetscInt ParseSum(ExpressionParser *parser)
Parses a left-associative chain of + and -, folding each operator into a node.
Definition expression.c:434
PetscInt capacity
Definition expression.c:98
char error[256]
Definition expression.c:104
PetscInt length
Definition expression.c:87
static PetscInt ParseTerm(ExpressionParser *parser)
Parses a left-associative chain of *, /, %, folding each operator into a node.
Definition expression.c:414
static PetscInt ParseOr(ExpressionParser *parser)
and ('or' and)*
Definition expression.c:489
static PetscInt ParseAnd(ExpressionParser *parser)
not ('and' not)*
Definition expression.c:479
PetscReal upper
Definition expression.c:72
PetscErrorCode PicurvSourceFaceSlotsDestroy(PicurvSourceFaceSlots *slots)
Implementation of PicurvSourceFaceSlotsDestroy().
static PetscInt ParsePower(ExpressionParser *parser)
Parses an atom and, when a power operator follows, appends a power node.
Definition expression.c:385
PetscInt name_count
Definition expression.c:100
One postfix instruction.
Definition expression.c:79
One tree node; children index the node pool.
Definition expression.c:69
Parser state: the text, a cursor, the node pool, and the names in scope.
Definition expression.c:93
The expression language shared by every configured field, and the keyed hashes its random draws and o...
PicurvExpression * bulk
Target mean speed [m/s] in t, or NULL.
Definition expression.h:160
PetscBool bulk_time_dependent
Definition expression.h:161
PetscInt normal
Logical axis of the face normal.
Definition expression.h:222
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
PetscInt n[2]
Owned slots along the two tangential axes; 0 off the face.
Definition expression.h:226
@ PICURV_SOURCE_VAR_XI
Arc-length fraction along the xi grid line, 0 at the - face.
Definition expression.h:131
@ PICURV_SOURCE_VAR_T
Physical time [s].
Definition expression.h:130
@ PICURV_SOURCE_VAR_R
On a face, distance from the face's centre [m].
Definition expression.h:138
@ 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_COUNT
Definition expression.h:139
@ PICURV_SOURCE_VAR_CELL_COUNT
Variables a source reads on every target.
Definition expression.h:137
@ PICURV_SOURCE_VAR_Z
Physical z [m].
Definition expression.h:129
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
PicurvExpression * component[3]
Definition expression.h:158
PetscBool time_dependent
Some component reads t.
Definition expression.h:159
PetscInt64 salt
Event that produced the particle: 0 for the t=0 population.
Definition expression.h:70
PetscReal(* values)[PICURV_SOURCE_VAR_COUNT]
Variables per slot; t is the caller's.
Definition expression.h:227
PetscInt64 field
Field whose expression draws, the default stream's namespace.
Definition expression.h:71
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
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
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
@ 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
PetscInt64 seed
Run seed (-particle_random_seed).
Definition expression.h:68
PetscInt count
1 for the normal frame, otherwise 3; 0 for staged data alone.
Definition expression.h:157
What a random draw is keyed on, besides the particle and its stream.
Definition expression.h:67
What a source reads at each boundary-face slot this rank owns, gathered once.
Definition expression.h:221
A prescribed velocity, for any target: one compiled expression per value, the frame they are in,...
Definition expression.h:155
Public interface for grid, solver, and metric setup routines.
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
Logging utilities and macros for PETSc-based applications.
#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
#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
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:1033
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled function: an always-on aggregate timer.
Definition logging.h:1004
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1126
PetscInt KM
Definition variables.h:1137
PetscReal L_ref
Definition variables.h:870
Vec Ucont
Definition variables.h:1162
PetscScalar x
Definition variables.h:142
PetscScalar z
Definition variables.h:142
ScalingCtx scaling
Definition variables.h:973
PetscInt JM
Definition variables.h:1137
Vec lCenty
Definition variables.h:1218
Vec lCentx
Definition variables.h:1218
PetscInt GridOrientation
Definition variables.h:1141
DMDALocalInfo info
Definition variables.h:1135
PetscScalar y
Definition variables.h:142
PetscInt IM
Definition variables.h:1137
PetscReal P_ref
Definition variables.h:873
Vec lCentz
Definition variables.h:1218
PetscReal U_ref
Definition variables.h:871
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:300
A 3D point or vector with PetscScalar components.
Definition variables.h:141
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1123
A generic C-style linked list node for integers.
Definition variables.h:472