gmres_solve Subroutine

public subroutine gmres_solve(solution, lhs, rhs, resid_norm, flag, nlhs, nrpre, nli, tol, precond, rpar, ipar, resid_update, iter_max, krylov_dim, inner_prod, norm)

A wraper for the nitsol implementation of the generalised minimal residual method (GMRES) for iteratively solving linear systems. This provides a more general interface not specifically intended for use with Newton iteration. It also uses Fortran 90 features to provide a more convenient call signature.

Arguments

Type IntentOptional AttributesName
real(kind=r8), intent(inout), dimension(:):: solution

On input, an initial guess of the solution to the linear system. On output, the iteratively determined solution.

procedure(mat_mult) :: lhs

The linear operator on the left hand side of the linear system.

real(kind=r8), intent(in), dimension(:):: rhs

The right hand side of the linear system being solved

real(kind=r8), intent(out) :: resid_norm

GMRES residual norm on return

integer, intent(out) :: flag

Termination flag. Values have the following meanings:

0
normal termination: acceptable solution found
1
failure
2
failure
3
Acceptable solution not found in iter_max GMRES iterations
4
Insignificant residual norm reduction of a cycle of kdmax steps (stagnation) before an acceptable solution has been found.
5
Dangerous ill-conditioning detected before an acceptable solution has been found.
integer, intent(out), optional :: nlhs

Number of evaluations of the left hand side of the system

integer, intent(out), optional :: nrpre

Number of evaluations of the right-preconditioner

integer, intent(out), optional :: nli

Number of iterations

real(kind=r8), intent(in), optional :: tol

The tolerance for the solution. Default is size(solution) * 1e-8.

procedure(mat_mult), optional :: precond

A right-preconditioner which may be used to improve convergence of the solution.

real(kind=r8), intent(inout), optional dimension(*):: rpar

Parameter/work array passed to the lhs and precond routines.

integer, intent(inout), optional dimension(*):: ipar

Parameter/work array passed to the lhs and precond routines

integer, intent(in), optional :: resid_update

Residual update flag. On GMRES restarts, the residual can be updated using a linear combination (iresup == 0) or by direct evaluation (iresup == 1). The first is cheap (one n-vector saxpy) but may lose accuracy with extreme residual reduction; the second retains accuracy better but costs one product. Default is 0.

integer, intent(in), optional :: iter_max

Maximum allowable number of GMRES iterations. Default is 1000.

integer, intent(in), optional :: krylov_dim

Maximum Krylov subspace dimension; default 10.

procedure(dinpr_intr), optional :: inner_prod

Inner-product routine, either user-supplied or BLAS ddot.

procedure(dnorm_intr), optional :: norm

Norm routine, either user supplied or BLAS dnrm2.


Calls

proc~~gmres_solve~~CallsGraph proc~gmres_solve gmres_solve interface~nitgm2 nitgm2 proc~gmres_solve->interface~nitgm2

Called by

proc~~gmres_solve~~CalledByGraph proc~gmres_solve gmres_solve proc~quasilinear_solve quasilinear_solve proc~quasilinear_solve->proc~gmres_solve

Contents

Source Code


