@@ -51,7 +51,7 @@ function liouville_transform(sys::AbstractODESystem; kwargs...)
51
51
end
52
52
53
53
"""
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...)
55
55
56
56
Transform the independent variable (e.g. ``t``) of the ODE system `sys` to a dependent variable `iv` (e.g. ``f(t)``).
57
57
An 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′)
89
89
y(x)
90
90
```
91
91
"""
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... )
93
93
if ! iscomplete (sys)
94
94
error (" Cannot change independent variable of incomplete system $(nameof (sys)) " )
95
95
elseif isscheduled (sys)
@@ -100,9 +100,10 @@ function change_independent_variable(sys::AbstractODESystem, iv, eq = nothing; d
100
100
101
101
iv = unwrap (iv)
102
102
iv1 = get_iv (sys) # e.g. t
103
-
104
103
if ! iscall (iv) || ! isequal (only (arguments (iv)), iv1)
105
104
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." )
106
107
end
107
108
108
109
iv2func = iv # e.g. a(t)
@@ -111,50 +112,53 @@ function change_independent_variable(sys::AbstractODESystem, iv, eq = nothing; d
111
112
D1 = Differential (iv1)
112
113
D2 = Differential (iv2)
113
114
115
+ iv1name = nameof (iv1) # e.g. t
116
+ iv1func, = @variables $ iv1name (iv2) # e.g. t(a)
117
+
114
118
div2name = Symbol (iv2name, :_t )
115
119
div2, = @variables $ div2name (iv2) # e.g. a_t(a)
116
120
117
121
ddiv2name = Symbol (iv2name, :_tt )
118
122
ddiv2, = @variables $ ddiv2name (iv2) # e.g. a_tt(a)
119
123
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)
127
125
# 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)
129
128
for var1 in vars
130
129
if Symbolics. iscall (var1) && ! isequal (var1, iv2func) # && isequal(only(arguments(var1)), iv1) # skip e.g. constants
131
130
name = nameof (operation (var1))
132
131
var2, = @variables $ name (iv2func)
133
- eq = substitute (eq , var1 => var2; fold = false )
132
+ ex = substitute (ex , var1 => var2; fold = false )
134
133
end
135
134
end
136
- verbose && println (" 2. " , eq)
137
135
138
136
# 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
152
147
end
153
148
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
156
156
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 )
158
162
error (" Cannot change independent variable from $iv1 to $iv2 with singular transformation $(div2 ~ div2_div1) ." )
159
163
end
160
164
@@ -167,14 +171,14 @@ function change_independent_variable(sys::AbstractODESystem, iv, eq = nothing; d
167
171
push! (eqs, ddiv2 ~ ddiv1_ddiv2) # e.g. https://math.stackexchange.com/questions/249253/second-derivative-of-the-inverse-function # TODO : higher orders
168
172
169
173
# 4) If requested, instead remove and insert dummy equations
170
- if ! dummies
174
+ if ! dummies # TODO : make dummies = false work with more fields!
171
175
dummyidxs = findall (eq -> isequal (eq. lhs, div2) || isequal (eq. lhs, ddiv2), eqs)
172
176
dummyeqs = splice! (eqs, dummyidxs) # return and remove dummy equations
173
177
dummysubs = Dict (eq. lhs => eq. rhs for eq in dummyeqs)
174
178
eqs = substitute .(eqs, Ref (dummysubs)) # don't iterate over dummysubs
175
179
end
176
180
177
181
# 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... )
179
183
return sys2
180
184
end
0 commit comments