Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vern9 interpolation produces wrong results with save_idxs #2140

Open
henry2004y opened this issue Feb 13, 2024 · 3 comments
Open

Vern9 interpolation produces wrong results with save_idxs #2140

henry2004y opened this issue Feb 13, 2024 · 3 comments
Labels

Comments

@henry2004y
Copy link

henry2004y commented Feb 13, 2024

Describe the bug 🐞

When performing interpolation of the solution solved with Vern9(), if the user specifies save_idxs, it outputs different more discontinous interpolation results.

Expected behavior

Whether or not save_idxs is set, the interpolation results for each idxs should be identical.

Minimal Reproducible Example 👇

using OrdinaryDiffEq
using GLMakie # switch to Plots if needed

function lorenz(du,u,p,t)
    du[1] = p[1]*(u[2]-u[1])
    du[2] = u[1]*(p[2]-u[3]) - u[2]
    du[3] = u[1]*u[2] - p[3]*u[3]
end

u0 = [1., 5., 10.]
tspan = (0., 100.)
p = (10.0,28.0,8/3)
prob = ODEProblem(lorenz, u0, tspan, p)

sol = solve(prob, Vern9()); # full output
sol1 = solve(prob, Vern9(), save_idxs=[1]); # save only the 1st var

f = Figure(fontsize=18)
ax = Axis(f[1, 1])

plot!(ax, sol1, idxs=1)
plot!(ax, sol, idxs=1)

ax.scene.plots[2*2-1].color = Makie.wong_colors()[2]

test

Environment (please complete the following information):

  • Output of using Pkg; Pkg.status()
  [e74ebddf] Batsrus v0.5.2 `D:\Computer\Batsrus.jl`
  [6e4b80f9] BenchmarkTools v1.4.0
  [8ce10254] Bumper v0.6.0
  [13f3f980] CairoMakie v0.11.8
  [ae650224] ChunkSplitters v2.2.0
  [150eb455] CoordinateTransformations v0.6.3
  [82cc6244] DataInterpolations v4.6.0
  [864edb3b] DataStructures v0.18.16
  [459566f4] DiffEqCallbacks v2.36.1
  [b4f34e82] Distances v0.10.11
  [05065131] FieldTracer v0.1.11 `D:\Computer\FieldTracer.jl#master`
  [f6369f11] ForwardDiff v0.10.36
  [e9467ef8] GLMakie v0.9.8
  [db073c08] GeoMakie v0.6.1
  [c27321d9] Glob v1.3.1
  [f67ccb44] HDF5 v0.17.1
  [a98d9a8b] Interpolations v0.15.1
  [033835bb] JLD2 v0.4.45
  [23992714] MAT v0.10.6
⌃ [eacbb407] Meshes v0.40.4
  [1dea7af3] OrdinaryDiffEq v6.70.1
  [32113eaa] PkgBenchmark v0.2.12
  [c3e4b0f8] Pluto v0.19.38
  [92933f4c] ProgressMeter v1.9.0
  [438e738f] PyCall v1.96.4
  [d330b81b] PyPlot v2.11.2
  [dc215faf] ReadVTK v0.2.0
  [295af30f] Revise v3.5.13
  [0bca4576] SciMLBase v2.24.0
  [90137ffa] StaticArrays v1.9.2
  [953b605b] TestParticle v0.9.0 `https://github.com/henry2004y/TestParticle.jl.git#boris_dt_recipe`
  [7d2ba682] Vlasiator v0.11.5 `D:\Computer\Vlasiator.jl`
  [de93d4bb] VlasiatorPyPlot v0.1.4
  [64499a7a] WriteVTK v1.18.2
  • Output of versioninfo()
Julia Version 1.10.0
Commit 3120989f39 (2023-12-25 18:01 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: 12 × Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, skylake)
  Threads: 1 on 12 virtual cores

Additional context

Tsit5() does not have this issue. I haven't check other algorithms.

@ChrisRackauckas
Copy link
Member

Chaotic is extremely sensitive to numerical details (i.e. 1 ulp changes cause O(1) changes). Can you show this on a non-chaotic system?

@henry2004y
Copy link
Author

Here is an example in a non-chaotic system:

using OrdinaryDiffEq
using GLMakie

l = 1.0                             # length [m]
m = 1.0                             # mass [kg]
g = 9.81                            # gravitational acceleration [m/s²]

