Skip to main content
Version: v2.0.1

Math Expressions

Mathematical expressions {math-expr} are permitted on different arguments of the configuration options. They consist of a string that when interpreted and evaluated returns a numeric value. Such expressions may contain typical math operations such as +, -, *, sin, tan, sqrt, etc.

Example

The example below initializes the variable u with u(0)=1+2β‹…10βˆ’10\texttt{u}_{(0)}=1+2\cdot 10^{-10} and sets its diffusion coefficient to Du=2β‹…10βˆ’10\mathsf{D}_{\texttt{u}} = \sqrt{2\cdot 10^{-10}}.

config.ini
[model.scalar_value.u]
initial.expression = 1.0 + 2e-10
cross_diffusion.u.expression = sqrt(2e-10)

Note how the contents of these arguments need to be understood by a suitable math interpreter.

Syntax

The exact syntax permitted within math expressions {math-expr} depends on the parser type used to interpret the expression. Every expression may be instructed to be interpreted with a different parser by setting [*.]parser_type={enum}. The possible parsers are:

'ExprTk'

The C++ Mathematical Expression Toolkit Library. Focuses in functionality and performance.

https://www.partow.net/programming/exprtk/index.html

'MuParser'

A fast math parser library. Available in most package managers.

https://beltoforion.de/en/muparser

'SymEngine'

A symbolic manipulation library. Best known for its python bindings: SymPy.

https://symengine.org

'SymEngineSBML'

A SBML flavored syntax of the 'SymEngine' parser.

https://symengine.org

Parser availability

Some parsers may not be available depending on how dune-copasi is compiled. To see the available parsers use the --parser-list command.

Parser composition

Every expression in {math-expr} is compiled independently. This means that different parser types may be used in the same model definition. This is helful when the syntax of one parser is more convenient to write one expression. For example, the ExprTk parser allows the usage of ternary conditional operator cond ? expr1 : expr2 whereas SymEngine does not:

config.ini
[parser_context.pulse]
type = function
expression = t, t0, dt: abs(t-t0) < dt ? 1 : 0
parser_type = ExprTk

[model.scalar_value.u]
reaction.expression = 1e10 + pulse(time, 10, 1)
reaction.parser_type = SymEngine

Keywords

Math expressions may be evaluated in many different contextual environments, such as, in the initialization of variables, a coefficient interpretation, or as an aid to define the cells that belong to a comartment. Depending on the context, several keywords are defined in order to help expressing the desired mathematical model. These keywords are declared and defined in the following way:

Contextual Transient Keyword​

Whenever the context of the evaluation is transient, the time keyword will evaluate to the corresponding simulation time tt. In other words,

time:=t.\texttt{time}:=t.

Contextual Domain Keywords​

Whenever an expression is evaluated at a position xβˆˆΞ©βŠ‚Rdx\in\Omega\subset\mathbb{R}^d then this position is also available within the {math-expr}. Such position is made available through the position_[x|y|z] keywords such that

[position_x, position_y, position_z]:=x^,[\texttt{position\_x},\,\texttt{position\_y},\,\texttt{position\_z}]:=\hat{x},

where x^∈R3\hat{x}\in \mathbb{R}^3 is the extension of x∈Rdx\in\mathbb{R}^d into R3\mathbb{R}^3.

mesh definition

We assume that the domain Ξ©\Omega is covered by a mesh Th={T1,…,TM}\mathcal{T}_h = \{T_1, \ldots , T_M\} consisting of elements which are closed sets satisfying

⋃T∈ThT=Ξ©β€Ύ,βˆ€T,Tβ€²βˆˆTh,Tβ‰ Tβ€²:T˚∩TΛšβ€²=βˆ…,\bigcup_{T\in \mathcal{T}_h} T = \overline{\Omega}, \quad \forall T, T' \in \mathcal{T}_h, T\neq T' : \mathring{T} \cap \mathring{T}' = \emptyset,

