EDIT
I’m trying to solve a simple mixed point BVP such as:
\partial_z \mathbf{a} = C_1 \mathbf{a} + g \mathbf{a} \mathbf{b} \mathbf{b}^{*}
\partial_z \mathbf{b} = C_2 \mathbf{b} + g \mathbf{b} \mathbf{a} \mathbf{a}^{*}
with BCs:
\mathbf{a}(z=0) = \mathbf{A}
\mathbf{b}(z=L) = \mathbf{B}
Note that \mathbf{a} and \mathbf{b} are complex valued vectors so this is a large system of boundary value problem. Usually, this problem can be solved via fixed-point iteration.
Below is a currently working code that I’ve converted completely to real values and cleaned up as a minimal example. Now, I have the following issues when increasing the magnitude of the nonlinear factor gg:
- MIRK4 crashes Julia
- Switching to a FIRK such as:
alg = RadauIIa5(;jac_alg = BVPJacobianAlgorithm(bc_diffmode = AutoFiniteDiff(), nonbc_diffmode = AutoFiniteDiff()), nested_nlsolve=true)
gives an error:
MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{NonlinearFunction{true, SciMLBase.FullSpecialize, BoundaryValueDiffEqFIRK.var"#80#94"{BVProblem{Vector{Float64}, Tuple{Float64, Float64}, true, Nothing, Params, BVPFunction{true, SciMLBase.FullSpecialize, false, typeof(f_packed!), typeof(bc_packed!), Nothing, Nothing, Nothing, UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing, Nothing, Dict{Any, Any}}, Nothing}, Nothing, Nothing, SciMLBase.StandardBVProblem, Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}}, BVPFunction{true, SciMLBase.FullSpecialize, false, typeof(f_packed!), typeof(bc_packed!), Nothing, Nothing, Nothing, UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing, Nothing, Dict{Any, Any}}, Nothing}, BoundaryValueDiffEqFIRK.FIRKTableau{true, Int64, Matrix{Float64}, Vector{Float64}, Vector{Float64}}}, UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, Nothing, Nothing}, Float64}, Float64, 12})
The type `Float64` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
(::Type{T})(::Real, ::RoundingMode) where T<:AbstractFloat
@ Base rounding.jl:265
(::Type{T})(::T) where T<:Number
@ Core boot.jl:900
Float64(::IrrationalConstants.Sqrthalfπ)
@ IrrationalConstants ~/.julia/packages/IrrationalConstants/lWTip/src/macro.jl:131
...
I want to be able to solve a large system where a and b are large (2^14) so I was hoping FIRKs would work.
Edit Working Code:
using BoundaryValueDiffEq
using DiffEqBase, OrdinaryDiffEq
# ---------- parameters ----------
mutable struct Params
M::Int
C::NTuple{2,ComplexF64} # linear coefficients for a,b
g::NTuple{2,ComplexF64} # nonlinear coefficients
zbc::NTuple{2,Float64} # (z_left, z_right)
A_re::Vector{Float64} # target a(0): real part
A_im::Vector{Float64} # target a(0): imag part
B_re::Vector{Float64} # target b(L): real part
B_im::Vector{Float64} # target b(L): imag part
tmp_b2::Vector{Float64} # workspace: |b|^2
end
# ---------- RHS on packed real state ----------
# v = [a_r(1:M); a_i(1:M); b_r(1:M); b_i(1:M)]
function f_packed!(dv::AbstractVector{T}, v::AbstractVector{T}, P::Params, z) where {T}
M = P.M
ar = @view v[1:M]; ai = @view v[M+1:2M]
br = @view v[2M+1:3M]; bi = @view v[3M+1:4M]
dar = @view dv[1:M]; dai = @view dv[M+1:2M]
dbr = @view dv[2M+1:3M]; dbi = @view dv[3M+1:4M]
# |b|^2
@inbounds @simd for i in 1:M
P.tmp_b2[i] = br[i]*br[i] + bi[i]*bi[i]
end
C1, C2 = P.C
g1, g2 = P.g
@inbounds @simd for i in 1:M
ar_i = ar[i]; ai_i = ai[i]
br_i = br[i]; bi_i = bi[i]
b2 = P.tmp_b2[i] # real scalar
# ---- da/dz = (C1 + g1*|b|^2)*a
s1 = C1 + g1*b2 # complex scalar
s1r = real(s1); s1i = imag(s1)
dar[i] = s1r*ar_i - s1i*ai_i
dai[i] = s1r*ai_i + s1i*ar_i
# ---- db/dz = C2*b + (g2*|b|^2)*a
# linear part on b
c2r = real(C2); c2i = imag(C2)
dbr[i] = c2r*br_i - c2i*bi_i
dbi[i] = c2r*bi_i + c2i*br_i
# nonlinear part proportional to a
t2 = g2*b2
t2r = real(t2); t2i = imag(t2)
dbr[i] += t2r*ar_i - t2i*ai_i
dbi[i] += t2r*ai_i + t2i*ar_i
end
return nothing
end
# ---------- boundary conditions on packed real state ----------
# Require: a(0) = A, b(L) = B (complex equalities → two real equations each)
function bc_packed!(res, u, P::Params, args...)
M = P.M
# left boundary (z = zbc[1]): a(0) = A
uL = u(P.zbc[1]) # Vector{Float64}, length 4M
@inbounds @simd for i in 1:M
res[i] = uL[i] - P.A_re[i] # a_r(0) - A_r = 0
res[M + i] = uL[M + i] - P.A_im[i] # a_i(0) - A_i = 0
end
# right boundary (z = zbc[2]): b(L) = B
uR = u(P.zbc[2])
off = 2M
@inbounds @simd for i in 1:M
res[2M + i] = uR[off + i] - P.B_re[i] # b_r(L) - B_r = 0
res[3M + i] = uR[off + M + i] - P.B_im[i] # b_i(L) - B_i = 0
end
return nothing
end
# helper: split a packed state vector v (Float64, length 4M) into Complex a,b (length M each)
function unpack_state(v::AbstractVector{<:Real}, M::Int)
a_re = @view v[1:M]
a_im = @view v[M+1:2M]
b_re = @view v[2M+1:3M]
b_im = @view v[3M+1:4M]
a = complex.(a_re, a_im)
b = complex.(b_re, b_im)
return a, b
end
# ---------- problem setup ----------
M = 2^5
@show M
L = 2.0
x = collect(range(-5.0, 5.0; length=M))
A = @. exp(-x^2) # complex A(x) with 0 imag
B = @. 1.0e-6 * exp(-x^2)
A_re = Float64.(real.(A)); A_im = Float64.(imag.(A))
B_re = Float64.(real.(B)); B_im = Float64.(imag.(B))
cc = -1.0e-1
C = (cc + 0im, cc + 0im)
gg = -1.0e-4
g = (gg + 0.0im, gg + 0.0im)
P = Params(M, C, g, (0.0, L), A_re, A_im, B_re, B_im, zeros(M))
# initial guess, packed real
y0 = vcat(A_re, A_im, B_re, B_im);
prob = BVProblem(f_packed!, bc_packed!, y0, (0.0, L), P)
alg = MIRK4(;jac_alg = BVPJacobianAlgorithm(bc_diffmode = AutoFiniteDiff(), nonbc_diffmode = AutoFiniteDiff()))
sol = solve(prob, alg; dt= L/100)
OLD
Below I have a code where I collected both vectors into one and made it into real values u. The RHS function converts the single vector into the two complex vectors does the computation then repacks them back into u and similarly for the mixed point boundary conditions.
The main errors I’m getting come from the Autodiff and Jacobian computaitons which are holding me back. So I’m wondering the following:
- Can
BVProblemhandle complex values? - How do I turn off
Autodiffin the usage of hte Jacobian? - What other ways can I use to solve this?
CODE: Note that I have some fftw stuff that I’m using for future problems so ignor this
# Uses Params work buffers, no allocations.
# ---------- parameters & nonlinearity kernel ----------
mutable struct Params
M::Int
C::AbstractArray
g::AbstractArray
zbc::Vector{Float64}
bcL::Vector{ComplexF64} # boundary condition at z=0
bcL_pack::Vector{Float64} # packed boundary condition at z=0
bcR::Vector{ComplexF64} # boundary condition at z=L
bcR_pack::Vector{Float64} # packed boundary condition at z=L
plan_f::Any #FFTW.cFFTWPlan #{ComplexF64,-1,false,1} # fft!
plan_i::Any #FFTW.cFFTWPlan #{ComplexF64, 1,false,1} # ifft!
xa::Vector{ComplexF64} # work: fft(a)
xb::Vector{ComplexF64} # work: fft(b)
tmp::Vector{ComplexF64}# work: pointwise
end
# Define Na, Nb from N; edit these to your physics.
function Na!(out, a, b, P::Params)
# Nxy!(out, a, b, P) # example: same as N(a,b)
@inbounds @simd for i in 1:P.M
out[i] = P.g[1]*a[i]*b[i]*conj(b[i]) # example: Na(a,b) = a*b
end
end
function Nb!(out, a, b, P::Params)
# Nxy!(out, b, a, P) # example: N(b,a); change as needed
@inbounds @simd for i in 1:P.M
out[i] = P.g[2]*a[i]*b[i]*conj(b[i]) # example: Na(a,b) = a*b
end
end
# ---------- ODE RHS ----------
# state u = [a; b] length 2M (Complex)
function f!(du, u, P::Params, z)
M = P.M
a = @view u[1:M]
b = @view u[M+1:2M]
da = @view du[1:M]
db = @view du[M+1:2M]
# da = R*a + Na(a,b)
@inbounds @simd for i in 1:M
da[i] = P.C[1] * a[i]
end
Na!(P.tmp, a, b, P) # tmp reuse
@inbounds @simd for i in 1:M
da[i] += P.tmp[i]
end
# db = S*b + Nb(a,b)
@inbounds @simd for i in 1:M
db[i] = P.C[2]* b[i]
end
Nb!(P.tmp, b, a, P) # example Nb = N(b,a); swap to Nb! if different
@inbounds @simd for i in 1:M
db[i] += P.tmp[i]
end
return nothing
end
# (a+ib) -> [Re; Im]
pack(u::AbstractVector{T}) where {T<:Complex} = reinterpret(real(T), u)
# [Re; Im] -> (a+ib)
unpack(v::AbstractVector{T}) where {T<:Real} = reinterpret(Complex{T}, v)
function f_real!(dv, v, p, z)
u = unpack(v) # Complex view (no copy)
du = similar(u)
f!(du, u, p, z) # your complex RHS
dv .= pack(du) # write back as real
return nothing
end
function bc_real!(res,
u,
P::Params,
args...)
M2 = 2 * P.M
@inbounds @simd for i in 1:M2
res[i] = u(P.zbc[1])[i] - P.bcL_pack[i] # a(0) = A
end
off = M2
@inbounds @simd for i in 1:M2
res[off + i] = u(P.zbc[2])[off + i] - P.bcR_pack[i] # b(L) = B
end
return nothing
end
M = 2^6
L = 10.0 # length of integration domain
zspan = (0.0, L)
zbc = [zspan[1], zspan[2]] # boundary points
x = Array(range( -10.0, 10.0, length=M))
A = @. exp(-x^2) + 0.0im; # example: A(x) = exp(x)
A_pack = pack(A)
B = @. 1.0e-6 * exp(-x^2) + 0.0im; # example: B(x) = exp(x)
B_pack = pack(B)
C = [-1.0e-6, -1.0e-6]
g = [-1.0+1.0im, -1.0+1.0im]
# work buffers
xa = similar(ComplexF64[], M); resize!(xa,M)
xb = similar(ComplexF64[], M); resize!(xb,M)
tmp = similar(ComplexF64[], M); resize!(tmp,M)
# plans in-place (reuse buffers)
plan_f = FFTW.plan_fft!(xa; flags=FFTW.MEASURE)
plan_i = FFTW.plan_ifft!(xa; flags=FFTW.MEASURE) # we’ll swap buffers as needed
P = Params(M,C,g, zbc, A, A_pack, B, B_pack, plan_f,plan_i,xa,xb,tmp);
initguess = vcat(A,B);
initguess_pack = pack(initguess) ; # packed initial guess
prob = BVProblem(f_real!, bc_real!, initguess_pack, zspan, P)
sol = solve(prob, MIRK4(); dt = L/100)
and the following error:
{
"name": "LoadError",
"message": "MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{BoundaryValueDiffEqMIRK.var\"#46#52\"{BoundaryValueDiffEqMIRK.MIRKCache{true, Float64, false, BoundaryValueDiffEqCore.DiffCacheNeeded, false, Tuple{Int64}, BVPFunction{true, SciMLBase.FullSpecialize, false, typeof(f_real!), typeof(bc_real!), Nothing, Nothing, Nothing, UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing, Nothing, Dict{Any, Any}}, Nothing}, typeof(bc_real!), BVProblem{Base.ReinterpretArray{Float64, 1, ComplexF64, Vector{ComplexF64}, false}, Tuple{Float64, Float64}, true, Nothing, Params, BVPFunction{true, SciMLBase.FullSpecialize, false, typeof(f_real!), typeof(bc_real!), Nothing, Nothing, Nothing, UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing, Nothing, Dict{Any, Any}}, Nothing}, Nothing, Nothing, SciMLBase.StandardBVProblem, Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}}, SciMLBase.StandardBVProblem, Params, MIRK4{Nothing, BVPJacobianAlgorithm{AutoSparse{AutoForwardDiff{nothing, Nothing}, SparseConnectivityTracer.TracerLocalSparsityDetector{SparseConnectivityTracer.GradientTracer{Int64, BitSet}, SparseConnectivityTracer.HessianTracer{Int64, BitSet, Dict{Int64, BitSet}, SparseConnectivityTracer.NotShared}}, SparseMatrixColorings.GreedyColoringAlgorithm{:direct, SparseMatrixColorings.NaturalOrder}}, AutoSparse{AutoForwardDiff{nothing, Nothing}, SparseConnectivityTracer.TracerLocalSparsityDetector{SparseConnectivityTracer.GradientTracer{Int64, BitSet}, SparseConnectivityTracer.HessianTracer{Int64, BitSet, Dict{Int64, BitSet}, SparseConnectivityTracer.NotShared}}, SparseMatrixColorings.GreedyColoringAlgorithm{:direct, SparseMatrixColorings.NaturalOrder}}, Nothing}, Float64}, BoundaryValueDiffEqMIRK.MIRKTableau{Int64, Vector{Float64}, Vector{Float64}, Vector{Float64}, Matrix{Float64}}, BoundaryValueDiffEqMIRK.MIRKInterpTableau{Int64, Vector{Float64}, Vector{Float64}, Vector{Float64}, Float64, Int64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{PreallocationTools.DiffCache{Matrix{Float64}, Vector{Float64}}}, VectorOfArray{Float64, 3, Vector{Matrix{Float64}}}, Vector{PreallocationTools.DiffCache{Vector{Float64}, Vector{Float64}}}, VectorOfArray{Float64, 2, Vector{Vector{Float64}}}, Vector{PreallocationTools.DiffCache{Vector{Float64}, Vector{Float64}}}, PreallocationTools.DiffCache{Vector{Float64}, Vector{Float64}}, Vector{Float64}, VectorOfArray{Float64, 2, Vector{Vector{Float64}}}, VectorOfArray{Float64, 2, Vector{Vector{Float64}}}, Tuple{Int64}, @NamedTuple{abstol::Float64}, @NamedTuple{abstol::Float64, dt::Float64, adaptive::Bool, controller::BoundaryValueDiffEqCore.DefectControl{Float64}, fit_parameters::Bool}}, BoundaryValueDiffEqCore.DiffCacheNeeded}, Float64}, Float64, 12})\nThe type `Float64` exists, but no method is defined for this combination of argument types when trying to construct it.\n\n\u001b[0mClosest candidates are:\n\u001b[0m (::Type{T})(::Real, \u001b[91m::RoundingMode\u001b[39m) where T<:AbstractFloat\n\u001b[0m\u001b[90m @\u001b[39m \u001b[90mBase\u001b[39m \u001b[90m\u001b[4mrounding.jl:265\u001b[24m\u001b[39m\n\u001b[0m (::Type{T})(::T) where T<:Number\n\u001b[0m\u001b[90m @\u001b[39m \u001b[90mCore\u001b[39m \u001b[90m\u001b[4mboot.jl:900\u001b[24m\u001b[39m\n\u001b[0m Float64(\u001b[91m::IrrationalConstants.Sqrthalfπ\u001b[39m)\n\u001b[0m\u001b[90m @\u001b[39m \u001b[33mIrrationalConstants\u001b[39m \u001b[90m~/.julia/packages/IrrationalConstants/lWTip/src/\u001b[39m\u001b[90m\u001b[4mmacro.jl:131\u001b[24m\u001b[39m\n\u001b[0m ...\n",
"stack": "MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{BoundaryValueDiffEqMIRK.var\"#46#52\"{BoundaryValueDiffEqMIRK.MIRKCache{true, Float64, false, BoundaryValueDiffEqCore.DiffCacheNeeded, false, Tuple{Int64}, BVPFunction{true, SciMLBase.FullSpecialize, false, typeof(f_real!), typeof(bc_real!), Nothing, Nothing, Nothing, UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing, Nothing, Dict{Any, Any}}, Nothing}, typeof(bc_real!), BVProblem{Base.ReinterpretArray{Float64, 1, ComplexF64, Vector{ComplexF64}, false}, Tuple{Float64, Float64}, true, Nothing, Params, BVPFunction{true, SciMLBase.FullSpecialize, false, typeof(f_real!), typeof(bc_real!), Nothing, Nothing, Nothing, UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing, Nothing, Dict{Any, Any}}, Nothing}, Nothing, Nothing, SciMLBase.StandardBVProblem, Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}}, SciMLBase.StandardBVProblem, Params, MIRK4{Nothing, BVPJacobianAlgorithm{AutoSparse{AutoForwardDiff{nothing, Nothing}, SparseConnectivityTracer.TracerLocalSparsityDetector{SparseConnectivityTracer.GradientTracer{Int64, BitSet}, SparseConnectivityTracer.HessianTracer{Int64, BitSet, Dict{Int64, BitSet}, SparseConnectivityTracer.NotShared}}, SparseMatrixColorings.GreedyColoringAlgorithm{:direct, SparseMatrixColorings.NaturalOrder}}, AutoSparse{AutoForwardDiff{nothing, Nothing}, SparseConnectivityTracer.TracerLocalSparsityDetector{SparseConnectivityTracer.GradientTracer{Int64, BitSet}, SparseConnectivityTracer.HessianTracer{Int64, BitSet, Dict{Int64, BitSet}, SparseConnectivityTracer.NotShared}}, SparseMatrixColorings.GreedyColoringAlgorithm{:direct, SparseMatrixColorings.NaturalOrder}}, Nothing}, Float64}, BoundaryValueDiffEqMIRK.MIRKTableau{Int64, Vector{Float64}, Vector{Float64}, Vector{Float64}, Matrix{Float64}}, BoundaryValueDiffEqMIRK.MIRKInterpTableau{Int64, Vector{Float64}, Vector{Float64}, Vector{Float64}, Float64, Int64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{PreallocationTools.DiffCache{Matrix{Float64}, Vector{Float64}}}, VectorOfArray{Float64, 3, Vector{Matrix{Float64}}}, Vector{PreallocationTools.DiffCache{Vector{Float64}, Vector{Float64}}}, VectorOfArray{Float64, 2, Vector{Vector{Float64}}}, Vector{PreallocationTools.DiffCache{Vector{Float64}, Vector{Float64}}}, PreallocationTools.DiffCache{Vector{Float64}, Vector{Float64}}, Vector{Float64}, VectorOfArray{Float64, 2, Vector{Vector{Float64}}}, VectorOfArray{Float64, 2, Vector{Vector{Float64}}}, Tuple{Int64}, @NamedTuple{abstol::Float64}, @NamedTuple{abstol::Float64, dt::Float64, adaptive::Bool, controller::BoundaryValueDiffEqCore.DefectControl{Float64}, fit_parameters::Bool}}, BoundaryValueDiffEqCore.DiffCacheNeeded}, Float64}, Float64, 12})\nThe type `Float64` exists, but no method is defined for this combination of argument types when trying to construct it.\n\n\u001b[0mClosest candidates are:\n\u001b[0m (::Type{T})(::Real, \u001b[91m::RoundingMode\u001b[39m) where T<:AbstractFloat\n\u001b[0m\u001b[90m @\u001b[39m \u001b[90mBase\u001b[39m \u001b[90m\u001b[4mrounding.jl:265\u001b[24m\u001b[39m\n\u001b[0m (::Type{T})(::T) where T<:Number\n\u001b[0m\u001b[90m @\u001b[39m \u001b[90mCore\u001b[39m \u001b[90m\u001b[4mboot.jl:900\u001b[24m\u001b[39m\n\u001b[0m Float64(\u001b[91m::IrrationalConstants.Sqrthalfπ\u001b[39m)\n\u001b[0m\u001b[90m