Skip to content

Commit 614e9bd

Browse files
Fix MIRK extrema boundary conditions under AD
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
1 parent fa36d42 commit 614e9bd

3 files changed

Lines changed: 74 additions & 17 deletions

File tree

lib/BoundaryValueDiffEqMIRK/src/interpolation.jl

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,22 @@ end
205205

206206
@inline __build_interpolation(cache::MIRKCache, u::AbstractVector) = MIRKInterpolation(cache.mesh, u, cache)
207207

208+
@inline __stage_values(k, prototype) = k
209+
@inline __stage_values(k::PreallocationTools.DiffCache, prototype) = get_tmp(k, prototype)
210+
211+
@inline __copy_stage_values(k, prototype) = copy(k)
212+
@inline __copy_stage_values(k::PreallocationTools.DiffCache, prototype) = copy(get_tmp(k, prototype))
213+
214+
@inline __stage_weight_eltype(k, weights) = typeof(zero(eltype(k)) * zero(eltype(weights)))
215+
216+
function __stage_weighted_zero(prototype, k, weights)
217+
T = __stage_weight_eltype(k, weights)
218+
return fill!(similar(prototype, T), zero(T))
219+
end
220+
221+
@inline __primal_value(x) = x
222+
@inline __primal_value(x::ForwardDiff.Dual) = __primal_value(ForwardDiff.value(x))
223+
208224
"""
209225
EvalSol
210226
@@ -224,8 +240,7 @@ function (s::EvalSol{C})(tval::Number) where {C <: MIRKCache}
224240
dt = cache.mesh_dt[ii]
225241
τ = (tval - t[ii]) / dt
226242
w, _ = interp_weights(τ, alg)
227-
K = __needs_diffcache(alg.jac_alg) ? @view(k_discrete[ii].du[:, 1:stage]) :
228-
@view(k_discrete[ii][:, 1:stage])
243+
K = @view(__stage_values(k_discrete[ii], z)[:, 1:stage])
229244
KI = @view(k_interp.u[ii][1:length_z, 1:(cache.ITU.s_star - stage)])
230245
__maybe_matmul!(@view(z[1:length_z]), K, @view(w[1:stage]))
231246
__maybe_matmul!(@view(z[1:length_z]), KI, @view(w[(stage + 1):cache.ITU.s_star]), true, true)
@@ -256,8 +271,7 @@ function (s::EvalSol{C})(tvals::AbstractArray{<:Number}) where {C <: MIRKCache}
256271
dt = mesh_dt[ii]
257272
τ = (tval - t[ii]) / dt
258273
w, _ = interp_weights(τ, alg)
259-
K = __needs_diffcache(alg.jac_alg) ? @view(k_discrete[ii].du[:, 1:stage]) :
260-
@view(k_discrete[ii][:, 1:stage])
274+
K = @view(__stage_values(k_discrete[ii], zvals[i])[:, 1:stage])
261275
KI = @view(k_interp.u[ii][1:length_z, 1:(cache.ITU.s_star - stage)])
262276
__maybe_matmul!(@view(zvals[i][1:length_z]), K, @view(w[1:stage]))
263277
__maybe_matmul!(
@@ -276,14 +290,15 @@ end
276290

277291
# Intermediate derivative solution for evaluating derivative boundary conditions
278292
function (s::EvalSol{C})(tval::Number, ::Type{Val{1}}) where {C <: MIRKCache}
279-
(; t, cache) = s
293+
(; t, u, cache) = s
280294
(; alg, stage, k_discrete, k_interp, mesh_dt) = cache
281-
z′ = zeros(typeof(tval), cache.M)
282295
ii = interval(t, tval)
283296
dt = mesh_dt[ii]
284297
τ = (tval - t[ii]) / dt
285298
_, w′ = interp_weights(τ, alg)
286-
__maybe_matmul!(z′, @view(k_discrete[ii].du[:, 1:stage]), @view(w′[1:stage]))
299+
K = __stage_values(k_discrete[ii], last(u))
300+
z′ = __stage_weighted_zero(last(u), K, w′)
301+
__maybe_matmul!(z′, @view(K[:, 1:stage]), @view(w′[1:stage]))
287302
__maybe_matmul!(
288303
z′, @view(k_interp.u[ii][:, 1:(cache.ITU.s_star - stage)]), @view(w′[(stage + 1):cache.ITU.s_star]),
289304
true, true
@@ -372,19 +387,48 @@ end
372387
end
373388

374389
"""
375-
update_eval_sol!(eval_sol::EvalSol, y_, cache::MIRKCache)
390+
update_eval_sol!(eval_sol::EvalSol, y_, cache::MIRKCache, u)
376391
377392
Update the intermediate solution `eval_sol` with the new flattened solution `y_` and the cache.
378393
When evaluating boundary conditions with new solution during nonlinear solving, we should
379394
always update the intermediate solution with discrete solution + discrete stages + new stages
380395
(Continuous MIRK: u(meshᵢ + τ*dt) = yᵢ + dt sum br(τ)*kr).
381396
"""
382-
@views function update_eval_sol!(eval_sol::EvalSol, y_, cache::MIRKCache)
383-
eval_sol.u[1:end] .= __restructure_sol(y_, cache.in_size)
397+
@views function update_eval_sol!(eval_sol::EvalSol, y_, cache::MIRKCache, u)
398+
y = __restructure_sol(y_, cache.in_size)
399+
T_y = eltype(u)
400+
if eltype(first(eval_sol.u)) !== T_y || T_y !== eltype(cache)
401+
eval_cache = __mirk_eval_cache(cache, y, u)
402+
interp_setup!(eval_cache)
403+
return EvalSol(y, cache.mesh, eval_cache)
404+
end
405+
eval_sol.u[1:end] .= y
384406
eval_sol.cache.k_discrete[1:end] .= cache.k_discrete
385407
eval_sol.cache.k_interp.u[1:end] .= cache.k_interp.u
386408
interp_setup!(eval_sol.cache)
387-
return nothing
409+
return eval_sol
410+
end
411+
412+
function __zeroed_similar_vector_of_array(x::AbstractVectorOfArray, prototype)
413+
T = eltype(prototype)
414+
z = zero(first(prototype))
415+
return VectorOfArray([fill!(similar(xᵢ, T), z) for xᵢ in x.u])
416+
end
417+
418+
function __mirk_eval_cache(
419+
cache::MIRKCache{iip, T, use_both, DC, tune_parameters}, y, u
420+
) where {iip, T, use_both, DC, tune_parameters}
421+
k_discrete = [__copy_stage_values(k, u) for k in cache.k_discrete]
422+
k_interp = __zeroed_similar_vector_of_array(cache.k_interp, u)
423+
new_stages = __zeroed_similar_vector_of_array(cache.new_stages, u)
424+
return MIRKCache{iip, T, use_both, NoDiffCacheNeeded, tune_parameters}(
425+
cache.order, cache.stage, cache.M, cache.in_size, cache.f, cache.bc, cache.prob,
426+
cache.problem_type, cache.p, cache.alg, cache.TU, cache.ITU, cache.f_prototype,
427+
cache.bcresid_prototype, cache.mesh, cache.mesh_dt, k_discrete, k_interp, y,
428+
cache.y₀, cache.y₀_flat, cache.residual, cache.fᵢ_cache, cache.fᵢ₂_cache,
429+
cache.errors, new_stages, cache.resid_size, cache.singular_term, cache.nlsolve_kwargs,
430+
cache.optimize_kwargs, cache.kwargs, cache.verbose
431+
)
388432
end
389433

390434
"""
@@ -399,7 +443,7 @@ function __construct_then_solve_root_problem(sol::EvalSol{C}, tspan::Tuple) wher
399443
nlsols = Vector{SciMLBase.NonlinearSolution}(undef, length(nlprobs))
400444
nlsolve_alg = __FastShortcutNonlinearPolyalg(eltype(sol.cache))
401445
for i in 1:n
402-
f = @closure (t, p) -> sol(t, Val{1})[i]
446+
f = @closure (t, p) -> __primal_value(sol(t, Val{1})[i])
403447
nlprob = NonlinearProblem(f, sol.cache.prob.u0[i], tspan)
404448
nlsols[i] = solve(nlprob, nlsolve_alg)
405449
end

