Skip to content

Commit d060e1c

Browse files
committed
docs: update docs to v10
1 parent fb26f21 commit d060e1c

File tree

2 files changed

+65
-25
lines changed

2 files changed

+65
-25
lines changed

docs/Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ BifurcationKit = "0f109fa4-8a5d-4b75-95aa-f515264e7665"
55
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
66
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
77
DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0"
8+
DiffEqDevTools = "f3b72e0c-5b89-59e1-b016-84e28bfd966d"
89
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
910
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
1011
DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821"
1112
FMI = "14a09403-18e3-468f-ad8a-74f8dda2d9ac"
1213
FMIZoo = "724179cf-c260-40a9-bd27-cccc6fe2f195"
14+
InfiniteOpt = "20393b10-9daf-11e9-18c9-8db751c92c57"
15+
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
1316
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1417
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
1518
ModelingToolkitStandardLibrary = "16a59e39-deab-5bd0-87e4-056b12336739"

docs/src/tutorials/dynamic_optimization.md

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Solving Dynamic Optimization Problems
22
Systems in ModelingToolkit.jl can be directly converted to dynamic optimization or optimal control problems. In such systems, one has one or more input variables that are externally controlled to control the dynamics of the system. A dynamic optimization solves for the optimal time trajectory of the input variables in order to maximize or minimize a desired objective function. For example, a car driver might like to know how to step on the accelerator if the goal is to finish a race while using the least gas.
33

4-
To begin, let us take a rocket launch example. The input variable here is the thrust exerted by the engine. The rocket state is described by its current height and velocity.
4+
To begin, let us take a rocket launch example. The input variable here is the thrust exerted by the engine. The rocket state is described by its current height, mass, and velocity. The mass decreases as the rocket loses fuel while thrusting.
55

66
```@example dynamic_opt
77
using ModelingToolkit
@@ -12,8 +12,8 @@ D = ModelingToolkit.D_nounits
1212
@variables begin
1313
h(..)
1414
v(..)
15-
m(..) [bounds = (m_c, 1)]
16-
T(..) [input = true, bounds = (0, Tₘ)]
15+
m(..), [bounds = (m_c, 1)]
16+
T(..), [input = true, bounds = (0, Tₘ)]
1717
end
1818
1919
drag(h, v) = D_c * v^2 * exp(-h_c * (h - h₀) / h₀)
@@ -27,8 +27,8 @@ eqs = [D(h(t)) ~ v(t),
2727
costs = [-h(te)]
2828
cons = [T(te) ~ 0, m(te) ~ m_c]
2929
30-
@named rocket = ODESystem(eqs, t; costs, constraints = cons)
31-
rocket, input_idxs = structural_simplify(rocket, ([T(t)], []))
30+
@named rocket = System(eqs, t; costs, constraints = cons)
31+
rocket = mtkcompile(rocket, inputs = [T(t)])
3232
3333
u0map = [h(t) => h₀, m(t) => m₀, v(t) => 0]
3434
pmap = [
@@ -37,70 +37,107 @@ pmap = [
3737
```
3838
What we would like to optimize here is the final height of the rocket. We do this by providing a vector of expressions corresponding to the costs. By default, the sense of the optimization is to minimize the provided cost. So to maximize the rocket height at the final time, we write `-h(te)` as the cost.
3939

