expression_v2¶
Expression parser version 2 and namespace.
The syntax of an expression is as follows:
Integers or decimal numbers are denoted in the usual way. Examples:
1
,1.2
,.2
.Variables are denoted with a string of characters. The first character must not be a digit. Unlike Python variables, underscores are not allowed, as they have a special meaning. If the variable is an array with one or more axes, all those axes should be labeled with a latin character, the index, and appended to the variable with an underscore. For example an array
a
with two axes can be denoted witha_ij
. Optionally, a single numeral may be used to select an item at the concerning axis. Example: ina_i0
the first axis ofa
is labeledi
and the first element of the second axis is selected. If the same index occurs twice, the trace is taken along the concerning axes. Example: the trace of the first and third axes ofb
is denoted byb_iji
. It is invalid to specify an index more than twice.A term, the product of two or more arrays or scalars, is denoted by space-separated variables, constants or compound expressions. Example:
a b c
denotes the product of the scalarsa
,b
andc
. A term may start with a number, but a number is not allowed in other parts of the term. Example:2 a
denotes two timesa
;2 2 a
and2 a 2`
are invalid. When two arrays in a term have the same index, this index is summed. Example:a_i b_i
denotes the inner product ofa
andb
andA_ij b_j`
a matrix vector product. It is not allowed to use an index more than twice in a term.The operator
/
denotes a fraction. Example: ina b / c d
a b
is the numerator andc d
the denominator. Both the numerator and the denominator may start with a number. Example:2 a / 3 b
. The denominator must be a scalar. Example:2 / a_i b_i
is valid, but2 a_i / b_i
is not.Warning
This syntax is different from the Python syntax. In Python
a*b / c*d
is mathematically equivalent toa*b*d/c
.The operators
+
and-
denote add and subtract. Both operators should be surrounded by whitespace, e.g.a + b
. Both operands should have the same shape. Example:a_ij + b_i c_j
is a valid, provided that the lengths of the axes with the same indices match, buta_ij + b_i
is invalid. At the beginning of an expression or a compound-
may be used to negate the following term. Example: in-a b + c
the terma b
is negated before addingc
. It is not allowed to negate other terms:a + -b
is invalid, so isa -b
.An expression surrounded by parentheses is a compound expression and can be used as single entity in a term. Example:
(a_i + b_i) c_i
denotes the inner product ofa_i + b_i
withc_i
.Exponentiation is denoted by a
^
, where the left and right operands should be a number, variable or compound expression and the right operand should be a scalar. Example:a^2
denotes the square ofa
,a^-2
denotesa
to the power-2
anda^(1 / 2)
the square root ofa
. Note that the power has precedence over a unary minus:-2^2
is interpreted as-(2^2)
.An expression surrounded by square brackets or curly braces denotes the jump or mean, respectively, of the enclosed expression. Example:
[a_i]
denotes the jump ofa_i
and{a_i + b_i}
denotes the mean ofa_i + b_i
.A function call is denoted by a name — following the same rules as for a variable name — optionally followed by
_
and indices for generated axes, directly followed by the left parenthesis(
, without a space. A function takes a single argument with any shape and returns an array with the same shape plus an axis per index listed after the underscore. The function is applied pointwise to the argument. If an index for a generated axis is also present in the argument, the trace is taken along the concerning axes after the function call.
- class nutils.expression_v2.Namespace¶
Bases:
object
Namespace for
Array
objects supporting assignments with tensor expressions.The
Namespace
object is used to storeArray
objects.>>> from nutils import function >>> ns = Namespace() >>> ns.A = function.zeros([2, 3]) >>> ns.x = function.zeros([3]) >>> ns.c = 2
In addition to the assignment of
Array
objects, it is also possible to specify an array using a tensor expression string — seenutils.expression_v2
for the syntax. All attributes defined in this namespace are available as variables in the expression. If the array defined by the expression has one or more dimensions the indices of the axes should be appended to the attribute name. Example:>>> ns.cAx_i = 'c A_ij x_j'
It is also possible to simply evaluate an expression without storing its value in the namespace using
expression @ ns
:>>> '2 c' @ ns Array<> >>> 'c A_ij x_j' @ ns Array<2> >>> 'A_ij' @ ns # indices are ordered alphabetically Array<2,3>
Note that evaluating an expression with an incompatible length raises an exception:
>>> 'A_ij + A_ji' @ ns Traceback (most recent call last): ... nutils.expression_v2.ExpressionSyntaxError: Index i has length 2 in the first term [^] but length 3 in the second term [~]. A_ij + A_ji ^^^^ ~~~~
When evaluating an expression through this namespace the following functions are available:
opposite
,sin
,cos
,tan
,sinh
,cosh
,tanh
,arcsin
,arccos
,arctanh
,exp
,abs
,ln
,log
,log2
,log10
,sqrt
,sign
,conj
,real
andimag
.Additional pointwise functions can be assigned to the namespace similar to variables:
>>> ns.sqr = lambda u: u**2 >>> 'sqr(x_i)' @ ns # same as 'x_i^2' Array<3>
- define_for(self, _Namespace__name, *, gradient=None, curl=None, normal=None, jacobians=())¶
Define gradient, normal or jacobian for the given geometry.
- Parameters:
name (
str
) – Define the gradient, normal or jacobian for the geometry with the given name in this namespace.gradient (
str
, optional) – Define the gradient function with the given name. The function generates axes with the same shape as the given geometry.curl (
str
, optional) – Define the curl function with the given name. The function generates two axes of length 3 where the last axis should be traced with an axis of the argument, e.g. curl_ij(u_j).normal (
str
, optional) – Define the normal with the given name. The normal has the same shape as the geometry.jacobians (sequence of
str
, optional) – Define the jacobians for decreasing dimensions, starting at the dimensions of the geometry. The jacobians are always scalars.
Example
>>> from nutils import function, mesh >>> ns = Namespace() >>> topo, ns.x = mesh.rectilinear([2, 2]) >>> ns.define_for('x', gradient='∇', normal='n', jacobians=('dV', 'dS')) >>> ns.basis = topo.basis('spline', degree=1) >>> ns.u = function.dotarg('u', ns.basis) >>> ns.v = function.dotarg('v', ns.basis) >>> res = topo.integral('-∇_i(v) ∇_i(u) dV' @ ns, degree=2) >>> res += topo.boundary.integral('∇_i(v) u n_i dS' @ ns, degree=2)
- copy_(self, **replacements)¶
Return a copy of this namespace.
- Parameters:
**replacements (
nutils.function.Array
) – Argument replacements to apply to the copy of this namespace.- Returns:
A copy of this namespace.
- Return type:
- __weakref__¶
list of weak references to the object (if defined)