-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More expressions "improvements" #1764
Conversation
Enables more clear swiching than the H(x) Heaviside function Like the C++ functions in BOUT++, one of two expressions is evaluated, depending on the sign of the first expression.
Allows the definition of new scopes with local variables, by setting values in the `Context` object. One use is to simplify expressions using local variables, for efficiency and readability e.g. ensuring that a value is positive: ``` positive_value = H(expression) * expression ``` where the expression would be evaluated twice. This can be replaced by: ``` positive_value = [value = expression]( H({value}) * {value} ) ``` This mechanism can also be used to implement simple functions ``` mycosh = 0.5 * (exp({arg}) + exp(-{arg})) ``` which can be called by defining a new Context: ``` result = [arg = x*2](mycosh) ``` (sort-of a dynamic scope system) so we could write: ``` floor = H({arg}) * {arg} positive_value = [arg = expression](floor) ```
Some code not recompiled previously, now updated. Another test defining two variables in a `Context` object.
Long expressions can become hard to read in the options file. This adopts the Python approach, where if there are unbalanced brackets ('()' or '[]') then the expression continues on the next line. In the BOUT.inp file we can therefore write ``` value = (something + more - things) ``` or define complex expression using a Context scope: ``` result = [a = expression, b = something]( {a} + {b}) ```
// Ensure that brackets '()' and '[]'are balanced | ||
|
||
// Count net number of opening and closing brackets | ||
auto count_brackets = [](const string& input) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another way of writing this, using std algorithms:
auto count_brackets = [](const std::string& input) {
return std::count_if(begin(input), end(input), [](const char& ch) { return ch == '(' or ch == '['; })
- std::count_if(begin(input), end(input), [](const char& ch) { return ch == ')' or ch == ']'; });
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks yes that would work. It traverses the string twice, but it is nicer in many ways. I'll change to this.
Enables a simple kind of loop, but one which is guaranteed to finish. Iterates a given symbol from 0 to count-1, evaluating and summing a given expression.
Using parseExpression captures the rest of the string, rather than just the next expression. Instead call parseParenExpr to stop when parentheses close. Added tests to reproduce this.
By default recursive expressions are not allowed in the input options. What is committed here adds an input option to allow recursion: ``` [input] max_recursion_depth = 10 # 0 = none, -1 = unlimited ``` By putting a limit on the depth we avoid the halting problem at least, and expressions should (eventually) terminate, preferably before they overflow the stack. As is traditional, I present to you the Fibonacci sequence: ``` fib = where({n} - 2.5, [n={n}-1](fib) + [n={n}-2](fib), 1) ``` so if `n` = 1 or 2 then `fib` = 1, but if `n` = 3 or above then the recursion fun begins. And another ill-considered language is born.
`FieldFactory` reads `input:max_recursion_depth` as an int. `Options` then creates a `FieldFactory` to parse the string to an int. This change uses `std::stoi` to do the string conversion. This means no fancy expressions in this parameter, but that should not be needed.
Documenting new features for expressions, and some examples of how to use them. Some of the existing documentation was out of date from earlier changes.
Slight efficiency improvement when repeatedly iterating over
Built on top of PR #1750 , this adds some features for making complex expressions in the input file easier to read / organise.
Local Scope
Adds syntax to define local scope. Underneath this inserts a
FieldGenerator
which modifies theContext
object. One use is to simplify expressions using local variables, for efficiency and readability e.g. ensuring that a value is positive:where the expression would be evaluated twice. This can be replaced by:
This mechanism can also be used to implement simple functions
which can be called by defining a new Context:
(a sort of dynamic scope system)
so we could write:
where
functionAdds a
where(test, gt0, lt0)
function toFieldFactory
. This enables more clear swiching than the H(x) Heaviside function. Like the C++ functions in BOUT++, one of two expressions is evaluated,depending on the sign of the first expression.
Multi-line expressions
Enable multi-line expressions in input options, since long expressions can become hard to read in the options file if all on one line. This adopts the Python approach, where if there are unbalanced brackets ('()' or '[]') then the expression continues on the next line.
In the BOUT.inp file we can therefore write
or define complex expression using a Context scope:
sum
function for loopingAdd a
sum
function enables a simple kind of loop, but one which is guaranteed to finish. Iterates a given symbol from 0 to count-1, evaluating and summing a given expression. Example:Recursive expressions
By default recursive expressions are not allowed in the input options. These can be enabled by setting
input:max_recursion_depth != 0
e.g.:By putting a limit on the depth we avoid the halting problem at least, and expressions should (eventually) terminate, preferably before they overflow the stack.
As is traditional, I present to you the Fibonacci sequence:
so if
n
= 1 or 2 thenfib
= 1, but ifn
= 3 or above then the recursion fun begins.And another ill-considered language is born. Sorry, I think it was inevitable.