Skip to content

Commit

Permalink
Add BVPSOL and BVPM2
Browse files Browse the repository at this point in the history
  • Loading branch information
avik-pal committed Sep 19, 2023
1 parent 103601c commit 4889ba5
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 4 deletions.
8 changes: 8 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,24 @@ ConcreteStructs = "0.2"
DiffEqBase = "6.94.2"
ForwardDiff = "0.10"
NonlinearSolve = "2"
ODEInterface = "0.5"
Reexport = "0.2, 1.0"
SciMLBase = "1"
Setfield = "1"
TruncatedStacktraces = "1"
UnPack = "1"
julia = "1.6"

[weakdeps]
ODEInterface = "54ca160b-1b9f-5127-a996-1867f4bc2a2c"

[extensions]
BVPODEInterfaceExt = "ODEInterface"

[extras]
DiffEqDevTools = "f3b72e0c-5b89-59e1-b016-84e28bfd966d"
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
ODEInterface = "54ca160b-1b9f-5127-a996-1867f4bc2a2c"
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
Expand Down
95 changes: 95 additions & 0 deletions ext/BVPODEInterfaceExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
module BVPODEInterfaceExt

using SciMLBase, BoundaryValueDiffEq, ODEInterface
import ODEInterface: OptionsODE, OPT_ATOL, OPT_RTOL, OPT_METHODCHOICE, OPT_DIAGNOSTICOUTPUT,
OPT_ERRORCONTROL, OPT_SINGULARTERM, OPT_MAXSTEPS, OPT_BVPCLASS, OPT_SOLMETHOD,
OPT_RHS_CALLMODE, RHS_CALL_INSITU, evalSolution
import ODEInterface: Bvpm2, bvpm2_init, bvpm2_solve, bvpm2_destroy, bvpm2_get_x
import ODEInterface: bvpsol

function _test_bvpm2_bvpsol_problem_criteria(_, ::SciMLBase.StandardBVProblem, alg::Symbol)
throw(ArgumentError("$(alg) does not support standard BVProblem. Only TwoPointBVProblem is supported."))
end
function _test_bvpm2_bvpsol_problem_criteria(prob, ::TwoPointBVProblem, alg::Symbol)
@assert isinplace(prob) "$(alg) only supports inplace TwoPointBVProblem!"
end

#------
# BVPM2
#------
_no_param(::SciMLBase.NullParameters) = Float64[]
_no_param(p) = p

bvpm2_bc(bc, ya, yb, bca, bcb) = bc((bca, bcb), (ya, yb), SciMLBase.NullParameters())
bvpm2_bc(bc, ya, yb, p, bca, bcb) = bc((bca, bcb), (ya, yb), p)

bvp2m_f(f, t, u, du) = f(du, u, SciMLBase.NullParameters(), t)
bvp2m_f(f, t, u, p, du) = f(du, u, p, t)

## TODO: We can specify Drhs using forwarddiff if we want to
function SciMLBase.__solve(prob::BVProblem, alg::BVPM2; dt = 0.0, reltol = 1e-3, kwargs...)
_test_bvpm2_bvpsol_problem_criteria(prob, prob.problem_type, :BVPM2)

has_initial_guess = prob.u0 isa AbstractVector{<:AbstractArray}
no_odes, n, u0 = if has_initial_guess
length(first(prob.u0)), (length(prob.u0) - 1), reduce(hcat, prob.u0)
else
dt 0 && throw(ArgumentError("dt must be positive"))
length(prob.u0), Int(cld((prob.tspan[2] - prob.tspan[1]), dt)), prob.u0
end

mesh = collect(range(prob.tspan[1], stop = prob.tspan[2], length = n + 1))

no_left_bc = length(first(prob.f.bcresid_prototype.x))

initial_guess = Bvpm2()
bvpm2_init(initial_guess, no_odes, no_left_bc, mesh, u0, _no_param(prob.p),
alg.max_num_subintervals)

rhs = (args...) -> bvp2m_f(prob.f, args...)
bc = (args...) -> bvpm2_bc(prob.bc, args...)

opt = OptionsODE(OPT_RTOL => reltol, OPT_METHODCHOICE => alg.method_choice,
OPT_DIAGNOSTICOUTPUT => alg.diagnostic_output,
OPT_SINGULARTERM => alg.singular_term, OPT_ERRORCONTROL => alg.error_control)

sol, retcode, stats = bvpm2_solve(initial_guess, rhs, bc, opt)
retcode = retcode 0 ? ReturnCode.Success : ReturnCode.Failure

x_mesh = bvpm2_get_x(sol)
return DiffEqBase.build_solution(prob, alg, x_mesh, eachcol(evalSolution(sol, x_mesh));
retcode, stats)
end

