Skip to content

Commit 7c85930

Browse files
Merge pull request #2489 from AayushSabharwal/as/docs-v9
docs: update docs for MTKv9
2 parents dca0f83 + 9535ba7 commit 7c85930

34 files changed

+530
-215
lines changed

NEWS.md

+1
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,4 @@
4949
`(single_parameter => expression_involving_other_parameters)` and a `Vector` of these can be passed to
5050
the `parameter_dependencies` keyword argument of `ODESystem`, `SDESystem` and `JumpSystem`. The dependent
5151
parameters are updated whenever other parameters are modified, e.g. in callbacks.
52+
- Support for `IfElse.jl` has been dropped. `Base.ifelse` can be used instead.

Project.toml

-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ FindFirstFunctions = "64ca27bc-2ba2-4a57-88aa-44e436879224"
2323
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
2424
FunctionWrappersWrappers = "77dc65aa-8811-40c2-897b-53d922fa7daf"
2525
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
26-
IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173"
2726
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
2827
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
2928
JumpProcesses = "ccbc3e58-028d-4f4c-8cd5-9ae44345cda5"
@@ -81,7 +80,6 @@ FindFirstFunctions = "1"
8180
ForwardDiff = "0.10.3"
8281
FunctionWrappersWrappers = "0.1"
8382
Graphs = "1.5.2"
84-
IfElse = "0.1"
8583
InteractiveUtils = "1"
8684
JuliaFormatter = "1.0.47"
8785
JumpProcesses = "9.1"

docs/Project.toml

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@ BifurcationKit = "0f109fa4-8a5d-4b75-95aa-f515264e7665"
44
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa"
55
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
66
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
7+
DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821"
78
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
89
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
9-
ModelingToolkitDesigner = "23d639d0-9462-4d1e-84fe-d700424865b8"
1010
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
1111
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
1212
Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba"
1313
OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e"
1414
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
1515
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
1616
StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
17-
StructuralIdentifiability = "220ca800-aa68-49bb-acd8-6037fa93a544"
17+
SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"
1818
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
1919
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
2020
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
@@ -26,15 +26,15 @@ DifferentialEquations = "7.6"
2626
Distributions = "0.25"
2727
Documenter = "1"
2828
ModelingToolkit = "8.33, 9"
29-
ModelingToolkitDesigner = "1"
29+
DynamicQuantities = "^0.11.2"
3030
NonlinearSolve = "0.3, 1, 2, 3"
3131
Optim = "1.7"
3232
Optimization = "3.9"
3333
OptimizationOptimJL = "0.1"
3434
OrdinaryDiffEq = "6.31"
3535
Plots = "1.36"
3636
StochasticDiffEq = "6"
37-
StructuralIdentifiability = "0.4, 0.5"
37+
SymbolicIndexingInterface = "0.3.1"
3838
SymbolicUtils = "1"
3939
Symbolics = "5"
4040
Unitful = "1.12"

docs/src/basics/Events.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,12 @@ ModelingToolkit therefore supports regular Julia functions as affects: instead
144144
of one or more equations, an affect is defined as a `tuple`:
145145

146146
```julia
147-
[x ~ 0] => (affect!, [v, x], [p, q], ctx)
147+
[x ~ 0] => (affect!, [v, x], [p, q], [discretes...], ctx)
148148
```
149149

150150
where, `affect!` is a Julia function with the signature: `affect!(integ, u, p, ctx)`; `[u,v]` and `[p,q]` are the symbolic unknowns (variables) and parameters
151-
that are accessed by `affect!`, respectively; and `ctx` is any context that is
152-
passed to `affect!` as the `ctx` argument.
151+
that are accessed by `affect!`, respectively; `discretes` are the parameters modified by `affect!`, if any;
152+
and `ctx` is any context that is passed to `affect!` as the `ctx` argument.
153153

