Skip to content

Commit 9444d93

Browse files
committed
Add tutorial for changing independent variable
1 parent ee21223 commit 9444d93

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

docs/pages.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pages = [
1010
"tutorials/stochastic_diffeq.md",
1111
"tutorials/discrete_system.md",
1212
"tutorials/parameter_identifiability.md",
13+
"tutorials/change_independent_variable.md",
1314
"tutorials/bifurcation_diagram_computation.md",
1415
"tutorials/SampledData.md",
1516
"tutorials/domain_connections.md",
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Changing the independent variable of ODEs
2+
3+
Ordinary differential equations describe the rate of change of some dependent variables with respect to one independent variable.
4+
For the modeler it is often most natural to write down the equations with a particular independent variable, say time $t$.
5+
In many cases there are good reasons for reparametrizing ODEs in terms of a different independent variable:
6+
7+
1. One may want $y(x)$ as a function of $x$ instead of $(x(t), y(t))$ as a function of $t$
8+
2. Some differential equations vary more nicely (e.g. less stiff or better behaved) with respect to one independent variable than another.
9+
3. It can reduce the number of equations that must be solved (e.g. $y(x)$ is one equation, while $(x(t), y(t))$ are two).
10+
11+
To manually change the independent variable of an ODE, one must rewrite all equations in terms of a new variable and transform differentials with the chain rule.
12+
This is mechanical and error-prone.
13+
ModelingToolkit provides the utility function [`change_independent_variable`](@ref) that automates this process.
14+
15+
## 1. Get one dependent variable as a function of another
16+
17+
Consider a projectile shot with some initial velocity in a gravitational field.
18+
```@example changeivar
19+
using ModelingToolkit
20+
@independent_variables t
21+
D = Differential(t)
22+
@variables x(t) y(t)
23+
@parameters g = 9.81 v # gravitational acceleration and constant horizontal velocity
24+
M1 = ODESystem([
25+
D(D(y)) ~ -g, D(x) ~ v # constant horizontal velocity
26+
], t; initialization_eqs = [
27+
#x ~ 0.0, # TODO: handle?
28+
y ~ 0.0, D(x) ~ D(y) # equal initial horizontal and vertical velocity (45 °)
29+
], name = :M) |> complete
30+
M1s = structural_simplify(M1)
31+
```
32+
This is the standard parametrization that arises naturally from kinematics and Newton's laws.
33+
It expresses the position $(x(t), y(t))$ as a function of time $t$.
34+
But suppose we want to determine whether the projectile hits a target 10 meters away.
35+
There are at least three ways of answering this:
36+
* Solve the ODE for $(x(t), y(t))$ and use a callback to terminate when $x$ reaches 10 meters, and evaluate $y$ at the final time.
37+
* Solve the ODE for $(x(t), y(t))$ and use root finding to find the time when $x$ reaches 10 meters, and evaluate $y$ at that time.
38+
* Solve the ODE for $y(x)$ and evaluate it at 10 meters.
39+
40+
We will demonstrate the last method by changing the independent variable from $t$ to $x$.
41+
This transformation is well-defined for any non-zero horizontal velocity $v$.
42+
```@example changeivar
43+
M2 = change_independent_variable(M1, M1.x; dummies = true) |> complete
44+
@assert M2.x isa Num # hide
45+
M2s = structural_simplify(M2; allow_symbolic = true)
46+
```
47+
The derivatives are now with respect to the new independent variable $x$, which can be accessed with `M2.x`.
48+
Notice how the number of equations has also decreased from three to two, as $\mathrm{d}x/\mathrm{d}t$ has been turned into an observed equation.
49+
It is straightforward to evolve the ODE for 10 meters and plot the resulting trajectory $y(x)$:
50+
```@example changeivar
51+
using OrdinaryDiffEq, Plots
52+
prob = ODEProblem(M2s, [], [0.0, 10.0], [v => 8.0]) # throw 10 meters with x-velocity 8 m/s
53+
sol = solve(prob, Tsit5())
54+
plot(sol; idxs = M2.y) # must index by M2.y = y(x); not M1.y = y(t)!
55+
```
56+
57+
## 2. Reduce stiffness by changing to a logarithmic time axis
58+
59+
In cosmology, the [Friedmann equations](https://en.wikipedia.org/wiki/Friedmann_equations) describe the expansion of the universe.
60+
In terms of conformal time $t$, they can be written
61+
```@example changeivar
62+
@variables a(t) Ω(t) Ωr(t) Ωm(t) ΩΛ(t)
63+
M1 = ODESystem([
64+
D(a) ~ √(Ω) * a^2
65+
Ω ~ Ωr + Ωm + ΩΛ
66+
D(Ωm) ~ -3 * D(a)/a * Ωm
67+
D(Ωr) ~ -4 * D(a)/a * Ωr
68+
D(ΩΛ) ~ 0
69+
], t; initialization_eqs = [
70+
ΩΛ + Ωr + Ωm ~ 1
71+
], name = :M) |> complete
72+
M1s = structural_simplify(M1)
73+
```
74+
Of course, we can solve this ODE as it is:
75+
```@example changeivar
76+
prob = ODEProblem(M1s, [M1.a => 1.0, M1.Ωr => 5e-5, M1.Ωm => 0.3], (0.0, -3.3), [])
77+
sol = solve(prob, Tsit5(); reltol = 1e-5)
78+
@assert Symbol(sol.retcode) == :Unstable # surrounding text assumes this was unstable # hide
79+
plot(sol, idxs = [M1.a, M1.Ωr/M1.Ω, M1.Ωm/M1.Ω, M1.ΩΛ/M1.Ω])
80+
```
81+
The solver becomes unstable due to stiffness.
82+
Also notice the interesting dynamics taking place towards the end of the integration (in the early universe), which gets compressed into a very small time interval.
83+
These ODEs would benefit from being defined with respect to a logarithmic "time" that better captures the evolution of the universe through *orders of magnitude* of time.
84+
85+
It is therefore common to write these ODEs in terms of $b = \ln a$.
86+
To do this, we will change the independent variable in two stages; from $t$ to $a$ to $b$.
87+
Notice that $\mathrm{d}a/\mathrm{d}t > 0$ provided that $\Omega > 0$, and $\mathrm{d}b/\mathrm{d}a > 0$, so the transformation is well-defined.
88+
First, we transform from $t$ to $a$:
89+
```@example changeivar
90+
M2 = change_independent_variable(M1, M1.a; dummies = true) |> complete
91+
```
92+
Unlike the original, notice that this system is *non-autonomous* because the independent variable $a$ appears explicitly in the equations!
93+
This means that to change the independent variable from $a$ to $b$, we must provide not only the rate of change relation $db(a)/da = \exp(-b)$, but *also* the equation $a(b) = \exp(b)$ so $a$ can be eliminated in favor of $b$:
94+
```@example changeivar
95+
a = M2.a
96+
Da = Differential(a)
97+
@variables b(a)
98+
M3 = change_independent_variable(M2, b, [Da(b) ~ exp(-b), a ~ exp(b)]) |> complete
99+
```
100+
We can now solve and plot the ODE in terms of $b$:
101+
```@example changeivar
102+
M3s = structural_simplify(M3; allow_symbolic = true)
103+
prob = ODEProblem(M3s, [M3.Ωr => 5e-5, M3.Ωm => 0.3], (0, -15), [])
104+
sol = solve(prob, Tsit5())
105+
@assert Symbol(sol.retcode) == :Success # surrounding text assumes the solution was successful # hide
106+
plot(sol, idxs = [M3.Ωr/M3.Ω, M3.Ωm/M3.Ω, M3.ΩΛ/M3.Ω])
107+
```
108+
Notice that the variables vary "more nicely" with respect to $b$ than $t$, making the solver happier and avoiding numerical problems.

0 commit comments

Comments
 (0)