Skip to content

Commit 9cfc718

Browse files
author
Your Name
committed
OOP NonlinearSolveAlg: extend W-reuse to WOperator/StaticWOperator + refresh J each should_update
1 parent 10b1bd5 commit 9cfc718

4 files changed

Lines changed: 130 additions & 8 deletions

File tree

lib/OrdinaryDiffEqNonlinearSolve/src/OrdinaryDiffEqNonlinearSolve.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import OrdinaryDiffEqCore: _initialize_dae!,
5555
import OrdinaryDiffEqDifferentiation: update_W!, is_always_new, build_uf, build_J_W,
5656
WOperator, StaticWOperator, wrapprecs,
5757
build_jac_config, dolinsolve,
58-
resize_jac_config!, jacobian2W!, jacobian!
58+
resize_jac_config!, jacobian2W!, jacobian!, jacobian
5959

6060
import StaticArraysCore: StaticArray
6161

lib/OrdinaryDiffEqNonlinearSolve/src/newton.jl

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@ function initialize!(
4646
integrator.stats.njacs += cache.cache.stats.njacs
4747
integrator.stats.nsolve += cache.cache.stats.nsolve
4848
end
49+
50+
if cache.W !== nothing
51+
dtgamma = method === DIRK ? γ * dt : γ * dt / α
52+
W_γdt = cache.W_γdt
53+
first_call = iszero(W_γdt)
54+
should_update = first_call || alg.always_new ||
55+
nlsolver.status === Divergence ||
56+
abs(inv(dtgamma) / inv(W_γdt) - 1) > alg.new_W_dt_cutoff
57+
if should_update
58+
_update_nlsolvealg_W_oop!(cache, integrator, dtgamma, tstep)
59+
cache.W_γdt = dtgamma
60+
integrator.stats.nw += 1
61+
cache.new_W = true
62+
else
63+
cache.new_W = false
64+
end
65+
end
66+
4967
if f isa DAEFunction
5068
nlp_params = (tmp, α, tstep, invγdt, p, dt, uprev, f)
5169
else
@@ -153,6 +171,31 @@ function _update_nlsolvealg_W!(nlcache, integrator, dtgamma, tstep, new_jac = tr
153171
return nothing
154172
end
155173

174+
function _update_nlsolvealg_W_oop!(nlcache, integrator, dtgamma, tstep)
175+
(; f, p, uprev, alg) = integrator
176+
mass_matrix = f.mass_matrix
177+
uf = nlcache.uf
178+
if SciMLBase.has_jac(f)
179+
J_new = f.jac(uprev, p, tstep)
180+
else
181+
uf.f = nlsolve_f(f, alg)
182+
uf.p = p
183+
uf.t = tstep
184+
J_new = jacobian(uf, uprev, integrator)
185+
end
186+
W = nlcache.W[]
187+
if W isa StaticWOperator
188+
nlcache.W[] = StaticWOperator(J_new - mass_matrix * inv(dtgamma))
189+
else
190+
copyto!(W.J, J_new)
191+
W.gamma = dtgamma
192+
if nlcache.J !== nothing && ArrayInterface.can_setindex(nlcache.J)
193+
copyto!(nlcache.J, J_new)
194+
end
195+
end
196+
return nothing
197+
end
198+
156199
## compute_step!
157200

158201
@muladd function compute_step!(nlsolver::NLSolver{<:NonlinearSolveAlg, false}, integrator)
@@ -694,14 +737,26 @@ function Base.resize!(nlcache::NLNewtonCache, ::AbstractNLSolver, integrator, i:
694737
return nothing
695738
end
696739

740+
function _resize_nlsolvealg_W!(nlcache, integrator, i)
741+
W = nlcache.W[]
742+
W isa StaticWOperator && return nothing
743+
if nlcache.J !== nothing && ArrayInterface.can_setindex(nlcache.J)
744+
nlcache.J = similar(nlcache.J, i, i)
745+
end
746+
nlcache.W[] = WOperator{SciMLBase.isinplace(integrator.sol.prob)}(
747+
integrator.f.mass_matrix, integrator.dt, nlcache.J, integrator.u
748+
)
749+
return nothing
750+
end
751+
697752
function Base.resize!(nlcache::NonlinearSolveCache, ::AbstractNLSolver, integrator, i::Int)
698753
nlcache.ustep === nothing || resize!(nlcache.ustep, i)
699754
nlcache.k === nothing || resize!(nlcache.k, i)
700755
nlcache.atmp === nothing || resize!(nlcache.atmp, i)
701756
nlcache.du1 === nothing || resize!(nlcache.du1, i)
702757
nlcache.weight === nothing || resize!(nlcache.weight, i)
703758
nlcache.jac_config === nothing || resize_jac_config!(nlcache, integrator)
704-
nlcache.W === nothing || resize_J_W!(nlcache, integrator, i)
759+
nlcache.W === nothing || _resize_nlsolvealg_W!(nlcache, integrator, i)
705760
nlcache.W_γdt = zero(nlcache.W_γdt)
706761
nlcache.new_W = true
707762
return nothing

lib/OrdinaryDiffEqNonlinearSolve/src/utils.jl

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -499,21 +499,52 @@ function build_nlsolver(
499499
γ = tTypeNoUnits(γ)
500500
α = tTypeNoUnits(α)
501501
dt = tTypeNoUnits(dt)
502+
use_w_reuse = !isdae && f.nlstep_data === nothing && (
503+
W isa StaticWOperator ||
504+
(W isa WOperator && W.J !== nothing && !(W.J isa AbstractSciMLOperator))
505+
)
502506
nlf = isdae ? oopdaenlf : oopodenlf
503507
nlp_params = if isdae
504508
(tmp, α, tstep, invγdt, p, dt, uprev, f)
505509
else
506510
(tmp, γ, α, tstep, invγdt, DIRK, p, dt, f)
507511
end
508-
prob = NonlinearProblem(
509-
NonlinearFunction{false, SciMLBase.FullSpecialize}(nlf),
510-
copy(ztmp), nlp_params
511-
)
512+
W_ref = use_w_reuse ? Ref{typeof(W)}(W) : nothing
513+
prob = if use_w_reuse
514+
if W isa StaticWOperator
515+
nlf_jac = let W_ref = W_ref
516+
(z, p) -> W_ref[].W
517+
end
518+
NonlinearProblem(
519+
NonlinearFunction{false, SciMLBase.FullSpecialize}(
520+
nlf; jac = nlf_jac, jac_prototype = W.W
521+
),
522+
copy(ztmp), nlp_params
523+
)
524+
else
525+
nlf_jac = let W_ref = W_ref
526+
(z, p) -> convert(AbstractMatrix, W_ref[])
527+
end
528+
NonlinearProblem(
529+
NonlinearFunction{false, SciMLBase.FullSpecialize}(
530+
nlf; jac = nlf_jac, jac_prototype = W.J
531+
),
532+
copy(ztmp), nlp_params
533+
)
534+
end
535+
else
536+
NonlinearProblem(
537+
NonlinearFunction{false, SciMLBase.FullSpecialize}(nlf),
538+
copy(ztmp), nlp_params
539+
)
540+
end
512541
inner_alg = _nlalg_with_linsolve(nlalg.alg, alg.linsolve)
513542
cache = init(prob, inner_alg, verbose = verbose.nonlinear_verbosity)
514543
nlcache = NonlinearSolveCache(
515544
nothing, tstep, nothing, nothing, invγdt, prob, cache,
516-
nothing, nothing, nothing, nothing, nothing, nothing,
545+
use_w_reuse ? J : nothing,
546+
W_ref,
547+
use_w_reuse ? uf : nothing, nothing, nothing, nothing,
517548
zero(tstep), true
518549
)
519550
else

lib/OrdinaryDiffEqNonlinearSolve/test/linear_nonlinear_tests.jl

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using OrdinaryDiffEqSDIRK, OrdinaryDiffEqBDF, OrdinaryDiffEqRosenbrock, Test, Random,
2-
LinearAlgebra, LinearSolve, ADTypes
2+
LinearAlgebra, LinearSolve, ADTypes, SciMLBase
33
using OrdinaryDiffEqNonlinearSolve: NonlinearSolveAlg
44
using NonlinearSolve: NewtonRaphson
55
Random.seed!(123)
@@ -208,3 +208,39 @@ let integ = init(
208208
step!(integ)
209209
@test !iszero(integ.cache.nlsolver.cache.weight)
210210
end
211+
212+
# NonlinearSolveAlg OOP W-reuse regression: plain `Vector` u0 with
213+
# `concrete_jac = true` must not crash on the first Jacobian evaluation
214+
# (the raw-WOperator-passed-to-Matrix-cache defect) and J must be refreshed
215+
# each `should_update` (not frozen at build_nlsolver-time).
216+
let
217+
vdp(u, p, t) = [u[2], p[1] * ((1 - u[1]^2) * u[2] - u[1])]
218+
prob = ODEProblem(vdp, [2.0, 0.0], (0.0, 1.0), [1.0e3])
219+
sol = solve(
220+
prob,
221+
TRBDF2(
222+
nlsolve = NonlinearSolveAlg(NewtonRaphson(; autodiff = AutoForwardDiff())),
223+
concrete_jac = true
224+
); reltol = 1.0e-8, abstol = 1.0e-10
225+
)
226+
@test SciMLBase.successful_retcode(sol.retcode)
227+
@test sol.stats.njacs > 0
228+
end
229+
230+
# StaticArrays / StaticWOperator variant of the same repro.
231+
using StaticArrays
232+
let
233+
vdp_static(u, p, t) = SVector(u[2], p[1] * ((1 - u[1]^2) * u[2] - u[1]))
234+
prob = ODEProblem(
235+
vdp_static, SVector(2.0, 0.0), (0.0, 1.0), SVector(1.0e3)
236+
)
237+
sol = solve(
238+
prob,
239+
TRBDF2(
240+
nlsolve = NonlinearSolveAlg(NewtonRaphson(; autodiff = AutoForwardDiff())),
241+
concrete_jac = true
242+
); reltol = 1.0e-8, abstol = 1.0e-10
243+
)
244+
@test SciMLBase.successful_retcode(sol.retcode)
245+
@test sol.stats.njacs > 0
246+
end

0 commit comments

Comments
 (0)