diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c94459..446e9c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added + - New function `HYPRE.GetFinalRelativeResidualNorm(s::HYPRESolver)` for getting the final + residual norm from a solver. This function dispatches on the solver to the corresponding + C API wrapper `LibHYPRE.HYPRE_${Solver}GetFinalRelativeResidualNorm`. ([#14][github-14]) + - New function `HYPRE.GetNumIterations(s::HYPRESolver)` for getting the number of + iterations from a solver. This function dispatches on the solver to the corresponding C + API wrapper `LibHYPRE.HYPRE_${Solver}GetNumIterations`. ([#14][github-14]) ## [1.3.1] - 2023-01-14 ### Fixed @@ -58,6 +65,7 @@ Initial release of HYPRE.jl. [github-8]: https://github.com/fredrikekre/HYPRE.jl/pull/8 [github-12]: https://github.com/fredrikekre/HYPRE.jl/pull/12 [github-13]: https://github.com/fredrikekre/HYPRE.jl/pull/13 +[github-14]: https://github.com/fredrikekre/HYPRE.jl/pull/14 [1.0.0]: https://github.com/fredrikekre/HYPRE.jl/releases/tag/v1.0.0 [1.1.0]: https://github.com/fredrikekre/HYPRE.jl/compare/v1.0.0...v1.1.0 diff --git a/src/solvers.jl b/src/solvers.jl index 563c98d..9c0659b 100644 --- a/src/solvers.jl +++ b/src/solvers.jl @@ -501,3 +501,68 @@ function Internals.set_precond(pcg::PCG, p::HYPRESolver) @check HYPRE_ParCSRPCGSetPrecond(pcg, solve_f, setup_f, p) return nothing end + + +########################################################## +# Extracting information about the solution from solvers # +########################################################## + +""" + HYPRE.GetFinalRelativeResidualNorm(s::HYPRESolver) + +Return the final relative residual norm from the last solve with solver `s`. + +This function dispatches on the solver to the corresponding C API wrapper +`LibHYPRE.HYPRE_\$(Solver)GetFinalRelativeResidualNorm`. +""" +function GetFinalRelativeResidualNorm(s::HYPRESolver) + r = Ref{HYPRE_Real}() + if s isa BiCGSTAB + @check HYPRE_ParCSRBiCGSTABGetFinalRelativeResidualNorm(s, r) + elseif s isa BoomerAMG + @check HYPRE_BoomerAMGGetFinalRelativeResidualNorm(s, r) + elseif s isa FlexGMRES + @check HYPRE_ParCSRFlexGMRESGetFinalRelativeResidualNorm(s, r) + elseif s isa GMRES + @check HYPRE_ParCSRGMRESGetFinalRelativeResidualNorm(s, r) + elseif s isa Hybrid + @check HYPRE_ParCSRHybridGetFinalRelativeResidualNorm(s, r) + elseif s isa ILU + @check HYPRE_ILUGetFinalRelativeResidualNorm(s, r) + elseif s isa PCG + @check HYPRE_ParCSRPCGGetFinalRelativeResidualNorm(s, r) + else + throw(ArgumentError("cannot get residual norm for $(typeof(s))")) + end + return r[] +end + +""" + HYPRE.GetNumIterations(s::HYPRESolver) + +Return number of iterations during the last solve with solver `s`. + +This function dispatches on the solver to the corresponding C API wrapper +`LibHYPRE.HYPRE_\$(Solver)GetNumIterations`. +""" +function GetNumIterations(s::HYPRESolver) + r = Ref{HYPRE_Int}() + if s isa BiCGSTAB + @check HYPRE_ParCSRBiCGSTABGetNumIterations(s, r) + elseif s isa BoomerAMG + @check HYPRE_BoomerAMGGetNumIterations(s, r) + elseif s isa FlexGMRES + @check HYPRE_ParCSRFlexGMRESGetNumIterations(s, r) + elseif s isa GMRES + @check HYPRE_ParCSRGMRESGetNumIterations(s, r) + elseif s isa Hybrid + @check HYPRE_ParCSRHybridGetNumIterations(s, r) + elseif s isa ILU + @check HYPRE_ILUGetNumIterations(s, r) + elseif s isa PCG + @check HYPRE_ParCSRPCGGetNumIterations(s, r) + else + throw(ArgumentError("cannot get number of iterations for $(typeof(s))")) + end + return r[] +end diff --git a/test/runtests.jl b/test/runtests.jl index d10063d..2592061 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -381,6 +381,9 @@ end x_h = HYPRE.solve(bicg, A_h, b_h) copy!(x, x_h) @test x ≈ A \ b atol=tol + # Test solver queries + @test HYPRE.GetFinalRelativeResidualNorm(bicg) < tol + @test HYPRE.GetNumIterations(bicg) > 0 # Solve with preconditioner precond = HYPRE.BoomerAMG(; MaxIter = 1, Tol = 0.0) @@ -440,6 +443,9 @@ end x_h = HYPRE.solve(amg, A_h, b_h) copy!(x, x_h) @test x ≈ A \ b atol = tol * norm(b) + # Test solver queries + @test HYPRE.GetFinalRelativeResidualNorm(amg) < tol + @test HYPRE.GetNumIterations(amg) > 0 end @testset "FlexGMRES" begin @@ -466,6 +472,9 @@ end x_h = HYPRE.solve(gmres, A_h, b_h) copy!(x, x_h) @test x ≈ A \ b atol=tol + # Test solver queries + @test HYPRE.GetFinalRelativeResidualNorm(gmres) < tol + @test HYPRE.GetNumIterations(gmres) > 0 # Solve with preconditioner precond = HYPRE.BoomerAMG() @@ -506,6 +515,9 @@ end x_h = HYPRE.solve(gmres, A_h, b_h) copy!(x, x_h) @test x ≈ A \ b atol=tol + # Test solver queries + @test HYPRE.GetFinalRelativeResidualNorm(gmres) < tol + @test HYPRE.GetNumIterations(gmres) > 0 # Solve with preconditioner precond = HYPRE.BoomerAMG(; MaxIter = 1, Tol = 0.0) @@ -545,6 +557,9 @@ end x_h = HYPRE.solve(hybrid, A_h, b_h) copy!(x, x_h) @test x ≈ A \ b atol=tol + # Test solver queries + @test HYPRE.GetFinalRelativeResidualNorm(hybrid) < tol + @test HYPRE.GetNumIterations(hybrid) > 0 # Solve with given preconditioner precond = HYPRE.BoomerAMG() @@ -585,6 +600,9 @@ end x_h = HYPRE.solve(ilu, A_h, b_h) copy!(x, x_h) @test x ≈ A \ b atol=tol + # Test solver queries + @test HYPRE.GetFinalRelativeResidualNorm(ilu) < tol + @test HYPRE.GetNumIterations(ilu) > 0 # Use as preconditioner to PCG precond = HYPRE.ILU() @@ -623,6 +641,9 @@ end copy!(x, x_h) # Test result with direct solver @test x ≈ A \ b atol=tol + # Test solver queries (should error) + @test_throws ArgumentError("cannot get residual norm for HYPRE.ParaSails") HYPRE.GetFinalRelativeResidualNorm(parasails) + @test_throws ArgumentError("cannot get number of iterations for HYPRE.ParaSails") HYPRE.GetNumIterations(parasails) end @testset "(ParCSR)PCG" begin @@ -650,6 +671,10 @@ end x_h = HYPRE.solve(pcg, A_h, b_h) copy!(x, x_h) @test x ≈ A \ b atol=tol + # Test solver queries + @test HYPRE.GetFinalRelativeResidualNorm(pcg) < tol + @test HYPRE.GetNumIterations(pcg) > 0 + # Solve with AMG preconditioner precond = HYPRE.BoomerAMG(; MaxIter = 1, Tol = 0.0) pcg = HYPRE.PCG(; Tol = tol, Precond = precond)