You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: docs/src/tutorials/change_independent_variable.md
+47-31
Original file line number
Diff line number
Diff line change
@@ -2,42 +2,46 @@
2
2
3
3
Ordinary differential equations describe the rate of change of some dependent variables with respect to one independent variable.
4
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:
5
+
However, in many cases there are good reasons for changing the independent variable:
6
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).
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) 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
10
11
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
12
This is mechanical and error-prone.
13
13
ModelingToolkit provides the utility function [`change_independent_variable`](@ref) that automates this process.
14
14
15
15
## 1. Get one dependent variable as a function of another
16
16
17
-
Consider a projectile shot with some initial velocity in a gravitational field.
17
+
Consider a projectile shot with some initial velocity in a vertical gravitational field with constant horizontal velocity.
18
+
18
19
```@example changeivar
19
20
using ModelingToolkit
20
21
@independent_variables t
21
22
D = Differential(t)
22
23
@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
The derivatives are now with respect to the new independent variable $x$, which can be accessed with `M2.x`.
52
58
53
59
!!! warn
60
+
54
61
At this point `x`, `M1.x`, `M1s.x`, `M2.x`, `M2s.x` are *three* different variables.
55
62
Meanwhile `y`, `M1.y`, `M1s.y`, `M2.y` and `M2s.y` are *four* different variables.
56
63
It can be instructive to inspect these yourself to see their subtle differences.
57
64
58
65
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.
59
66
It is straightforward to evolve the ODE for 10 meters and plot the resulting trajectory $y(x)$:
67
+
60
68
```@example changeivar
61
69
using OrdinaryDiffEq, Plots
62
70
prob = ODEProblem(M2s, [M2s.y => 0.0], [0.0, 10.0], [v => 8.0]) # throw 10 meters with x-velocity 8 m/s
@@ -65,67 +73,75 @@ plot(sol; idxs = M2.y) # must index by M2.y = y(x); not M1.y = y(t)!
65
73
```
66
74
67
75
!!! tip "Usage tips"
76
+
68
77
Look up the documentation of [`change_independent_variable`](@ref) for tips on how to use it.
69
-
78
+
70
79
For example, if you also need $t(x)$, you can tell it to add a differential equation for the old independent variable in terms of the new one using the [inverse function rule](https://en.wikipedia.org/wiki/Inverse_function_rule) (here $\mathrm{d}t/\mathrm{d}x = 1 / (\mathrm{d}x/\mathrm{d}t)$). If you know an analytical expression between the independent variables (here $t = x/v$), you can also pass it directly to the function to avoid the extra differential equation.
71
80
72
81
## 2. Alleviating stiffness by changing to logarithmic time
73
82
74
83
In cosmology, the [Friedmann equations](https://en.wikipedia.org/wiki/Friedmann_equations) describe the expansion of the universe.
75
84
In terms of conformal time $t$, they can be written
85
+
76
86
```@example changeivar
77
87
@variables a(t) Ω(t)
78
88
a = GlobalScope(a) # global var needed by all species
79
89
function species(w; kw...)
80
-
eqs = [D(Ω) ~ -3(1 + w) * D(a)/a * Ω]
90
+
eqs = [D(Ω) ~ -3(1 + w) * D(a) / a * Ω]
81
91
return ODESystem(eqs, t, [Ω], []; kw...)
82
92
end
83
-
@named r = species(1//3) # radiation
93
+
@named r = species(1 // 3) # radiation
84
94
@named m = species(0) # matter
85
95
@named Λ = species(-1) # dark energy / cosmological constant
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.
106
-
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.
114
+
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, rather than linear time.
107
115
108
116
It is therefore common to write these ODEs in terms of $b = \ln a$.
109
-
To do this, we will change the independent variable in two stages; from $t$ to $a$ to $b$.
110
-
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.
117
+
To do this, we will change the independent variable in two stages; first from $t$ to $a$, and then from $a$ to $b$.
118
+
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 since $t \leftrightarrow a \leftrightarrow b$ are one-to-one.
111
119
First, we transform from $t$ to $a$:
120
+
112
121
```@example changeivar
113
122
M2 = change_independent_variable(M1, M1.a)
123
+
@assert !ModelingToolkit.isautonomous(M2) # hide
124
+
M2 # hide
114
125
```
126
+
115
127
Unlike the original, notice that this system is *non-autonomous* because the independent variable $a$ appears explicitly in the equations!
116
128
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$:
129
+
117
130
```@example changeivar
118
131
a = M2.a
119
132
Da = Differential(a)
120
133
@variables b(a)
121
134
M3 = change_independent_variable(M2, b, [Da(b) ~ exp(-b), a ~ exp(b)])
122
135
```
136
+
123
137
We can now solve and plot the ODE in terms of $b$:
Transform the independent variable (e.g. ``t``) of the ODE system `sys` to a dependent variable `iv` (e.g. ``u(t)``).
57
63
The transformation is well-defined when the mapping between the new and old independent variables are one-to-one.
@@ -68,13 +74,13 @@ Any extra equations `eqs` involving the new and old independent variables will b
68
74
# Usage before structural simplification
69
75
70
76
The variable change must take place before structural simplification.
71
-
Subsequently, consider passing `allow_symbolic = true` to `structural_simplify(sys)` to reduce the number of unknowns, with the understanding that the transformation is well-defined.
77
+
In following calls to `structural_simplify`, consider passing `allow_symbolic = true` to avoid undesired constraint equations between between dummy variables.
72
78
73
79
# Usage with non-autonomous systems
74
80
75
-
If `sys` is non-autonomous (i.e. ``t`` appears explicitly in its equations), it is often desirable to also pass an algebraic equation relating the new and old independent variables (e.g. ``t = f(u(t))``).
76
-
Otherwise the transformed system will be underdetermined and cannot be structurally simplified without additional changes.
77
-
If an algebraic relation is not known, consider using `add_old_diff`.
81
+
If `sys` is non-autonomous (i.e. ``t`` appears explicitly in its equations), consider passing an algebraic equation relating the new and old independent variables (e.g. ``t = f(u(t))``).
82
+
Otherwise the transformed system can be underdetermined.
83
+
If an algebraic relation is not known, consider using `add_old_diff` instead.
@set! sys.eqs = [get_eqs(sys); eqs] # add extra equations we derived before starting transformation process
131
-
@set! sys.unknowns = [get_unknowns(sys); [iv1, div2_of_iv1]] # add new variables, will be transformed to e.g. t(u) and uˍt(u) # add dummy variables and old independent variable as a function of the new one
140
+
@set! sys.eqs = [get_eqs(sys); eqs] # add extra equations we derived
141
+
@set! sys.unknowns = [get_unknowns(sys); [iv1, div2_of_iv1]] # add new variables, will be transformed to e.g. t(u) and uˍt(u)
132
142
133
-
# Create a utility that performs the chain rule on an expression, followed by insertion of the new independent variable
0 commit comments