40-
Now we can construct a problem and solve it. Let us use JuMP as our backend here.
40+
Now we can construct a problem and solve it. Let us use JuMP as our backend here. Note that the package trigger is actually [InfiniteOpt](https://infiniteopt.github.io/InfiniteOpt.jl/stable/), and not JuMP - this package includes JuMP but is designed for optimization on function spaces. Additionally we need to load the solver package - we will use [Ipopt](https://github.com/jump-dev/Ipopt.jl) here (a good choice in general).
41+
42+
Here we have also loaded DiffEqDevTools because we will need to construct the ODE tableau. This is only needed if one desires a custom ODE tableau for the collocation - by default the solver will use RadauIIA5.
4143
```@example dynamic_opt
42-
using InfiniteOpt
43-
jprob = JuMPDynamicOptProblem(rocket, u0map, (ts, te), pmap; dt = 0.001, cse = false)
44-
jsol = solve(jprob, JuMPCollocation(Ipopt.Optimizer, constructRadauIIA5()))
44+
using InfiniteOpt, Ipopt, DiffEqDevTools
45+
jprob = JuMPDynamicOptProblem(rocket, [u0map; pmap], (ts, te); dt = 0.001)
46+
jsol = solve(jprob, JuMPCollocation(Ipopt.Optimizer, constructRadauIIA5()));
4547
```
48+
The solution has three fields: `jsol.sol` is the ODE solution for the states, `jsol.input_sol` is the ODE solution for the inputs, and `jsol.model` is the wrapped model that we can use to query things like objective and constraint residuals.
4649

47-
Let's plot our final solution and the controller here:
50+
Let's plot the final solution and the controller here:
4851
```@example dynamic_opt
4952
using CairoMakie
50-
plot(
51-
plot(jsol.sol, title = "Rocket trajectory"),
52-
plot(jsol.input_sol, title = "Control trajectory")
53-
)
53+
fig = Figure(resolution = (800, 400))
54+
ax1 = Axis(fig[1,1], title = "Rocket trajectory", xlabel = "Time")
55+
ax2 = Axis(fig[1,2], title = "Control trajectory", xlabel = "Time")
56+
57+
for u in unknowns(rocket)
58+
lines!(ax1, jsol.sol.t, jsol.sol[u], label = string(u))
59+
end
60+
lines!(ax2, jsol.input_sol, label = "Thrust")
61+
axislegend(ax1)
62+
axislegend(ax2)
63+
fig
5464
```
5565

56-
###### Free final time problems
66+
### Free final time problems
5767
There are additionally a class of dynamic optimization problems where we would like to know how to control our system to achieve something in the least time. Such problems are called free final time problems, since the final time is unknown. To model these problems in ModelingToolkit, we declare the final time as a parameter.
5868

69+
Below we have a model system called the double integrator. We control the acceleration of a block in order to reach a desired destination in the least time.
5970
```@example dynamic_opt
60-
@variables x(..) v(..)
61-
@variables u(..) [bounds = (-1.0, 1.0), input = true]
71+
@variables begin
72+
x(..)
73+
v(..)
74+
u(..), [bounds = (-1.0, 1.0), input = true]
75+
end
76+
6277
@parameters tf
6378
6479
constr = [v(tf) ~ 0, x(tf) ~ 0]
6580
cost = [tf] # Minimize time
6681
67-
@named block = ODESystem(
82+
@named block = System(
6883
[D(x(t)) ~ v(t), D(v(t)) ~ u(t)], t; costs = cost, constraints = constr)
6984
70-
block, input_idxs = structural_simplify(block, ([u(t)], []))
85+
block = mtkcompile(block; inputs = [u(t)])
7186
7287
u0map = [x(t) => 1.0, v(t) => 0.0]
7388
tspan = (0.0, tf)
7489
parammap = [u(t) => 0.0, tf => 1.0]
7590
```
91+
The `tf` mapping in the parameter map is treated as an initial guess.
7692

7793
Please note that, at the moment, free final time problems cannot support constraints defined at definite time values, like `x(3) ~ 2`.
7894

7995
!!! warning
8096

81-
The Pyomo collocation methods (LagrangeRadau, LagrangeLegendre) currently are bugged for free final time problems. Strongly suggest using BackwardEuler() for such problems.
97+
The Pyomo collocation methods (LagrangeRadau, LagrangeLegendre) currently are bugged for free final time problems. Strongly suggest using BackwardEuler() for such problems when using Pyomo as the backend.
8298

83-
Let's solve plot our final solution and the controller for the block, using InfiniteOpt as the backend:
99+
When declaring the problem in this case we need to provide the number of steps, since dt can't be known in advanced. Let's solve plot our final solution and the controller for the block, using InfiniteOpt as the backend:
84100
```@example dynamic_opt
85-
iprob = InfiniteOptDynamicOptProblem(rocket, u0map, (ts, te), pmap; dt = 0.001, cse = false)
86-
isol = solve(iprob, InfiniteOptCollocation(Ipopt.Optimizer))
101+
iprob = InfiniteOptDynamicOptProblem(block, [u0map; parammap], tspan; steps = 100)
102+
isol = solve(iprob, InfiniteOptCollocation(Ipopt.Optimizer));
103+
```
104+
105+
Let's plot the final solution and the controller here:
106+
```@example dynamic_opt
107+
fig = Figure(resolution = (800, 400))
108+
ax1 = Axis(fig[1,1], title = "Block trajectory", xlabel = "Time")
109+
ax2 = Axis(fig[1,2], title = "Control trajectory", xlabel = "Time")
110+
111+
for u in unknowns(block)
112+
lines!(ax1, isol.sol.t, isol.sol[u], label = string(u))
113+
end
114+
lines!(ax2, isol.input_sol, label = "Acceleration")
115+
axislegend(ax1)
116+
axislegend(ax2)
117+
fig
87118
```
88119

89120
### Solvers
90121
Currently 4 backends are exposed for solving dynamic optimization problems using collocation: JuMP, InfiniteOpt, CasADi, and Pyomo.
91122

92-
Please note that there are differences in how to construct the collocation solver for the different cases. For example, the Python based ones, CasADi and Pyomo, expect the solver to be passed in as a string (CasADi and Pyomo come pre-loaded with certain solvers, but other solvers may need to be manually installed using `pip` or `conda`), while JuMP/InfiniteOpt expect the optimizer object to be passed in directly:
123+
Please note that there are differences in how to construct the collocation solver for the different cases. For example, the Python based ones, CasADi and Pyomo, expect the solver to be passed in as a string (CasADi and Pyomo come pre-loaded with Ipopt, but other solvers may need to be manually installed using `pip` or `conda`), while JuMP/InfiniteOpt expect the optimizer object to be passed in directly:
93124
```
94125
JuMPCollocation(Ipopt.Optimizer, constructRK4())
95126
CasADiCollocation("ipopt", constructRK4())
96127
```
97128

98-
**JuMP** and **CasADi** collocation require an ODE tableau to be passed in. These can be constructed by calling the `constructX()` functions from DiffEqDevTools. If none is passed in, both solvers will default to using Radau second-order with five collocation points.
129+
**JuMP** and **CasADi** collocation require an ODE tableau to be passed in. These can be constructed by calling the `constructX()` functions from DiffEqDevTools. The list of tableaus can be found [here](https://docs.sciml.ai/DiffEqDevDocs/dev/internals/tableaus/). If none is passed in, both solvers will default to using Radau second-order with five collocation points.
99130

100131
**Pyomo** and **InfiniteOpt** each have their own built-in collocation methods.
101132
1. **InfiniteOpt**: The list of InfiniteOpt collocation methods can be found [in the table on this page](https://infiniteopt.github.io/InfiniteOpt.jl/stable/guide/derivative/). If none is passed in, the solver defaults to `FiniteDifference(Backward())`, which is effectively implicit Euler.
102133
2. **Pyomo**: The list of Pyomo collocation methods can be found [at the bottom of this page](https://github.com/SciML/Pyomo.jl). If none is passed in, the solver defaults to a `LagrangeRadau(3)`.
103134

135+
Some examples of the latter two collocations:
136+
```julia
137+
PyomoCollocation("ipopt", LagrangeRadau(2))
138+
InfiniteOptCollocation(Ipopt.Optimizer, OrthogonalCollocation(3))
139+
```
140+
104141
```@docs; canonical = false
105142
JuMPCollocation
106143
InfiniteOptCollocation

0 commit comments

Comments
 (0)