Skip to content

Commit 1a9bc39

Browse files
committed
Implement some stats and better handling of inner solver kwargs
1 parent 75d7aac commit 1a9bc39

File tree

2 files changed

+75
-35
lines changed

2 files changed

+75
-35
lines changed

examples/bratu.jl

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,67 +43,97 @@ const dx = 1 / (N + 1) # Grid-spacing
4343
x = LinRange(0.0 + dx, 1.0 - dx, N)
4444
u₀ = sin.(x .* π)
4545

46+
lines(x, u₀, label = "Inital guess")
47+
4648
# ## Reference solution evaluated over domain
4749
reference = true_sol_bratu.(x)
4850

49-
# ## Solving using inplace and out-of-place variants
50-
uₖ_1 = newton_krylov!(
51+
f, ax = lines(x, u₀, label = "Inital guess")
52+
lines!(ax, x, reference, label = "Reference solution")
53+
axislegend(ax, position = :cb)
54+
f
55+
56+
# ## Solving using inplace variant and CG
57+
uₖ, _ = newton_krylov!(
5158
(res, u) -> bratu!(res, u, dx, λ),
5259
copy(u₀), similar(u₀);
5360
Solver = CgSolver,
5461
)
5562

56-
uₖ_2 = newton_krylov(
63+
ϵ = abs2.(uₖ .- reference)
64+
65+
let
66+
fig = Figure(size = (800, 800))
67+
ax = Axis(fig[1, 1], title = "", ylabel = "", xlabel = "")
68+
69+
lines!(ax, x, reference, label = "True solution")
70+
lines!(ax, x, u₀, label = "Initial guess")
71+
lines!(ax, x, uₖ, label = "Newton-Krylov solution")
72+
73+
axislegend(ax, position = :cb)
74+
75+
ax = Axis(fig[1, 2], title = "Error", ylabel = "abs2 err", xlabel = "")
76+
lines!(ax, abs2.(uₖ .- reference))
77+
78+
fig
79+
end
80+
81+
# ## Solving using the out of place variant
82+
83+
_, stats = newton_krylov(
5784
(u) -> bratu(u, dx, λ),
5885
copy(u₀);
5986
Solver = CgSolver
6087
)
61-
62-
ϵ1 = abs2.(uₖ_1 .- reference)
63-
ϵ2 = abs2.(uₖ_1 .- reference)
88+
stats
6489

6590
# ## Solving with a fixed forcing
66-
newton_krylov!(
91+
_, stats = newton_krylov!(
6792
(res, u) -> bratu!(res, u, dx, λ),
6893
copy(u₀), similar(u₀);
6994
Solver = CgSolver,
70-
forcing = NewtonKrylov.Fixed()
95+
forcing = NewtonKrylov.Fixed(0.1)
7196
)
97+
stats
7298

7399
# ## Solving with no forcing
74-
newton_krylov!(
100+
_, stats = newton_krylov!(
75101
(res, u) -> bratu!(res, u, dx, λ),
76102
copy(u₀), similar(u₀);
77103
Solver = CgSolver,
78104
forcing = nothing
79105
)
106+
stats
80107

81-
# ## Solve using GMRES -- very slow
108+
# ## Solve using GMRES -- doesn't converge
82109
# ```julia
83-
# @time newton_krylov!(
84-
# (res, u) -> bratu!(res, u, dx, λ),
85-
# copy(u₀), similar(u₀);
86-
# Solver = GmresSolver,
110+
# _, stats = newton_krylov!(
111+
# (res, u) -> bratu!(res, u, dx, λ),
112+
# copy(u₀), similar(u₀);
113+
# Solver = GmresSolver,
87114
# )
115+
# stats
88116
# ```
89117

90118
# ## Solve using GMRES + ILU Preconditoner
91-
@time newton_krylov!(
119+
_, stats = newton_krylov!(
92120
(res, u) -> bratu!(res, u, dx, λ),
93121
copy(u₀), similar(u₀);
94122
Solver = GmresSolver,
95123
N = (J) -> ilu(collect(J)), # Assembles the full Jacobian
96-
ldiv = true,
124+
krylov_kwargs = (; ldiv = true)
97125
)
126+
stats
98127

99128
# ## Solve using FGMRES + ILU Preconditoner
100-
@time newton_krylov!(
129+
_, stats = newton_krylov!(
101130
(res, u) -> bratu!(res, u, dx, λ),
102131
copy(u₀), similar(u₀);
103132
Solver = FgmresSolver,
104133
N = (J) -> ilu(collect(J)), # Assembles the full Jacobian
105-
ldiv = true,
134+
krylov_kwargs = (; ldiv = true)
106135
)
136+
stats
107137

108138
# ## Solve using FGMRES + GMRES Preconditoner
109139
struct GmresPreconditioner{JOp}
@@ -116,20 +146,21 @@ function LinearAlgebra.mul!(y, P::GmresPreconditioner, x)
116146
return copyto!(y, sol)
117147
end
118148

119-
@time newton_krylov!(
149+
_, stats = newton_krylov!(
120150
(res, u) -> bratu!(res, u, dx, λ),
121151
copy(u₀), similar(u₀);
122152
Solver = FgmresSolver,
123-
N = (J) -> GmresPreconditioner(J, 30),
153+
N = (J) -> GmresPreconditioner(J, 5),
124154
)
155+
stats
125156

126157
# ## Explodes..
127158
# ```julia
128159
# newton_krylov!(
129160
# (res, u) -> bratu!(res, u, dx, λ),
130161
# copy(u₀), similar(u₀);
131-
# verbose = 1,
132162
# Solver = CglsSolver, # CgneSolver
163+
# krylov_kwargs = (; verbose=1)
133164
# )
134165
# ```
135166
#
@@ -138,7 +169,7 @@ end
138169
# (res, u) -> bratu!(res, u, dx, λ),
139170
# copy(u₀), similar(u₀);
140171
# verbose = 1,
141-
# Solver = BicgstabSolver,
172+
# Solver = BicgstabSolver, # L=2
142173
# η_max = nothing
143174
# )
144175
# ```

src/NewtonKrylov.jl

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ function newton_krylov!(F!, u₀, M::Int = length(u₀); kwargs...)
128128
return newton_krylov!(F!, u₀, res; kwargs...)
129129
end
130130

131+
struct Stats
132+
outer_iterations::Int
133+
inner_iterations::Int
134+
end
135+
function update(stats::Stats, inner_iterations)
136+
return Stats(
137+
stats.outer_iterations + 1,
138+
stats.inner_iterations + inner_iterations
139+
)
140+
end
141+
131142
"""
132143
133144
## Arguments
@@ -151,8 +162,9 @@ function newton_krylov!(
151162
Solver = CgSolver,
152163
M = nothing,
153164
N = nothing,
154-
ldiv = false
165+
krylov_kwargs = (;)
155166
)
167+
t₀ = time_ns()
156168
F!(res, u) # res = F(u)
157169
n_res = norm(res)
158170
tol = tol_rel * n_res + tol_abs
@@ -166,10 +178,10 @@ function newton_krylov!(
166178
J = JacobianOperator(F!, res, u)
167179
solver = Solver(J, res)
168180

169-
n_iter = 1
170-
while n_res > tol && n_iter <= max_niter
181+
stats = Stats(0, 0)
182+
while n_res > tol && stats.outer_iterations <= max_niter
171183
# Handle kwargs for Preconditoners
172-
kwargs = (; ldiv, verbose = verbose - 1)
184+
kwargs = krylov_kwargs
173185
if N !== nothing
174186
kwargs = (; N = N(J), kwargs...)
175187
end
@@ -198,22 +210,19 @@ function newton_krylov!(
198210
n_res = norm(res)
199211

200212
if isinf(n_res) || isnan(n_res)
201-
error("Inner solver blew up at iter=$n_iter")
213+
@error "Inner solver blew up" stats
214+
break
202215
end
203216

204217
if forcing !== nothing
205218
η = forcing(η, tol, n_res, n_res_prior)
206219
end
207220

208-
verbose > 0 && @info "Newton" iter = n_iter n_res η
209-
n_iter += 1
221+
verbose > 0 && @info "Newton" iter = n_res η stats
222+
stats = update(stats, solver.stats.niter)
210223
end
211-
return u
224+
t = (time_ns() - t₀) / 1.0e9
225+
return u, (; solved = n_res <= tol, stats, t)
212226
end
213227

214-
# TODO:
215-
# - Better statistic
216-
# - Allow choice of Krylov solver
217-
# - maxit_krylov?
218-
219228
end # module NewtonKrylov

0 commit comments

Comments
 (0)