diff --git a/gen/solver_options.jl b/gen/solver_options.jl index 7cdf98f..961e51a 100644 --- a/gen/solver_options.jl +++ b/gen/solver_options.jl @@ -55,6 +55,7 @@ open(joinpath(@__DIR__, "..", "src", "solver_options.jl"), "w") do io generate_options(io, "BiCGSTAB", "HYPRE_ParCSRBiCGSTABSet", "HYPRE_BiCGSTABSet") generate_options(io, "BoomerAMG", "HYPRE_BoomerAMGSet") + generate_options(io, "FlexGMRES", "HYPRE_ParCSRFlexGMRESSet", "HYPRE_FlexGMRESSet") # generate_options(io, "FSAI", "HYPRE_FSAISet") generate_options(io, "GMRES", "HYPRE_ParCSRGMRESSet", "HYPRE_GMRESSet") generate_options(io, "ILU", "HYPRE_ILUSet") diff --git a/src/solver_options.jl b/src/solver_options.jl index 50d6604..a3405df 100644 --- a/src/solver_options.jl +++ b/src/solver_options.jl @@ -289,6 +289,36 @@ function Internals.set_options(s::BoomerAMG, kwargs) end end +function Internals.set_options(s::FlexGMRES, kwargs) + solver = s.solver + for (k, v) in kwargs + if k === :ConvergenceFactorTol + @check HYPRE_FlexGMRESSetConvergenceFactorTol(solver, v) + elseif k === :AbsoluteTol + @check HYPRE_ParCSRFlexGMRESSetAbsoluteTol(solver, v) + elseif k === :KDim + @check HYPRE_ParCSRFlexGMRESSetKDim(solver, v) + elseif k === :Logging + @check HYPRE_ParCSRFlexGMRESSetLogging(solver, v) + elseif k === :MaxIter + @check HYPRE_ParCSRFlexGMRESSetMaxIter(solver, v) + elseif k === :MinIter + @check HYPRE_ParCSRFlexGMRESSetMinIter(solver, v) + elseif k === :ModifyPC + @check HYPRE_ParCSRFlexGMRESSetModifyPC(solver, v) + elseif k === :Precond + Internals.set_precond_defaults(v) + Internals.set_precond(s, v) + elseif k === :PrintLevel + @check HYPRE_ParCSRFlexGMRESSetPrintLevel(solver, v) + elseif k === :Tol + @check HYPRE_ParCSRFlexGMRESSetTol(solver, v) + else + throw(ArgumentError("unknown option $k for HYPRE.FlexGMRES")) + end + end +end + function Internals.set_options(s::GMRES, kwargs) solver = s.solver for (k, v) in kwargs diff --git a/src/solvers.jl b/src/solvers.jl index a341958..5435292 100644 --- a/src/solvers.jl +++ b/src/solvers.jl @@ -152,6 +152,44 @@ function Internals.set_precond_defaults(amg::BoomerAMG) end +############# +# FlexGMRES # +############# + +mutable struct FlexGMRES <: HYPRESolver + comm::MPI.Comm + solver::HYPRE_Solver + function FlexGMRES(comm::MPI.Comm=MPI.COMM_NULL; kwargs...) + # comm defaults to COMM_NULL since it is unused in HYPRE_ParCSRFlexGMRESCreate + solver = new(comm, C_NULL) + solver_ref = Ref{HYPRE_Solver}(C_NULL) + @check HYPRE_ParCSRFlexGMRESCreate(comm, solver_ref) + solver.solver = solver_ref[] + # Attach a finalizer + finalizer(x -> HYPRE_ParCSRFlexGMRESDestroy(x.solver), solver) + # Set the options + Internals.set_options(solver, kwargs) + return solver + end +end + +function solve!(flex::FlexGMRES, x::HYPREVector, A::HYPREMatrix, b::HYPREVector) + @check HYPRE_ParCSRFlexGMRESSetup(flex.solver, A.parmatrix, b.parvector, x.parvector) + @check HYPRE_ParCSRFlexGMRESSolve(flex.solver, A.parmatrix, b.parvector, x.parvector) + return x +end + +Internals.setup_func(::FlexGMRES) = HYPRE_ParCSRFlexGMRESSetup +Internals.solve_func(::FlexGMRES) = HYPRE_ParCSRFlexGMRESSolve + +function Internals.set_precond(flex::FlexGMRES, p::HYPRESolver) + solve_f = Internals.solve_func(p) + setup_f = Internals.setup_func(p) + @check HYPRE_ParCSRFlexGMRESSetPrecond(flex.solver, solve_f, setup_f, p.solver) + return nothing +end + + ######### ## FSAI # ######### diff --git a/test/runtests.jl b/test/runtests.jl index b65e0c0..cbc3acf 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -370,6 +370,46 @@ end @test x ≈ A \ b atol=tol end +@testset "FlexGMRES" begin + # Solver constructor and options + @test_throws( + ArgumentError("unknown option UnknownOption for HYPRE.FlexGMRES"), + HYPRE.FlexGMRES(; UnknownOption = 1) + ) + # Setup + A = sprand(100, 100, 0.05); A = A'A + 5I + b = rand(100) + x = zeros(100) + A_h = HYPREMatrix(A) + b_h = HYPREVector(b) + x_h = HYPREVector(x) + # Solve + tol = 1e-9 + gmres = HYPRE.FlexGMRES(; Tol = tol) + HYPRE.solve!(gmres, x_h, A_h, b_h) + copy!(x, x_h) + # Test result with direct solver + @test x ≈ A \ b atol=tol + # Test without passing initial guess + x_h = HYPRE.solve(gmres, A_h, b_h) + copy!(x, x_h) + @test x ≈ A \ b atol=tol + + # Solve with preconditioner + precond = HYPRE.BoomerAMG() + gmres = HYPRE.FlexGMRES(; Tol = tol, Precond = precond) + x_h = HYPREVector(zeros(100)) + HYPRE.solve!(gmres, x_h, A_h, b_h) + copy!(x, x_h) + # Test result with direct solver + @test x ≈ A \ b atol=tol + # Test without passing initial guess + x_h = HYPRE.solve(gmres, A_h, b_h) + copy!(x, x_h) + @test x ≈ A \ b atol=tol +end + + @testset "GMRES" begin # Solver constructor and options @test_throws(