154154
`affect!` receives a [DifferentialEquations.jl
155155
integrator](https://docs.sciml.ai/DiffEqDocs/stable/basics/integrator/)
@@ -172,7 +172,7 @@ When accessing variables of a sub-system, it can be useful to rename them
172172
(alternatively, an affect function may be reused in different contexts):
173173

174174
```julia
175-
[x ~ 0] => (affect!, [resistor₊v => :v, x], [p, q => :p2], ctx)
175+
[x ~ 0] => (affect!, [resistor₊v => :v, x], [p, q => :p2], [], ctx)
176176
```
177177

178178
Here, the symbolic variable `resistor₊v` is passed as `v` while the symbolic
@@ -191,7 +191,7 @@ function bb_affect!(integ, u, p, ctx)
191191
integ.u[u.v] = -integ.u[u.v]
192192
end
193193
194-
reflect = [x ~ 0] => (bb_affect!, [v], [], nothing)
194+
reflect = [x ~ 0] => (bb_affect!, [v], [], [], nothing)
195195
196196
@mtkbuild bb_sys = ODESystem(bb_eqs, t, sts, par,
197197
continuous_events = reflect)

docs/src/basics/FAQ.md

+5-6
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,11 @@ pnew = varmap_to_vars([β => 3.0, c => 10.0, γ => 2.0], parameters(sys))
2424

2525
## How do I handle `if` statements in my symbolic forms?
2626

27-
For statements that are in the `if then else` form, use `IfElse.ifelse` from the
28-
[IfElse.jl](https://github.com/SciML/IfElse.jl) package to represent the code in a
29-
functional form. For handling direct `if` statements, you can use equivalent boolean
30-
mathematical expressions. For example, `if x > 0 ...` can be implemented as just
31-
`(x > 0) * `, where if `x <= 0` then the boolean will evaluate to `0` and thus the
32-
term will be excluded from the model.
27+
For statements that are in the `if then else` form, use `Base.ifelse` from the
28+
to represent the code in a functional form. For handling direct `if` statements,
29+
you can use equivalent boolean mathematical expressions. For example, `if x > 0 ...`
30+
can be implemented as just `(x > 0) * `, where if `x <= 0` then the boolean will
31+
evaluate to `0` and thus the term will be excluded from the model.
3332

3433
## ERROR: TypeError: non-boolean (Num) used in boolean context?
3534

docs/src/basics/Validation.md

+18-14
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ ModelingToolkit.jl provides extensive functionality for model validation and uni
77
Units may be assigned with the following syntax.
88

99
```@example validation
10-
using ModelingToolkit, Unitful
10+
using ModelingToolkit, DynamicQuantities
1111
@variables t [unit = u"s"] x(t) [unit = u"m"] g(t) w(t) [unit = "Hz"]
1212
1313
@variables(t, [unit = u"s"], x(t), [unit = u"m"], g(t), w(t), [unit = "Hz"])
@@ -45,7 +45,7 @@ ModelingToolkit.get_unit
4545
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.
4646

4747
```@example validation
48-
using ModelingToolkit, Unitful
48+
using ModelingToolkit, DynamicQuantities
4949
@parameters τ [unit = u"ms"]
5050
@variables t [unit = u"ms"] E(t) [unit = u"kJ"] P(t) [unit = u"MW"]
5151
D = Differential(t)
@@ -59,13 +59,17 @@ ModelingToolkit.validate(eqs[1])
5959
```
6060

6161
```@example validation
62-
ModelingToolkit.get_unit(eqs[1].rhs)
62+
try
63+
ModelingToolkit.get_unit(eqs[1].rhs)
64+
catch e
65+
showerror(stdout, e)
66+
end
6367
```
6468

6569
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.
6670

6771
```@example validation
68-
using ModelingToolkit, Unitful
72+
using ModelingToolkit, DynamicQuantities
6973
@parameters τ [unit = u"ms"]
7074
@variables t [unit = u"ms"] E(t) [unit = u"J"] P(t) [unit = u"MW"]
7175
D = Differential(t)
@@ -81,10 +85,9 @@ expect an object type, while two-parameter calls expect a function type as the f
8185
second argument.
8286

8387
```@example validation2
84-
using ModelingToolkit, Unitful
88+
using ModelingToolkit, DynamicQuantities
89+
using ModelingToolkit: t_nounits as t, D_nounits as D
8590
# Composite type parameter in registered function
86-
@variables t
87-
D = Differential(t)
8891
struct NewType
8992
f::Any
9093
end
@@ -101,16 +104,17 @@ end
101104
sts = @variables a(t)=0 [unit = u"cm"]
102105
ps = @parameters s=-1 [unit = u"cm"] c=c [unit = u"cm"]
103106
eqs = [D(a) ~ dummycomplex(c, s);]
104-
sys = ODESystem(eqs, t, [sts...;], [ps...;], name = :sys)
107+
sys = ODESystem(
108+
eqs, t, [sts...;], [ps...;], name = :sys, checks = ~ModelingToolkit.CheckUnits)
105109
sys_simple = structural_simplify(sys)
106110
```
107111

108-
## `Unitful` Literals
112+
## `DynamicQuantities` Literals
109113

110114
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.
111115

112116
```julia
113-
using ModelingToolkit, Unitful
117+
using ModelingToolkit, DynamicQuantities
114118
@variables t [unit = u"ms"] E(t) [unit = u"J"] P(t) [unit = u"MW"]
115119
D = Differential(t)
116120
eqs = [D(E) ~ P - E / 1u"ms"]
@@ -124,7 +128,7 @@ ModelingToolkit.validate(eqs) #Returns false while displaying a warning message
124128
Instead, they should be parameterized:
125129

126130
```@example validation3
127-
using ModelingToolkit, Unitful
131+
using ModelingToolkit, DynamicQuantities
128132
@parameters τ [unit = u"ms"]
129133
@variables t [unit = u"ms"] E(t) [unit = u"kJ"] P(t) [unit = u"MW"]
130134
D = Differential(t)
@@ -138,8 +142,8 @@ eqs = [D(E) ~ P - myfunc(E, τ)]
138142
ModelingToolkit.validate(eqs) #Returns true
139143
```
140144

141-
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
142-
future when `ModelingToolkit` is extended to support eliminating `Unitful` literals from functions.
145+
It is recommended *not* to circumvent unit validation by specializing user-defined functions on `DynamicQuantities` arguments vs. `Numbers`. This both fails to take advantage of `validate` for ensuring correctness, and may cause in errors in the
146+
future when `ModelingToolkit` is extended to support eliminating `DynamicQuantities` literals from functions.
143147

144148
## Other Restrictions
145149

@@ -149,7 +153,7 @@ future when `ModelingToolkit` is extended to support eliminating `Unitful` liter
149153

150154
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.
151155

152-
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`.
156+
Symbolic exponents for unitful variables *are* supported (ex: `P^γ` in thermodynamics). However, this means that `ModelingToolkit` cannot reduce such expressions to `DynamicQuantities.Quantity` 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`.
153157

154158
## Parameter & Initial Condition Values
155159

docs/src/basics/Variable_metadata.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ In the example below, we define a system with tunable parameters and extract bou
142142
@variables x(t)=0 u(t)=0 [input = true] y(t)=0 [output = true]
143143
@parameters T [tunable = true, bounds = (0, Inf)]
144144
@parameters k [tunable = true, bounds = (0, Inf)]
145-
eqs = [Dₜ(x) ~ (-x + k * u) / T # A first-order system with time constant T and gain k
145+
eqs = [D(x) ~ (-x + k * u) / T # A first-order system with time constant T and gain k
146146
y ~ x]
147147
sys = ODESystem(eqs, t, name = :tunable_first_order)
148148
```
@@ -159,6 +159,9 @@ lb, ub = getbounds(p) # operating on a vector, we get lower and upper bound vect
159159
b = getbounds(sys) # Operating on the system, we get a dict
160160
```
161161

162+
See also: [`ModelingToolkit.dump_variable_metadata`](@ref), [`ModelingToolkit.dump_parameters`](@ref),
163+
[`ModelingToolkit.dump_unknowns`](@ref).
164+
162165
## Index
163166

164167
```@index
@@ -172,3 +175,9 @@ Modules = [ModelingToolkit]
172175
Pages = ["variables.jl"]
173176
Private = false
174177
```
178+
179+
```@docs
180+
ModelingToolkit.dump_variable_metadata
181+
ModelingToolkit.dump_parameters
182+
ModelingToolkit.dump_unknowns
183+
```

docs/src/examples/parsing.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ From there, we can use ModelingToolkit to transform the symbolic equations into
2323
nonlinear solve:
2424

2525
```@example parsing
26-
using ModelingToolkit, NonlinearSolve
26+
using ModelingToolkit, SymbolicIndexingInterface, NonlinearSolve
2727
vars = union(ModelingToolkit.vars.(eqs)...)
2828
@mtkbuild ns = NonlinearSystem(eqs, vars, [])
2929
30-
prob = NonlinearProblem(ns, [1.0, 1.0, 1.0])
30+
varmap = Dict(SymbolicIndexingInterface.getname.(vars) .=> vars)
31+
prob = NonlinearProblem(ns, [varmap[:x] => 1.0, varmap[:y] => 1.0, varmap[:z] => 1.0])
3132
sol = solve(prob, NewtonRaphson())
3233
```

docs/src/internals.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ The call chain typically looks like this, with the function names in the case of
3232

3333
1. Problem constructor ([`ODEProblem`](@ref))
3434
2. Build an `DEFunction` ([`process_DEProblem`](@ref) -> [`ODEFunction`](@ref)
35-
3. Write actual executable code ([`generate_function`](@ref))
35+
3. Write actual executable code ([`generate_function`](@ref) or [`generate_custom_function`](@ref))
3636

3737
Apart from [`generate_function`](@ref), 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`.

docs/src/tutorials/domain_connections.md

+1-39
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ end
9494
nothing #hide
9595
```
9696

97-
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 unknown variables.
97+
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.
9898

9999
```@example domain
100100
@component function System(; name)
@@ -115,20 +115,6 @@ end
115115
nothing #hide
116116
```
117117

118-
```@setup domain
119-
# code to generate diagrams...
120-
# using ModelingToolkitDesigner
121-
# path = raw"C:\Work\Assets\ModelingToolkit.jl\domain_connections"
122-
# design = ODESystemDesign(odesys, path);
123-
124-
# using CairoMakie
125-
# CairoMakie.set_theme!(Theme(;fontsize=12))
126-
# fig = ModelingToolkitDesigner.view(design, false)
127-
# save(joinpath(path, "odesys.svg"), fig; resolution=(300,300))
128-
```
129-
130-
![odesys](https://github.com/SciML/ModelingToolkit.jl/assets/40798837/d19fbcf4-781c-4743-87b7-30bed348ff98)
131-
132118
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.
133119

134120
```@repl domain
@@ -195,14 +181,6 @@ end
195181
nothing #hide
196182
```
197183

198-
```@setup domain
199-
# design = ODESystemDesign(actsys2, path);
200-
# fig = ModelingToolkitDesigner.view(design, false)
201-
# save(joinpath(path, "actsys2.svg"), fig; resolution=(500,300))
202-
```
203-
204-
![actsys2](https://github.com/SciML/ModelingToolkit.jl/assets/40798837/8ed50035-f6ac-48cb-a585-1ef415154a02)
205-
206184
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 unknown variables are connected.
207185

208186
```@example domain
@@ -227,14 +205,6 @@ end
227205
nothing #hide
228206
```
229207

230-
```@setup domain
231-
# design = ODESystemDesign(actsys1, path);
232-
# fig = ModelingToolkitDesigner.view(design, false)
233-
# save(joinpath(path, "actsys1.svg"), fig; resolution=(500,300))
234-
```
235-
236-
![actsys1](https://github.com/SciML/ModelingToolkit.jl/assets/40798837/054404eb-dbb7-4b85-95c0-c9503d0c4d00)
237-
238208
## Special Connection Cases (`domain_connect()`)
239209

240210
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.
@@ -282,14 +252,6 @@ end
282252
nothing #hide
283253
```
284254

285-
```@setup domain
286-
# design = ODESystemDesign(ressys, path);
287-
# fig = ModelingToolkitDesigner.view(design, false)
288-
# save(joinpath(path, "ressys.svg"), fig; resolution=(500,300))
289-
```
290-
291-
![ressys](https://github.com/SciML/ModelingToolkit.jl/assets/40798837/3740f0e2-7324-4c1f-af8b-eba02cfece81)
292-
293255
When `structural_simplify()` is applied to this system it can be seen that the defaults are missing for `res.port_b` and `vol.port`.
294256

295257
```@repl domain

docs/src/tutorials/ode_modeling.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ While defining the model `UnitstepFOLFactory`, an initial guess of 0.0 is assign
338338
Additionally, these initial guesses can be modified while creating instances of `UnitstepFOLFactory` by passing arguments.
339339

340340
```@example ode2
341-
@named fol = UnitstepFOLFactory(; x = 0.1)
341+
@mtkbuild fol = UnitstepFOLFactory(; x = 0.1)
342342
sol = ODEProblem(fol, [], (0.0, 5.0), []) |> solve
343343
```
344344

docs/src/tutorials/optimization.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ Let's solve the classical _Rosenbrock function_ in two dimensions.
66

77
First, we need to make some imports.
88

9-
```@example rosenbrock_2d
9+
```julia
1010
using ModelingToolkit, Optimization, OptimizationOptimJL
1111
```
1212

1313
Now we can define our optimization problem.
1414

15-
```@example rosenbrock_2d
15+
```julia
1616
@variables begin
1717
x, [bounds = (-2.0, 2.0)]
1818
y, [bounds = (-1.0, 3.0)]
@@ -44,7 +44,7 @@ Every optimization problem consists of a set of _optimization variables_. In thi
4444

4545
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.
4646

47-
```@example rosenbrock_2d
47+
```julia
4848
u0 = [x => 1.0
4949
y => 2.0]
5050
p = [a => 1.0
@@ -56,7 +56,7 @@ solve(prob, GradientDescent())
5656

5757
## Rosenbrock Function with Constraints
5858

59-
```@example rosenbrock_2d_cstr
59+
```julia
6060
using ModelingToolkit, Optimization, OptimizationOptimJL
6161

6262
@variables begin

0 commit comments

Comments
 (0)