Skip to content

Commit bbf745e

Browse files
Fix DAE mass matrix initialization with vector abstol
Fixes issue #2820 where DAE mass matrix initialization failed when using vector abstol instead of scalar abstol. The problem was in multiple locations in initialize_dae.jl where the code attempted direct comparison of norm results (Float64) with abstol (which can be Vector{Float64}). This caused MethodError when abstol was a vector. The fix normalizes the error by dividing by abstol before taking the norm, then checks if the resulting norm is <= 1. This approach: - Works with both scalar and vector abstol - Maintains backward compatibility - Follows the tolerance checking pattern used elsewhere Changes: - Updated all tolerance comparisons in _initialize_dae! functions - Fixed both ShampineCollocationInit and BrownFullBasicInit algorithms - Applied fix to all variants (in-place/out-of-place, ODE/DAE problems) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 9b97006 commit bbf745e

File tree

1 file changed

+19
-14
lines changed

1 file changed

+19
-14
lines changed

lib/OrdinaryDiffEqNonlinearSolve/src/initialize_dae.jl

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,24 @@ function _initialize_dae!(integrator, prob::ODEProblem, alg::ShampineCollocation
5757
f(tmp, u0, p, t)
5858
tmp .= ArrayInterface.restructure(tmp, algebraic_eqs .* _vec(tmp))
5959

60-
integrator.opts.internalnorm(tmp, t) <= integrator.opts.abstol && return
60+
integrator.opts.internalnorm(tmp ./ integrator.opts.abstol, t) <= 1 && return
6161

6262
if isdefined(integrator.cache, :nlsolver) && !isnothing(alg.nlsolve)
6363
# backward Euler
6464
nlsolver = integrator.cache.nlsolver
65-
oldγ, oldc, oldmethod, olddt = nlsolver.γ, nlsolver.c, nlsolver.method,
65+
oldγ, oldc, oldmethod,
66+
olddt = nlsolver.γ, nlsolver.c, nlsolver.method,
6667
integrator.dt
6768
nlsolver.tmp .= integrator.uprev
6869
nlsolver.γ, nlsolver.c = 1, 1
6970
nlsolver.method = DIRK
7071
integrator.dt = dt
7172
z = nlsolve!(nlsolver, integrator, integrator.cache)
72-
nlsolver.γ, nlsolver.c, nlsolver.method, integrator.dt = oldγ, oldc, oldmethod,
73+
nlsolver.γ, nlsolver.c, nlsolver.method,
74+
integrator.dt = oldγ, oldc, oldmethod,
7375
olddt
7476
failed = nlsolvefail(nlsolver)
75-
@.. broadcast=false integrator.u=integrator.uprev + z
77+
@.. broadcast=false integrator.u=integrator.uprev+z
7678
else
7779

7880
# _u0 should be non-dual since NonlinearSolve does not differentiate the solver
@@ -169,22 +171,24 @@ function _initialize_dae!(integrator, prob::ODEProblem, alg::ShampineCollocation
169171
du = f(u0, p, t)
170172
resid = _vec(du)[algebraic_eqs]
171173

172-
integrator.opts.internalnorm(resid, t) <= integrator.opts.abstol && return
174+
integrator.opts.internalnorm(resid ./ integrator.opts.abstol, t) <= 1 && return
173175

174176
if isdefined(integrator.cache, :nlsolver) && !isnothing(alg.nlsolve)
175177
# backward Euler
176178
nlsolver = integrator.cache.nlsolver
177-
oldγ, oldc, oldmethod, olddt = nlsolver.γ, nlsolver.c, nlsolver.method,
179+
oldγ, oldc, oldmethod,
180+
olddt = nlsolver.γ, nlsolver.c, nlsolver.method,
178181
integrator.dt
179182
nlsolver.tmp .= integrator.uprev
180183
nlsolver.γ, nlsolver.c = 1, 1
181184
nlsolver.method = DIRK
182185
integrator.dt = dt
183186
z = nlsolve!(nlsolver, integrator, integrator.cache)
184-
nlsolver.γ, nlsolver.c, nlsolver.method, integrator.dt = oldγ, oldc, oldmethod,
187+
nlsolver.γ, nlsolver.c, nlsolver.method,
188+
integrator.dt = oldγ, oldc, oldmethod,
185189
olddt
186190
failed = nlsolvefail(nlsolver)
187-
@.. broadcast=false integrator.u=integrator.uprev + z
191+
@.. broadcast=false integrator.u=integrator.uprev+z
188192
else
189193
nlequation_oop = @closure (u, _) -> begin
190194
update_coefficients!(M, u, p, t)
@@ -235,7 +239,7 @@ function _initialize_dae!(integrator, prob::DAEProblem,
235239
dt = t != 0 ? min(t / 1000, dtmax / 10) : dtmax / 10 # Haven't implemented norm reduction
236240

237241
f(resid, integrator.du, u0, p, t)
238-
integrator.opts.internalnorm(resid, t) <= integrator.opts.abstol && return
242+
integrator.opts.internalnorm(resid ./ integrator.opts.abstol, t) <= 1 && return
239243

240244
# _du and _u should be non-dual since NonlinearSolve does not differentiate the solver
241245
# These non-dual values are thus used to make the caches
@@ -316,7 +320,7 @@ function _initialize_dae!(integrator, prob::DAEProblem,
316320
nlequation = (u, _) -> nlequation_oop(u)
317321

318322
resid = f(integrator.du, u0, p, t)
319-
integrator.opts.internalnorm(resid, t) <= integrator.opts.abstol && return
323+
integrator.opts.internalnorm(resid ./ integrator.opts.abstol, t) <= 1 && return
320324

321325
jac = if isnothing(f.jac)
322326
f.jac
@@ -381,7 +385,7 @@ function _initialize_dae!(integrator, prob::ODEProblem,
381385

382386
tmp .= ArrayInterface.restructure(tmp, algebraic_eqs .* _vec(tmp))
383387

384-
integrator.opts.internalnorm(tmp, t) <= alg.abstol && return
388+
integrator.opts.internalnorm(tmp ./ alg.abstol, t) <= 1 && return
385389
alg_u = @view u[algebraic_vars]
386390

387391
# These non-dual values are thus used to make the caches
@@ -460,7 +464,7 @@ function _initialize_dae!(integrator, prob::ODEProblem,
460464
du = f(u0, p, t)
461465
resid = _vec(du)[algebraic_eqs]
462466

463-
integrator.opts.internalnorm(resid, t) <= alg.abstol && return
467+
integrator.opts.internalnorm(resid ./ alg.abstol, t) <= 1 && return
464468

465469
isAD = alg_autodiff(integrator.alg) isa AutoForwardDiff
466470
if isAD
@@ -539,7 +543,7 @@ function _initialize_dae!(integrator, prob::DAEProblem,
539543
normtmp = get_tmp_cache(integrator)[1]
540544
f(normtmp, du, u, p, t)
541545

542-
if integrator.opts.internalnorm(normtmp, t) <= alg.abstol
546+
if integrator.opts.internalnorm(normtmp ./ alg.abstol, t) <= 1
543547
return
544548
elseif differential_vars === nothing
545549
error("differential_vars must be set for DAE initialization to occur. Either set consistent initial conditions, differential_vars, or use a different initialization algorithm.")
@@ -600,7 +604,8 @@ function _initialize_dae!(integrator, prob::DAEProblem,
600604
@unpack p, t, f = integrator
601605
differential_vars = prob.differential_vars
602606

603-
if integrator.opts.internalnorm(f(integrator.du, integrator.u, p, t), t) <= alg.abstol
607+
if integrator.opts.internalnorm(f(integrator.du, integrator.u, p, t) ./ alg.abstol, t) <=
608+
1
604609
return
605610
elseif differential_vars === nothing
606611
error("differential_vars must be set for DAE initialization to occur. Either set consistent initial conditions, differential_vars, or use a different initialization algorithm.")

0 commit comments

Comments
 (0)