@@ -370,7 +370,7 @@ solvers_scaling = [
370370 (; pkg = :nonlinearsolve, sparsity = :none, name = "NR (No Sparsity)", alg = NewtonRaphson()),
371371 (; pkg = :nonlinearsolve, sparsity = :exact, name = "NR (Exact Sparsity)", alg = NewtonRaphson()),
372372 (; pkg = :wrapper, sparsity = :none, name = "NR [NLsolve.jl]", alg = NLsolveJL(; method = :newton, autodiff = :forward)),
373- (; pkg = :wrapper, sparsity = :none, name = "Mod. NR [Sundials]", alg = KINSOL()),
373+ (; pkg = :wrapper, sparsity = :none, name = "NR [Sundials]", alg = KINSOL(; linear_solver = :LapackDense, maxsetupcalls=1 )),
374374 (; pkg = :wrapper, sparsity = :none, name = "NR [PETSc] (No Sparsity)", alg = PETScSNES(; snes_type = "newtonls", snes_linesearch_type = "basic", autodiff = missing)),
375375 (; pkg = :wrapper, sparsity = :exact, name = "NR [PETSc] (Exact Sparsity)", alg = PETScSNES(; snes_type = "newtonls", snes_linesearch_type = "basic")),
376376
@@ -534,177 +534,6 @@ end
534534save("brusselator_scaling.svg", fig)
535535```
536536
537- # Jacobian-Free Newton / TR Krylov Methods
538-
539- In this section, we will benchmark jacobian-free nonlinear solvers with Krylov methods. We
540- will use preconditioning from `AlgebraicMultigrid.jl` and `IncompleteLU.jl`. Unfortunately,
541- our ability to use 3rd party software is limited here, since only `Sundials.jl` supports
542- jacobian-free methods via `:GMRES`.
543-
544- ```julia
545- using AlgebraicMultigrid, IncompleteLU
546-
547- incompletelu(W, p = nothing) = ilu(W, τ = 50.0), LinearAlgebra.I
548-
549- function algebraicmultigrid(W, p = nothing)
550- return aspreconditioner(ruge_stuben(convert(AbstractMatrix, W))), LinearAlgebra.I
551- end
552-
553- function algebraicmultigrid_jacobi(W, p = nothing)
554- A = convert(AbstractMatrix, W)
555- Pl = AlgebraicMultigrid.aspreconditioner(AlgebraicMultigrid.ruge_stuben(
556- A, presmoother = AlgebraicMultigrid.Jacobi(rand(size(A, 1))),
557- postsmoother = AlgebraicMultigrid.Jacobi(rand(size(A, 1)))
558- ))
559- return Pl, LinearAlgebra.I
560- end
561-
562- Ns = 2 .^ (2:7)
563-
564- solvers_scaling_jacobian_free = [
565- (; pkg = :nonlinearsolve, name = "Newton Krylov", alg = NewtonRaphson(; linsolve = KrylovJL_GMRES())),
566- (; pkg = :nonlinearsolve, name = "Newton Krylov (ILU)", alg = NewtonRaphson(; linsolve = KrylovJL_GMRES(; precs = incompletelu), concrete_jac = true)),
567- (; pkg = :nonlinearsolve, name = "Newton Krylov (AMG)", alg = NewtonRaphson(; linsolve = KrylovJL_GMRES(; precs = algebraicmultigrid), concrete_jac = true)),
568- # (; pkg = :nonlinearsolve, name = "Newton Krylov (AMG Jacobi)", alg = NewtonRaphson(; linsolve = KrylovJL_GMRES(; precs = algebraicmultigrid_jacobi), concrete_jac = true)),
569- (; pkg = :wrapper, name = "Newton Krylov [PETSc]", alg = PETScSNES(; snes_type = "newtonls", snes_linesearch_type = "basic", ksp_type = "gmres", snes_mf = true)),
570- (; pkg = :wrapper, name = "Newton Krylov (ILU) [PETSc]", alg = PETScSNES(; snes_type = "newtonls", snes_linesearch_type = "basic", ksp_type = "gmres", pc_type = "ilu")),
571- (; pkg = :wrapper, name = "Newton Krylov (AMG) [PETSc]", alg = PETScSNES(; snes_type = "newtonls", snes_linesearch_type = "basic", ksp_type = "gmres", pc_type = "gamg")),
572- # (; pkg = :wrapper, name = "Newton Krylov (AMG Jacobi) [PETSc]", alg = PETScSNES(; snes_type = "newtonls", snes_linesearch_type = "basic", ksp_type = "gmres", pc_type = "gamg", mg_levels_ksp_type = "richardson", mg_levels_pc_type = "jacobi")),
573- (; pkg = :nonlinearsolve, name = "TR Krylov", alg = TrustRegion(; linsolve = KrylovJL_GMRES())),
574- (; pkg = :nonlinearsolve, name = "TR Krylov (ILU)", alg = TrustRegion(; linsolve = KrylovJL_GMRES(; precs = incompletelu), concrete_jac = true)),
575- (; pkg = :nonlinearsolve, name = "TR Krylov (AMG)", alg = TrustRegion(; linsolve = KrylovJL_GMRES(; precs = algebraicmultigrid), concrete_jac = true)),
576- # (; pkg = :nonlinearsolve, name = "TR Krylov (AMG Jacobi)", alg = TrustRegion(; linsolve = KrylovJL_GMRES(; precs = algebraicmultigrid_jacobi), concrete_jac = true)),
577- # (; pkg = :wrapper, name = "TR Krylov [PETSc]", alg = PETScSNES(; snes_type = "newtontr", ksp_type = "gmres", snes_mf = true)),
578- # (; pkg = :wrapper, name = "TR Krylov (ILU) [PETSc]", alg = PETScSNES(; snes_type = "newtontr", ksp_type = "gmres", pc_type = "ilu")),
579- # (; pkg = :wrapper, name = "TR Krylov (AMG) [PETSc]", alg = PETScSNES(; snes_type = "newtontr", ksp_type = "gmres", pc_type = "gamg")),
580- # (; pkg = :wrapper, name = "TR Krylov (AMG Jacobi) [PETSc]", alg = PETScSNES(; snes_type = "newtontr", ksp_type = "gmres", pc_type = "gamg", mg_levels_ksp_type = "richardson", mg_levels_pc_type = "jacobi")),
581- (; pkg = :wrapper, name = "Newton Krylov [Sundials]", alg = KINSOL(; linear_solver = :GMRES)),
582- ]
583-
584- runtimes_scaling = zeros(length(solvers_scaling_jacobian_free), length(Ns)) .- 1
585-
586- for (i, N) in enumerate(Ns)
587- prob = generate_brusselator_problem(
588- N; sparsity = TracerSparsityDetector()
589- )
590-
591- @info "Benchmarking N = $N"
592-
593- for (j, solver) in enumerate(solvers_scaling_jacobian_free)
594- alg = solver.alg
595- name = solver.name
596-
597- if (j > 1 && runtimes_scaling[j - 1, i] == -1)
598- # The last benchmark failed so skip this too
599- runtimes_scaling[j, i] = NaN
600- @warn "$(name): Would Have Timed out"
601- else
602- function benchmark_function()
603- termination_condition = (alg isa PETScSNES || alg isa KINSOL) ?
604- nothing :
605- NonlinearSolveBase.AbsNormTerminationMode(Base.Fix1(maximum, abs))
606- sol = solve(prob, alg; abstol=1e-5, reltol=1e-5,
607- linsolve_kwargs = (; abstol = 1e-6, reltol = 1e-6),
608- termination_condition)
609- if SciMLBase.successful_retcode(sol) || norm(sol.resid, Inf) ≤ 1e-4
610- runtimes_scaling[j, i] = @belapsed solve($prob, $alg; abstol=1e-5,
611- reltol=1e-5,
612- linsolve_kwargs = (; abstol = 1e-6, reltol = 1e-6),
613- termination_condition=$termination_condition)
614- else
615- runtimes_scaling[j, i] = NaN
616- end
617- @info "$(name): $(runtimes_scaling[j, i]) | $(norm(sol.resid, Inf)) | $(sol.retcode)"
618- end
619-
620- timeout(benchmark_function, 600)
621-
622- if runtimes_scaling[j, i] == -1
623- @warn "$(name): Timed out"
624- runtimes_scaling[j, i] = NaN
625- end
626- end
627- end
628-
629- println()
630- end
631- ```
632-
633- Plot the results.
634-
635- ```julia
636- fig = begin
637- ASPECT_RATIO = 0.7
638- WIDTH = 1200
639- HEIGHT = round(Int, WIDTH * ASPECT_RATIO)
640- STROKEWIDTH = 2.5
641-
642- cycle = Cycle([:marker], covary = true)
643- colors = cgrad(:tableau_20, length(solvers_scaling_jacobian_free); categorical = true)
644- theme = Theme(Lines = (cycle = cycle,), Scatter = (cycle = cycle,))
645- LINESTYLES = Dict(
646- (:nonlinearsolve, :none) => :solid,
647- (:nonlinearsolve, :amg) => :dot,
648- (:nonlinearsolve, :amg_jacobi) => :dash,
649- (:nonlinearsolve, :ilu) => :dashdot,
650- )
651-
652- Ns_ = Ns .^ 2 .* 2
653-
654- with_theme(theme) do
655- fig = Figure(; size = (WIDTH, HEIGHT))
656-
657- ax = Axis(fig[1, 1:2], ylabel = L"Time ($s$)", xlabel = L"Problem Size ($N$)",
658- xscale = log10, yscale = log10, xlabelsize = 22, ylabelsize = 22,
659- xticklabelsize = 20, yticklabelsize = 20, xtickwidth = STROKEWIDTH,
660- ytickwidth = STROKEWIDTH, spinewidth = STROKEWIDTH)
661-
662- idxs = get_ordering(runtimes_scaling)
663-
664- ls, scs, labels = [], [], []
665- for (i, solver) in zip(idxs, solvers_scaling_jacobian_free[idxs])
666- all(isnan, runtimes_scaling[i, :]) && continue
667- precon = occursin("AMG Jacobi", solver.name) ? :amg_jacobi : occursin("AMG", solver.name) ? :amg : occursin("ILU", solver.name) ? :ilu : :none
668- linestyle = LINESTYLES[(solver.pkg, precon)]
669- l = lines!(Ns_, runtimes_scaling[i, :]; linewidth = 5, color = colors[i],
670- linestyle)
671- sc = scatter!(Ns_, runtimes_scaling[i, :]; markersize = 16, strokewidth = 2,
672- color = colors[i])
673- push!(ls, l)
674- push!(scs, sc)
675- push!(labels, solver.name)
676- end
677-
678- axislegend(ax, [[l, sc] for (l, sc) in zip(ls, scs)], labels,
679- "Successful Solvers";
680- framevisible=true, framewidth = STROKEWIDTH, orientation = :vertical,
681- titlesize = 20, labelsize = 16, position = :rb,
682- tellheight = true, tellwidth = false, patchsize = (40.0f0, 20.0f0))
683-
684- axislegend(ax, [
685- LineElement(; linestyle = :solid, linewidth = 5),
686- LineElement(; linestyle = :dot, linewidth = 5),
687- LineElement(; linestyle = :dash, linewidth = 5),
688- LineElement(; linestyle = :dashdot, linewidth = 5),
689- ], ["No Preconditioning", "AMG", "AMG Jacobi", "Incomplete LU"],
690- "Preconditioning"; framevisible=true, framewidth = STROKEWIDTH,
691- orientation = :vertical, titlesize = 20, labelsize = 16,
692- tellheight = true, tellwidth = true, patchsize = (40.0f0, 20.0f0),
693- position = :lt)
694-
695- fig[0, :] = Label(fig,
696- "Brusselator 2D: Scaling of Jacobian-Free Nonlinear Solvers with Problem Size",
697- fontsize = 24, tellwidth = false, font = :bold)
698-
699- return fig
700- end
701- end
702- ```
703-
704- ```julia
705- save("brusselator_krylov_methods_scaling.svg", fig)
706- ```
707-
708537# Work-Precision Diagram
709538
710539In this section, we will generate the work-precision of the solvers. All solvers that can
@@ -723,7 +552,7 @@ solvers_all = [
723552 (; pkg = :wrapper, name = "NR [NLsolve.jl]", solver = Dict(:alg => NLsolveJL(; method = :newton, autodiff = :forward))),
724553 (; pkg = :wrapper, name = "TR [NLsolve.jl]", solver = Dict(:alg => NLsolveJL(; autodiff = :forward))),
725554
726- (; pkg = :wrapper, name = "NR [Sundials]", solver = Dict(:alg => KINSOL())),
555+ (; pkg = :wrapper, name = "NR [Sundials]", solver = Dict(:alg => KINSOL(; linear_solver = :LapackDense, maxsetupcalls=1 ))),
727556 (; pkg = :wrapper, name = "Newton Krylov [Sundials]", solver = Dict(:alg => KINSOL(; linear_solver = :GMRES))),
728557
729558 (; pkg = :wrapper, name = "Mod. Powell [MINPACK]", solver = Dict(:alg => CMINPACK())),
0 commit comments