Skip to content

Commit acec3cb

Browse files
committed
Change independent variable of simple 1st order system
1 parent a553fd1 commit acec3cb

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

src/systems/diffeqs/basic_transformations.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,34 @@ function liouville_transform(sys::AbstractODESystem)
5555
vars = [unknowns(sys); trJ]
5656
ODESystem(neweqs, t, vars, parameters(sys), checks = false)
5757
end
58+
59+
# TODO: handle case when new iv is a variable already in the system
60+
function change_independent_variable(sys::AbstractODESystem, iv, iv1_of_iv2; kwargs...)
61+
iv1 = ModelingToolkit.get_iv(sys) # old independent variable
62+
iv2 = iv # new independent variable
63+
64+
name2 = nameof(iv2)
65+
iv2func, = @variables $name2(iv1)
66+
67+
eqs = ModelingToolkit.get_eqs(sys) |> copy # don't modify original system
68+
vars = []
69+
for (i, eq) in enumerate(eqs)
70+
vars = Symbolics.get_variables(eq)
71+
for var1 in vars
72+
if Symbolics.iscall(var1) # skip e.g. constants
73+
name = nameof(operation(var1))
74+
var2, = @variables $name(iv2func)
75+
eq = substitute(eq, var1 => var2; fold = false)
76+
end
77+
end
78+
eq = expand_derivatives(eq) # expand out with chain rule to get d(iv2)/d(iv1)
79+
div1_div2 = Differential(iv2)(iv1_of_iv2) |> expand_derivatives
80+
div2_div1 = 1 / div1_div2
81+
eq = substitute(eq, Differential(iv1)(iv2func) => div2_div1) # substitute in d(iv1)/d(iv2)
82+
eq = substitute(eq, iv2func => iv2) # make iv2 independent
83+
eqs[i] = eq
84+
end
85+
86+
sys2 = typeof(sys)(eqs, iv2; name = nameof(sys), description = description(sys), kwargs...)
87+
return sys2
88+
end

test/basic_transformations.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,28 @@ u0 = [x => 1.0,
3232
prob = ODEProblem(sys2, u0, tspan, p, jac = true)
3333
sol = solve(prob, Tsit5())
3434
@test sol[end, end] 1.0742818931017244
35+
36+
@testset "Change independent variable" begin
37+
# Autonomous 1st order (t doesn't appear in the equations)
38+
@independent_variables t
39+
@variables x(t) y(t) z(t)
40+
eqs = [
41+
D(x) ~ y
42+
D(y) ~ 2*D(x)
43+
z ~ x + D(y)
44+
]
45+
@named sys1 = ODESystem(eqs, t)
46+
47+
@independent_variables s
48+
@named sys2 = ModelingToolkit.change_independent_variable(sys1, s, s^2)
49+
50+
sys1 = structural_simplify(sys1)
51+
sys2 = structural_simplify(sys2)
52+
prob1 = ODEProblem(sys1, unknowns(sys1) .=> 1.0, (0.0, 1.0))
53+
prob2 = ODEProblem(sys2, unknowns(sys2) .=> 1.0, (0.0, 1.0))
54+
sol1 = solve(prob1, Tsit5(); reltol = 1e-10, abstol = 1e-10)
55+
sol2 = solve(prob2, Tsit5(); reltol = 1e-10, abstol = 1e-10)
56+
ts = range(0.0, 1.0, length = 50)
57+
ss = .√(ts)
58+
@test isapprox(sol1(ts), sol2(ss); atol = 1e-8)
59+
end

0 commit comments

Comments
 (0)