Symbolic variables and variable metadata

ModelingToolkit uses Symbolics.jl for the symbolic manipulation infrastructure. In fact, the @variables macro is defined in Symbolics.jl. In addition to @variables, ModelingToolkit defines @parameters, @independent_variables, @constants and @brownians. These macros function identically to @variables but allow ModelingToolkit to attach additional metadata.

Missing docstring.

Missing docstring for Symbolics.@variables. Check Documenter's build log for details.

Missing docstring.

Missing docstring for @independent_variables. Check Documenter's build log for details.

Missing docstring.

Missing docstring for @parameters. Check Documenter's build log for details.

Missing docstring.

Missing docstring for @constants. Check Documenter's build log for details.

Missing docstring.

Missing docstring for @brownians. Check Documenter's build log for details.

Symbolic variables can have metadata attached to them. The defaults and guesses assigned at variable construction time are examples of this metadata. ModelingToolkit also defines additional types of metadata.

Variable defaults

Variables can be assigned default values during construction. For example:

@variables x(t) = 1 y(t) = x

Here x has a default of 1, and y has a default of x. While both of these cases are stored under the same metadata key, ModelingToolkit treats them differently. Constant (non-symbolic) defaults (such as that of x) are translated to initial conditions (initial_conditions). Symbolic defaults (such as that of y) are translated to bindings (bindings). For more information on the difference between bindings and initial conditions, please refer to the documentation on initialization of systems, and specifically the section on bindings and initial conditions.

Missing docstring.

Missing docstring for ModelingToolkit.hasdefault. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.getdefault. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.setdefault. Check Documenter's build log for details.

Variable descriptions

Descriptive strings can be attached to variables using the [description = "descriptive string"] syntax:

using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
@variables u [description = "This is my input"]
getdescription(u)
"This is my input"

When variables with descriptions are present in systems, they will be printed when the system is shown in the terminal:

@variables u(t) [description = "A short description of u"]
@parameters p [description = "A description of p"]
@named sys = System([u ~ p], t)
Model sys:
Equations (1):
  1 standard: see equations(sys)
Unknowns (1): see unknowns(sys)
  u(t): A short description of u
Parameters (1): see parameters(sys)
  p: A description of p

Calling help on the variable u displays the description, alongside other metadata:

help?> u

  A variable of type Symbolics.Num (Num wraps anything in a type that is a subtype of Real)

  Metadata
  ≡≡≡≡≡≡≡≡≡≡

  ModelingToolkit.VariableDescription: This is my input

  Symbolics.VariableSource: (:variables, :u)
Missing docstring.

Missing docstring for hasdescription. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getdescription. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.VariableDescription. Check Documenter's build log for details.

Connect

Variables in connectors can have connect metadata which describes the type of connections.

Flow is used for variables that represent physical quantities that "flow" ex: current in a resistor. These variables sum up to zero in connections.

Stream can be specified for variables that flow bi-directionally.

using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D

@variables i(t) [connect = Flow]
@variables k(t) [connect = Stream]
hasconnect(i)
true
getconnect(k)
Stream
Missing docstring.

Missing docstring for hasconnect. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getconnect. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.VariableConnectType. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Flow. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Stream. Check Documenter's build log for details.

Input or output

Designate a variable as either an input or an output using the following

using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D

@variables u [input = true]
isinput(u)
true
@variables y [output = true]
isoutput(y)
true
Missing docstring.

Missing docstring for isinput. Check Documenter's build log for details.

Missing docstring.

Missing docstring for isoutput. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.setinput. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.setoutput. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.VariableInput. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.VariableOutput. Check Documenter's build log for details.

Bounds

Bounds are useful when parameters are to be optimized, or to express intervals of uncertainty.

julia> @variables u [bounds = (-1, 1)];
julia> hasbounds(u)true
julia> getbounds(u)(-1, 1)

Bounds can also be specified for array variables. A scalar array bound is applied to each element of the array. A bound may also be specified as an array, in which case the size of the array must match the size of the symbolic variable.

julia> @variables x[1:2, 1:2] [bounds = (-1, 1)];
julia> hasbounds(x)true
julia> getbounds(x)(-1, 1)
julia> getbounds(x[1, 1])ERROR: AssertionError: !(symbolic_has_known_size(arrx)) || SU.shape(arrx) == SU.shape(b)
julia> getbounds(x[1:2, 1])ERROR: AssertionError: !(symbolic_has_known_size(arrx)) || SU.shape(arrx) == SU.shape(b)
julia> @variables x[1:2] [bounds = (-Inf, [1.0, Inf])];
julia> hasbounds(x)true
julia> getbounds(x)(-Inf, [1.0, Inf])
julia> getbounds(x[2])ERROR: AssertionError: !(symbolic_has_known_size(arrx)) || SU.shape(arrx) == SU.shape(b)
julia> hasbounds(x[2])ERROR: AssertionError: !(symbolic_has_known_size(arrx)) || SU.shape(arrx) == SU.shape(b)
Missing docstring.

Missing docstring for hasbounds. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getbounds. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.VariableBounds. Check Documenter's build log for details.

Guess

Specify an initial guess for variables of a System. This is used when building the InitializationProblem.

julia> @variables u [guess = 1];
julia> hasguess(u)true
julia> getguess(u)1
Missing docstring.

Missing docstring for hasguess. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getguess. Check Documenter's build log for details.

When a system is constructed, the guesses of the involved variables are stored in a Dict in the system. After this point, the guess metadata of the variable is irrelevant.

Missing docstring.

Missing docstring for guesses. Check Documenter's build log for details.

Mark input as a disturbance

Indicate that an input is not available for control, i.e., it's a disturbance input.

@variables u [input = true, disturbance = true]
isdisturbance(u)
true
Missing docstring.

