Skip to content

Commit 4c900fb

Browse files
authored
chore: update to ModelingToolkit.jl v11 (#279)
- docs: update code blocks to match new style using `get_u0`, `get_p` - refactor!: Automatically infer variables and parameters in `System` construction. No longer need to pass `u0` and `p` arrays explicitly. - refactor!: remove `defaults` keyword dropped in MTK v11 - refactor!: use `scalarize` operations where appropriate to simplify model definitions - refactor: use `AstrodynamicalModels.CartesianSTM()` or `I(6)` in place of literal matrices - test: Use `MTK.get_u0`, `MTK.get_p` to correctly handle passing of variable and parameter values to `ODEFunction`. Closes #270
1 parent 1db98ac commit 4c900fb

File tree

26 files changed

+717
-823
lines changed

26 files changed

+717
-823
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ SPICE = "5bab7191-041a-5c2e-a744-024b9c3a5062"
1717

1818
[compat]
1919
AstrodynamicalCalculations = "0.5, 1"
20-
AstrodynamicalModels = "3.4"
20+
AstrodynamicalModels = "3.4, 3.8"
2121
AstrodynamicalSolvers = "0.5, 1"
2222
DocStringExtensions = "0.9"
2323
EphemerisSources = "1.0.0"

lib/AstrodynamicalModels/Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ AstrodynamicalCalculations = "1"
2828
DocStringExtensions = "0.9"
2929
LinearAlgebra = "1"
3030
Memoize = "0.4"
31-
ModelingToolkit = "9"
31+
ModelingToolkit = "11"
3232
SPICEBodies = "1"
3333
SciMLBase = "2"
3434
StaticArrays = "1"
35-
Symbolics = "5, 6"
35+
Symbolics = "7"
3636
julia = "1.10"

lib/AstrodynamicalModels/docs/src/models/attitude/index.qmd

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ _Quaternion kinematics, and dynamics!_
77
#| echo: false
88
using AstrodynamicalModels
99
using ModelingToolkit
10+
using ModelingToolkit: get_u0, get_p
1011
```
1112

1213
## Overview
@@ -57,7 +58,10 @@ Finally, let's construct a Julia function which implements these dynamics!
5758
#| echo: true
5859
#| output: true
5960
f = AttitudeFunction()
60-
let u = randn(7), p = randn(15), t = 0
61-
f(u, p, t)
61+
let u = randn(7), p = [randn(9), randn(3), rand(3)], t = 0
62+
sys = f.sys
63+
u0 = get_u0(sys, ModelingToolkit.unknowns(sys) .=> u)
64+
p = get_p(sys, [:J => p[1], :L => p[2], :f => p[3]]) # Or get_p(sys, ModelingToolkit.parameters(sys) .=> p)
65+
f(u0, p, t)
6266
end
6367
```

lib/AstrodynamicalModels/docs/src/models/entry/index.qmd

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ _Also known as canonical entry dynamics!_
77
#| output: false
88
using AstrodynamicalModels
99
using ModelingToolkit
10+
using ModelingToolkit: get_u0, get_p
1011
```
1112

1213
## Overview
@@ -44,6 +45,9 @@ Finally, let's construct a Julia function which implements these dynamics!
4445
#| echo: true
4546
f = PlanarEntryFunction()
4647
let u = abs.(randn(4)), p = abs.(randn(7)), t = 0
47-
f(u, p, t)
48+
sys = f.sys
49+
u0 = get_u0(sys, [:γ, :v, :r, :θ] .=> u) # Or get_u0(sys, ModelingToolkit.unknowns(sys) .=> u)
50+
p = get_p(sys, ModelingToolkit.parameters(sys) .=> p)
51+
f(u0, p, t)
4852
end
4953
```

lib/AstrodynamicalModels/docs/src/models/nb/index.qmd

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ _Also known as NBP dynamics!_
77
#| output: false
88
using AstrodynamicalModels
99
using ModelingToolkit
10+
using ModelingToolkit: get_u0, get_p
1011
```
1112

1213
## Overview
@@ -49,7 +50,10 @@ Finally, let's construct a Julia function which implements these dynamics!
4950
```{julia}
5051
#| echo: true
5152
f = NBFunction(2)
52-
let u = randn(12), m = randn(2), G = rand(), t = 0
53-
f(u, [G, m...], t)
53+
let u = randn(12), m = randn(2), G = randn(), t = 0
54+
sys = f.sys
55+
u0 = get_u0(sys, ModelingToolkit.unknowns(sys) .=> u)
56+
p = get_p(sys, [:m => m, :G => G]) # Or get_p(sys, ModelingToolkit.parameters(sys) .=> [m, G])
57+
f(u0, p, t)
5458
end
5559
```

lib/AstrodynamicalModels/docs/src/models/r2b/index.qmd

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ _Also known as R2BP dynamics!_
77
#| output: false
88
using AstrodynamicalModels
99
using ModelingToolkit
10+
using ModelingToolkit: get_u0, get_p
1011
```
1112

1213
## Overview
@@ -65,7 +66,10 @@ Finally, let's construct a Julia function which implements these dynamics!
6566
```{julia}
6667
#| echo: true
6768
f = R2BFunction()
68-
let u = randn(6), p = [3e6], t = 0
69-
f(u, p, t)
69+
let u = randn(6), p = 3e6, t = 0
70+
sys = f.sys
71+
u0 = get_u0(sys, [:x, :y, :z, :ẋ, :ẏ, :ż] .=> u) # Or get_u0(sys, ModelingToolkit.unknowns(sys) .=> u)
72+
p = get_p(sys, [:μ => p]) # Or get_p(sys, ModelingToolkit.parameters(sys) .=> p)
73+
f(u0, p, t)
7074
end
7175
```

lib/AstrodynamicalModels/src/AstrodynamicalModels.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,14 @@ Return the parameter vector for an `Orbit`.
579579
"""
580580
parameters(orbit::Orbit) = orbit.parameters
581581

582+
"""
583+
Return the operating point for an `Orbit`.
584+
"""
585+
op(orbit::Orbit) = [
586+
[:x, :y, :z, :ẋ, :ẏ, :ż] .=> orbit.state
587+
=> orbit.parameters[1]
588+
]
589+
582590
Base.getindex(orbit::AstrodynamicalOrbit, args...) = Base.getindex(state(orbit), args...)
583591
Base.setindex!(orbit::AstrodynamicalOrbit, args...) = Base.setindex!(state(orbit), args...)
584592

lib/AstrodynamicalModels/src/Attitude.jl

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -167,26 +167,20 @@ model = Attitude()
167167
@memoize function AttitudeSystem(;
168168
stm = false,
169169
name = :Attitude,
170-
defaults = Pair{ModelingToolkit.Num,<:Number}[],
171170
kwargs...,
172171
)
173172

174173
@variables begin
175174
q(t)[1:4], [description = "scalar-last attitude quaternion"]
176175
ω(t)[1:3], [description = "body rates in radians per second"]
177176
end
177+
178178
@parameters begin
179179
J[1:3, 1:3], [description = "moment of inertial matrix"]
180180
L[1:3], [description = "lever arm where input torque is applied"]
181181
f[1:3], [description = "input torque"]
182182
end
183183

184-
q = Symbolics.scalarize(q)
185-
ω = Symbolics.scalarize(ω)
186-
J = Symbolics.scalarize(J)
187-
L = Symbolics.scalarize(L)
188-
f = Symbolics.scalarize(f)
189-
190184
A = [
191185
0 ω[3] -ω[2] ω[1]
192186
-ω[3] 0 ω[1] ω[2]
@@ -201,25 +195,15 @@ model = Attitude()
201195
]
202196

203197
eqs = [
204-
D.(q) .~ (1 // 2) * (A * q) ;
205-
D.(ω) .~ (-inv(J) * ωx * J * ω + inv(J) * L + f)
198+
Symbolics.scalarize(D(q) ~ (1 // 2) * (A * q)) ;
199+
Symbolics.scalarize(D(ω) ~ -inv(J) * ωx * (J * ω) + inv(J) * L + f)
206200
]
207201

208-
u = [q; ω]
209-
210202
if stm
211-
@variables (Φ(t))[1:7, 1:7], [description = "state transition matrix estimate"]
212-
A = Symbolics.jacobian(map(el -> el.rhs, eqs), u)
213-
214-
Φ = Symbolics.scalarize(Φ)
215-
216-
LHS = D.(Φ)
217-
RHS = A * Φ
218-
219-
eqs = [eqs; vec([LHS[i] ~ RHS[i] for i in eachindex(LHS)])]
203+
@variables Φ(t)[1:7, 1:7], [description = "state transition matrix estimate"]
220204

221-
u = [u; vec(Φ)]
222-
defaults = [defaults; vec(Φ .=> Float64.(I(7)))]
205+
A = Symbolics.jacobian(map(el -> el.rhs, eqs), vcat(q, ω))
206+
eqs = vcat(eqs, vec(Symbolics.scalarize(D(Φ) ~ A * Φ)))
223207
end
224208

225209
if string(name) == "Attitude" && stm
@@ -228,9 +212,8 @@ model = Attitude()
228212
modelname = name
229213
end
230214

231-
return System(eqs, t, u, [vec(J); L; f];
215+
return System(eqs, t;
232216
name = modelname,
233-
defaults,
234217
kwargs...,
235218
)
236219
end
@@ -248,15 +231,18 @@ are passed directly to `SciMLBase.ODEFunction`.
248231
249232
```julia
250233
f = AttitudeFunction()
251-
let u = randn(7), p = randn(15), t = NaN # time invariant
252-
f(u, p, t)
234+
let u = randn(7), p = [randn(9), randn(3), rand(3)], t = NaN
235+
sys = f.sys
236+
u0 = get_u0(sys, ModelingToolkit.unknowns(sys) .=> u)
237+
p = get_p(sys, [:J => p[1], :L => p[2], :f => p[3]]) # Or get_p(sys, ModelingToolkit.parameters(sys) .=> p)
238+
f(u0, p, t)
253239
end
254240
```
255241
"""
256242
@memoize function AttitudeFunction(; stm = false, name = :Attitude, kwargs...)
257243
defaults = (; jac = true)
258244
options = merge(defaults, kwargs)
259-
sys = complete(AttitudeSystem(; stm = stm, name = name); split = false)
245+
sys = complete(AttitudeSystem(; stm, name); split = true)
260246
return ODEFunction{true,SciMLBase.FullSpecialize}(
261247
sys;
262248
options...,

lib/AstrodynamicalModels/src/CR3BP.jl

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ model = CR3BSystem(; stm=true)
4646
@memoize function CR3BSystem(;
4747
stm = false,
4848
name = :CR3B,
49-
defaults = Pair{ModelingToolkit.Num,<:Number}[],
5049
kwargs...,
5150
)
5251
@parameters μ
@@ -73,20 +72,10 @@ model = CR3BSystem(; stm=true)
7372
);
7473
]
7574