lib/BoundaryValueDiffEqMIRK/src/mirk.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ end
469469
y_ = recursive_unflatten!(y, u)
470470
resids = [get_tmp(r, u) for r in residual]
471471
Φ!(resids[2:end], cache, y_, u, trait, constraint)
472-
update_eval_sol!(eval_sol, y_, cache)
472+
eval_sol = update_eval_sol!(eval_sol, y_, cache, u)
473473
eval_bc_residual!(resids[1], pt, bc!, eval_sol, p, mesh)
474474
recursive_flatten!(resid, resids)
475475
return nothing
@@ -481,7 +481,7 @@ end
481481
) where {BC}
482482
y_ = recursive_unflatten!(y, u)
483483
Φ!(residual[2:end], cache, y_, u, trait, constraint)
484-
update_eval_sol!(eval_sol, y_, cache)
484+
eval_sol = update_eval_sol!(eval_sol, y_, cache, u)
485485
eval_bc_residual!(residual[1], pt, bc!, eval_sol, p, mesh)
486486
recursive_flatten!(resid, residual)
487487
return nothing
@@ -541,7 +541,7 @@ end
541541
) where {BC}
542542
y_ = recursive_unflatten!(y, u)
543543
resid_co = Φ(cache, y_, u, trait)
544-
update_eval_sol!(eval_sol, y_, cache)
544+
eval_sol = update_eval_sol!(eval_sol, y_, cache, u)
545545
resid_bc = eval_bc_residual(pt, bc, eval_sol, p, mesh)
546546
return vcat(resid_bc, mapreduce(vec, vcat, resid_co))
547547
end
@@ -605,7 +605,7 @@ function __construct_problem(
605605
) where {iip, T, UB, DC, tune_parameters, BC, C, LF}
606606
(; jac_alg) = cache.alg
607607
(; f_prototype, bcresid_prototype, prob) = cache
608-
(; bc_diffmode) = jac_alg
608+
bc_diffmode = get_dense_ad(jac_alg.bc_diffmode)
609609
N = length(cache.mesh)
610610

