|
| 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