-
-
Notifications
You must be signed in to change notification settings - Fork 215
/
Copy pathsearch_index.js
3 lines (3 loc) · 380 KB
/
search_index.js
1
2
3
var documenterSearchIndex = {"docs":
[{"location":"basics/AbstractSystem/#The-AbstractSystem-Interface","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"","category":"section"},{"location":"basics/AbstractSystem/#Overview","page":"The AbstractSystem Interface","title":"Overview","text":"","category":"section"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"The AbstractSystem interface is the core of the system level of ModelingToolkit.jl. It establishes a common set of functionality that is used between systems representing ODEs, PDEs, SDEs and more, allowing users to have a common framework for model manipulation and compilation.","category":"page"},{"location":"basics/AbstractSystem/#Subtypes","page":"The AbstractSystem Interface","title":"Subtypes","text":"","category":"section"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"There are three immediate subtypes of AbstractSystem, classified by how many independent variables each type has:","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"AbstractTimeIndependentSystem: has no independent variable (e.g.: NonlinearSystem)\nAbstractTimeDependentSystem: has a single independent variable (e.g.: ODESystem)\nAbstractMultivariateSystem: may have multiple independent variables (e.g.: PDESystem)","category":"page"},{"location":"basics/AbstractSystem/#Constructors-and-Naming","page":"The AbstractSystem Interface","title":"Constructors and Naming","text":"","category":"section"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"The AbstractSystem interface has a consistent method for constructing systems. Generally, it follows the order of:","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"Equations\nIndependent Variables\nDependent Variables (or States)\nParameters","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"All other pieces are handled via keyword arguments. AbstractSystems share the same keyword arguments, which are:","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"system: This is used for specifying subsystems for hierarchical modeling with reusable components. For more information, see the components page.\nDefaults: Keyword arguments like defaults are used for specifying default values which are used. If a value is not given at the SciMLProblem construction time, its numerical value will be the default.","category":"page"},{"location":"basics/AbstractSystem/#Composition-and-Accessor-Functions","page":"The AbstractSystem Interface","title":"Composition and Accessor Functions","text":"","category":"section"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"Each AbstractSystem has lists of variables in context, such as distinguishing parameters vs states. In addition, an AbstractSystem can also hold other AbstractSystem types. Direct accessing of the values, such as sys.states, gives the immediate list, while the accessor functions states(sys) gives the total set, which includes that of all systems held inside.","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"The values which are common to all AbstractSystems are:","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"equations(sys): All equations that define the system and its subsystems.\nstates(sys): All the states in the system and its subsystems.\nparameters(sys): All parameters of the system and its subsystems.\nnameof(sys): The name of the current-level system.\nget_eqs(sys): Equations that define the current-level system.\nget_states(sys): States that are in the current-level system.\nget_ps(sys): Parameters that are in the current-level system.\nget_systems(sys): Subsystems of the current-level system.","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"Optionally, a system could have:","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"observed(sys): All observed equations of the system and its subsystems.\nindependent_variables(sys): The independent variables of a system.\ndefaults(sys): A Dict that maps variables/parameters into their default values for the system and its subsystems.\nget_observed(sys): Observed equations of the current-level system.\nget_continuous_events(sys): SymbolicContinuousCallbacks of the current-level system.\nget_defaults(sys): A Dict that maps variables into their default values for the current-level system.\nget_noiseeqs(sys): Noise equations of the current-level system.\nget_metadata(sys): Any metadata about the system or its origin to be used by downstream packages.","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"Note that if you know a system is an AbstractTimeDependentSystem you could use get_iv to get the unique independent variable directly, rather than using independent_variables(sys)[1], which is clunky and may cause problems if sys is an AbstractMultivariateSystem because there may be more than one independent variable. AbstractTimeIndependentSystems do not have a method get_iv, and independent_variables(sys) will return a size-zero result for such. For an AbstractMultivariateSystem, get_ivs is equivalent.","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"A system could also have caches:","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"get_jac(sys): The Jacobian of a system.\nget_tgrad(sys): The gradient with respect to time of a system.","category":"page"},{"location":"basics/AbstractSystem/#Transformations","page":"The AbstractSystem Interface","title":"Transformations","text":"","category":"section"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"Transformations are functions which send a valid AbstractSystem definition to another AbstractSystem. These are passes, like optimizations (e.g., Block-Lower Triangle transformations), or changes to the representation, which allow for alternative numerical methods to be utilized on the model (e.g., DAE index reduction).","category":"page"},{"location":"basics/AbstractSystem/#Analyses","page":"The AbstractSystem Interface","title":"Analyses","text":"","category":"section"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"Analyses are functions on a system which return information about the corresponding properties, like whether its parameters are structurally identifiable, or whether it's linear.","category":"page"},{"location":"basics/AbstractSystem/#Function-Calculation-and-Generation","page":"The AbstractSystem Interface","title":"Function Calculation and Generation","text":"","category":"section"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"The calculation and generation functions allow for calculating additional quantities to enhance the numerical methods applied to the resulting system. The calculations, like calculate_jacobian, generate ModelingToolkit IR for the Jacobian of the system, while the generations, like generate_jacobian, generate compiled output for the numerical solvers by applying build_function to the generated code. Additionally, many systems have function-type outputs, which cobble together the generation functionality for a system, for example, ODEFunction can be used to generate a DifferentialEquations-based ODEFunction with compiled version of the ODE itself, the Jacobian, the mass matrix, etc.","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"Below are the possible calculation and generation functions:","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"calculate_tgrad\ncalculate_gradient\ncalculate_jacobian\ncalculate_factorized_W\ncalculate_hessian\ngenerate_tgrad\ngenerate_gradient\ngenerate_jacobian\ngenerate_factorized_W\ngenerate_hessian","category":"page"},{"location":"basics/AbstractSystem/#ModelingToolkit.calculate_tgrad","page":"The AbstractSystem Interface","title":"ModelingToolkit.calculate_tgrad","text":"calculate_tgrad(sys::AbstractTimeDependentSystem)\n\nCalculate the time gradient of a system.\n\nReturns a vector of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/#ModelingToolkit.calculate_gradient","page":"The AbstractSystem Interface","title":"ModelingToolkit.calculate_gradient","text":"calculate_gradient(sys::AbstractSystem)\n\nCalculate the gradient of a scalar system.\n\nReturns a vector of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/#ModelingToolkit.calculate_jacobian","page":"The AbstractSystem Interface","title":"ModelingToolkit.calculate_jacobian","text":"calculate_jacobian(sys::AbstractSystem)\n\nCalculate the Jacobian matrix of a system.\n\nReturns a matrix of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/#ModelingToolkit.calculate_factorized_W","page":"The AbstractSystem Interface","title":"ModelingToolkit.calculate_factorized_W","text":"calculate_factorized_W(sys::AbstractSystem)\n\nCalculate the factorized W-matrix of a system.\n\nReturns a matrix of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/#ModelingToolkit.calculate_hessian","page":"The AbstractSystem Interface","title":"ModelingToolkit.calculate_hessian","text":"calculate_hessian(sys::AbstractSystem)\n\nCalculate the hessian matrix of a scalar system.\n\nReturns a matrix of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/#ModelingToolkit.generate_tgrad","page":"The AbstractSystem Interface","title":"ModelingToolkit.generate_tgrad","text":"generate_tgrad(sys::AbstractTimeDependentSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; kwargs...)\n\nGenerates a function for the time gradient of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/#ModelingToolkit.generate_gradient","page":"The AbstractSystem Interface","title":"ModelingToolkit.generate_gradient","text":"generate_gradient(sys::AbstractSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; kwargs...)\n\nGenerates a function for the gradient of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/#ModelingToolkit.generate_jacobian","page":"The AbstractSystem Interface","title":"ModelingToolkit.generate_jacobian","text":"generate_jacobian(sys::AbstractSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; sparse = false, kwargs...)\n\nGenerates a function for the Jacobian matrix of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/#ModelingToolkit.generate_factorized_W","page":"The AbstractSystem Interface","title":"ModelingToolkit.generate_factorized_W","text":"generate_factorized_W(sys::AbstractSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; sparse = false, kwargs...)\n\nGenerates a function for the factorized W matrix of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/#ModelingToolkit.generate_hessian","page":"The AbstractSystem Interface","title":"ModelingToolkit.generate_hessian","text":"generate_hessian(sys::AbstractSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; sparse = false, kwargs...)\n\nGenerates a function for the hessian matrix of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"Additionally, jacobian_sparsity(sys) and hessian_sparsity(sys) exist on the appropriate systems for fast generation of the sparsity patterns via an abstract interpretation without requiring differentiation.","category":"page"},{"location":"basics/AbstractSystem/#Problem-Constructors","page":"The AbstractSystem Interface","title":"Problem Constructors","text":"","category":"section"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"At the end, the system types have DEProblem constructors, like ODEProblem, which allow for directly generating the problem types required for numerical methods. The first argument is always the AbstractSystem, and the next arguments match the argument order of their original constructors. Whenever an array would normally be provided, such as u0 the initial condition of an ODEProblem, it is instead replaced with a variable map, i.e., an array of pairs var=>value, which allows the user to designate the values without having to know the order that ModelingToolkit is internally using.","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"For the value maps, the parameters are allowed to be functions of each other, and value maps of states can be functions of the parameters, i.e. you can do:","category":"page"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"u0 = [\n lorenz1.x => 2.0\n lorenz2.x => lorenz1.x * lorenz1.p\n]","category":"page"},{"location":"basics/AbstractSystem/#Default-Value-Handling","page":"The AbstractSystem Interface","title":"Default Value Handling","text":"","category":"section"},{"location":"basics/AbstractSystem/","page":"The AbstractSystem Interface","title":"The AbstractSystem Interface","text":"The AbstractSystem types allow for specifying default values, for example defaults inside of them. At problem construction time, these values are merged into the value maps, where for any repeats the value maps override the default. In addition, defaults of a higher level in the system override the defaults of a lower level in the system.","category":"page"},{"location":"basics/Variable_metadata/#symbolic_metadata","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"It is possible to add metadata to symbolic variables, the metadata will be displayed when calling help on a variable.","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"The following information can be added (note, it's possible to extend this to user-defined metadata as well)","category":"page"},{"location":"basics/Variable_metadata/#Variable-descriptions","page":"Symbolic Metadata","title":"Variable descriptions","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Descriptive strings can be attached to variables using the [description = \"descriptive string\"] syntax:","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"using ModelingToolkit\n@variables u [description = \"This is my input\"]\ngetdescription(u)","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"When variables with descriptions are present in systems, they will be printed when the system is shown in the terminal:","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"@parameters t\n@variables u(t) [description = \"A short description of u\"]\n@parameters p [description = \"A description of p\"]\n@named sys = ODESystem([u ~ p], t)\nshow(stdout, \"text/plain\", sys) # hide","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Calling help on the variable u displays the description, alongside other metadata:","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"help?> u\n\n A variable of type Symbolics.Num (Num wraps anything in a type that is a subtype of Real)\n\n Metadata\n ≡≡≡≡≡≡≡≡≡≡\n\n ModelingToolkit.VariableDescription: This is my input\n\n Symbolics.VariableSource: (:variables, :u)","category":"page"},{"location":"basics/Variable_metadata/#Connect","page":"Symbolic Metadata","title":"Connect","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Variables in connectors can have connect metadata which describes the type of connections.","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Flow is used for variables that represent physical quantities that \"flow\" ex: current in a resistor. These variables sum up to zero in connections.","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Stream can be specified for variables that flow bi-directionally.","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"using ModelingToolkit\n\n@variables t, i(t) [connect = Flow]\n@variables k(t) [connect = Stream]","category":"page"},{"location":"basics/Variable_metadata/#Input-or-output","page":"Symbolic Metadata","title":"Input or output","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Designate a variable as either an input or an output using the following","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"using ModelingToolkit\n@variables u [input = true]\nisinput(u)","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"@variables y [output = true]\nisoutput(y)","category":"page"},{"location":"basics/Variable_metadata/#Bounds","page":"Symbolic Metadata","title":"Bounds","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Bounds are useful when parameters are to be optimized, or to express intervals of uncertainty.","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"@variables u [bounds = (-1, 1)]\nhasbounds(u)","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"getbounds(u)","category":"page"},{"location":"basics/Variable_metadata/#Guess","page":"Symbolic Metadata","title":"Guess","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Specify an initial guess for custom initial conditions of an ODESystem.","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"@variables u [guess = 1]\nhasguess(u)","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"getguess(u)","category":"page"},{"location":"basics/Variable_metadata/#Mark-input-as-a-disturbance","page":"Symbolic Metadata","title":"Mark input as a disturbance","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Indicate that an input is not available for control, i.e., it's a disturbance input.","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"@variables u [input = true, disturbance = true]\nisdisturbance(u)","category":"page"},{"location":"basics/Variable_metadata/#Mark-parameter-as-tunable","page":"Symbolic Metadata","title":"Mark parameter as tunable","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Indicate that a parameter can be automatically tuned by parameter optimization or automatic control tuning apps.","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"@parameters Kp [tunable = true]\nistunable(Kp)","category":"page"},{"location":"basics/Variable_metadata/#Probability-distributions","page":"Symbolic Metadata","title":"Probability distributions","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"A probability distribution may be associated with a parameter to indicate either uncertainty about its value, or as a prior distribution for Bayesian optimization.","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"using Distributions\nd = Normal(10, 1)\n@parameters m [dist = d]\nhasdist(m)","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"getdist(m)","category":"page"},{"location":"basics/Variable_metadata/#Additional-functions","page":"Symbolic Metadata","title":"Additional functions","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"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","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"@parameters t\nDₜ = Differential(t)\n@variables x(t)=0 u(t)=0 [input = true] y(t)=0 [output = true]\n@parameters T [tunable = true, bounds = (0, Inf)]\n@parameters k [tunable = true, bounds = (0, Inf)]\neqs = [Dₜ(x) ~ (-x + k * u) / T # A first-order system with time constant T and gain k\n y ~ x]\nsys = ODESystem(eqs, t, name = :tunable_first_order)","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"p = tunable_parameters(sys) # extract all parameters marked as tunable","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"lb, ub = getbounds(p) # operating on a vector, we get lower and upper bound vectors","category":"page"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"b = getbounds(sys) # Operating on the system, we get a dict","category":"page"},{"location":"basics/Variable_metadata/#Index","page":"Symbolic Metadata","title":"Index","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Pages = [\"Variable_metadata.md\"]","category":"page"},{"location":"basics/Variable_metadata/#Docstrings","page":"Symbolic Metadata","title":"Docstrings","text":"","category":"section"},{"location":"basics/Variable_metadata/","page":"Symbolic Metadata","title":"Symbolic Metadata","text":"Modules = [ModelingToolkit]\nPages = [\"variables.jl\"]\nPrivate = false","category":"page"},{"location":"basics/Variable_metadata/#ModelingToolkit.getbounds","page":"Symbolic Metadata","title":"ModelingToolkit.getbounds","text":"getbounds(sys::ModelingToolkit.AbstractSystem, p = parameters(sys))\n\nReturns a dict with pairs p => (lb, ub) mapping parameters of sys to lower and upper bounds. Create parameters with bounds like this\n\n@parameters p [bounds=(-1, 1)]\n\nTo obtain state bounds, call getbounds(sys, states(sys))\n\n\n\n\n\n","category":"function"},{"location":"basics/Variable_metadata/#ModelingToolkit.getbounds-Tuple{AbstractVector}","page":"Symbolic Metadata","title":"ModelingToolkit.getbounds","text":"lb, ub = getbounds(p::AbstractVector)\n\nReturn vectors of lower and upper bounds of parameter vector p. Create parameters with bounds like this\n\n@parameters p [bounds=(-1, 1)]\n\nSee also tunable_parameters, hasbounds\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.getbounds-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.getbounds","text":"getbounds(x)\n\nGet the bounds associated with symbolic variable x. Create parameters with bounds like this\n\n@parameters p [bounds=(-1, 1)]\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.getdescription-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.getdescription","text":"getdescription(x)\n\nReturn any description attached to variables x. If no description is attached, an empty string is returned.\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.getdist-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.getdist","text":"getdist(x)\n\nGet 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\n\nusing Distributions\nd = Normal(0, 1)\n@parameters u [dist = d]\nhasdist(u) # true\ngetdist(u) # retrieve distribution\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.getguess-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.getguess","text":"getguess(x)\n\nGet the guess for the initial value associated with symbolic variable x. Create variables with a guess like this\n\n@variables x [guess=1]\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.hasbounds-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.hasbounds","text":"hasbounds(x)\n\nDetermine whether symbolic variable x has bounds associated with it. See also getbounds.\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.hasdist-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.hasdist","text":"hasdist(x)\n\nDetermine whether symbolic variable x has a probability distribution associated with it.\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.hasguess-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.hasguess","text":"hasguess(x)\n\nDetermine whether symbolic variable x has a guess associated with it. See also getguess.\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.isbinaryvar-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.isbinaryvar","text":"isbinaryvar(x)\n\nDetermine if a variable is binary.\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.isdisturbance-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.isdisturbance","text":"isdisturbance(x)\n\nDetermine whether symbolic variable x is marked as a disturbance input.\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.isintegervar-Tuple{Any}","page":"Symbolic Metadata","title":"ModelingToolkit.isintegervar","text":"isintegervar(x)\n\nDetermine if a variable is an integer.\n\n\n\n\n\n","category":"method"},{"location":"basics/Variable_metadata/#ModelingToolkit.istunable","page":"Symbolic Metadata","title":"ModelingToolkit.istunable","text":"istunable(x, default = false)\n\nDetermine whether symbolic variable x is marked as a tunable for an automatic tuning algorithm.\n\ndefault indicates whether variables without tunable metadata are to be considered tunable or not.\n\nCreate a tunable parameter by\n\n@parameters u [tunable=true]\n\nSee also tunable_parameters, getbounds\n\n\n\n\n\n","category":"function"},{"location":"basics/Variable_metadata/#ModelingToolkit.tunable_parameters","page":"Symbolic Metadata","title":"ModelingToolkit.tunable_parameters","text":"tunable_parameters(sys, p = parameters(sys); default=false)\n\nGet all parameters of sys that are marked as tunable.\n\nKeyword argument default indicates whether variables without tunable metadata are to be considered tunable or not.\n\nCreate a tunable parameter by\n\n@parameters u [tunable=true]\n\nSee also getbounds, istunable\n\n\n\n\n\n","category":"function"},{"location":"basics/Variable_metadata/#ModelingToolkit.@brownian-Tuple","page":"Symbolic Metadata","title":"ModelingToolkit.@brownian","text":"Define one or more Brownian variables.\n\n\n\n\n\n","category":"macro"},{"location":"examples/modelingtoolkitize_index_reduction/#Automated-Index-Reduction-of-DAEs","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"","category":"section"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"In many cases one may accidentally write down a DAE that is not easily solvable by numerical methods. In this tutorial, we will walk through an example of a pendulum which accidentally generates an index-3 DAE, and show how to use the modelingtoolkitize to correct the model definition before solving.","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/#Copy-Pastable-Example","page":"Automated Index Reduction of DAEs","title":"Copy-Pastable Example","text":"","category":"section"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"using ModelingToolkit\nusing LinearAlgebra\nusing OrdinaryDiffEq\nusing Plots\n\nfunction pendulum!(du, u, p, t)\n x, dx, y, dy, T = u\n g, L = p\n du[1] = dx\n du[2] = T * x\n du[3] = dy\n du[4] = T * y - g\n du[5] = x^2 + y^2 - L^2\n return nothing\nend\npendulum_fun! = ODEFunction(pendulum!, mass_matrix = Diagonal([1, 1, 1, 1, 0]))\nu0 = [1.0, 0, 0, 0, 0]\np = [9.8, 1]\ntspan = (0, 10.0)\npendulum_prob = ODEProblem(pendulum_fun!, u0, tspan, p)\ntraced_sys = modelingtoolkitize(pendulum_prob)\npendulum_sys = structural_simplify(dae_index_lowering(traced_sys))\nprob = ODAEProblem(pendulum_sys, [], tspan)\nsol = solve(prob, Tsit5(), abstol = 1e-8, reltol = 1e-8)\nplot(sol, idxs = states(traced_sys))","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/#Explanation","page":"Automated Index Reduction of DAEs","title":"Explanation","text":"","category":"section"},{"location":"examples/modelingtoolkitize_index_reduction/#Attempting-to-Solve-the-Equation","page":"Automated Index Reduction of DAEs","title":"Attempting to Solve the Equation","text":"","category":"section"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"In this tutorial, we will look at the pendulum system:","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"beginaligned\n x^prime = v_x\n v_x^prime = Tx\n y^prime = v_y\n v_y^prime = Ty - g\n 0 = x^2 + y^2 - L^2\nendaligned","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"As a good DifferentialEquations.jl user, one would follow the mass matrix DAE tutorial to arrive at code for simulating the model:","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"using OrdinaryDiffEq, LinearAlgebra\nfunction pendulum!(du, u, p, t)\n x, dx, y, dy, T = u\n g, L = p\n du[1] = dx\n du[2] = T * x\n du[3] = dy\n du[4] = T * y - g\n du[5] = x^2 + y^2 - L^2\nend\npendulum_fun! = ODEFunction(pendulum!, mass_matrix = Diagonal([1, 1, 1, 1, 0]))\nu0 = [1.0, 0, 0, 0, 0];\np = [9.8, 1];\ntspan = (0, 10.0);\npendulum_prob = ODEProblem(pendulum_fun!, u0, tspan, p)\nsolve(pendulum_prob, Rodas4())","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"However, one will quickly be greeted with the unfortunate message:","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"┌ Warning: First function call produced NaNs. Exiting.\n└ @ OrdinaryDiffEq C:\\Users\\accou\\.julia\\packages\\OrdinaryDiffEq\\yCczp\\src\\initdt.jl:76\n┌ Warning: Automatic dt set the starting dt as NaN, causing instability.\n└ @ OrdinaryDiffEq C:\\Users\\accou\\.julia\\packages\\OrdinaryDiffEq\\yCczp\\src\\solve.jl:485\n┌ Warning: NaN dt detected. Likely a NaN value in the state, parameters, or derivative value caused this outcome.\n└ @ SciMLBase C:\\Users\\accou\\.julia\\packages\\SciMLBase\\DrPil\\src\\integrator_interface.jl:325","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"Did you implement the DAE incorrectly? No. Is the solver broken? No.","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/#Understanding-DAE-Index","page":"Automated Index Reduction of DAEs","title":"Understanding DAE Index","text":"","category":"section"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"It turns out that this is a property of the DAE that we are attempting to solve. This kind of DAE is known as an index-3 DAE. For a complete discussion of DAE index, see this article. Essentially, the issue here is that we have 4 differential variables (x, v_x, y, v_y) and one algebraic variable T (which we can know because there is no D(T) term in the equations). An index-1 DAE always satisfies that the Jacobian of the algebraic equations is non-singular. Here, the first 4 equations are differential equations, with the last term the algebraic relationship. However, the partial derivative of x^2 + y^2 - L^2 w.r.t. T is zero, and thus the Jacobian of the algebraic equations is the zero matrix, and thus it's singular. This is a rapid way to see whether the DAE is index 1!","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"The problem with higher order DAEs is that the matrices used in Newton solves are singular or close to singular when applied to such problems. Because of this fact, the nonlinear solvers (or Rosenbrock methods) break down, making them difficult to solve. The classic paper DAEs are not ODEs goes into detail on this and shows that many methods are no longer convergent when index is higher than one. So, it's not necessarily the fault of the solver or the implementation: this is known.","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"But that's not a satisfying answer, so what do you do about it?","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/#Transforming-Higher-Order-DAEs-to-Index-1-DAEs","page":"Automated Index Reduction of DAEs","title":"Transforming Higher Order DAEs to Index-1 DAEs","text":"","category":"section"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"It turns out that higher order DAEs can be transformed into lower order DAEs. If you differentiate the last equation two times and perform a substitution, you can arrive at the following set of equations:","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"beginaligned\nx^prime = v_x \nv_x^prime = x T \ny^prime = v_y \nv_y^prime = y T - g \n0 = 2 left(v_x^2 + v_y^2 + y ( y T - g ) + T x^2 right)\nendaligned","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"Note that this is mathematically-equivalent to the equation that we had before, but the Jacobian w.r.t. T of the algebraic equation is no longer zero because of the substitution. This means that if you wrote down this version of the model, it will be index-1 and solve correctly! In fact, this is how DAE index is commonly defined: the number of differentiations it takes to transform the DAE into an ODE, where an ODE is an index-0 DAE by substituting out all of the algebraic relationships.","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/#Automating-the-Index-Reduction","page":"Automated Index Reduction of DAEs","title":"Automating the Index Reduction","text":"","category":"section"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"However, requiring the user to sit there and work through this process on potentially millions of equations is an unfathomable mental overhead. But, we can avoid this by using methods like the Pantelides algorithm for automatically performing this reduction to index 1. While this requires the ModelingToolkit symbolic form, we use modelingtoolkitize to transform the numerical code into symbolic code, run dae_index_lowering lowering, then transform back to numerical code with ODEProblem, and solve with a numerical solver. Let's try that out:","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"traced_sys = modelingtoolkitize(pendulum_prob)\npendulum_sys = structural_simplify(dae_index_lowering(traced_sys))\nprob = ODEProblem(pendulum_sys, Pair[], tspan)\nsol = solve(prob, Rodas4())\n\nusing Plots\nplot(sol, idxs = states(traced_sys))","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"Note that plotting using states(traced_sys) is done so that any variables which are symbolically eliminated, or any variable reordering done for enhanced parallelism/performance, still show up in the resulting plot and the plot is shown in the same order as the original numerical code.","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"Note that we can even go a bit further. If we use the ODAEProblem constructor, we can remove the algebraic equations from the states of the system and fully transform the index-3 DAE into an index-0 ODE which can be solved via an explicit Runge-Kutta method:","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"traced_sys = modelingtoolkitize(pendulum_prob)\npendulum_sys = structural_simplify(dae_index_lowering(traced_sys))\nprob = ODAEProblem(pendulum_sys, Pair[], tspan)\nsol = solve(prob, Tsit5(), abstol = 1e-8, reltol = 1e-8)\nplot(sol, idxs = states(traced_sys))","category":"page"},{"location":"examples/modelingtoolkitize_index_reduction/","page":"Automated Index Reduction of DAEs","title":"Automated Index Reduction of DAEs","text":"And there you go: this has transformed the model from being too hard to solve with implicit DAE solvers, to something that is easily solved with explicit Runge-Kutta methods for non-stiff equations.","category":"page"},{"location":"systems/NonlinearSystem/#NonlinearSystem","page":"NonlinearSystem","title":"NonlinearSystem","text":"","category":"section"},{"location":"systems/NonlinearSystem/#System-Constructors","page":"NonlinearSystem","title":"System Constructors","text":"","category":"section"},{"location":"systems/NonlinearSystem/","page":"NonlinearSystem","title":"NonlinearSystem","text":"NonlinearSystem","category":"page"},{"location":"systems/NonlinearSystem/#ModelingToolkit.NonlinearSystem","page":"NonlinearSystem","title":"ModelingToolkit.NonlinearSystem","text":"struct NonlinearSystem <: AbstractTimeIndependentSystem\n\nA nonlinear system of equations.\n\nFields\n\ntag: A tag for the system. If two systems have the same tag, then they are structurally identical.\n\neqs: Vector of equations defining the system.\nstates: Unknown variables.\nps: Parameters.\nvar_to_name: Array variables.\nobserved: Observed states.\njac: Jacobian matrix. Note: this field will not be defined until calculate_jacobian is called on the system.\n\nname: The name of the system.\n\nsystems: The internal systems. These are required to have unique names.\n\ndefaults: The default values to use when initial conditions and/or parameters are not supplied in ODEProblem.\n\nconnector_type: Type of the system.\n\nmetadata: Metadata for the system, to be used by downstream packages.\n\ngui_metadata: Metadata for MTK GUI.\n\ntearing_state: Cache for intermediate tearing state.\n\nsubstitutions: Substitutions generated by tearing.\n\ncomplete: If a model sys is complete, then sys.x no longer performs namespacing.\n\nparent: The hierarchical parent system before simplification.\n\nExamples\n\n@variables x y z\n@parameters σ ρ β\n\neqs = [0 ~ σ*(y-x),\n 0 ~ x*(ρ-z)-y,\n 0 ~ x*y - β*z]\n@named ns = NonlinearSystem(eqs, [x,y,z],[σ,ρ,β])\n\n\n\n\n\n","category":"type"},{"location":"systems/NonlinearSystem/#Composition-and-Accessor-Functions","page":"NonlinearSystem","title":"Composition and Accessor Functions","text":"","category":"section"},{"location":"systems/NonlinearSystem/","page":"NonlinearSystem","title":"NonlinearSystem","text":"get_eqs(sys) or equations(sys): The equations that define the nonlinear system.\nget_states(sys) or states(sys): The set of states in the nonlinear system.\nget_ps(sys) or parameters(sys): The parameters of the nonlinear system.\nget_u0_p(sys, u0map, parammap) Numeric arrays for the initial condition and parameters given var => value maps.","category":"page"},{"location":"systems/NonlinearSystem/#Transformations","page":"NonlinearSystem","title":"Transformations","text":"","category":"section"},{"location":"systems/NonlinearSystem/","page":"NonlinearSystem","title":"NonlinearSystem","text":"structural_simplify\nalias_elimination\ntearing","category":"page"},{"location":"systems/NonlinearSystem/#ModelingToolkit.structural_simplify-systems-NonlinearSystem","page":"NonlinearSystem","title":"ModelingToolkit.structural_simplify","text":"structural_simplify(sys; ...)\nstructural_simplify(sys, io; simplify, kwargs...)\n\n\nStructurally simplify algebraic equations in a system and compute the topological sort of the observed equations. When simplify=true, the simplify function will be applied during the tearing process. It also takes kwargs allow_symbolic=false and allow_parameter=true which limits the coefficient types during tearing.\n\nThe optional argument io may take a tuple (inputs, outputs). This will convert all inputs to parameters and allow them to be unconnected, i.e., simplification will allow models where n_states = n_equations - n_inputs.\n\n\n\n\n\n","category":"function"},{"location":"systems/NonlinearSystem/#ModelingToolkit.StructuralTransformations.tearing-systems-NonlinearSystem","page":"NonlinearSystem","title":"ModelingToolkit.StructuralTransformations.tearing","text":"tearing(sys; simplify=false)\n\nTear the nonlinear equations in system. When simplify=true, we simplify the new residual equations after tearing. End users are encouraged to call structural_simplify instead, which calls this function internally.\n\n\n\n\n\n","category":"function"},{"location":"systems/NonlinearSystem/#Analyses","page":"NonlinearSystem","title":"Analyses","text":"","category":"section"},{"location":"systems/NonlinearSystem/","page":"NonlinearSystem","title":"NonlinearSystem","text":"ModelingToolkit.isaffine\nModelingToolkit.islinear","category":"page"},{"location":"systems/NonlinearSystem/#Applicable-Calculation-and-Generation-Functions","page":"NonlinearSystem","title":"Applicable Calculation and Generation Functions","text":"","category":"section"},{"location":"systems/NonlinearSystem/","page":"NonlinearSystem","title":"NonlinearSystem","text":"calculate_jacobian\ngenerate_jacobian\njacobian_sparsity","category":"page"},{"location":"systems/NonlinearSystem/#ModelingToolkit.calculate_jacobian-systems-NonlinearSystem","page":"NonlinearSystem","title":"ModelingToolkit.calculate_jacobian","text":"calculate_jacobian(sys::AbstractSystem)\n\nCalculate the Jacobian matrix of a system.\n\nReturns a matrix of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"systems/NonlinearSystem/#ModelingToolkit.generate_jacobian-systems-NonlinearSystem","page":"NonlinearSystem","title":"ModelingToolkit.generate_jacobian","text":"generate_jacobian(sys::AbstractSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; sparse = false, kwargs...)\n\nGenerates a function for the Jacobian matrix of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"systems/NonlinearSystem/#Problem-Constructors","page":"NonlinearSystem","title":"Problem Constructors","text":"","category":"section"},{"location":"systems/NonlinearSystem/","page":"NonlinearSystem","title":"NonlinearSystem","text":"NonlinearFunction(sys::ModelingToolkit.NonlinearSystem, args...)\nNonlinearProblem(sys::ModelingToolkit.NonlinearSystem, args...)","category":"page"},{"location":"systems/NonlinearSystem/#SciMLBase.NonlinearFunction-Tuple{NonlinearSystem, Vararg{Any}}","page":"NonlinearSystem","title":"SciMLBase.NonlinearFunction","text":"SciMLBase.NonlinearFunction{iip}(sys::NonlinearSystem, dvs = states(sys),\n ps = parameters(sys);\n version = nothing,\n jac = false,\n sparse = false,\n kwargs...) where {iip}\n\nCreate an NonlinearFunction from the NonlinearSystem. The arguments dvs and ps are used to set the order of the dependent variable and parameter vectors, respectively.\n\n\n\n\n\n","category":"method"},{"location":"systems/NonlinearSystem/#SciMLBase.NonlinearProblem-Tuple{NonlinearSystem, Vararg{Any}}","page":"NonlinearSystem","title":"SciMLBase.NonlinearProblem","text":"DiffEqBase.NonlinearProblem{iip}(sys::NonlinearSystem, u0map,\n parammap = DiffEqBase.NullParameters();\n jac = false, sparse = false,\n checkbounds = false,\n linenumbers = true, parallel = SerialForm(),\n kwargs...) where {iip}\n\nGenerates an NonlinearProblem from a NonlinearSystem and allows for automatically symbolically calculating numerical enhancements.\n\n\n\n\n\n","category":"method"},{"location":"systems/NonlinearSystem/#Torn-Problem-Constructors","page":"NonlinearSystem","title":"Torn Problem Constructors","text":"","category":"section"},{"location":"systems/NonlinearSystem/","page":"NonlinearSystem","title":"NonlinearSystem","text":"BlockNonlinearProblem","category":"page"},{"location":"systems/NonlinearSystem/#Expression-Constructors","page":"NonlinearSystem","title":"Expression Constructors","text":"","category":"section"},{"location":"systems/NonlinearSystem/","page":"NonlinearSystem","title":"NonlinearSystem","text":"NonlinearFunctionExpr\nNonlinearProblemExpr","category":"page"},{"location":"systems/NonlinearSystem/#ModelingToolkit.NonlinearFunctionExpr","page":"NonlinearSystem","title":"ModelingToolkit.NonlinearFunctionExpr","text":"SciMLBase.NonlinearFunctionExpr{iip}(sys::NonlinearSystem, dvs = states(sys),\n ps = parameters(sys);\n version = nothing,\n jac = false,\n sparse = false,\n kwargs...) where {iip}\n\nCreate a Julia expression for an ODEFunction from the ODESystem. The arguments dvs and ps are used to set the order of the dependent variable and parameter vectors, respectively.\n\n\n\n\n\n","category":"type"},{"location":"systems/NonlinearSystem/#ModelingToolkit.NonlinearProblemExpr","page":"NonlinearSystem","title":"ModelingToolkit.NonlinearProblemExpr","text":"DiffEqBase.NonlinearProblemExpr{iip}(sys::NonlinearSystem, u0map,\n parammap = DiffEqBase.NullParameters();\n jac = false, sparse = false,\n checkbounds = false,\n linenumbers = true, parallel = SerialForm(),\n kwargs...) where {iip}\n\nGenerates a Julia expression for a NonlinearProblem from a NonlinearSystem and allows for automatically symbolically calculating numerical enhancements.\n\n\n\n\n\n","category":"type"},{"location":"tutorials/ode_modeling/#Getting-Started-with-ModelingToolkit.jl","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"This is an introductory tutorial for ModelingToolkit (MTK). We will demonstrate the basics of the package by demonstrating how to define and simulate simple Ordinary Differential Equation (ODE) systems.","category":"page"},{"location":"tutorials/ode_modeling/#Installing-ModelingToolkit","page":"Getting Started with ModelingToolkit.jl","title":"Installing ModelingToolkit","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"To install ModelingToolkit, use the Julia package manager. This can be done as follows:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"using Pkg\nPkg.add(\"ModelingToolkit\")","category":"page"},{"location":"tutorials/ode_modeling/#Copy-Pastable-Simplified-Example","page":"Getting Started with ModelingToolkit.jl","title":"Copy-Pastable Simplified Example","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"A much deeper tutorial with forcing functions and sparse Jacobians is below. But if you want to just see some code and run, here's an example:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"using ModelingToolkit\n\n@variables t\nD = Differential(t)\n\n@mtkmodel FOL begin\n @parameters begin\n τ # parameters\n end\n @variables begin\n x(t) # dependent variables\n end\n @equations begin\n D(x) ~ (1 - x) / τ\n end\nend\n\nusing DifferentialEquations: solve\n@mtkbuild fol = FOL()\nprob = ODEProblem(fol, [fol.x => 0.0], (0.0, 10.0), [fol.τ => 3.0])\nsol = solve(prob)\n\nusing Plots\nplot(sol)","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Now let's start digging into MTK!","category":"page"},{"location":"tutorials/ode_modeling/#Your-very-first-ODE","page":"Getting Started with ModelingToolkit.jl","title":"Your very first ODE","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Let us start with a minimal example. The system to be modelled is a first-order lag element:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"dotx = fracf(t) - x(t)tau","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Here, t is the independent variable (time), x(t) is the (scalar) state variable, f(t) is an external forcing function, and tau is a parameter. In MTK, this system can be modelled as follows. For simplicity, we first set the forcing function to a time-independent value 1. And the independent variable t is automatically added by mtkmodel.","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"using ModelingToolkit\n\n@variables t\nD = Differential(t)\n\n@mtkmodel FOL begin\n @parameters begin\n τ # parameters\n end\n @variables begin\n x(t) # dependent variables\n end\n @equations begin\n D(x) ~ (1 - x) / τ\n end\nend\n\n@mtkbuild fol = FOL()","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Note that equations in MTK use the tilde character (~) as equality sign.","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"@mtkbuild creates an instance of FOL named as fol.","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"After construction of the ODE, you can solve it using DifferentialEquations.jl:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"using DifferentialEquations\nusing Plots\n\nprob = ODEProblem(fol, [fol.x => 0.0], (0.0, 10.0), [fol.τ => 3.0])\nplot(solve(prob))","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"The initial state and the parameter values are specified using a mapping from the actual symbolic elements to their values, represented as an array of Pairs, which are constructed using the => operator.","category":"page"},{"location":"tutorials/ode_modeling/#Algebraic-relations-and-structural-simplification","page":"Getting Started with ModelingToolkit.jl","title":"Algebraic relations and structural simplification","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"You could separate the calculation of the right-hand side, by introducing an intermediate variable RHS:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"using ModelingToolkit\n\n@mtkmodel FOL begin\n @parameters begin\n τ # parameters\n end\n @variables begin\n x(t) # dependent variables\n RHS(t)\n end\n begin\n D = Differential(t)\n end\n @equations begin\n RHS ~ (1 - x) / τ\n D(x) ~ RHS\n end\nend\n\n@mtkbuild fol = FOL()","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"You can look at the equations by using the command equations:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"equations(fol)","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Notice that there is only one equation in this system, Differential(t)(x(t)) ~ RHS(t). The other equation was removed from the system and was transformed into an observed variable. Observed equations are variables which can be computed on-demand but are not necessary for the solution of the system, and thus MTK tracks it separately. One can check the observed equations via the observed function:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"observed(fol)","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"For more information on this process, see Observables and Variable Elimination.","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"MTK still knows how to calculate them out of the information available in a simulation result. The intermediate variable RHS therefore can be plotted along with the state variable. Note that this has to be requested explicitly like as follows:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"prob = ODEProblem(fol,\n [fol.x => 0.0],\n (0.0, 10.0),\n [fol.τ => 3.0])\nsol = solve(prob)\nplot(sol, vars = [fol.x, fol.RHS])","category":"page"},{"location":"tutorials/ode_modeling/#Named-Indexing-of-Solutions","page":"Getting Started with ModelingToolkit.jl","title":"Named Indexing of Solutions","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Note that the indexing of the solution similarly works via the names, and so to get the time series for x, one would do:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"sol[fol.x]","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"or to get the second value in the time series for x:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"sol[fol.x, 2]","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Similarly, the time series for RHS can be retrieved using the same indexing:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"sol[fol.RHS]","category":"page"},{"location":"tutorials/ode_modeling/#Specifying-a-time-variable-forcing-function","page":"Getting Started with ModelingToolkit.jl","title":"Specifying a time-variable forcing function","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"What if the forcing function (the “external input”) f(t) is not constant? Obviously, one could use an explicit, symbolic function of time:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"@mtkmodel FOL begin\n @parameters begin\n τ # parameters\n end\n @variables begin\n x(t) # dependent variables\n f(t)\n end\n begin\n D = Differential(t)\n end\n @equations begin\n f ~ sin(t)\n D(x) ~ (f - x) / τ\n end\nend\n\n@named fol_variable_f = FOL()","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"But often this function might not be available in an explicit form. Instead the function might be provided as time-series data. MTK handles this situation by allowing us to “register” arbitrary Julia functions, which are excluded from symbolic transformations, and thus used as-is. So, you could, for example, interpolate given the time-series using DataInterpolations.jl. Here, we illustrate this option by a simple lookup (\"zero-order hold\") of a vector of random values:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"value_vector = randn(10)\nf_fun(t) = t >= 10 ? value_vector[end] : value_vector[Int(floor(t)) + 1]\n@register_symbolic f_fun(t)\n\n@mtkmodel FOLExternalFunction begin\n @parameters begin\n τ # parameters\n end\n @variables begin\n x(t) # dependent variables\n f(t)\n end\n @structural_parameters begin\n h = 1\n end\n begin\n D = Differential(t)\n end\n @equations begin\n f ~ f_fun(t)\n D(x) ~ (f - x) / τ\n end\nend\n\n@mtkbuild fol_external_f = FOLExternalFunction()\nprob = ODEProblem(fol_external_f,\n [fol_external_f.x => 0.0],\n (0.0, 10.0),\n [fol_external_f.τ => 0.75])\n\nsol = solve(prob)\nplot(sol, vars = [fol_external_f.x, fol_external_f.f])","category":"page"},{"location":"tutorials/ode_modeling/#Building-component-based,-hierarchical-models","page":"Getting Started with ModelingToolkit.jl","title":"Building component-based, hierarchical models","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Working with simple one-equation systems is already fun, but composing more complex systems from simple ones is even more fun. Best practice for such a “modeling framework” could be to use factory functions for model components:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"function fol_factory(separate = false; name)\n @parameters τ\n @variables t x(t) f(t) RHS(t)\n\n eqs = separate ? [RHS ~ (f - x) / τ,\n D(x) ~ RHS] :\n D(x) ~ (f - x) / τ\n\n ODESystem(eqs; name)\nend","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Such a factory can then be used to instantiate the same component multiple times, but allows for customization:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"@named fol_1 = fol_factory()\n@named fol_2 = fol_factory(true) # has observable RHS","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"The @named macro rewrites fol_2 = fol_factory(true) into fol_2 = fol_factory(true,:fol_2). Now, these two components can be used as subsystems of a parent system, i.e. one level higher in the model hierarchy. The connections between the components again are just algebraic relations:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"connections = [fol_1.f ~ 1.5,\n fol_2.f ~ fol_1.x]\n\nconnected = compose(ODESystem(connections, name = :connected), fol_1, fol_2)","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"All equations, variables, and parameters are collected, but the structure of the hierarchical model is still preserved. This means you can still get information about fol_1 by addressing it by connected.fol_1, or its parameter by connected.fol_1.τ. Before simulation, we again eliminate the algebraic variables and connection equations from the system using structural simplification:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"connected_simp = structural_simplify(connected)","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"full_equations(connected_simp)","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"As expected, only the two state-derivative equations remain, as if you had manually eliminated as many variables as possible from the equations. Some observed variables are not expanded unless full_equations is used. As mentioned above, the hierarchical structure is preserved. So, the initial state and the parameter values can be specified accordingly when building the ODEProblem:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"u0 = [fol_1.x => -0.5,\n fol_2.x => 1.0]\n\np = [fol_1.τ => 2.0,\n fol_2.τ => 4.0]\n\nprob = ODEProblem(connected_simp, u0, (0.0, 10.0), p)\nplot(solve(prob))","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"More on this topic may be found in Composing Models and Building Reusable Components.","category":"page"},{"location":"tutorials/ode_modeling/#Initial-Guess","page":"Getting Started with ModelingToolkit.jl","title":"Initial Guess","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"It is often a good idea to specify reasonable values for the initial state and the parameters of a model component. Then, these do not have to be explicitly specified when constructing the ODEProblem.","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"@mtkmodel UnitstepFOLFactory begin\n @parameters begin\n τ = 1.0\n end\n @variables begin\n x(t) = 0.0\n end\n @equations begin\n D(x) ~ (1 - x) / τ\n end\nend","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"While defining the model UnitstepFOLFactory, an initial guess of 0.0 is assigned to x(t) and 1.0 to τ. Additionally, these initial guesses can be modified while creating instances of UnitstepFOLFactory by passing arguments.","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"@named fol = UnitstepFOLFactory(; x = 0.1)\nsol = ODEProblem(fol, [], (0.0, 5.0), []) |> solve","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"In non-DSL definitions, one can pass defaults dictionary to set the initial guess of the symbolic variables.","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"using ModelingToolkit\n\nfunction UnitstepFOLFactory(; name)\n @parameters τ\n @variables t x(t)\n ODESystem(D(x) ~ (1 - x) / τ; name, defaults = Dict(x => 0.0, τ => 1.0))\nend","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Note that the defaults can be functions of the other variables, which is then resolved at the time of the problem construction. Of course, the factory function could accept additional arguments to optionally specify the initial state or parameter values, etc.","category":"page"},{"location":"tutorials/ode_modeling/#Symbolic-and-sparse-derivatives","page":"Getting Started with ModelingToolkit.jl","title":"Symbolic and sparse derivatives","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"One advantage of a symbolic toolkit is that derivatives can be calculated explicitly, and that the incidence matrix of partial derivatives (the “sparsity pattern”) can also be explicitly derived. These two facts lead to a substantial speedup of all model calculations, e.g. when simulating a model over time using an ODE solver.","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"By default, analytical derivatives and sparse matrices, e.g. for the Jacobian, the matrix of first partial derivatives, are not used. Let's benchmark this (prob still is the problem using the connected_simp system above):","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"using BenchmarkTools\n@btime solve(prob, Rodas4());\nnothing # hide","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Now have MTK provide sparse, analytical derivatives to the solver. This has to be specified during the construction of the ODEProblem:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"prob_an = ODEProblem(connected_simp, u0, (0.0, 10.0), p; jac = true)\n@btime solve($prob_an, Rodas4());\nnothing # hide","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"prob_an = ODEProblem(connected_simp, u0, (0.0, 10.0), p; jac = true, sparse = true)\n@btime solve($prob_an, Rodas4());\nnothing # hide","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"The speedup is significant. For this small dense model (3 of 4 entries are populated), using sparse matrices is counterproductive in terms of required memory allocations. For large, hierarchically built models, which tend to be sparse, speedup and the reduction of memory allocation can be expected to be substantial. In addition, these problem builders allow for automatic parallelism using the structural information. For more information, see the ODESystem page.","category":"page"},{"location":"tutorials/ode_modeling/#Notes-and-pointers-how-to-go-on","page":"Getting Started with ModelingToolkit.jl","title":"Notes and pointers how to go on","text":"","category":"section"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Here are some notes that may be helpful during your initial steps with MTK:","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"The @mtkmodel macro is for high-level usage of MTK. However, in many cases you may need to programmatically generate ODESystems. If that's the case, check out the Programmatically Generating and Scripting ODESystems Tutorial.\nVector-valued parameters and variables are possible. A cleaner, more consistent treatment of these is still a work in progress, however. Once finished, this introductory tutorial will also cover this feature.","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Where to go next?","category":"page"},{"location":"tutorials/ode_modeling/","page":"Getting Started with ModelingToolkit.jl","title":"Getting Started with ModelingToolkit.jl","text":"Not sure how MTK relates to similar tools and packages? Read Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages.\nFor a more detailed explanation of @mtkmodel checkout Defining components with @mtkmodel and connectors with @connectors\nDepending on what you want to do with MTK, have a look at some of the other Symbolic Modeling Tutorials.\nIf you want to automatically convert an existing function to a symbolic representation, you might go through the ModelingToolkitize Tutorials.\nTo learn more about the inner workings of MTK, consider the sections under Basics and System Types.","category":"page"},{"location":"examples/tearing_parallelism/#Exposing-More-Parallelism-By-Tearing-Algebraic-Equations-in-ODESystems","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"","category":"section"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"Sometimes it can be very non-trivial to parallelize a system. In this tutorial, we will demonstrate how to make use of structural_simplify to expose more parallelism in the solution process and parallelize the resulting simulation.","category":"page"},{"location":"examples/tearing_parallelism/#The-Component-Library","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"The Component Library","text":"","category":"section"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"The following tutorial will use the following set of components describing electrical circuits:","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"using ModelingToolkit, OrdinaryDiffEq\n\n# Basic electric components\n@variables t\nconst D = Differential(t)\n@connector function Pin(; name)\n @variables v(t)=1.0 i(t)=1.0 [connect = Flow]\n ODESystem(Equation[], t, [v, i], [], name = name)\nend\n\nfunction Ground(; name)\n @named g = Pin()\n eqs = [g.v ~ 0]\n compose(ODESystem(eqs, t, [], [], name = name), g)\nend\n\nfunction ConstantVoltage(; name, V = 1.0)\n val = V\n @named p = Pin()\n @named n = Pin()\n @parameters V = V\n eqs = [V ~ p.v - n.v\n 0 ~ p.i + n.i]\n compose(ODESystem(eqs, t, [], [V], name = name), p, n)\nend\n\n@connector function HeatPort(; name)\n @variables T(t)=293.15 Q_flow(t)=0.0 [connect = Flow]\n ODESystem(Equation[], t, [T, Q_flow], [], name = name)\nend\n\nfunction HeatingResistor(; name, R = 1.0, TAmbient = 293.15, alpha = 1.0)\n @named p = Pin()\n @named n = Pin()\n @named h = HeatPort()\n @variables v(t) RTherm(t)\n @parameters R=R TAmbient=TAmbient alpha=alpha\n eqs = [RTherm ~ R * (1 + alpha * (h.T - TAmbient))\n v ~ p.i * RTherm\n h.Q_flow ~ -v * p.i # -LossPower\n v ~ p.v - n.v\n 0 ~ p.i + n.i]\n compose(ODESystem(eqs, t, [v, RTherm], [R, TAmbient, alpha],\n name = name), p, n, h)\nend\n\nfunction HeatCapacitor(; name, rho = 8050, V = 1, cp = 460, TAmbient = 293.15)\n @parameters rho=rho V=V cp=cp\n C = rho * V * cp\n @named h = HeatPort()\n eqs = [\n D(h.T) ~ h.Q_flow / C,\n ]\n compose(ODESystem(eqs, t, [], [rho, V, cp],\n name = name), h)\nend\n\nfunction Capacitor(; name, C = 1.0)\n @named p = Pin()\n @named n = Pin()\n @variables v(t) = 0.0\n @parameters C = C\n eqs = [v ~ p.v - n.v\n 0 ~ p.i + n.i\n D(v) ~ p.i / C]\n compose(ODESystem(eqs, t, [v], [C],\n name = name), p, n)\nend\n\nfunction parallel_rc_model(i; name, source, ground, R, C)\n resistor = HeatingResistor(name = Symbol(:resistor, i), R = R)\n capacitor = Capacitor(name = Symbol(:capacitor, i), C = C)\n heat_capacitor = HeatCapacitor(name = Symbol(:heat_capacitor, i))\n\n rc_eqs = [connect(source.p, resistor.p)\n connect(resistor.n, capacitor.p)\n connect(capacitor.n, source.n, ground.g)\n connect(resistor.h, heat_capacitor.h)]\n\n compose(ODESystem(rc_eqs, t, name = Symbol(name, i)),\n [resistor, capacitor, source, ground, heat_capacitor])\nend","category":"page"},{"location":"examples/tearing_parallelism/#The-Model","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"The Model","text":"","category":"section"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"Assuming that the components are defined, our model is 50 resistors and capacitors connected in parallel. Thus following the acausal components tutorial, we can connect a bunch of RC components as follows:","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"V = 2.0\n@named source = ConstantVoltage(V = V)\n@named ground = Ground()\nN = 50\nRs = 10 .^ range(0, stop = -4, length = N)\nCs = 10 .^ range(-3, stop = 0, length = N)\nrc_systems = map(1:N) do i\n parallel_rc_model(i; name = :rc, source = source, ground = ground, R = Rs[i], C = Cs[i])\nend;\n@variables E(t) = 0.0\neqs = [\n D(E) ~ sum(((i, sys),) -> getproperty(sys, Symbol(:resistor, i)).h.Q_flow,\n enumerate(rc_systems)),\n]\n@named _big_rc = ODESystem(eqs, t, [E], [])\n@named big_rc = compose(_big_rc, rc_systems)","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"Now let's say we want to expose a bit more parallelism via running tearing. How do we do that?","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"sys = structural_simplify(big_rc)","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"Done, that's it. There's no more to it.","category":"page"},{"location":"examples/tearing_parallelism/#What-Happened?","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"What Happened?","text":"","category":"section"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"Yes, that's a good question! Let's investigate a little bit more what had happened. If you look at the system we defined:","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"length(equations(big_rc))","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"You see, it started as a massive 1051 set of equations. However, after eliminating redundancies, we arrive at 151 equations:","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"equations(sys)","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"That's not all though. In addition, the tearing process has turned the sets of nonlinear equations into separate blocks and constructed a DAG for the dependencies between the blocks. We can use the bipartite graph functionality to dig in and investigate what this means:","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"using ModelingToolkit.BipartiteGraphs\nts = TearingState(expand_connections(big_rc))\ninc_org = BipartiteGraphs.incidence_matrix(ts.structure.graph)\nblt_org = StructuralTransformations.sorted_incidence_matrix(ts, only_algeqs = true,\n only_algvars = true)\nblt_reduced = StructuralTransformations.sorted_incidence_matrix(ModelingToolkit.get_tearing_state(sys),\n only_algeqs = true,\n only_algvars = true)","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"(Image: )","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"The figure on the left is the original incidence matrix of the algebraic equations. Notice that the original formulation of the model has dependencies between different equations, and so the full set of equations must be solved together. That exposes no parallelism. However, the Block Lower Triangular (BLT) transformation exposes independent blocks. This is then further improved by the tearing process, which removes 90% of the equations and transforms the nonlinear equations into 50 independent blocks, which can now all be solved in parallel. The conclusion is that, your attempts to parallelize are neigh: performing parallelism after structural simplification greatly improves the problem that can be parallelized, so this is better than trying to do it by hand.","category":"page"},{"location":"examples/tearing_parallelism/","page":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","title":"Exposing More Parallelism By Tearing Algebraic Equations in ODESystems","text":"After performing this, you can construct the ODEProblem/ODAEProblem and set parallel_form to use the exposed parallelism in multithreaded function constructions, but this showcases why structural_simplify is so important to that process.","category":"page"},{"location":"tutorials/programmatically_generating/#programmatically","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"","category":"section"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"In the following tutorial we will discuss how to programmatically generate ODESystems. This is for cases where one is writing functions that generating ODESystems, for example if implementing a reader which parses some file format to generate an ODESystem (for example, SBML), or for writing functions that transform an ODESystem (for example, if you write a function that log-transforms a variable in an ODESystem).","category":"page"},{"location":"tutorials/programmatically_generating/#The-Representation-of-a-ModelingToolkit-System","page":"Programmatically Generating and Scripting ODESystems","title":"The Representation of a ModelingToolkit System","text":"","category":"section"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"ModelingToolkit is built on Symbolics.jl, a symbolic Computer Algebra System (CAS) developed in Julia. As such, all CAS functionality is available on ModelingToolkit systems, such as symbolic differentiation, Groebner basis calculations, and whatever else you can think of. Under the hood, all ModelingToolkit variables and expressions are Symbolics.jl variables and expressions. Thus when scripting a ModelingToolkit system, one simply needs to generate Symbolics.jl variables and equations as demonstrated in the Symbolics.jl documentation. This looks like:","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"using Symbolics\n@variables t x(t) y(t) # Define variables\nD = Differential(t) # Define a differential operator\neqs = [D(x) ~ y\n D(y) ~ x] # Define an array of equations","category":"page"},{"location":"tutorials/programmatically_generating/#The-Non-DSL-(non-@mtkmodel)-Way-of-Defining-an-ODESystem","page":"Programmatically Generating and Scripting ODESystems","title":"The Non-DSL (non-@mtkmodel) Way of Defining an ODESystem","text":"","category":"section"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"Using @mtkmodel is the preferred way of defining ODEs with MTK. However, let us look at how we can define the same system without @mtkmodel. This is useful for defining PDESystem etc.","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"using ModelingToolkit\n\n@variables t x(t) # independent and dependent variables\n@parameters τ # parameters\n@constants h = 1 # constants\nD = Differential(t) # define an operator for the differentiation w.r.t. time\neqs = [D(x) ~ (h - x) / τ] # create an array of equations\n\n# your first ODE, consisting of a single equation, indicated by ~\n@named fol_model = ODESystem(eqs, t)\n\n# Perform the standard transformations and mark the model complete\n# Note: Complete models cannot be subsystems of other models!\nfol_model = complete(structural_simplify(fol_model))","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"As you can see, generating an ODESystem is as simple as creating the array of equations and passing it to the ODESystem constructor.","category":"page"},{"location":"tutorials/programmatically_generating/#Understanding-the-Difference-Between-the-Julia-Variable-and-the-Symbolic-Variable","page":"Programmatically Generating and Scripting ODESystems","title":"Understanding the Difference Between the Julia Variable and the Symbolic Variable","text":"","category":"section"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"In the most basic usage of ModelingToolkit and Symbolics, the name of the Julia variable and the symbolic variable are the same. For example, when we do:","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"@variables a","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"the name of the symbolic variable is a and same with the Julia variable. However, we can de-couple these by setting a to a new symbolic variable, for example:","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"b = only(@variables(a))","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"Now the Julia variable b refers to the variable named a. However, the downside of this current approach is that it requires that the user writing the script knows the name a that they want to place to the variable. But what if for example we needed to get the variable's name from a file?","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"To do this, one can interpolate a symbol into the @variables macro using $. For example:","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"a = :c\nb = only(@variables($a))","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"In this example, @variables($a) created a variable named c, and set this variable to b.","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"Variables are not the only thing with names. For example, when you build a system, it knows its name that name is used in the namespacing. In the standard usage, again the Julia variable and the symbolic name are made the same via:","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"@named fol_model = ODESystem(eqs, t)","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"However, one can decouple these two properties by noting that @named is simply shorthand for the following:","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"fol_model = ODESystem(eqs, t; name = :fol_model)","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"Thus if we had read a name from a file and wish to populate an ODESystem with said name, we could do:","category":"page"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"namesym = :name_from_file\nfol_model = ODESystem(eqs, t; name = namesym)","category":"page"},{"location":"tutorials/programmatically_generating/#Warning-About-Mutation","page":"Programmatically Generating and Scripting ODESystems","title":"Warning About Mutation","text":"","category":"section"},{"location":"tutorials/programmatically_generating/","page":"Programmatically Generating and Scripting ODESystems","title":"Programmatically Generating and Scripting ODESystems","text":"Be advsied that it's never a good idea to mutate an ODESystem, or any AbstractSystem.","category":"page"},{"location":"comparison/#Comparison-of-ModelingToolkit-vs-Equation-Based-and-Block-Modeling-Languages","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","text":"","category":"section"},{"location":"comparison/#Comparison-Against-Modelica","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison Against Modelica","text":"","category":"section"},{"location":"comparison/","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","text":"Both Modelica and ModelingToolkit.jl are acausal modeling languages.\nModelica is a language with many different implementations, such as Dymola and OpenModelica, which have differing levels of performance and can give different results on the same model. Many of the commonly used Modelica compilers are not open-source. ModelingToolkit.jl is a language with a single canonical open-source implementation.\nAll current Modelica compiler implementations are fixed and not extendable by the users from the Modelica language itself. For example, the Dymola compiler shares its symbolic processing pipeline, which is roughly equivalent to the dae_index_lowering and structural_simplify of ModelingToolkit.jl. ModelingToolkit.jl is an open and hackable transformation system which allows users to add new non-standard transformations and control the order of application.\nModelica is a declarative programming language. ModelingToolkit.jl is a declarative symbolic modeling language used from within the Julia programming language. Its programming language semantics, such as loop constructs and conditionals, can be used to more easily generate models.\nModelica is an object-oriented single dispatch language. ModelingToolkit.jl, built on Julia, uses multiple dispatch extensively to simplify code.\nMany Modelica compilers supply a GUI. ModelingToolkit.jl does not.\nModelica can be used to simulate ODE and DAE systems. ModelingToolkit.jl has a much more expansive set of system types, including nonlinear systems, SDEs, PDEs, and more.","category":"page"},{"location":"comparison/#Comparison-Against-Simulink","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison Against Simulink","text":"","category":"section"},{"location":"comparison/","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","text":"Simulink is a causal modeling environment, whereas ModelingToolkit.jl is an acausal modeling environment. For an overview of the differences, consult academic reviews such as this one. In this sense, ModelingToolkit.jl is more similar to the Simscape sub-environment.\nSimulink is used from MATLAB while ModelingToolkit.jl is used from Julia. Thus any user-defined functions have the performance of their host language. For information on the performance differences between Julia and MATLAB, consult open benchmarks, which demonstrate Julia as an order of magnitude or more faster in many cases due to its JIT compilation.\nSimulink uses the MATLAB differential equation solvers, while ModelingToolkit.jl uses DifferentialEquations.jl. For a systematic comparison between the solvers, consult open benchmarks, which demonstrate two orders of magnitude performance advantage for the native Julia solvers across many benchmark problems.\nSimulink comes with a Graphical User Interface (GUI), ModelingToolkit.jl does not.\nSimulink is a proprietary software, meaning users cannot actively modify or extend the software. ModelingToolkit.jl is built in Julia and used in Julia, where users can actively extend and modify the software interactively in the REPL and contribute to its open-source repositories.\nSimulink covers ODE and DAE systems. ModelingToolkit.jl has a much more expansive set of system types, including SDEs, PDEs, optimization problems, and more.","category":"page"},{"location":"comparison/#Comparison-Against-CASADI","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison Against CASADI","text":"","category":"section"},{"location":"comparison/","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","text":"CASADI is written in C++ but used from Python/MATLAB, meaning that it cannot be directly extended by users unless they are using the C++ interface and run a local build of CASADI. ModelingToolkit.jl is both written and used from Julia, meaning that users can easily extend the library on the fly, even interactively in the REPL.\nCASADI includes limited support for Computer Algebra System (CAS) functionality, while ModelingToolkit.jl is built on the full Symbolics.jl CAS.\nCASADI supports DAE and ODE problems via SUNDIALS IDAS and CVODES. ModelingToolkit.jl supports DAE and ODE problems via DifferentialEquations.jl, of which Sundials.jl is <1% of the total available solvers and is outperformed by the native Julia solvers on the vast majority of the benchmark equations. In addition, the DifferentialEquations.jl interface is confederated, meaning that any user can dynamically extend the system to add new solvers to the interface by defining new dispatches of solve.\nCASADI's DAEBuilder does not implement efficiency transformations like tearing, which are standard in the ModelingToolkit.jl transformation pipeline.\nCASADI supports special functionality for quadratic programming problems, while ModelingToolkit only provides nonlinear programming via OptimizationSystem.\nModelingToolkit.jl integrates with its host language Julia, so Julia code can be automatically converted into ModelingToolkit expressions. Users of CASADI must explicitly create CASADI expressions.","category":"page"},{"location":"comparison/#Comparison-Against-Modia.jl","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison Against Modia.jl","text":"","category":"section"},{"location":"comparison/","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","text":"Modia.jl uses Julia's expression objects for representing its equations. ModelingToolkit.jl uses Symbolics.jl, and thus the Julia expressions follow Julia semantics and can be manipulated using a computer algebra system (CAS).\nModia's compilation pipeline is similar to the Dymola symbolic processing pipeline with some improvements. ModelingToolkit.jl has an open transformation pipeline that allows for users to extend and reorder transformation passes, where structural_simplify is an adaptation of the Modia.jl-improved alias elimination and tearing algorithms.\nBoth Modia and ModelingToolkit generate DAEProblem and ODEProblem forms for solving with DifferentialEquations.jl.\nModelingToolkit.jl integrates with its host language Julia, so Julia code can be automatically converted into ModelingToolkit expressions. Users of Modia must explicitly create Modia expressions.\nModia covers DAE systems. ModelingToolkit.jl has a much more expansive set of system types, including SDEs, PDEs, optimization problems, and more.","category":"page"},{"location":"comparison/#Comparison-Against-Causal.jl","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison Against Causal.jl","text":"","category":"section"},{"location":"comparison/","page":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","title":"Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages","text":"Causal.jl is a causal modeling environment, whereas ModelingToolkit.jl is an acausal modeling environment. For an overview of the differences, consult academic reviews such as this one.\nBoth ModelingToolkit.jl and Causal.jl use DifferentialEquations.jl as the backend solver library.\nCausal.jl lets one add arbitrary equation systems to a given node, and allow the output to effect the next node. This means an SDE may drive an ODE. These two portions are solved with different solver methods in tandem. In ModelingToolkit.jl, such connections promote the whole system to an SDE. This results in better accuracy and stability, though in some cases it can be less performant.\nCausal.jl, similar to Simulink, breaks algebraic loops via inexact heuristics. ModelingToolkit.jl treats algebraic loops exactly through algebraic equations in the generated model.","category":"page"},{"location":"tutorials/optimization/#Modeling-Optimization-Problems","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"","category":"section"},{"location":"tutorials/optimization/#Rosenbrock-Function-in-2D","page":"Modeling Optimization Problems","title":"Rosenbrock Function in 2D","text":"","category":"section"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"Let's solve the classical Rosenbrock function in two dimensions.","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"First, we need to make some imports.","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"using ModelingToolkit, Optimization, OptimizationOptimJL","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"Now we can define our optimization problem.","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"@variables begin\n x, [bounds = (-2.0, 2.0)]\n y, [bounds = (-1.0, 3.0)]\nend\n@parameters a=1 b=1\nloss = (a - x)^2 + b * (y - x^2)^2\n@named sys = OptimizationSystem(loss, [x, y], [a, b])","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"A visualization of the objective function is depicted below.","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"using Plots\nx = -2:0.01:2\ny = -1:0.01:3\ncontour(x, y, (x, y) -> (1 - x)^2 + 100 * (y - x^2)^2, fill = true, color = :viridis,\n ratio = :equal, xlims = (-2, 2))\nsavefig(\"obj_fun.png\");\nnothing; # hide","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"(Image: plot of the Rosenbrock function)","category":"page"},{"location":"tutorials/optimization/#Explanation","page":"Modeling Optimization Problems","title":"Explanation","text":"","category":"section"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"Every optimization problem consists of a set of optimization variables. In this case, we create two variables. Additionally, we assign box constraints for each of them. In the next step, we create two parameters for the problem with @parameters. While it is not needed to do this, it makes it easier to remake the problem later with different values for the parameters. The objective function is specified as well and finally, everything is used to construct an OptimizationSystem.","category":"page"},{"location":"tutorials/optimization/#Building-and-Solving-the-Optimization-Problem","page":"Modeling Optimization Problems","title":"Building and Solving the Optimization Problem","text":"","category":"section"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"Next, the actual OptimizationProblem can be created. At this stage, an initial guess u0 for the optimization variables needs to be provided via map, using the symbols from before. Concrete values for the parameters of the system can also be provided or changed. However, if the parameters have default values assigned, they are used automatically.","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"u0 = [x => 1.0\n y => 2.0]\np = [a => 1.0\n b => 100.0]\n\nprob = OptimizationProblem(sys, u0, p, grad = true, hess = true)\nsolve(prob, GradientDescent())","category":"page"},{"location":"tutorials/optimization/#Rosenbrock-Function-with-Constraints","page":"Modeling Optimization Problems","title":"Rosenbrock Function with Constraints","text":"","category":"section"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"using ModelingToolkit, Optimization, OptimizationOptimJL\n\n@variables begin\n x, [bounds = (-2.0, 2.0)]\n y, [bounds = (-1.0, 3.0)]\nend\n@parameters a=1 b=100\nloss = (a - x)^2 + b * (y - x^2)^2\ncons = [\n x^2 + y^2 ≲ 1,\n]\n@named sys = OptimizationSystem(loss, [x, y], [a, b], constraints = cons)\nu0 = [x => 0.14\n y => 0.14]\nprob = OptimizationProblem(sys, u0, grad = true, hess = true, cons_j = true, cons_h = true)\nsolve(prob, IPNewton())","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"A visualization of the objective function and the inequality constraint is depicted below.","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"using Plots\nx = -2:0.01:2\ny = -1:0.01:3\ncontour(x, y, (x, y) -> (1 - x)^2 + 100 * (y - x^2)^2, fill = true, color = :viridis,\n ratio = :equal, xlims = (-2, 2))\ncontour!(x, y, (x, y) -> x^2 + y^2, levels = [1], color = :lightblue, line = 4)\nsavefig(\"obj_fun_c.png\");\nnothing; # hide","category":"page"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"(Image: plot of the Rosenbrock function with constraint)","category":"page"},{"location":"tutorials/optimization/#Explanation-2","page":"Modeling Optimization Problems","title":"Explanation","text":"","category":"section"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"Equality and inequality constraints can be added to the OptimizationSystem. An equality constraint can be specified via an Equation, e.g., x^2 + y^2 ~ 1. While inequality constraints via an Inequality, e.g., x^2 + y^2 ≲ 1. The syntax is here \\lesssim and \\gtrsim.","category":"page"},{"location":"tutorials/optimization/#Nested-Systems","page":"Modeling Optimization Problems","title":"Nested Systems","text":"","category":"section"},{"location":"tutorials/optimization/","page":"Modeling Optimization Problems","title":"Modeling Optimization Problems","text":"Needs more text, but it's super cool and auto-parallelizes and sparsifies too. Plus, you can hierarchically nest systems to have it generate huge optimization problems.","category":"page"},{"location":"basics/FAQ/#Frequently-Asked-Questions","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"","category":"section"},{"location":"basics/FAQ/#Getting-the-index-for-a-symbol","page":"Frequently Asked Questions","title":"Getting the index for a symbol","text":"","category":"section"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Since ordering of symbols is not guaranteed after symbolic transformations, one should normally refer to values by their name. For example, sol[lorenz.x] from the solution. But what if you need to get the index? The following helper function will do the trick:","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"indexof(sym, syms) = findfirst(isequal(sym), syms)\nindexof(σ, parameters(sys))","category":"page"},{"location":"basics/FAQ/#Transforming-value-maps-to-arrays","page":"Frequently Asked Questions","title":"Transforming value maps to arrays","text":"","category":"section"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"ModelingToolkit.jl allows (and recommends) input maps like [x => 2.0, y => 3.0] because symbol ordering is not guaranteed. However, what if you want to get the lowered array? You can use the internal function varmap_to_vars. For example:","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"pnew = varmap_to_vars([β => 3.0, c => 10.0, γ => 2.0], parameters(sys))","category":"page"},{"location":"basics/FAQ/#How-do-I-handle-if-statements-in-my-symbolic-forms?","page":"Frequently Asked Questions","title":"How do I handle if statements in my symbolic forms?","text":"","category":"section"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"For statements that are in the if then else form, use IfElse.ifelse from the IfElse.jl package to represent the code in a functional form. For handling direct if statements, you can use equivalent boolean mathematical expressions. For example, if x > 0 ... can be implemented as just (x > 0) *, where if x <= 0 then the boolean will evaluate to 0 and thus the term will be excluded from the model.","category":"page"},{"location":"basics/FAQ/#ERROR:-TypeError:-non-boolean-(Num)-used-in-boolean-context?","page":"Frequently Asked Questions","title":"ERROR: TypeError: non-boolean (Num) used in boolean context?","text":"","category":"section"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you see the error:","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"ERROR: TypeError: non-boolean (Num) used in boolean context","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"then it's likely you are trying to trace through a function which cannot be directly represented in Julia symbols. The techniques to handle this problem, such as @register_symbolic, are described in detail in the Symbolics.jl documentation.","category":"page"},{"location":"basics/FAQ/#Using-ModelingToolkit-with-Optimization-/-Automatic-Differentiation","page":"Frequently Asked Questions","title":"Using ModelingToolkit with Optimization / Automatic Differentiation","text":"","category":"section"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you are using ModelingToolkit inside a loss function and are having issues with mixing MTK with automatic differentiation, getting performance, etc… don't! Instead, use MTK outside the loss function to generate the code, and then use the generated code inside the loss function.","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"For example, let's say you were building ODEProblems in the loss function like:","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"function loss(p)\n prob = ODEProblem(sys, [], [p1 => p[1], p2 => p[2]])\n sol = solve(prob, Tsit5())\n sum(abs2, sol)\nend","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Since ODEProblem on a MTK sys will have to generate code, this will be slower than caching the generated code, and will require automatic differentiation to go through the code generation process itself. All of this is unnecessary. Instead, generate the problem once outside the loss function, and remake the prob inside the loss function:","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"prob = ODEProblem(sys, [], [p1 => p[1], p2 => p[2]])\nfunction loss(p)\n remake(prob, p = ...)\n sol = solve(prob, Tsit5())\n sum(abs2, sol)\nend","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Now, one has to be careful with remake to ensure that the parameters are in the right order. One can use the previously mentioned indexing functionality to generate index maps for reordering p like:","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"p = @parameters x y z\nidxs = ModelingToolkit.varmap_to_vars([p[1] => 1, p[2] => 2, p[3] => 3], p)\np[idxs]","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Using this, the fixed index map can be used in the loss function. This would look like:","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"prob = ODEProblem(sys, [], [p1 => p[1], p2 => p[2]])\nidxs = Int.(ModelingToolkit.varmap_to_vars([p1 => 1, p2 => 2], p))\nfunction loss(p)\n remake(prob, p = p[idxs])\n sol = solve(prob, Tsit5())\n sum(abs2, sol)\nend","category":"page"},{"location":"basics/FAQ/#ERROR:-ArgumentError:-SymbolicUtils.BasicSymbolic{Real}[xˍt(t)]-are-missing-from-the-variable-map.","page":"Frequently Asked Questions","title":"ERROR: ArgumentError: SymbolicUtils.BasicSymbolic{Real}[xˍt(t)] are missing from the variable map.","text":"","category":"section"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"This error can come up after running structural_simplify on a system that generates dummy derivatives (i.e. variables with ˍt). For example, here even though all the variables are defined with initial values, the ODEProblem generation will throw an error that defaults are missing from the variable map.","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"@variables t\nsts = @variables x1(t)=0.0 x2(t)=0.0 x3(t)=0.0 x4(t)=0.0\nD = Differential(t)\neqs = [x1 + x2 + 1 ~ 0\n x1 + x2 + x3 + 2 ~ 0\n x1 + D(x3) + x4 + 3 ~ 0\n 2 * D(D(x1)) + D(D(x2)) + D(D(x3)) + D(x4) + 4 ~ 0]\n@named sys = ODESystem(eqs, t)\nsys = structural_simplify(sys)\nprob = ODEProblem(sys, [], (0,1))","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"We can solve this problem by using the missing_variable_defaults() function","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"prob = ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0,1))","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"This function provides 0 for the default values, which is a safe assumption for dummy derivatives of most models. However, the 2nd argument allows for a different default value or values to be used if needed.","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"julia> ModelingToolkit.missing_variable_defaults(sys, [1,2,3])\n3-element Vector{Pair}:\n x1ˍt(t) => 1\n x2ˍtt(t) => 2\n x3ˍtt(t) => 3","category":"page"},{"location":"basics/FAQ/#Change-the-state-vector-type","page":"Frequently Asked Questions","title":"Change the state vector type","text":"","category":"section"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Use the u0_constructor keyword argument to map an array to the desired container type. For example:","category":"page"},{"location":"basics/FAQ/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"using ModelingToolkit, StaticArrays\n@variables t\nsts = @variables x1(t)=0.0\nD = Differential(t)\neqs = [D(x1) ~ 1.1 * x1]\n@named sys = ODESystem(eqs, t)\nsys = structural_simplify(sys)\nprob = ODEProblem{false}(sys, [], (0,1); u0_constructor = x->SVector(x...))","category":"page"},{"location":"tutorials/acausal_components/#acausal","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"","category":"section"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"In this tutorial, we will build a hierarchical acausal component-based model of the RC circuit. The RC circuit is a simple example where we connect a resistor and a capacitor. Kirchhoff's laws are then applied to state equalities between currents and voltages. This specifies a differential-algebraic equation (DAE) system, where the algebraic equations are given by the constraints and equalities between different component variables. We then simplify this to an ODE by eliminating the equalities before solving. Let's see this in action.","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"note: Note\nThis tutorial teaches how to build the entire RC circuit from scratch. However, to simulate electric components with more ease, check out the ModelingToolkitStandardLibrary.jl which includes a tutorial for simulating RC circuits with pre-built components","category":"page"},{"location":"tutorials/acausal_components/#Copy-Paste-Example","page":"Acausal Component-Based Modeling","title":"Copy-Paste Example","text":"","category":"section"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"using ModelingToolkit, Plots, DifferentialEquations\n\n@variables t\n@connector Pin begin\n v(t)\n i(t), [connect = Flow]\nend\n\n@mtkmodel Ground begin\n @components begin\n g = Pin()\n end\n @equations begin\n g.v ~ 0\n end\nend\n\n@mtkmodel OnePort begin\n @components begin\n p = Pin()\n n = Pin()\n end\n @variables begin\n v(t)\n i(t)\n end\n @equations begin\n v ~ p.v - n.v\n 0 ~ p.i + n.i\n i ~ p.i\n end\nend\n\n@mtkmodel Resistor begin\n @extend OnePort()\n @parameters begin\n R = 1.0 # Sets the default resistance\n end\n @equations begin\n v ~ i * R\n end\nend\n\nD = Differential(t)\n\n@mtkmodel Capacitor begin\n @extend OnePort()\n @parameters begin\n C = 1.0\n end\n @equations begin\n D(v) ~ i / C\n end\nend\n\n@mtkmodel ConstantVoltage begin\n @extend OnePort()\n @parameters begin\n V = 1.0\n end\n @equations begin\n V ~ v\n end\nend\n\n@mtkmodel RCModel begin\n @components begin\n resistor = Resistor(R = 1.0)\n capacitor = Capacitor(C = 1.0)\n source = ConstantVoltage(V = 1.0)\n ground = Ground()\n end\n @equations begin\n connect(source.p, resistor.p)\n connect(resistor.n, capacitor.p)\n connect(capacitor.n, source.n)\n connect(capacitor.n, ground.g)\n end\nend\n\n@mtkbuild rc_model = RCModel(resistor.R = 2.0)\nu0 = [\n rc_model.capacitor.v => 0.0,\n]\nprob = ODEProblem(rc_model, u0, (0, 10.0))\nsol = solve(prob)\nplot(sol)","category":"page"},{"location":"tutorials/acausal_components/#Explanation","page":"Acausal Component-Based Modeling","title":"Explanation","text":"","category":"section"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"We wish to build the following RC circuit by building individual components and connecting the pins:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"(Image: )","category":"page"},{"location":"tutorials/acausal_components/#Building-the-Component-Library","page":"Acausal Component-Based Modeling","title":"Building the Component Library","text":"","category":"section"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"For each of our components, we use ModelingToolkit Model that emits an ODESystem. At the top, we start with defining the fundamental qualities of an electric circuit component. At every input and output pin, a circuit component has two values: the current at the pin and the voltage. Thus we define the Pin component (connector) to simply be the values there. Whenever two Pins in a circuit are connected together, the system satisfies Kirchhoff's laws, i.e. that currents sum to zero and voltages across the pins are equal. [connect = Flow] informs MTK that currents ought to sum to zero, and by default, variables are equal in a connection.","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"@connector Pin begin\n v(t)\n i(t), [connect = Flow]\nend","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"Note that this is an incompletely specified ODESystem: it cannot be simulated on its own because the equations for v(t) and i(t) are unknown. Instead, this just gives a common syntax for receiving this pair with some default values. One can then construct a Pin using the @named helper macro:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"@named mypin1 = Pin()","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"Next, we build our ground node. A ground node is just a pin that is connected to a constant voltage reservoir, typically taken to be V = 0. Thus to define this component, we generate an ODESystem with a Pin subcomponent and specify that the voltage in such a Pin is equal to zero. This gives:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"@mtkmodel Ground begin\n @components begin\n g = Pin()\n end\n @equations begin\n g.v ~ 0\n end\nend","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"Next we build a OnePort: an abstraction for all simple electric component with two pins. The voltage difference between the positive pin and the negative pin is the voltage of the component, the current between two pins must sum to zero, and the current of the component equals to the current of the positive pin.","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"@mtkmodel OnePort begin\n @components begin\n p = Pin()\n n = Pin()\n end\n @variables begin\n v(t)\n i(t)\n end\n @equations begin\n v ~ p.v - n.v\n 0 ~ p.i + n.i\n i ~ p.i\n end\nend","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"Next we build a resistor. A resistor is an object that has two Pins, the positive and the negative pins, and follows Ohm's law: v = i*r. The voltage of the resistor is given as the voltage difference across the two pins, while by conservation of charge we know that the current in must equal the current out, which means (no matter the direction of the current flow) the sum of the currents must be zero. This leads to our resistor equations:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"@mtkmodel Resistor begin\n @extend OnePort()\n @parameters begin\n R = 1.0\n end\n @equations begin\n v ~ i * R\n end\nend","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"Notice that we have created this system with a default parameter R for the resistor's resistance. By doing so, if the resistance of this resistor is not overridden by a higher level default or overridden at ODEProblem construction time, this will be the value of the resistance. Also, note the use of @extend. For the Resistor, we want to simply inherit OnePort's equations and states and extend them with a new equation. Note that v, i are not namespaced as oneport.v or oneport.i.","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"Using our knowledge of circuits, we similarly construct the Capacitor:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"D = Differential(t)\n\n@mtkmodel Capacitor begin\n @extend OnePort()\n @parameters begin\n C = 1.0\n end\n @equations begin\n D(v) ~ i / C\n end\nend","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"Now we want to build a constant voltage electric source term. We can think of this as similarly being a two pin object, where the object itself is kept at a constant voltage, essentially generating the electric current. We would then model this as:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"@mtkmodel ConstantVoltage begin\n @extend OnePort()\n @parameters begin\n V = 1.0\n end\n @equations begin\n V ~ v\n end\nend","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"Note that as we are extending only v from OnePort, it is explicitly specified as a tuple.","category":"page"},{"location":"tutorials/acausal_components/#Connecting-and-Simulating-Our-Electric-Circuit","page":"Acausal Component-Based Modeling","title":"Connecting and Simulating Our Electric Circuit","text":"","category":"section"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"Now we are ready to simulate our circuit. Let's build our four components: a resistor, capacitor, source, and ground term. For simplicity, we will make all of our parameter values 1.0. As resistor, capacitor, source lists R, C, V in their argument list, they are promoted as arguments of RCModel as resistor.R, capacitor.C, source.V","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"@mtkmodel RCModel begin\n @components begin\n resistor = Resistor(R = 1.0)\n capacitor = Capacitor(C = 1.0)\n source = ConstantVoltage(V = 1.0)\n ground = Ground()\n end\n @equations begin\n connect(source.p, resistor.p)\n connect(resistor.n, capacitor.p)\n connect(capacitor.n, source.n)\n connect(capacitor.n, ground.g)\n end\nend","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"We can create a RCModel component with @named. And using subcomponent_name.parameter we can set the parameters or defaults values of variables of subcomponents.","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"@mtkbuild rc_model = RCModel(resistor.R = 2.0)","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"This model is acausal because we have not specified anything about the causality of the model. We have simply specified what is true about each of the variables. This forms a system of differential-algebraic equations (DAEs) which define the evolution of each state of the system. The equations are:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"equations(expand_connections(rc_model))","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"the states are:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"states(rc_model)","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"and the parameters are:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"parameters(rc_model)","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"The observed equations are:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"observed(rc_model)","category":"page"},{"location":"tutorials/acausal_components/#Solving-this-System","page":"Acausal Component-Based Modeling","title":"Solving this System","text":"","category":"section"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"We are left with a system of only two equations with two state variables. One of the equations is a differential equation, while the other is an algebraic equation. We can then give the values for the initial conditions of our states, and solve the system by converting it to an ODEProblem in mass matrix form and solving it with an ODEProblem mass matrix DAE solver. This is done as follows:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"u0 = [rc_model.capacitor.v => 0.0\n rc_model.capacitor.p.i => 0.0]\n\nprob = ODEProblem(rc_model, u0, (0, 10.0))\nsol = solve(prob)\nplot(sol)","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"However, what if we wanted to plot the timeseries of a different variable? Do not worry, that information was not thrown away! Instead, transformations like structural_simplify simply change state variables into observables which are defined by observed equations.","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"observed(rc_model)","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"These are explicit algebraic equations which can then be used to reconstruct the required variables on the fly. This leads to dramatic computational savings because implicitly solving an ODE scales like O(n^3), so making there be as few states as possible is good!","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"The solution object can be accessed via its symbols. For example, let's retrieve the voltage of the resistor over time:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"sol[rc_model.resistor.v]","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"or we can plot the timeseries of the resistor's voltage:","category":"page"},{"location":"tutorials/acausal_components/","page":"Acausal Component-Based Modeling","title":"Acausal Component-Based Modeling","text":"plot(sol, idxs = [rc_model.resistor.v])","category":"page"},{"location":"examples/sparse_jacobians/#Automated-Sparse-Analytical-Jacobians","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"","category":"section"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"In many cases where you have large stiff differential equations, getting a sparse Jacobian can be essential for performance. In this tutorial, we will show how to use modelingtoolkitize to regenerate an ODEProblem code with the analytical solution to the sparse Jacobian, along with the sparsity pattern required by DifferentialEquations.jl's solvers to specialize the solving process.","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"First, let's start out with an implementation of the 2-dimensional Brusselator partial differential equation discretized using finite differences:","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"using DifferentialEquations, ModelingToolkit\n\nconst N = 32\nconst xyd_brusselator = range(0, stop = 1, length = N)\nbrusselator_f(x, y, t) = (((x - 0.3)^2 + (y - 0.6)^2) <= 0.1^2) * (t >= 1.1) * 5.0\nlimit(a, N) = a == N + 1 ? 1 : a == 0 ? N : a\nfunction brusselator_2d_loop(du, u, p, t)\n A, B, alpha, dx = p\n alpha = alpha / dx^2\n @inbounds for I in CartesianIndices((N, N))\n i, j = Tuple(I)\n x, y = xyd_brusselator[I[1]], xyd_brusselator[I[2]]\n ip1, im1, jp1, jm1 = limit(i + 1, N), limit(i - 1, N), limit(j + 1, N),\n limit(j - 1, N)\n du[i, j, 1] = alpha * (u[im1, j, 1] + u[ip1, j, 1] + u[i, jp1, 1] + u[i, jm1, 1] -\n 4u[i, j, 1]) +\n B + u[i, j, 1]^2 * u[i, j, 2] - (A + 1) * u[i, j, 1] +\n brusselator_f(x, y, t)\n du[i, j, 2] = alpha * (u[im1, j, 2] + u[ip1, j, 2] + u[i, jp1, 2] + u[i, jm1, 2] -\n 4u[i, j, 2]) +\n A * u[i, j, 1] - u[i, j, 1]^2 * u[i, j, 2]\n end\nend\np = (3.4, 1.0, 10.0, step(xyd_brusselator))\n\nfunction init_brusselator_2d(xyd)\n N = length(xyd)\n u = zeros(N, N, 2)\n for I in CartesianIndices((N, N))\n x = xyd[I[1]]\n y = xyd[I[2]]\n u[I, 1] = 22 * (y * (1 - y))^(3 / 2)\n u[I, 2] = 27 * (x * (1 - x))^(3 / 2)\n end\n u\nend\nu0 = init_brusselator_2d(xyd_brusselator)\nprob = ODEProblem(brusselator_2d_loop, u0, (0.0, 11.5), p)","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"Now let's use modelingtoolkitize to generate the symbolic version:","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"sys = modelingtoolkitize(prob);\nnothing # hide","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"Now we regenerate the problem using jac=true for the analytical Jacobian and sparse=true to make it sparse:","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"sparseprob = ODEProblem(sys, Pair[], (0.0, 11.5), jac = true, sparse = true)","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"Hard? No! How much did that help?","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"using BenchmarkTools\n@btime solve(prob, save_everystep = false);\nreturn nothing # hide","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"@btime solve(sparseprob, save_everystep = false);\nreturn nothing # hide","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"Notice though that the analytical solution to the Jacobian can be quite expensive. Thus in some cases we may only want to get the sparsity pattern. In this case, we can simply do:","category":"page"},{"location":"examples/sparse_jacobians/","page":"Automated Sparse Analytical Jacobians","title":"Automated Sparse Analytical Jacobians","text":"sparsepatternprob = ODEProblem(sys, Pair[], (0.0, 11.5), sparse = true)\n@btime solve(sparsepatternprob, save_everystep = false);\nreturn nothing # hide","category":"page"},{"location":"basics/Composition/#components","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"","category":"section"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"The symbolic models of ModelingToolkit can be composed together to easily build large models. The composition is lazy and only instantiated at the time of conversion to numerical models, allowing a more performant way in terms of computation time and memory.","category":"page"},{"location":"basics/Composition/#Simple-Model-Composition-Example","page":"Composing Models and Building Reusable Components","title":"Simple Model Composition Example","text":"","category":"section"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"The following is an example of building a model in a library with an optional forcing function, and allowing the user to specify the forcing later. Here, the library author defines a component named decay. The user then builds two decay components and connects them, saying the forcing term of decay1 is a constant while the forcing term of decay2 is the value of the state variable x.","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"using ModelingToolkit\n\nfunction decay(; name)\n @parameters t a\n @variables x(t) f(t)\n D = Differential(t)\n ODESystem([\n D(x) ~ -a * x + f,\n ];\n name = name)\nend\n\n@named decay1 = decay()\n@named decay2 = decay()\n\n@parameters t\nD = Differential(t)\nconnected = compose(ODESystem([decay2.f ~ decay1.x\n D(decay1.f) ~ 0], t; name = :connected), decay1, decay2)\n\nequations(connected)\n\n#4-element Vector{Equation}:\n# Differential(t)(decay1₊f(t)) ~ 0\n# decay2₊f(t) ~ decay1₊x(t)\n# Differential(t)(decay1₊x(t)) ~ decay1₊f(t) - (decay1₊a*(decay1₊x(t)))\n# Differential(t)(decay2₊x(t)) ~ decay2₊f(t) - (decay2₊a*(decay2₊x(t)))\n\nsimplified_sys = structural_simplify(connected)\n\nequations(simplified_sys)","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"Now we can solve the system:","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"x0 = [decay1.x => 1.0\n decay1.f => 0.0\n decay2.x => 1.0]\np = [decay1.a => 0.1\n decay2.a => 0.2]\n\nusing DifferentialEquations\nprob = ODEProblem(simplified_sys, x0, (0.0, 100.0), p)\nsol = solve(prob, Tsit5())\nsol[decay2.f]","category":"page"},{"location":"basics/Composition/#Basics-of-Model-Composition","page":"Composing Models and Building Reusable Components","title":"Basics of Model Composition","text":"","category":"section"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"Every AbstractSystem has a system keyword argument for specifying subsystems. A model is the composition of itself and its subsystems. For example, if we have:","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"@named sys = compose(ODESystem(eqs, indepvar, states, ps), subsys)","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"the equations of sys is the concatenation of get_eqs(sys) and equations(subsys), the states are the concatenation of their states, etc. When the ODEProblem or ODEFunction is generated from this system, it will build and compile the functions associated with this composition.","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"The new equations within the higher level system can access the variables in the lower level system by namespacing via the nameof(subsys). For example, let's say there is a variable x in states and a variable x in subsys. We can declare that these two variables are the same by specifying their equality: x ~ subsys.x in the eqs for sys. This algebraic relationship can then be simplified by transformations like structural_simplify which will be described later.","category":"page"},{"location":"basics/Composition/#Numerics-with-Composed-Models","page":"Composing Models and Building Reusable Components","title":"Numerics with Composed Models","text":"","category":"section"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"These composed models can then be directly transformed into their associated SciMLProblem type using the standard constructors. When this is done, the initial conditions and parameters must be specified in their namespaced form. For example:","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"u0 = [x => 2.0\n subsys.x => 2.0]","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"Note that any default values within the given subcomponent will be used if no override is provided at construction time. If any values for initial conditions or parameters are unspecified, an error will be thrown.","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"When the model is numerically solved, the solution can be accessed via its symbolic values. For example, if sol is the ODESolution, one can use sol[x] and sol[subsys.x] to access the respective timeseries in the solution. All other indexing rules stay the same, so sol[x,1:5] accesses the first through fifth values of x. Note that this can be done even if the variable x is eliminated from the system from transformations like alias_elimination or tearing: the variable will be lazily reconstructed on demand.","category":"page"},{"location":"basics/Composition/#Variable-scope-and-parameter-expressions","page":"Composing Models and Building Reusable Components","title":"Variable scope and parameter expressions","text":"","category":"section"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"In some scenarios, it could be useful for model parameters to be expressed in terms of other parameters, or shared between common subsystems. To facilitate this, ModelingToolkit supports symbolic expressions in default values, and scoped variables.","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"With symbolic parameters, it is possible to set the default value of a parameter or initial condition to an expression of other variables.","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"# ...\nsys = ODESystem(\n# ...\n# directly in the defaults argument\n defaults = Pair{Num, Any}[x => u,\n y => σ,\n z => u - 0.1])\n# by assigning to the parameter\nsys.y = u * 1.1","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"In a hierarchical system, variables of the subsystem get namespaced by the name of the system they are in. This prevents naming clashes, but also enforces that every state and parameter is local to the subsystem it is used in. In some cases it might be desirable to have variables and parameters that are shared between subsystems, or even global. This can be accomplished as follows.","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"@parameters t a b c d e f\n\n# a is a local variable\nb = ParentScope(b) # b is a variable that belongs to one level up in the hierarchy\nc = ParentScope(ParentScope(c)) # ParentScope can be nested\nd = DelayParentScope(d) # skips one level before applying ParentScope\ne = DelayParentScope(e, 2) # second argument allows skipping N levels\nf = GlobalScope(f)\n\np = [a, b, c, d, e, f]\n\nlevel0 = ODESystem(Equation[], t, [], p; name = :level0)\nlevel1 = ODESystem(Equation[], t, [], []; name = :level1) ∘ level0\nparameters(level1)\n#level0₊a\n#b\n#c\n#level0₊d\n#level0₊e\n#f\nlevel2 = ODESystem(Equation[], t, [], []; name = :level2) ∘ level1\nparameters(level2)\n#level1₊level0₊a\n#level1₊b\n#c\n#level0₊d\n#level1₊level0₊e\n#f\nlevel3 = ODESystem(Equation[], t, [], []; name = :level3) ∘ level2\nparameters(level3)\n#level2₊level1₊level0₊a\n#level2₊level1₊b\n#level2₊c\n#level2₊level0₊d\n#level1₊level0₊e\n#f","category":"page"},{"location":"basics/Composition/#Structural-Simplify","page":"Composing Models and Building Reusable Components","title":"Structural Simplify","text":"","category":"section"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"In many cases, the nicest way to build a model may leave a lot of unnecessary variables. Thus one may want to remove these equations before numerically solving. The structural_simplify function removes these trivial equality relationships and trivial singularity equations, i.e. equations which result in 0~0 expressions, in over-specified systems.","category":"page"},{"location":"basics/Composition/#Inheritance-and-Combine","page":"Composing Models and Building Reusable Components","title":"Inheritance and Combine","text":"","category":"section"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"Model inheritance can be done in two ways: implicitly or explicitly. First, one can use the extend function to extend a base model with another set of equations, states, and parameters. An example can be found in the acausal components tutorial.","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"The explicit way is to shadow variables with equality expressions. For example, let's assume we have three separate systems which we want to compose to a single one. This is how one could explicitly forward all states and parameters to the higher level system:","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"using ModelingToolkit, OrdinaryDiffEq, Plots\n\n## Library code\n\n@parameters t\nD = Differential(t)\n\n@variables S(t), I(t), R(t)\nN = S + I + R\n@parameters β, γ\n\n@named seqn = ODESystem([D(S) ~ -β * S * I / N])\n@named ieqn = ODESystem([D(I) ~ β * S * I / N - γ * I])\n@named reqn = ODESystem([D(R) ~ γ * I])\n\nsir = compose(ODESystem([\n S ~ ieqn.S,\n I ~ seqn.I,\n R ~ ieqn.R,\n ieqn.S ~ seqn.S,\n seqn.I ~ ieqn.I,\n seqn.R ~ reqn.R,\n ieqn.R ~ reqn.R,\n reqn.I ~ ieqn.I], t, [S, I, R], [β, γ],\n defaults = [seqn.β => β\n ieqn.β => β\n ieqn.γ => γ\n reqn.γ => γ], name = :sir), seqn, ieqn, reqn)","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"Note that the states are forwarded by an equality relationship, while the parameters are forwarded through a relationship in their default values. The user of this model can then solve this model simply by specifying the values at the highest level:","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"sireqn_simple = structural_simplify(sir)\n\nequations(sireqn_simple)","category":"page"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"## User Code\n\nu0 = [seqn.S => 990.0,\n ieqn.I => 10.0,\n reqn.R => 0.0]\n\np = [β => 0.5\n γ => 0.25]\n\ntspan = (0.0, 40.0)\nprob = ODEProblem(sireqn_simple, u0, tspan, p, jac = true)\nsol = solve(prob, Tsit5())\nsol[reqn.R]","category":"page"},{"location":"basics/Composition/#Tearing-Problem-Construction","page":"Composing Models and Building Reusable Components","title":"Tearing Problem Construction","text":"","category":"section"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"Some system types, specifically ODESystem and NonlinearSystem, can be further reduced if structural_simplify has already been applied to them. This is done by using the alternative problem constructors, ODAEProblem and BlockNonlinearProblem respectively. In these cases, the constructor uses the knowledge of the strongly connected components calculated during the process of simplification as the basis for building pre-simplified nonlinear systems in the implicit solving. In summary: these problems are structurally modified, but could be more efficient and more stable.","category":"page"},{"location":"basics/Composition/#Components-with-discontinuous-dynamics","page":"Composing Models and Building Reusable Components","title":"Components with discontinuous dynamics","text":"","category":"section"},{"location":"basics/Composition/","page":"Composing Models and Building Reusable Components","title":"Composing Models and Building Reusable Components","text":"When modeling, e.g., impacts, saturations or Coulomb friction, the dynamic equations are discontinuous in either the state or one of its derivatives. This causes the solver to take very small steps around the discontinuity, and sometimes leads to early stopping due to dt <= dt_min. The correct way to handle such dynamics is to tell the solver about the discontinuity by a root-finding equation, which can be modeling using ODESystem's event support. Please see the tutorial on Callbacks and Events for details and examples.","category":"page"},{"location":"tutorials/stochastic_diffeq/#Modeling-with-Stochasticity","page":"Modeling with Stochasticity","title":"Modeling with Stochasticity","text":"","category":"section"},{"location":"tutorials/stochastic_diffeq/","page":"Modeling with Stochasticity","title":"Modeling with Stochasticity","text":"All models with ODESystem are deterministic. SDESystem adds another element to the model: randomness. This is a stochastic differential equation which has a deterministic (drift) component and a stochastic (diffusion) component. Let's take the Lorenz equation from the first tutorial and extend it to have multiplicative noise.","category":"page"},{"location":"tutorials/stochastic_diffeq/","page":"Modeling with Stochasticity","title":"Modeling with Stochasticity","text":"using ModelingToolkit, StochasticDiffEq\n\n# Define some variables\n@parameters σ ρ β\n@variables t x(t) y(t) z(t)\nD = Differential(t)\n\neqs = [D(x) ~ σ * (y - x),\n D(y) ~ x * (ρ - z) - y,\n D(z) ~ x * y - β * z]\n\nnoiseeqs = [0.1 * x,\n 0.1 * y,\n 0.1 * z]\n\n@named de = SDESystem(eqs, noiseeqs, t, [x, y, z], [σ, ρ, β])\n\nu0map = [\n x => 1.0,\n y => 0.0,\n z => 0.0,\n]\n\nparammap = [\n σ => 10.0,\n β => 26.0,\n ρ => 2.33,\n]\n\nprob = SDEProblem(de, u0map, (0.0, 100.0), parammap)\nsol = solve(prob, SOSRI())","category":"page"},{"location":"tutorials/parameter_identifiability/#Parameter-Identifiability-in-ODE-Models","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"","category":"section"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"Ordinary differential equations are commonly used for modeling real-world processes. The problem of parameter identifiability is one of the key design challenges for mathematical models. A parameter is said to be identifiable if one can recover its value from experimental data. Structural identifiability is a theoretical property of a model that answers this question. In this tutorial, we will show how to use StructuralIdentifiability.jl with ModelingToolkit.jl to assess identifiability of parameters in ODE models. The theory behind StructuralIdentifiability.jl is presented in paper [4].","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"We will start by illustrating local identifiability in which a parameter is known up to finitely many values, and then proceed to determining global identifiability, that is, which parameters can be identified uniquely.","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"The package has a standalone data structure for ordinary differential equations, but is also compatible with ODESystem type from ModelingToolkit.jl.","category":"page"},{"location":"tutorials/parameter_identifiability/#Local-Identifiability","page":"Parameter Identifiability in ODE Models","title":"Local Identifiability","text":"","category":"section"},{"location":"tutorials/parameter_identifiability/#Input-System","page":"Parameter Identifiability in ODE Models","title":"Input System","text":"","category":"section"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"We will consider the following model:","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"begincases\nfracdx_4dt = - frack_5 x_4k_6 + x_4\nfracdx_5dt = frack_5 x_4k_6 + x_4 - frack_7 x_5(k_8 + x_5 + x_6)\nfracdx_6dt = frack_7 x_5(k_8 + x_5 + x_6) - frack_9 x_6 (k_10 - x_6) k_10\nfracdx_7dt = frack_9 x_6 (k_10 - x_6) k_10\ny_1 = x_4\ny_2 = x_5endcases","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"This model describes the biohydrogenation[1] process[2] with unknown initial conditions.","category":"page"},{"location":"tutorials/parameter_identifiability/#Using-the-ODESystem-object","page":"Parameter Identifiability in ODE Models","title":"Using the ODESystem object","text":"","category":"section"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"To define the ode system in Julia, we use ModelingToolkit.jl.","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"We first define the parameters, variables, differential equations and the output equations.","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"using StructuralIdentifiability, ModelingToolkit\n\n@variables t\nD = Differential(t)\n\n@mtkmodel Biohydrogenation begin\n @variables begin\n x4(t)\n x5(t)\n x6(t)\n x7(t)\n y1(t), [output = true]\n y2(t), [output = true]\n end\n @parameters begin\n k5\n k6\n k7\n k8\n k9\n k10\n end\n # define equations\n @equations begin\n D(x4) ~ -k5 * x4 / (k6 + x4)\n D(x5) ~ k5 * x4 / (k6 + x4) - k7 * x5 / (k8 + x5 + x6)\n D(x6) ~ k7 * x5 / (k8 + x5 + x6) - k9 * x6 * (k10 - x6) / k10\n D(x7) ~ k9 * x6 * (k10 - x6) / k10\n y1 ~ x4\n y2 ~ x5\n end\nend\n\n# define the system\n@named de = Biohydrogenation()\nde = complete(de)","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"After that, we are ready to check the system for local identifiability:","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"# query local identifiability\n# we pass the ode-system\nlocal_id_all = assess_local_identifiability(de, p = 0.99)","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"We can see that all states (except x_7) and all parameters are locally identifiable with probability 0.99.","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"Let's try to check specific parameters and their combinations","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"to_check = [de.k5, de.k7, de.k10 / de.k9, de.k5 + de.k6]\nlocal_id_some = assess_local_identifiability(de, funcs_to_check = to_check, p = 0.99)","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"Notice that in this case, everything (except the state variable x_7) is locally identifiable, including combinations such as k_10k_9 k_5+k_6","category":"page"},{"location":"tutorials/parameter_identifiability/#Global-Identifiability","page":"Parameter Identifiability in ODE Models","title":"Global Identifiability","text":"","category":"section"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"In this part tutorial, let us cover an example problem of querying the ODE for globally identifiable parameters.","category":"page"},{"location":"tutorials/parameter_identifiability/#Input-System-2","page":"Parameter Identifiability in ODE Models","title":"Input System","text":"","category":"section"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"Let us consider the following four-dimensional model with two outputs:","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"begincases\nx_1(t) = -b x_1(t) + frac1 c + x_4(t)\nx_2(t) = alpha x_1(t) - beta x_2(t)\nx_3(t) = gamma x_2(t) - delta x_3(t)\nx_4(t) = sigma x_4(t) frac(gamma x_2(t) - delta x_3(t)) x_3(t)\ny(t) = x_1(t)\nendcases","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"We will run a global identifiability check on this enzyme dynamics[3] model. We will use the default settings: the probability of correctness will be p=0.99 and we are interested in identifiability of all possible parameters.","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"Global identifiability needs information about local identifiability first, but the function we chose here will take care of that extra step for us.","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"using StructuralIdentifiability, ModelingToolkit\n\n@variables t\nD = Differential(t)\n\n@mtkmodel GoodwinOsc begin\n @parameters begin\n b\n c\n α\n β\n γ\n δ\n σ\n end\n @variables begin\n x1(t)\n x2(t)\n x3(t)\n x4(t)\n y(t), [output = true]\n y2(t), [output = true]\n end\n @equations begin\n D(x1) ~ -b * x1 + 1 / (c + x4)\n D(x2) ~ α * x1 - β * x2\n D(x3) ~ γ * x2 - δ * x3\n D(x4) ~ σ * x4 * (γ * x2 - δ * x3) / x3\n y ~ x1 + x2\n y2 ~ x2\n end\nend\n\n@named ode = GoodwinOsc()\n\nglobal_id = assess_identifiability(ode)","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"We can see that only parameters a, g are unidentifiable, and everything else can be uniquely recovered.","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"Let us consider the same system but with two inputs, and we will find out identifiability with probability 0.9 for parameters c and b:","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"using StructuralIdentifiability, ModelingToolkit\n\n@variables t\nD = Differential(t)\n\n@mtkmodel GoodwinOscillator begin\n @parameters begin\n b\n c\n α\n β\n γ\n δ\n σ\n end\n @variables begin\n x1(t)\n x2(t)\n x3(t)\n x4(t)\n y(t), [output = true]\n y2(t), [output = true]\n u1(t), [input = true]\n u2(t), [input = true]\n end\n @equations begin\n D(x1) ~ -b * x1 + 1 / (c + x4)\n D(x2) ~ α * x1 - β * x2 - u1\n D(x3) ~ γ * x2 - δ * x3 + u2\n D(x4) ~ σ * x4 * (γ * x2 - δ * x3) / x3\n y ~ x1 + x2\n y2 ~ x2\n end\nend\n\n@named ode = GoodwinOscillator()\node = complete(ode)\n\n# check only 2 parameters\nto_check = [ode.b, ode.c]\n\nglobal_id = assess_identifiability(ode, funcs_to_check = to_check, p = 0.9)","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"Both parameters b, c are globally identifiable with probability 0.9 in this case.","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"[1]: R. Munoz-Tamayo, L. Puillet, J.B. Daniel, D. Sauvant, O. Martin, M. Taghipoor, P. Blavy Review: To be or not to be an identifiable model. Is this a relevant question in animal science modelling?, Animal, Vol 12 (4), 701-712, 2018. The model is the ODE system (3) in Supplementary Material 2, initial conditions are assumed to be unknown.","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"[2]: Moate P.J., Boston R.C., Jenkins T.C. and Lean I.J., Kinetics of Ruminal Lipolysis of Triacylglycerol and Biohydrogenationof Long-Chain Fatty Acids: New Insights from Old Data, Journal of Dairy Science 91, 731–742, 2008","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"[3]: Goodwin, B.C. Oscillatory behavior in enzymatic control processes, Advances in Enzyme Regulation, Vol 3 (C), 425-437, 1965","category":"page"},{"location":"tutorials/parameter_identifiability/","page":"Parameter Identifiability in ODE Models","title":"Parameter Identifiability in ODE Models","text":"[4]: Dong, R., Goodbrake, C., Harrington, H. A., & Pogudin, G. Computing input-output projections of dynamical models with applications to structural identifiability. arXiv preprint arXiv:2111.00991.","category":"page"},{"location":"basics/Validation/#units","page":"Model Validation and Units","title":"Model Validation and Units","text":"","category":"section"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"ModelingToolkit.jl provides extensive functionality for model validation and unit checking. This is done by providing metadata to the variable types and then running the validation functions which identify malformed systems and non-physical equations. This approach provides high performance and compatibility with numerical solvers.","category":"page"},{"location":"basics/Validation/#Assigning-Units","page":"Model Validation and Units","title":"Assigning Units","text":"","category":"section"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Units may be assigned with the following syntax.","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"using ModelingToolkit, Unitful\n@variables t [unit = u\"s\"] x(t) [unit = u\"m\"] g(t) w(t) [unit = \"Hz\"]\n\n@variables(t, [unit = u\"s\"], x(t), [unit = u\"m\"], g(t), w(t), [unit = \"Hz\"])\n\n@variables(begin\n t, [unit = u\"s\"],\n x(t), [unit = u\"m\"],\n g(t),\n w(t), [unit = \"Hz\"]\nend)\n\n# Simultaneously set default value (use plain numbers, not quantities)\n@variables x=10 [unit = u\"m\"]\n\n# Symbolic array: unit applies to all elements\n@variables x[1:3] [unit = u\"m\"]","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Do not use quantities such as 1u\"s\", 1/u\"s\" or u\"1/s\" as these will result in errors; instead use u\"s\", u\"s^-1\", or u\"s\"^-1.","category":"page"},{"location":"basics/Validation/#Unit-Validation-and-Inspection","page":"Model Validation and Units","title":"Unit Validation & Inspection","text":"","category":"section"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Unit validation of equations happens automatically when creating a system. However, for debugging purposes, one may wish to validate the equations directly using validate.","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"ModelingToolkit.validate","category":"page"},{"location":"basics/Validation/#ModelingToolkit.validate","page":"Model Validation and Units","title":"ModelingToolkit.validate","text":"Returns true iff units of equations are valid.\n\n\n\n\n\n","category":"function"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Inside, validate uses get_unit, which may be directly applied to any term. Note that validate will not throw an error in the event of incompatible units, but get_unit will. If you would rather receive a warning instead of an error, use safe_get_unit which will yield nothing in the event of an error. Unit agreement is tested with ModelingToolkit.equivalent(u1,u2).","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"ModelingToolkit.get_unit","category":"page"},{"location":"basics/Validation/#ModelingToolkit.get_unit","page":"Model Validation and Units","title":"ModelingToolkit.get_unit","text":"Find the unit of a symbolic item.\n\n\n\n\n\n","category":"function"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Example usage below. Note that ModelingToolkit does not force unit conversions to preferred units in the event of nonstandard combinations – it merely checks that the equations are consistent.","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"using ModelingToolkit, Unitful\n@parameters τ [unit = u\"ms\"]\n@variables t [unit = u\"ms\"] E(t) [unit = u\"kJ\"] P(t) [unit = u\"MW\"]\nD = Differential(t)\neqs = eqs = [D(E) ~ P - E / τ,\n 0 ~ P]\nModelingToolkit.validate(eqs)","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"ModelingToolkit.validate(eqs[1])","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"ModelingToolkit.get_unit(eqs[1].rhs)","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"An example of an inconsistent system: at present, ModelingToolkit requires that the units of all terms in an equation or sum to be equal-valued (ModelingToolkit.equivalent(u1,u2)), rather than simply dimensionally consistent. In the future, the validation stage may be upgraded to support the insertion of conversion factors into the equations.","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"using ModelingToolkit, Unitful\n@parameters τ [unit = u\"ms\"]\n@variables t [unit = u\"ms\"] E(t) [unit = u\"J\"] P(t) [unit = u\"MW\"]\nD = Differential(t)\neqs = eqs = [D(E) ~ P - E / τ,\n 0 ~ P]\nModelingToolkit.validate(eqs) #Returns false while displaying a warning message","category":"page"},{"location":"basics/Validation/#User-Defined-Registered-Functions-and-Types","page":"Model Validation and Units","title":"User-Defined Registered Functions and Types","text":"","category":"section"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"In order to validate user-defined types and registered functions, specialize get_unit. Single-parameter calls to get_unit expect an object type, while two-parameter calls expect a function type as the first argument, and a vector of arguments as the second argument.","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"using ModelingToolkit, Unitful\n# Composite type parameter in registered function\n@parameters t\nD = Differential(t)\nstruct NewType\n f::Any\nend\n@register_symbolic dummycomplex(complex::Num, scalar)\ndummycomplex(complex, scalar) = complex.f - scalar\n\nc = NewType(1)\nModelingToolkit.get_unit(x::NewType) = ModelingToolkit.get_unit(x.f)\nfunction ModelingToolkit.get_unit(op::typeof(dummycomplex), args)\n argunits = ModelingToolkit.get_unit.(args)\n ModelingToolkit.get_unit(-, args)\nend\n\nsts = @variables a(t)=0 [unit = u\"cm\"]\nps = @parameters s=-1 [unit = u\"cm\"] c=c [unit = u\"cm\"]\neqs = [D(a) ~ dummycomplex(c, s);]\nsys = ODESystem(eqs, t, [sts...;], [ps...;], name = :sys)\nsys_simple = structural_simplify(sys)","category":"page"},{"location":"basics/Validation/#Unitful-Literals","page":"Model Validation and Units","title":"Unitful Literals","text":"","category":"section"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"In order for a function to work correctly during both validation & execution, the function must be unit-agnostic. That is, no unitful literals may be used. Any unitful quantity must either be a parameter or variable. For example, these equations will not validate successfully.","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"using ModelingToolkit, Unitful\n@variables t [unit = u\"ms\"] E(t) [unit = u\"J\"] P(t) [unit = u\"MW\"]\nD = Differential(t)\neqs = [D(E) ~ P - E / 1u\"ms\"]\nModelingToolkit.validate(eqs) #Returns false while displaying a warning message\n\nmyfunc(E) = E / 1u\"ms\"\neqs = [D(E) ~ P - myfunc(E)]\nModelingToolkit.validate(eqs) #Returns false while displaying a warning message","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Instead, they should be parameterized:","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"using ModelingToolkit, Unitful\n@parameters τ [unit = u\"ms\"]\n@variables t [unit = u\"ms\"] E(t) [unit = u\"kJ\"] P(t) [unit = u\"MW\"]\nD = Differential(t)\neqs = [D(E) ~ P - E / τ]\nModelingToolkit.validate(eqs) #Returns true","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"myfunc(E, τ) = E / τ\neqs = [D(E) ~ P - myfunc(E, τ)]\nModelingToolkit.validate(eqs) #Returns true","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"It is recommended not to circumvent unit validation by specializing user-defined functions on Unitful arguments vs. Numbers. This both fails to take advantage of validate for ensuring correctness, and may cause in errors in the future when ModelingToolkit is extended to support eliminating Unitful literals from functions.","category":"page"},{"location":"basics/Validation/#Other-Restrictions","page":"Model Validation and Units","title":"Other Restrictions","text":"","category":"section"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Unitful provides non-scalar units such as dBm, °C, etc. At this time, ModelingToolkit only supports scalar quantities. Additionally, angular degrees (°) are not supported because trigonometric functions will treat plain numerical values as radians, which would lead systems validated using degrees to behave erroneously when being solved.","category":"page"},{"location":"basics/Validation/#Troubleshooting-and-Gotchas","page":"Model Validation and Units","title":"Troubleshooting & Gotchas","text":"","category":"section"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"If a system fails to validate due to unit issues, at least one warning message will appear, including a line number as well as the unit types and expressions that were in conflict. Some system constructors re-order equations before the unit checking can be done, in which case the equation numbers may be inaccurate. The printed expression that the problem resides in is always correctly shown.","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Symbolic exponents for unitful variables are supported (ex: P^γ in thermodynamics). However, this means that ModelingToolkit cannot reduce such expressions to Unitful.Unitlike subtypes at validation time because the exponent value is not available. In this case ModelingToolkit.get_unit is type-unstable, yielding a symbolic result, which can still be checked for symbolic equality with ModelingToolkit.equivalent.","category":"page"},{"location":"basics/Validation/#Parameter-and-Initial-Condition-Values","page":"Model Validation and Units","title":"Parameter & Initial Condition Values","text":"","category":"section"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Parameter and initial condition values are supplied to problem constructors as plain numbers, with the understanding that they have been converted to the appropriate units. This is done for simplicity of interfacing with optimization solvers. Some helper function for dealing with value maps:","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"function remove_units(p::Dict)\n Dict(k => Unitful.ustrip(ModelingToolkit.get_unit(k), v) for (k, v) in p)\nend\nadd_units(p::Dict) = Dict(k => v * ModelingToolkit.get_unit(k) for (k, v) in p)","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"Recommended usage:","category":"page"},{"location":"basics/Validation/","page":"Model Validation and Units","title":"Model Validation and Units","text":"pars = @parameters τ [unit = u\"ms\"]\np = Dict(τ => 1u\"ms\")\nODEProblem(sys, remove_units(u0), tspan, remove_units(p))","category":"page"},{"location":"systems/SDESystem/#SDESystem","page":"SDESystem","title":"SDESystem","text":"","category":"section"},{"location":"systems/SDESystem/#System-Constructors","page":"SDESystem","title":"System Constructors","text":"","category":"section"},{"location":"systems/SDESystem/","page":"SDESystem","title":"SDESystem","text":"SDESystem","category":"page"},{"location":"systems/SDESystem/#ModelingToolkit.SDESystem","page":"SDESystem","title":"ModelingToolkit.SDESystem","text":"struct SDESystem <: ModelingToolkit.AbstractODESystem\n\nA system of stochastic differential equations.\n\nFields\n\ntag: A tag for the system. If two systems have the same tag, then they are structurally identical.\n\neqs: The expressions defining the drift term.\nnoiseeqs: The expressions defining the diffusion term.\niv: Independent variable.\nstates: Dependent (state) variables. Must not contain the independent variable.\nps: Parameter variables. Must not contain the independent variable.\ntspan: Time span.\nvar_to_name: Array variables.\nctrls: Control parameters (some subset of ps).\nobserved: Observed states.\ntgrad: Time-derivative matrix. Note: this field will not be defined until calculate_tgrad is called on the system.\n\njac: Jacobian matrix. Note: this field will not be defined until calculate_jacobian is called on the system.\n\nctrl_jac: Control Jacobian matrix. Note: this field will not be defined until calculate_control_jacobian is called on the system.\n\nWfact: Note: this field will not be defined until generate_factorized_W is called on the system.\n\nWfact_t: Note: this field will not be defined until generate_factorized_W is called on the system.\n\nname: The name of the system.\n\nsystems: The internal systems. These are required to have unique names.\n\ndefaults: The default values to use when initial conditions and/or parameters are not supplied in ODEProblem.\n\nconnector_type: Type of the system.\n\ncontinuous_events: A Vector{SymbolicContinuousCallback} that model events. The integrator will use root finding to guarantee that it steps at each zero crossing.\n\ndiscrete_events: A Vector{SymbolicDiscreteCallback} that models events. Symbolic analog to SciMLBase.DiscreteCallback that executes an affect when a given condition is true at the end of an integration step.\n\nmetadata: Metadata for the system, to be used by downstream packages.\n\ngui_metadata: Metadata for MTK GUI.\n\ncomplete: If a model sys is complete, then sys.x no longer performs namespacing.\n\nparent: The hierarchical parent system before simplification.\n\nExample\n\nusing ModelingToolkit\n\n@parameters σ ρ β\n@variables t x(t) y(t) z(t)\nD = Differential(t)\n\neqs = [D(x) ~ σ*(y-x),\n D(y) ~ x*(ρ-z)-y,\n D(z) ~ x*y - β*z]\n\nnoiseeqs = [0.1*x,\n 0.1*y,\n 0.1*z]\n\n@named de = SDESystem(eqs,noiseeqs,t,[x,y,z],[σ,ρ,β]; tspan = (0, 1000.0))\n\n\n\n\n\n","category":"type"},{"location":"systems/SDESystem/","page":"SDESystem","title":"SDESystem","text":"To convert an ODESystem to an SDESystem directly:","category":"page"},{"location":"systems/SDESystem/","page":"SDESystem","title":"SDESystem","text":"ode = ODESystem(eqs,t,[x,y,z],[σ,ρ,β])\nsde = SDESystem(ode, noiseeqs)","category":"page"},{"location":"systems/SDESystem/#Composition-and-Accessor-Functions","page":"SDESystem","title":"Composition and Accessor Functions","text":"","category":"section"},{"location":"systems/SDESystem/","page":"SDESystem","title":"SDESystem","text":"get_eqs(sys) or equations(sys): The equations that define the SDE.\nget_states(sys) or states(sys): The set of states in the SDE.\nget_ps(sys) or parameters(sys): The parameters of the SDE.\nget_iv(sys): The independent variable of the SDE.","category":"page"},{"location":"systems/SDESystem/#Transformations","page":"SDESystem","title":"Transformations","text":"","category":"section"},{"location":"systems/SDESystem/","page":"SDESystem","title":"SDESystem","text":"structural_simplify\nalias_elimination","category":"page"},{"location":"systems/SDESystem/#ModelingToolkit.structural_simplify-systems-SDESystem","page":"SDESystem","title":"ModelingToolkit.structural_simplify","text":"structural_simplify(sys; ...)\nstructural_simplify(sys, io; simplify, kwargs...)\n\n\nStructurally simplify algebraic equations in a system and compute the topological sort of the observed equations. When simplify=true, the simplify function will be applied during the tearing process. It also takes kwargs allow_symbolic=false and allow_parameter=true which limits the coefficient types during tearing.\n\nThe optional argument io may take a tuple (inputs, outputs). This will convert all inputs to parameters and allow them to be unconnected, i.e., simplification will allow models where n_states = n_equations - n_inputs.\n\n\n\n\n\n","category":"function"},{"location":"systems/SDESystem/","page":"SDESystem","title":"SDESystem","text":"ModelingToolkit.Girsanov_transform","category":"page"},{"location":"systems/SDESystem/#ModelingToolkit.Girsanov_transform","page":"SDESystem","title":"ModelingToolkit.Girsanov_transform","text":"Girsanov_transform(sys::SDESystem, u; θ0) -> SDESystem\n\n\nMeasure transformation method that allows for a reduction in the variance of an estimator Exp(g(X_t)). Input: Original SDE system and symbolic function u(t,x) with scalar output that defines the adjustable parameters d in the Girsanov transformation. Optional: initial condition for θ0. Output: Modified SDESystem with additional component θ_t and initial value θ0, as well as the weight θ_t/θ0 as observed equation, such that the estimator Exp(g(X_t)θ_t/θ0) has a smaller variance.\n\nReference: Kloeden, P. E., Platen, E., & Schurz, H. (2012). Numerical solution of SDE through computer experiments. Springer Science & Business Media.\n\nExample\n\nusing ModelingToolkit\n\n@parameters α β\n@variables t x(t) y(t) z(t)\nD = Differential(t)\n\neqs = [D(x) ~ α*x]\nnoiseeqs = [β*x]\n\n@named de = SDESystem(eqs,noiseeqs,t,[x],[α,β])\n\n# define u (user choice)\nu = x\nθ0 = 0.1\ng(x) = x[1]^2\ndemod = ModelingToolkit.Girsanov_transform(de, u; θ0=0.1)\n\nu0modmap = [\n x => x0\n]\n\nparammap = [\n α => 1.5,\n β => 1.0\n]\n\nprobmod = SDEProblem(demod,u0modmap,(0.0,1.0),parammap)\nensemble_probmod = EnsembleProblem(probmod;\n output_func = (sol,i) -> (g(sol[x,end])*sol[demod.weight,end],false),\n )\n\nsimmod = solve(ensemble_probmod,EM(),dt=dt,trajectories=numtraj)\n\n\n\n\n\n","category":"function"},{"location":"systems/SDESystem/#Analyses","page":"SDESystem","title":"Analyses","text":"","category":"section"},{"location":"systems/SDESystem/#Applicable-Calculation-and-Generation-Functions","page":"SDESystem","title":"Applicable Calculation and Generation Functions","text":"","category":"section"},{"location":"systems/SDESystem/","page":"SDESystem","title":"SDESystem","text":"calculate_jacobian\ncalculate_tgrad\ncalculate_factorized_W\ngenerate_jacobian\ngenerate_tgrad\ngenerate_factorized_W\njacobian_sparsity","category":"page"},{"location":"systems/SDESystem/#ModelingToolkit.calculate_jacobian-systems-SDESystem","page":"SDESystem","title":"ModelingToolkit.calculate_jacobian","text":"calculate_jacobian(sys::AbstractSystem)\n\nCalculate the Jacobian matrix of a system.\n\nReturns a matrix of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"systems/SDESystem/#ModelingToolkit.calculate_tgrad-systems-SDESystem","page":"SDESystem","title":"ModelingToolkit.calculate_tgrad","text":"calculate_tgrad(sys::AbstractTimeDependentSystem)\n\nCalculate the time gradient of a system.\n\nReturns a vector of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"systems/SDESystem/#ModelingToolkit.calculate_factorized_W-systems-SDESystem","page":"SDESystem","title":"ModelingToolkit.calculate_factorized_W","text":"calculate_factorized_W(sys::AbstractSystem)\n\nCalculate the factorized W-matrix of a system.\n\nReturns a matrix of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"systems/SDESystem/#ModelingToolkit.generate_jacobian-systems-SDESystem","page":"SDESystem","title":"ModelingToolkit.generate_jacobian","text":"generate_jacobian(sys::AbstractSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; sparse = false, kwargs...)\n\nGenerates a function for the Jacobian matrix of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"systems/SDESystem/#ModelingToolkit.generate_tgrad-systems-SDESystem","page":"SDESystem","title":"ModelingToolkit.generate_tgrad","text":"generate_tgrad(sys::AbstractTimeDependentSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; kwargs...)\n\nGenerates a function for the time gradient of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"systems/SDESystem/#ModelingToolkit.generate_factorized_W-systems-SDESystem","page":"SDESystem","title":"ModelingToolkit.generate_factorized_W","text":"generate_factorized_W(sys::AbstractSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; sparse = false, kwargs...)\n\nGenerates a function for the factorized W matrix of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"systems/SDESystem/#Problem-Constructors","page":"SDESystem","title":"Problem Constructors","text":"","category":"section"},{"location":"systems/SDESystem/","page":"SDESystem","title":"SDESystem","text":"SDEFunction(sys::ModelingToolkit.SDESystem, args...)\nSDEProblem(sys::ModelingToolkit.SDESystem, args...)","category":"page"},{"location":"systems/SDESystem/#SciMLBase.SDEFunction-Tuple{SDESystem, Vararg{Any}}","page":"SDESystem","title":"SciMLBase.SDEFunction","text":"DiffEqBase.SDEFunction{iip}(sys::SDESystem, dvs = sys.states, ps = sys.ps;\n version = nothing, tgrad = false, sparse = false,\n jac = false, Wfact = false, kwargs...) where {iip}\n\nCreate an SDEFunction from the SDESystem. The arguments dvs and ps are used to set the order of the dependent variable and parameter vectors, respectively.\n\n\n\n\n\n","category":"method"},{"location":"systems/SDESystem/#SciMLBase.SDEProblem-Tuple{SDESystem, Vararg{Any}}","page":"SDESystem","title":"SciMLBase.SDEProblem","text":"DiffEqBase.SDEProblem{iip}(sys::SDESystem, u0map, tspan, p = parammap;\n version = nothing, tgrad = false,\n jac = false, Wfact = false,\n checkbounds = false, sparse = false,\n sparsenoise = sparse,\n skipzeros = true, fillzeros = true,\n linenumbers = true, parallel = SerialForm(),\n kwargs...)\n\nGenerates an SDEProblem from an SDESystem and allows for automatically symbolically calculating numerical enhancements.\n\n\n\n\n\n","category":"method"},{"location":"systems/SDESystem/#Expression-Constructors","page":"SDESystem","title":"Expression Constructors","text":"","category":"section"},{"location":"systems/SDESystem/","page":"SDESystem","title":"SDESystem","text":"SDEFunctionExpr\nSDEProblemExpr","category":"page"},{"location":"systems/SDESystem/#ModelingToolkit.SDEFunctionExpr","page":"SDESystem","title":"ModelingToolkit.SDEFunctionExpr","text":"DiffEqBase.SDEFunctionExpr{iip}(sys::AbstractODESystem, dvs = states(sys),\n ps = parameters(sys);\n version = nothing, tgrad = false,\n jac = false, Wfact = false,\n skipzeros = true, fillzeros = true,\n sparse = false,\n kwargs...) where {iip}\n\nCreate a Julia expression for an SDEFunction from the SDESystem. The arguments dvs and ps are used to set the order of the dependent variable and parameter vectors, respectively.\n\n\n\n\n\n","category":"type"},{"location":"systems/SDESystem/#ModelingToolkit.SDEProblemExpr","page":"SDESystem","title":"ModelingToolkit.SDEProblemExpr","text":"DiffEqBase.SDEProblemExpr{iip}(sys::AbstractODESystem, u0map, tspan,\n parammap = DiffEqBase.NullParameters();\n version = nothing, tgrad = false,\n jac = false, Wfact = false,\n checkbounds = false, sparse = false,\n linenumbers = true, parallel = SerialForm(),\n kwargs...) where {iip}\n\nGenerates a Julia expression for constructing an ODEProblem from an ODESystem and allows for automatically symbolically calculating numerical enhancements.\n\n\n\n\n\n","category":"type"},{"location":"tutorials/bifurcation_diagram_computation/#bifurcation_diagrams","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"","category":"section"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"Bifurcation diagrams describes how, for a dynamic system, the quantity and quality of its steady states changes with a parameter's value. These can be computed through the BifurcationKit.jl package. ModelingToolkit provides a simple interface for creating BifurcationKit compatible BifurcationProblems from NonlinearSystems and ODESystems. All the features provided by BifurcationKit can then be applied to these systems. This tutorial provides a brief introduction for these features, with BifurcationKit.jl providing a more extensive documentation.","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/#Creating-a-BifurcationProblem","page":"Bifurcation Diagrams","title":"Creating a BifurcationProblem","text":"","category":"section"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"Let us first consider a simple NonlinearSystem:","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"using ModelingToolkit\n@variables t x(t) y(t)\n@parameters μ α\neqs = [0 ~ μ * x - x^3 + α * y,\n 0 ~ -y]\n@named nsys = NonlinearSystem(eqs, [x, y], [μ, α])","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"we wish to compute a bifurcation diagram for this system as we vary the parameter μ. For this, we need to provide the following information:","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"The system for which we wish to compute the bifurcation diagram (nsys).\nThe parameter which we wish to vary (μ).\nThe parameter set for which we want to compute the bifurcation diagram.\nAn initial guess of the state of the system for which there is a steady state at our provided parameter value.\nThe variable which value we wish to plot in the bifurcation diagram (this argument is optional, if not provided, BifurcationKit default plot functions are used).","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"We declare this additional information:","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"bif_par = μ\np_start = [μ => -1.0, α => 1.0]\nu0_guess = [x => 1.0, y => 1.0]\nplot_var = x;","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"For the initial state guess (u0_guess), typically any value can be provided, however, read BifurcatioKit's documentation for more details.","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"We can now create our BifurcationProblem, which can be provided as input to BifurcationKit's various functions.","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"using BifurcationKit\nbprob = BifurcationProblem(nsys,\n u0_guess,\n p_start,\n bif_par;\n plot_var = plot_var,\n jac = false)","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"Here, the jac argument (by default set to true) sets whenever to provide BifurcationKit with a Jacobian or not.","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/#Computing-a-bifurcation-diagram","page":"Bifurcation Diagrams","title":"Computing a bifurcation diagram","text":"","category":"section"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"Let us consider the BifurcationProblem from the last section. If we wish to compute the corresponding bifurcation diagram we must first declare various settings used by BifurcationKit to compute the diagram. These are stored in a ContinuationPar structure (which also contain a NewtonPar structure).","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"p_span = (-4.0, 6.0)\nopt_newton = NewtonPar(tol = 1e-9, max_iterations = 20)\nopts_br = ContinuationPar(dsmin = 0.001, dsmax = 0.05, ds = 0.01,\n max_steps = 100, nev = 2, newton_options = opt_newton,\n p_min = p_span[1], p_max = p_span[2],\n detect_bifurcation = 3, n_inversion = 4, tol_bisection_eigenvalue = 1e-8,\n dsmin_bisection = 1e-9);","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"Here, p_span sets the interval over which we wish to compute the diagram.","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"Next, we can use this as input to our bifurcation diagram, and then plot it.","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"bf = bifurcationdiagram(bprob, PALC(), 2, (args...) -> opts_br; bothside = true)","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"Here, the value 2 sets how sub-branches of the diagram that BifurcationKit should compute. Generally, for bifurcation diagrams, it is recommended to use the bothside=true argument.","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"using Plots\nplot(bf;\n putspecialptlegend = false,\n markersize = 2,\n plotfold = false,\n xguide = \"μ\",\n yguide = \"x\")","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"Here, the system exhibits a pitchfork bifurcation at μ=0.0.","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/#Using-ODESystem-inputs","page":"Bifurcation Diagrams","title":"Using ODESystem inputs","text":"","category":"section"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"It is also possible to use ODESystems (rather than NonlinearSystems) as input to BifurcationProblem. Here follows a brief such example.","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"using BifurcationKit, ModelingToolkit, Plots\n\n@variables t x(t) y(t)\n@parameters μ\nD = Differential(t)\neqs = [D(x) ~ μ * x - y - x * (x^2 + y^2),\n D(y) ~ x + μ * y - y * (x^2 + y^2)]\n@named osys = ODESystem(eqs, t)\n\nbif_par = μ\nplot_var = x\np_start = [μ => 1.0]\nu0_guess = [x => 0.0, y => 0.0]\n\nbprob = BifurcationProblem(osys,\n u0_guess,\n p_start,\n bif_par;\n plot_var = plot_var,\n jac = false)\n\np_span = (-3.0, 3.0)\nopt_newton = NewtonPar(tol = 1e-9, max_iterations = 20)\nopts_br = ContinuationPar(dsmin = 0.001, dsmax = 0.05, ds = 0.01,\n max_steps = 100, nev = 2, newton_options = opt_newton,\n p_max = p_span[2], p_min = p_span[1],\n detect_bifurcation = 3, n_inversion = 4, tol_bisection_eigenvalue = 1e-8,\n dsmin_bisection = 1e-9)\n\nbf = bifurcationdiagram(bprob, PALC(), 2, (args...) -> opts_br; bothside = true)\nusing Plots\nplot(bf;\n putspecialptlegend = false,\n markersize = 2,\n plotfold = false,\n xguide = \"μ\",\n yguide = \"x\")","category":"page"},{"location":"tutorials/bifurcation_diagram_computation/","page":"Bifurcation Diagrams","title":"Bifurcation Diagrams","text":"Here, the value of x in the steady state does not change, however, at μ=0 a Hopf bifurcation occur and the steady state turn unstable.","category":"page"},{"location":"basics/Events/#events","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"","category":"section"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"ModelingToolkit provides several ways to represent system events, which enable system state or parameters to be changed when certain conditions are satisfied, or can be used to detect discontinuities. These events are ultimately converted into DifferentialEquations.jl ContinuousCallbacks or DiscreteCallbacks, or into more specialized callback types from the DiffEqCallbacks.jl library.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"ODESystems and SDESystems accept keyword arguments continuous_events and discrete_events to symbolically encode continuous or discrete callbacks. JumpSystems currently support only discrete_events. Continuous events are applied when a given condition becomes zero, with root finding used to determine the time at which a zero crossing occurred. Discrete events are applied when a condition tested after each timestep evaluates to true. See the DifferentialEquations docs for more detail.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Events involve both a condition function (for the zero crossing or truth test), and an affect function (for determining how to update the system when the event occurs). These can both be specified symbolically, but a more general functional affect representation is also allowed, as described below.","category":"page"},{"location":"basics/Events/#Continuous-Events","page":"Event Handling and Callback Functions","title":"Continuous Events","text":"","category":"section"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"The basic purely symbolic continuous event interface to encode one continuous event is","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"AbstractSystem(eqs, ...; continuous_events::Vector{Equation})\nAbstractSystem(eqs, ...; continuous_events::Pair{Vector{Equation}, Vector{Equation}})","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"In the former, equations that evaluate to 0 will represent conditions that should be detected by the integrator, for example to force stepping to times of discontinuities. The latter allow modeling of events that have an effect on the state, where the first entry in the Pair is a vector of equations describing event conditions, and the second vector of equations describes the effect on the state. Each affect equation must be of the form","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"single_state_variable ~ expression_involving_any_variables_or_parameters","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"or","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"single_parameter ~ expression_involving_any_variables_or_parameters","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"In this basic interface, multiple variables can be changed in one event, or multiple parameters, but not a mix of parameters and variables. The latter can be handled via more general functional affects.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Finally, multiple events can be encoded via a Vector{Pair{Vector{Equation}, Vector{Equation}}}.","category":"page"},{"location":"basics/Events/#Example:-Friction","page":"Event Handling and Callback Functions","title":"Example: Friction","text":"","category":"section"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"The system below illustrates how continuous events can be used to model Coulomb friction","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"using ModelingToolkit, OrdinaryDiffEq, Plots\nfunction UnitMassWithFriction(k; name)\n @variables t x(t)=0 v(t)=0\n D = Differential(t)\n eqs = [D(x) ~ v\n D(v) ~ sin(t) - k * sign(v)]\n ODESystem(eqs, t; continuous_events = [v ~ 0], name) # when v = 0 there is a discontinuity\nend\n@named m = UnitMassWithFriction(0.7)\nprob = ODEProblem(m, Pair[], (0, 10pi))\nsol = solve(prob, Tsit5())\nplot(sol)","category":"page"},{"location":"basics/Events/#Example:-Bouncing-ball","page":"Event Handling and Callback Functions","title":"Example: Bouncing ball","text":"","category":"section"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"In the documentation for DifferentialEquations, we have an example where a bouncing ball is simulated using callbacks which have an affect! on the state. We can model the same system using ModelingToolkit like this","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"@variables t x(t)=1 v(t)=0\nD = Differential(t)\n\nroot_eqs = [x ~ 0] # the event happens at the ground x(t) = 0\naffect = [v ~ -v] # the effect is that the velocity changes sign\n\n@named ball = ODESystem([D(x) ~ v\n D(v) ~ -9.8], t; continuous_events = root_eqs => affect) # equation => affect\n\nball = structural_simplify(ball)\n\ntspan = (0.0, 5.0)\nprob = ODEProblem(ball, Pair[], tspan)\nsol = solve(prob, Tsit5())\n@assert 0 <= minimum(sol[x]) <= 1e-10 # the ball never went through the floor but got very close\nplot(sol)","category":"page"},{"location":"basics/Events/#Test-bouncing-ball-in-2D-with-walls","page":"Event Handling and Callback Functions","title":"Test bouncing ball in 2D with walls","text":"","category":"section"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Multiple events? No problem! This example models a bouncing ball in 2D that is enclosed by two walls at y = pm 15.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"@variables t x(t)=1 y(t)=0 vx(t)=0 vy(t)=2\nD = Differential(t)\n\ncontinuous_events = [[x ~ 0] => [vx ~ -vx]\n [y ~ -1.5, y ~ 1.5] => [vy ~ -vy]]\n\n@named ball = ODESystem([\n D(x) ~ vx,\n D(y) ~ vy,\n D(vx) ~ -9.8 - 0.1vx, # gravity + some small air resistance\n D(vy) ~ -0.1vy,\n ], t; continuous_events)\n\nball = structural_simplify(ball)\n\ntspan = (0.0, 10.0)\nprob = ODEProblem(ball, Pair[], tspan)\n\nsol = solve(prob, Tsit5())\n@assert 0 <= minimum(sol[x]) <= 1e-10 # the ball never went through the floor but got very close\n@assert minimum(sol[y]) > -1.5 # check wall conditions\n@assert maximum(sol[y]) < 1.5 # check wall conditions\n\ntv = sort([LinRange(0, 10, 200); sol.t])\nplot(sol(tv)[y], sol(tv)[x], line_z = tv)\nvline!([-1.5, 1.5], l = (:black, 5), primary = false)\nhline!([0], l = (:black, 5), primary = false)","category":"page"},{"location":"basics/Events/#func_affects","page":"Event Handling and Callback Functions","title":"Generalized functional affect support","text":"","category":"section"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"In some instances, a more flexible response to events is needed, which cannot be encapsulated by symbolic equations. For example, a component may implement complex behavior that is inconvenient or impossible to represent symbolically. ModelingToolkit therefore supports regular Julia functions as affects: instead of one or more equations, an affect is defined as a tuple:","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"[x ~ 0] => (affect!, [v, x], [p, q], ctx)","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"where, affect! is a Julia function with the signature: affect!(integ, u, p, ctx); [u,v] and [p,q] are the symbolic states (variables) and parameters that are accessed by affect!, respectively; and ctx is any context that is passed to affect! as the ctx argument.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"affect! receives a DifferentialEquations.jl integrator as its first argument, which can then be used to access states and parameters that are provided in the u and p arguments (implemented as NamedTuples). The integrator can also be manipulated more generally to control solution behavior, see the integrator interface documentation. In affect functions, we have that","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"function affect!(integ, u, p, ctx)\n # integ.t is the current time\n # integ.u[u.v] is the value of the state `v` above\n # integ.p[p.q] is the value of the parameter `q` above\nend","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"When accessing variables of a sub-system, it can be useful to rename them (alternatively, an affect function may be reused in different contexts):","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"[x ~ 0] => (affect!, [resistor₊v => :v, x], [p, q => :p2], ctx)","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Here, the symbolic variable resistor₊v is passed as v while the symbolic parameter q has been renamed p2.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"As an example, here is the bouncing ball example from above using the functional affect interface:","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"sts = @variables x(t), v(t)\npar = @parameters g = 9.8\nbb_eqs = [D(x) ~ v\n D(v) ~ -g]\n\nfunction bb_affect!(integ, u, p, ctx)\n integ.u[u.v] = -integ.u[u.v]\nend\n\nreflect = [x ~ 0] => (bb_affect!, [v], [], nothing)\n\n@named bb_model = ODESystem(bb_eqs, t, sts, par,\n continuous_events = reflect)\n\nbb_sys = structural_simplify(bb_model)\nu0 = [v => 0.0, x => 1.0]\n\nbb_prob = ODEProblem(bb_sys, u0, (0, 5.0))\nbb_sol = solve(bb_prob, Tsit5())\n\nplot(bb_sol)","category":"page"},{"location":"basics/Events/#Discrete-events-support","page":"Event Handling and Callback Functions","title":"Discrete events support","text":"","category":"section"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"In addition to continuous events, discrete events are also supported. The general interface to represent a collection of discrete events is","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"AbstractSystem(eqs, ...; discrete_events = [condition1 => affect1, condition2 => affect2])","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"where conditions are symbolic expressions that should evaluate to true when an individual affect should be executed. Here affect1 and affect2 are each either a vector of one or more symbolic equations, or a functional affect, just as for continuous events. As before, for any one event the symbolic affect equations can either all change states (i.e. variables) or all change parameters, but one cannot currently mix state and parameter changes within one individual event.","category":"page"},{"location":"basics/Events/#Example:-Injecting-cells-into-a-population","page":"Event Handling and Callback Functions","title":"Example: Injecting cells into a population","text":"","category":"section"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Suppose we have a population of N(t) cells that can grow and die, and at time t1 we want to inject M more cells into the population. We can model this by","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"@parameters M tinject α\n@variables t N(t)\nDₜ = Differential(t)\neqs = [Dₜ(N) ~ α - N]\n\n# at time tinject we inject M cells\ninjection = (t == tinject) => [N ~ N + M]\n\nu0 = [N => 0.0]\ntspan = (0.0, 20.0)\np = [α => 100.0, tinject => 10.0, M => 50]\n@named osys = ODESystem(eqs, t, [N], [α, M, tinject]; discrete_events = injection)\noprob = ODEProblem(osys, u0, tspan, p)\nsol = solve(oprob, Tsit5(); tstops = 10.0)\nplot(sol)","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Notice, with generic discrete events that we want to occur at one or more fixed times, we need to also set the tstops keyword argument to solve to ensure the integrator stops at that time. In the next section, we show how one can avoid this by using a preset-time callback.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Note that more general logical expressions can be built, for example, suppose we want the event to occur at that time only if the solution is smaller than 50% of its steady-state value (which is 100). We can encode this by modifying the event to","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"injection = ((t == tinject) & (N < 50)) => [N ~ N + M]\n\n@named osys = ODESystem(eqs, t, [N], [M, tinject, α]; discrete_events = injection)\noprob = ODEProblem(osys, u0, tspan, p)\nsol = solve(oprob, Tsit5(); tstops = 10.0)\nplot(sol)","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Since the solution is not smaller than half its steady-state value at the event time, the event condition now returns false. Here we used logical and, &, instead of the short-circuiting logical and, &&, as currently the latter cannot be used within symbolic expressions.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Let's now also add a drug at time tkill that turns off production of new cells, modeled by setting α = 0.0","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"@parameters tkill\n\n# we reset the first event to just occur at tinject\ninjection = (t == tinject) => [N ~ N + M]\n\n# at time tkill we turn off production of cells\nkilling = (t == tkill) => [α ~ 0.0]\n\ntspan = (0.0, 30.0)\np = [α => 100.0, tinject => 10.0, M => 50, tkill => 20.0]\n@named osys = ODESystem(eqs, t, [N], [α, M, tinject, tkill];\n discrete_events = [injection, killing])\noprob = ODEProblem(osys, u0, tspan, p)\nsol = solve(oprob, Tsit5(); tstops = [10.0, 20.0])\nplot(sol)","category":"page"},{"location":"basics/Events/#Periodic-and-preset-time-events","page":"Event Handling and Callback Functions","title":"Periodic and preset-time events","text":"","category":"section"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Two important subclasses of discrete events are periodic and preset-time events.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"A preset-time event is triggered at specific set times, which can be passed in a vector like","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"discrete_events = [[1.0, 4.0] => [v ~ -v]]","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"This will change the sign of v only at t = 1.0 and t = 4.0.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"As such, our last example with treatment and killing could instead be modeled by","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"injection = [10.0] => [N ~ N + M]\nkilling = [20.0] => [α ~ 0.0]\n\np = [α => 100.0, M => 50]\n@named osys = ODESystem(eqs, t, [N], [α, M];\n discrete_events = [injection, killing])\noprob = ODEProblem(osys, u0, tspan, p)\nsol = solve(oprob, Tsit5())\nplot(sol)","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Notice, one advantage of using a preset-time event is that one does not need to also specify tstops in the call to solve.","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"A periodic event is triggered at fixed intervals (e.g. every Δt seconds). To specify a periodic interval, pass the interval as the condition for the event. For example,","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"discrete_events = [1.0 => [v ~ -v]]","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"will change the sign of v at t = 1.0, 2.0, ...","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"Finally, we note that to specify an event at precisely one time, say 2.0 below, one must still use a vector","category":"page"},{"location":"basics/Events/","page":"Event Handling and Callback Functions","title":"Event Handling and Callback Functions","text":"discrete_events = [[2.0] => [v ~ -v]]","category":"page"},{"location":"examples/higher_order/#Automatic-Transformation-of-Nth-Order-ODEs-to-1st-Order-ODEs","page":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","title":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","text":"","category":"section"},{"location":"examples/higher_order/","page":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","title":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","text":"ModelingToolkit has a system for transformations of mathematical systems. These transformations allow for symbolically changing the representation of the model to problems that are easier to numerically solve. One simple to demonstrate transformation is the structural_simplify, which does a lot of tricks, one being the transformation that turns an Nth order ODE into N coupled 1st order ODEs.","category":"page"},{"location":"examples/higher_order/","page":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","title":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","text":"To see this, let's define a second order riff on the Lorenz equations. We utilize the derivative operator twice here to define the second order:","category":"page"},{"location":"examples/higher_order/","page":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","title":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","text":"using ModelingToolkit, OrdinaryDiffEq\n\n@parameters σ ρ β\n@variables t x(t) y(t) z(t)\nD = Differential(t)\n\neqs = [D(D(x)) ~ σ * (y - x),\n D(y) ~ x * (ρ - z) - y,\n D(z) ~ x * y - β * z]\n\n@named sys = ODESystem(eqs)","category":"page"},{"location":"examples/higher_order/","page":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","title":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","text":"Note that we could've used an alternative syntax for 2nd order, i.e. D = Differential(t)^2 and then D(x) would be the second derivative, and this syntax extends to N-th order. Also, we can use * or ∘ to compose Differentials, like Differential(t) * Differential(x).","category":"page"},{"location":"examples/higher_order/","page":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","title":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","text":"Now let's transform this into the ODESystem of first order components. We do this by calling structural_simplify:","category":"page"},{"location":"examples/higher_order/","page":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","title":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","text":"sys = structural_simplify(sys)","category":"page"},{"location":"examples/higher_order/","page":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","title":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","text":"Now we can directly numerically solve the lowered system. Note that, following the original problem, the solution requires knowing the initial condition for x', and thus we include that in our input specification:","category":"page"},{"location":"examples/higher_order/","page":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","title":"Automatic Transformation of Nth Order ODEs to 1st Order ODEs","text":"u0 = [D(x) => 2.0,\n x => 1.0,\n y => 0.0,\n z => 0.0]\n\np = [σ => 28.0,\n ρ => 10.0,\n β => 8 / 3]\n\ntspan = (0.0, 100.0)\nprob = ODEProblem(sys, u0, tspan, p, jac = true)\nsol = solve(prob, Tsit5())\nusing Plots;\nplot(sol, idxs = (x, y));","category":"page"},{"location":"examples/parsing/#Parsing-Expressions-into-Solvable-Systems","page":"Parsing Expressions into Solvable Systems","title":"Parsing Expressions into Solvable Systems","text":"","category":"section"},{"location":"examples/parsing/","page":"Parsing Expressions into Solvable Systems","title":"Parsing Expressions into Solvable Systems","text":"Many times when creating DSLs or creating ModelingToolkit extensions to read new file formats, it can become imperative to parse expressions. In many cases, it can be easy to use Base.parse to take things to standard Julia expressions, but how can you take a Base.Expr and generate symbolic forms from that? For example, say we had the following system we wanted to solve:","category":"page"},{"location":"examples/parsing/","page":"Parsing Expressions into Solvable Systems","title":"Parsing Expressions into Solvable Systems","text":"ex = [:(y ~ x)\n :(y ~ -2x + 3 / z)\n :(z ~ 2)]","category":"page"},{"location":"examples/parsing/","page":"Parsing Expressions into Solvable Systems","title":"Parsing Expressions into Solvable Systems","text":"We can use the function parse_expr_to_symbolic from Symbolics.jl to generate the symbolic form of the expression:","category":"page"},{"location":"examples/parsing/","page":"Parsing Expressions into Solvable Systems","title":"Parsing Expressions into Solvable Systems","text":"using Symbolics\neqs = parse_expr_to_symbolic.(ex, (Main,))","category":"page"},{"location":"examples/parsing/","page":"Parsing Expressions into Solvable Systems","title":"Parsing Expressions into Solvable Systems","text":"From there, we can use ModelingToolkit to transform the symbolic equations into a numerical nonlinear solve:","category":"page"},{"location":"examples/parsing/","page":"Parsing Expressions into Solvable Systems","title":"Parsing Expressions into Solvable Systems","text":"using ModelingToolkit, NonlinearSolve\nvars = union(ModelingToolkit.vars.(eqs)...)\n@named ns = NonlinearSystem(eqs, vars, [])\n\nprob = NonlinearProblem(ns, [1.0, 1.0, 1.0])\nsol = solve(prob, NewtonRaphson())","category":"page"},{"location":"internals/#Internal-Details","page":"Internal Details","title":"Internal Details","text":"","category":"section"},{"location":"internals/","page":"Internal Details","title":"Internal Details","text":"This is a page for detailing some of the inner workings to help future contributors to the library.","category":"page"},{"location":"internals/#Observables-and-Variable-Elimination","page":"Internal Details","title":"Observables and Variable Elimination","text":"","category":"section"},{"location":"internals/","page":"Internal Details","title":"Internal Details","text":"In the variable “elimination” algorithms, what is actually done is that variables are removed from being states and equations are moved into the observed category of the system. The observed equations are explicit algebraic equations which are then substituted out to completely eliminate these variables from the other equations, allowing the system to act as though these variables no longer exist.","category":"page"},{"location":"internals/","page":"Internal Details","title":"Internal Details","text":"However, a user may want to interact with such variables, for example, plotting their output. For this reason, these relationships are stored, and are then used to generate the observed equation found in the SciMLFunction interface, so that sol[x] lazily reconstructs the observed variable when necessary. In this sense, there is an equivalence between observables and the variable elimination system.","category":"page"},{"location":"internals/","page":"Internal Details","title":"Internal Details","text":"The procedure for variable elimination inside structural_simplify is","category":"page"},{"location":"internals/","page":"Internal Details","title":"Internal Details","text":"ModelingToolkit.initialize_system_structure.\nModelingToolkit.alias_elimination. This step moves equations into observed(sys).\nModelingToolkit.dae_index_lowering by means of pantelides! (if the system is an ODESystem).\nModelingToolkit.tearing.","category":"page"},{"location":"internals/#Preparing-a-system-for-simulation","page":"Internal Details","title":"Preparing a system for simulation","text":"","category":"section"},{"location":"internals/","page":"Internal Details","title":"Internal Details","text":"Before a simulation or optimization can be performed, the symbolic equations stored in an AbstractSystem must be converted into executable code. This step typically occurs after the simplification explained above, and is performed when an instance of a SciMLBase.SciMLProblem, such as a ODEProblem, is constructed. The call chain typically looks like this, with the function names in the case of an ODESystem indicated in parentheses","category":"page"},{"location":"internals/","page":"Internal Details","title":"Internal Details","text":"Problem constructor (ODEProblem)\nBuild an DEFunction (process_DEProblem -> ODEFunction\nWrite actual executable code (generate_function)","category":"page"},{"location":"internals/","page":"Internal Details","title":"Internal Details","text":"Apart from generate_function, which generates the dynamics function, ODEFunction also builds functions for observed equations (build_explicit_observed_function) and Jacobians (generate_jacobian) etc. These are all stored in the ODEFunction.","category":"page"},{"location":"basics/Linearization/#linearization","page":"Linearization","title":"Linearization","text":"","category":"section"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"A nonlinear dynamical system with state (differential and algebraic) x and input signals u","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"M dot x = f(x u)","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"can be linearized using the function linearize to produce a linear statespace system on the form","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"beginaligned\ndot x = Ax + Bu\ny = Cx + Du\nendaligned","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"The linearize function expects the user to specify the inputs u and the outputs y using the syntax shown in the example below. The system model is not supposed to be simplified before calling linearize:","category":"page"},{"location":"basics/Linearization/#Example","page":"Linearization","title":"Example","text":"","category":"section"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"using ModelingToolkit\n@variables t x(t)=0 y(t)=0 u(t)=0 r(t)=0\n@parameters kp = 1\nD = Differential(t)\n\neqs = [u ~ kp * (r - y) # P controller\n D(x) ~ -x + u # First-order plant\n y ~ x] # Output equation\n\n@named sys = ODESystem(eqs, t)\nmatrices, simplified_sys = linearize(sys, [r], [y]) # Linearize from r to y\nmatrices","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"The named tuple matrices contains the matrices of the linear statespace representation, while simplified_sys is an ODESystem that, among other things, indicates the state order in the linear system through","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"using ModelingToolkit: inputs, outputs\n[states(simplified_sys); inputs(simplified_sys); outputs(simplified_sys)]","category":"page"},{"location":"basics/Linearization/#Operating-point","page":"Linearization","title":"Operating point","text":"","category":"section"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"The operating point to linearize around can be specified with the keyword argument op like this: op = Dict(x => 1, r => 2). The operating point may include specification of state variables, input variables and parameters. For variables that are not specified in op, the default value specified in the model will be used if available, if no value is specified, an error is thrown.","category":"page"},{"location":"basics/Linearization/#Batch-linearization-and-algebraic-variables","page":"Linearization","title":"Batch linearization and algebraic variables","text":"","category":"section"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"If linearization is to be performed around multiple operating points, the simplification of the system has to be carried out a single time only. To facilitate this, the lower-level function ModelingToolkit.linearization_function is available. This function further allows you to obtain separate Jacobians for the differential and algebraic parts of the model. For ODE models without algebraic equations, the statespace representation above is available from the output of linearization_function as A, B, C, D = f_x, f_u, h_x, h_u.","category":"page"},{"location":"basics/Linearization/#Symbolic-linearization","page":"Linearization","title":"Symbolic linearization","text":"","category":"section"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"The function ModelingToolkit.linearize_symbolic works similar to ModelingToolkit.linearize but returns symbolic rather than numeric Jacobians. Symbolic linearization have several limitations and no all systems that can be linearized numerically can be linearized symbolically.","category":"page"},{"location":"basics/Linearization/#Input-derivatives","page":"Linearization","title":"Input derivatives","text":"","category":"section"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"Physical systems are always proper, i.e., they do not differentiate causal inputs. However, ModelingToolkit allows you to model non-proper systems, such as inverse models, and may sometimes fail to find a realization of a proper system on proper form. In these situations, linearize may throw an error mentioning","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"Input derivatives appeared in expressions (-g_z\\g_u != 0)","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"This means that to simulate this system, some order of derivatives of the input is required. To allow linearize to proceed in this situation, one may pass the keyword argument allow_input_derivatives = true, in which case the resulting model will have twice as many inputs, 2n_u, where the last n_u inputs correspond to dot u.","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"If the modeled system is actually proper (but MTK failed to find a proper realization), further numerical simplification can be applied to the resulting statespace system to obtain a proper form. Such simplification is currently available in the package ControlSystemsMTK.","category":"page"},{"location":"basics/Linearization/#Tools-for-linear-analysis","page":"Linearization","title":"Tools for linear analysis","text":"","category":"section"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"ModelingToolkitStandardLibrary contains a set of tools for more advanced linear analysis. These can be used to make it easier to work with and analyze causal models, such as control and signal-processing systems.","category":"page"},{"location":"basics/Linearization/#Docstrings","page":"Linearization","title":"Docstrings","text":"","category":"section"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"Pages = [\"Linearization.md\"]","category":"page"},{"location":"basics/Linearization/","page":"Linearization","title":"Linearization","text":"linearize\nModelingToolkit.linearize_symbolic\nModelingToolkit.linearization_function","category":"page"},{"location":"basics/Linearization/#ModelingToolkit.linearize","page":"Linearization","title":"ModelingToolkit.linearize","text":"(; A, B, C, D), simplified_sys = linearize(sys, inputs, outputs; t=0.0, op = Dict(), allow_input_derivatives = false, zero_dummy_der=false, kwargs...)\n(; A, B, C, D) = linearize(simplified_sys, lin_fun; t=0.0, op = Dict(), allow_input_derivatives = false, zero_dummy_der=false)\n\nLinearize sys between inputs and outputs, both vectors of variables. Return a NamedTuple with the matrices of a linear statespace representation on the form\n\nbeginaligned\nx = Ax + Bu\ny = Cx + Du\nendaligned\n\nThe first signature automatically calls linearization_function internally, while the second signature expects the outputs of linearization_function as input.\n\nop denotes the operating point around which to linearize. If none is provided, the default values of sys are used.\n\nIf allow_input_derivatives = false, an error will be thrown if input derivatives (u) appear as inputs in the linearized equations. If input derivatives are allowed, the returned B matrix will be of double width, corresponding to the input [u; u̇].\n\nzero_dummy_der can be set to automatically set the operating point to zero for all dummy derivatives.\n\nSee also linearization_function which provides a lower-level interface, linearize_symbolic and ModelingToolkit.reorder_states.\n\nSee extended help for an example.\n\nThe implementation and notation follows that of \"Linear Analysis Approach for Modelica Models\", Allain et al. 2009\n\nExtended help\n\nThis example builds the following feedback interconnection and linearizes it from the input of F to the output of P.\n\n\n r ┌─────┐ ┌─────┐ ┌─────┐\n───►│ ├──────►│ │ u │ │\n │ F │ │ C ├────►│ P │ y\n └─────┘ ┌►│ │ │ ├─┬─►\n │ └─────┘ └─────┘ │\n │ │\n └─────────────────────┘\n\nusing ModelingToolkit\n@variables t\nfunction plant(; name)\n @variables x(t) = 1\n @variables u(t)=0 y(t)=0\n D = Differential(t)\n eqs = [D(x) ~ -x + u\n y ~ x]\n ODESystem(eqs, t; name = name)\nend\n\nfunction ref_filt(; name)\n @variables x(t)=0 y(t)=0\n @variables u(t)=0 [input = true]\n D = Differential(t)\n eqs = [D(x) ~ -2 * x + u\n y ~ x]\n ODESystem(eqs, t, name = name)\nend\n\nfunction controller(kp; name)\n @variables y(t)=0 r(t)=0 u(t)=0\n @parameters kp = kp\n eqs = [\n u ~ kp * (r - y),\n ]\n ODESystem(eqs, t; name = name)\nend\n\n@named f = ref_filt()\n@named c = controller(1)\n@named p = plant()\n\nconnections = [f.y ~ c.r # filtered reference to controller reference\n c.u ~ p.u # controller output to plant input\n p.y ~ c.y]\n\n@named cl = ODESystem(connections, t, systems = [f, c, p])\n\nlsys0, ssys = linearize(cl, [f.u], [p.x])\ndesired_order = [f.x, p.x]\nlsys = ModelingToolkit.reorder_states(lsys0, states(ssys), desired_order)\n\n@assert lsys.A == [-2 0; 1 -2]\n@assert lsys.B == [1; 0;;]\n@assert lsys.C == [0 1]\n@assert lsys.D[] == 0\n\n## Symbolic linearization\nlsys_sym, _ = ModelingToolkit.linearize_symbolic(cl, [f.u], [p.x])\n\n@assert substitute(lsys_sym.A, ModelingToolkit.defaults(cl)) == lsys.A\n\n\n\n\n\n","category":"function"},{"location":"basics/Linearization/#ModelingToolkit.linearize_symbolic","page":"Linearization","title":"ModelingToolkit.linearize_symbolic","text":"(; A, B, C, D), simplified_sys = linearize_symbolic(sys::AbstractSystem, inputs, outputs; simplify = false, allow_input_derivatives = false, kwargs...)\n\nSimilar to linearize, but returns symbolic matrices A,B,C,D rather than numeric. While linearize uses ForwardDiff to perform the linearization, this function uses Symbolics.jacobian.\n\nSee linearize for a description of the arguments.\n\nExtended help\n\nThe named tuple returned as the first argument additionally contains the jacobians f_x, f_z, g_x, g_z, f_u, g_u, h_x, h_z, h_u of\n\nbeginaligned\nx = f(x z u) \n0 = g(x z u) \ny = h(x z u)\nendaligned\n\nwhere x are differential state variables, z algebraic variables, u inputs and y outputs.\n\n\n\n\n\n","category":"function"},{"location":"basics/Linearization/#ModelingToolkit.linearization_function","page":"Linearization","title":"ModelingToolkit.linearization_function","text":"lin_fun, simplified_sys = linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, initialize = true, kwargs...)\n\nReturn a function that linearizes the system sys. The function linearize provides a higher-level and easier to use interface.\n\nlin_fun is a function (variables, p, t) -> (; f_x, f_z, g_x, g_z, f_u, g_u, h_x, h_z, h_u), i.e., it returns a NamedTuple with the Jacobians of f,g,h for the nonlinear sys (technically for simplified_sys) on the form\n\nbeginaligned\nx = f(x z u) \n0 = g(x z u) \ny = h(x z u)\nendaligned\n\nwhere x are differential state variables, z algebraic variables, u inputs and y outputs. To obtain a linear statespace representation, see linearize. The input argument variables is a vector defining the operating point, corresponding to states(simplified_sys) and p is a vector corresponding to the parameters of simplified_sys. Note: all variables in inputs have been converted to parameters in simplified_sys.\n\nThe simplified_sys has undergone structural_simplify and had any occurring input or output variables replaced with the variables provided in arguments inputs and outputs. The states of this system also indicate the order of the states that holds for the linearized matrices.\n\nArguments:\n\nsys: An ODESystem. This function will automatically apply simplification passes on sys and return the resulting simplified_sys.\ninputs: A vector of variables that indicate the inputs of the linearized input-output model.\noutputs: A vector of variables that indicate the outputs of the linearized input-output model.\nsimplify: Apply simplification in tearing.\ninitialize: If true, a check is performed to ensure that the operating point is consistent (satisfies algebraic equations). If the op is not consistent, initialization is performed.\nkwargs: Are passed on to find_solvables!\n\nSee also linearize which provides a higher-level interface.\n\n\n\n\n\n","category":"function"},{"location":"systems/PDESystem/#PDESystem","page":"PDESystem","title":"PDESystem","text":"","category":"section"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"PDESystem is the common symbolic PDE specification for the SciML ecosystem. It is currently being built as a component of the ModelingToolkit ecosystem,","category":"page"},{"location":"systems/PDESystem/#Vision","page":"PDESystem","title":"Vision","text":"","category":"section"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"The vision for the common PDE interface is that a user should only have to specify their PDE once, mathematically, and have instant access to everything as simple as a finite difference method with constant grid spacing, to something as complex as a distributed multi-GPU discontinuous Galerkin method.","category":"page"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"The key to the common PDE interface is a separation of the symbolic handling from the numerical world. All the discretizers should not “solve” the PDE, but instead be a conversion of the mathematical specification to a numerical problem. Preferably, the transformation should be to another ModelingToolkit.jl AbstractSystem, but in some cases this cannot be done or will not be performant, so a SciMLProblem is the other choice.","category":"page"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"These elementary problems, such as solving linear systems Ax=b, solving nonlinear systems f(x)=0, ODEs, etc. are all defined by SciMLBase.jl, which then numerical solvers can all target these common forms. Thus, someone who works on linear solvers doesn't necessarily need to be working on a discontinuous Galerkin or finite element library, but instead \"linear solvers that are good for matrices A with properties ...\" which are then accessible by every other discretization method in the common PDE interface.","category":"page"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"Similar to the rest of the AbstractSystem types, transformation, and analysis functions will allow for simplifying the PDE before solving it, and constructing block symbolic functions like Jacobians.","category":"page"},{"location":"systems/PDESystem/#Constructors","page":"PDESystem","title":"Constructors","text":"","category":"section"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"PDESystem","category":"page"},{"location":"systems/PDESystem/#ModelingToolkit.PDESystem","page":"PDESystem","title":"ModelingToolkit.PDESystem","text":"struct PDESystem <: AbstractMultivariateSystem\n\nA system of partial differential equations.\n\nFields\n\neqs: The equations which define the PDE.\nbcs: The boundary conditions.\ndomain: The domain for the independent variables.\nivs: The independent variables.\ndvs: The dependent variables.\nps: The parameters.\ndefaults: The default values to use when initial conditions and/or parameters are not supplied in ODEProblem.\n\nconnector_type: Type of the system.\n\nsystems: The internal systems. These are required to have unique names.\n\nanalytic: A vector of explicit symbolic expressions for the analytic solutions of each dependent variable. e.g. analytic = [u(t, x) ~ a*sin(c*t) * cos(k*x)].\n\nanalytic_func: A vector of functions for the analytic solutions of each dependent variable. Will be generated from analytic if not provided. Should have the same argument signature as the variable, and a ps argument as the last argument, which takes an indexable of parameter values in the order you specified them in ps. e.g. analytic_func = [u(t, x) => (ps, t, x) -> ps[1]*sin(ps[2]*t) * cos(ps[3]*x)].\n\nname: The name of the system.\n\nmetadata: Metadata for the system, to be used by downstream packages.\n\ngui_metadata: Metadata for MTK GUI.\n\nExample\n\nusing ModelingToolkit\n\n@parameters x\n@variables t u(..)\nDxx = Differential(x)^2\nDtt = Differential(t)^2\nDt = Differential(t)\n\n#2D PDE\nC=1\neq = Dtt(u(t,x)) ~ C^2*Dxx(u(t,x))\n\n# Initial and boundary conditions\nbcs = [u(t,0) ~ 0.,# for all t > 0\n u(t,1) ~ 0.,# for all t > 0\n u(0,x) ~ x*(1. - x), #for all 0 < x < 1\n Dt(u(0,x)) ~ 0. ] #for all 0 < x < 1]\n\n# Space and time domains\ndomains = [t ∈ (0.0,1.0),\n x ∈ (0.0,1.0)]\n\n@named pde_system = PDESystem(eq,bcs,domains,[t,x],[u])\n\n\n\n\n\n","category":"type"},{"location":"systems/PDESystem/#Domains-(WIP)","page":"PDESystem","title":"Domains (WIP)","text":"","category":"section"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"Domains are specifying by saying indepvar in domain, where indepvar is a single or a collection of independent variables, and domain is the chosen domain type. A 2-tuple can be used to indicate an Interval. Thus forms for the indepvar can be like:","category":"page"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"t ∈ (0.0, 1.0)\n(t, x) ∈ UnitDisk()\n[v, w, x, y, z] ∈ VectorUnitBall(5)","category":"page"},{"location":"systems/PDESystem/#Domain-Types-(WIP)","page":"PDESystem","title":"Domain Types (WIP)","text":"","category":"section"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"Interval(a,b): Defines the domain of an interval from a to b (requires explicit import from DomainSets.jl, but a 2-tuple can be used instead)","category":"page"},{"location":"systems/PDESystem/#discretize-and-symbolic_discretize","page":"PDESystem","title":"discretize and symbolic_discretize","text":"","category":"section"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"The only functions which act on a PDESystem are the following:","category":"page"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"discretize(sys,discretizer): produces the outputted AbstractSystem or SciMLProblem.\nsymbolic_discretize(sys,discretizer): produces a debugging symbolic description of the discretized problem.","category":"page"},{"location":"systems/PDESystem/#Boundary-Conditions-(WIP)","page":"PDESystem","title":"Boundary Conditions (WIP)","text":"","category":"section"},{"location":"systems/PDESystem/#Transformations","page":"PDESystem","title":"Transformations","text":"","category":"section"},{"location":"systems/PDESystem/#Analyses","page":"PDESystem","title":"Analyses","text":"","category":"section"},{"location":"systems/PDESystem/#Discretizer-Ecosystem","page":"PDESystem","title":"Discretizer Ecosystem","text":"","category":"section"},{"location":"systems/PDESystem/#NeuralPDE.jl:-PhysicsInformedNN","page":"PDESystem","title":"NeuralPDE.jl: PhysicsInformedNN","text":"","category":"section"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"NeuralPDE.jl defines the PhysicsInformedNN discretizer which uses a DiffEqFlux.jl neural network to solve the differential equation.","category":"page"},{"location":"systems/PDESystem/#MethodOfLines.jl:-MOLFiniteDifference","page":"PDESystem","title":"MethodOfLines.jl: MOLFiniteDifference","text":"","category":"section"},{"location":"systems/PDESystem/","page":"PDESystem","title":"PDESystem","text":"MethodOfLines.jl defines the MOLFiniteDifference discretizer which performs a finite difference discretization. Includes support for higher approximation order stencils and nonuniform grids.","category":"page"},{"location":"systems/OptimizationSystem/#OptimizationSystem","page":"OptimizationSystem","title":"OptimizationSystem","text":"","category":"section"},{"location":"systems/OptimizationSystem/#System-Constructors","page":"OptimizationSystem","title":"System Constructors","text":"","category":"section"},{"location":"systems/OptimizationSystem/","page":"OptimizationSystem","title":"OptimizationSystem","text":"OptimizationSystem","category":"page"},{"location":"systems/OptimizationSystem/#ModelingToolkit.OptimizationSystem","page":"OptimizationSystem","title":"ModelingToolkit.OptimizationSystem","text":"struct OptimizationSystem <: ModelingToolkit.AbstractOptimizationSystem\n\nA scalar equation for optimization.\n\nFields\n\ntag: A tag for the system. If two systems have the same tag, then they are structurally identical.\n\nop: Objective function of the system.\nstates: Unknown variables.\nps: Parameters.\nvar_to_name: Array variables.\nobserved: Observed variables.\nconstraints: List of constraint equations of the system.\nname: The name of the system.\nsystems: The internal systems. These are required to have unique names.\ndefaults: The default values to use when initial guess and/or parameters are not supplied in OptimizationProblem.\n\nmetadata: Metadata for the system, to be used by downstream packages.\n\ngui_metadata: Metadata for MTK GUI.\n\ncomplete: If a model sys is complete, then sys.x no longer performs namespacing.\n\nparent: The hierarchical parent system before simplification.\n\nExamples\n\n@variables x y z\n@parameters a b c\n\nobj = a * (y - x) + x * (b - z) - y + x * y - c * z\ncons = [x^2 + y^2 ≲ 1]\n@named os = OptimizationSystem(obj, [x, y, z], [a, b, c]; constraints = cons)\n\n\n\n\n\n","category":"type"},{"location":"systems/OptimizationSystem/#Composition-and-Accessor-Functions","page":"OptimizationSystem","title":"Composition and Accessor Functions","text":"","category":"section"},{"location":"systems/OptimizationSystem/","page":"OptimizationSystem","title":"OptimizationSystem","text":"get_op(sys): The objective to be minimized.\nget_states(sys) or states(sys): The set of states for the optimization.\nget_ps(sys) or parameters(sys): The parameters for the optimization.\nget_constraints(sys) or constraints(sys): The constraints for the optimization.","category":"page"},{"location":"systems/OptimizationSystem/#Transformations","page":"OptimizationSystem","title":"Transformations","text":"","category":"section"},{"location":"systems/OptimizationSystem/#Analyses","page":"OptimizationSystem","title":"Analyses","text":"","category":"section"},{"location":"systems/OptimizationSystem/#Applicable-Calculation-and-Generation-Functions","page":"OptimizationSystem","title":"Applicable Calculation and Generation Functions","text":"","category":"section"},{"location":"systems/OptimizationSystem/","page":"OptimizationSystem","title":"OptimizationSystem","text":"calculate_gradient\ncalculate_hessian\ngenerate_gradient\ngenerate_hessian\nhessian_sparsity","category":"page"},{"location":"systems/OptimizationSystem/#Problem-Constructors","page":"OptimizationSystem","title":"Problem Constructors","text":"","category":"section"},{"location":"systems/OptimizationSystem/","page":"OptimizationSystem","title":"OptimizationSystem","text":"OptimizationProblem(sys::ModelingToolkit.OptimizationSystem, args...)","category":"page"},{"location":"systems/OptimizationSystem/#SciMLBase.OptimizationProblem-Tuple{OptimizationSystem, Vararg{Any}}","page":"OptimizationSystem","title":"SciMLBase.OptimizationProblem","text":"DiffEqBase.OptimizationProblem{iip}(sys::OptimizationSystem, u0map,\n parammap = DiffEqBase.NullParameters();\n grad = false,\n hess = false, sparse = false,\n cons_j = false, cons_h = false,\n checkbounds = false,\n linenumbers = true, parallel = SerialForm(),\n kwargs...) where {iip}\n\nGenerates an OptimizationProblem from an OptimizationSystem and allows for automatically symbolically calculating numerical enhancements.\n\nCertain solvers require setting cons_j, cons_h to true for constrained-optimization problems.\n\n\n\n\n\n","category":"method"},{"location":"systems/OptimizationSystem/#Expression-Constructors","page":"OptimizationSystem","title":"Expression Constructors","text":"","category":"section"},{"location":"systems/OptimizationSystem/","page":"OptimizationSystem","title":"OptimizationSystem","text":"OptimizationProblemExpr","category":"page"},{"location":"systems/OptimizationSystem/#ModelingToolkit.OptimizationProblemExpr","page":"OptimizationSystem","title":"ModelingToolkit.OptimizationProblemExpr","text":"DiffEqBase.OptimizationProblemExpr{iip}(sys::OptimizationSystem,\n parammap = DiffEqBase.NullParameters();\n u0 = nothing,\n grad = false,\n hes = false, sparse = false,\n checkbounds = false,\n linenumbers = true, parallel = SerialForm(),\n kwargs...) where {iip}\n\nGenerates a Julia expression for an OptimizationProblem from an OptimizationSystem and allows for automatically symbolically calculating numerical enhancements.\n\n\n\n\n\n","category":"type"},{"location":"tutorials/modelingtoolkitize/#Modelingtoolkitize:-Automatically-Translating-Numerical-to-Symbolic-Code","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"","category":"section"},{"location":"tutorials/modelingtoolkitize/#What-is-modelingtoolkitize?","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"What is modelingtoolkitize?","text":"","category":"section"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"From the other tutorials you will have learned that ModelingToolkit is a symbolic library with all kinds of goodies, such as the ability to derive analytical expressions for things like Jacobians, determine the sparsity of a set of equations, perform index reduction, tearing, and other transformations to improve both stability and performance. All of these are good things, but all of these require that one has defined the problem symbolically.","category":"page"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"But what happens if one wants to use ModelingToolkit functionality on code that is already written for DifferentialEquations.jl, NonlinearSolve.jl, Optimization.jl, or beyond?","category":"page"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"modelingtoolktize is a function in ModelingToolkit which takes a numerically-defined SciMLProblem and transforms it into its symbolic ModelingToolkit equivalent. By doing so, ModelingToolkit analysis passes and transformations can be run as intermediate steps to improve a simulation code before it's passed to the solver.","category":"page"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"note: Note\nmodelingtoolkitize does have some limitations, i.e. not all codes that work with the numerical solvers will work with modelingtoolkitize. Namely, it requires the ability to trace the equations with Symbolics.jl Num types. Generally, a code which is compatible with forward-mode automatic differentiation is compatible with modelingtoolkitize.","category":"page"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"warn: Warn\nmodelingtoolkitize expressions cannot keep control flow structures (loops), and thus equations with long loops will be translated into large expressions, which can increase the compile time of the equations and reduce the SIMD vectorization achieved by LLVM.","category":"page"},{"location":"tutorials/modelingtoolkitize/#Example-Usage:-Generating-an-Analytical-Jacobian-Expression-for-an-ODE-Code","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Example Usage: Generating an Analytical Jacobian Expression for an ODE Code","text":"","category":"section"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"Take, for example, the Robertson ODE defined as an ODEProblem for DifferentialEquations.jl:","category":"page"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"using DifferentialEquations, ModelingToolkit\nfunction rober(du, u, p, t)\n y₁, y₂, y₃ = u\n k₁, k₂, k₃ = p\n du[1] = -k₁ * y₁ + k₃ * y₂ * y₃\n du[2] = k₁ * y₁ - k₂ * y₂^2 - k₃ * y₂ * y₃\n du[3] = k₂ * y₂^2\n nothing\nend\nprob = ODEProblem(rober, [1.0, 0.0, 0.0], (0.0, 1e5), (0.04, 3e7, 1e4))","category":"page"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"If we want to get a symbolic representation, we can simply call modelingtoolkitize on the prob, which will return an ODESystem:","category":"page"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"sys = modelingtoolkitize(prob)","category":"page"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"Using this, we can symbolically build the Jacobian and then rebuild the ODEProblem:","category":"page"},{"location":"tutorials/modelingtoolkitize/","page":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","title":"Modelingtoolkitize: Automatically Translating Numerical to Symbolic Code","text":"prob_jac = ODEProblem(sys, [], (0.0, 1e5), jac = true)","category":"page"},{"location":"basics/MTKModel_Connector/#mtkmodel_connector","page":"Components and Connectors","title":"Components and Connectors","text":"","category":"section"},{"location":"basics/MTKModel_Connector/#MTK-Model","page":"Components and Connectors","title":"MTK Model","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"MTK represents components and connectors with Model.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"ModelingToolkit.Model","category":"page"},{"location":"basics/MTKModel_Connector/#ModelingToolkit.Model","page":"Components and Connectors","title":"ModelingToolkit.Model","text":"struct Model{F, S}\n\nModelingToolkit component or connector with metadata\n\nFields\n\nf: The constructor that returns ODESystem.\nstructure: The dictionary with metadata like keyword arguments (:kwargs), base system this Model extends (:extend), sub-components of the Model (:components), variables (:variables), parameters (:parameters), structural parameters (:structural_parameters) and equations (:equations).\n\nisconnector: This flag is true when the Model is a connector and is false when it is a component\n\n\n\n\n\n","category":"type"},{"location":"basics/MTKModel_Connector/#Components","page":"Components and Connectors","title":"Components","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Components are models from various domains. These models contain states and their equations.","category":"page"},{"location":"basics/MTKModel_Connector/#mtkmodel","page":"Components and Connectors","title":"Defining components with @mtkmodel","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"@mtkmodel is a convenience macro to define components. It returns ModelingToolkit.Model, which includes a constructor that returns the ODESystem, a structure dictionary with metadata, and flag isconnector which is set to false.","category":"page"},{"location":"basics/MTKModel_Connector/#What-can-an-MTK-Model-definition-have?","page":"Components and Connectors","title":"What can an MTK-Model definition have?","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"@mtkmodel definition contains begin blocks of","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"@components: for listing sub-components of the system\n@equations: for the list of equations\n@extend: for extending a base system and unpacking its states\n@icon : for embedding the model icon\n@parameters: for specifying the symbolic parameters\n@structural_parameters: for specifying non-symbolic parameters\n@variables: for specifying the states","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Let's explore these in more detail with the following example:","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"using ModelingToolkit\n\n@mtkmodel ModelA begin\n @parameters begin\n k\n k_array[1:2]\n end\nend\n\n@mtkmodel ModelB begin\n @parameters begin\n p1 = 1.0, [description = \"Parameter of ModelB\"]\n p2 = 1.0, [description = \"Parameter of ModelB\"]\n end\nend\n\n@mtkmodel ModelC begin\n @icon \"https://github.com/SciML/SciMLDocs/blob/main/docs/src/assets/logo.png\"\n @structural_parameters begin\n f = sin\n end\n begin\n v_var = 1.0\n end\n @variables begin\n v(t) = v_var\n v_array(t)[1:2, 1:3]\n end\n @extend ModelB(; p1)\n @components begin\n model_a = ModelA(; k_array)\n end\n @equations begin\n model_a.k ~ f(v)\n end\nend","category":"page"},{"location":"basics/MTKModel_Connector/#@icon","page":"Components and Connectors","title":"@icon","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"An icon can be embedded in 3 ways:","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"URI\nPath to a valid image-file.<br> It can be an absolute path. Or, a path relative to an icon directory; which is DEPOT_PATH[1]/mtk_icons by default and can be changed by setting ENV[\"MTK_ICONS_DIR\"].<br> Internally, it is saved in the File URI scheme.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"@mtkmodel WithPathtoIcon begin\n @icon \"/home/user/.julia/dev/mtk_icons/icon.png\"\n # Rest of the model definition\nend","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Inlined SVG.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"@mtkmodel WithInlinedSVGIcon begin\n @icon \"\"\"<svg height=\"100\" width=\"100\">\n <circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"green\" fill=\"none\" stroke-width=\"3\"/>\n </svg>\n \"\"\"\n # Rest of the model definition\nend","category":"page"},{"location":"basics/MTKModel_Connector/#@structural_parameters-begin-block","page":"Components and Connectors","title":"@structural_parameters begin block","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"This block is for non symbolic input arguments. These are for inputs that usually are not meant to be part of components; but influence how they are defined. One can list inputs like boolean flags, functions etc... here.\nWhenever default values are specified, unlike parameters/variables, they are reflected in the keyword argument list.","category":"page"},{"location":"basics/MTKModel_Connector/#@parameters-and-@variables-begin-block","page":"Components and Connectors","title":"@parameters and @variables begin block","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Parameters and variables are declared with respective begin blocks.\nVariables must be functions of an independent variable.\nOptionally, initial guess and metadata can be specified for these parameters and variables. See ModelB in the above example.\nAlong with creating parameters and variables, keyword arguments of same name with default value nothing are created.\nWhenever a parameter or variable has initial value, for example v(t) = 0.0, a symbolic variable named v with initial value 0.0 and a keyword argument v, with default value nothing are created. <br> This way, users can optionally pass new value of v while creating a component.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"julia> @mtkbuild model_c1 = ModelC(; v = 2.0);\n\njulia> ModelingToolkit.getdefault(model_c1.v)\n2.0","category":"page"},{"location":"basics/MTKModel_Connector/#@extend-begin-block","page":"Components and Connectors","title":"@extend begin block","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Partial systems can be extended in a higher system as @extend PartialSystem(; kwargs).\nKeyword arguments pf partial system in the @extend definition are added as the keyword arguments of the base system.\nNote that in above example, p1 is promoted as an argument of ModelC. Users can set the value of p1. However, as p2 isn't listed in the model definition, its initial guess can't be specified while creating an instance of ModelC.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"julia> @mtkbuild model_c2 = ModelC(; p1 = 2.0)\n","category":"page"},{"location":"basics/MTKModel_Connector/#@components-begin-block","page":"Components and Connectors","title":"@components begin block","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Declare the subcomponents within @components begin block.\nThe arguments in these subcomponents are promoted as keyword arguments as subcomponent_name__argname with nothing as default value.\nWhenever components are created with @named macro, these can be accessed with . operator as subcomponent_name.argname\nIn the above example, as k of model_a isn't listed while defining the sub-component in ModelC, its default value can't be modified by users. While k_array can be set as:","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"using ModelingToolkit: getdefault\n\n@mtkbuild model_c3 = ModelC(; model_a.k_array = [1.0, 2.0])\n\ngetdefault(model_c3.model_a.k_array[1])\n# 1.0\ngetdefault(model_c3.model_a.k_array[2])\n# 2.0\n\n@mtkbuild model_c4 = ModelC(model_a.k_array = 3.0)\n\ngetdefault(model_c4.model_a.k_array[1])\n# 3.0\ngetdefault(model_c4.model_a.k_array[2])\n# 3.0","category":"page"},{"location":"basics/MTKModel_Connector/#@equations-begin-block","page":"Components and Connectors","title":"@equations begin block","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"List all the equations here","category":"page"},{"location":"basics/MTKModel_Connector/#A-begin-block","page":"Components and Connectors","title":"A begin block","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Any other Julia operations can be included with dedicated begin blocks.","category":"page"},{"location":"basics/MTKModel_Connector/#Connectors","page":"Components and Connectors","title":"Connectors","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Connectors are special models that can be used to connect different components together. MTK provides 3 distinct connectors:","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"DomainConnector: A connector which has only one state which is of Flow type, specified by [connect = Flow].\nStreamConnector: A connector which has atleast one stream variable, specified by [connect = Stream]. A StreamConnector must have exactly one flow variable.\nRegularConnector: Connectors that don't fall under above categories.","category":"page"},{"location":"basics/MTKModel_Connector/#connector","page":"Components and Connectors","title":"Defining connectors with @connector","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"@connector returns ModelingToolkit.Model. It includes a constructor that returns a connector ODESystem, a structure dictionary with metadata, and flag isconnector which is set to true.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"A simple connector can be defined with syntax similar to following example:","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"using ModelingToolkit\n\n@connector Pin begin\n v(t) = 0.0, [description = \"Voltage\"]\n i(t), [connect = Flow]\nend","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Variables (as functions of independent variable) are listed out in the definition. These variables can optionally have initial values and metadata like description, connect and so on. For more details on setting metadata, check out Symbolic Metadata.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Similar to @mtkmodel, @connector accepts begin blocks of @components, @equations, @extend, @parameters, @structural_parameters, @variables. These keywords mean the same as described above for @mtkmodel. For example, the following HydraulicFluid connector is defined with parameters, variables and equations.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"@connector HydraulicFluid begin\n @parameters begin\n ρ = 997\n β = 2.09e9\n μ = 0.0010016\n n = 1\n let_gas = 1\n ρ_gas = 0.0073955\n p_gas = -1000\n end\n @variables begin\n dm(t) = 0.0, [connect = Flow]\n end\n @equations begin\n dm ~ 0\n end\nend","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"note: Note\nFor more examples of usage, checkout ModelingToolkitStandardLibrary.jl","category":"page"},{"location":"basics/MTKModel_Connector/#More-on-Model.structure","page":"Components and Connectors","title":"More on Model.structure","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"structure stores metadata that describes composition of a model. It includes:","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":":components: List of sub-components in the form of [[name, subcomponentname],...].\n:extend: The list of extended states, name given to the base system, and name of the base system.\n:structural_parameters: Dictionary of structural parameters mapped to their default values.\n:parameters: Dictionary of symbolic parameters mapped to their metadata. For parameter arrays, length is added to the metadata as :size.\n:variables: Dictionary of symbolic variables mapped to their metadata. For variable arrays, length is added to the metadata as :size.\n:kwargs: Dictionary of keyword arguments mapped to their default values.\n:independent_variable: Independent variable, which is added while generating the Model.\n:equations: List of equations (represented as strings).","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"For example, the structure of ModelC is:","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"julia> ModelC.structure\nDict{Symbol, Any} with 7 entries:\n :components => [[:model_a, :ModelA]]\n :variables => Dict{Symbol, Dict{Symbol, Any}}(:v=>Dict(:default=>:v_var), :v_array=>Dict(:size=>(2, 3)))\n :icon => URI(\"https://github.com/SciML/SciMLDocs/blob/main/docs/src/assets/logo.png\")\n :kwargs => Dict{Symbol, Any}(:f=>:sin, :v=>:v_var, :v_array=>nothing, :model_a__k_array=>nothing, :p1=>nothing)\n :independent_variable => t\n :extend => Any[[:p2, :p1], Symbol(\"#mtkmodel__anonymous__ModelB\"), :ModelB]\n :equations => [\"model_a.k ~ f(v)\"]","category":"page"},{"location":"basics/MTKModel_Connector/#Using-conditional-statements","page":"Components and Connectors","title":"Using conditional statements","text":"","category":"section"},{"location":"basics/MTKModel_Connector/#Conditional-elements-of-the-system","page":"Components and Connectors","title":"Conditional elements of the system","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Both @mtkmodel and @connector support conditionally defining parameters, variables, equations, and components.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"The if-elseif-else statements can be used inside @equations, @parameters, @variables, @components.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"using ModelingToolkit\n\n@mtkmodel C begin end\n\n@mtkmodel BranchInsideTheBlock begin\n @structural_parameters begin\n flag = true\n end\n @parameters begin\n if flag\n a1\n else\n a2\n end\n end\n @components begin\n if flag\n sys1 = C()\n else\n sys2 = C()\n end\n end\nend","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Alternatively, the @equations, @parameters, @variables, @components can be used inside the if-elseif-else statements.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"@mtkmodel BranchOutsideTheBlock begin\n @structural_parameters begin\n flag = true\n end\n if flag\n @parameters begin\n a1\n end\n @components begin\n sys1 = C()\n end\n @equations begin\n a1 ~ 0\n end\n else\n @parameters begin\n a2\n end\n @equations begin\n a2 ~ 0\n end\n end\nend","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"The conditional parts are reflected in the structure. For BranchOutsideTheBlock, the metadata is:","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"julia> BranchOutsideTheBlock.structure\nDict{Symbol, Any} with 5 entries:\n :components => Any[(:if, :flag, [[:sys1, :C]], Any[])]\n :kwargs => Dict{Symbol, Any}(:flag=>true)\n :independent_variable => t\n :parameters => Dict{Symbol, Dict{Symbol, Any}}(:a1=>Dict(:condition=>(:if, :flag, Dict{Symbol, Any}(:kwargs => Dict{Any, Any}(:a1 => nothing), :parameters => Any[Dict{Symbol, Dict{Symbol, Any}}(:a1 => Dict())]), Dict{Symbol, Any}(:kwargs => Dict{Any, Any}(:a2 => nothing), :parameters => Any[Dict{Symbol, Dict{Symbol, Any}}(:a2 => Dict())]))\n :equations => Any[(:if, :flag, [\"a1 ~ 0\"], [\"a2 ~ 0\"])]","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Conditional entries are entered in the format of (branch, condition, [case when it is true], [case when it is false]); where branch is either :if or :elseif.<br> The [case when it is false] is either an empty vector or nothing when only if branch is present; it is a vector or dictionary whenever else branch is present; it is a conditional tuple whenever elseif branches are present.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"For the conditional components and equations these condition tuples are added directly, while for parameters and variables these are added as :condition metadata.","category":"page"},{"location":"basics/MTKModel_Connector/#Conditional-initial-guess-of-symbolic-variables","page":"Components and Connectors","title":"Conditional initial guess of symbolic variables","text":"","category":"section"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"Using ternary operator or if-elseif-else statement, conditional initial guesses can be assigned to parameters and variables.","category":"page"},{"location":"basics/MTKModel_Connector/","page":"Components and Connectors","title":"Components and Connectors","text":"@mtkmodel DefaultValues begin\n @structural_parameters begin\n flag = true\n end\n @parameters begin\n p = flag ? 1 : 2\n end\nend","category":"page"},{"location":"systems/ODESystem/#ODESystem","page":"ODESystem","title":"ODESystem","text":"","category":"section"},{"location":"systems/ODESystem/#System-Constructors","page":"ODESystem","title":"System Constructors","text":"","category":"section"},{"location":"systems/ODESystem/","page":"ODESystem","title":"ODESystem","text":"ODESystem","category":"page"},{"location":"systems/ODESystem/#ModelingToolkit.ODESystem","page":"ODESystem","title":"ModelingToolkit.ODESystem","text":"struct ODESystem <: ModelingToolkit.AbstractODESystem\n\nA system of ordinary differential equations.\n\nFields\n\ntag: A tag for the system. If two systems have the same tag, then they are structurally identical.\n\neqs: The ODEs defining the system.\niv: Independent variable.\nstates: Dependent (state) variables. Must not contain the independent variable.\nN.B.: If torn_matching !== nothing, this includes all variables. Actual ODE states are determined by the SelectedState() entries in torn_matching.\n\nps: Parameter variables. Must not contain the independent variable.\ntspan: Time span.\nvar_to_name: Array variables.\nctrls: Control parameters (some subset of ps).\nobserved: Observed states.\ntgrad: Time-derivative matrix. Note: this field will not be defined until calculate_tgrad is called on the system.\n\njac: Jacobian matrix. Note: this field will not be defined until calculate_jacobian is called on the system.\n\nctrl_jac: Control Jacobian matrix. Note: this field will not be defined until calculate_control_jacobian is called on the system.\n\nWfact: Note: this field will not be defined until generate_factorized_W is called on the system.\n\nWfact_t: Note: this field will not be defined until generate_factorized_W is called on the system.\n\nname: The name of the system.\n\nsystems: The internal systems. These are required to have unique names.\n\ndefaults: The default values to use when initial conditions and/or parameters are not supplied in ODEProblem.\n\ntorn_matching: Tearing result specifying how to solve the system.\n\nconnector_type: Type of the system.\n\npreface: Inject assignment statements before the evaluation of the RHS function.\n\ncontinuous_events: A Vector{SymbolicContinuousCallback} that model events. The integrator will use root finding to guarantee that it steps at each zero crossing.\n\ndiscrete_events: A Vector{SymbolicDiscreteCallback} that models events. Symbolic analog to SciMLBase.DiscreteCallback that executes an affect when a given condition is true at the end of an integration step.\n\nmetadata: Metadata for the system, to be used by downstream packages.\n\ngui_metadata: Metadata for MTK GUI.\n\ntearing_state: Cache for intermediate tearing state.\n\nsubstitutions: Substitutions generated by tearing.\n\ncomplete: If a model sys is complete, then sys.x no longer performs namespacing.\n\ndiscrete_subsystems: A list of discrete subsystems.\n\nunknown_states: A list of actual states needed to be solved by solvers. Only used for ODAEProblem.\n\nsplit_idxs: A vector of vectors of indices for the split parameters.\n\nparent: The hierarchical parent system before simplification.\n\nExample\n\nusing ModelingToolkit\n\n@parameters σ ρ β\n@variables t x(t) y(t) z(t)\nD = Differential(t)\n\neqs = [D(x) ~ σ*(y-x),\n D(y) ~ x*(ρ-z)-y,\n D(z) ~ x*y - β*z]\n\n@named de = ODESystem(eqs,t,[x,y,z],[σ,ρ,β],tspan=(0, 1000.0))\n\n\n\n\n\n","category":"type"},{"location":"systems/ODESystem/#Composition-and-Accessor-Functions","page":"ODESystem","title":"Composition and Accessor Functions","text":"","category":"section"},{"location":"systems/ODESystem/","page":"ODESystem","title":"ODESystem","text":"get_eqs(sys) or equations(sys): The equations that define the ODE.\nget_states(sys) or states(sys): The set of states in the ODE.\nget_ps(sys) or parameters(sys): The parameters of the ODE.\nget_iv(sys): The independent variable of the ODE.\nget_u0_p(sys, u0map, parammap) Numeric arrays for the initial condition and parameters given var => value maps.","category":"page"},{"location":"systems/ODESystem/#Transformations","page":"ODESystem","title":"Transformations","text":"","category":"section"},{"location":"systems/ODESystem/","page":"ODESystem","title":"ODESystem","text":"structural_simplify\node_order_lowering\ndae_index_lowering\nliouville_transform\nalias_elimination\ntearing","category":"page"},{"location":"systems/ODESystem/#ModelingToolkit.structural_simplify","page":"ODESystem","title":"ModelingToolkit.structural_simplify","text":"structural_simplify(sys; ...)\nstructural_simplify(sys, io; simplify, kwargs...)\n\n\nStructurally simplify algebraic equations in a system and compute the topological sort of the observed equations. When simplify=true, the simplify function will be applied during the tearing process. It also takes kwargs allow_symbolic=false and allow_parameter=true which limits the coefficient types during tearing.\n\nThe optional argument io may take a tuple (inputs, outputs). This will convert all inputs to parameters and allow them to be unconnected, i.e., simplification will allow models where n_states = n_equations - n_inputs.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#ModelingToolkit.ode_order_lowering","page":"ODESystem","title":"ModelingToolkit.ode_order_lowering","text":"ode_order_lowering(sys::ODESystem) -> Any\n\n\nTakes a Nth order ODESystem and returns a new ODESystem written in first order form by defining new variables which represent the N-1 derivatives.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#ModelingToolkit.StructuralTransformations.dae_index_lowering","page":"ODESystem","title":"ModelingToolkit.StructuralTransformations.dae_index_lowering","text":"dae_index_lowering(sys::ODESystem; kwargs...) -> ODESystem\n\nPerform the Pantelides algorithm to transform a higher index DAE to an index 1 DAE. kwargs are forwarded to pantelides!. End users are encouraged to call structural_simplify instead, which calls this function internally.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#ModelingToolkit.liouville_transform","page":"ODESystem","title":"ModelingToolkit.liouville_transform","text":"liouville_transform(sys::ModelingToolkit.AbstractODESystem)\n\n\nGenerates the Liouville transformed set of ODEs, which is the original ODE system with a new variable trJ appended, corresponding to the -tr(Jacobian). This variable is used for properties like uncertainty propagation from a given initial distribution density.\n\nFor example, if u=p*u and p follows a probability distribution f(p), then the probability density of a future value with a given choice of p is computed by setting the initial trJ = f(p), and the final value of trJ is the probability of u(t).\n\nExample:\n\nusing ModelingToolkit, OrdinaryDiffEq, Test\n\n@parameters t α β γ δ\n@variables x(t) y(t)\nD = Differential(t)\n\neqs = [D(x) ~ α*x - β*x*y,\n D(y) ~ -δ*y + γ*x*y]\n\nsys = ODESystem(eqs)\nsys2 = liouville_transform(sys)\n@variables trJ\n\nu0 = [x => 1.0,\n y => 1.0,\n trJ => 1.0]\n\nprob = ODEProblem(sys2,u0,tspan,p)\nsol = solve(prob,Tsit5())\n\nWhere sol[3,:] is the evolution of trJ over time.\n\nSources:\n\nProbabilistic Robustness Analysis of F-16 Controller Performance: An Optimal Transport Approach\n\nAbhishek Halder, Kooktae Lee, and Raktim Bhattacharya https://abhishekhalder.bitbucket.io/F16ACC2013Final.pdf\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#ModelingToolkit.StructuralTransformations.tearing","page":"ODESystem","title":"ModelingToolkit.StructuralTransformations.tearing","text":"tearing(sys; simplify=false)\n\nTear the nonlinear equations in system. When simplify=true, we simplify the new residual equations after tearing. End users are encouraged to call structural_simplify instead, which calls this function internally.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#Analyses","page":"ODESystem","title":"Analyses","text":"","category":"section"},{"location":"systems/ODESystem/","page":"ODESystem","title":"ODESystem","text":"ModelingToolkit.islinear\nModelingToolkit.isautonomous\nModelingToolkit.isaffine","category":"page"},{"location":"systems/ODESystem/#Applicable-Calculation-and-Generation-Functions","page":"ODESystem","title":"Applicable Calculation and Generation Functions","text":"","category":"section"},{"location":"systems/ODESystem/","page":"ODESystem","title":"ODESystem","text":"calculate_jacobian\ncalculate_tgrad\ncalculate_factorized_W\ngenerate_jacobian\ngenerate_tgrad\ngenerate_factorized_W\njacobian_sparsity","category":"page"},{"location":"systems/ODESystem/#ModelingToolkit.calculate_jacobian-systems-ODESystem","page":"ODESystem","title":"ModelingToolkit.calculate_jacobian","text":"calculate_jacobian(sys::AbstractSystem)\n\nCalculate the Jacobian matrix of a system.\n\nReturns a matrix of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#ModelingToolkit.calculate_tgrad-systems-ODESystem","page":"ODESystem","title":"ModelingToolkit.calculate_tgrad","text":"calculate_tgrad(sys::AbstractTimeDependentSystem)\n\nCalculate the time gradient of a system.\n\nReturns a vector of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#ModelingToolkit.calculate_factorized_W-systems-ODESystem","page":"ODESystem","title":"ModelingToolkit.calculate_factorized_W","text":"calculate_factorized_W(sys::AbstractSystem)\n\nCalculate the factorized W-matrix of a system.\n\nReturns a matrix of Num instances. The result from the first call will be cached in the system object.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#ModelingToolkit.generate_jacobian-systems-ODESystem","page":"ODESystem","title":"ModelingToolkit.generate_jacobian","text":"generate_jacobian(sys::AbstractSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; sparse = false, kwargs...)\n\nGenerates a function for the Jacobian matrix of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#ModelingToolkit.generate_tgrad-systems-ODESystem","page":"ODESystem","title":"ModelingToolkit.generate_tgrad","text":"generate_tgrad(sys::AbstractTimeDependentSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; kwargs...)\n\nGenerates a function for the time gradient of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#ModelingToolkit.generate_factorized_W-systems-ODESystem","page":"ODESystem","title":"ModelingToolkit.generate_factorized_W","text":"generate_factorized_W(sys::AbstractSystem, dvs = states(sys), ps = parameters(sys),\n expression = Val{true}; sparse = false, kwargs...)\n\nGenerates a function for the factorized W matrix of a system. Extra arguments control the arguments to the internal build_function call.\n\n\n\n\n\n","category":"function"},{"location":"systems/ODESystem/#Standard-Problem-Constructors","page":"ODESystem","title":"Standard Problem Constructors","text":"","category":"section"},{"location":"systems/ODESystem/","page":"ODESystem","title":"ODESystem","text":"ODEFunction(sys::ModelingToolkit.AbstractODESystem, args...)\nODEProblem(sys::ModelingToolkit.AbstractODESystem, args...)\nSteadyStateProblem(sys::ModelingToolkit.AbstractODESystem, args...)","category":"page"},{"location":"systems/ODESystem/#SciMLBase.ODEFunction-Tuple{ModelingToolkit.AbstractODESystem, Vararg{Any}}","page":"ODESystem","title":"SciMLBase.ODEFunction","text":"DiffEqBase.ODEFunction{iip}(sys::AbstractODESystem, dvs = states(sys),\n ps = parameters(sys);\n version = nothing, tgrad = false,\n jac = false,\n sparse = false,\n kwargs...) where {iip}\n\nCreate an ODEFunction from the ODESystem. The arguments dvs and ps are used to set the order of the dependent variable and parameter vectors, respectively.\n\n\n\n\n\n","category":"method"},{"location":"systems/ODESystem/#SciMLBase.ODEProblem-Tuple{ModelingToolkit.AbstractODESystem, Vararg{Any}}","page":"ODESystem","title":"SciMLBase.ODEProblem","text":"DiffEqBase.ODEProblem{iip}(sys::AbstractODESystem, u0map, tspan,\n parammap = DiffEqBase.NullParameters();\n version = nothing, tgrad = false,\n jac = false,\n checkbounds = false, sparse = false,\n simplify = false,\n linenumbers = true, parallel = SerialForm(),\n kwargs...) where {iip}\n\nGenerates an ODEProblem from an ODESystem and allows for automatically symbolically calculating numerical enhancements.\n\n\n\n\n\n","category":"method"},{"location":"systems/ODESystem/#SciMLBase.SteadyStateProblem-Tuple{ModelingToolkit.AbstractODESystem, Vararg{Any}}","page":"ODESystem","title":"SciMLBase.SteadyStateProblem","text":"SciMLBase.SteadyStateProblem(sys::AbstractODESystem, u0map,\n parammap = DiffEqBase.NullParameters();\n version = nothing, tgrad = false,\n jac = false,\n checkbounds = false, sparse = false,\n linenumbers = true, parallel = SerialForm(),\n kwargs...) where {iip}\n\nGenerates an SteadyStateProblem from an ODESystem and allows for automatically symbolically calculating numerical enhancements.\n\n\n\n\n\n","category":"method"},{"location":"systems/ODESystem/#Torn-Problem-Constructors","page":"ODESystem","title":"Torn Problem Constructors","text":"","category":"section"},{"location":"systems/ODESystem/","page":"ODESystem","title":"ODESystem","text":"ODAEProblem","category":"page"},{"location":"systems/ODESystem/#ModelingToolkit.StructuralTransformations.ODAEProblem","page":"ODESystem","title":"ModelingToolkit.StructuralTransformations.ODAEProblem","text":"ODAEProblem{iip}(sys, u0map, tspan, parammap = DiffEqBase.NullParameters(); kw...)\n\nThis constructor acts similar to the one for ODEProblem with the following changes: ODESystems can sometimes be further reduced if structural_simplify has already been applied to them. In these cases, the constructor uses the knowledge of the strongly connected components calculated during the process of simplification as the basis for building pre-simplified nonlinear systems in the implicit solving.\n\nIn summary: these problems are structurally modified, but could be more efficient and more stable. Note, the returned object is still of type ODEProblem.\n\n\n\n\n\n","category":"type"},{"location":"systems/ODESystem/#Expression-Constructors","page":"ODESystem","title":"Expression Constructors","text":"","category":"section"},{"location":"systems/ODESystem/","page":"ODESystem","title":"ODESystem","text":"ODEFunctionExpr\nDAEFunctionExpr\nSteadyStateProblemExpr","category":"page"},{"location":"systems/ODESystem/#ModelingToolkit.ODEFunctionExpr","page":"ODESystem","title":"ModelingToolkit.ODEFunctionExpr","text":"ODEFunctionExpr{iip}(sys::AbstractODESystem, dvs = states(sys),\n ps = parameters(sys);\n version = nothing, tgrad = false,\n jac = false,\n sparse = false,\n kwargs...) where {iip}\n\nCreate a Julia expression for an ODEFunction from the ODESystem. The arguments dvs and ps are used to set the order of the dependent variable and parameter vectors, respectively.\n\n\n\n\n\n","category":"type"},{"location":"systems/ODESystem/#ModelingToolkit.DAEFunctionExpr","page":"ODESystem","title":"ModelingToolkit.DAEFunctionExpr","text":"DAEFunctionExpr{iip}(sys::AbstractODESystem, dvs = states(sys),\n ps = parameters(sys);\n version = nothing, tgrad = false,\n jac = false,\n sparse = false,\n kwargs...) where {iip}\n\nCreate a Julia expression for an ODEFunction from the ODESystem. The arguments dvs and ps are used to set the order of the dependent variable and parameter vectors, respectively.\n\n\n\n\n\n","category":"type"},{"location":"systems/ODESystem/#ModelingToolkit.SteadyStateProblemExpr","page":"ODESystem","title":"ModelingToolkit.SteadyStateProblemExpr","text":"SciMLBase.SteadyStateProblemExpr(sys::AbstractODESystem, u0map,\n parammap = DiffEqBase.NullParameters();\n version = nothing, tgrad = false,\n jac = false,\n checkbounds = false, sparse = false,\n skipzeros = true, fillzeros = true,\n linenumbers = true, parallel = SerialForm(),\n kwargs...) where {iip}\n\nGenerates a Julia expression for building a SteadyStateProblem from an ODESystem and allows for automatically symbolically calculating numerical enhancements.\n\n\n\n\n\n","category":"type"},{"location":"examples/spring_mass/#Component-Based-Modeling-of-a-Spring-Mass-System","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"","category":"section"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"In this tutorial, we will build a simple component-based model of a spring-mass system. A spring-mass system consists of one or more masses connected by springs. Hooke's law gives the force exerted by a spring when it is extended or compressed by a given distance. This specifies a differential-equation system where the acceleration of the masses is specified using the forces acting on them.","category":"page"},{"location":"examples/spring_mass/#Copy-Paste-Example","page":"Component-Based Modeling of a Spring-Mass System","title":"Copy-Paste Example","text":"","category":"section"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"using ModelingToolkit, Plots, DifferentialEquations, LinearAlgebra\nusing Symbolics: scalarize\n\n@variables t\nD = Differential(t)\n\nfunction Mass(; name, m = 1.0, xy = [0.0, 0.0], u = [0.0, 0.0])\n ps = @parameters m = m\n sts = @variables pos(t)[1:2]=xy v(t)[1:2]=u\n eqs = scalarize(D.(pos) .~ v)\n ODESystem(eqs, t, [pos..., v...], ps; name)\nend\n\nfunction Spring(; name, k = 1e4, l = 1.0)\n ps = @parameters k=k l=l\n @variables x(t), dir(t)[1:2]\n ODESystem(Equation[], t, [x, dir...], ps; name)\nend\n\nfunction connect_spring(spring, a, b)\n [spring.x ~ norm(scalarize(a .- b))\n scalarize(spring.dir .~ scalarize(a .- b))]\nend\n\nfunction spring_force(spring)\n -spring.k .* scalarize(spring.dir) .* (spring.x - spring.l) ./ spring.x\nend\n\nm = 1.0\nxy = [1.0, -1.0]\nk = 1e4\nl = 1.0\ncenter = [0.0, 0.0]\ng = [0.0, -9.81]\n@named mass = Mass(m = m, xy = xy)\n@named spring = Spring(k = k, l = l)\n\neqs = [connect_spring(spring, mass.pos, center)\n scalarize(D.(mass.v) .~ spring_force(spring) / mass.m .+ g)]\n\n@named _model = ODESystem(eqs, t, [spring.x; spring.dir; mass.pos], [])\n@named model = compose(_model, mass, spring)\nsys = structural_simplify(model)\n\nprob = ODEProblem(sys, [], (0.0, 3.0))\nsol = solve(prob, Rosenbrock23())\nplot(sol)","category":"page"},{"location":"examples/spring_mass/#Explanation","page":"Component-Based Modeling of a Spring-Mass System","title":"Explanation","text":"","category":"section"},{"location":"examples/spring_mass/#Building-the-components","page":"Component-Based Modeling of a Spring-Mass System","title":"Building the components","text":"","category":"section"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"For each component, we use a Julia function that returns an ODESystem. At the top, we define the fundamental properties of a Mass: it has a mass m, a position pos and a velocity v. We also define that the velocity is the rate of change of position with respect to time.","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"function Mass(; name, m = 1.0, xy = [0.0, 0.0], u = [0.0, 0.0])\n ps = @parameters m = m\n sts = @variables pos(t)[1:2]=xy v(t)[1:2]=u\n eqs = scalarize(D.(pos) .~ v)\n ODESystem(eqs, t, [pos..., v...], ps; name)\nend","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"Note that this is an incompletely specified ODESystem. It cannot be simulated on its own, since the equations for the velocity v[1:2](t) are unknown. Notice the addition of a name keyword. This allows us to generate different masses with different names. A Mass can now be constructed as:","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"Mass(name = :mass1)","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"Or using the @named helper macro","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"@named mass1 = Mass()","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"Next, we build the spring component. It is characterized by the spring constant k and the length l of the spring when no force is applied to it. The state of a spring is defined by its current length and direction.","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"function Spring(; name, k = 1e4, l = 1.0)\n ps = @parameters k=k l=l\n @variables x(t), dir(t)[1:2]\n ODESystem(Equation[], t, [x, dir...], ps; name)\nend","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"We now define functions that help construct the equations for a mass-spring system. First, the connect_spring function connects a spring between two positions a and b. Note that a and b can be the pos of a Mass, or just a fixed position such as [0., 0.]. In that sense, the length of the spring x is given by the length of the vector dir joining a and b.","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"function connect_spring(spring, a, b)\n [spring.x ~ norm(scalarize(a .- b))\n scalarize(spring.dir .~ scalarize(a .- b))]\nend","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"Lastly, we define the spring_force function that takes a spring and returns the force exerted by this spring.","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"function spring_force(spring)\n -spring.k .* scalarize(spring.dir) .* (spring.x - spring.l) ./ spring.x\nend","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"To create our system, we will first create the components: a mass and a spring. This is done as follows:","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"m = 1.0\nxy = [1.0, -1.0]\nk = 1e4\nl = 1.0\ncenter = [0.0, 0.0]\ng = [0.0, -9.81]\n@named mass = Mass(m = m, xy = xy)\n@named spring = Spring(k = k, l = l)","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"We can now create the equations describing this system, by connecting spring to mass and a fixed point.","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"eqs = [connect_spring(spring, mass.pos, center)\n scalarize(D.(mass.v) .~ spring_force(spring) / mass.m .+ g)]","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"Finally, we can build the model using these equations and components.","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"@named _model = ODESystem(eqs, t, [spring.x; spring.dir; mass.pos], [])\n@named model = compose(_model, mass, spring)","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"We can take a look at the equations in the model using the equations function.","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"equations(model)","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"The states of this model are:","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"states(model)","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"And the parameters of this model are:","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"parameters(model)","category":"page"},{"location":"examples/spring_mass/#Simplifying-and-solving-this-system","page":"Component-Based Modeling of a Spring-Mass System","title":"Simplifying and solving this system","text":"","category":"section"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"This system can be solved directly as a DAE using one of the DAE solvers from DifferentialEquations.jl. However, we can symbolically simplify the system first beforehand. Running structural_simplify eliminates unnecessary variables from the model to give the leanest numerical representation of the system.","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"sys = structural_simplify(model)\nequations(sys)","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"We are left with only 4 equations involving 4 state variables (mass.pos[1], mass.pos[2], mass.v[1], mass.v[2]). We can solve the system by converting it to an ODEProblem. Some observed variables are not expanded by default. To view the complete equations, one can do","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"full_equations(sys)","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"This is done as follows:","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"prob = ODEProblem(sys, [], (0.0, 3.0))\nsol = solve(prob, Rosenbrock23())\nplot(sol)","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"What if we want the timeseries of a different variable? That information is not lost! Instead, structural_simplify simply changes state variables into observed variables.","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"observed(sys)","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"These are explicit algebraic equations which can be used to reconstruct the required variables on the fly. This leads to dramatic computational savings since implicitly solving an ODE scales as O(n^3), so fewer states are significantly better!","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"We can access these variables using the solution object. For example, let's retrieve the x-position of the mass over time:","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"sol[mass.pos[1]]","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"We can also plot the path of the mass:","category":"page"},{"location":"examples/spring_mass/","page":"Component-Based Modeling of a Spring-Mass System","title":"Component-Based Modeling of a Spring-Mass System","text":"plot(sol, idxs = (mass.pos[1], mass.pos[2]))","category":"page"},{"location":"basics/DependencyGraphs/#Dependency-Graphs","page":"Dependency Graphs","title":"Dependency Graphs","text":"","category":"section"},{"location":"basics/DependencyGraphs/#Types","page":"Dependency Graphs","title":"Types","text":"","category":"section"},{"location":"basics/DependencyGraphs/","page":"Dependency Graphs","title":"Dependency Graphs","text":"BipartiteGraph","category":"page"},{"location":"basics/DependencyGraphs/#ModelingToolkit.BipartiteGraphs.BipartiteGraph","page":"Dependency Graphs","title":"ModelingToolkit.BipartiteGraphs.BipartiteGraph","text":"mutable struct BipartiteGraph{I<:Integer, M} <: Graphs.AbstractGraph{I<:Integer}\n\nA bipartite graph representation between two, possibly distinct, sets of vertices (source and dependencies). Maps source vertices, labelled 1:N₁, to vertices on which they depend (labelled 1:N₂).\n\nFields\n\nne\nfadjlist\nbadjlist\nmetadata\n\nExample\n\nusing ModelingToolkit\n\nne = 4\nsrcverts = 1:4\ndepverts = 1:2\n\n# six source vertices\nfadjlist = [[1],[1],[2],[2],[1],[1,2]]\n\n# two vertices they depend on\nbadjlist = [[1,2,5,6],[3,4,6]]\n\nbg = BipartiteGraph(7, fadjlist, badjlist)\n\n\n\n\n\n","category":"type"},{"location":"basics/DependencyGraphs/#Utility-functions-for-BiPartiteGraphs","page":"Dependency Graphs","title":"Utility functions for BiPartiteGraphs","text":"","category":"section"},{"location":"basics/DependencyGraphs/","page":"Dependency Graphs","title":"Dependency Graphs","text":"Base.isequal","category":"page"},{"location":"basics/DependencyGraphs/#Base.isequal","page":"Dependency Graphs","title":"Base.isequal","text":"Base.isequal(bg1::BipartiteGraph{T}, bg2::BipartiteGraph{T}) where {T <: Integer}\n\nTest whether two BipartiteGraphs are equal.\n\n\n\n\n\n","category":"function"},{"location":"basics/DependencyGraphs/#Functions-for-calculating-dependency-graphs","page":"Dependency Graphs","title":"Functions for calculating dependency graphs","text":"","category":"section"},{"location":"basics/DependencyGraphs/","page":"Dependency Graphs","title":"Dependency Graphs","text":"equation_dependencies\nasgraph\nvariable_dependencies\nasdigraph\neqeq_dependencies\nvarvar_dependencies","category":"page"},{"location":"basics/DependencyGraphs/#ModelingToolkit.equation_dependencies","page":"Dependency Graphs","title":"ModelingToolkit.equation_dependencies","text":"equation_dependencies(sys::AbstractSystem; variables = states(sys))\n\nGiven an AbstractSystem calculate for each equation the variables it depends on.\n\nNotes:\n\nVariables that are not in variables are filtered out.\nget_variables! is used to determine the variables within a given equation.\nreturns a Vector{Vector{Variable}}() mapping the index of an equation to the variables it depends on.\n\nExample:\n\nusing ModelingToolkit\n@parameters β γ κ η\n@variables t S(t) I(t) R(t)\n\nrate₁ = β * S * I\nrate₂ = γ * I + t\naffect₁ = [S ~ S - 1, I ~ I + 1]\naffect₂ = [I ~ I - 1, R ~ R + 1]\nj₁ = ModelingToolkit.ConstantRateJump(rate₁, affect₁)\nj₂ = ModelingToolkit.VariableRateJump(rate₂, affect₂)\n\n# create a JumpSystem using these jumps\n@named jumpsys = JumpSystem([j₁, j₂], t, [S, I, R], [β, γ])\n\n# dependency of each jump rate function on state variables\nequation_dependencies(jumpsys)\n\n# dependency of each jump rate function on parameters\nequation_dependencies(jumpsys, variables = parameters(jumpsys))\n\n\n\n\n\n","category":"function"},{"location":"basics/DependencyGraphs/#ModelingToolkit.asgraph","page":"Dependency Graphs","title":"ModelingToolkit.asgraph","text":"asgraph(eqdeps, vtois)\n\nConvert a collection of equation dependencies, for example as returned by equation_dependencies, to a BipartiteGraph.\n\nNotes:\n\nvtois should provide a Dict like mapping from each Variable dependency in eqdeps to the integer idx of the variable to use in the graph.\n\nExample: Continuing the example started in equation_dependencies\n\ndigr = asgraph(equation_dependencies(jumpsys),\n Dict(s => i for (i, s) in enumerate(states(jumpsys))))\n\n\n\n\n\nasgraph(sys::AbstractSystem; variables = states(sys),\n variablestoids = Dict(convert(Variable, v) => i for (i, v) in enumerate(variables)))\n\nConvert an AbstractSystem to a BipartiteGraph mapping the index of equations to the indices of variables they depend on.\n\nNotes:\n\nDefaults for kwargs creating a mapping from equations(sys) to states(sys) they depend on.\nvariables should provide the list of variables to use for generating the dependency graph.\nvariablestoids should provide Dict like mapping from a Variable to its Int index within variables.\n\nExample: Continuing the example started in equation_dependencies\n\ndigr = asgraph(jumpsys)\n\n\n\n\n\n","category":"function"},{"location":"basics/DependencyGraphs/#ModelingToolkit.variable_dependencies","page":"Dependency Graphs","title":"ModelingToolkit.variable_dependencies","text":"variable_dependencies(sys::AbstractSystem; variables = states(sys),\n variablestoids = nothing)\n\nFor each variable, determine the equations that modify it and return as a BipartiteGraph.\n\nNotes:\n\nDependencies are returned as a BipartiteGraph mapping variable indices to the indices of equations that modify them.\nvariables denotes the list of variables to determine dependencies for.\nvariablestoids denotes a Dict mapping Variables to their Int index in variables.\n\nExample: Continuing the example of equation_dependencies\n\nvariable_dependencies(jumpsys)\n\n\n\n\n\n","category":"function"},{"location":"basics/DependencyGraphs/#ModelingToolkit.asdigraph","page":"Dependency Graphs","title":"ModelingToolkit.asdigraph","text":"asdigraph(g::BipartiteGraph, sys::AbstractSystem; variables = states(sys),\n equationsfirst = true)\n\nConvert a BipartiteGraph to a LightGraph.SimpleDiGraph.\n\nNotes:\n\nThe resulting SimpleDiGraph unifies the two sets of vertices (equations and then states in the case it comes from asgraph), producing one ordered set of integer vertices (SimpleDiGraph does not support two distinct collections of vertices, so they must be merged).\nvariables gives the variables that g are associated with (usually the states of a system).\nequationsfirst (default is true) gives whether the BipartiteGraph gives a mapping from equations to variables they depend on (true), as calculated by asgraph, or whether it gives a mapping from variables to the equations that modify them, as calculated by variable_dependencies.\n\nExample: Continuing the example in asgraph\n\ndg = asdigraph(digr, jumpsys)\n\n\n\n\n\n","category":"function"},{"location":"basics/DependencyGraphs/#ModelingToolkit.eqeq_dependencies","page":"Dependency Graphs","title":"ModelingToolkit.eqeq_dependencies","text":"eqeq_dependencies(eqdeps::BipartiteGraph{T},\n vardeps::BipartiteGraph{T}) where {T <: Integer}\n\nCalculate a LightGraph.SimpleDiGraph that maps each equation to equations they depend on.\n\nNotes:\n\nThe fadjlist of the SimpleDiGraph maps from an equation to the equations that modify variables it depends on.\nThe badjlist of the SimpleDiGraph maps from an equation to equations that depend on variables it modifies.\n\nExample: Continuing the example of equation_dependencies\n\neqeqdep = eqeq_dependencies(asgraph(jumpsys), variable_dependencies(jumpsys))\n\n\n\n\n\n","category":"function"},{"location":"basics/DependencyGraphs/#ModelingToolkit.varvar_dependencies","page":"Dependency Graphs","title":"ModelingToolkit.varvar_dependencies","text":"function varvar_dependencies(eqdeps::BipartiteGraph{T},\n vardeps::BipartiteGraph{T}) where {T <: Integer}\n eqeq_dependencies(vardeps, eqdeps)\nend\n\nCalculate a LightGraph.SimpleDiGraph that maps each variable to variables they depend on.\n\nNotes:\n\nThe fadjlist of the SimpleDiGraph maps from a variable to the variables that depend on it.\nThe badjlist of the SimpleDiGraph maps from a variable to variables on which it depends.\n\nExample: Continuing the example of equation_dependencies\n\nvarvardep = varvar_dependencies(asgraph(jumpsys), variable_dependencies(jumpsys))\n\n\n\n\n\n","category":"function"},{"location":"tutorials/nonlinear/#Modeling-Nonlinear-Systems","page":"Modeling Nonlinear Systems","title":"Modeling Nonlinear Systems","text":"","category":"section"},{"location":"tutorials/nonlinear/","page":"Modeling Nonlinear Systems","title":"Modeling Nonlinear Systems","text":"In this example, we will go one step deeper and showcase the direct function generation capabilities in ModelingToolkit.jl to build nonlinear systems. Let's say we wanted to solve for the steady state of an ODE. This steady state is reached when the nonlinear system of differential equations equals zero. We use (unknown) variables for our nonlinear system.","category":"page"},{"location":"tutorials/nonlinear/","page":"Modeling Nonlinear Systems","title":"Modeling Nonlinear Systems","text":"using ModelingToolkit, NonlinearSolve\n\n@variables x y z\n@parameters σ ρ β\n\n# Define a nonlinear system\neqs = [0 ~ σ * (y - x),\n 0 ~ x * (ρ - z) - y,\n 0 ~ x * y - β * z]\n@named ns = NonlinearSystem(eqs, [x, y, z], [σ, ρ, β])\n\nguess = [x => 1.0,\n y => 0.0,\n z => 0.0]\n\nps = [σ => 10.0\n ρ => 26.0\n β => 8 / 3]\n\nprob = NonlinearProblem(ns, guess, ps)\nsol = solve(prob, NewtonRaphson())","category":"page"},{"location":"tutorials/nonlinear/","page":"Modeling Nonlinear Systems","title":"Modeling Nonlinear Systems","text":"We can similarly ask to generate the NonlinearProblem with the analytical Jacobian function:","category":"page"},{"location":"tutorials/nonlinear/","page":"Modeling Nonlinear Systems","title":"Modeling Nonlinear Systems","text":"prob = NonlinearProblem(ns, guess, ps, jac = true)\nsol = solve(prob, NewtonRaphson())","category":"page"},{"location":"systems/JumpSystem/#JumpSystem","page":"JumpSystem","title":"JumpSystem","text":"","category":"section"},{"location":"systems/JumpSystem/#System-Constructors","page":"JumpSystem","title":"System Constructors","text":"","category":"section"},{"location":"systems/JumpSystem/","page":"JumpSystem","title":"JumpSystem","text":"JumpSystem","category":"page"},{"location":"systems/JumpSystem/#ModelingToolkit.JumpSystem","page":"JumpSystem","title":"ModelingToolkit.JumpSystem","text":"struct JumpSystem{U<:RecursiveArrayTools.ArrayPartition} <: AbstractTimeDependentSystem\n\nA system of jump processes.\n\nFields\n\ntag: A tag for the system. If two systems have the same tag, then they are structurally identical.\n\neqs: The jumps of the system. Allowable types are ConstantRateJump, VariableRateJump, MassActionJump.\n\niv: The independent variable, usually time.\nstates: The dependent variables, representing the state of the system. Must not contain the independent variable.\nps: The parameters of the system. Must not contain the independent variable.\nvar_to_name: Array variables.\nobserved: Observed states.\nname: The name of the system.\nsystems: The internal systems. These are required to have unique names.\ndefaults: The default values to use when initial conditions and/or parameters are not supplied in ODEProblem.\n\nconnector_type: Type of the system.\n\ndiscrete_events: A Vector{SymbolicDiscreteCallback} that models events. Symbolic analog to SciMLBase.DiscreteCallback that executes an affect when a given condition is true at the end of an integration step. Note, one must make sure to call reset_aggregated_jumps!(integrator) if using a custom affect function that changes any state value or parameter.\n\nmetadata: Metadata for the system, to be used by downstream packages.\n\ngui_metadata: Metadata for MTK GUI.\n\ncomplete: If a model sys is complete, then sys.x no longer performs namespacing.\n\nExample\n\nusing ModelingToolkit, JumpProcesses\n\n@parameters β γ\n@variables t S(t) I(t) R(t)\nrate₁ = β*S*I\naffect₁ = [S ~ S - 1, I ~ I + 1]\nrate₂ = γ*I\naffect₂ = [I ~ I - 1, R ~ R + 1]\nj₁ = ConstantRateJump(rate₁,affect₁)\nj₂ = ConstantRateJump(rate₂,affect₂)\nj₃ = MassActionJump(2*β+γ, [R => 1], [S => 1, R => -1])\n@named js = JumpSystem([j₁,j₂,j₃], t, [S,I,R], [β,γ])\n\n\n\n\n\n","category":"type"},{"location":"systems/JumpSystem/#Composition-and-Accessor-Functions","page":"JumpSystem","title":"Composition and Accessor Functions","text":"","category":"section"},{"location":"systems/JumpSystem/","page":"JumpSystem","title":"JumpSystem","text":"get_eqs(sys) or equations(sys): The equations that define the jump system.\nget_states(sys) or states(sys): The set of states in the jump system.\nget_ps(sys) or parameters(sys): The parameters of the jump system.\nget_iv(sys): The independent variable of the jump system.","category":"page"},{"location":"systems/JumpSystem/#Transformations","page":"JumpSystem","title":"Transformations","text":"","category":"section"},{"location":"systems/JumpSystem/","page":"JumpSystem","title":"JumpSystem","text":"structural_simplify","category":"page"},{"location":"systems/JumpSystem/#ModelingToolkit.structural_simplify-systems-JumpSystem","page":"JumpSystem","title":"ModelingToolkit.structural_simplify","text":"structural_simplify(sys; ...)\nstructural_simplify(sys, io; simplify, kwargs...)\n\n\nStructurally simplify algebraic equations in a system and compute the topological sort of the observed equations. When simplify=true, the simplify function will be applied during the tearing process. It also takes kwargs allow_symbolic=false and allow_parameter=true which limits the coefficient types during tearing.\n\nThe optional argument io may take a tuple (inputs, outputs). This will convert all inputs to parameters and allow them to be unconnected, i.e., simplification will allow models where n_states = n_equations - n_inputs.\n\n\n\n\n\n","category":"function"},{"location":"systems/JumpSystem/#Analyses","page":"JumpSystem","title":"Analyses","text":"","category":"section"},{"location":"systems/JumpSystem/#Problem-Constructors","page":"JumpSystem","title":"Problem Constructors","text":"","category":"section"},{"location":"systems/JumpSystem/","page":"JumpSystem","title":"JumpSystem","text":"DiscreteProblem(sys::ModelingToolkit.DiscreteSystem, u0map, tspan)","category":"page"},{"location":"systems/JumpSystem/#SciMLBase.DiscreteProblem-Tuple{DiscreteSystem, Any, Any}-systems-JumpSystem","page":"JumpSystem","title":"SciMLBase.DiscreteProblem","text":"DiscreteProblem(sys::DiscreteSystem; ...) -> Any\nDiscreteProblem(sys::DiscreteSystem, u0map; ...) -> Any\nDiscreteProblem(\n sys::DiscreteSystem,\n u0map,\n tspan;\n ...\n) -> Any\nDiscreteProblem(\n sys::DiscreteSystem,\n u0map,\n tspan,\n parammap;\n eval_module,\n eval_expression,\n use_union,\n kwargs...\n) -> Any\n\n\nGenerates an DiscreteProblem from an DiscreteSystem.\n\n\n\n\n\n","category":"method"},{"location":"systems/JumpSystem/","page":"JumpSystem","title":"JumpSystem","text":"JumpProblem(sys::JumpSystem, prob, aggregator)","category":"page"},{"location":"systems/JumpSystem/#JumpProcesses.JumpProblem-Tuple{JumpSystem, Any, Any}","page":"JumpSystem","title":"JumpProcesses.JumpProblem","text":"DiffEqBase.JumpProblem(js::JumpSystem, prob, aggregator; kwargs...)\n\nGenerates a JumpProblem from a JumpSystem.\n\nContinuing the example from the DiscreteProblem definition:\n\njprob = JumpProblem(js, dprob, Direct())\nsol = solve(jprob, SSAStepper())\n\n\n\n\n\n","category":"method"},{"location":"basics/ContextualVariables/#Contextual-Variable-Types","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"","category":"section"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"ModelingToolkit.jl has a system of contextual variable types which allows for helping the system transformation machinery do complex manipulations and automatic detection. The standard variable definition in ModelingToolkit.jl is the @variable which is defined by Symbolics.jl. For example:","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"@variables x y(x)","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"This is used for the “normal” variable of a given system, like the states of a differential equation or objective function. All the macros below support the same syntax as @variables.","category":"page"},{"location":"basics/ContextualVariables/#Parameters","page":"Contextual Variable Types","title":"Parameters","text":"","category":"section"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"All modeling projects have some form of parameters. @parameters marks a variable as being the parameter of some system, which allows automatic detection algorithms to ignore such variables when attempting to find the states of a system.","category":"page"},{"location":"basics/ContextualVariables/#Constants","page":"Contextual Variable Types","title":"Constants","text":"","category":"section"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"Constants, defined by e.g. @constants myconst1 are like parameters that:","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"always have a default value, which must be assigned when the constants are declared\ndo not show up in the list of parameters of a system.","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"The intended use-cases for constants are:","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"representing literals (e.g., π) symbolically, which results in cleaner Latexification of equations (avoids turning d ~ 2π*r into d = 6.283185307179586 r)\nallowing auto-generated unit conversion factors to live outside the list of parameters\nrepresenting fundamental constants (e.g., speed of light c) that should never be adjusted inadvertently.","category":"page"},{"location":"basics/ContextualVariables/#Wildcard-Variable-Arguments","page":"Contextual Variable Types","title":"Wildcard Variable Arguments","text":"","category":"section"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"@variables u(..)","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"It is possible to define a dependent variable which is an open function as above, for which its arguments must be specified each time it is used. This is useful with PDEs for example, where one may need to use u(t, x) in the equations, but will need to be able to write u(t, 0.0) to define a boundary condition at x = 0.","category":"page"},{"location":"basics/ContextualVariables/#Variable-metadata","page":"Contextual Variable Types","title":"Variable metadata","text":"","category":"section"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"In many engineering systems, some variables act like “flows” while others do not. For example, in circuit models you have current which flows, and the related voltage which does not. Or in thermal models you have heat flows. In these cases, the connect statement enforces conservation of flow between all of the connected components.","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"For example, the following specifies that x is a 2x2 matrix of flow variables with the unit m^3/s:","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"@variables x[1:2, 1:2] [connect = Flow; unit = u\"m^3/s\"]","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"ModelingToolkit defines connect, unit, noise, and description keys for the metadata. One can get and set metadata by","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"julia> @variables x [unit = u\"m^3/s\"];\n\njulia> hasmetadata(x, VariableUnit)\ntrue\n\njulia> ModelingToolkit.get_unit(x)\nm³ s⁻¹\n\njulia> x = setmetadata(x, VariableUnit, u\"m/s\")\nx\n\njulia> ModelingToolkit.get_unit(x)\nm s⁻¹","category":"page"},{"location":"basics/ContextualVariables/","page":"Contextual Variable Types","title":"Contextual Variable Types","text":"See Symbolic Metadata for more details on variable metadata.","category":"page"},{"location":"systems/DiscreteSystem/#DiscreteSystem","page":"DiscreteSystem","title":"DiscreteSystem","text":"","category":"section"},{"location":"systems/DiscreteSystem/#System-Constructors","page":"DiscreteSystem","title":"System Constructors","text":"","category":"section"},{"location":"systems/DiscreteSystem/","page":"DiscreteSystem","title":"DiscreteSystem","text":"DiscreteSystem","category":"page"},{"location":"systems/DiscreteSystem/#ModelingToolkit.DiscreteSystem","page":"DiscreteSystem","title":"ModelingToolkit.DiscreteSystem","text":"struct DiscreteSystem <: AbstractTimeDependentSystem\n\nA system of difference equations.\n\nFields\n\ntag: A tag for the system. If two systems have the same tag, then they are structurally identical.\n\neqs: The differential equations defining the discrete system.\niv: Independent variable.\nstates: Dependent (state) variables. Must not contain the independent variable.\nps: Parameter variables. Must not contain the independent variable.\ntspan: Time span.\nvar_to_name: Array variables.\nctrls: Control parameters (some subset of ps).\nobserved: Observed states.\nname: The name of the system\n\nsystems: The internal systems. These are required to have unique names.\n\ndefaults: The default values to use when initial conditions and/or parameters are not supplied in DiscreteProblem.\n\npreface: Inject assignment statements before the evaluation of the RHS function.\n\nconnector_type: Type of the system.\n\nmetadata: Metadata for the system, to be used by downstream packages.\n\ngui_metadata: Metadata for MTK GUI.\n\ntearing_state: Cache for intermediate tearing state.\n\nsubstitutions: Substitutions generated by tearing.\n\ncomplete: If a model sys is complete, then sys.x no longer performs namespacing.\n\nparent: The hierarchical parent system before simplification.\n\nExample\n\nusing ModelingToolkit\n\n@parameters σ=28.0 ρ=10.0 β=8/3 δt=0.1\n@variables t x(t)=1.0 y(t)=0.0 z(t)=0.0\nD = Difference(t; dt=δt)\n\neqs = [D(x) ~ σ*(y-x),\n D(y) ~ x*(ρ-z)-y,\n D(z) ~ x*y - β*z]\n\n@named de = DiscreteSystem(eqs,t,[x,y,z],[σ,ρ,β]; tspan = (0, 1000.0)) # or\n@named de = DiscreteSystem(eqs)\n\n\n\n\n\n","category":"type"},{"location":"systems/DiscreteSystem/#Composition-and-Accessor-Functions","page":"DiscreteSystem","title":"Composition and Accessor Functions","text":"","category":"section"},{"location":"systems/DiscreteSystem/","page":"DiscreteSystem","title":"DiscreteSystem","text":"get_eqs(sys) or equations(sys): The equations that define the Discrete System.\nget_delay_val(sys): The delay of the Discrete System.\nget_iv(sys): The independent variable of the Discrete System.\nget_u0_p(sys, u0map, parammap) Numeric arrays for the initial condition and parameters given var => value maps.","category":"page"},{"location":"systems/DiscreteSystem/#Transformations","page":"DiscreteSystem","title":"Transformations","text":"","category":"section"},{"location":"systems/DiscreteSystem/#Analyses","page":"DiscreteSystem","title":"Analyses","text":"","category":"section"},{"location":"systems/DiscreteSystem/#Applicable-Calculation-and-Generation-Functions","page":"DiscreteSystem","title":"Applicable Calculation and Generation Functions","text":"","category":"section"},{"location":"systems/DiscreteSystem/#Standard-Problem-Constructors","page":"DiscreteSystem","title":"Standard Problem Constructors","text":"","category":"section"},{"location":"systems/DiscreteSystem/","page":"DiscreteSystem","title":"DiscreteSystem","text":"DiscreteFunction(sys::ModelingToolkit.DiscreteSystem, args...)\nDiscreteProblem(sys::ModelingToolkit.DiscreteSystem, u0map, tspan)","category":"page"},{"location":"systems/DiscreteSystem/#SciMLBase.DiscreteFunction-Tuple{DiscreteSystem, Vararg{Any}}","page":"DiscreteSystem","title":"SciMLBase.DiscreteFunction","text":"SciMLBase.DiscreteFunction{iip}(sys::DiscreteSystem, dvs = states(sys),\n ps = parameters(sys);\n version = nothing,\n kwargs...) where {iip}\n\nCreate a DiscreteFunction from the DiscreteSystem. The arguments dvs and ps are used to set the order of the dependent variable and parameter vectors, respectively.\n\n\n\n\n\n","category":"method"},{"location":"systems/DiscreteSystem/#SciMLBase.DiscreteProblem-Tuple{DiscreteSystem, Any, Any}","page":"DiscreteSystem","title":"SciMLBase.DiscreteProblem","text":"DiscreteProblem(sys::DiscreteSystem; ...) -> Any\nDiscreteProblem(sys::DiscreteSystem, u0map; ...) -> Any\nDiscreteProblem(\n sys::DiscreteSystem,\n u0map,\n tspan;\n ...\n) -> Any\nDiscreteProblem(\n sys::DiscreteSystem,\n u0map,\n tspan,\n parammap;\n eval_module,\n eval_expression,\n use_union,\n kwargs...\n) -> Any\n\n\nGenerates an DiscreteProblem from an DiscreteSystem.\n\n\n\n\n\n","category":"method"},{"location":"systems/DiscreteSystem/#Expression-Constructors","page":"DiscreteSystem","title":"Expression Constructors","text":"","category":"section"},{"location":"systems/DiscreteSystem/","page":"DiscreteSystem","title":"DiscreteSystem","text":"DiscreteFunctionExpr\nDiscreteProblemExpr","category":"page"},{"location":"systems/DiscreteSystem/#ModelingToolkit.DiscreteFunctionExpr","page":"DiscreteSystem","title":"ModelingToolkit.DiscreteFunctionExpr","text":"DiscreteFunctionExpr{iip}(sys::DiscreteSystem, dvs = states(sys),\n ps = parameters(sys);\n version = nothing,\n kwargs...) where {iip}\n\nCreate a Julia expression for an DiscreteFunction from the DiscreteSystem. The arguments dvs and ps are used to set the order of the dependent variable and parameter vectors, respectively.\n\n\n\n\n\n","category":"type"},{"location":"systems/DiscreteSystem/#ModelingToolkit.DiscreteProblemExpr","page":"DiscreteSystem","title":"ModelingToolkit.DiscreteProblemExpr","text":"DiffEqBase.DiscreteProblemExpr(sys::JumpSystem, u0map, tspan,\n parammap = DiffEqBase.NullParameters; kwargs...)\n\nGenerates a blank DiscreteProblem for a JumpSystem to utilize as its solving prob.prob. This is used in the case where there are no ODEs and no SDEs associated with the system.\n\nContinuing the example from the JumpSystem definition:\n\nusing DiffEqBase, JumpProcesses\nu₀map = [S => 999, I => 1, R => 0]\nparammap = [β => 0.1 / 1000, γ => 0.01]\ntspan = (0.0, 250.0)\ndprob = DiscreteProblem(js, u₀map, tspan, parammap)\n\n\n\n\n\n","category":"type"},{"location":"#ModelingToolkit.jl:-High-Performance-Symbolic-Numeric-Equation-Based-Modeling","page":"Home","title":"ModelingToolkit.jl: High-Performance Symbolic-Numeric Equation-Based Modeling","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"ModelingToolkit.jl is a modeling language for high-performance symbolic-numeric computation in scientific computing and scientific machine learning. It then mixes ideas from symbolic computational algebra systems with causal and acausal equation-based modeling frameworks to give an extendable and parallel modeling system. It allows for users to give a high-level description of a model for symbolic preprocessing to analyze and enhance the model. Automatic symbolic transformations, such as index reduction of differential-algebraic equations, make it possible to solve equations that are impossible to solve with a purely numeric-based technique.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"To install ModelingToolkit.jl, use the Julia package manager:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg\nPkg.add(\"ModelingToolkit\")","category":"page"},{"location":"#Citation","page":"Home","title":"Citation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If you use ModelingToolkit in your work, please cite the following:","category":"page"},{"location":"","page":"Home","title":"Home","text":"@misc{ma2021modelingtoolkit,\n title={ModelingToolkit: A Composable Graph Transformation System For Equation-Based Modeling},\n author={Yingbo Ma and Shashi Gowda and Ranjan Anantharaman and Chris Laughman and Viral Shah and Chris Rackauckas},\n year={2021},\n eprint={2103.05244},\n archivePrefix={arXiv},\n primaryClass={cs.MS}\n}","category":"page"},{"location":"#Feature-Summary","page":"Home","title":"Feature Summary","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"ModelingToolkit.jl is a symbolic-numeric modeling package. Thus it combines some of the features from symbolic computing packages like SymPy or Mathematica with the ideas of equation-based modeling systems like the causal Simulink and the acausal Modelica. It bridges the gap between many different kinds of equations, allowing one to quickly and easily transform systems of DAEs into optimization problems, or vice-versa, and then simplify and parallelize the resulting expressions before generating code.","category":"page"},{"location":"#Feature-List","page":"Home","title":"Feature List","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Causal and acausal modeling (Simulink/Modelica)\nAutomated model transformation, simplification, and composition\nAutomatic conversion of numerical models into symbolic models\nComposition of models through the components, a lazy connection system, and tools for expanding/flattening\nPervasive parallelism in symbolic computations and generated functions\nTransformations like alias elimination and tearing of nonlinear systems for efficiently numerically handling large-scale systems of equations\nThe ability to use the entire Symbolics.jl Computer Algebra System (CAS) as part of the modeling process.\nImport models from common formats like SBML, CellML, BioNetGen, and more.\nExtensibility: the whole system is written in pure Julia, so adding new functions, simplification rules, and model transformations has no barrier.","category":"page"},{"location":"","page":"Home","title":"Home","text":"For information on how to use the Symbolics.jl CAS system that ModelingToolkit.jl is built on, consult the Symbolics.jl documentation","category":"page"},{"location":"#Equation-Types","page":"Home","title":"Equation Types","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Ordinary differential equations\nStochastic differential equations\nPartial differential equations\nNonlinear systems\nOptimization problems\nContinuous-Time Markov Chains\nChemical Reactions (via Catalyst.jl)\nNonlinear Optimal Control","category":"page"},{"location":"#Standard-Library","page":"Home","title":"Standard Library","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"For quick development, ModelingToolkit.jl includes ModelingToolkitStandardLibrary.jl, a standard library of prebuilt components for the ModelingToolkit ecosystem.","category":"page"},{"location":"#Model-Import-Formats","page":"Home","title":"Model Import Formats","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"CellMLToolkit.jl: Import CellML models into ModelingToolkit\nRepository of more than a thousand pre-made models\nFocus on biomedical models in areas such as: Calcium Dynamics, Cardiovascular Circulation, Cell Cycle, Cell Migration, Circadian Rhythms, Electrophysiology, Endocrine, Excitation-Contraction Coupling, Gene Regulation, Hepatology, Immunology, Ion Transport, Mechanical Constitutive Laws, Metabolism, Myofilament Mechanics, Neurobiology, pH Regulation, PKPD, Protein Modules, Signal Transduction, and Synthetic Biology.\nSBMLToolkit.jl: Import SBML models into ModelingToolkit\nUses the robust libsbml library for parsing and transforming the SBML\nReactionNetworkImporters.jl: Import various models into ModelingToolkit\nSupports the BioNetGen .net file\nSupports importing networks specified by stoichiometric matrices","category":"page"},{"location":"#Extension-Libraries","page":"Home","title":"Extension Libraries","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Because ModelingToolkit.jl is the core foundation of an equation-based modeling ecosystem, there is a large set of libraries adding features to this system. Below is an incomplete list of extension libraries one may want to be aware of:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Catalyst.jl: Symbolic representations of chemical reactions\nSymbolically build and represent large systems of chemical reactions\nGenerate code for ODEs, SDEs, continuous-time Markov Chains, and more\nSimulate the models using the SciML ecosystem with O(1) Gillespie methods\nDataDrivenDiffEq.jl: Automatic identification of equations from data\nAutomated construction of ODEs and DAEs from data\nRepresentations of Koopman operators and Dynamic Mode Decomposition (DMD)\nMomentClosure.jl: Automatic transformation of ReactionSystems into deterministic systems\nGenerates ODESystems for the moment closures\nAllows for geometrically-distributed random reaction rates\nReactionMechanismSimulator.jl: Simulating and analyzing large chemical reaction mechanisms\nIdeal gas and dilute liquid phases.\nConstant T and P and constant V adiabatic ideal gas reactors.\nConstant T and V dilute liquid reactors.\nDiffusion limited rates. Sensitivity analysis for all reactors.\nFlux diagrams with molecular images (if molecular information is provided).\nNumCME.jl: High-performance simulation of chemical master equations (CME)\nTransient solution of the CME\nDynamic state spaces\nAccepts reaction systems defined using Catalyst.jl DSL.\nFiniteStateProjection.jl: High-performance simulation of chemical master equations (CME) via finite state projections\nAccepts reaction systems defined using Catalyst.jl DSL.","category":"page"},{"location":"#Compatible-Numerical-Solvers","page":"Home","title":"Compatible Numerical Solvers","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"All of the symbolic systems have a direct conversion to a numerical system, which can then be handled through the SciML interfaces. For example, after building a model and performing symbolic manipulations, an ODESystem can be converted into an ODEProblem to then be solved by a numerical ODE solver. Below is a list of the solver libraries which are the numerical targets of the ModelingToolkit system:","category":"page"},{"location":"","page":"Home","title":"Home","text":"DifferentialEquations.jl\nMulti-package interface of high performance numerical solvers for ODESystem, SDESystem, and JumpSystem\nNonlinearSolve.jl\nHigh performance numerical solving of NonlinearSystem\nOptimization.jl\nMulti-package interface for numerical solving OptimizationSystem\nNeuralPDE.jl\nPhysics-Informed Neural Network (PINN) training on PDESystem\nMethodOfLines.jl\nAutomated finite difference method (FDM) discretization of PDESystem","category":"page"},{"location":"#Contributing","page":"Home","title":"Contributing","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Please refer to the SciML ColPrac: Contributor's Guide on Collaborative Practices for Community Packages for guidance on PRs, issues, and other matters relating to contributing to SciML.\nSee the SciML Style Guide for common coding practices and other style decisions.\nThere are a few community forums:\nThe #diffeq-bridged and #sciml-bridged channels in the Julia Slack\nThe #diffeq-bridged and #sciml-bridged channels in the Julia Zulip\nOn the Julia Discourse forums\nSee also SciML Community page","category":"page"},{"location":"#Reproducibility","page":"Home","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"<details><summary>The documentation of this SciML package was built using these direct dependencies,</summary>","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"</details>","category":"page"},{"location":"","page":"Home","title":"Home","text":"<details><summary>and using this machine and Julia version.</summary>","category":"page"},{"location":"","page":"Home","title":"Home","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"</details>","category":"page"},{"location":"","page":"Home","title":"Home","text":"<details><summary>A more complete overview of all dependencies and their versions is also provided.</summary>","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status(; mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"</details>","category":"page"},{"location":"","page":"Home","title":"Home","text":"using TOML\nusing Markdown\nversion = TOML.parse(read(\"../../Project.toml\", String))[\"version\"]\nname = TOML.parse(read(\"../../Project.toml\", String))[\"name\"]\nlink_manifest = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Manifest.toml\"\nlink_project = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Project.toml\"\nMarkdown.parse(\"\"\"You can also download the\n[manifest]($link_manifest)\nfile and the\n[project]($link_project)\nfile.\n\"\"\")","category":"page"},{"location":"examples/perturbation/#perturb_diff","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"","category":"section"},{"location":"examples/perturbation/#Prelims","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Prelims","text":"","category":"section"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"In the previous tutorial, Mixed Symbolic-Numeric Perturbation Theory, we discussed how to solve algebraic equations using Symbolics.jl. Here, our goal is to extend the method to differential equations. First, we import the following helper functions that were introduced in Mixed Symbolic/Numerical Methods for Perturbation Theory - Algebraic Equations:","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"using Symbolics, SymbolicUtils\n\ndef_taylor(x, ps) = sum([a * x^i for (i, a) in enumerate(ps)])\ndef_taylor(x, ps, p₀) = p₀ + def_taylor(x, ps)\n\nfunction collect_powers(eq, x, ns; max_power = 100)\n eq = substitute(expand(eq), Dict(x^j => 0 for j in (last(ns) + 1):max_power))\n\n eqs = []\n for i in ns\n powers = Dict(x^j => (i == j ? 1 : 0) for j in 1:last(ns))\n push!(eqs, substitute(eq, powers))\n end\n eqs\nend\n\nfunction solve_coef(eqs, ps)\n vals = Dict()\n\n for i in 1:length(ps)\n eq = substitute(eqs[i], vals)\n vals[ps[i]] = Symbolics.solve_for(eq ~ 0, ps[i])\n end\n vals\nend","category":"page"},{"location":"examples/perturbation/#The-Trajectory-of-a-Ball!","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"The Trajectory of a Ball!","text":"","category":"section"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"In the first two examples, we applied the perturbation method to algebraic problems. However, the main power of the perturbation method is to solve differential equations (usually ODEs, but also occasionally PDEs). Surprisingly, the main procedure developed to solve algebraic problems works well for differential equations. In fact, we will use the same two helper functions, collect_powers and solve_coef. The main difference is in the way we expand the dependent variables. For algebraic problems, the coefficients of epsilon are constants; whereas, for differential equations, they are functions of the dependent variable (usually time).","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"As the first ODE example, we have chosen a simple and well-behaved problem, which is a variation of a standard first-year physics problem: what is the trajectory of an object (say, a ball, or a rocket) thrown vertically at velocity v from the surface of a planet? Assuming a constant acceleration of gravity, g, every burgeoning physicist knows the answer: x(t) = x(0) + vt - frac12gt^2. However, what happens if g is not constant? Specifically, g is inversely proportional to the distant from the center of the planet. If v is large and the projectile travels a large fraction of the radius of the planet, the assumption of constant gravity does not hold anymore. However, unless v is large compared to the escape velocity, the correction is usually small. After simplifications and change of variables to dimensionless, the problem becomes","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":" ddotx(t) = -frac1(1 + epsilon x(t))^2","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"with the initial conditions x(0) = 0, and dotx(0) = 1. Note that for epsilon = 0, this equation transforms back to the standard one. Let's start with defining the variables","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"n = 3\n@variables ϵ t y[1:n](t) ∂∂y[1:n](t)","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"Next, we define x.","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"x = def_taylor(ϵ, y[3:end], y[2])","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"We need the second derivative of x. It may seem that we can do this using Differential(t); however, this operation needs to wait for a few steps because we need to manipulate the differentials as separate variables. Instead, we define dummy variables ∂∂y as the placeholder for the second derivatives and define","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"∂∂x = def_taylor(ϵ, ∂∂y[3:end], ∂∂y[2])","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"as the second derivative of x. After rearrangement, our governing equation is ddotx(t)(1 + epsilon x(t))^-2 + 1 = 0, or","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"eq = ∂∂x * (1 + ϵ * x)^2 + 1","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"The next two steps are the same as the ones for algebraic equations (note that we pass 1:n to collect_powers because the zeroth order term is needed here)","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"eqs = collect_powers(eq, ϵ, 1:n)","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"and,","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"vals = solve_coef(eqs, ∂∂y)","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"Our system of ODEs is forming. Now is the time to convert ∂∂s to the correct Symbolics.jl form by substitution:","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"D = Differential(t)\nsubs = Dict(∂∂y[i] => D(D(y[i])) for i in eachindex(y))\neqs = [substitute(first(v), subs) ~ substitute(last(v), subs) for v in vals]","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"We are nearly there! From this point on, the rest is standard ODE solving procedures. Potentially, we can use a symbolic ODE solver to find a closed form solution to this problem. However, Symbolics.jl currently does not support this functionality. Instead, we solve the problem numerically. We form an ODESystem, lower the order (convert second derivatives to first), generate an ODEProblem (after passing the correct initial conditions), and, finally, solve it.","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"using ModelingToolkit, DifferentialEquations\n\n@named sys = ODESystem(eqs, t)\nsys = structural_simplify(sys)\nstates(sys)","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"# the initial conditions\n# everything is zero except the initial velocity\nu0 = zeros(2n + 2)\nu0[3] = 1.0 # y₀ˍt\n\nprob = ODEProblem(sys, u0, (0, 3.0))\nsol = solve(prob; dtmax = 0.01)","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"Finally, we calculate the solution to the problem as a function of ϵ by substituting the solution to the ODE system back into the defining equation for x. Note that 𝜀 is a number, compared to ϵ, which is a symbolic variable.","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"X = 𝜀 -> sum([𝜀^(i - 1) * sol[y[i]] for i in eachindex(y)])","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"Using X, we can plot the trajectory for a range of 𝜀s.","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"using Plots\n\nplot(sol.t, hcat([X(𝜀) for 𝜀 in 0.0:0.1:0.5]...))","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"As expected, as 𝜀 becomes larger (meaning the gravity is less with altitude), the object goes higher and stays up for a longer duration. Of course, we could have solved the problem directly using as ODE solver. One of the benefits of the perturbation method is that we need to run the ODE solver only once and then can just calculate the answer for different values of 𝜀; whereas, if we had used the direct method, we would need to run the solver once for each value of 𝜀.","category":"page"},{"location":"examples/perturbation/#A-Weakly-Nonlinear-Oscillator","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"A Weakly Nonlinear Oscillator","text":"","category":"section"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"For the next example, we have chosen a simple example from a very important class of problems, the nonlinear oscillators. As we will see, perturbation theory has difficulty providing a good solution to this problem, but the process is instructive. This example closely follows the chapter 7.6 of Nonlinear Dynamics and Chaos by Steven Strogatz.","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"The goal is to solve ddotx + 2epsilondotx + x = 0, where the dot signifies time-derivatives and the initial conditions are x(0) = 0 and dotx(0) = 1. If epsilon = 0, the problem reduces to the simple linear harmonic oscillator with the exact solution x(t) = sin(t). We follow the same steps as the previous example.","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"n = 3\n@variables ϵ t y[1:n](t) ∂y[1:n] ∂∂y[1:n]\nx = def_taylor(ϵ, y[3:end], y[2])\n∂x = def_taylor(ϵ, ∂y[3:end], ∂y[2])\n∂∂x = def_taylor(ϵ, ∂∂y[3:end], ∂∂y[2])","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"This time we also need the first derivative terms. Continuing,","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"eq = ∂∂x + 2 * ϵ * ∂x + x\neqs = collect_powers(eq, ϵ, 0:n)\nvals = solve_coef(eqs, ∂∂y)","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"Next, we need to replace ∂s and ∂∂s with their Symbolics.jl counterparts:","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"D = Differential(t)\nsubs1 = Dict(∂y[i] => D(y[i]) for i in eachindex(y))\nsubs2 = Dict(∂∂y[i] => D(D(y[i])) for i in eachindex(y))\nsubs = subs1 ∪ subs2\neqs = [substitute(first(v), subs) ~ substitute(last(v), subs) for v in vals]","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"We continue with converting 'eqs' to an ODEProblem, solving it, and finally plot the results against the exact solution to the original problem, which is x(t epsilon) = (1 - epsilon)^-12 e^-epsilon t sin((1- epsilon^2)^12t),","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"@named sys = ODESystem(eqs, t)\nsys = structural_simplify(sys)","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"# the initial conditions\nu0 = zeros(2n + 2)\nu0[3] = 1.0 # y₀ˍt\n\nprob = ODEProblem(sys, u0, (0, 50.0))\nsol = solve(prob; dtmax = 0.01)\n\nX = 𝜀 -> sum([𝜀^(i - 1) * sol[y[i]] for i in eachindex(y)])\nT = sol.t\nY = 𝜀 -> exp.(-𝜀 * T) .* sin.(sqrt(1 - 𝜀^2) * T) / sqrt(1 - 𝜀^2) # exact solution\n\nplot(sol.t, [Y(0.1), X(0.1)])","category":"page"},{"location":"examples/perturbation/","page":"Symbolic-Numeric Perturbation Theory for ODEs","title":"Symbolic-Numeric Perturbation Theory for ODEs","text":"The figure is similar to Figure 7.6.2 in Nonlinear Dynamics and Chaos. The two curves fit well for the first couple of cycles, but then the perturbation method curve diverges from the true solution. The main reason is that the problem has two or more time-scales that introduce secular terms in the solution. One solution is to explicitly account for the two time scales and use an analytic method called two-timing.","category":"page"},{"location":"tutorials/domain_connections/#domains","page":"Domains","title":"Domains","text":"","category":"section"},{"location":"tutorials/domain_connections/#Basics","page":"Domains","title":"Basics","text":"","category":"section"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"A domain in ModelingToolkit.jl is a network of connected components that share properties of the medium in the network. For example, a collection of hydraulic components connected together will have a fluid medium. Using the domain feature, one only needs to define and set the fluid medium properties once, in one component, rather than at each component. The way this works in ModelingToolkit.jl is by defining a connector (with Through/Flow and Across variables) with parameters defining the medium of the domain. Then a second connector is defined, with the same parameters, and the same Through/Flow variable, which acts as the setter. For example, a hydraulic domain may have a hydraulic connector, HydraulicPort, that defines a fluid medium with density (ρ), viscosity (μ), and a bulk modulus (β), a through/flow variable mass flow (dm) and an across variable pressure (p).","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"using ModelingToolkit\n\n@parameters t\nD = Differential(t)\n\n@connector function HydraulicPort(; p_int, name)\n pars = @parameters begin\n ρ\n β\n μ\n end\n\n vars = @variables begin\n p(t) = p_int\n dm(t), [connect = Flow]\n end\n\n ODESystem(Equation[], t, vars, pars; name, defaults = [dm => 0])\nend\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"The fluid medium setter for HydralicPort may be defined as HydraulicFluid with the same parameters and through/flow variable. But now, the parameters can be set through the function keywords.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"@connector function HydraulicFluid(;\n density = 997,\n bulk_modulus = 2.09e9,\n viscosity = 0.0010016,\n name)\n pars = @parameters begin\n ρ = density\n β = bulk_modulus\n μ = viscosity\n end\n\n vars = @variables begin\n dm(t), [connect = Flow]\n end\n\n eqs = [\n dm ~ 0,\n ]\n\n ODESystem(eqs, t, vars, pars; name, defaults = [dm => 0])\nend\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"Now, we can connect a HydraulicFluid component to any HydraulicPort connector, and the parameters of all HydraulicPort's in the network will be automatically set. Let's consider a simple example, connecting a pressure source component to a volume component. Note that we don't need to define density for the volume component, it's supplied by the HydraulicPort (port.ρ).","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"@component function FixedPressure(; p, name)\n pars = @parameters p = p\n systems = @named begin\n port = HydraulicPort(; p_int = p)\n end\n\n eqs = [port.p ~ p]\n\n ODESystem(eqs, t, [], pars; name, systems)\nend\n\n@component function FixedVolume(; vol, p_int, name)\n pars = @parameters begin\n p_int = p_int\n vol = vol\n end\n\n systems = @named begin\n port = HydraulicPort(; p_int)\n end\n\n vars = @variables begin\n rho(t) = port.ρ\n drho(t) = 0\n end\n\n # let\n dm = port.dm\n p = port.p\n\n eqs = [D(rho) ~ drho\n rho ~ port.ρ * (1 + p / port.β)\n dm ~ drho * vol]\n\n ODESystem(eqs, t, vars, pars; name, systems)\nend\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"When the system is defined we can generate a fluid component and connect it to the system. Here fluid is connected to the src.port, but it could also be connected to vol.port, any connection in the network is fine. Note: we can visualize the system using ModelingToolkitDesigner.jl, where a dashed line is used to show the fluid connection to represent a domain connection that is only transporting parameters and not states.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"@component function System(; name)\n systems = @named begin\n src = FixedPressure(; p = 200e5)\n vol = FixedVolume(; vol = 0.1, p_int = 200e5)\n\n fluid = HydraulicFluid(; density = 876)\n end\n\n eqs = [connect(fluid, src.port)\n connect(src.port, vol.port)]\n\n ODESystem(eqs, t, [], []; systems, name)\nend\n\n@named odesys = System()\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"# code to generate diagrams...\n# using ModelingToolkitDesigner \n# path = raw\"C:\\Work\\Assets\\ModelingToolkit.jl\\domain_connections\" \n# design = ODESystemDesign(odesys, path); \n\n# using CairoMakie\n# CairoMakie.set_theme!(Theme(;fontsize=12))\n# fig = ModelingToolkitDesigner.view(design, false)\n# save(joinpath(path, \"odesys.svg\"), fig; resolution=(300,300))","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"(Image: odesys)","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"To see how the domain works, we can examine the set parameter values for each of the ports src.port and vol.port. First we assemble the system using structural_simplify() and then check the default value of vol.port.ρ, whichs points to the setter value fluid₊ρ. Likewise, src.port.ρ, will also point to the setter value fluid₊ρ. Therefore, there is now only 1 defined density value fluid₊ρ which sets the density for the connected network.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"sys = structural_simplify(odesys)\nModelingToolkit.defaults(sys)[complete(odesys).vol.port.ρ]","category":"page"},{"location":"tutorials/domain_connections/#Multiple-Domain-Networks","page":"Domains","title":"Multiple Domain Networks","text":"","category":"section"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"If we have a more complicated system, for example a hydraulic actuator, with a separated fluid on both sides of the piston, it's possible we might have 2 separate domain networks. In this case we can connect 2 separate fluids, or the same fluid, to both networks. First a simple actuator is defined with 2 ports.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"@component function Actuator(; p_int, mass, area, name)\n pars = @parameters begin\n p_int = p_int\n mass = mass\n area = area\n end\n\n systems = @named begin\n port_a = HydraulicPort(; p_int)\n port_b = HydraulicPort(; p_int)\n end\n\n vars = @variables begin\n x(t) = 0\n dx(t) = 0\n ddx(t) = 0\n end\n\n eqs = [D(x) ~ dx\n D(dx) ~ ddx\n mass * ddx ~ (port_a.p - port_b.p) * area\n port_a.dm ~ +(port_a.ρ) * dx * area\n port_b.dm ~ -(port_b.ρ) * dx * area]\n\n ODESystem(eqs, t, vars, pars; name, systems)\nend\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"A system with 2 different fluids is defined and connected to each separate domain network.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"@component function ActuatorSystem2(; name)\n systems = @named begin\n src_a = FixedPressure(; p = 200e5)\n src_b = FixedPressure(; p = 200e5)\n act = Actuator(; p_int = 200e5, mass = 1000, area = 0.1)\n\n fluid_a = HydraulicFluid(; density = 876)\n fluid_b = HydraulicFluid(; density = 999)\n end\n\n eqs = [connect(fluid_a, src_a.port)\n connect(fluid_b, src_b.port)\n connect(src_a.port, act.port_a)\n connect(src_b.port, act.port_b)]\n\n ODESystem(eqs, t, [], []; systems, name)\nend\n\n@named actsys2 = ActuatorSystem2()\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"# design = ODESystemDesign(actsys2, path);\n# fig = ModelingToolkitDesigner.view(design, false)\n# save(joinpath(path, \"actsys2.svg\"), fig; resolution=(500,300))","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"(Image: actsys2)","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"After running structural_simplify() on actsys2, the defaults will show that act.port_a.ρ points to fluid_a₊ρ and act.port_b.ρ points to fluid_b₊ρ. This is a special case, in most cases a hydraulic system will have only 1 fluid, however this simple system has 2 separate domain networks. Therefore, we can connect a single fluid to both networks. This does not interfere with the mathematical equations of the system, since no states are connected.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"@component function ActuatorSystem1(; name)\n systems = @named begin\n src_a = FixedPressure(; p = 200e5)\n src_b = FixedPressure(; p = 200e5)\n act = Actuator(; p_int = 200e5, mass = 1000, area = 0.1)\n\n fluid = HydraulicFluid(; density = 876)\n end\n\n eqs = [connect(fluid, src_a.port)\n connect(fluid, src_b.port)\n connect(src_a.port, act.port_a)\n connect(src_b.port, act.port_b)]\n\n ODESystem(eqs, t, [], []; systems, name)\nend\n\n@named actsys1 = ActuatorSystem1()\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"# design = ODESystemDesign(actsys1, path);\n# fig = ModelingToolkitDesigner.view(design, false)\n# save(joinpath(path, \"actsys1.svg\"), fig; resolution=(500,300))","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"(Image: actsys1)","category":"page"},{"location":"tutorials/domain_connections/#Special-Connection-Cases-(domain_connect())","page":"Domains","title":"Special Connection Cases (domain_connect())","text":"","category":"section"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"In some cases a component will be defined with 2 connectors of the same domain, but they are not connected. For example the Restrictor defined here gives equations to define the behavior of how the 2 connectors port_a and port_b are physically connected.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"@component function Restrictor(; name, p_int)\n pars = @parameters begin\n K = 0.1\n p_int = p_int\n end\n\n systems = @named begin\n port_a = HydraulicPort(; p_int)\n port_b = HydraulicPort(; p_int)\n end\n\n eqs = [port_a.dm ~ (port_a.p - port_b.p) * K\n 0 ~ port_a.dm + port_b.dm]\n\n ODESystem(eqs, t, [], pars; systems, name)\nend\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"Adding the Restrictor to the original system example will cause a break in the domain network, since a connect(port_a, port_b) is not defined.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"@component function RestrictorSystem(; name)\n systems = @named begin\n src = FixedPressure(; p = 200e5)\n res = Restrictor(; p_int = 200e5)\n vol = FixedVolume(; vol = 0.1, p_int = 200e5)\n\n fluid = HydraulicFluid(; density = 876)\n end\n\n eqs = [connect(fluid, src.port)\n connect(src.port, res.port_a)\n connect(res.port_b, vol.port)]\n\n ODESystem(eqs, t, [], []; systems, name)\nend\n\n@named ressys = RestrictorSystem()\nsys = structural_simplify(ressys)\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"# design = ODESystemDesign(ressys, path);\n# fig = ModelingToolkitDesigner.view(design, false)\n# save(joinpath(path, \"ressys.svg\"), fig; resolution=(500,300))","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"(Image: ressys)","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"When structural_simplify() is applied to this system it can be seen that the defaults are missing for res.port_b and vol.port.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"ModelingToolkit.defaults(sys)[complete(ressys).res.port_a.ρ]\nModelingToolkit.defaults(sys)[complete(ressys).res.port_b.ρ]\nModelingToolkit.defaults(sys)[complete(ressys).vol.port.ρ]","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"To ensure that the Restrictor component does not disrupt the domain network, the domain_connect() function can be used, which explicitly only connects the domain network and not the states.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"@component function Restrictor(; name, p_int)\n pars = @parameters begin\n K = 0.1\n p_int = p_int\n end\n\n systems = @named begin\n port_a = HydraulicPort(; p_int)\n port_b = HydraulicPort(; p_int)\n end\n\n eqs = [domain_connect(port_a, port_b) # <-- connect the domain network\n port_a.dm ~ (port_a.p - port_b.p) * K\n 0 ~ port_a.dm + port_b.dm]\n\n ODESystem(eqs, t, [], pars; systems, name)\nend\n\n@named ressys = RestrictorSystem()\nsys = structural_simplify(ressys)\nnothing #hide","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"Now that the Restrictor component is properly defined using domain_connect(), the defaults for res.port_b and vol.port are properly defined.","category":"page"},{"location":"tutorials/domain_connections/","page":"Domains","title":"Domains","text":"ModelingToolkit.defaults(sys)[complete(ressys).res.port_a.ρ]\nModelingToolkit.defaults(sys)[complete(ressys).res.port_b.ρ]\nModelingToolkit.defaults(sys)[complete(ressys).vol.port.ρ]","category":"page"}]
}