Skip to content

Commit 3f1732a

Browse files
authored
Use DiffEqs convention to define the ODEs function (#70)
* Use the convention of DiffEqs to define the ODEs function * Fix and extend macro to new format of eqs, including tests * Reshape is a view * Fix an error in parse_eqs.jl; allow `parse_eqs` kwarg in DiffEqs ... with tests * Update docs * Add few more tests using the exact solutions * Update required version of DiffEqBase * Require Julia 1.0. Fix travis and appveyor * Increase coverage for lyap_taylorinteg and parse_eqs * Fixed generic methods of taylorinteg, related tests and docstrings * Remove unused variables in taylorstep! and lyap_taylorstep! and reorganize some tests * Add an index to api.md, and change a title in docs * Fix a taylorinteg method, and increase test coverage * Fixes after review * Add Project.toml, delete REQUIRE and test/REQUIRE * Add warning about breaking changes * Updates in README.md
1 parent fba7634 commit 3f1732a

32 files changed

+929
-681
lines changed

.travis.yml

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ os:
66
- osx
77

88
julia:
9-
- 0.7
109
- 1.0
1110
- 1.1
1211
- nightly

Project.toml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name = "TaylorIntegration"
2+
uuid = "92b13dbe-c966-51a2-8445-caca9f8a7d42"
3+
repo = "https://github.com/PerezHz/TaylorIntegration.jl.git"
4+
version = "0.5.0"
5+
6+
[deps]
7+
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
8+
Espresso = "6912e4f1-e036-58b0-9138-08d1e6358ea9"
9+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
10+
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
11+
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
12+
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
13+
TaylorSeries = "6aa5eb33-94cf-58f4-a9d0-e4b2c4fc25ea"
14+
15+
[compat]
16+
DiffEqBase = "≥ 5.0.0"
17+
Elliptic = "≥ 0.5.0"
18+
Espresso = "≥ 0.6.0"
19+
Reexport = "≥ 0.2.0"
20+
Requires = "≥ 0.5.2"
21+
TaylorSeries = "≥ 0.10.0"
22+
julia = "≥ 1.0.0"
23+
24+
[extras]
25+
Elliptic = "b305315f-e792-5b7a-8f41-49f472929428"
26+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
27+
TaylorSeries = "6aa5eb33-94cf-58f4-a9d0-e4b2c4fc25ea"
28+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
29+
30+
[targets]
31+
test = ["TaylorSeries", "LinearAlgebra", "Test", "Elliptic"]

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ Coverage:
1717
Documentation:
1818
[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://PerezHz.github.io/TaylorIntegration.jl/latest) [![](https://img.shields.io/badge/docs-stable-blue.svg)](https://PerezHz.github.io/TaylorIntegration.jl/stable)
1919

20-
DOI (v0.4.0):
21-
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.2562365.svg)](https://doi.org/10.5281/zenodo.2562365)
20+
DOI (Zenodo):
21+
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.2562353.svg)](https://doi.org/10.5281/zenodo.2562353)
2222

2323
## Authors
2424

@@ -31,6 +31,9 @@ Comments, suggestions, and improvements are welcome and appreciated.
3131

3232
## Examples
3333

34+
> *NOTE: Version 0.5.0 includes breaking changes related with the
35+
form of the function that defines the equations of motion (which follows now the convention of [`DifferentialEquations.jl`](http://docs.juliadiffeq.org/stable/tutorials/ode_example.html)). Then, code that used to work in previous versions has to be amended.*
36+
3437
+ [x'=x^2](http://nbviewer.jupyter.org/github/PerezHz/TaylorIntegration.jl/blob/master/examples/x-dot-equals-x-squared.ipynb)
3538
+ [Kepler problem](http://nbviewer.jupyter.org/github/PerezHz/TaylorIntegration.jl/blob/master/examples/Kepler-problem.ipynb)
3639
+ [High-order polynomial approximations to special functions and integral transforms](http://nbviewer.jupyter.org/github/PerezHz/TaylorIntegration.jl/blob/master/examples/Polynomial-approx-special-functions-integral-transforms.ipynb)

REQUIRE

-6
This file was deleted.

appveyor.yml

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
environment:
22
matrix:
3-
- julia_version: 0.7
43
- julia_version: 1.0
54
- julia_version: 1.1
65
- julia_version: nightly

docs/Project.toml

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
33
Elliptic = "b305315f-e792-5b7a-8f41-49f472929428"
44
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
55
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
6-
ParameterizedFunctions = "65888b18-ceab-5e60-b2b9-181511a3b968"
76
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
87

98
[compat]

docs/src/api.md

+9
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,12 @@ lyap_taylorinteg
2020
Modules = [TaylorIntegration]
2121
Public = false
2222
```
23+
24+
## Index
25+
26+
```@index
27+
Pages = ["api.md"]
28+
Module = ["TaylorIntegration"]
29+
Order = [:function]
30+
Public = true
31+
```

docs/src/common.md

+46-22
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@
33
Here, we show an example of interoperability between `TaylorIntegration.jl` and
44
[`DifferentialEquations.jl`](https://github.com/JuliaDiffEq/DifferentialEquations.jl), i.e.,
55
how to use `TaylorIntegration.jl` from the `DifferentialEquations`
6-
ecosystem. the basic requirement is to load `DiffEqBase.jl`, which
6+
ecosystem. The basic requirement is to load `DiffEqBase.jl`, which
77
sets-up the common interface.
8-
Below, we shall also use `ParameterizedFunctions.jl` to define the appropriate
9-
system of ODEs, and use `OrdinaryDiffEq.jl` to compare
8+
Below, we shall also use `OrdinaryDiffEq.jl` to compare
109
the accuracy of `TaylorIntegration.jl` with respect to
1110
high-accuracy methods for non-stiff problems (`Vern9` method).
11+
While `DifferentialEquations` offers many macros to simplify certain
12+
aspects, we do not rely on them simply because using properly `@taylorize`
13+
improves the performance.
1214

1315
The problem we will integrate in this example is the planar circular restricted
1416
three-body problem (PCR3BP, also capitalized as PCRTBP). The PCR3BP describes
1517
the motion of a body with negligible mass ``m_3`` under the gravitational influence of two
1618
bodies with masses ``m_1`` and ``m_2``, such that ``m_1 \ge m_2``. It is
17-
assumed that ``m_3`` is much smaller than the other two masses, and therefore it
19+
assumed that ``m_3`` is much smaller than the other two masses so it
20+
does not influence their motion, and therefore it
1821
is simply considered as a massless test particle.
1922
The body with the greater mass ``m_1`` is referred as the *primary*, and
2023
``m_2`` as the *secondary*. These bodies are together
@@ -33,6 +36,7 @@ using Plots
3336
const μ = 0.01
3437
nothing # hide
3538
```
39+
3640
The Hamiltonian for the PCR3BP in the synodic frame (i.e., a frame which rotates
3741
such that the primaries are at rest on the ``x`` axis) is
3842
```math
@@ -51,7 +55,7 @@ V(x, y) = - \frac{1-\mu}{\sqrt{(x-\mu)^2+y^2}} - \frac{\mu}{\sqrt{(x+1-\mu)^2+y^
5155
is the gravitational potential associated to the primaries. The RHS of Eq.
5256
(\ref{eq-pcr3bp-hamiltonian}) is also known as the *Jacobi constant*, since it is a
5357
preserved quantity of motion in the PCR3BP. We will use this property to check
54-
the accuracy of the obtained solution.
58+
the accuracy of the solutions computed.
5559
```@example common
5660
V(x, y) = - (1-μ)/sqrt((x-μ)^2+y^2) - μ/sqrt((x+1-μ)^2+y^2)
5761
H(x, y, px, py) = (px^2+py^2)/2 - (x*py-y*px) + V(x, y)
@@ -69,26 +73,35 @@ The equations of motion for the PCR3BP are
6973
\dot{p_y} &=& - \frac{(1-\mu)y }{((x-\mu)^2+y^2)^{3/2}} - \frac{\mu y }{((x+1-\mu)^2+y^2)^{3/2}} - p_x.
7074
\end{eqnarray}
7175
```
72-
We define this system of ODEs with `ParameterizedFunctions.jl`
73-
```@example common
74-
using ParameterizedFunctions
7576

76-
f = @ode_def PCR3BP begin
77-
dx = px + y
78-
dy = py - x
79-
dpx = - (1-μ)*(x-μ)*((x-μ)^2+y^2)^-1.5 - μ*(x+1-μ)*((x+1-μ)^2+y^2)^-1.5 + py
80-
dpy = - (1-μ)*y *((x-μ)^2+y^2)^-1.5 - μ*y *((x+1-μ)^2+y^2)^-1.5 - px
81-
end μ
77+
We define this system of ODEs using the most naive approach
78+
```@example common
79+
function f(dq, q, param, t)
80+
local μ = param[1]
81+
x, y, px, py = q
82+
dq[1] = px + y
83+
dq[2] = py - x
84+
dq[3] = - (1-μ)*(x-μ)*((x-μ)^2+y^2)^-1.5 - μ*(x+1-μ)*((x+1-μ)^2+y^2)^-1.5 + py
85+
dq[4] = - (1-μ)*y *((x-μ)^2+y^2)^-1.5 - μ*y *((x+1-μ)^2+y^2)^-1.5 - px
86+
return nothing
87+
end
8288
nothing # hide
8389
```
90+
Note that `DifferentialEquations` offers interesting alternatives to write
91+
these equations of motion in a simpler and more convenient way,
92+
for example, using the macro `@ode_def`,
93+
see [`ParameterizedFunctions.jl`](https://github.com/JuliaDiffEq/ParameterizedFunctions.jl).
94+
We have not used that flexibility here because `TaylorIntegration.jl` has
95+
[`@taylorize`](@ref), which under certain circumstances allows to
96+
important speed-ups.
8497

8598
We shall define the initial conditions ``q_0 = (x_0, y_0, p_{x,0}, p_{y,0})`` such that
8699
``H(q_0) = J_0``, where ``J_0`` is a prescribed value. In order to do this,
87100
we select ``y_0 = p_{x,0} = 0`` and compute the value of ``p_{y,0}`` for which
88101
``H(q_0) = J_0`` holds.
89102

90103
We consider a value for ``J_0`` such that the test particle is
91-
able to display close encounters with both primaries, but cannot escape to infinity.
104+
able to display close encounters with *both* primaries, but cannot escape to infinity.
92105
We may obtain a first
93106
approximation to the desired value of ``J_0`` if we plot the projection of the
94107
zero-velocity curves on the ``x``-axis.
@@ -135,8 +148,9 @@ Hamiltonian evaluated at the initial condition is indeed equal to `J0`.
135148
H(q0) == J0
136149
```
137150

138-
Following [the tutorial](http://docs.juliadiffeq.org/latest/tutorials/ode_example.html)
139-
of `DifferentialEquations.jl`, we define an `ODEProblem` for the integration;
151+
Following the `DifferentialEquations.jl`
152+
[tutorial](http://docs.juliadiffeq.org/latest/tutorials/ode_example.html),
153+
we define an `ODEProblem` for the integration;
140154
`TaylorIntegration.jl` can be used via its common interface bindings with `DiffEqBase.jl`; both packages need to be loaded explicitly.
141155
```@example common
142156
tspan = (0.0, 1000.0)
@@ -233,18 +247,28 @@ Finally, we comment on the time spent by each integration.
233247
```@example common
234248
@elapsed solve(prob, Vern9(), abstol=1e-20);
235249
```
236-
Clearly, the integration with `TaylorMethod()` takes *much longer* than that using
250+
The integration with `TaylorMethod()` takes *much longer* than that using
237251
`Vern9()`. Yet, as shown above, the former preserves the Jacobi constant
238-
to a high accuracy while displaying the correct dynamics; whereas the latter
252+
to a high accuracy, whereas the latter
239253
solution loses accuracy in the sense of not conserving the Jacobi constant,
240254
which is an important property to trust the result of the integration.
255+
A fairer comparison is obtained by pushing the native methods of `DiffEqs`
256+
to reach similar accuracy for the integral of motion, as the one
257+
obtained by `TaylorIntegration.jl`. Such comparable situation has
258+
a performance cost, which then makes `TaylorIntegration.jl` comparable
259+
or even faster in some cases; see [[2]](@ref refsPCR3BP).
241260

242-
Finally, we shall mention here that a way to improve the integration time in
261+
Finally, as mentioned above, a way to improve the integration time in
243262
`TaylorIntegration` is using the macro [`@taylorize`](@ref); see
244-
[this section](@ref taylorize) for details. Yet, using the macro is not
245-
currently compatible with the common interface with `DifferentialEquations`.
263+
[this section](@ref taylorize) for details. Under certain circumstances
264+
it is possible to improve the performance, also with the common interface
265+
with `DifferentialEquations`, which restricts some of the great
266+
flexibility that `DifferentialEquations` allows when writing the function
267+
containing the differential equations.
246268

247269

248270
### [References](@id refsPCR3BP)
249271

250272
[1] Murray, Carl D., Stanley F. Dermott. Solar System dynamics. Cambridge University Press, 1999.
273+
274+
[2] [DiffEqBenchmarks.jl/DynamicalODE](https://nbviewer.jupyter.org/github/JuliaDiffEq/DiffEqBenchmarks.jl/blob/master/DynamicalODE/Henon-Heiles_energy_conservation_benchmark.ipynb)

docs/src/index.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@ pkg> add TaylorIntegration
2828
This package is developed as part of academic research.
2929
If you would like to help supporting it, please star the repository as
3030
such metrics may help us secure funding. If you use this software, we
31-
would be grateful if you could cite our work as follows:
31+
would be grateful if you could cite our work as follows (Bibtex entry
32+
can be found [here](https://zenodo.org/record/2562353/export/hx)):
3233

33-
J.A. Pérez-Hernández, L. Benet, TaylorIntegration.jl: Taylor Integration in Julia, https://github.com/PerezHz/TaylorIntegration.jl
34+
J.A. Pérez-Hernández and L. Benet
35+
TaylorIntegration.jl: Taylor Integration in Julia
36+
https://github.com/PerezHz/TaylorIntegration.jl
37+
DOI:[10.5281/zenodo.2562352](https://doi.org/10.5281/zenodo.2562352)
3438

3539

3640
### Acknowledgments

docs/src/kepler.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ and ``v_x`` and ``v_y`` its velocity. The function `kepler_eqs!` *mutates*
2323
the vectors corresponding to the LHS of the equations of motion.
2424

2525
```@example kepler
26-
function kepler_eqs!(t, q, dq)
26+
function kepler_eqs!(dq, q, params, t)
2727
dq[1] = q[3]
2828
dq[2] = q[4]
2929
rr = ( q[1]^2 + q[2]^2 )^(3/2)
@@ -70,11 +70,11 @@ end
7070
q0 = ini_cond(aKep, eKep)
7171
```
7272

73-
We now perform the integration, using a 28 order expansion and
73+
We now perform the integration, using a 25 order expansion and
7474
absolute tolerance of ``10^{-20}``.
7575
```@example kepler
7676
using TaylorIntegration, Plots
77-
t, q = taylorinteg(kepler_eqs!, q0, 0.0, 10000*2pi, 28, 1.0e-20, maxsteps=700000);
77+
t, q = taylorinteg(kepler_eqs!, q0, 0.0, 10000*2pi, 25, 1.0e-20, maxsteps=700_000);
7878
t[end], q[end,:]
7979
```
8080

docs/src/lorenz_lyapunov.md

+13-12
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ where ``\sigma``, ``\rho`` and ``\beta`` are constant parameters.
2525
First, we write a Julia function which evaluates (in-place) the Lorenz system:
2626
```@example lorenz
2727
#Lorenz system ODE:
28-
function lorenz!(t, x, dx)
29-
dx[1] = σ*(x[2]-x[1])
30-
dx[2] = x[1]*(ρ-x[3])-x[2]
31-
dx[3] = x[1]*x[2]-β*x[3]
28+
function lorenz!(dq, q, params, t)
29+
σ, ρ, β = params
30+
x, y, z = q
31+
dq[1] = σ*(y-x)
32+
dq[2] = x*(ρ-z)-y
33+
dq[3] = x*y-β*z
3234
nothing
3335
end
3436
nothing #hide
@@ -38,9 +40,7 @@ Below, we use the the parameters ``\sigma = 16.0``, ``\beta = 4`` and
3840
```@example lorenz
3941
#Lorenz system parameters
4042
#we use the `const` prefix in order to help the compiler speed things up
41-
const σ = 16.0
42-
const β = 4.0
43-
const ρ = 45.92
43+
const params = [16.0, 45.92, 4.0]
4444
nothing # hide
4545
```
4646

@@ -66,7 +66,7 @@ using TaylorIntegration
6666
xi = set_variables("δ", order=1, numvars=length(x0))
6767
x0TN = x0 .+ xi
6868
dx0TN = similar(x0TN)
69-
lorenz!(t0, x0TN, dx0TN)
69+
lorenz!(dx0TN, x0TN, params, t0)
7070
jjac = TaylorSeries.jacobian(dx0TN)
7171
lorenztr = tr(jjac) #trace of Lorenz system Jacobian matrix
7272
nothing # hide
@@ -76,7 +76,8 @@ As explained above, the user may provide a function which computes the Jacobian
7676
of the ODE in-place:
7777
```@example lorenz
7878
#Lorenz system Jacobian (in-place):
79-
function lorenz_jac!(jac, t, x)
79+
function lorenz_jac!(jac, x, params, t)
80+
σ, ρ, β = params
8081
jac[1,1] = -σ + zero(x[1])
8182
jac[1,2] = σ + zero(x[1])
8283
jac[1,3] = zero(x[1])
@@ -99,7 +100,7 @@ We can actually check the consistency of `lorenz_jac!` with the computation
99100
of the jacobian using automatic differentiation techniques. Below we use
100101
the initial conditions `x0`, but it is easy to generalize this.
101102
```@example lorenz
102-
lorenz_jac!(jjac, t0, x0) # update the matrix `jjac` using Jacobian provided by the user
103+
lorenz_jac!(jjac, x0, params, t0) # update the matrix `jjac` using Jacobian provided by the user
103104
TaylorSeries.jacobian(dx0TN) == jjac # `dx0TN` is obtained via automatic differentiation
104105
```
105106

@@ -113,12 +114,12 @@ one with the values of the Lyapunov spectrum.
113114

114115
We first carry out the integration computing internally the Jacobian
115116
```@example lorenz
116-
tv, xv, λv = lyap_taylorinteg(lorenz!, x0, t0, tmax, 28, 1e-20; maxsteps=2000000);
117+
tv, xv, λv = lyap_taylorinteg(lorenz!, x0, t0, tmax, 28, 1e-20, params; maxsteps=2000000);
117118
nothing # hide
118119
```
119120
Now, the integration is obtained exploiting `lorenz_jac!`:
120121
```@example lorenz
121-
tv_, xv_, λv_ = lyap_taylorinteg(lorenz!, x0, t0, tmax, 28, 1e-20, lorenz_jac!; maxsteps=2000000);
122+
tv_, xv_, λv_ = lyap_taylorinteg(lorenz!, x0, t0, tmax, 28, 1e-20, params, lorenz_jac!; maxsteps=2000000);
122123
nothing # hide
123124
```
124125
In terms of performance the second method is about ~50% faster than the first.

docs/src/pendulum.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ nothing # hide
4646
```
4747
The equations of motion are:
4848
```@example pendulum
49-
function pendulum!(t, x, dx)
49+
function pendulum!(dx, x, p, t)
5050
dx[1] = x[2]
5151
dx[2] = -sin(x[1])
5252
end

docs/src/root_finding.md

+11-9
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ H(x,y,p,q) = 0.5*(p^2+q^2) + V(x, y)
2323
H(x) = H(x...)
2424
2525
# Equations of motion
26-
function henonheiles!(t, x, dx)
27-
dx[1] = x[3]
28-
dx[2] = x[4]
29-
dx[3] = -x[1]-2x[2]*x[1]
30-
dx[4] = -x[2]-(x[1]^2-x[2]^2)
26+
function henonheiles!(dq, q, p, t)
27+
x, y, px, py = q
28+
dq[1] = px
29+
dq[2] = py
30+
dq[3] = -x-2y*x
31+
dq[4] = -y-(x^2-y^2)
3132
nothing
3233
end
3334
nothing # hide
@@ -76,17 +77,17 @@ discard a particular crossing (or crossings), the function `g` must then return
7677
a `nothing` value, as will be demonstrated below.
7778

7879
For the present example, we are looking for crossings through the surface ``x=0``,
79-
which corresponds to `g(t, x, dx) = x[1]`. But since we are looking only for the
80+
which corresponds to `g(dx, x, p, t) = x[1]`. But since we are looking only for the
8081
crossings through ``x=0`` which also satisfy ``\dot x > 0``, we have to define
8182
`g` in such a way that the crossing is discarded *only* if ``\dot x < 0``. One
8283
way to achieve this, is to define `g` such that if ``\dot x > 0`` then `g`
8384
returns the value of `x[1]`; otherwise, if ``\dot x < 0``, then `g` returns a
8485
`nothing` value.
8586

86-
Therefore, we define `g` as
87+
Therefore, we define (following again the convention of `DifferentialEquations.jl`) the function `g` as
8788
```@example poincare
8889
# x=0, px>0 section
89-
function g(t, x, dx)
90+
function g(dx, x, p, t)
9091
px_ = constant_term(x[3])
9192
# if px > 0...
9293
if px_ > zero(px_)
@@ -191,7 +192,8 @@ In order to properly handle this case, we need to extend the definition of
191192
`g` to be useful for `Taylor1{TaylorN{T}}` vectors.
192193
```@example poincare
193194
#specialized method of g for Taylor1{TaylorN{T}}'s
194-
function g(t, x::Array{Taylor1{TaylorN{T}},1}, dx::Array{Taylor1{TaylorN{T}},1}) where {T<:Number}
195+
function g(dx::Array{Taylor1{TaylorN{T}},1}, x::Array{Taylor1{TaylorN{T}},1},
196+
p, t) where {T<:Number}
195197
px_ = constant_term(constant_term(x[3]))
196198
if px_ > zero( T )
197199
return x[1]

0 commit comments

Comments
 (0)