LCOV - code coverage report
Current view: top level - shared/common/src/28_numeric_noabirule - m_iterative_solvers.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 37.6 % 194 73
Test Date: 2026-09-20 15:27:41 Functions: 50.0 % 10 5

            Line data    Source code
       1              : !!****m* ABINIT/m_iterative_solvers
       2              : !! NAME
       3              : !!  m_iterative_solvers
       4              : !!
       5              : !! FUNCTION
       6              : !!  This module contains basic (matrix-free) MPI-aware iterative solvers (GMRES, CG).
       7              : !!
       8              : !! COPYRIGHT
       9              : !! TODO
      10              : !!
      11              : !! SOURCE
      12              : 
      13              : #if defined HAVE_CONFIG_H
      14              : #include "config.h"
      15              : #endif
      16              : 
      17              : #include "abi_common.h"
      18              : 
      19              : module m_iterative_solvers
      20              : 
      21              :     use m_errors
      22              :     use defs_basis
      23              :     use m_xmpi
      24              :     use m_abicore
      25              :     use m_specialmsg
      26              : 
      27              :     implicit none
      28              :     private
      29              :     public :: cg_linear_solver, gmres_linear_solver, cg_eigen_solver_treshold
      30              : 
      31              :     contains
      32              : 
      33              : !-------------------------------------------------------------------------------------------
      34              : ! Eigensolvers
      35              : !-------------------------------------------------------------------------------------------
      36              : 
      37              :     !****f* m_iterative_solvers/cg_eigen_solver_treshold
      38              :     !! NAME
      39              :     !!  cg_eigen_solver_treshold
      40              :     !!
      41              :     !! FUNCTION
      42              :     !!  Compute the smallest eigenvalues and corresponding eigenvectors of a matrix using
      43              :     !!  the Conjugate Gradient method to minimize the Rayleigh quotient.
      44              :     !!  The stopping criterion is : at least one of the computed eigenvalues is above 'treshold'.
      45              :     !!
      46              :     !! INPUTS
      47              :     !!  n              = Size of the matrix.
      48              :     !!  matvec         = Subroutine that performs matrix-vector multiplication.
      49              :     !!  x0             = Initial guess for the eigenvector.
      50              :     !!  tol            = Convergence tolerance for the residual norm.
      51              :     !!  max_iter       = Maximum number of iterations for the Conjugate Gradient method.
      52              :     !!  max_neig       = Maximum number of eigenvalues to compute.
      53              :     !!  eigenvalue_threshold = Threshold below which to stop computing further eigenvalues.
      54              :     !!
      55              :     !! OUTPUTS
      56              :     !!  eigenvalues    = Array of computed smallest eigenvalues.
      57              :     !!  eigenvectors   = Matrix of corresponding eigenvectors.
      58              :     !!  n_eig          = Number of computed eigenvalues.
      59              :     !!
      60              :     !! SOURCE
      61            0 :     subroutine cg_eigen_solver_treshold(n, matvec, x0, tol, max_iter, max_neig, eigenvalue_threshold, eigenvalues, eigenvectors, n_eig)
      62              : 
      63              :         ! Input parameters
      64              :         integer, intent(in) :: n, max_iter, max_neig
      65              :         real(dp), intent(in) :: tol, eigenvalue_threshold
      66              :         real(dp), intent(in) :: x0(n)
      67              :         interface
      68              :             subroutine matvec(n_, x, y)
      69              :                 integer, intent(in) :: n_
      70              :                 double precision, intent(inout), target :: x(n_), y(n_)
      71              :             end subroutine matvec
      72              :         end interface
      73              : 
      74              :         ! Output parameters
      75              :         real(dp), intent(out) :: eigenvalues(max_neig)
      76              :         real(dp), intent(out) :: eigenvectors(n, max_neig)
      77              :         integer, intent(out) :: n_eig
      78              : 
      79              :         ! Local variables
      80            0 :         real(dp) :: x(n), Ax(n), p(n), g(n), beta
      81              :         real(dp) :: residual_norm, rayleigh_quotient
      82              :         integer :: i, j, iter
      83              :         logical :: do_exit
      84              :         integer :: ierr
      85              : 
      86              :         ! *************************************************************************
      87              : 
      88              :         ! Initialize variables
      89            0 :         x = x0
      90            0 :         call matvec(n, x, Ax)                                   ! Compute initial matrix-vector product
      91            0 :         rayleigh_quotient = dot_product(x, Ax) / dot_product(x, x)
      92            0 :         g = 2/dot_product(x, x) * (Ax - rayleigh_quotient * x)  ! Gradient of Rayleigh quotient
      93            0 :         p = 0
      94            0 :         beta = 0
      95              : 
      96              :         ! Conjugate Gradient iterations to minimize Rayleigh quotient
      97            0 :         do iter = 1, max_iter
      98            0 :             call cg_update(n, matvec, x, Ax, p, g, beta, rayleigh_quotient, residual_norm)
      99            0 :             do_exit = (residual_norm < tol)
     100            0 :             call xmpi_bcast(do_exit, 0, xmpi_world, ierr)
     101            0 :             if (do_exit) exit
     102              :         end do
     103              : 
     104              :         ! Store the computed eigenvalue and eigenvector
     105            0 :         eigenvalues(1) = rayleigh_quotient
     106            0 :         eigenvectors(:, 1) = x/norm2(x)
     107            0 :         n_eig = 1
     108              : 
     109              :         ! If more eigenvalues are required, use deflation to compute subsequent eigenvalues
     110            0 :         do j = 2, max_neig
     111              : 
     112              :             ! Check if the eigenvalue is below the threshold
     113            0 :             do_exit = (eigenvalues(j-1) < eigenvalue_threshold)
     114            0 :             call xmpi_bcast(do_exit, 0, xmpi_world, ierr)
     115            0 :             if (do_exit) exit
     116              : 
     117              :             ! If it is not, we need to compute more eigenvalues :
     118              :             ! Orthogonalize the initial guess against previously computed eigenvectors
     119            0 :             x = x0
     120            0 :             do i = 1, j - 1
     121            0 :                 x = x - dot_product(p, eigenvectors(:, i))/dot_product(eigenvectors(:, i), eigenvectors(:, i)) * eigenvectors(:, i)
     122              :             end do
     123              : 
     124              :             ! Initialization
     125            0 :             call matvec(n, x, Ax)                                   ! Compute initial matrix-vector product
     126            0 :             rayleigh_quotient = dot_product(x, Ax) / dot_product(x, x)
     127            0 :             g = 2/dot_product(x, x) * (Ax - rayleigh_quotient * x)  ! Gradient of Rayleigh quotient
     128            0 :             p = 0
     129            0 :             beta = 0
     130              : 
     131              :             ! Perform conjugate gradient iterations
     132            0 :             do iter = 1, max_iter
     133            0 :                 call cg_update(n, matvec, x, Ax, p, g, beta, rayleigh_quotient, residual_norm, eigenvectors(:, 1:j-1))
     134            0 :                 do_exit = (residual_norm < tol)
     135            0 :                 call xmpi_bcast(do_exit, 0, xmpi_world, ierr)   ! MPI aware: avoid desynchronization.
     136            0 :                 if (do_exit) exit
     137              :             end do
     138              : 
     139              :             ! Store the computed eigenvalue and eigenvector
     140            0 :             eigenvalues(j) = rayleigh_quotient
     141            0 :             eigenvectors(:, j) = x/norm2(x)
     142            0 :             n_eig = n_eig + 1
     143              : 
     144              :         end do
     145              : 
     146            0 :     end subroutine cg_eigen_solver_treshold
     147              : 
     148              :     !****f* m_iterative_solvers/cg_update
     149              :     !! NAME
     150              :     !!  cg_update
     151              :     !!
     152              :     !! FUNCTION
     153              :     !!  Perform a single Conjugate Gradient update step to minimize the Rayleigh quotient
     154              :     !!  of a self-adjoint operator given by matvec. This method is used to approximate
     155              :     !!  the eigenvalue and eigenvector of the operator.
     156              :     !!
     157              :     !! INPUTS
     158              :     !!  n              = Integer, size of the matrix (number of rows/columns).
     159              :     !!  matvec         = Subroutine, performs matrix-vector multiplication (A * v).
     160              :     !!
     161              :     !! INPUT/OUTPUTS
     162              :     !!  x              = Current solution vector (eigenvector approximation).
     163              :     !!  Ax             = A * x, where A is the operator defined by matvec.
     164              :     !!  p              = Previous search direction vector.
     165              :     !!  g              = Gradient of the Rayleigh-quotient at x.
     166              :     !!  beta           = Scalar, used to update the search direction.
     167              :     !!
     168              :     !! OUTPUTS
     169              :     !!  rayleigh_quotient = Current approximation of the eigenvalue (lambda).
     170              :     !!  residual_norm     = Norm of the residual.
     171              :     !!
     172              :     !! NOTES
     173              :     !!  - The input vectors (x, r, p, Ap) must be properly initialized before calling
     174              :     !!    this subroutine.
     175              :     !!  - The subroutine assumes that the operator is self-adjoint (Hermitian).
     176              :     !!
     177              :     !! SOURCE
     178            0 :     subroutine cg_update(n, matvec, x, Ax, p, g, beta, rayleigh_quotient, residual_norm, eigenvectors)
     179              :         ! Arguments
     180              :         integer, intent(in) :: n
     181              :         real(dp), optional, intent(in) :: eigenvectors(:,:)
     182              :         interface
     183              :             subroutine matvec(n_, x, y)
     184              :                 integer, intent(in) :: n_
     185              :                 double precision, intent(inout), target :: x(n_), y(n_)
     186              :             end subroutine matvec
     187              :         end interface
     188              :         real(dp), intent(inout) :: x(n), Ax(n), g(n), p(n), beta
     189              :         real(dp), intent(inout) :: rayleigh_quotient, residual_norm
     190              : 
     191              :         ! Local variables
     192            0 :         real(dp) :: Ap(n), a, b, c, d, e, f, alpha, alpha_(2)
     193              :         integer :: n_eig, i
     194              : 
     195              :         ! *************************************************************************
     196            0 :         if (present(eigenvectors)) then
     197            0 :             n_eig = size(eigenvectors, 2)   ! Number of eigenvectors already computed
     198              :         else
     199              :             n_eig = 0
     200              :         end if
     201              : 
     202              :         ! Update search direction
     203            0 :         p = -g + beta*p
     204              :         ! Orthogonalize the search direction against previously computed eigenvectors
     205            0 :         do i = 1, n_eig
     206            0 :             p = p - dot_product(p, eigenvectors(:, i))/dot_product(eigenvectors(:, i), eigenvectors(:, i)) * eigenvectors(:, i)
     207              :         end do
     208              : 
     209              :         ! Compute A*p
     210            0 :         call matvec(n, p, Ap)
     211              : 
     212              :         ! Compute alpha (step size = minimizer of R(x+alpha*p) that is the solution (+) of a quadratic problem)
     213            0 :         a = dot_product(p, Ap)
     214            0 :         b = 2*dot_product(x, Ap)
     215            0 :         c = dot_product(x, Ax)
     216            0 :         d = dot_product(p, p)
     217            0 :         e = 2*dot_product(x, p)
     218            0 :         f = dot_product(x, x)
     219            0 :         alpha_ = quadratic_roots(a*e-b*d, 2*(f*a-d*c), b*f-c*e)
     220            0 :         alpha = alpha_(1)
     221              : 
     222              :         ! Update solution vector
     223            0 :         x = x + alpha * p
     224            0 :         Ax = Ax + alpha * Ap
     225              : 
     226              :         ! Update the Rayleigh-quotient
     227            0 :         rayleigh_quotient = dot_product(x, Ax)/dot_product(x, x)
     228              : 
     229              :         ! Update the (x-normalized) gradient and beta = dot(g, g)/dot(g_old, g_old)
     230            0 :         beta = 1/dot_product(g, g)
     231            0 :         g = 2/norm2(x) * (Ax - rayleigh_quotient * x)
     232            0 :         beta = beta * dot_product(g, g)
     233              : 
     234              :         ! Update the residual norm (= norm of the gradient)
     235            0 :         residual_norm = norm2(g)
     236              : 
     237            0 :     end subroutine cg_update
     238              : 
     239            0 :     function quadratic_roots(a, b, c) result(r)
     240              :         ! Arguments
     241              :         real(dp) :: a, b, c
     242              :         real(dp) :: r(2)
     243              : 
     244              :         ! Local variables
     245              :         real(dp) :: discriminant, sqrt_discriminant
     246              : 
     247              :         ! *************************************************************************
     248              : 
     249            0 :         if (a == 0.0_dp) then
     250            0 :             r(1) = 0.0_dp
     251            0 :             r(2) = 0.0_dp
     252            0 :             return
     253              :         end if
     254              : 
     255            0 :         discriminant = b**2 - 4.0_dp * a * c
     256            0 :         if (discriminant < 0.0_dp) then
     257            0 :             r(1) = 0.0_dp
     258            0 :             r(2) = 0.0_dp
     259            0 :             return
     260              :         end if
     261              : 
     262            0 :         sqrt_discriminant = sqrt(discriminant)
     263            0 :         r(1) = (-b + sqrt_discriminant) / (2.0_dp * a)
     264            0 :         r(2) = (-b - sqrt_discriminant) / (2.0_dp * a)
     265              : 
     266              :     end function quadratic_roots
     267              : 
     268              : !-------------------------------------------------------------------------------------------
     269              : ! Linear solvers (CG and GMRES)
     270              : !-------------------------------------------------------------------------------------------
     271              : 
     272            0 :     subroutine cg_linear_solver(n, matvec, rhs, est, cg_maxiter, cg_rtol, verbose)
     273              : 
     274              :         !Arguments ------------------------------------
     275              :         integer, intent(in) :: n, cg_maxiter
     276              :         real(dp), intent(in) :: cg_rtol
     277              :         real(dp), intent(in) :: rhs(:)
     278              :         logical, intent(in) :: verbose
     279              :         real(dp),intent(inout) :: est(:)
     280              :         character(len=500) :: msg
     281              :         interface
     282              :             subroutine matvec(n_, x, y)
     283              :                 integer, intent(in) :: n_
     284              :                 double precision, intent(inout), target :: x(n_), y(n_)
     285              :             end subroutine matvec
     286              :         end interface
     287              : 
     288              :         !Local variables-------------------------------
     289            0 :         real(dp) :: r(n), p(n), Ap(n)
     290              :         real(dp) :: alpha, beta, rsold, rsnew
     291              :         integer :: iter
     292              :         logical :: do_exit
     293              :         integer :: ierr
     294              : 
     295              :         ! *************************************************************************
     296              : 
     297              :         ! Initialize
     298            0 :         call matvec(n, est, Ap)
     299            0 :         r = rhs - Ap
     300            0 :         p = r
     301            0 :         rsold = dot_product(r, r)
     302              : 
     303              :         ! Conjugate Gradient iterations
     304            0 :         do iter = 1, cg_maxiter
     305            0 :             call matvec(n, p, Ap)
     306            0 :             alpha = rsold / dot_product(p, Ap)
     307            0 :             est = est + alpha * p
     308            0 :             r = r - alpha * Ap
     309            0 :             rsnew = dot_product(r, r)
     310              : 
     311              :             ! Check for convergence
     312            0 :             do_exit = (sqrt(rsnew) < cg_rtol)
     313            0 :             call xmpi_bcast(do_exit, 0, xmpi_world, ierr)   ! MPI aware: avoid desynchronization.
     314            0 :             if (do_exit) exit
     315              : 
     316            0 :             beta = rsnew / rsold
     317            0 :             p = r + beta * p
     318            0 :             write(msg, *)'cg: it=', iter,' res=', sqrt(rsnew)
     319            0 :             if (verbose) call wrtout(std_out, msg)
     320              :             !if (verbose) write(std_out,*) 'cg: it=', iter,' res=', sqrt(rsnew)
     321            0 :             rsold = rsnew
     322              :         end do
     323              : 
     324            0 :     end subroutine cg_linear_solver
     325              : 
     326              :     !****f* m_iterative_solvers/call_gmresm
     327              :     !! NAME
     328              :     !!  call_gmresm
     329              :     !!
     330              :     !! FUNCTION
     331              :     !!  Call the gmresm (Willis, A. (2017) SoftwareX 6, 124-127, code at the end of this file) routine to solve a linear system.
     332              :     !!
     333              :     !! INPUTS
     334              :     !!  n              = Size of the matrix.
     335              :     !!  matvec         = Subroutine that performs matrix-vector multiplication.
     336              :     !!  rhs            = Right-hand side vector of the linear system.
     337              :     !!  gmres_maxiter  = Maximum number of iterations for the GMRES algorithm.
     338              :     !!  gmres_rtol     = Relative tolerance for convergence.
     339              :     !!  verbose        = Logical, if true, print residuals at each iteration.
     340              :     !!
     341              :     !! INPUT/OUTPUTS
     342              :     !!  est            = Initial guess for the solution vector, updated with the computed solution.
     343              :     !!
     344              :     !! SOURCE
     345           18 :     subroutine call_gmresm(n, matvec, est, rhs, gmres_maxiter, gmres_rtol, verbose)
     346              :         !Arguments ------------------------------------
     347              :         integer, intent(in) :: n, gmres_maxiter
     348              :         real(dp), intent(in) :: gmres_rtol
     349              :         real(dp), intent(in) :: rhs(n)
     350              :         logical, intent(in) :: verbose
     351              :         real(dp), intent(inout) :: est(n)
     352              :         interface
     353              :             subroutine matvec(n_, x, y)
     354              :                 integer, intent(in) :: n_
     355              :                 double precision, intent(inout), target :: x(n_), y(n_)
     356              :             end subroutine matvec
     357              :         end interface
     358              :         !Local variables-------------------------------
     359              :         integer :: its, info, m
     360              :         real(dp) :: res, del
     361           18 :         real(dp), allocatable :: h(:, :), v(:, :)
     362              : 
     363              :         ! *************************************************************************
     364              : 
     365           18 :         m = gmres_maxiter
     366           72 :         ABI_MALLOC(h, (m+1, m))
     367           72 :         ABI_MALLOC(v, (n, m+1))
     368       121518 :         res = gmres_rtol * norm2(rhs)
     369           18 :         del = 0
     370           18 :         its = gmres_maxiter  ! No restart
     371           18 :         info = 0
     372           18 :         if (verbose) then
     373            0 :             info = 1
     374              :         end if
     375           18 :         call gmresm(m, n, est, rhs, matvec, psolve, dotprd, h, v, res, del, its, info)
     376           18 :         ABI_FREE(h)
     377           18 :         ABI_FREE(v)
     378              : 
     379              :         contains
     380              :         ! Dummy :  No preconditioning
     381          108 :         subroutine psolve(n_, x)
     382              :             integer, intent(in) :: n_
     383              :             real(dp), intent(inout) :: x(n_)
     384              :             ! ***********************
     385              :             ! We do nothing here but don't wan't to be flashed by abirule.
     386              :             if (.false.) then
     387              :                 x = zero
     388              :             end if
     389          108 :         end subroutine psolve
     390              :         ! Dot product
     391          406 :         function dotprd(n_, a, b) result(c)
     392              :             integer, intent(in) :: n_
     393              :             real(dp), intent(inout) :: a(n_), b(n_)
     394              :             real(dp) :: c
     395              :             ! ***********************
     396      2740906 :             c = dot_product(a, b)
     397          406 :         end function dotprd
     398              : 
     399              :     end subroutine call_gmresm
     400              : 
     401              :     !****f* m_iterative_solvers/gmres_linear_solver
     402              :     !! NAME
     403              :     !!  gmres_linear_solver
     404              :     !!
     405              :     !! FUNCTION
     406              :     !!  Solve a linear system using GMRES. Depending on the availability of MKL,
     407              :     !!  it either calls the MKL FGMRES routine or the gmresm routine.
     408              :     !!
     409              :     !! INPUTS
     410              :     !!  n              = Size of the matrix.
     411              :     !!  matvec         = Subroutine that performs matrix-vector multiplication.
     412              :     !!  rhs            = Right-hand side vector of the linear system.
     413              :     !!  gmres_maxiter  = Maximum number of iterations for the GMRES algorithm.
     414              :     !!  gmres_rtol     = Relative tolerance for convergence.
     415              :     !!
     416              :     !! INPUT/OUTPUTS
     417              :     !!  est            = Initial guess for the solution vector, updated with the computed solution.
     418              :     !!
     419              :     !! SOURCE
     420           18 :     subroutine gmres_linear_solver(n, matvec, rhs, est, gmres_maxiter, gmres_rtol, verbose)
     421              :         !Arguments ------------------------------------
     422              :         integer, intent(in) :: n, gmres_maxiter
     423              :         real(dp), intent(in) :: gmres_rtol
     424              :         real(dp), intent(in) :: rhs(n)
     425              :         logical :: verbose
     426              :         real(dp), intent(inout) :: est(n)
     427              :         interface
     428              :             subroutine matvec(n_, x, y)
     429              :                 integer, intent(in) :: n_
     430              :                 double precision, intent(inout), target :: x(n_), y(n_)
     431              :             end subroutine matvec
     432              :         end interface
     433              : 
     434              :         ! *************************************************************************
     435              : 
     436           18 :         call call_gmresm(n, matvec, est, rhs, gmres_maxiter, gmres_rtol, verbose)
     437              : 
     438           18 :     end subroutine gmres_linear_solver
     439              : 
     440              : !-------------------------------------------------------------------------------------------
     441              : 
     442              : !----------------------------------------------------------------------
     443              : ! Openpipeflow.org.  If used in your work, please cite
     444              : ! Willis, A. (2017) SoftwareX 6, 124-127.
     445              : ! https://doi.org/10.1016/j.softx.2017.05.003 (open access)
     446              : !                                      Thanks in advance! Ashley 2019.
     447              : !----------------------------------------------------------------------
     448              : ! solve A x = b for x ;
     449              : ! minimise |Ax-b| subject to constraint |x| < delta .
     450              : ! requires lapack routines dgelsy, dgesvd.
     451              : !----------------------------------------------------------------------
     452              : ! m       gmres dimension
     453              : ! n       dimension of x
     454              : ! x       on input:  guess for x, can be 0
     455              : !         on exit:  solution x, subject to constraint if del>0
     456              : ! b       input b
     457              : ! matvec  performs y := A x, call matvec(N,x, y)
     458              : ! psolve  preconditioner, solve M x_out = x_in, call psolve(N,x)
     459              : ! dotprd  dot product, d = dotprd(n,a,b)
     460              : ! h       Hessian matrix,  size (m+1)*m
     461              : ! v       Krylov subspace, size n*(m+1)
     462              : ! res     on input: |Ax-b|/|b|<res;
     463              : !         on exit:  residual reached
     464              : ! del     on input: if(del>0) then the x returned is the hookstep
     465              : !         on exit:  norm of next b predicted by hook
     466              : ! its     on input: max num its;
     467              : !         on exit:  number of its taken
     468              : ! info    on input: if(info==1) print* residuals
     469              : !                   if(info==2) recalc hookstep with new del
     470              : !         on exit:  0 sucessful, 1 method breakdown, 2 max its
     471              : !                                                       A.P.Willis 2008
     472              : !----------------------------------------------------------------------
     473              : 
     474           18 :  subroutine gmresm(m,n,x,b,matvec,psolve,dotprd,h,v,res,del,its,info)
     475              :    implicit none
     476              :    integer,          intent(in)    :: m
     477              :    integer,          intent(in)    :: n
     478              :    real(dp), intent(inout), target :: x(n)
     479              :    real(dp), intent(in)            :: b(n)
     480              :    interface
     481              :       subroutine matvec(n_, x, y)
     482              :          integer, intent(in) :: n_
     483              :          double precision, intent(inout), target :: x(n_), y(n_)
     484              :       end subroutine matvec
     485              :       subroutine psolve(n_, x)
     486              :          integer, intent(in) :: n_
     487              :          double precision, intent(inout) :: x(n_)
     488              :       end subroutine psolve
     489              :       function dotprd(n_, a, b) result(c)
     490              :          integer, intent(in) :: n_
     491              :          double precision, intent(inout) :: a(n_), b(n_)
     492              :          double precision :: c
     493              :       end function dotprd
     494              :    end interface
     495              :    real(dp), intent(inout)         :: h(m+1,m)
     496              :    real(dp), intent(inout)         :: v(n,m+1)
     497              :    real(dp), intent(inout)         :: res
     498              :    real(dp), intent(inout)         :: del
     499              :    integer,          intent(inout) :: its
     500              :    integer,          intent(inout) :: info
     501              :    real(dp) :: tol,res_,stgn
     502           36 :    real(dp), target :: w(n), z(n)
     503           36 :    real(dp) :: h_(m+1,m), y(m+1), p(m+1), work(4*m+1)
     504           18 :    integer :: imx, piv(m), rank, i
     505              :    real(dp), save :: beta
     506              :    integer, save :: j
     507              :    logical :: done
     508              :    integer :: ierr
     509              :    character(len=500) :: msg
     510              : 
     511           18 :    if(info==2) then
     512            0 :       call hookstep(j,h,m,beta,del, y)
     513            0 :       z = matmul(v(:,1:j),y(1:j))
     514            0 :       call psolve(n, z)
     515            0 :       x = z
     516            0 :       info = 0
     517            0 :       return
     518              :     end if
     519              : 
     520           18 :    tol = res
     521           18 :    imx = its
     522           18 :    its = 0
     523      2551896 :    v   = 0d0
     524              : 
     525              :  1 continue
     526           18 :    res_ = 1d99
     527           18 :    stgn = 1d0 - 1d-14
     528              : 
     529           18 :    beta = dsqrt(dotprd(n,x,x))
     530       121518 :    if(beta==0d0)  w = 0d0
     531           18 :    if(beta/=0d0)  call matvec(n,x, w)
     532       121518 :    w = b - w
     533           18 :    beta = dsqrt(dotprd(n,w,w))
     534       121518 :    v(:,1) = w / beta
     535              : 
     536         7938 :    h = 0d0
     537           90 :    do j = 1, m
     538           90 :       its = its + 1
     539       607590 :       z = v(:,j)
     540           90 :       call psolve(n, z)
     541           90 :       call matvec(n, z, w)
     542          370 :       do i = 1, j
     543          280 :          h(i,j) = dotprd(n,w,v(1,i))
     544      1890370 :          w = w - h(i,j)*v(:,i)
     545              :       end do
     546           90 :       h(j+1,j) = dsqrt(dotprd(n,w,w))
     547       607590 :       v(:,j+1) = w / h(j+1,j)
     548              : 
     549           90 :       p(1) = beta
     550          370 :       p(2:j+1) = 0d0
     551         1760 :       h_(1:j+1,1:j) = h(1:j+1,1:j)
     552              :       !call dgelsy(j+1,j,1,h_(1:m+1, 1:j),m+1,p,m+1,piv,m,rank,work,4*m+1,i)
     553           90 :       call dgelsy(j+1,j,1,h_,m+1,p,m+1,piv,m,rank,work,4*m+1,i)
     554           90 :       if(i/=0) stop 'gmresm: dgelsy'
     555         1980 :       y = p
     556              : 
     557         2680 :       p(1:j+1) = - matmul(h(1:j+1,1:j),y(1:j))
     558           90 :       p(1) = p(1) + beta
     559          460 :       res = dsqrt(dot_product(p(1:j+1),p(1:j+1)))
     560              :       ! MPI aware: broadcast the 'res' value of master to avoid desynchronization.
     561           90 :       call xmpi_bcast(res, 0, xmpi_world, ierr)
     562              :       !if(info==1) print*, 'gmresm: it=', its,' res=', real(res)
     563           90 :       write(msg, *)'gmresm: it=', its,' res=', real(res)
     564           90 :       if(info==1) call wrtout(std_out, msg)
     565              : 
     566           90 :       done = (res<=tol .or. its==imx .or. res>res_)
     567           72 :       if(done .or. j==m) then
     568           18 :         if(del>0d0)  call hookstep(j,h,m,beta,del, y)
     569       729108 :          z = matmul(v(:,1:j),y(1:j))
     570           18 :          call psolve(n, z)
     571       121518 :          x = x + z
     572           18 :         if(its==imx) info = 2
     573           18 :         if(res>res_) info = 1
     574           18 :         if(res<=tol) info = 0
     575           18 :          if(done)     return
     576              :         !if(del>0d0)  print*, 'gmres: warning! restart affects hookstep'
     577            0 :         if(del>0d0) call wrtout(std_out, 'gmres: warning! restart affects hookstep')
     578              :          goto 1       ! (j==m) restart
     579              :       end if
     580           72 :       res_ = res*stgn
     581              : 
     582              :    end do
     583              : 
     584              :  end subroutine gmresm
     585              : 
     586              : 
     587              : !-----------------------------------------------------------------
     588              : ! replace y with a vector that generates a hookstep
     589              : ! c.f. Viswanath (2008) arXiv:0809.1498
     590              : !-----------------------------------------------------------------
     591            0 :  subroutine hookstep(j,h,m,beta,del, y)
     592              :    implicit none
     593              :    integer,          intent(in)    :: j, m
     594              :    real(dp), intent(in)            :: h(m+1,j), beta
     595              :    real(dp), intent(inout)         :: del
     596              :    real(dp), intent(out)           :: y(j)
     597            0 :    real(dp) :: a(j+1,j), s(j), u(j+1,j+1), vt(j,j), work(5*(j+1))
     598            0 :    real(dp) :: p(j+1), q(j), mu, qn
     599              :    integer :: info
     600              : 
     601            0 :    a = h(1:j+1,1:j)
     602              : 
     603            0 :    call dgesvd('A','A',j+1,j,a,j+1,s,u,j+1,vt,j,work,5*(j+1),info)
     604            0 :    if(info/=0) stop 'hookstep: dgesvd'
     605              : 
     606            0 :    p(1:j) = beta * u(1,1:j)
     607              : 
     608            0 :    mu = max(s(j)*s(j)*1d-6,1d-99)
     609            0 :    qn = 1d99
     610            0 :    do while(qn>del)
     611            0 :       mu = mu * 1.1d0
     612            0 :       q = p(1:j)*s/(mu+s*s)
     613            0 :       qn = dsqrt(dot_product(q,q))
     614              :    end do
     615              : 
     616            0 :    y = matmul(q,vt)
     617              : 
     618            0 :    p = - matmul(h(1:j+1,1:j),y(1:j))
     619            0 :    p(1) = p(1) + beta
     620            0 :    del = dsqrt(dot_product(p,p))
     621              : 
     622            0 :  end subroutine hookstep
     623              : 
     624           90 : end module m_iterative_solvers
     625              : !!***
        

Generated by: LCOV version 2.3-1