OrdinaryDiffEq.jl icon indicating copy to clipboard operation
OrdinaryDiffEq.jl copied to clipboard

`sol(t, Val{2})` on FBDF includes spurious discontinuities for (differential) variable in DAE system

Open topolarity opened this issue 1 year ago • 3 comments

using ModelingToolkit

@variables t
D = Differential(t)

@mtkmodel Foo begin
    @parameters begin
        ω₁
        ω₂
        l
    end
    @variables begin
        x(t) # differential variable
        y(t) # algebraic variable
    end
    @equations begin
        D(D(x)) ~ -ω₁^2 * x     # solution: x(t) = cos(ω₁t)
        y^l     ~ sin(ω₂ * t)   # solution: y(t) = sin(ω₂t) when l == 1.0
    end
end

@mtkbuild foo = Foo();

prob = ODEProblem(foo,
                  [foo.x => 1.0, D(foo.x) => 0.0, foo.y => 0.0],
                  (0.0, 2pi),
                  [foo.ω₁ => 1.0, foo.ω₂ => 10.0, foo.l => 1.0]);
               
using OrdinaryDiffEq: solve, FBDF
using Plots

sol = solve(prob, FBDF())

t = 0.01:1e-5:2pi
plot(t, sol(t, Val{0}, idxs=foo.x).u; label="primal")
plot!(t, sol(t, Val{1}, idxs=foo.x).u; label="first-derivative")
plot!(t, sol(t, Val{2}, idxs=foo.x).u; label="second-derivative")

yields the following plot: image

The discontinuities appear to align with the zero-crossings of the algebraic variable: image

This gets worse if foo.ω₂ is increased to 100.0: image

topolarity avatar Nov 29 '23 21:11 topolarity

We looked into this a bit more and found that there are actually issues in both the first derivative and the primal, just much smaller (but in the case of the first derivative, still much larger than abstol).

Code:

using OrdinaryDiffEq: solve, FBDF
using WGLMakie

deftol = 1e-8
sol = solve(prob, FBDF(); abstol=deftol, reltol=deftol)

t = 0.01:1e-5:2pi

# Signal plots
begin
    fig = Figure()
    ax = Axis(fig[1,1]; title="Signals")
    lines!(ax, t, sol(t, Val{0}, idxs=foo.x).u; label="primal")
    lines!(ax, t, sol(t, Val{1}, idxs=foo.x).u; label="first derivative")
    lines!(ax, t, sol(t, Val{2}, idxs=foo.x).u; label="second derivative")
    axislegend(ax)
    fig
end

# Error plots
begin
    fig = Figure()
    ax = Axis(fig[1,1]; title="Normalized Error")
    primal_err = sol(t, Val{0}, idxs=foo.x).u .- cos.(t)
    lines!(ax, t, primal_err./maximum(abs.(primal_err)); label="primal")

    d1_err = sol(t, Val{1}, idxs=foo.x).u .+ sin.(t)
    lines!(ax, t, d1_err./maximum(abs.(d1_err)); label="first-derivative")

    d2_err = sol(t, Val{2}, idxs=foo.x).u .+ cos.(t)
    lines!(ax, t, d2_err./maximum(abs.(d2_err)); label="second-derivative")
    axislegend(ax)
    fig
end

Plots:

image image

staticfloat avatar Nov 30 '23 22:11 staticfloat

Is specific to FBDF:

using ModelingToolkit

@variables t
D = Differential(t)

@mtkmodel Foo begin
    @parameters begin
        ω₁
        ω₂
        l
    end
    @variables begin
        x(t) # differential variable
        y(t) # algebraic variable
    end
    @equations begin
        D(D(x)) ~ -ω₁^2 * x     # solution: x(t) = cos(ω₁t)
        y^l     ~ sin(ω₂ * t)   # solution: y(t) = sin(ω₂t) when l == 1.0
    end
end

@mtkbuild foo = Foo();

prob = ODEProblem(foo,
                  [foo.x => 1.0, D(foo.x) => 0.0, foo.y => 0.0],
                  (0.0, 2pi),
                  [foo.ω₁ => 1.0, foo.ω₂ => 10.0, foo.l => 1.0]);

using OrdinaryDiffEq
using Plots

sol = solve(prob, RadauIIA5(), abstol=1e-8, reltol=1e-8)
t = 0.01:1e-4:2pi
plot(t, sol(t, Val{2}, idxs=foo.x).u)
savefig("plot1.png")

sol = solve(prob, QNDF(), abstol=1e-8, reltol=1e-8)
t = 0.01:1e-4:2pi
plot(t, sol(t, Val{2}, idxs=foo.x).u)
savefig("plot2.png")

sol = solve(prob, FBDF(), abstol=1e-8, reltol=1e-8)
t = 0.01:1e-4:2pi
plot(t, sol(t, Val{2}, idxs=foo.x).u)
savefig("plot3.png")

plot1 plot2 plot3

ChrisRackauckas avatar Dec 29 '23 13:12 ChrisRackauckas

f(integrator.fsallast, u, p, tdt)

FBDF is slightly lying about its derivative. Or rather, the first derivative is "correct" for differential variables, but the interpolation polynomial of the FBDF method is different from the interpolation polynomial that we are showing from its solution. We should populate fsallast with the first derivative estimate from its internal polynomial interpolation. This would also make algebraic variable interpolation work for this method in principle.

ChrisRackauckas avatar Dec 29 '23 13:12 ChrisRackauckas