Skip to content

Commit 2397d9a

Browse files
committed
Change independent variable of incomplete systems
1 parent 5ca058d commit 2397d9a

File tree

3 files changed

+23
-28
lines changed

3 files changed

+23
-28
lines changed

docs/src/tutorials/change_independent_variable.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ D = Differential(t)
2424
M1 = ODESystem([
2525
D(D(y)) ~ -g, D(x) ~ v # constant horizontal velocity
2626
], t; defaults = [
27-
y => 0.0
27+
y => 0.0 # start on the ground
2828
], initialization_eqs = [
2929
#x ~ 0.0, # TODO: handle? # hide
3030
D(x) ~ D(y) # equal initial horizontal and vertical velocity (45 °)
31-
], name = :M) |> complete
31+
], name = :M)
3232
M1s = structural_simplify(M1)
3333
```
3434
This is the standard parametrization that arises naturally from kinematics and Newton's laws.
@@ -42,7 +42,7 @@ There are at least three ways of answering this:
4242
We will demonstrate the last method by changing the independent variable from $t$ to $x$.
4343
This transformation is well-defined for any non-zero horizontal velocity $v$.
4444
```@example changeivar
45-
M2 = change_independent_variable(M1, M1.x; dummies = true)
45+
M2 = change_independent_variable(M1, x; dummies = true)
4646
@assert M2.x isa Num # hide
4747
M2s = structural_simplify(M2; allow_symbolic = true)
4848
```
@@ -79,12 +79,11 @@ initialization_eqs = [
7979
]
8080
M1 = ODESystem(eqs, t, [Ω, a], []; initialization_eqs, name = :M)
8181
M1 = compose(M1, r, m, Λ)
82-
M1 = complete(M1; flatten = false)
8382
M1s = structural_simplify(M1)
8483
```
8584
Of course, we can solve this ODE as it is:
8685
```@example changeivar
87-
prob = ODEProblem(M1s, [M1.a => 1.0, M1.r.Ω => 5e-5, M1.m.Ω => 0.3], (0.0, -3.3), [])
86+
prob = ODEProblem(M1s, [M1s.a => 1.0, M1s.r.Ω => 5e-5, M1s.m.Ω => 0.3], (0.0, -3.3), [])
8887
sol = solve(prob, Tsit5(); reltol = 1e-5)
8988
@assert Symbol(sol.retcode) == :Unstable # surrounding text assumes this was unstable # hide
9089
plot(sol, idxs = [M1.a, M1.r.Ω/M1.Ω, M1.m.Ω/M1.Ω, M1.Λ.Ω/M1.Ω])
@@ -111,7 +110,7 @@ M3 = change_independent_variable(M2, b, [Da(b) ~ exp(-b), a ~ exp(b)])
111110
We can now solve and plot the ODE in terms of $b$:
112111
```@example changeivar
113112
M3s = structural_simplify(M3; allow_symbolic = true)
114-
prob = ODEProblem(M3s, [M3.r.Ω => 5e-5, M3.m.Ω => 0.3], (0, -15), [])
113+
prob = ODEProblem(M3s, [M3s.r.Ω => 5e-5, M3s.m.Ω => 0.3], (0, -15), [])
115114
sol = solve(prob, Tsit5())
116115
@assert Symbol(sol.retcode) == :Success # surrounding text assumes the solution was successful # hide
117116
plot(sol, idxs = [M3.r.Ω/M3.Ω, M3.m.Ω/M3.Ω, M3.Λ.Ω/M3.Ω])

src/systems/diffeqs/basic_transformations.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,7 @@ function change_independent_variable(sys::AbstractODESystem, iv, eqs = []; dummi
103103
iv2_of_iv1 = unwrap(iv) # e.g. u(t)
104104
iv1 = get_iv(sys) # e.g. t
105105

106-
if !iscomplete(sys)
107-
error("System $(nameof(sys)) is incomplete. Complete it first!")
108-
elseif is_dde(sys)
106+
if is_dde(sys)
109107
error("System $(nameof(sys)) contains delay differential equations (DDEs). This is currently not supported!")
110108
elseif isscheduled(sys)
111109
error("System $(nameof(sys)) is structurally simplified. Change independent variable before structural simplification!")

test/basic_transformations.jl

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ end
2828
@testset "Change independent variable (trivial)" begin
2929
@variables x(t) y(t)
3030
eqs1 = [D(D(x)) ~ D(x) + x, D(y) ~ 1]
31-
M1 = ODESystem(eqs1, t; name = :M) |> complete
32-
M2 = change_independent_variable(M1, M1.y)
31+
M1 = ODESystem(eqs1, t; name = :M)
32+
M2 = change_independent_variable(M1, y)
3333
eqs2 = substitute(equations(M2), M2.y => M1.t) # system should be equivalent when parametrized with y (since D(y) ~ 1), so substitute back ...
3434
@test eqs1[1] == only(eqs2) # ... and check that the equations are unmodified
3535
end
@@ -43,8 +43,8 @@ end
4343
D(s) ~ 1 / (2*s)
4444
]
4545
initialization_eqs = [x ~ 1.0, y ~ 1.0, D(y) ~ 0.0]
46-
M1 = ODESystem(eqs, t; initialization_eqs, name = :M) |> complete
47-
M2 = change_independent_variable(M1, M1.s; dummies = true)
46+
M1 = ODESystem(eqs, t; initialization_eqs, name = :M)
47+
M2 = change_independent_variable(M1, s; dummies = true)
4848

