Adaptive time-stepping for semi-linear ODE (Kuramoto-Sivashinsky)

Hello,

I am interested in solving the 1D Kuramoto-Sivashinsky equation for a field u(x,t) with periodic BCs:
\frac{\partial u}{\partial t} = - \frac{\partial^2 u}{\partial x^2} - \frac{\partial^4 u}{\partial x^4} - u \frac{\partial u}{\partial x}, \mbox{ for } x \in [0, L]

I am using the SplitODEProblem structure from OrdinaryDiffEq.jl to solve this semi-linear PDE. I am only seeing fixed-time stepping solvers. Are there adaptive time-stepping available with DiffEq.jl? I am also interested in accelerating this piece of code.

My implementation:

using ApproxFun
using OrdinaryDiffEq
using FFTW
using BandedMatrices
using CairoMakie

#####################
## Setup 

struct Kuramoto_Fourier{R,S, U}
    L::Float64
    Nx::Int64
    Δx::Float64
    grid::Vector{Float64}
    T::R
    Tinv::S
    D::BandedMatrices.BandedMatrix
    D2::BandedMatrices.BandedMatrix
    D4::BandedMatrices.BandedMatrix
    # Store the diagonal operator -(D^2 + D^4) for the integrating factor
    mD24::U
    cache::Vector{Float64}
    u::Vector{Float64}
end

function Kuramoto_Fourier(L::Float64, Nx::Int64)
    Δx = 1/Nx
    S = Fourier(0..L)
    grid = points(S, Nx)
    D2 = Derivative(S,2)[1:Nx,1:Nx]
    D  = (Derivative(S) → S)[1:Nx,1:Nx]
    T = ApproxFun.plan_transform(S, Nx)
    Tinv = ApproxFun.plan_itransform(S, Nx)
    D4 = Derivative(S,4)[1:Nx,1:Nx]

    mD24 = DiffEqArrayOperator(-Diagonal(D4 + D2))

    cache = zeros(Nx)
    u = zeros(Nx)

    return Kuramoto_Fourier{typeof(T), typeof(Tinv), typeof(mD24)}(L, Nx, Δx, grid, T, Tinv, D, D2, D4, mD24, cache, u)
end

# Compute the term -u \partial u /\partial x
function spectral_kuramoto!(dû,û,p::Kuramoto_Fourier,t)
    T = p.T
    Tinv = p.Tinv
    D = p.D
    cache = p.cache
    u = p.u
    mul!(u, D, û)
    mul!(cache, Tinv, u)
    mul!(u, Tinv, û)
    @. cache = cache * u
    mul!(u, T, cache)
    @. dû = - u
end

#####################
##  Example 

L = 16*π
Nx = 256
params = Kuramoto_Fourier(L, Nx)

û0 = params.T*map(xi-> 1 + cos(xi/16)*(1  + sin(xi/16)), params.grid)

prob = SplitODEProblem(params.mD24, spectral_kuramoto!, û0, (0.0,300.0), params);

@time sol  = solve(prob, ETDRK4(), dt = 0.05)

# Not sure how to use KenCarp58()
# @time sol  = solve(prob, KenCarp58(), adaptive =true)

# Snapshots at t = 0 and t = 300
fig = Figure()
ax = Axis(fig[1,1])
lines!(ax, params.grid, params.Tinv*sol(0))
lines!(ax, params.grid, params.Tinv*sol(300))
resize_to_layout!(fig)
fig

# x-t diagram
heatmap(sol.t[1:end], params.grid, hcat(map(ui-> params.Tinv*ui, sol.u[1:end])...)')
1 Like

All of the additive RK methods are adaptive. See the documentation:

This was removed years ago. Where are you finding this?

1 Like

Thank you.

The mD24 = DiffEqArrayOperator(-Diagonal(D4 + D2)) piece comes from some early pandemic code. What would be the modern way of writing the linear operator?

I tried to use the solver `KenCarp3() but I get the warning message

sol  = solve(prob, KenCarp3(), adaptive =true)
┌ Warning: Split ODE problem do not work with factorization linear solvers. Bug detailed in https://github.com/SciML/OrdinaryDiffEq.jl/pull/1643. Defaulting to linsolve=KrylovJL()
└ @ OrdinaryDiffEq /state/partition1/llgrid/pkg/julia/julia-1.9.2/local/share/julia/packages/OrdinaryDiffEq/SUoOD/src/alg_utils.jl:248

How to address this?

Change to GMRES. I’ve been wanting to handle that for a bit but haven’t gotten to it.