function pendulum!(du, u, p, t)
    du[1] = u[2]                    # θ'(t) = ω(t)
    du[2] = -3g / (2l) * sin(u[1]) + 3 / (m * l^2) * p(t) # ω'(t) = -3g/(2l) sin θ(t) + 3/(ml^2)M(t)
end

θ₀ = 0.01                           # initial angular deflection [rad]
ω₀ = 0.0                            # initial angular velocity [rad/s]
u₀ = [θ₀, ω₀]                       # initial state vector
tspan = (0.0, 10.0)                  # time interval

M = t -> 0.1sin(t)                    # external torque [Nm]

prob = ODEProblem(pendulum!, u₀, tspan, M)

sol = solve(prob, Vern9(), reltol = 1e-8, abstol = 1e-8)

sol1 = solve(prob, Vern9(), reltol = 1e-8, abstol = 1e-8, save_idxs=[1])

f = Figure(fontsize=18)
ax = Axis(f[1, 1])

plot!(ax, sol1, idxs=1)
plot!(ax, sol, idxs=1)

ax.scene.plots[2*2-1].color = Makie.wong_colors()[2]

test (1)

@ChrisRackauckas
Copy link
Member

Oh I see. This is what the warning in the docs is about:

https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/#Explicit-Runge-Kutta-Methods

Additionally, the following algorithms have a lazy interpolant:

BS5 - Bogacki-Shampine 5/4 Runge-Kutta method. (lazy 5th order interpolant).
Vern6 - Verner's “Most Efficient” 6/5 Runge-Kutta method. (lazy 6th order interpolant).
Vern7 - Verner's “Most Efficient” 7/6 Runge-Kutta method. (lazy 7th order interpolant).
Vern8 - Verner's “Most Efficient” 8/7 Runge-Kutta method. (lazy 8th order interpolant)
Vern9 - Verner's “Most Efficient” 9/8 Runge-Kutta method. (lazy 9th order interpolant)

These methods require a few extra steps in order to compute the high order interpolation, but these steps are only taken when the interpolation is used. These methods when lazy assume that the parameter vector p will be unchanged between the moment of the interval solving and the interpolation. If p is changed in a ContinuousCallback, or in a DiscreteCallback and the continuous solution is used after the full solution, then set lazy=false.

If followed it's fine:

using OrdinaryDiffEq
using GLMakie

l = 1.0                             # length [m]
m = 1.0                             # mass [kg]
g = 9.81                            # gravitational acceleration [m/s²]

function pendulum!(du, u, p, t)
    du[1] = u[2]                    # θ'(t) = ω(t)
    du[2] = -3g / (2l) * sin(u[1]) + 3 / (m * l^2) * p(t) # ω'(t) = -3g/(2l) sin θ(t) + 3/(ml^2)M(t)
end

θ₀ = 0.01                           # initial angular deflection [rad]
ω₀ = 0.0                            # initial angular velocity [rad/s]
u₀ = [θ₀, ω₀]                       # initial state vector
tspan = (0.0, 10.0)                  # time interval

M = t -> 0.1sin(t)                    # external torque [Nm]

prob = ODEProblem(pendulum!, u₀, tspan, M)

sol = solve(prob, Vern9(lazy=false), reltol = 1e-8, abstol = 1e-8)

sol1 = solve(prob, Vern9(lazy=false), reltol = 1e-8, abstol = 1e-8, save_idxs=[1])

f = Figure(fontsize=18)
ax = Axis(f[1, 1])

plot!(ax, sol1, idxs=1)
plot!(ax, sol, idxs=1)

ax.scene.plots[2*2-1].color = Makie.wong_colors()[2]

This is why in the global defaulting system, solve(prob) if it chooses a Verner method it just defaults to lazy=false, but if you manually choose a Verner method it's currently assumed the choice was done correctly.

With SciMLStructures.jl interface many cases of this will get fixed by having it save and reconstruct the Discrete AbstractPortion of the canonicalize, but the specific case here is an edge case that wouldn't even be caught there. Maybe what we need to do is add a special warning for lazy interpolants so that if the parameters is not a SciMLStructure or standard set of constants (Dict, array, tuple, etc.) then throw a warning by default and set lazy=false, and let people easily turn that warning off by specifying Vern9(lazy=true)? I'm not sure of a better way of signaling here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants