@@ -51,7 +51,7 @@ function liouville_transform(sys::AbstractODESystem; kwargs...)
5151end
5252
5353"""
54- function change_independent_variable(sys::AbstractODESystem, iv, eq = nothing ; dummies = false, simplify = true, verbose = false, kwargs...)
54+ function change_independent_variable(sys::AbstractODESystem, iv, eqs = [] ; dummies = false, simplify = true, verbose = false, kwargs...)
5555
5656Transform the independent variable (e.g. ``t``) of the ODE system `sys` to a dependent variable `iv` (e.g. ``f(t)``).
5757An equation in `sys` must define the rate of change of the new independent variable (e.g. ``df(t)/dt``).
@@ -89,7 +89,7 @@ julia> unknowns(M′)
8989 y(x)
9090```
9191"""
92- function change_independent_variable (sys:: AbstractODESystem , iv, eq = nothing ; dummies = false , simplify = true , verbose = false , kwargs... )
92+ function change_independent_variable (sys:: AbstractODESystem , iv, eqs = [] ; dummies = false , simplify = true , verbose = false , kwargs... )
9393 if ! iscomplete (sys)
9494 error (" Cannot change independent variable of incomplete system $(nameof (sys)) " )
9595 elseif isscheduled (sys)
@@ -100,9 +100,10 @@ function change_independent_variable(sys::AbstractODESystem, iv, eq = nothing; d
100100
101101 iv = unwrap (iv)
102102 iv1 = get_iv (sys) # e.g. t
103-
104103 if ! iscall (iv) || ! isequal (only (arguments (iv)), iv1)
105104 error (" New independent variable $iv is not a function of the independent variable $iv1 of the system $(nameof (sys)) " )
105+ elseif ! isautonomous (sys) && isempty (findall (eq -> isequal (eq. lhs, iv1), eqs))
106+ error (" System $(nameof (sys)) is autonomous in $iv1 . An equation of the form $iv1 ~ F($iv ) must be provided." )
106107 end
107108
108109 iv2func = iv # e.g. a(t)
@@ -111,50 +112,53 @@ function change_independent_variable(sys::AbstractODESystem, iv, eq = nothing; d
111112 D1 = Differential (iv1)
112113 D2 = Differential (iv2)
113114
115+ iv1name = nameof (iv1) # e.g. t
116+ iv1func, = @variables $ iv1name (iv2) # e.g. t(a)
117+
114118 div2name = Symbol (iv2name, :_t )
115119 div2, = @variables $ div2name (iv2) # e.g. a_t(a)
116120
117121 ddiv2name = Symbol (iv2name, :_tt )
118122 ddiv2, = @variables $ ddiv2name (iv2) # e.g. a_tt(a)
119123
120- eqs = ModelingToolkit. get_eqs (sys) |> copy # don't modify original system
121- ! isnothing (eq) && push! (eqs, eq)
122- vars = []
123- div2_div1 = nothing
124- for (i, eq) in enumerate (eqs)
125- verbose && println (" 1. " , eq)
126-
124+ function transform (ex)
127125 # 1) Substitute f(t₁) => f(t₂(t₁)) in all variables
128- vars = Symbolics. get_variables (eq)
126+ verbose && println (" 1. " , ex)
127+ vars = Symbolics. get_variables (ex)
129128 for var1 in vars
130129 if Symbolics. iscall (var1) && ! isequal (var1, iv2func) # && isequal(only(arguments(var1)), iv1) # skip e.g. constants
131130 name = nameof (operation (var1))
132131 var2, = @variables $ name (iv2func)
133- eq = substitute (eq , var1 => var2; fold = false )
132+ ex = substitute (ex , var1 => var2; fold = false )
134133 end
135134 end
136- verbose && println (" 2. " , eq)
137135
138136 # 2) Substitute in dummy variables for dⁿt₂/dt₁ⁿ
139- eq = expand_derivatives (eq) # expand out with chain rule to get d(iv2)/d(iv1)
140- verbose && println (" 3. " , eq)
141- eq = substitute (eq, D1 (D1 (iv2func)) => ddiv2) # order 2 # TODO : more orders
142- eq = substitute (eq, D1 (iv2func) => div2) # order 1; e.g. D(a(t)) => a_t(t)
143- eq = substitute (eq, iv2func => iv2) # order 0; make iv2 independent
144- verbose && println (" 4. " , eq)
145- verbose && println ()
146-
147- eqs[i] = eq
148-
149- if isequal (eq. lhs, div2)
150- div2_div1 = eq. rhs
151- end
137+ verbose && println (" 2. " , ex)
138+ ex = expand_derivatives (ex) # expand out with chain rule to get d(iv2)/d(iv1)
139+ verbose && println (" 3. " , ex)
140+ ex = substitute (ex, D1 (D1 (iv2func)) => ddiv2) # order 2 # TODO : more orders
141+ ex = substitute (ex, D1 (iv2func) => div2) # order 1; e.g. D(a(t)) => a_t(t)
142+ ex = substitute (ex, iv2func => iv2) # order 0; make iv2 independent
143+ verbose && println (" 4. " , ex)
144+ ex = substitute (ex, iv1 => iv1func) # if autonomous
145+ verbose && println (" 5. " , ex)
146+ return ex
152147 end
153148
154- verbose && println (" Found $div2 = $div2_div1 " )
155- if isnothing (div2_div1)
149+ eqs = [get_eqs (sys); eqs]
150+ eqs = map (transform, eqs)
151+
152+ initialization_eqs = map (transform, get_initialization_eqs (sys))
153+
154+ div2_div1_idxs = findall (eq -> isequal (eq. lhs, div2), eqs)
155+ if length (div2_div1_idxs) == 0
156156 error (" No equation for $D1 ($iv2func ) was specified." )
157- elseif isequal (div2_div1, 0 )
157+ elseif length (div2_div1_idxs) >= 2
158+ error (" Multiple equations for $D1 ($iv2func ) were specified." )
159+ end
160+ div2_div1 = eqs[only (div2_div1_idxs)]. rhs
161+ if isequal (div2_div1, 0 )
158162 error (" Cannot change independent variable from $iv1 to $iv2 with singular transformation $(div2 ~ div2_div1) ." )
159163 end
160164
@@ -167,14 +171,14 @@ function change_independent_variable(sys::AbstractODESystem, iv, eq = nothing; d
167171 push! (eqs, ddiv2 ~ ddiv1_ddiv2) # e.g. https://math.stackexchange.com/questions/249253/second-derivative-of-the-inverse-function # TODO : higher orders
168172
169173 # 4) If requested, instead remove and insert dummy equations
170- if ! dummies
174+ if ! dummies # TODO : make dummies = false work with more fields!
171175 dummyidxs = findall (eq -> isequal (eq. lhs, div2) || isequal (eq. lhs, ddiv2), eqs)
172176 dummyeqs = splice! (eqs, dummyidxs) # return and remove dummy equations
173177 dummysubs = Dict (eq. lhs => eq. rhs for eq in dummyeqs)
174178 eqs = substitute .(eqs, Ref (dummysubs)) # don't iterate over dummysubs
175179 end
176180
177181 # 5) Recreate system with new equations
178- sys2 = typeof (sys)(eqs, iv2; name = nameof (sys), description = description (sys), kwargs... )
182+ sys2 = typeof (sys)(eqs, iv2; initialization_eqs, name = nameof (sys), description = description (sys), kwargs... )
179183 return sys2
180184end
0 commit comments