Missing docstring for isdisturbance. Check Documenter's build log for details.

Mark parameter as tunable

Indicate that a parameter can be automatically tuned by parameter optimization or automatic control tuning apps.

@parameters Kp [tunable = true]
istunable(Kp)
true
Missing docstring.

Missing docstring for istunable. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.isconstant. Check Documenter's build log for details.

Note

@constants is a convenient way to create @parameters with tunable = false metadata

Probability distributions

A probability distribution may be associated with a parameter to indicate either uncertainty about its value, or as a prior distribution for Bayesian optimization.

julia> using Distributions;WARNING: using Distributions.shape in module SymbolicUtilsDistributionsExt conflicts with an existing identifier.
julia> d = Normal(10, 1);
julia> @parameters m [dist = d];
julia> hasdist(m)true
julia> getdist(m)Distributions.Normal{Float64}(μ=10.0, σ=1.0)
Missing docstring.

Missing docstring for hasdist. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getdist. Check Documenter's build log for details.

Irreducible

A variable can be marked irreducible to prevent it from being moved to an observed state. This forces the variable to be computed during solving so that it can be accessed in callbacks

@variables important_value [irreducible = true]
isirreducible(important_value)
true
Missing docstring.

Missing docstring for isirreducible. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.VariableIrreducible. Check Documenter's build log for details.

State Priority

When a model is structurally simplified, the algorithm will try to ensure that the variables with higher state priority become states of the system. A variable's state priority is a number set using the state_priority metadata.

@variables important_dof [state_priority = 10] unimportant_dof [state_priority = -2]
state_priority(important_dof)
10.0
Missing docstring.

Missing docstring for state_priority. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.VariableStatePriority. Check Documenter's build log for details.

Units

Units for variables can be designated using symbolic metadata. For more information, please see the model validation and units section of the docs. Note that getunit is not equivalent to get_unit - the former is a metadata getter for individual variables (and is provided so the same interface function for unit exists like other metadata), while the latter is used to handle more general symbolic expressions.

julia> using DynamicQuantities;
julia> @variables speed [unit = u"m/s"];
julia> hasunit(speed)true
julia> getunit(speed)1.0 m s⁻¹
Missing docstring.

Missing docstring for hasunit. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getunit. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.VariableUnit. Check Documenter's build log for details.

Variable type

This metadata is used by the System constructor for automatically identifying the different types of variables in a system.

Missing docstring.

Missing docstring for ModelingToolkit.VariableType. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.MTKVariableTypeCtx. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.isparameter. Check Documenter's build log for details.

Miscellaneous metadata

User-defined metadata can be added using the misc metadata. This can be queried using the hasmisc and getmisc functions.

julia> @variables u [misc = :conserved_parameter] y [misc = [2, 4, 6]];
julia> hasmisc(u)true
julia> getmisc(y)3-element Vector{Int64}: 2 4 6
Missing docstring.

Missing docstring for hasmisc. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getmisc. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.VariableMisc. Check Documenter's build log for details.

Dumping metadata

ModelingToolkit allows dumping the metadata of a variable as a NamedTuple.

Missing docstring.

Missing docstring for ModelingToolkit.dump_variable_metadata. Check Documenter's build log for details.

Additional functions

For systems that contain parameters with metadata like described above, have some additional functions defined for convenience. In the example below, we define a system with tunable parameters and extract bounds vectors

@variables x(t)=0 u(t)=0 [input=true] y(t)=0 [output=true]
@parameters T [tunable = true, bounds = (0, Inf)]
@parameters k [tunable = true, bounds = (0, Inf)]
eqs = [D(x) ~ (-x + k * u) / T # A first-order system with time constant T and gain k
       y ~ x]
sys = System(eqs, t, name = :tunable_first_order)
Model tunable_first_order:
Equations (2):
  2 standard: see equations(tunable_first_order)
Unknowns (3): see unknowns(tunable_first_order)
  x(t)
  u(t)
  y(t)
Parameters (2): see parameters(tunable_first_order)
  k
  T
p = tunable_parameters(sys) # extract all parameters marked as tunable
2-element Vector{SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}}:
 k
 T
lb, ub = getbounds(p) # operating on a vector, we get lower and upper bound vectors
(lb = [0, 0], ub = [Inf, Inf])
b = getbounds(sys) # Operating on the system, we get a dict
Dict{SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}, Tuple{Int64, Float64}} with 2 entries:
  T => (0, Inf)
  k => (0, Inf)

See also:

Missing docstring.

Missing docstring for tunable_parameters. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.dump_unknowns. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ModelingToolkit.dump_parameters. Check Documenter's build log for details.

Symbolic operators

ModelingToolkit makes heavy use of "operators". These are custom functions that are applied to symbolic variables. The most common operator is the Differential operator, defined in Symbolics.jl.

Missing docstring.

Missing docstring for Symbolics.Differential. Check Documenter's build log for details.

ModelingToolkit also defines a plethora of custom operators.

Missing docstring.

Missing docstring for Pre. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Initial. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Shift. Check Documenter's build log for details.

Missing docstring.

Missing docstring for EvalAt. Check Documenter's build log for details.

While not an operator, ShiftIndex is commonly used to use Shift operators in a more convenient way when writing discrete systems.

Missing docstring.

Missing docstring for ShiftIndex. Check Documenter's build log for details.

Sampled time operators

The following operators are used in hybrid ODE systems, where part of the dynamics of the system happen at discrete intervals on a clock. While ModelingToolkit cannot yet simulate such systems, it has the capability to represent them.

Warn

These operators are considered experimental API.

Missing docstring.

Missing docstring for Sample. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Hold. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SampleTime. Check Documenter's build log for details.

Missing docstring.

Missing docstring for sampletime. Check Documenter's build log for details.