76-
u = [r; v]
77-
7875
if stm
79-
@variables (Φ(t))[1:6, 1:6], [description = "state transition matrix estimate"]
80-
A = Symbolics.jacobian(map(el -> el.rhs, eqs), u)
81-
82-
Φ = Symbolics.scalarize(Φ)
83-
LHS = D.(Φ)
84-
RHS = A * Φ
85-
86-
eqs = [eqs; vec([LHS[i] ~ RHS[i] for i in eachindex(LHS)])]
87-
88-
u = [u; vec(Φ)]
89-
defaults = [defaults; vec.=> Float64.(I(6)))]
76+
@variables Φ(t)[1:6, 1:6], [description = "state transition matrix estimate"]
77+
A = Symbolics.jacobian(map(el -> el.rhs, eqs), [r; v])
78+
eqs = [eqs; vec(Symbolics.scalarize(D(Φ) ~ A * Φ))]
9079
end
9180

9281
if string(name) == "CR3B" && stm
@@ -95,9 +84,8 @@ model = CR3BSystem(; stm=true)
9584
modelname = name
9685
end
9786

98-
return System(eqs, t, u, [μ];
87+
return System(eqs, t;
9988
name = modelname,
100-
defaults,
10189
kwargs...,
10290
)
10391
end
@@ -119,15 +107,18 @@ directly to `SciMLBase.ODEFunction`.
119107
120108
```julia
121109
f = CR3BFunction(; stm=false, jac=true)
122-
let u = randn(6), p = randn(1), t = 0
123-
f(u, p, t)
110+
let u = randn(6), p = randn(), t = 0
111+
sys = f.sys
112+
u0 = get_u0(sys, [:x, :y, :z, :ẋ, :ẏ, :ż] .=> u) # Or get_u0(sys, ModelingToolkit.unknowns(sys) .=> u)
113+
p = get_p(sys, [:μ => p]) # Or get_p(sys, ModelingToolkit.parameters(sys) .=> p)
114+
f(u0, p, t)
124115
end
125116
```
126117
"""
127118
@memoize function CR3BFunction(; stm = false, name = :CR3B, kwargs...)
128119
defaults = (; jac = true)
129120
options = merge(defaults, kwargs)
130-
sys = complete(CR3BSystem(; stm = stm, name = name); split = false)
121+
sys = complete(CR3BSystem(; stm, name); split = true)
131122
return ODEFunction{true,SciMLBase.FullSpecialize}(
132123
sys;
133124
options...,
@@ -146,13 +137,12 @@ AstrodynamicalModels.CR3BOrbit(; state::AbstractVector, parameters::AbstractVect
146137
"""
147138
Return an `ODEProblem` for the provided CR3B system.
148139
"""
149-
CR3BProblem(u0, tspan, p; kwargs...) = ODEProblem(CR3BFunction(), u0, tspan, p; kwargs...)
140+
CR3BProblem(op, tspan; kwargs...) = ODEProblem(complete(CR3BSystem()), op, tspan; kwargs...)
150141
CR3BProblem(orbit::AstrodynamicalOrbit, tspan::Union{<:Tuple,<:AbstractArray}; kwargs...) =
151142
ODEProblem(
152-
CR3BFunction(),
153-
AstrodynamicalModels.state(orbit),
154-
tspan,
155-
AstrodynamicalModels.parameters(orbit);
143+
complete(CR3BSystem()),
144+
AstrodynamicalModels.op(orbit),
145+
tspan;
156146
kwargs...,
157147
)
158148
CR3BProblem(orbit::AstrodynamicalOrbit, Δt; kwargs...) =

lib/AstrodynamicalModels/src/Entry.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ model = PlanarEntrySystem()
7777
"""
7878
@memoize function PlanarEntrySystem(;
7979
name = :PlanarEntry,
80-
defaults = Pair{ModelingToolkit.Num,<:Number}[],
8180
kwargs...,
8281
)
8382

@@ -114,9 +113,8 @@ model = PlanarEntrySystem()
114113
D(θ) ~ (v / r) * cos(γ),
115114
]
116115