4949
M1 = structural_simplify(M1; allow_symbolic = true)
5050
M2 = structural_simplify(M2; allow_symbolic = true)
@@ -75,11 +75,11 @@ end
7575
]
7676
M1 = ODESystem(eqs, t, [Ω, a, ȧ, ϕ], []; name = :M)
7777
M1 = compose(M1, r, m, Λ)
78-
M1 = complete(M1; flatten = false)
7978

8079
# Apply in two steps, where derivatives are defined at each step: first t -> a, then a -> b
8180
M2 = change_independent_variable(M1, M1.a; dummies = true)
82-
a, ȧ, Ω, Ωr, Ωm, ΩΛ, ϕ, aˍt, aˍtt = M2.a, M2.ȧ, M2.Ω, M2.r.Ω, M2.m.Ω, M2.Λ.Ω, M2.ϕ, M2.aˍt, M2.aˍtt
81+
M2c = complete(M2) # just for the following equation comparison (without namespacing)
82+
a, ȧ, Ω, Ωr, Ωm, ΩΛ, ϕ, aˍt, aˍtt = M2c.a, M2c.ȧ, M2c.Ω, M2c.r.Ω, M2c.m.Ω, M2c.Λ.Ω, M2c.ϕ, M2c.aˍt, M2c.aˍtt
8383
Da = Differential(a)
8484
@test Set(equations(M2)) == Set([
8585
aˍt ~# 1st order dummy equation
@@ -103,16 +103,16 @@ end
103103

104104
@testset "Change independent variable (simple)" begin
105105
@variables x(t)
106-
Mt = ODESystem([D(x) ~ 2*x], t; name = :M) |> complete
107-
Mx = change_independent_variable(Mt, Mt.x; dummies = true)
106+
Mt = ODESystem([D(x) ~ 2*x], t; name = :M)
107+
Mx = change_independent_variable(Mt, x; dummies = true)
108108
@test (@variables x xˍt(x) xˍtt(x); Set(equations(Mx)) == Set([xˍt ~ 2*x, xˍtt ~ 2*xˍt]))
109109
end
110110

111111
@testset "Change independent variable (free fall)" begin
112112
@variables x(t) y(t)
113113
@parameters g v # gravitational acceleration and constant horizontal velocity
114-
Mt = ODESystem([D(D(y)) ~ -g, D(x) ~ v], t; name = :M) |> complete # gives (x, y) as function of t, ...
115-
Mx = change_independent_variable(Mt, Mt.x; dummies = false) # ... but we want y as a function of x
114+
Mt = ODESystem([D(D(y)) ~ -g, D(x) ~ v], t; name = :M) # gives (x, y) as function of t, ...
115+
Mx = change_independent_variable(Mt, x; dummies = false) # ... but we want y as a function of x
116116
Mx = structural_simplify(Mx; allow_symbolic = true)
117117
Dx = Differential(Mx.x)
118118
prob = ODEProblem(Mx, [Mx.y => 0.0, Dx(Mx.y) => 1.0], (0.0, 20.0), [g => 9.81, v => 10.0]) # 1 = dy/dx = (dy/dt)/(dx/dt) means equal initial horizontal and vertical velocities
@@ -127,8 +127,8 @@ end
127127
M1 = ODESystem([ # crazy non-autonomous non-linear 2nd order ODE
128128
D(D(y)) ~ D(x)^2 + D(y^3) |> expand_derivatives # expand D(y^3) # TODO: make this test 3rd order
129129
D(x) ~ x^4 + y^5 + t^6
130-
], t; name = :M) |> complete
131-
M2 = change_independent_variable(M1, M1.x; dummies = true)
130+
], t; name = :M)
131+
M2 = change_independent_variable(M1, x; dummies = true)
132132

133133
# Compare to pen-and-paper result
134134
@independent_variables x
@@ -144,18 +144,16 @@ end
144144
@testset "Change independent variable (errors)" begin
145145
@variables x(t) y z(y) w(t) v(t)
146146
M = ODESystem([D(x) ~ 0, v ~ x], t; name = :M)
147-
@test_throws "incomplete" change_independent_variable(M, M.x)
148-
M = complete(M)
149-
@test_throws "singular" change_independent_variable(M, M.x)
147+
@test_throws "singular" change_independent_variable(M, x)
150148
@test_throws "structurally simplified" change_independent_variable(structural_simplify(M), y)
151149
@test_throws "Got 0 equations:" change_independent_variable(M, w)
152150
@test_throws "Got 0 equations:" change_independent_variable(M, v)
153-
M = ODESystem([D(x) ~ 1, v ~ 1], t; name = :M) |> complete
154-
@test_throws "Got 2 equations:" change_independent_variable(M, M.x, [D(x) ~ 2])
151+
M = ODESystem([D(x) ~ 1, v ~ 1], t; name = :M)
152+
@test_throws "Got 2 equations:" change_independent_variable(M, x, [D(x) ~ 2])
155153
@test_throws "not a function of the independent variable" change_independent_variable(M, y)
156154
@test_throws "not a function of the independent variable" change_independent_variable(M, z)
157155
M = ODESystem([D(x) ~ 0, v ~ x], t; name = :M)
158156
@variables x(..) # require explicit argument
159-
M = ODESystem([D(x(t)) ~ x(t-1)], t; name = :M) |> complete
160-
@test_throws "DDE" change_independent_variable(M, M.x)
157+
M = ODESystem([D(x(t)) ~ x(t-1)], t; name = :M)
158+
@test_throws "DDE" change_independent_variable(M, x(t))
161159
end

0 commit comments

Comments
 (0)