Skip to content

Commit 78aaae1

Browse files
Fix SCCNonlinearProblem not having u0 field (issue #758)
SCCNonlinearProblem does not have a u0 field - each subproblem has its own u0. The solve function was incorrectly trying to access prob.u0 when u0 was not provided, causing a FieldError. Changes: - Remove the u0 = prob.u0 fallback since SCCNonlinearProblem has no u0 field - Add regression test to prevent this from happening again Closes #758 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 9f462d1 commit 78aaae1

File tree

2 files changed

+59
-24
lines changed

2 files changed

+59
-24
lines changed

lib/SCCNonlinearSolve/src/SCCNonlinearSolve.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ end
3636

3737
function CommonSolve.solve(prob::SciMLBase.SCCNonlinearProblem, alg::SCCAlg;
3838
sensealg = nothing, u0 = nothing, p = nothing, kwargs...)
39-
u0 = u0 !== nothing ? u0 : prob.u0
39+
# Note: SCCNonlinearProblem does not have a u0 field - each subproblem has its own u0.
40+
# The u0 parameter here is only used for AD hooks, not for actual solving.
4041
p = p !== nothing ? p : prob.p
4142
scc_solve_up(prob, sensealg, u0, p, alg; kwargs...)
4243
end

lib/SCCNonlinearSolve/test/core_tests.jl

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,40 @@ end
7676
@test sol manualscc scc_sol_default
7777
end
7878

79+
@testitem "SCCNonlinearProblem solve without explicit u0 (issue #758)" setup=[CoreRootfindTesting] tags=[:core] begin
80+
# Regression test for https://github.com/SciML/NonlinearSolve.jl/issues/758
81+
# SCCNonlinearProblem does not have a u0 field, so calling solve() without
82+
# explicit u0 should not try to access prob.u0
83+
using SCCNonlinearSolve
84+
import NonlinearSolve
85+
86+
# Create simple nonlinear subproblems (OOP style, returning vectors)
87+
f1(u, p)=[u[1]^2-2.0]
88+
f2(u, p)=[u[1]-1.0]
89+
90+
prob1=NonlinearProblem(f1, [1.0])
91+
prob2=NonlinearProblem(f2, [1.0])
92+
93+
# Create explicit functions (identity - just pass through)
94+
explicitfun1!(p, sols)=nothing
95+
explicitfun2!(p, sols)=nothing
96+
97+
# Create the SCC problem using the same pattern as existing tests
98+
scc_prob=SciMLBase.SCCNonlinearProblem(
99+
(prob1, prob2),
100+
SciMLBase.Void{Any}.([explicitfun1!, explicitfun2!])
101+
)
102+
103+
# This should not throw an error about prob.u0 field
104+
sol=SciMLBase.solve(scc_prob)
105+
106+
@test SciMLBase.successful_retcode(sol)
107+
# First subproblem: u^2 = 2 → u = sqrt(2)
108+
@test sol.u[1] sqrt(2.0)
109+
# Second subproblem: u = 1
110+
@test sol.u[2] 1.0
111+
end
112+
79113
@testitem "SCC Residuals Transfer" setup=[CoreRootfindTesting] tags=[:core] begin
80114
using NonlinearSolveFirstOrder
81115
using LinearAlgebra
@@ -85,36 +119,36 @@ end
85119

86120
# Nonlinear problem
87121
function f1(du, u, p)
88-
du[1] = u[1]^2 - 2.0
89-
du[2] = u[2] - u[1]
122+
du[1]=u[1]^2-2.0
123+
du[2]=u[2]-u[1]
90124
end
91-
explicitfun1(p, sols) = nothing
92-
prob1 = NonlinearProblem(
125+
explicitfun1(p, sols)=nothing
126+
prob1=NonlinearProblem(
93127
NonlinearFunction{true, SciMLBase.NoSpecialize}(f1), [1.0, 1.0], nothing)
94128

95129
# Linear problem
96-
A2 = [1.0 0.5; 0.5 1.0]
97-
b2 = [1.0, 2.0]
98-
prob2 = LinearProblem(A2, b2)
99-
explicitfun2(p, sols) = nothing
130+
A2=[1.0 0.5; 0.5 1.0]
131+
b2=[1.0, 2.0]
132+
prob2=LinearProblem(A2, b2)
133+
explicitfun2(p, sols)=nothing
100134

101135
# Another nonlinear problem
102136
function f3(du, u, p)
103-
du[1] = u[1] + u[2] - 3.0
104-
du[2] = u[1] * u[2] - 2.0
137+
du[1]=u[1]+u[2]-3.0
138+
du[2]=u[1]*u[2]-2.0
105139
end
106-
explicitfun3(p, sols) = nothing
107-
prob3 = NonlinearProblem(
140+
explicitfun3(p, sols)=nothing
141+
prob3=NonlinearProblem(
108142
NonlinearFunction{true, SciMLBase.NoSpecialize}(f3), [1.0, 2.0], nothing)
109143

110144
# Create SCC problem
111-
sccprob = SciMLBase.SCCNonlinearProblem((prob1, prob2, prob3),
145+
sccprob=SciMLBase.SCCNonlinearProblem((prob1, prob2, prob3),
112146
SciMLBase.Void{Any}.([explicitfun1, explicitfun2, explicitfun3]))
113147

114148
# Solve with SCCAlg
115149
using SCCNonlinearSolve
116-
scc_alg = SCCNonlinearSolve.SCCAlg(nlalg = NewtonRaphson(), linalg = nothing)
117-
scc_sol = solve(sccprob, scc_alg)
150+
scc_alg=SCCNonlinearSolve.SCCAlg(nlalg = NewtonRaphson(), linalg = nothing)
151+
scc_sol=solve(sccprob, scc_alg)
118152

119153
# Test that solution was successful
120154
@test SciMLBase.successful_retcode(scc_sol)
@@ -124,26 +158,26 @@ end
124158
@test !any(isnothing, scc_sol.resid)
125159

126160
# Test that residuals have the correct length
127-
expected_length = length(prob1.u0) + length(prob2.b) + length(prob3.u0)
161+
expected_length=length(prob1.u0)+length(prob2.b)+length(prob3.u0)
128162
@test length(scc_sol.resid) == expected_length
129163

130164
# Test that residuals are small (near zero for converged solution)
131165
@test norm(scc_sol.resid) < 1e-10
132166

133167
# Manually compute residuals to verify correctness
134-
u1 = scc_sol.u[1:2]
135-
u2 = scc_sol.u[3:4]
136-
u3 = scc_sol.u[5:6]
168+
u1=scc_sol.u[1:2]
169+
u2=scc_sol.u[3:4]
170+
u3=scc_sol.u[5:6]
137171

138172
# Compute residuals for each component
139-
resid1 = zeros(2)
173+
resid1=zeros(2)
140174
f1(resid1, u1, nothing)
141175

142-
resid2 = A2 * u2 - b2
176+
resid2=A2*u2-b2
143177

144-
resid3 = zeros(2)
178+
resid3=zeros(2)
145179
f3(resid3, u3, nothing)
146180

147-
expected_resid = vcat(resid1, resid2, resid3)
181+
expected_resid=vcat(resid1, resid2, resid3)
148182
@test scc_sol.resid expected_resid atol=1e-10
149183
end

0 commit comments

Comments
 (0)