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 for Symbolics.@variables. Check Documenter's build log for details.
ModelingToolkitBase.@independent_variables — Macro
@independent_variables t₁ t₂ ...Define one or more independent variables. For example:
@independent_variables t
@variables x(t)ModelingToolkitBase.@parameters — Macro
Define one or more known parameters. A parameter is a non-time-dependent quantity in the model.
See also @independent_variables, @variables and @constants.
ModelingToolkitBase.@constants — Macro
Define one or more constants.
See also @independent_variables, @parameters and @variables.
ModelingToolkitBase.@brownians — Macro
Define one or more Brownian variables.
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) = xHere 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.
ModelingToolkitBase.hasdefault — Function
hasdefault(v) -> Any
Check if the symbolic variable v has a default value.
ModelingToolkitBase.getdefault — Function
getdefault(v) -> Any
Return the default value of symbolic variable v.
ModelingToolkitBase.setdefault — Function
setdefault(v, val) -> Any
Set the default value of symbolic variable v to val.
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 pCalling 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)ModelingToolkitBase.hasdescription — Function
hasdescription(x) -> Any
Check if variable x has a non-empty attached description.
ModelingToolkitBase.getdescription — Function
getdescription(x)Return any description attached to variables x. If no description is attached, an empty string is returned.
ModelingToolkitBase.VariableDescription — Type
struct VariableDescriptionSymbolic metadata key for storing the description of a symbolic variable.
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)truegetconnect(k)StreamModelingToolkitBase.hasconnect — Function
hasconnect(x)Determine whether variable x has a connect type. See also getconnect.
ModelingToolkitBase.getconnect — Function
getconnect(x)Get the connect type of x. See also hasconnect.
ModelingToolkitBase.VariableConnectType — Type
struct VariableConnectTypeSymbolic metadata key for storing the type of connector that a variable is.
ModelingToolkitBase.Flow — Type
struct Flow <: ModelingToolkitBase.AbstractConnectTypeFlag which is meant to be passed to the connect metadata of a variable to affect how it behaves when the connector it is in is part of a connect equation. Flow denotes that the sum of marked variable in all connectors in the connection set must sum to zero. For example, electric current sums to zero at a junction (assuming appropriate signs are used for current flowing in and out of the function).
For more information, refer to the Connection semantics section of the docs.
See also: connect, @connector, Equality, Stream.
ModelingToolkitBase.Stream — Type
struct Stream <: ModelingToolkitBase.AbstractConnectTypeFlag which is meant to be passed to the connect metadata of a variable to affect how it behaves when the connector it is in is part of a connect equation. Stream denotes that the variable is part of a special stream connector.
For more information, refer to the Connection semantics section of the docs.
See also: connect, @connector, Equality, Flow.
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)trueModelingToolkitBase.isinput — Function
isinput(x) -> Bool
Check if variable x is marked as an input.
ModelingToolkitBase.isoutput — Function
isoutput(x) -> Bool
Check if variable x is marked as an output.
ModelingToolkitBase.setinput — Function
setinput(x, v::Bool) -> Any
Set the input metadata of variable x to v.
ModelingToolkitBase.setoutput — Function
setoutput(x, v::Bool) -> Any
Set the output metadata of variable x to v.
ModelingToolkitBase.VariableInput — Type
struct VariableInputSymbolic metadata key for storing whether a symbolic variable is an input or not.
ModelingToolkitBase.VariableOutput — Type
struct VariableOutputSymbolic metadata key for storing whether a symbolic variable is an output or not.
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)truejulia> 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)truejulia> 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)truejulia> 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)
ModelingToolkitBase.hasbounds — Function
hasbounds(x)Determine whether symbolic variable x has bounds associated with it. See also getbounds.
ModelingToolkitBase.getbounds — Function
getbounds(x)Get the bounds associated with symbolic variable x. Create parameters with bounds like this
@parameters p [bounds=(-1, 1)]getbounds(sys::ModelingToolkitBase.AbstractSystem, p = parameters(sys))Returns a dict with pairs p => (lb, ub) mapping parameters of sys to lower and upper bounds. Create parameters with bounds like this
@parameters p [bounds=(-1, 1)]To obtain unknown variable bounds, call getbounds(sys, unknowns(sys))
lb, ub = getbounds(p::AbstractVector)Return vectors of lower and upper bounds of parameter vector p. Create parameters with bounds like this
@parameters p [bounds=(-1, 1)]See also tunable_parameters, hasbounds
ModelingToolkitBase.VariableBounds — Type
struct VariableBoundsSymbolic metadata key for specifying the bounds of a symbolic variable.
Nominal Value
A nominal value represents the characteristic magnitude of a variable. This is useful for scaling constraints in optimal control problems, preventing ill-conditioning when variables have vastly different magnitudes. The default nominal value is 1.0.
julia> @variables x [nominal = 1000.0];julia> hasnominal(x)truejulia> getnominal(x)1000.0
Nominal values can also be specified for array variables:
julia> @variables x[1:3] [nominal = [100.0, 200.0, 300.0]];julia> getnominal(x)3-element Vector{Float64}: 100.0 200.0 300.0julia> getnominal(x[1])100.0
ModelingToolkitBase.hasnominal — Function
hasnominal(x)Determine whether symbolic variable x has a nominal value associated with it. See also getnominal.
ModelingToolkitBase.getnominal — Function
getnominal(x)Get the nominal value associated with symbolic variable x. Returns 1.0 if no nominal value is set. Create variables with a nominal value like this
@variables x [nominal = 4785.0]getnominal(sys::ModelingToolkitBase.AbstractSystem, vars = parameters(sys))Returns a dict with pairs var => nominal mapping variables of sys to their nominal values. Create variables with a nominal value like this
@variables x [nominal = 40.0]To obtain unknown variable nominal values, call getnominal(sys, unknowns(sys))
Missing docstring for ModelingToolkit.VariableNominal. 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)truejulia> getguess(u)1
ModelingToolkitBase.hasguess — Function
hasguess(x)Determine whether symbolic variable x has a guess associated with it. See also getguess.
ModelingToolkitBase.getguess — Function
getguess(x)Get the guess for the initial value associated with symbolic variable x. Create variables with a guess like this
@variables x [guess=1]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.
ModelingToolkitBase.guesses — Function
guesses(sys::ModelingToolkitBase.AbstractSystem) -> Any
Get the guesses for variables in the initialization system of the system sys and its subsystems.
See also initialization_equations and ModelingToolkitBase.get_guesses.
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)trueModelingToolkitBase.isdisturbance — Function
isdisturbance(x)Determine whether symbolic variable x is marked as a disturbance input.
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)trueModelingToolkitBase.istunable — Function
istunable(x, default = true)Determine whether symbolic variable x is marked as a tunable for an automatic tuning algorithm.
default indicates whether variables without tunable metadata are to be considered tunable or not.
Create a tunable parameter by
@parameters u [tunable=true]See also tunable_parameters, getbounds
ModelingToolkitBase.isconstant — Function
Test whether x is a constant-type Sym.
@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;julia> d = Normal(10, 1);julia> @parameters m [dist = d];julia> hasdist(m)truejulia> getdist(m)Distributions.Normal{Float64}(μ=10.0, σ=1.0)
ModelingToolkitBase.hasdist — Function
hasdist(x)Determine whether symbolic variable x has a probability distribution associated with it.
ModelingToolkitBase.getdist — Function
getdist(x)Get the probability distribution associated with symbolic variable x. If no distribution is associated with x, nothing is returned. Create parameters with associated distributions like this
using Distributions
d = Normal(0, 1)
@parameters u [dist = d]
hasdist(u) # true
getdist(u) # retrieve distributionIrreducible
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)trueModelingToolkitBase.isirreducible — Function
isirreducible(x) -> Bool
Check if x is marked as irreducible. This prevents it from being eliminated as an observed variable in mtkcompile.
ModelingToolkitBase.VariableIrreducible — Type
struct VariableIrreducibleSymbolic metadata key for storing whether a symbolic variable is irreducible or not.
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.0ModelingToolkitBase.state_priority — Function
state_priority(x) -> Float64
Return the state_priority metadata of variable x. This influences its priority to be chosen as a state in mtkcompile.
ModelingToolkitBase.VariableStatePriority — Type
struct VariableStatePrioritySymbolic metadata key for storing the priority a variable has for being selected during state selection.
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)truejulia> getunit(speed)1.0 m s⁻¹
ModelingToolkitBase.hasunit — Function
hasunit(x)Check if the variable x has a unit.
ModelingToolkitBase.getunit — Function
getunit(x)Fetch the unit associated with variable x. This function is a metadata getter for an individual variable, while get_unit is used for unit inference on more complicated sdymbolic expressions.
ModelingToolkitBase.VariableUnit — Type
struct VariableUnitSymbolic metadata key for storing the unit associated with a symbolic variable.
Variable type
This metadata is used by the System constructor for automatically identifying the different types of variables in a system.
ModelingToolkitBase.VariableType — Type
@enum VariableTypeThe type of the declared variable, used for automatic identification of variables/parameters/brownians/etc. by the System constructor.
ModelingToolkitBase.MTKVariableTypeCtx — Type
struct MTKVariableTypeCtxThe symbolic metadata key for storing the VariableType.
ModelingToolkitBase.isparameter — Function
Check if the variable contains the metadata identifying it as a parameter.
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)truejulia> getmisc(y)3-element Vector{Int64}: 2 4 6
ModelingToolkitBase.hasmisc — Function
hasmisc(x)Determine whether a symbolic variable x has misc metadata associated with it.
See also getmisc(x).
ModelingToolkitBase.getmisc — Function
getmisc(x)Fetch any miscellaneous data associated with symbolic variable x. See also hasmisc(x).
ModelingToolkitBase.VariableMisc — Type
struct VariableMiscSymbolic metadata key for storing miscellaneous information about a symbolic variable.
Dumping metadata
ModelingToolkit allows dumping the metadata of a variable as a NamedTuple.
ModelingToolkitBase.dump_variable_metadata — Function
dump_variable_metadata(var)Return all the metadata associated with symbolic variable var as a NamedTuple.
using ModelingToolkitBase
@parameters p::Int [description = "My description", bounds = (0.5, 1.5)]
ModelingToolkitBase.dump_variable_metadata(p)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
Tp = tunable_parameters(sys) # extract all parameters marked as tunable2-element Vector{SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}}:
k
Tlb, 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 dictDict{SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}, Tuple{Int64, Float64}} with 2 entries:
k => (0, Inf)
T => (0, Inf)See also:
ModelingToolkitBase.tunable_parameters — Function
tunable_parameters(sys, p = parameters(sys; initial_parameters = true); default=true)Get all parameters of sys that are marked as tunable.
Keyword argument default indicates whether variables without tunable metadata are to be considered tunable or not.
Create a tunable parameter by
@parameters u [tunable=true]For systems created with split = true (the default) and default = true passed to this function, the order of parameters returned is the order in which they are stored in the tunables portion of MTKParameters. Note that array variables will not be scalarized. To obtain the flattened representation of the tunables portion, call Symbolics.scalarize(tunable_parameters(sys)) and concatenate the resulting arrays.
See also getbounds, istunable, MTKParameters, complete
ModelingToolkitBase.dump_unknowns — Function
dump_unknowns(sys::AbstractSystem)Return an array of NamedTuples containing the metadata associated with each unknown in sys. Also includes the default value of the unknown, if provided.
using ModelingToolkitBase
using DynamicQuantities
using ModelingToolkitBase: t, D
@parameters p = 1.0, [description = "My parameter", tunable = false] q = 2.0, [description = "Other parameter"]
@variables x(t) = 3.0 [unit = u"m"]
@named sys = System(Equation[], t, [x], [p, q])
ModelingToolkitBase.dump_unknowns(sys)See also: ModelingToolkitBase.dump_variable_metadata, ModelingToolkitBase.dump_parameters
ModelingToolkitBase.dump_parameters — Function
dump_parameters(sys::AbstractSystem)Return an array of NamedTuples containing the metadata associated with each parameter in sys. Also includes the default value of the parameter, if provided.
using ModelingToolkitBase
using DynamicQuantities
using ModelingToolkitBase: t, D
@parameters p = 1.0, [description = "My parameter", tunable = false] q = 2.0, [description = "Other parameter"]
@variables x(t) = 3.0 [unit = u"m"]
@named sys = System(Equation[], t, [x], [p, q])
ModelingToolkitBase.dump_parameters(sys)See also: ModelingToolkitBase.dump_variable_metadata, ModelingToolkitBase.dump_unknowns
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 for Symbolics.Differential. Check Documenter's build log for details.
ModelingToolkit also defines a plethora of custom operators.
ModelingToolkitBase.Pre — Type
Pre(x)The Pre operator. Used by the callback system to indicate the value of a parameter or variable before the callback is triggered.
ModelingToolkitBase.Initial — Type
Initial(x)The Initial operator. Used by initialization to store constant constraints on variables of a system. See the documentation section on initialization for more information.
ModelingToolkitBase.Shift — Type
struct Shift <: SymbolicUtils.OperatorRepresents a shift operator.
Fields
t: Fixed Shiftsteps
Examples
julia> using Symbolics
julia> Δ = Shift(t)
(::Shift) (generic function with 2 methods)ModelingToolkitBase.EvalAt — Type
EvalAt(t)An operator that evaluates time-dependent variables at a specific absolute time point t.
Fields
t::Union{SymbolicT, Number}: The absolute time at which to evaluate the variable.
Description
EvalAt is used to evaluate time-dependent variables at a specific time point. This is particularly useful in optimization problems where you need to specify constraints or costs at particular moments in time, or delay differential equations for setting a delay time.
The operator works by replacing the time argument of time-dependent variables with the specified time t. For variables that don't depend on time, EvalAt returns them unchanged.
Behavior
- For time-dependent variables like
x(t),EvalAt(τ)(x)returnsx(τ) - For time-independent parameters,
EvalAtreturns them unchanged - For derivatives,
EvalAtevaluates the derivative at the specified time - For arrays of variables,
EvalAtis applied element-wise
Examples
using ModelingToolkitBase
@variables t x(t) y(t)
@parameters p
# Evaluate x at time t=1.0
EvalAt(1.0)(x) # Returns x(1.0)
# Works with parameters (returns unchanged)
EvalAt(1.0)(p) # Returns p
# Works with derivatives
D = Differential(t)
EvalAt(1.0)(D(x)) # Returns D(x) evaluated at t=1.0Errors
- Throws an error when applied to variables with more than one argument (e.g.,
z(u, t))
See also: Differential
While not an operator, ShiftIndex is commonly used to use Shift operators in a more convenient way when writing discrete systems.
ModelingToolkitBase.ShiftIndex — Type
ShiftIndexThe ShiftIndex operator allows you to index a signal and obtain a shifted discrete-time signal. If the signal is continuous-time, the signal is sampled before shifting.
Examples
julia> t = ModelingToolkitBase.t_nounits;
julia> @variables x(t);
julia> k = ShiftIndex(t, 0.1);
julia> x(k) # no shift
x(t)
julia> x(k+1) # shift
Shift(1)(x(t))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.
ModelingToolkitBase.Sample — Type
struct Sample <: SymbolicUtils.OperatorRepresents a sample operator. A discrete-time signal is created by sampling a continuous-time signal.
Constructors
Sample(clock::Union{TimeDomain, InferredTimeDomain} = InferredDiscrete())Sample(dt::Real)
Sample(x::Num), with a single argument, is shorthand for Sample()(x).
Fields
clock
Examples
julia> using Symbolics
julia> t = ModelingToolkit.t_nounits
julia> Δ = Sample(0.01)
(::Sample) (generic function with 2 methods)ModelingToolkitBase.Hold — Type
struct Hold <: SymbolicUtils.OperatorRepresents a hold operator. A continuous-time signal is produced by holding a discrete-time signal x with zero-order hold.
cont_x = Hold()(disc_x)ModelingToolkitBase.SampleTime — Type
function SampleTime()SampleTime() can be used in the equations of a hybrid system to represent time sampled at the inferred clock for that equation.