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

Allow uEltype to be an immutable vector, e.g. SVector from StaticArrays.jl #50

Closed
ranocha opened this issue May 14, 2017 · 8 comments
Closed

Comments

@ranocha
Copy link
Member

ranocha commented May 14, 2017

I'm experimenting with systems of PDEs. After a semidiscretisation, I would like to store the solution as a vector of some (immutable) vectors, e.g. SVector{2,Float64} from StaticArrays.jl. A minimal working example is

using StaticArrays
using DiffEqBase, OrdinaryDiffEq

u0 = zeros(SVector{2,Float64}, 2) + 1

f = (t,u,du) -> du .= u
ode = ODEProblem(f, u0, (0.,1.))
sol = solve(ode, Euler(), dt=1.e-2)

This results in

No precise constructor found. Length of input was 1 while length of StaticArrays.SVector{2,Float64} is 2.

 in #init#84(::Int64, ::Array{Float64,1}, ::Array{Float64,1}, ::Array{Float64,1}, ::Void, ::Bool, ::Void, ::Bool, ::Bool, ::Bool, ::Float64, ::Bool, ::Rational{Int64}, ::Void, ::Void, ::Int64, ::Rational{Int64}, ::Rational{Int64}, ::Bool, ::Rational{Int64}, ::Rational{Int64}, ::Int64, ::Float64, ::Float64, ::DiffEqBase.#ODE_DEFAULT_NORM, ::DiffEqBase.#ODE_DEFAULT_ISOUTOFDOMAIN, ::DiffEqBase.#ODE_DEFAULT_UNSTABLE_CHECK, ::Bool, ::Bool, ::Bool, ::Bool, ::Bool, ::Bool, ::Bool, ::Int64, ::String, ::DiffEqBase.#ODE_DEFAULT_PROG_MESSAGE, ::Void, ::Void, ::Bool, ::Bool, ::Array{Any,1}, ::DiffEqBase.#init, ::DiffEqBase.ODEProblem{Array{StaticArrays.SVector{2,Float64},1},Float64,true,##1#2,Void,UniformScaling{Int64}}, ::OrdinaryDiffEq.Euler, ::Array{Any,1}, ::Array{Any,1}, ::Array{Any,1}, ::Type{Val{true}}) at /home/hendrik/.julia/v0.5/OrdinaryDiffEq/src/solve.jl:123
 in (::DiffEqBase.#kw##init)(::Array{Any,1}, ::DiffEqBase.#init, ::DiffEqBase.ODEProblem{Array{StaticArrays.SVector{2,Float64},1},Float64,true,##1#2,Void,UniformScaling{Int64}}, ::OrdinaryDiffEq.Euler, ::Array{Any,1}, ::Array{Any,1}, ::Array{Any,1}, ::Type{Val{true}}) at ./<missing>:0
 in #solve#83(::Array{Any,1}, ::Function, ::DiffEqBase.ODEProblem{Array{StaticArrays.SVector{2,Float64},1},Float64,true,##1#2,Void,UniformScaling{Int64}}, ::OrdinaryDiffEq.Euler, ::Array{Any,1}, ::Array{Any,1}, ::Array{Any,1}, ::Type{Val{true}}) at /home/hendrik/.julia/v0.5/OrdinaryDiffEq/src/solve.jl:6
 in (::DiffEqBase.#kw##solve)(::Array{Any,1}, ::DiffEqBase.#solve, ::DiffEqBase.ODEProblem{Array{StaticArrays.SVector{2,Float64},1},Float64,true,##1#2,Void,UniformScaling{Int64}}, ::OrdinaryDiffEq.Euler, ::Array{Any,1}, ::Array{Any,1}, ::Array{Any,1}, ::Type{Val{true}}) at ./<missing>:0 (repeats 2 times)

Here, the problem is uEltype(1). Thus, the problem is essentially the assumption that uEltype is a scalar.

A first workaround may be to define a suitable constructor as follows

using StaticArrays
using DiffEqBase, OrdinaryDiffEq

(::Type{SVector{2,T}}){T}(val::Real) = SVector{2,T}(val, val)

u0 = zeros(SVector{2,Float64}, 2) + 1

f = (t,u,du) -> du .= u
ode = ODEProblem(f, u0, (0.,1.))
sol = solve(ode, Euler(), dt=1.e-2)

However, this results in

indexed assignment not defined for StaticArrays.SVector{2,Float64}

 in copy!(::StaticArrays.SVector{2,Float64}, ::StaticArrays.SVector{2,Float64}) at ./multidimensional.jl:616
 in recursivecopy! at /home/hendrik/.julia/v0.5/RecursiveArrayTools/src/utils.jl:2 [inlined]
 in recursivecopy!(::Array{StaticArrays.SVector{2,Float64},1}, ::Array{StaticArrays.SVector{2,Float64},1}) at /home/hendrik/.julia/v0.5/RecursiveArrayTools/src/utils.jl:7
 in apply_step! at /home/hendrik/.julia/v0.5/OrdinaryDiffEq/src/integrators/integrator_utils.jl:247 [inlined]
 in loopheader! at /home/hendrik/.julia/v0.5/OrdinaryDiffEq/src/integrators/integrator_utils.jl:10 [inlined]
 in solve!(::OrdinaryDiffEq.ODEIntegrator{OrdinaryDiffEq.Euler,Array{StaticArrays.SVector{2,Float64},1},Float64,Float64,Float64,Array{Array{StaticArrays.SVector{2,Float64},1},1},DiffEqBase.ODESolution{StaticArrays.SVector{2,Float64},2,Array{Array{StaticArrays.SVector{2,Float64},1},1},Void,Void,Array{Float64,1},Array{Array{Array{StaticArrays.SVector{2,Float64},1},1},1},DiffEqBase.ODEProblem{Array{StaticArrays.SVector{2,Float64},1},Float64,true,##3#4,Void,UniformScaling{Int64}},OrdinaryDiffEq.Euler,OrdinaryDiffEq.InterpolationData{##3#4,Array{Array{StaticArrays.SVector{2,Float64},1},1},Array{Float64,1},Array{Array{Array{StaticArrays.SVector{2,Float64},1},1},1},OrdinaryDiffEq.EulerCache{Array{StaticArrays.SVector{2,Float64},1},Array{StaticArrays.SVector{2,Float64},1}}}},Array{StaticArrays.SVector{2,Float64},1},##3#4,Void,OrdinaryDiffEq.EulerCache{Array{StaticArrays.SVector{2,Float64},1},Array{StaticArrays.SVector{2,Float64},1}},OrdinaryDiffEq.DEOptions{StaticArrays.SVector{2,Float64},Float64,Float64,Float64,DiffEqBase.#ODE_DEFAULT_NORM,DiffEqBase.CallbackSet{Tuple{},Tuple{}},DiffEqBase.#ODE_DEFAULT_ISOUTOFDOMAIN,DiffEqBase.#ODE_DEFAULT_PROG_MESSAGE,DiffEqBase.#ODE_DEFAULT_UNSTABLE_CHECK,DataStructures.BinaryHeap{Float64,DataStructures.LessThan},Void,Void}}) at /home/hendrik/.julia/v0.5/OrdinaryDiffEq/src/solve.jl:306
 in #solve#83(::Array{Any,1}, ::Function, ::DiffEqBase.ODEProblem{Array{StaticArrays.SVector{2,Float64},1},Float64,true,##3#4,Void,UniformScaling{Int64}}, ::OrdinaryDiffEq.Euler, ::Array{Any,1}, ::Array{Any,1}, ::Array{Any,1}, ::Type{Val{true}}) at /home/hendrik/.julia/v0.5/OrdinaryDiffEq/src/solve.jl:7
 in (::DiffEqBase.#kw##solve)(::Array{Any,1}, ::DiffEqBase.#solve, ::DiffEqBase.ODEProblem{Array{StaticArrays.SVector{2,Float64},1},Float64,true,##3#4,Void,UniformScaling{Int64}}, ::OrdinaryDiffEq.Euler, ::Array{Any,1}, ::Array{Any,1}, ::Array{Any,1}, ::Type{Val{true}}) at ./<missing>:0 (repeats 2 times)

Hence, the assumption that uEltype is a scalar appears again.

However, if I use an algorithm from ODE.jl,

using StaticArrays
using DiffEqBase, OrdinaryDiffEq, ODE

u0 = zeros(SVector{2,Float64}, 2) + 1

f = (t,u,du) -> du .= u
ode = ODEProblem(f, u0, (0.,1.))
sol = solve(ode, ode4(), dt=1.e-2)

sol[end]

everything works fine, the result is

2-element Array{StaticArrays.SVector{2,Float64},1}:
 [2.70833,2.70833]
 [2.70833,2.70833]

However, I would really like to use the algorithms from OrdinaryDiffEq.jl - of course. I can invest some work and time to get things done. Since the implicit assumption uEltype <: Number seems to be widespread, some guidance from more experienced developers of the DiffEq suite would be very nice.

What do you think of something like uScalarType instead of uEltype for tolerances etc.? Additionally, the copying in RecursiveArrayTools would have to be adapted.

@ChrisRackauckas
Copy link
Member

Here, the problem is uEltype(1). Thus, the problem is essentially the assumption that uEltype is a scalar.

That's fixed by a recursive_eltype instead of eltype. A lot of the other things can be fixed by using the recursive versions.

What do you think of something like uScalarType instead of uEltype for tolerances etc.? Additionally, the copying in RecursiveArrayTools would have to be adapted.

Yeah, this should get worked out using RecursiveArrayTools. I'll find out what's needed. recursivecopy! might need to specialize on static arrays since they break a lot of the rules.

@ChrisRackauckas
Copy link
Member

I just noticed something though: you don't want to use this with stiff methods (unless you use the @ode_def or define the inverse W function) since its linear solve is really bad right now:

https://github.com/JuliaArrays/StaticArrays.jl/blob/master/src/solve.jl#L4

Just to keep in mind.

@ranocha
Copy link
Member Author

ranocha commented May 14, 2017

Great, thanks. Actually, I'm more interested in hyperbolic PDEs where the explicit strong-stability preserving SSPRK methods will be used.

@ChrisRackauckas
Copy link
Member

Yeah, I'm getting this done. Actually, I built RecursiveArrayTools.jl at first to make this exact use case work. Finishing this up shouldn't be bad. I have MArrays working.

@ChrisRackauckas
Copy link
Member

I got it working. A few things to convert to recursive calls, and the definitions:

function RecursiveArrayTools.recursivecopy!{T<:StaticArray,N}(b::AbstractArray{T,N},a::AbstractArray{T,N})
  @inbounds for i in eachindex(a)
    b[i] = a[i]
  end
end

function RecursiveArrayTools.recursivecopy!{T<:MArray,N}(b::AbstractArray{T,N},a::AbstractArray{T,N})
  @inbounds for i in eachindex(a)
    recursivecopy!(b[i],a[i])
  end
end

This means StaticArrays needs to be added to RecursiveArrayTools.jl for this to work. But I'll allow it since it's a Julia-only library. The real solution, which wouldn't require a dependency on each array type like this, would need a Base change:

JuliaLang/julia#21869

For now, we should be good though. I'll get this all tagged.

@ChrisRackauckas
Copy link
Member

ChrisRackauckas commented May 14, 2017

using StaticArrays
using DiffEqBase, OrdinaryDiffEq

using RecursiveArrayTools

u0 = zeros(MVector{2,Float64}, 2) + 1
u0[1] = ones(MVector{2,Float64}) + 1
f = (t,u,du) -> du .= u
ode = ODEProblem(f, u0, (0.,1.))
sol = solve(ode, Euler(), dt=1.e-2)

u0 = zeros(SVector{2,Float64}, 2) + 1
u0[1] = ones(SVector{2,Float64}) + 1
ode = ODEProblem(f, u0, (0.,1.))
sol = solve(ode, Euler(), dt=1.e-2)

sol = solve(ode, SSPRK22(), dt=1.e-2)


u0 = zero(MVector{2,Float64}) + 1
ode = ODEProblem(f, u0, (0.,1.))
sol = solve(ode, Euler(), dt=1.e-2)

u0 = zero(SVector{2,Float64}) + 1
f = (t,u) -> u
ode = ODEProblem(f, u0, (0.,1.))
sol = solve(ode, Euler(), dt=1.e-2)

Looks good. We may even want to add this to the starting tutorial, since SVector is quite fast and doesn't require in-place updates! Actually, most "small" problems should probably be doing this.

@ChrisRackauckas
Copy link
Member

ChrisRackauckas commented May 14, 2017

All works, except adaptive timestepping. That needs a smarter default norm. You can pass a norm in internalnorm which will work, but we can probably just handle this by default as well by just recursively norming.

@ranocha
Copy link
Member Author

ranocha commented May 15, 2017

Great work, thanks a lot. My examples are working now. I'm prototyping some ideas that are planned to become something like HyperbolicDiffEq.jl for hyperbolic conservation laws, where this stuff is really helpful.

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

No branches or pull requests

2 participants