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: 38.3 % 196 75
Test Date: 2026-11-07 19:31:18 Functions: 50.0 % 10 5
Branches: 28.1 % 356 100

             Branch data     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                 :             :    real(dp) :: rcond
     503                 :          36 :    real(dp), target :: w(n), z(n)
     504                 :          36 :    real(dp) :: h_(m+1,m), y(m+1), p(m+1), work(4*m+1)
     505                 :          18 :    integer :: imx, piv(m), rank, i
     506                 :             :    real(dp), save :: beta
     507                 :             :    integer, save :: j
     508                 :             :    logical :: done
     509                 :             :    integer :: ierr
     510                 :             :    character(len=500) :: msg
     511                 :             : 
     512         [ -  + ]:          18 :    if(info==2) then
     513                 :           0 :       call hookstep(j,h,m,beta,del, y)
     514   [ #  #  #  #  :           0 :       z = matmul(v(:,1:j),y(1:j))
                   #  # ]
     515                 :           0 :       call psolve(n, z)
     516         [ #  # ]:           0 :       x = z
     517                 :           0 :       info = 0
     518                 :           0 :       return
     519                 :             :     end if
     520                 :             : 
     521                 :          18 :    tol = res
     522                 :          18 :    imx = its
     523                 :          18 :    its = 0
     524   [ +  +  +  + ]:     2551896 :    v   = 0d0
     525         [ +  + ]:         378 :    piv = 0
     526                 :          18 :    rcond = 1.0d-14
     527                 :             : 
     528                 :             :  1 continue
     529                 :          18 :    res_ = 1d99
     530                 :          18 :    stgn = 1d0 - 1d-14
     531                 :             : 
     532                 :          18 :    beta = dsqrt(dotprd(n,x,x))
     533   [ +  -  +  + ]:      121518 :    if(beta==0d0)  w = 0d0
     534         [ -  + ]:          18 :    if(beta/=0d0)  call matvec(n,x, w)
     535         [ +  + ]:      121518 :    w = b - w
     536                 :          18 :    beta = dsqrt(dotprd(n,w,w))
     537         [ +  + ]:      121518 :    v(:,1) = w / beta
     538                 :             : 
     539   [ +  +  +  + ]:        7938 :    h = 0d0
     540         [ +  - ]:          90 :    do j = 1, m
     541                 :          90 :       its = its + 1
     542         [ +  + ]:      607590 :       z = v(:,j)
     543                 :          90 :       call psolve(n, z)
     544                 :          90 :       call matvec(n, z, w)
     545         [ +  + ]:         370 :       do i = 1, j
     546                 :         280 :          h(i,j) = dotprd(n,w,v(1,i))
     547         [ +  + ]:     1890370 :          w = w - h(i,j)*v(:,i)
     548                 :             :       end do
     549                 :          90 :       h(j+1,j) = dsqrt(dotprd(n,w,w))
     550         [ +  + ]:      607590 :       v(:,j+1) = w / h(j+1,j)
     551                 :             : 
     552                 :          90 :       p(1) = beta
     553         [ +  + ]:         370 :       p(2:j+1) = 0d0
     554   [ +  +  +  + ]:        1760 :       h_(1:j+1,1:j) = h(1:j+1,1:j)
     555                 :             :       !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)
     556                 :          90 :       call dgelsy(j+1,j,1,h_,m+1,p,m+1,piv,rcond,rank,work,4*m+1,i)
     557         [ -  + ]:          90 :       if(i/=0) stop 'gmresm: dgelsy'
     558         [ +  + ]:        1980 :       y = p
     559                 :             : 
     560   [ +  -  +  -  :        2680 :       p(1:j+1) = - matmul(h(1:j+1,1:j),y(1:j))
          +  -  -  +  -  
          +  +  +  +  +  
          +  +  +  +  +  
                      - ]
     561                 :          90 :       p(1) = p(1) + beta
     562         [ +  + ]:         460 :       res = dsqrt(dot_product(p(1:j+1),p(1:j+1)))
     563                 :             :       ! MPI aware: broadcast the 'res' value of master to avoid desynchronization.
     564                 :          90 :       call xmpi_bcast(res, 0, xmpi_world, ierr)
     565                 :             :       !if(info==1) print*, 'gmresm: it=', its,' res=', real(res)
     566                 :          90 :       write(msg, *)'gmresm: it=', its,' res=', real(res)
     567         [ -  + ]:          90 :       if(info==1) call wrtout(std_out, msg)
     568                 :             : 
     569   [ +  +  +  -  :          90 :       done = (res<=tol .or. its==imx .or. res>res_)
                   +  - ]
     570         [ -  + ]:          72 :       if(done .or. j==m) then
     571         [ -  + ]:          18 :         if(del>0d0)  call hookstep(j,h,m,beta,del, y)
     572   [ +  +  +  +  :      729108 :          z = matmul(v(:,1:j),y(1:j))
                   +  + ]
     573                 :          18 :          call psolve(n, z)
     574         [ +  + ]:      121518 :          x = x + z
     575         [ -  + ]:          18 :         if(its==imx) info = 2
     576         [ -  + ]:          18 :         if(res>res_) info = 1
     577         [ +  - ]:          18 :         if(res<=tol) info = 0
     578         [ -  + ]:          18 :          if(done)     return
     579                 :             :         !if(del>0d0)  print*, 'gmres: warning! restart affects hookstep'
     580         [ #  # ]:           0 :         if(del>0d0) call wrtout(std_out, 'gmres: warning! restart affects hookstep')
     581                 :             :          goto 1       ! (j==m) restart
     582                 :             :       end if
     583                 :          72 :       res_ = res*stgn
     584                 :             : 
     585                 :             :    end do
     586                 :             : 
     587                 :             :  end subroutine gmresm
     588                 :             : 
     589                 :             : 
     590                 :             : !-----------------------------------------------------------------
     591                 :             : ! replace y with a vector that generates a hookstep
     592                 :             : ! c.f. Viswanath (2008) arXiv:0809.1498
     593                 :             : !-----------------------------------------------------------------
     594                 :           0 :  subroutine hookstep(j,h,m,beta,del, y)
     595                 :             :    implicit none
     596                 :             :    integer,          intent(in)    :: j, m
     597                 :             :    real(dp), intent(in)            :: h(m+1,j), beta
     598                 :             :    real(dp), intent(inout)         :: del
     599                 :             :    real(dp), intent(out)           :: y(j)
     600                 :           0 :    real(dp) :: a(j+1,j), s(j), u(j+1,j+1), vt(j,j), work(5*(j+1))
     601                 :           0 :    real(dp) :: p(j+1), q(j), mu, qn
     602                 :             :    integer :: info
     603                 :             : 
     604   [ #  #  #  # ]:           0 :    a = h(1:j+1,1:j)
     605                 :             : 
     606                 :           0 :    call dgesvd('A','A',j+1,j,a,j+1,s,u,j+1,vt,j,work,5*(j+1),info)
     607         [ #  # ]:           0 :    if(info/=0) stop 'hookstep: dgesvd'
     608                 :             : 
     609         [ #  # ]:           0 :    p(1:j) = beta * u(1,1:j)
     610                 :             : 
     611                 :           0 :    mu = max(s(j)*s(j)*1d-6,1d-99)
     612                 :           0 :    qn = 1d99
     613         [ #  # ]:           0 :    do while(qn>del)
     614                 :           0 :       mu = mu * 1.1d0
     615         [ #  # ]:           0 :       q = p(1:j)*s/(mu+s*s)
     616         [ #  # ]:           0 :       qn = dsqrt(dot_product(q,q))
     617                 :             :    end do
     618                 :             : 
     619   [ #  #  #  #  :           0 :    y = matmul(q,vt)
             #  #  #  # ]
     620                 :             : 
     621   [ #  #  #  #  :           0 :    p = - matmul(h(1:j+1,1:j),y(1:j))
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                      # ]
     622                 :           0 :    p(1) = p(1) + beta
     623         [ #  # ]:           0 :    del = dsqrt(dot_product(p,p))
     624                 :             : 
     625                 :           0 :  end subroutine hookstep
     626                 :             : 
     627   [ -  +  -  - ]:          90 : end module m_iterative_solvers
     628                 :             : !!***
        

Generated by: LCOV version 2.3-1