Source Code

  subroutine gmres_solve(solution, lhs, rhs, resid_norm, flag, nlhs, nrpre, &
                         nli, tol, precond, rpar, ipar, resid_update,       &
                         iter_max, krylov_dim, inner_prod, norm)
    !* Author: Chris MacMackin
    !  Date: March 2017
    !
    ! A wraper for the
    ! [nitsol](http://users.wpi.edu/~walker/Papers/nitsol,SISC_19,1998,302-318.pdf)
    ! implementation of the generalised minimal residual method
    ! ([GMRES](https://en.wikipedia.org/wiki/Generalized_minimal_residual_method))
    ! for iteratively solving linear systems. This provides a more
    ! general interface not specifically intended for use with Newton
    ! iteration. It also uses Fortran 90 features to provide a more
    ! convenient call signature.
    !
    real(r8), dimension(:), intent(inout)           :: solution
      !! On input, an initial guess of the solution to the linear
      !! system. On output, the iteratively determined solution.
    procedure(mat_mult)                             :: lhs
      !! The linear operator on the left hand side of the linear
      !! system.
    real(r8), dimension(:), intent(in)              :: rhs
      !! The right hand side of the linear system being solved
    real(r8), intent(out)                           :: resid_norm
      !! GMRES residual norm on return
    integer, intent(out)                            :: flag
      !! Termination flag. Values have the following meanings:
      !!
      !!0
      !!:    normal termination: acceptable solution found
      !!
      !!1
      !!:    \(J\vec{v}\)  failure
      !!
      !!2
      !!:    \(P^{-1}\vec{v}\) failure
      !!
      !!3
      !!:    Acceptable solution not found in `iter_max` GMRES iterations
      !!
      !!4
      !!:    Insignificant residual norm reduction of a cycle of `kdmax` 
      !!     steps (stagnation) before an acceptable solution has been 
      !!     found.
      !!
      !!5
      !!:    Dangerous ill-conditioning detected before an acceptable 
      !!     solution has been found.
    real(r8), intent(in), optional                  :: tol
      !! The tolerance for the solution. Default is `size(solution) * 1e-8`.
    integer, intent(out), optional                  :: nlhs
        !! Number of evaluations of the left hand side of the system
    integer, intent(out), optional                  :: nrpre
        !! Number of evaluations of the right-preconditioner
    integer, intent(out), optional                  :: nli
        !! Number of iterations
    procedure(mat_mult), optional                   :: precond
      !! A right-preconditioner which may be used to improve
      !! convergence of the solution.
    real(r8), dimension(*), intent(inout), optional :: rpar
      !! Parameter/work array passed to the `lhs` and `precond` routines.
    integer, dimension(*), intent(inout), optional  :: ipar
      !! Parameter/work array passed to the `lhs` and `precond` routines
    integer, intent(in), optional                   :: resid_update
      !! Residual update flag. On GMRES restarts, the residual can
      !! be updated using a linear combination (`iresup == 0`) or by
      !! direct evaluation (`iresup == 1`). The first is cheap (one
      !! n-vector saxpy) but may lose accuracy with extreme residual
      !! reduction; the second retains accuracy better but costs one
      !! \(J\vec{v}\) product. Default is 0.
    integer, intent(in), optional                   :: iter_max
      !! Maximum allowable number of GMRES iterations. Default is
      !! 1000.
    integer, intent(in), optional                   :: krylov_dim
      !! Maximum Krylov subspace dimension; default 10.    
    procedure(dinpr_intr), optional                 :: inner_prod
      !! Inner-product routine, either user-supplied or BLAS `ddot`.
    procedure(dnorm_intr), optional                 :: norm
      !! Norm routine, either user supplied or BLAS dnrm2.
    
    integer  :: npoints, preflag, resup, itmax, kdim, nfe, lnlhs, lnrpre, lnli
    real(r8) :: eta
    procedure(dinpr_intr), pointer :: dinpr
    procedure(dnorm_intr), pointer :: dnorm
    real(r8), dimension(:), allocatable, save :: xcur, svbig, svsml, w, rwork
    real(r8), dimension(:,:), allocatable, save :: vv, rr

    lnlhs  = 0
    lnrpre = 0
    lnli   = 0

    npoints = size(solution)
    if (present(precond)) then
       preflag = 1
    else
       preflag = 0
    end if
    if (present(tol)) then
      eta = tol
    else
      eta = 1.e-8_r8 * npoints
    end if
    if (present(resid_update)) then
      resup = resid_update
    else
      resup = 0
    end if
    if (present(iter_max)) then
      itmax = iter_max
    else
      itmax = 1000
    end if
    if (present(krylov_dim)) then
      kdim = krylov_dim
    else
      kdim = 10
    end if
    if (present(inner_prod)) then
      dinpr => inner_prod
    else
      dinpr => ddot
    end if
    if (present(norm)) then
      dnorm => norm
    else
      dnorm => dnrm2
    end if

    xcur = solution

    if (allocated(vv)) then
      if (any(shape(vv) /= [npoints, kdim+1])) then
        deallocate(vv)
        allocate(vv(npoints, kdim+1))
      end if
      if (size(svbig) /= kdim) then
        deallocate(rr)
        deallocate(svbig)
        deallocate(svsml)
        deallocate(w)
        allocate(rr(kdim, kdim))
        allocate(svbig(kdim))
        allocate(svsml(kdim))
        allocate(w(kdim))
      end if
      if (size(rwork) /= npoints) then
        deallocate(rwork)
        allocate(rwork(npoints))
      end if
    else
      allocate(vv(npoints, kdim+1))
      allocate(rr(kdim, kdim))
      allocate(svbig(kdim))
      allocate(svsml(kdim))
      allocate(w(kdim))
      allocate(rwork(npoints))
    end if

    call nitgm2(npoints, xcur, -rhs, solution, eta, dummy_f, jacv, &
                rpar, ipar, 1, preflag, itmax, resup, 1, nfe,      &
                lnlhs, lnrpre, lnli, kdim, kdim+1, vv, rr, svbig,  &
                svsml, w, rwork, resid_norm, dinpr, dnorm, flag)

    if (present(nlhs))  nlhs  = lnlhs
    if (present(nrpre)) nrpre = lnrpre
    if (present(nli))   nli   = lnli

  contains
    
    subroutine jacv(n, xcur, fcur, ijob, v, z, rpar, ipar, itrmjv)
      !! A wrapper on the user-provided routines for the linear
      !! operator and preconditioner, to put them in the form NITSOL
      !! expects.
      !! 
      integer, intent(in)                   :: n
        ! Dimension of the problem
      real(r8), dimension(n), intent(in)    :: xcur
        ! Array of length `n` containing the current \(x\) value
      real(r8), dimension(n), intent(in)    :: fcur
        ! Array of length `n` containing the current \(f(x)\) value
      integer, intent(in)                   :: ijob
        ! Integer flag indicating which product is desired. 0
        ! indicates \(z = J\vec{v}\). 1 indicates \(z = P^{-1}\vec{v}\).
      real(r8), dimension(n), intent(in)    :: v
        ! An array of length `n` to be multiplied
      real(r8), dimension(n), intent(out)   :: z
        ! An array of length n containing the desired product on
        ! output.
      real(r8), dimension(*), intent(inout) :: rpar
        ! Parameter/work array 
      integer, dimension(*), intent(inout)  :: ipar
        ! Parameter/work array
      integer, intent(out)                  :: itrmjv
        ! Termination flag. 0 indcates normal termination, 1
        ! indicatesfailure to prodce \(J\vec{v}\), and 2 indicates
        ! failure to produce \(P^{-1}\vec{v}\)
      logical :: success
      itrmjv = 0
      if (ijob == 0) then
        z = lhs(v, xcur, fcur, rpar, ipar, success)
        if (.not. success) itrmjv = 1
      else if (ijob == 1) then
        z = precond(v, xcur, fcur, rpar, ipar, success)
        if (.not. success) itrmjv = 2
      else
        error stop ('`ijob` not equal to 0 or 1.')
      end if
    end subroutine jacv

  end subroutine  gmres_solve