The nonempty intersections F=TFβˆ’βˆ©TF+F = T_F^-\cap T_F^+ of codimension 1 form the interior skeleton Fhi={F1,…,FN}\mathcal{F}_h^i=\{F_1,\ldots,F_N\}. Each intersection is equipped with a unit normal vector Ξ½F\nu_F pointing from TFβˆ’T_F^- to TF+T_F^+. The intersections of an element F=TFβˆ’βˆ©βˆ‚Ξ©F=T_F^-\cap\partial\Omega with the domain boundary form the set of boundary intersections Fhβˆ‚Ξ©={F1,…,FL}\mathcal{F}_h^{\partial\Omega}= \{F_1,\ldots,F_L\}. Each boundary intersection is equipped with a unit normal vector Ξ½F\nu_F which coincides with the unit outer normal to the domain.

Furthermore, the contextual domain evaluation is always associated to a grid element ee. Its properties are also expossed within the parser as:

entity_volume:=∣e∣in_volume:={trueifΒ e∈Thfalseotherwisein_boundary:={trueifΒ e∈Fhβˆ‚Ξ©falseotherwisein_skeleton:={trueifΒ e∈Fhifalseotherwise\begin{aligned} \texttt{entity\_volume}&:=|e|\\ \texttt{in\_volume}&:= \left\{ \begin{matrix} \texttt{true} \quad& \text{if }e\in\mathcal{T}_h\\ \texttt{false} \quad& \text{otherwise} \end{matrix} \right.\\ \texttt{in\_boundary}&:= \left\{ \begin{matrix} \texttt{true} \quad& \text{if }e\in\mathcal{F}_h^{\partial\Omega}\\ \texttt{false} \quad& \text{otherwise} \end{matrix} \right.\\ \texttt{in\_skeleton}&:= \left\{ \begin{matrix} \texttt{true} \quad& \text{if }e\in\mathcal{F}_h^i\\ \texttt{false} \quad& \text{otherwise} \end{matrix} \right. \end{aligned}

In the cases where e∈Fhβˆ‚Ξ©βˆͺFhie\in\mathcal{F}_h^{\partial\Omega}\cup\mathcal{F}_h^i we also have that its unit outer normal vector is exposed with the keywords normal_[x|y|z] such that

[normal_x,normal_y,normal_z]:=Ξ½^F,[\texttt{normal\_x}, \texttt{normal\_y}, \texttt{normal\_z}] := \hat{\nu}_F,

where ν^F∈R3\hat{\nu}_F\in \mathbb{R}^3 is the extension of νF∈Rd\nu_F\in\mathbb{R}^d into R3\mathbb{R}^3.

Contextual Scalar Field Keywords​

There are cases where the evaluation of {math-expr} is made when the results of the scalar values {var} (or a trial function of them) are known. In such cases, the value of {var} evaluated at position xx is made available thoruhg the parsers. Additionally, its gradient βˆ‚βˆ‚x^\frac{\partial}{\partial \hat{x}}{var} is also represented by the grad_{var}_[x|y|z] tokens.

example

The configuration file to represent the system of ordinary differential equations

βˆ‚tuβˆ’Ru(u,z),βˆ‚tzβˆ’Rz(u,z)\begin{aligned} \partial_t \texttt{u}& - \mathcal{R}_\texttt{u}(\texttt{u}, \texttt{z}),\\ \partial_t \texttt{z}& - \mathcal{R}_\texttt{z}(\texttt{u}, \texttt{z}) \end{aligned}

with the reaction networks on the form of

Ru(u,z):=zβ‹…u2β‹…(1βˆ’u)βˆ’u,Rz(u,z):={1βˆ’z1.25ifΒ u≀0.1βˆ’zotherwise\begin{aligned} \mathcal{R}_\texttt{u}(\texttt{u}, \texttt{z})&:= \texttt{z}\cdot\texttt{u}^2\cdot(1-\texttt{u}) - \texttt{u},\\ \mathcal{R}_\texttt{z}(\texttt{u}, \texttt{z})&:= \left\{ \begin{matrix} \frac{1 - \texttt{z}}{1.25} \quad& \text{if }\texttt{u} \leq 0.1\\ -\texttt{z} \quad& \text{otherwise} \end{matrix} \right.\\ \end{aligned}

will contain something similar to this:

config.ini
[model.scalar_field.u]
storage.expression = 1
reaction.expression = z*u^2*(1-u) - u
reaction.jacobian.u.expression = u*z*(2-3*u)-1
reaction.jacobian.z.expression = (1-u)*u^2

[model.scalar_field.z]
storage.expression = 1
reaction.expression = (u <= 0.1) ? (1 - z)/1.25 : -z
reaction.jacobian.z.expression = (u <= 0.1) ? -1/1.25 : -1

Notice how the tokens u and z are allowed in each {math-expr} across different scalar field sub-sections of *.u and *.z. This allows to express non-linearities in a natural form.

Custom Keywords​

Config Options:
  • parser_context.{tkn}.type={enum}
  • parser_context.{tkn}.{...}={...}

Independently of the evaluation context, you are allowed to inject keywords into the parser in order to express mathematical expressions easier. The defined keywords will be defined and allowed to be used within math-expr definitions.

Constants​

Config Options:
  • parser_context.{tkn}.type='constant'
  • parser_context.{tkn}.value={float}

Defines a keyword {tkn} that when evaluated gets replaced by the constant contained in {float}.

Example
config.ini
[parser_context.water_diffusion]
type = constant
value = 2.299E10βˆ’9

is equivalent to

water_diffusion:=2.299β‹…10βˆ’9\texttt{water\_diffusion}:=2.299\cdot 10^{-9}

Function Math Expressions​

Config Options:
  • parser_context.{tkn}.type='function'
  • parser_context.{tkn}.expression={func-expr}
  • parser_context.{tkn}.parser_type={enum}

Declares a keyword {tkn} than may be used as a function within the parsers. The definition of the function starts with up to 4 comma separated string arguments followed by a colon and a math expression.

  • {args} := {arg0}[, {arg1}[, {arg2}[, {arg3}]]]
  • {func-expr} := {args}: {math-expr}

The math expression {math-expr} is allowed to use the arguments {args} as numerical values and any other custom defined keywords exceptuating other function keywords.

example

This example defines a keyword pulse that may be used in other math expression config option argument to evaluate {math-expr} when provided with 3 arguments.

config.ini
[parser_context.pulse]
type = function
expression = t, t0, dt: abs(t-t0) < dt ? 1 : 0

is equivalent to

pulse(t,t0,Ξ΄t):={1,if ∣tβˆ’t0∣<Ξ΄t0,otherwise.\texttt{pulse(}t, t_0, \delta_t\texttt{)}:= \left\{ \begin{matrix} 1, \quad& \text{if }|t-t_0| < \delta_t\\ 0, \quad& \text{otherwise} \end{matrix} \right. .
Linear interpolation
Config Options:
  • parser_context.{tkn}.interpolate={bool}
  • parser_context.{tkn}.domain.{arg}={float-pair}
  • parser_context.{tkn}.intervals={integer}
  • parser_context.{tkn}.out_of_bounds={enum}

In cases where {math-expr} is very expensive to compute and the function has one argument, these options allow to replace the function definition with a linear interpolation of {math-expr}.

example

This example defines a keyword my_exp referring to an interpolation of exp(x) evaluated in 100 sub-intervals between the domain values 0 and 10.

config.ini
[parser_context.my_exp]
type = function
expression = x: exp(x)
interpolate = true
domain.x = 0 10
intervals = 100
out_of_bounds = error

The interpolation may be disabled by setting interpolate=false, in which case exp(x) will be compiled and evaluated as any other mathematical expression.

Other functions

A {math-expr} with 'function' type is not allowed to contain other custom keywords defined as 'function' type. This means that recursive and composition of functions is not allowed.

example
config.ini
[parser_context.factorial]
type = function
expression = n: (n > 0) ? n * factorial(n-1) : n
# error: 'factorial' keyword is not known

[parser_context.f]
type = function
expression = x, y: sin(x) * cos(y)

[parser_context.g]
type = function
expression = x: f(x, x-1)
# error: 'f' keyword is not known
muparser max limit

There is a maximum limit to the number of functions may can be defined when the {math-expr} is compiled using [*.]parser_type='MuParser'. This limit is severely reduced when the assembly is made in concurrent mode. We recommend using other parsers when defining functions.

Random Field​

Config Options:
  • parser_context.{tkn}.type='random_field'
  • parser_context.{tkn}.{parafields-key}={parafields-value}

These options generate a Gaussian random field based on circulant embedding using parafields and declares it as a function with the keyword {tkn}. The number of arguments of such function depends on the dimension of the grid used to generate the random field. All parameters {parafields-key}={parafields-value} under the parser_context.{tkn} subsection will be forwarded to the parafields-core engine.

example

The example below shows the definition of the keyword my_rng in the parsers as a 2D function with the contents of a random field. Later, the keyword my_rng is used within a math expression to initialize the variable u using the contextual domain keywords position_x and position_y.

config.ini
[parser_context.my_rng]
type = random_field
grid.extensions = 1.0 1.0
grid.cells = 1000 1000
randomField.transform = logNormal
stochastic.corrLength = 0.10
stochastic.covariance = exponential
stochastic.variance = 0.2

[model.scalar_field.u]
initial.expression = my_rng(2*position_x, 2*position_y)
Out of domain behavior

If the resulting function {tkn} is evaluated outside the domain values, the arguments will be clamped to the nearest valid point in the domain.

Random Fields availability

This option is only available when dune-copasi is compiled with parafields.

1D Linear Interpolation​

Config Options:
  • parser_context.{tkn}.type='interpolation'
  • parser_context.{tkn}.domain={float-list}
  • parser_context.{tkn}.range={float-list}

A 1D function with a linear interpolation may be generated from of domain and range pairs.

example

A configuration file with the following input

config.ini
[parser_context.f]
type = interopation
domain = 0 1 2 3 4 5 6
range = 0 0.8415 0.9093 0.1411 βˆ’0.7568 βˆ’0.9589 βˆ’0.2794

represents a function f(x) with this graphical representation: Function f interpolation

Out of domain behavior

If the resulting function {tkn} is evaluated outside the domain values, the arguments will be clamped to the nearest valid point in the domain.

Images (TIFF Format)​

Config Options:
  • parser_context.{tkn}.type='tiff'
  • parser_context.{tkn}.path={path}

Reads an image file with tiff format and makes it available as a 2D function expression with the {tkn} token. Images with grayscale values of 8, 16, 32, and 64 bits are supported.

Expample

The example below shows the definition of the keyword my_image in the parsers as a 2D function with the contents of the file in path/to/file.tif. Later, the keyword my_image is used within a math expression to initialize the variable u using the contextual domain keywords position_x and position_y.

config.ini
[parser_context.my_image]
type = tiff
path = path/to/file.tif

[model.scalar_field.u]
initial.expression = my_image(2*position_x, 2*position_y)

Grid Cell Data (TXT Format)​

Config Options:
  • grid.cell_data.{dtkn}.path={prefix}

The keywords {dtkn} defined by grid.cell_data.{dtkn}.path={prefix} will be additionally available within the math expressions that are under a domain context. When evaluated it will contain the data defined within the {prefix} file.

Reserved Keywords​

The following keywords are reserved to have special meaning within the program:

KeywordDescription
no_valueIndicates that the result of an expression does not represent a numeric value
Parser reserved keywords

Additionally to those defined here, every underlying parser has its own set of keywords that are reserved. Visit the documentation of your preferred parser to learn more about its syntax and its reserved keywords.