117-
model = System(eqs, t, [γ, v, r, θ], [R, P, H, m, A, C, μ];
118-
name,
119-
defaults,
116+
model = System(eqs, t;
117+
name = name,
120118
kwargs...,
121119
)
122120

@@ -142,14 +140,17 @@ passed directly to `SciMLBase.ODEFunction`.
142140
```julia
143141
f = PlanarEntryFunction()
144142
let u = randn(4), p = randn(7), t = NaN # time invariant
145-
f(u, p, t)
143+
sys = f.sys
144+
u0 = get_u0(sys, [:γ, :v, :r, :θ] .=> u) # Or get_u0(sys, ModelingToolkit.unknowns(sys) .=> u)
145+
p = get_p(sys, ModelingToolkit.parameters(sys) .=> p)
146+
f(u0, p, t)
146147
end
147148
```
148149
"""
149150
@memoize function PlanarEntryFunction(; name = :PlanarEntry, kwargs...)
150151
defaults = (; jac = true)
151152
options = merge(defaults, kwargs)
152-
sys = complete(PlanarEntrySystem(; name = name); split = false)
153+
sys = complete(PlanarEntrySystem(; name); split = true)
153154
return ODEFunction{true,SciMLBase.FullSpecialize}(
154155
sys;
155156
options...,

0 commit comments

Comments
 (0)