611611
resid_bc = bcresid_prototype
@@ -694,7 +694,7 @@ function __construct_problem(
694694
) where {iip, T, UB, DC, tune_parameters, BC, C, LF}
695695
(; jac_alg) = cache.alg
696696
(; f_prototype, bcresid_prototype, prob) = cache
697-
(; bc_diffmode) = jac_alg
697+
bc_diffmode = get_dense_ad(jac_alg.bc_diffmode)
698698
N = length(cache.mesh)
699699

700700
resid_bc = bcresid_prototype

lib/BoundaryValueDiffEqMIRK/test/Core/mirk_basic_tests.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,19 @@ end
476476
sol = solve(prob, mirk_solver(Val(order)), dt = 0.001)
477477
@test SciMLBase.successful_retcode(sol)
478478
end
479+
480+
extrema_tspan = (0.0, pi / 2)
481+
function extrema_pendulum!(du, u, p, t)
482+
du[1] = u[2]
483+
du[2] = -9.81 * sin(u[1])
484+
end
485+
function extrema_bc!(residual, sol, p, t)
486+
residual[1] = maxsol(sol, extrema_tspan) - 5.0496477654230745
487+
residual[2] = minsol(sol, extrema_tspan) + 4.8161991710010925
488+
end
489+
extrema_prob = BVProblem(extrema_pendulum!, extrema_bc!, [pi / 2, pi / 2], extrema_tspan)
490+
extrema_sol = solve(extrema_prob, MIRK4(), dt = 0.05)
491+
@test SciMLBase.successful_retcode(extrema_sol)
479492
end
480493

481494
@testset "Test unknown parameters estimation" begin

0 commit comments

Comments
 (0)