#-------
# BVPSOL
#-------
bvpsol_f(f, t, u, du) = f(du, u, SciMLBase.NullParameters(), t)
function bvpsol_bc(bc, ra, rb, ya, yb, r)
bc((view(r, 1:(length(ra))), view(r, (length(ra) + 1):(length(ra) + length(rb)))),
(ya, yb), SciMLBase.NullParameters())
end

function SciMLBase.__solve(prob::BVProblem, alg::BVPSOL; maxiters = 1000, reltol = 1e-3,
dt = 0.0, kwargs...)
_test_bvpm2_bvpsol_problem_criteria(prob, prob.problem_type, :BVPSOL)
@assert isa(prob.p, SciMLBase.NullParameters) "BVPSOL only supports NullParameters!"
@assert isa(prob.u0, AbstractVector{<:AbstractArray}) "BVPSOL requires a vector of initial guesses!"
n, u0 = (length(prob.u0) - 1), reduce(hcat, prob.u0)
mesh = collect(range(prob.tspan[1], stop = prob.tspan[2], length = n + 1))

opt = OptionsODE(OPT_RTOL => reltol, OPT_MAXSTEPS => maxiters,
OPT_BVPCLASS => alg.bvpclass, OPT_SOLMETHOD => alg.sol_method,
OPT_RHS_CALLMODE => RHS_CALL_INSITU)

f! = (args...) -> bvpsol_f(prob.f, args...)
bc! = (args...) -> bvpsol_bc(prob.bc, first(prob.f.bcresid_prototype.x),
last(prob.f.bcresid_prototype.x), args...)

sol_t, sol_x, retcode, stats = bvpsol(f!, bc!, mesh, u0, alg.odesolver, opt)

return DiffEqBase.build_solution(prob, alg, sol_t, eachcol(sol_x);
retcode = retcode 0 ? ReturnCode.Success : ReturnCode.Failure, stats)
end

end
2 changes: 2 additions & 0 deletions src/BoundaryValueDiffEq.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ include("adaptivity.jl")
export Shooting
export MIRK2, MIRK3, MIRK4, MIRK5, MIRK6
export MIRKJacobianComputationAlgorithm
# From ODEInterface.jl
export BVPM2, BVPSOL

end
5 changes: 1 addition & 4 deletions src/adaptivity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ end
Generate a new mesh based on the `ŝ`.
"""
function redistribute!(cache::MIRKCache{iip, T},
Nsub_star,
ŝ,
mesh,
function redistribute!(cache::MIRKCache{iip, T}, Nsub_star, ŝ, mesh,
mesh_dt) where {iip, T}
N = length(mesh)
ζ = sum(ŝ .* mesh_dt) / Nsub_star
Expand Down
47 changes: 47 additions & 0 deletions src/algorithms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,50 @@ for order in (2, 3, 4, 5, 6)
end
end
end

"""
BVPM2(; max_num_subintervals = 3000, method_choice = 4, diagnostic_output = 1,
error_control = 1, singular_term = nothing)
BVPM2(max_num_subintervals::Int, method_choice::Int, diagnostic_output::Int,
error_control::Int, singular_term)
Fortran code for solving two-point boundary value problems. For detailed documentation, see
[ODEInterface.jl](https://github.com/luchr/ODEInterface.jl/blob/master/doc/SolverOptions.md#bvpm2).
!!! warning
Only supports inplace two-point boundary value problems, with very limited forms of
input structures!
!!! note
Only available in julia 1.9+ and if the `ODEInterface` package is loaded.
"""
Base.@kwdef struct BVPM2{S} <: BoundaryValueDiffEqAlgorithm
max_num_subintervals::Int = 3000
method_choice::Int = 4
diagnostic_output::Int = -1
error_control::Int = 1
singular_term::S = nothing
end

"""
BVPSOL(; bvpclass = 2, sol_method = 0, odesolver = nothing)
BVPSOL(bvpclass::Int, sol_methods::Int, odesolver)
A FORTRAN77 code which solves highly nonlinear two point boundary value problems using a
local linear solver (condensing algorithm) or a global sparse linear solver for the solution
of the arising linear subproblems, by Peter Deuflhard, Georg Bader, Lutz Weimann.
For detailed documentation, see
[ODEInterface.jl](https://github.com/luchr/ODEInterface.jl/blob/master/doc/SolverOptions.md#bvpsol).
!!! warning
Only supports inplace two-point boundary value problems, with very limited forms of
input structures!
!!! note
Only available in julia 1.9+ and if the `ODEInterface` package is loaded.
"""
Base.@kwdef struct BVPSOL{O} <: BoundaryValueDiffEqAlgorithm
bvpclass::Int = 2
sol_method::Int = 0
odesolver::O = nothing
end

0 comments on commit 4889ba5

Please sign in to comment.