LCOV - code coverage report
Current view: top level - shared/common/src/28_numeric_noabirule - m_slk.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 993 0
Test Date: 2026-09-21 13:49:52 Functions: 0.0 % 91 0

            Line data    Source code
       1              : !!****m* ABINIT/m_slk
       2              : !! NAME
       3              : !! m_slk
       4              : !!
       5              : !! FUNCTION
       6              : !! High-level objects and wrappers around the ScaLAPACK and ELPA API.
       7              : !!
       8              : !! COPYRIGHT
       9              : !! Copyright (C) 2004-2026 ABINIT group (CS,GZ,FB,MG,MT)
      10              : !! This file is distributed under the terms of the
      11              : !! GNU General Public License, see ~abinit/COPYING
      12              : !! or http://www.gnu.org/copyleft/gpl.txt .
      13              : !!
      14              : !! TODO
      15              : !! Provide fallbacks for sequential version (No scalapack) so that we can implement generic high-level algorithms.
      16              : !!
      17              : !! SOURCE
      18              : 
      19              : #if defined HAVE_CONFIG_H
      20              : #include "config.h"
      21              : #endif
      22              : 
      23              : #include "abi_common.h"
      24              : 
      25              : module m_slk
      26              : 
      27              :  use, intrinsic :: iso_c_binding
      28              :  use defs_basis
      29              :  USE_MPI
      30              :  use m_xmpi
      31              :  use m_xomp
      32              :  use m_errors
      33              :  use m_abicore
      34              :  use m_gputk
      35              : #ifdef HAVE_LINALG_ELPA
      36              :  use m_elpa
      37              : #endif
      38              : 
      39              :  use m_fstrings,      only : firstchar, toupper, itoa, sjoin, ltoa, string_in
      40              :  use m_time,          only : cwtime, cwtime_report
      41              :  !use m_numeric_tools, only : blocked_loop !, print_arr
      42              : 
      43              :  implicit none
      44              : 
      45              : #ifdef HAVE_MPI1
      46              :  include 'mpif.h'
      47              : #endif
      48              : 
      49              :  private
      50              : 
      51              :  ! scaLAPACK array descriptor.
      52              :  integer,private,parameter :: DLEN_ = 9    ! length
      53              :  integer,private,parameter :: Dtype_ = 1   ! type
      54              :  integer,private,parameter :: CTXT_ = 2    ! BLACS context
      55              :  integer,private,parameter :: M_ = 3       ! nb global lines
      56              :  integer,private,parameter :: N_ = 4       ! nb global columns
      57              :  integer,private,parameter :: MB_ = 5      ! nb lines of a block
      58              :  integer,private,parameter :: NB_ = 6      ! nb columns of a block
      59              :  integer,private,parameter :: RSRC_ = 7    ! line of processors at the beginning
      60              :  integer,private,parameter :: CSRC_ = 8    ! column of processors at the beginning
      61              :  integer,private,parameter :: LLD_ = 9     ! local number of lines
      62              : !!***
      63              : 
      64              : !----------------------------------------------------------------------
      65              : 
      66              : !!****t* m_slk/slk_grid_t
      67              : !! NAME
      68              : !!  slk_grid_t
      69              : !!
      70              : !! FUNCTION
      71              : !!  Grid of ScaLAPACK processors.
      72              : !!
      73              : !! SOURCE
      74              : 
      75              :  type,public :: slk_grid_t
      76              : 
      77              :    integer :: nprocs = -1
      78              :    ! Total number of processors
      79              : 
      80              :    integer :: dims(2) = -1
      81              :    ! Number of procs for rows/columns
      82              : 
      83              :    integer :: comm = xmpi_comm_null
      84              :    ! BLACS context i.e. MPI communicator.
      85              : 
      86              :    logical :: use_gpu = .false.
      87              :    ! Wether GPU is used, relevant for determining matrix block size.
      88              : 
      89              :  contains
      90              :    procedure :: init =>  slk_grid_init  ! Set up the processor grid for ScaLAPACK.
      91              :  end type slk_grid_t
      92              : !!***
      93              : 
      94              : !----------------------------------------------------------------------
      95              : 
      96              : !!****t* m_slk/slk_processor_t
      97              : !! NAME
      98              : !!  slk_processor_t
      99              : !!
     100              : !! FUNCTION
     101              : !!  One processor in the grid.
     102              : !!
     103              : !! SOURCE
     104              : 
     105              :  type,public :: slk_processor_t
     106              : 
     107              :    integer :: my_rank = -1
     108              :    ! rank of the processor in comm
     109              : 
     110              :    integer :: comm = xmpi_comm_null
     111              :    ! MPI communicator underlying the BLACS grid.
     112              : 
     113              :    integer :: coords(2) = -1
     114              :    ! Coordinates of the processor in the grid.
     115              : 
     116              :    type(slk_grid_t) :: grid
     117              :    ! the grid to which the processor is associated to.
     118              : 
     119              :  contains
     120              :    procedure :: init => slk_processor_init     ! Initializes an instance of processor ScaLAPACK from a MPI communicator.
     121              :    procedure :: free => slk_processor_free     ! Free the object
     122              :  end type slk_processor_t
     123              : !!***
     124              : 
     125              : !----------------------------------------------------------------------
     126              : 
     127              : !!****t* m_slk/basemat_t
     128              : !! NAME
     129              : !!  basemat_t
     130              : !!
     131              : !! FUNCTION
     132              : !!  Base class for scalapack matrices.
     133              : !!  Methods that are agnostic about type and precision or methods that can handle both
     134              : !!  single and double precision, real and complex arrays via `select type` should be defined here.
     135              : !!
     136              : !! SOURCE
     137              : 
     138              :  type, private :: basemat_t
     139              : 
     140              :    integer :: size_local(2) = -1
     141              :    ! dimensions of the local buffer.
     142              : 
     143              :    integer(c_size_t) :: bufsize = -1
     144              :    ! Size of the local buffer.
     145              : 
     146              :    integer :: size_global(2) = -1
     147              :    ! dimensions of the global matrix.
     148              : 
     149              :    integer :: size_blocs(2) = -1
     150              :    ! size of the block of consecutive data
     151              : 
     152              :    integer :: istwf_k = -1
     153              : 
     154              :    type(slk_processor_t),pointer :: processor => null()
     155              : 
     156              :    integer :: desc(DLEN_)
     157              :    ! Scalapack descriptor.
     158              : 
     159              :  contains
     160              : 
     161              :    procedure :: init => basemat_init
     162              :     ! Basic Constructor
     163              : 
     164              :    procedure :: glob2loc => basemat_glob2loc
     165              :     ! Determine the local indices of an element from its global indices and return haveit bool flag.
     166              : 
     167              :    procedure :: loc2glob => basemat_loc2glob
     168              :     ! Return global indices of a matrix element from the local indices.
     169              : 
     170              :    procedure :: loc2grow => basemat_loc2grow
     171              :     ! Determine the global row index from the local index
     172              : 
     173              :    procedure :: loc2gcol => basemat_loc2col
     174              :     ! Determine the global column index from the local index
     175              : 
     176              :    procedure :: idx_loc => basemat_idx_loc
     177              :     ! Local indices of an entry
     178              :     ! from its global indices, independently of the processor.
     179              : 
     180              :    procedure :: locmem_mb => basemat_locmem_mb
     181              :     ! Return memory allocated for the local buffer in Mb.
     182              : 
     183              :    procedure :: print => basemat_print
     184              :     ! Print info on the object.
     185              : 
     186              :    procedure :: check_local_shape => basemat_check_local_shape
     187              :    !  Debugging tool to test the local shape `lshape` of the local buffer.
     188              : 
     189              :    procedure :: is_gpu_mapped => basemat_is_gpu_mapped
     190              :    !  True if the local buffer is mapped to the GPU.
     191              : 
     192              :    procedure :: gpu_map => basemat_gpu_map
     193              :    ! Use Opemp to allocate/delete the local buffer on the GPU.
     194              : 
     195              :    procedure :: gpu_set_zero => basemat_gpu_set_zero
     196              :    ! Fill the local buffer on the GPU with zeros.
     197              : 
     198              :    procedure :: free => basemat_free
     199              :     ! Free memory
     200              : 
     201              :    procedure :: change_size_blocs => basemat_change_size_blocs
     202              :     ! Change the block sizes, return new object.
     203              : 
     204              :    procedure :: get_trace => basemat_get_trace
     205              :     ! Compute the trace of an N-by-N distributed matrix.
     206              : 
     207              :    procedure :: set_imag_diago_to_zero => basemat_set_imag_diago_to_zero
     208              :     ! Set the imaginary part of the diagonal to zero.
     209              : 
     210              :    procedure :: invert => basemat_invert
     211              :     ! Inverse of a complex matrix.
     212              : 
     213              :  end type basemat_t
     214              : !!***
     215              : 
     216              : !----------------------------------------------------------------------
     217              : 
     218              : !!****t* m_slk/slkmat_dp_t
     219              : !! NAME
     220              : !!  slkmat_dp_t
     221              : !!
     222              : !! FUNCTION
     223              : !!  high-level interface to ScaLAPACK matrix (double precision version)
     224              : !!
     225              : !! SOURCE
     226              : 
     227              :  type, public, extends(basemat_t) :: slkmat_dp_t
     228              : 
     229              :    real(dp),allocatable :: buffer_real(:,:)
     230              :     ! local part of the (real) matrix.
     231              :     ! The istwf_k option passed to the constructor defines whether we have a real or complex matrix
     232              : 
     233              :    complex(dpc),allocatable :: buffer_cplx(:,:)
     234              :     ! local part of the (complex) matrix
     235              : 
     236              :  contains
     237              : 
     238              :    procedure :: get_head_and_wings => slkmat_dp_get_head_and_wings
     239              :     ! Return global arrays with the head and the wings of the matrix.
     240              : 
     241              :    procedure :: set_head_and_wings => slkmat_dp_set_head_and_wings
     242              :     ! Set head and the wings of the matrix starting from global arrays.
     243              : 
     244              :    procedure :: copy => slkmat_dp_copy
     245              :     ! Copy object
     246              : 
     247              :    procedure :: hpd_invert => slkmat_dp_hpd_invert
     248              :     ! Inverse of a Hermitian positive definite matrix.
     249              : 
     250              :    procedure :: ptrans => slkmat_dp_ptrans
     251              :     ! Transpose matrix
     252              : 
     253              :    procedure :: cut => slkmat_dp_cut
     254              :     ! Extract submatrix and create new matrix with `size_blocs` and `processor`
     255              : 
     256              :    procedure :: take_from => slkmat_dp_take_from
     257              :     ! Take values from source
     258              : 
     259              :    procedure :: collect_cplx => slkmat_dp_collect_cplx
     260              :     ! Return on all processors the submatrix of shape (mm, nn) starting at position ija.
     261              : 
     262              :    procedure :: heev => slkmat_dp_heev
     263              :     ! Compute eigenvalues and, optionally, eigenvectors of an Hermitian matrix A. A * X = lambda * X
     264              : 
     265              :    procedure :: pzheevx => slkmat_dp_pzheevx
     266              :     ! Compute Eigenvalues and, optionally, eigenvectors of a complex Hermitian matrix A. ! A * X = lambda *  X
     267              : 
     268              :    procedure :: pzhegvx => slkmat_dp_pzhegvx
     269              :     ! Eigenvalues and, optionally, eigenvectors of a complex
     270              :     ! generalized Hermitian-definite eigenproblem, of the form
     271              :     ! sub( A )*x=(lambda)*sub( B )*x,  sub( A )*sub( B )x=(lambda)*x,
     272              :     ! or sub( B )*sub( A )*x=(lambda)*x.
     273              : 
     274              :    procedure :: symmetrize => slkmat_dp_symmetrize
     275              :     ! Symmetrizes a square scaLAPACK matrix.
     276              : 
     277              :    procedure :: bsize_and_type => slkmat_dp_bsize_and_type
     278              :     ! Returns the byte size and the MPI datatype
     279              : 
     280              :    procedure :: svd => slkmat_dp_svd
     281              :     ! Singular Value Decomposition (double precision version).
     282              : 
     283              :    procedure :: to_global => slkmat_dp_to_global
     284              :     ! Fill a full matrix with respect to a SCALAPACK matrix.
     285              : 
     286              :    procedure :: from_real_glob => slkmat_dp_from_real_glob
     287              :     ! Fills SCALAPACK matrix from full matrix.
     288              : 
     289              :    procedure :: from_global_pack => slkmat_dp_from_global_pack
     290              :     ! Fills SCALAPACK matrix from full matrix.
     291              : 
     292              :    procedure :: from_global_sym  => slkmat_dp_from_global_sym
     293              :     ! Fills SCALAPACK matrix from full matrix.
     294              : 
     295              :    procedure :: to_global_pack => slkmat_dp_to_global_pack
     296              :     ! Inserts a ScaLAPACK matrix into a global one.
     297              : 
     298              :    procedure :: to_real_glob => slkmat_dp_to_real_glob
     299              :     ! Inserts a ScaLAPACK matrix into a real matrix.
     300              : 
     301              :    procedure :: to_complex_glob => slkmat_dp_to_complex_glob
     302              :    ! Inserts a ScaLAPACK matrix into a complex matrix.
     303              : 
     304              :    procedure :: from_complex_glob => slkmat_dp_from_complex_glob
     305              :     ! Fills SCALAPACK matrix from a full matrix.
     306              : 
     307              :  end type slkmat_dp_t
     308              : !!***
     309              : 
     310              : !!****t* m_slk/slkmat_sp_t
     311              : !! NAME
     312              : !!  slkmat_sp_t
     313              : !!
     314              : !! FUNCTION
     315              : !!  High-level interface to ScaLAPACK matrix (single precision version).
     316              : !!
     317              : !! SOURCE
     318              : 
     319              :  type, public, extends(basemat_t) :: slkmat_sp_t
     320              : 
     321              :    real(sp),allocatable :: buffer_real(:,:)
     322              :     ! local part of the (real) matrix.
     323              :     ! The istwf_k option passed to the constructor defines whether we have a real or complex matrix
     324              : 
     325              :    complex(sp),allocatable :: buffer_cplx(:,:)
     326              :     ! local part of the (complex) matrix
     327              : 
     328              :  contains
     329              : 
     330              :    procedure :: copy => slkmat_sp_copy
     331              :     ! Copy object
     332              : 
     333              :    procedure :: take_from => slkmat_sp_take_from
     334              :     ! Take values from source
     335              : 
     336              :    procedure :: ptrans => slkmat_sp_ptrans
     337              :     ! Transpose matrix
     338              : 
     339              :    procedure :: set_head_and_wings => slkmat_sp_set_head_and_wings
     340              :     ! Set head and the wings of the matrix starting from global arrays.
     341              : 
     342              :    procedure :: cut => slkmat_sp_cut
     343              :     ! Extract submatrix and create new matrix with `size_blocs` and `processor`
     344              : 
     345              :    procedure :: collect_cplx => slkmat_sp_collect_cplx
     346              :     ! Return on all processors the submatrix of shape (mm, nn) starting at position ija.
     347              : 
     348              :    procedure :: heev => slkmat_sp_heev
     349              :     ! Compute eigenvalues and, optionally, eigenvectors of an Hermitian matrix A. A * X = lambda * X
     350              : 
     351              :    procedure :: hpd_invert => slkmat_sp_hpd_invert
     352              :     ! Inverse of a Hermitian positive definite matrix.
     353              : 
     354              :    procedure :: svd => slkmat_sp_svd
     355              :     ! Singular Value Decomposition (single precision version).
     356              : 
     357              :  end type slkmat_sp_t
     358              : !!***
     359              : 
     360              :  public :: block_dist_1d                   ! Return block size for one-dimensional block column/row distribution
     361              :  public :: slk_has_elpa                    ! Return True if ELPA support is activated
     362              : 
     363              :  ! ???
     364              :  public :: matrix_get_local_cplx           ! Return a local matrix coefficient of complex type.
     365              :  public :: matrix_get_local_real           ! Return a local matrix coefficient of double precision type.
     366              :  public :: matrix_set_local_cplx           ! Sets a local matrix coefficient of complex type.
     367              :  public :: matrix_set_local_real           ! Sets a local matrix coefficient of double precision type.
     368              :  ! ???
     369              : 
     370              :  public :: slk_matrix_from_global_dpc_2D   ! Fill a complex SCALAPACK matrix with respect to a global matrix.
     371              :  public :: slk_matrix_from_global_dpc_1Dp  ! Fill a complex SCALAPACK matrix with respect to a global matrix.
     372              :                                            ! target: double precision complex matrix in packed form.
     373              :  public :: slk_matrix_to_global_dpc_2D     ! Fill a global matrix with respect to a SCALAPACK matrix.
     374              :                                            ! target: Two-dimensional Double precision complex matrix.
     375              : 
     376              :  public :: slk_pgemm                       ! Compute: C := alpha*A*B + beta*C
     377              :  interface slk_pgemm
     378              :    module procedure slk_pgemm_dp
     379              :    module procedure slk_pgemm_sp
     380              :  end interface slk_pgemm
     381              : 
     382              : 
     383              :  public :: compute_eigen_problem             ! Compute eigenvalues and eigenvectors of: A * X = lambda * X.
     384              :                                              ! complex and real cases.
     385              :  public :: compute_generalized_eigen_problem ! Compute generalized eigenvalue problem
     386              :  public :: compute_eigen1                    ! Compute eigenvalues and eigenvectors.  complex and real cases.
     387              :  public :: compute_eigen2                    ! Compute eigenvalues and eigenvectors: A * X = lambda * B * X
     388              :                                              ! complex and real cases.
     389              : 
     390              :  public :: slk_write                         ! Writes a square scaLAPACK distributed matrix on an external file using MPI-IO.
     391              :  public :: slk_read                          ! Read a square scaLAPACK distributed matrix from an external file using MPI-IO.
     392              :  public :: slk_single_fview_read_mask        ! Returns an MPI datatype that can be used to read a scaLAPACK matrix from
     393              :                                              ! a binary file using MPI-IO.
     394              :                                              ! The view is created using the user-defined mask function
     395              :  public :: slk_single_fview_read             ! Returns an MPI datatype to read a scaLAPACK distributed matrix
     396              :                                              ! from a binary file using MPI-IO.
     397              :  public :: slk_single_fview_write            ! Returns an MPI datatype to write a scaLAPACK distributed matrix
     398              :                                              ! to a binary file using MPI-IO.
     399              : 
     400              :  public :: slk_array_free                    !  Deallocate array of slkmat_dp_t elements
     401              :  interface slk_array_free
     402              :    module procedure slk_array1_free
     403              :    module procedure slk_array2_free
     404              :    module procedure slk_array3_free
     405              :    module procedure slk_array4_free
     406              :    module procedure slk_array5_free
     407              :  end interface slk_array_free
     408              : 
     409              :  public :: slk_array_set_zero                  ! Elemental routine to zero the value of the local buffer.
     410              :  public :: slk_array_locmem_mb                 ! Compute memory allocated for an array of slkmat_dp_t elements
     411              : 
     412              :  public :: slk_array_gpu_set_zero              ! Zero the value of the local buffer on the GPU
     413              :  interface slk_array_gpu_set_zero
     414              :    module procedure slk_array1_gpu_set_zero
     415              :    module procedure slk_array2_gpu_set_zero
     416              :    module procedure slk_array3_gpu_set_zero
     417              :    module procedure slk_array4_gpu_set_zero
     418              :    module procedure slk_array5_gpu_set_zero
     419              :  end interface slk_array_gpu_set_zero
     420              : 
     421              :  ! External functions.
     422              : #if defined(HAVE_LINALG_SCALAPACK) || defined(HAVE_LINALG_ELPA)
     423              :  integer,external :: indxl2g, numroc
     424              :  real(dp),external :: PDLAMCH
     425              :  real(dp),external :: PDLATRA
     426              :  real(sp),external :: PSLATRA
     427              :  complex(sp),external :: PCLATRA
     428              :  complex(dp),external :: PZLATRA
     429              : #endif
     430              : 
     431              : #ifdef HAVE_LINALG_SLATE
     432              : 
     433              : interface
     434              :   ! Standard complex Hermitian: A * Z = Z * diag(w)
     435              :   subroutine slate_zheev_c(n, nb, nprow, npcol, comm_f, &
     436              :                             lda, a_data, ldz, z_data, w, nev, use_gpu, info) &
     437              :       bind(C, name="slate_zheev_c")
     438              :     use, intrinsic :: iso_c_binding
     439              :     integer(c_int), value :: n, nb, nprow, npcol, comm_f, lda, ldz, nev, use_gpu
     440              :     complex(c_double_complex), intent(inout) :: a_data(*)
     441              :     complex(c_double_complex), intent(out)   :: z_data(*)
     442              :     real(c_double),            intent(out)   :: w(*)
     443              :     integer(c_int),            intent(out)   :: info
     444              :   end subroutine slate_zheev_c
     445              : 
     446              :   ! Standard real symmetric: A * Z = Z * diag(w)
     447              :   subroutine slate_dsyev_c(n, nb, nprow, npcol, comm_f, &
     448              :                             lda, a_data, ldz, z_data, w, nev, use_gpu, info) &
     449              :       bind(C, name="slate_dsyev_c")
     450              :     use, intrinsic :: iso_c_binding
     451              :     integer(c_int), value :: n, nb, nprow, npcol, comm_f, lda, ldz, nev, use_gpu
     452              :     real(c_double), intent(inout) :: a_data(*)
     453              :     real(c_double), intent(out)   :: z_data(*)
     454              :     real(c_double), intent(out)   :: w(*)
     455              :     integer(c_int), intent(out)   :: info
     456              :   end subroutine slate_dsyev_c
     457              : 
     458              :   ! Generalised complex Hermitian: A * Z = B * Z * diag(w)
     459              :   subroutine slate_zhegv_c(n, nb, nprow, npcol, comm_f, &
     460              :                             lda, a_data, ldb, b_data, ldz, z_data, &
     461              :                             w, nev, use_gpu, info) &
     462              :       bind(C, name="slate_zhegv_c")
     463              :     use, intrinsic :: iso_c_binding
     464              :     integer(c_int), value :: n, nb, nprow, npcol, comm_f
     465              :     integer(c_int), value :: lda, ldb, ldz, nev, use_gpu
     466              :     complex(c_double_complex), intent(inout) :: a_data(*), b_data(*)
     467              :     complex(c_double_complex), intent(out)   :: z_data(*)
     468              :     real(c_double),            intent(out)   :: w(*)
     469              :     integer(c_int),            intent(out)   :: info
     470              :   end subroutine slate_zhegv_c
     471              : 
     472              :   ! Generalised real symmetric: A * Z = B * Z * diag(w)
     473              :   subroutine slate_dsygv_c(n, nb, nprow, npcol, comm_f, &
     474              :                             lda, a_data, ldb, b_data, ldz, z_data, &
     475              :                             w, nev, use_gpu, info) &
     476              :       bind(C, name="slate_dsygv_c")
     477              :     use, intrinsic :: iso_c_binding
     478              :     integer(c_int), value :: n, nb, nprow, npcol, comm_f
     479              :     integer(c_int), value :: lda, ldb, ldz, nev, use_gpu
     480              :     real(c_double), intent(inout) :: a_data(*), b_data(*)
     481              :     real(c_double), intent(out)   :: z_data(*)
     482              :     real(c_double), intent(out)   :: w(*)
     483              :     integer(c_int), intent(out)   :: info
     484              :   end subroutine slate_dsygv_c
     485              : 
     486              : end interface
     487              : 
     488              : #endif
     489              : 
     490              : contains  !==============================================================================
     491              : !!***
     492              : 
     493              : !!****f* m_slk/slk_grid_init
     494              : !! NAME
     495              : !!  slk_grid_init
     496              : !!
     497              : !! FUNCTION
     498              : !!  Set up the ScaLAPACK grid given the total number of processors.
     499              : !!
     500              : !! INPUTS
     501              : !!  nprocs= total number of processors
     502              : !!  comm= MPI communicator
     503              : !!  [grid_dims]=Number of procs for each dimension.
     504              : !!
     505              : !! OUTPUT
     506              : !!  grid= the grid of processors used by Scalapack
     507              : !!
     508              : !! SOURCE
     509              : 
     510            0 : subroutine slk_grid_init(grid, nprocs, comm, use_gpu, grid_dims)
     511              : 
     512              : !Arguments ------------------------------------
     513              :  class(slk_grid_t),intent(out) :: grid
     514              :  integer,intent(in) :: nprocs,comm
     515              :  logical,intent(in) :: use_gpu
     516              :  integer,optional,intent(in) :: grid_dims(2)
     517              : 
     518              : !Local variables-------------------------------
     519              :  integer :: i
     520              : ! *********************************************************************
     521              : 
     522            0 :  grid%nprocs = nprocs
     523              : 
     524            0 :  if (.not. present(grid_dims)) then
     525              :    ! Search for a rectangular grid of processors
     526            0 :    i=INT(SQRT(float(nprocs)))
     527            0 :    do while (MOD(nprocs,i) /= 0)
     528            0 :      i = i-1
     529              :    end do
     530            0 :    i=max(i,1)
     531              : 
     532            0 :    grid%dims(1) = i
     533            0 :    grid%dims(2) = INT(nprocs/i)
     534              : 
     535              :  else
     536            0 :    grid%dims = grid_dims
     537              :  end if
     538              : 
     539            0 :  ABI_CHECK(product(grid%dims) == nprocs, sjoin("grid%dims:", ltoa(grid%dims), "does not agree with nprocs:", itoa(nprocs)))
     540              : 
     541            0 :  grid%comm = comm
     542            0 :  grid%use_gpu = use_gpu
     543              : 
     544              : #ifdef HAVE_LINALG_SCALAPACK
     545              :  ! 'R': Use row-major natural ordering
     546              :  call BLACS_GRIDINIT(grid%comm, 'R', grid%dims(1), grid%dims(2))
     547              : #endif
     548              : 
     549            0 : end subroutine slk_grid_init
     550              : !!***
     551              : 
     552              : !----------------------------------------------------------------------
     553              : 
     554              : !!****f* m_slk/build_processor
     555              : !! NAME
     556              : !!  build_processor
     557              : !!
     558              : !! FUNCTION
     559              : !!  Builds a ScaLAPACK processor descriptor.
     560              : !!  Build of the data related to one processor in a grid
     561              : !!
     562              : !! INPUTS
     563              : !!  grid= array representing the grid of processors.
     564              : !!  my_rank= selected processor
     565              : !!  comm= MPI communicator
     566              : !!
     567              : !! OUTPUT
     568              : !!  processor= descriptor of a processor
     569              : !!
     570              : !! SOURCE
     571              : 
     572            0 : subroutine build_processor(processor, grid, my_rank, comm)
     573              : 
     574              : !Arguments ------------------------------------
     575              :  class(slk_processor_t),intent(inout) :: processor
     576              :  class(slk_grid_t),intent(in) :: grid
     577              :  integer,intent(in) :: my_rank, comm
     578              : ! *********************************************************************
     579              : 
     580            0 :  processor%grid = grid
     581            0 :  processor%my_rank = my_rank
     582            0 :  processor%comm = comm
     583              : 
     584              : #ifdef HAVE_LINALG_SCALAPACK
     585              :  call BLACS_GRIDINFO(grid%comm, processor%grid%dims(1), processor%grid%dims(2), &
     586              :                      processor%coords(1), processor%coords(2))
     587              : #endif
     588              : 
     589              :  ! These values are the same as those computed by BLACS_GRIDINFO
     590              :  ! except in the case where the my_rank argument is not the local proc
     591            0 :  processor%coords(1) = INT((my_rank) / grid%dims(2))
     592            0 :  processor%coords(2) = MOD((my_rank), grid%dims(2))
     593              : 
     594              : end subroutine build_processor
     595              : !!***
     596              : 
     597              : !----------------------------------------------------------------------
     598              : 
     599              : !!****f* m_slk/slk_processor_init
     600              : !! NAME
     601              : !!  slk_processor_init
     602              : !!
     603              : !! FUNCTION
     604              : !!  Initializes an instance of processor ScaLAPACK from an MPI communicator.
     605              : !!
     606              : !! INPUTS
     607              : !!  comm= MPI communicator
     608              : !!  [grid_dims]=Number of procs for each dimension.
     609              : !!
     610              : !! OUTPUT
     611              : !!  processor= descriptor of a processor
     612              : !!
     613              : !! SOURCE
     614              : 
     615            0 : subroutine slk_processor_init(processor, comm, &
     616              :                               grid_dims) ! optional
     617              : 
     618              : !Arguments ------------------------------------
     619              :  class(slk_processor_t),intent(out) :: processor
     620              :  integer, intent(in) :: comm
     621              :  integer,optional,intent(in) :: grid_dims(2)
     622              : 
     623              : !Local variables-------------------------------
     624              :  type(slk_grid_t) :: grid
     625              :  integer :: nprocs, my_rank
     626              : ! *********************************************************************
     627              : 
     628            0 :  nprocs = xmpi_comm_size(comm)
     629            0 :  my_rank = xmpi_comm_rank(comm)
     630              : 
     631            0 :  if (present(grid_dims)) then
     632            0 :    call grid%init(nprocs, comm, .false., grid_dims=grid_dims)
     633              :  else
     634            0 :    call grid%init(nprocs, comm, .false.)
     635              :  end if
     636              : 
     637            0 :  call build_processor(processor, grid, my_rank, comm)
     638              : 
     639            0 : end subroutine slk_processor_init
     640              : !!***
     641              : 
     642              : !----------------------------------------------------------------------
     643              : 
     644              : !!****f* m_slk/slk_processor_free
     645              : !! NAME
     646              : !!  slk_processor_free
     647              : !!
     648              : !! FUNCTION
     649              : !!  Removes a processor from the ScaLAPACK grid.
     650              : !!
     651              : !! SOURCE
     652              : 
     653            0 : subroutine slk_processor_free(processor)
     654              : 
     655              : !Arguments ------------------------------------
     656              :  class(slk_processor_t),intent(inout) :: processor
     657              : ! *********************************************************************
     658              : 
     659              : #ifdef HAVE_LINALG_SCALAPACK
     660              :  if (processor%grid%comm /= xmpi_comm_null) then
     661              :    call BLACS_GRIDEXIT(processor%grid%comm)
     662              :    !call BLACS_EXIT(0)
     663              :  end if
     664              : #endif
     665              : 
     666            0 : end subroutine slk_processor_free
     667              : !!***
     668              : 
     669              : !----------------------------------------------------------------------
     670              : 
     671              : !!****f* m_slk/basemat_init
     672              : !! NAME
     673              : !!  basemat_init
     674              : !!
     675              : !! FUNCTION
     676              : !!  Initialisation of a SCALAPACK matrix (each proc initializes its own part of the matrix)
     677              : !!
     678              : !! INPUTS
     679              : !!  processor= descriptor of a processor
     680              : !!  nbli_global= total number of lines
     681              : !!  nbco_global= total number of columns
     682              : !!  istwf_k= 2 if we have a real matrix else complex.
     683              : !!  [size_blocs]= custom block sizes. Use -1 to use global size along that direction.
     684              : !!    Useful to distribute only rows or columns. Obviously, [-1, -1] is not allowed.
     685              : !!
     686              : !! OUTPUT
     687              : !!  matrix= the matrix to process
     688              : !!
     689              : !! SOURCE
     690              : 
     691            0 : subroutine basemat_init(matrix, nbli_global, nbco_global, processor, istwf_k, &
     692              :                         size_blocs, gpu_action) ! optional
     693              : 
     694              : !Arguments ------------------------------------
     695              :  class(basemat_t),intent(inout) :: matrix
     696              :  integer,intent(in) :: nbli_global, nbco_global, istwf_k
     697              :  type(slk_processor_t),target,intent(in) :: processor
     698              :  integer,optional,intent(in) :: size_blocs(2)
     699              :  character(len=*),optional,intent(in) :: gpu_action
     700              : 
     701              : #ifdef HAVE_LINALG_SCALAPACK
     702              : !Local variables-------------------------------
     703              : #ifdef HAVE_LINALG_ELPA
     704              :  integer, parameter :: DEFAULT_SIZE_BLOCS = 1
     705              : #else
     706              :  ! As recommended by Intel MKL, a more sensible default than the previous value of 40
     707              :  integer, parameter :: DEFAULT_SIZE_BLOCS = 24
     708              : #endif
     709              :  ! As recommended in ELPA, which advises distributions as squared as possible using powers of 2
     710              :  integer, parameter :: DEFAULT_SIZE_BLOCS_GPU = 16
     711              :  integer :: info,sizeb
     712              :  !character(len=500) :: msg
     713              : ! *********************************************************************
     714              : 
     715              :  call matrix%free()
     716              : 
     717              :  sizeb = DEFAULT_SIZE_BLOCS
     718              :  if (processor%grid%use_gpu) sizeb = DEFAULT_SIZE_BLOCS_GPU
     719              : 
     720              :  !Records of the matrix type
     721              :  matrix%processor => processor
     722              :  matrix%size_blocs(1) = MIN(sizeb, nbli_global)
     723              :  matrix%size_blocs(2) = MIN(sizeb, nbco_global)
     724              : 
     725              : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
     726              :  if(matrix%size_blocs(1) .ne. matrix%size_blocs(2)) then
     727              :     matrix%size_blocs(1) = MIN(matrix%size_blocs(1), matrix%size_blocs(2))
     728              :     matrix%size_blocs(2) = matrix%size_blocs(1)
     729              :  end if
     730              : #endif
     731              : 
     732              :  ! Use custom block sizes.
     733              :  if (present(size_blocs)) then
     734              :    ABI_CHECK(.not. all(size_blocs == -1), "size_blocs [-1, -1]  is not allowed")
     735              :    if (size_blocs(1) == -1) then
     736              :      matrix%size_blocs(1) = nbli_global
     737              :    else
     738              :      matrix%size_blocs(1) = MIN(size_blocs(1), nbli_global)
     739              :    end if
     740              :    if (size_blocs(2) == -1) then
     741              :      matrix%size_blocs(2) = nbco_global
     742              :    else
     743              :      matrix%size_blocs(2) = MIN(size_blocs(2), nbco_global)
     744              :    end if
     745              :  end if
     746              : 
     747              :  matrix%size_global(1) = nbli_global
     748              :  matrix%size_global(2) = nbco_global
     749              :  !write(std_out, *)"matrix%size_global", matrix%size_global
     750              : 
     751              :  ! Size of the local buffer
     752              :  ! NUMROC computes the NUMber of Rows Or Columns of a distributed matrix owned by the process indicated by IPROC.
     753              :  ! NUMROC (n, nb, iproc, isrcproc, nprocs)
     754              :  matrix%size_local(1) = NUMROC(nbli_global, matrix%size_blocs(1), &
     755              :                                processor%coords(1), 0, processor%grid%dims(1))
     756              : 
     757              :  matrix%size_local(2) = NUMROC(nbco_global,matrix%size_blocs(2), &
     758              :                                processor%coords(2), 0, processor%grid%dims(2))
     759              : 
     760              :  matrix%bufsize = int(matrix%size_local(1), c_size_t) * int(matrix%size_local(2), c_size_t)
     761              : 
     762              :  call matrix%idx_loc(matrix%size_global(1), matrix%size_global(2), &
     763              :                      matrix%size_local(1), matrix%size_local(2))
     764              : 
     765              :  ! Initialisation of the SCALAPACK description of the matrix
     766              :  ! (desc, m, n, mb, nb, irsrc, icsrc, comm, lld, info)
     767              :  call DESCINIT(matrix%desc, nbli_global, nbco_global, &
     768              :                matrix%size_blocs(1), matrix%size_blocs(2), 0, 0, &
     769              :                processor%grid%comm, MAX(1, matrix%size_local(1)), info)
     770              : 
     771              :  if (info /= 0) then
     772              :    ABI_ERROR(sjoin("Error while initializing scalapack matrix. info:", itoa(info)))
     773              :  end if
     774              : 
     775              :  ! Allocate local buffer.
     776              :  matrix%istwf_k = istwf_k
     777              :  select type (matrix)
     778              :  class is (slkmat_dp_t)
     779              :    if (istwf_k /= 2) then
     780              :      ABI_MALLOC(matrix%buffer_cplx, (matrix%size_local(1), matrix%size_local(2)))
     781              :      matrix%buffer_cplx = czero
     782              :    else
     783              :      ABI_MALLOC(matrix%buffer_real, (matrix%size_local(1), matrix%size_local(2)))
     784              :      matrix%buffer_real = zero
     785              :    end if
     786              : 
     787              :  class is (slkmat_sp_t)
     788              :    if (istwf_k /= 2) then
     789              :      ABI_MALLOC(matrix%buffer_cplx, (matrix%size_local(1), matrix%size_local(2)))
     790              :      matrix%buffer_cplx = czero_sp
     791              :    else
     792              :      ABI_MALLOC(matrix%buffer_real, (matrix%size_local(1), matrix%size_local(2)))
     793              :      matrix%buffer_real = zero_sp
     794              :    end if
     795              : 
     796              :  class default
     797              :    ABI_ERROR("Wrong class")
     798              :  end select
     799              : 
     800              :  if (present(gpu_action)) call matrix%gpu_map(gpu_action)
     801              : #endif
     802              : 
     803            0 : end subroutine basemat_init
     804              : !!***
     805              : 
     806              : !----------------------------------------------------------------------
     807              : 
     808              : !!****f* m_slk/basemat_locmem_mb
     809              : !! NAME
     810              : !!  basemat_locmem_mb
     811              : !!
     812              : !! FUNCTION
     813              : !!  Returns memory allocated for the local buffer in Mb.
     814              : !!
     815              : !! SOURCE
     816              : 
     817            0 : pure real(dp) function basemat_locmem_mb(mat) result(locmem_mb)
     818              : 
     819              : !Arguments ------------------------------------
     820              :  class(basemat_t),intent(in) :: mat
     821              : ! *********************************************************************
     822              : 
     823            0 :  locmem_mb = zero
     824              :  select type (mat)
     825              :  class is (slkmat_dp_t)
     826            0 :    if (allocated(mat%buffer_real)) locmem_mb = product(int(shape(mat%buffer_real))) * dp
     827            0 :    if (allocated(mat%buffer_cplx)) locmem_mb = product(int(shape(mat%buffer_cplx))) * two * dp
     828              :  class is (slkmat_sp_t)
     829            0 :    if (allocated(mat%buffer_real)) locmem_mb = product(int(shape(mat%buffer_real))) * sp
     830            0 :    if (allocated(mat%buffer_cplx)) locmem_mb = product(int(shape(mat%buffer_cplx))) * two * sp
     831              :  end select
     832            0 :  locmem_mb = locmem_mb * b2Mb
     833              : 
     834            0 : end function basemat_locmem_mb
     835              : !!***
     836              : 
     837              : !----------------------------------------------------------------------
     838              : 
     839              : !!****f* m_slk/basemat_print
     840              : !! NAME
     841              : !!  basemat_print
     842              : !!
     843              : !! FUNCTION
     844              : !!  Print info on scalapack matrix.
     845              : !!
     846              : !! INPUTS
     847              : !!  [unit]=Unit number (default: std_out)
     848              : !!  [header]=title for info
     849              : !!  [prtvol]=Verbosity level (default: 0)
     850              : !!
     851              : !! SOURCE
     852              : 
     853            0 : subroutine basemat_print(mat, header, unit, prtvol)
     854              : 
     855              : !Arguments ------------------------------------
     856              :  class(basemat_t),intent(in) :: mat
     857              :  character(len=*),optional,intent(in) :: header
     858              :  integer,optional,intent(in) :: prtvol, unit
     859              : 
     860              : !Local variables-------------------------------
     861              :  integer :: unt, my_prtvol, grid_dims(2)
     862              :  character(len=50) :: matrix_dtype
     863              :  character(len=5000) :: msg
     864              : ! *********************************************************************
     865              : 
     866            0 :  unt = std_out; if (present(unit)) unt =unit
     867            0 :  my_prtvol = 0; if (present(prtvol)) my_prtvol = prtvol
     868              : 
     869            0 :  msg = ' ==== Info on scalapack matrix ==== '
     870            0 :  if (present(header)) msg=' ==== '//trim(adjustl(header))//' ==== '
     871            0 :  call wrtout(unt, msg)
     872              : 
     873            0 :  matrix_dtype = "undefined"
     874              :  select type (mat)
     875              :  class is (slkmat_dp_t)
     876            0 :    if (allocated(mat%buffer_real)) matrix_dtype = "real dp"
     877            0 :    if (allocated(mat%buffer_cplx)) matrix_dtype = "complex dp"
     878              : 
     879              :  class is (slkmat_sp_t)
     880            0 :    if (allocated(mat%buffer_real)) matrix_dtype = "real sp"
     881            0 :    if (allocated(mat%buffer_cplx)) matrix_dtype = "complex sp"
     882              : 
     883              :  class default
     884            0 :    ABI_ERROR("Wrong class")
     885              :  end select
     886              : 
     887            0 :  grid_dims = [-1, -1]
     888            0 :  if (associated(mat%processor)) grid_dims = mat%processor%grid%dims
     889              : 
     890              :  write(msg,'(5(3a),a,f8.1,a)') &
     891            0 :    "  matrix_dtype ..... ", trim(matrix_dtype), ch10, &
     892            0 :    "  size_global ...... ", trim(ltoa(mat%size_global)), ch10, &
     893            0 :    "  size_local ......  ", trim(ltoa(mat%size_local)), ch10, &
     894            0 :    "  size_blocs ......  ", trim(ltoa(mat%size_blocs)), ch10, &
     895            0 :    "  processor grid ... ", trim(ltoa(grid_dims)), ch10, &
     896            0 :    "  memory (Mb) ...... ", mat%locmem_mb(), ch10
     897            0 :  call wrtout(unt, msg)
     898              : 
     899              :  !if (prtvol > 10) call mat%write(unit)
     900              : 
     901            0 : end subroutine basemat_print
     902              : !!***
     903              : 
     904              : !----------------------------------------------------------------------
     905              : 
     906              : !!****f* m_slk/slkmat_check_shape
     907              : !! NAME
     908              : !!  slkmat_check_shape
     909              : !!
     910              : !! FUNCTION
     911              : !!  Debugging tool to test the local shape `lshape` of the local buffer.
     912              : !!  Return exit status in `ok` and error message in `msg`.
     913              : !!
     914              : !! SOURCE
     915              : 
     916            0 : logical function basemat_check_local_shape(mat, lshape, msg) result (ok)
     917              : 
     918              : !Arguments ------------------------------------
     919              :  class(basemat_t),intent(in) :: mat
     920              :  integer,intent(in) :: lshape(2)
     921              :  character(len=*),intent(out) :: msg
     922              : ! *********************************************************************
     923              : 
     924            0 :  msg = ""
     925            0 :  ok = all(mat%size_local == lshape)
     926            0 :  if (.not. ok) then
     927            0 :    msg = sjoin("mat%size_local:", ltoa(mat%size_local), " not equal to input local lshape ", ltoa(lshape))
     928            0 :    return
     929              :  end if
     930              : 
     931              :  select type (mat)
     932              :  class is (slkmat_dp_t)
     933            0 :    if (allocated(mat%buffer_cplx)) then
     934            0 :      ok = all(shape(mat%buffer_cplx) == lshape)
     935            0 :      if (.not. ok) then
     936            0 :        msg = sjoin("shape(buffer_cplx):", ltoa(shape(mat%buffer_cplx)), " != input local lshape ", ltoa(lshape)); return
     937              :      end if
     938            0 :    else if (allocated(mat%buffer_real)) then
     939            0 :      ok = all(shape(mat%buffer_real) == lshape)
     940            0 :      if (.not. ok) then
     941            0 :        msg = sjoin("shape(buffer_real):", ltoa(shape(mat%buffer_real)), " != input local lshape ", ltoa(lshape)); return
     942              :      end if
     943              :    end if
     944              : 
     945              :  class is (slkmat_sp_t)
     946              :    ! Same piece of code as above. May use include file!
     947            0 :    if (allocated(mat%buffer_cplx)) then
     948            0 :      ok = all(shape(mat%buffer_cplx) == lshape)
     949            0 :      if (.not. ok) then
     950            0 :        msg = sjoin("shape(buffer_cplx):", ltoa(shape(mat%buffer_cplx)), " != input local lshape ", ltoa(lshape)); return
     951              :      end if
     952            0 :    else if (allocated(mat%buffer_real)) then
     953            0 :      ok = all(shape(mat%buffer_real) == lshape)
     954            0 :      if (.not. ok) then
     955            0 :        msg = sjoin("shape(buffer_real):", ltoa(shape(mat%buffer_real)), " != input local lshape ", ltoa(lshape)); return
     956              :      end if
     957              :    end if
     958              : 
     959              :  class default
     960            0 :    ABI_ERROR("Wrong class")
     961              :  end select
     962              : 
     963            0 : end function basemat_check_local_shape
     964              : !!***
     965              : 
     966              : !----------------------------------------------------------------------
     967              : 
     968              : !!****f* m_slk/slkmat_dp_get_head_and_wings
     969              : !! NAME
     970              : !!  slkmat_dp_get_head_and_wings
     971              : !!
     972              : !! FUNCTION
     973              : !!  Return global arrays with head and wings of the matrix.
     974              : !!  If call_mpi if False, global MPI sum is postponed.
     975              : !!  Useful to reduce the number of MPI calls if one has to operate on multiple matrices.
     976              : !!
     977              : !! SOURCE
     978              : 
     979            0 : subroutine slkmat_dp_get_head_and_wings(mat, head, low_wing, up_wing, call_mpi)
     980              : 
     981              : !Arguments ------------------------------------
     982              :  class(slkmat_dp_t),intent(in) :: mat
     983              :  complex(dp),intent(out) :: head, low_wing(mat%size_global(1)), up_wing(mat%size_global(2))
     984              :  logical,intent(in) :: call_mpi
     985              : 
     986              : !Local variables-------------------------------
     987              :  integer :: ierr, il_g1, il_g2, iglob1, iglob2
     988              :  logical :: is_cplx
     989              : ! *********************************************************************
     990              : 
     991            0 :  head = zero; low_wing = zero; up_wing = zero
     992              : 
     993            0 :  is_cplx = allocated(mat%buffer_cplx)
     994              : 
     995            0 :  do il_g2=1,mat%size_local(2)
     996            0 :    iglob2 = mat%loc2gcol(il_g2)
     997            0 :    do il_g1=1,mat%size_local(1)
     998            0 :      iglob1 = mat%loc2grow(il_g1)
     999              : 
    1000            0 :      if (iglob1 == 1 .or. iglob2 == 1) then
    1001            0 :        if (iglob1 == 1 .and. iglob2 == 1) then
    1002            0 :          if (is_cplx) then
    1003            0 :            head = mat%buffer_cplx(il_g1, il_g2)
    1004              :          else
    1005            0 :            head = mat%buffer_real(il_g1, il_g2)
    1006              :          end if
    1007            0 :        else if (iglob1 == 1) then
    1008            0 :          if (is_cplx) then
    1009            0 :            up_wing(iglob2) = mat%buffer_cplx(il_g1, il_g2)
    1010              :          else
    1011            0 :            up_wing(iglob2) = mat%buffer_real(il_g1, il_g2)
    1012              :          end if
    1013            0 :        else if (iglob2 == 1) then
    1014            0 :          if (is_cplx) then
    1015            0 :            low_wing(iglob1) = mat%buffer_cplx(il_g1, il_g2)
    1016              :          else
    1017            0 :            low_wing(iglob1) = mat%buffer_real(il_g1, il_g2)
    1018              :          end if
    1019              :        end if
    1020              :      end if
    1021              : 
    1022              :    end do
    1023              :  end do
    1024              : 
    1025            0 :  if (call_mpi) then
    1026            0 :    call xmpi_sum(head, mat%processor%comm, ierr)
    1027            0 :    call xmpi_sum(low_wing, mat%processor%comm, ierr)
    1028            0 :    call xmpi_sum(up_wing, mat%processor%comm, ierr)
    1029              :  end if
    1030              : 
    1031            0 : end subroutine slkmat_dp_get_head_and_wings
    1032              : !!***
    1033              : 
    1034              : !----------------------------------------------------------------------
    1035              : 
    1036              : !!****f* m_slk/slkmat_dp_set_head_and_wings
    1037              : !! NAME
    1038              : !!  slkmat_dp_set_head_and_wings
    1039              : !!
    1040              : !! FUNCTION
    1041              : !!  Set head and wings of the matrix starting from global arrays.
    1042              : !!
    1043              : !! SOURCE
    1044              : 
    1045            0 : subroutine slkmat_dp_set_head_and_wings(mat, head, low_wing, up_wing)
    1046              : 
    1047              : !Arguments ------------------------------------
    1048              :  class(slkmat_dp_t),intent(inout) :: mat
    1049              :  complex(dp),intent(in) :: head, low_wing(mat%size_global(1)), up_wing(mat%size_global(2))
    1050              : 
    1051              : !Local variables-------------------------------
    1052              :  integer :: il_g1, il_g2, iglob1, iglob2
    1053              :  logical :: is_cplx
    1054              : ! *********************************************************************
    1055              : 
    1056            0 :  is_cplx = allocated(mat%buffer_cplx)
    1057              : 
    1058            0 :  do il_g2=1,mat%size_local(2)
    1059            0 :    iglob2 = mat%loc2gcol(il_g2)
    1060            0 :    do il_g1=1,mat%size_local(1)
    1061            0 :      iglob1 = mat%loc2grow(il_g1)
    1062              : 
    1063            0 :      if (iglob1 == 1 .or. iglob2 == 1) then
    1064            0 :        if (iglob1 == 1 .and. iglob2 == 1) then
    1065            0 :          if (is_cplx) then
    1066            0 :            mat%buffer_cplx(il_g1, il_g2) = head
    1067              :          else
    1068            0 :            mat%buffer_real(il_g1, il_g2) = real(head)
    1069              :          end if
    1070            0 :        else if (iglob1 == 1) then
    1071            0 :          if (is_cplx) then
    1072            0 :            mat%buffer_cplx(il_g1, il_g2) = up_wing(iglob2)
    1073              :          else
    1074            0 :            mat%buffer_real(il_g1, il_g2) = real(up_wing(iglob2))
    1075              :          end if
    1076            0 :        else if (iglob2 == 1) then
    1077            0 :          if (is_cplx) then
    1078            0 :            mat%buffer_cplx(il_g1, il_g2) = low_wing(iglob1)
    1079              :          else
    1080            0 :            mat%buffer_real(il_g1, il_g2) = real(low_wing(iglob1))
    1081              :          end if
    1082              :        end if
    1083              :      end if
    1084              : 
    1085              :    end do
    1086              :  end do
    1087              : 
    1088            0 : end subroutine slkmat_dp_set_head_and_wings
    1089              : !!***
    1090              : 
    1091              : !----------------------------------------------------------------------
    1092              : 
    1093              : !!****f* m_slk/slkmat_sp_set_head_and_wings
    1094              : !! NAME
    1095              : !!  slkmat_sp_set_head_and_wings
    1096              : !!
    1097              : !! FUNCTION
    1098              : !!  Set head and the wings of the matrix starting from global arrays.
    1099              : !!
    1100              : !! SOURCE
    1101              : 
    1102            0 : subroutine slkmat_sp_set_head_and_wings(mat, head, low_wing, up_wing)
    1103              : 
    1104              : !Arguments ------------------------------------
    1105              :  class(slkmat_sp_t),intent(inout) :: mat
    1106              :  complex(sp),intent(in) :: head, low_wing(mat%size_global(1)), up_wing(mat%size_global(2))
    1107              : 
    1108              : !Local variables-------------------------------
    1109              :  integer :: il_g1, il_g2, iglob1, iglob2
    1110              :  logical :: is_cplx
    1111              : ! *********************************************************************
    1112              : 
    1113            0 :  is_cplx = allocated(mat%buffer_cplx)
    1114              : 
    1115            0 :  do il_g2=1,mat%size_local(2)
    1116            0 :    iglob2 = mat%loc2gcol(il_g2)
    1117            0 :    do il_g1=1,mat%size_local(1)
    1118            0 :      iglob1 = mat%loc2grow(il_g1)
    1119              : 
    1120            0 :      if (iglob1 == 1 .or. iglob2 == 1) then
    1121            0 :        if (iglob1 == 1 .and. iglob2 == 1) then
    1122            0 :          if (is_cplx) then
    1123            0 :            mat%buffer_cplx(il_g1, il_g2) = head
    1124              :          else
    1125            0 :            mat%buffer_real(il_g1, il_g2) = real(head)
    1126              :          end if
    1127            0 :        else if (iglob1 == 1) then
    1128            0 :          if (is_cplx) then
    1129            0 :            mat%buffer_cplx(il_g1, il_g2) = up_wing(iglob2)
    1130              :          else
    1131            0 :            mat%buffer_real(il_g1, il_g2) = real(up_wing(iglob2))
    1132              :          end if
    1133            0 :        else if (iglob2 == 1) then
    1134            0 :          if (is_cplx) then
    1135            0 :            mat%buffer_cplx(il_g1, il_g2) = low_wing(iglob1)
    1136              :          else
    1137            0 :            mat%buffer_real(il_g1, il_g2) = real(low_wing(iglob1))
    1138              :          end if
    1139              :        end if
    1140              :      end if
    1141              : 
    1142              :    end do
    1143              :  end do
    1144              : 
    1145            0 : end subroutine slkmat_sp_set_head_and_wings
    1146              : !!***
    1147              : 
    1148              : !----------------------------------------------------------------------
    1149              : 
    1150              : !!****f* m_slk/slkmat_dp_copy
    1151              : !! NAME
    1152              : !!  slkmat_dp_copy
    1153              : !!
    1154              : !! FUNCTION
    1155              : !!  Copy in_mat to out_mat. If empty is True, the values in the local buffer are not copied. Default: False
    1156              : !!
    1157              : !! SOURCE
    1158              : 
    1159            0 : subroutine slkmat_dp_copy(in_mat, out_mat, empty)
    1160              : 
    1161              : !Arguments ------------------------------------
    1162              :  class(slkmat_dp_t),intent(in) :: in_mat
    1163              :  class(slkmat_dp_t),intent(out) :: out_mat
    1164              :  logical,optional,intent(in) :: empty
    1165              : 
    1166              : !Local variables-------------------------------
    1167              :  logical :: empty__
    1168              :  type(c_ptr) :: gpu_ptr
    1169              : ! *********************************************************************
    1170              : 
    1171              :  call out_mat%init(in_mat%size_global(1), in_mat%size_global(2), in_mat%processor, in_mat%istwf_k, &
    1172            0 :                    size_blocs=in_mat%size_blocs)
    1173              : 
    1174            0 :  empty__ = .False.; if (present(empty)) empty__ = empty
    1175            0 :  if (.not. empty__) then
    1176            0 :    if (in_mat%istwf_k == 1) then
    1177            0 :      out_mat%buffer_cplx = in_mat%buffer_cplx
    1178              :    else
    1179            0 :      out_mat%buffer_real = in_mat%buffer_real
    1180              :    end if
    1181              :  end if
    1182              : 
    1183            0 :  if (in_mat%is_gpu_mapped(gpu_ptr)) then
    1184            0 :    call out_mat%gpu_map("alloc")
    1185            0 :    if (in_mat%istwf_k == 1) then
    1186            0 :      call gpu_copy_complex(out_mat%buffer_cplx, in_mat%buffer_cplx, in_mat%bufsize)
    1187              :    else
    1188            0 :      call gpu_copy(out_mat%buffer_real, in_mat%buffer_real, in_mat%bufsize)
    1189              :    end if
    1190              :  end if
    1191              : 
    1192            0 : end subroutine slkmat_dp_copy
    1193              : !!***
    1194              : 
    1195              : !----------------------------------------------------------------------
    1196              : 
    1197              : !!****f* m_slk/slkmat_sp_copy
    1198              : !! NAME
    1199              : !!  slkmat_sp_copy
    1200              : !!
    1201              : !! FUNCTION
    1202              : !!  Copy in_mat to out_mat. If empty is True, the values in the local buffer are not copied. Default: False
    1203              : !!
    1204              : !! SOURCE
    1205              : 
    1206            0 : subroutine slkmat_sp_copy(in_mat, out_mat, empty)
    1207              : 
    1208              : !Arguments ------------------------------------
    1209              :  class(slkmat_sp_t),intent(in) :: in_mat
    1210              :  class(slkmat_sp_t),intent(out) :: out_mat
    1211              :  logical,optional,intent(in) :: empty
    1212              : 
    1213              : !Local variables-------------------------------
    1214              :  logical :: empty__
    1215              :  type(c_ptr) :: gpu_ptr
    1216              : ! *********************************************************************
    1217              : 
    1218              :  call out_mat%init(in_mat%size_global(1), in_mat%size_global(2), in_mat%processor, in_mat%istwf_k, &
    1219            0 :                    size_blocs=in_mat%size_blocs)
    1220              : 
    1221            0 :  empty__ = .False.; if (present(empty)) empty__ = empty
    1222            0 :  if (.not. empty__) then
    1223            0 :    if (in_mat%istwf_k == 1) then
    1224            0 :      out_mat%buffer_cplx = in_mat%buffer_cplx
    1225              :    else
    1226            0 :      out_mat%buffer_real = in_mat%buffer_real
    1227              :    end if
    1228              :  end if
    1229              : 
    1230            0 :  if (in_mat%is_gpu_mapped(gpu_ptr)) then
    1231            0 :    call out_mat%gpu_map("alloc")
    1232            0 :    if (in_mat%istwf_k == 1) then
    1233            0 :      call gpu_copy_complex_sp(out_mat%buffer_cplx, in_mat%buffer_cplx, in_mat%bufsize)
    1234              :    else
    1235            0 :      call gpu_copy_sp(out_mat%buffer_real, in_mat%buffer_real, in_mat%bufsize)
    1236              :    end if
    1237              :  end if
    1238              : 
    1239            0 : end subroutine slkmat_sp_copy
    1240              : !!***
    1241              : 
    1242              : !----------------------------------------------------------------------
    1243              : 
    1244              : !!****f* m_slk/basemat_free
    1245              : !! NAME
    1246              : !!  basemat_free
    1247              : !!
    1248              : !! FUNCTION
    1249              : !!  Free dynamic memory
    1250              : !!
    1251              : !! SOURCE
    1252              : 
    1253            0 : subroutine basemat_free(mat)
    1254              : 
    1255              : !Arguments ------------------------------------
    1256              :  class(basemat_t),intent(inout) :: mat
    1257              : 
    1258              : !Local variables-------------------------------
    1259              :  type(c_ptr) :: gpu_ptr
    1260              : ! *********************************************************************
    1261              : 
    1262              :  ! Don't free the grid. Just nullify the pointer as there might be other objects keeping a ref to processor.
    1263            0 :  mat%processor => null()
    1264              : 
    1265            0 :  mat%size_global = 0
    1266            0 :  mat%size_blocs = 0
    1267            0 :  mat%size_local = 0
    1268            0 :  mat%desc = 0
    1269              : 
    1270            0 :  if (mat%is_gpu_mapped(gpu_ptr)) then
    1271            0 :    call wrtout(std_out, "Deallocating mat%buffer on the GPU")
    1272            0 :    call mat%gpu_map("delete")
    1273              :  end if
    1274              : 
    1275              :  select type (mat)
    1276              :  class is (slkmat_dp_t)
    1277            0 :    ABI_SFREE(mat%buffer_cplx)
    1278            0 :    ABI_SFREE(mat%buffer_real)
    1279              : 
    1280              :  class is (slkmat_sp_t)
    1281            0 :    ABI_SFREE(mat%buffer_cplx)
    1282            0 :    ABI_SFREE(mat%buffer_real)
    1283              : 
    1284              :  class default
    1285            0 :    ABI_ERROR("Wrong class")
    1286              :  end select
    1287              : 
    1288            0 : end subroutine basemat_free
    1289              : !!***
    1290              : 
    1291              : !----------------------------------------------------------------------
    1292              : 
    1293              : !!****f* m_slk/slk_array1_free
    1294              : !! NAME
    1295              : !!  slk_array1_free
    1296              : !!
    1297              : !! FUNCTION
    1298              : !!  Deallocate 1d array of slkmat_dp_t elements
    1299              : !!
    1300              : !! SOURCE
    1301              : 
    1302            0 : subroutine slk_array1_free(slk_arr1)
    1303              :   class(basemat_t),intent(inout) :: slk_arr1(:)
    1304              :   integer :: i1
    1305            0 :   do i1=1,size(slk_arr1, dim=1)
    1306            0 :     call slk_arr1(i1)%free()
    1307              :   end do
    1308            0 : end subroutine slk_array1_free
    1309              : !!***
    1310              : 
    1311              : !----------------------------------------------------------------------
    1312              : 
    1313              : !!****f* m_slk/slk_array2_free
    1314              : !! NAME
    1315              : !!  slk_array2_free
    1316              : !!
    1317              : !! FUNCTION
    1318              : !!  Deallocate 2d array of slkmat_dp_t elements
    1319              : !!
    1320              : !! SOURCE
    1321              : 
    1322            0 : subroutine slk_array2_free(slk_arr2)
    1323              :   class(basemat_t),intent(inout) :: slk_arr2(:,:)
    1324              :   integer :: i1, i2
    1325            0 :   do i2=1,size(slk_arr2, dim=2)
    1326            0 :     do i1=1,size(slk_arr2, dim=1)
    1327            0 :       call slk_arr2(i1, i2)%free()
    1328              :     end do
    1329              :   end do
    1330            0 : end subroutine slk_array2_free
    1331              : !!***
    1332              : 
    1333              : !----------------------------------------------------------------------
    1334              : 
    1335              : !!****f* m_slk/slk_array3_free
    1336              : !! NAME
    1337              : !!  slk_array3_free
    1338              : !!
    1339              : !! FUNCTION
    1340              : !!  Deallocate 3d array of slkmat_dp_t elements
    1341              : !!
    1342              : !! SOURCE
    1343              : 
    1344            0 : subroutine slk_array3_free(slk_arr3)
    1345              :   class(basemat_t),intent(inout) :: slk_arr3(:,:,:)
    1346              :   integer :: i1, i2, i3
    1347            0 :   do i3=1,size(slk_arr3, dim=3)
    1348            0 :     do i2=1,size(slk_arr3, dim=2)
    1349            0 :       do i1=1,size(slk_arr3, dim=1)
    1350            0 :         call slk_arr3(i1, i2, i3)%free()
    1351              :       end do
    1352              :     end do
    1353              :   end do
    1354            0 : end subroutine slk_array3_free
    1355              : !!***
    1356              : 
    1357              : !----------------------------------------------------------------------
    1358              : 
    1359              : !!****f* m_slk/slk_array4_free
    1360              : !! NAME
    1361              : !!  slk_array4_free
    1362              : !!
    1363              : !! FUNCTION
    1364              : !!  Deallocate 4d array of slkmat_dp_t elements
    1365              : !!
    1366              : !! SOURCE
    1367              : 
    1368            0 : subroutine slk_array4_free(slk_arr4)
    1369              :   class(basemat_t),intent(inout) :: slk_arr4(:,:,:,:)
    1370              :   integer :: i1, i2, i3, i4
    1371            0 :   do i4=1,size(slk_arr4, dim=4)
    1372            0 :     do i3=1,size(slk_arr4, dim=3)
    1373            0 :       do i2=1,size(slk_arr4, dim=2)
    1374            0 :         do i1=1,size(slk_arr4, dim=1)
    1375            0 :           call slk_arr4(i1, i2, i3, i4)%free()
    1376              :         end do
    1377              :       end do
    1378              :     end do
    1379              :   end do
    1380            0 : end subroutine slk_array4_free
    1381              : !!***
    1382              : 
    1383              : !----------------------------------------------------------------------
    1384              : 
    1385              : !!****f* m_slk/slk_array5_free
    1386              : !! NAME
    1387              : !!  slk_array5_free
    1388              : !!
    1389              : !! FUNCTION
    1390              : !!  Deallocate 5d array of slkmat_dp_t elements
    1391              : !!
    1392              : !! SOURCE
    1393              : 
    1394            0 : subroutine slk_array5_free(slk_arr5)
    1395              :   class(basemat_t),intent(inout) :: slk_arr5(:,:,:,:,:)
    1396              :   integer :: i1, i2, i3, i4, i5
    1397            0 :   do i5=1,size(slk_arr5, dim=5)
    1398            0 :     do i4=1,size(slk_arr5, dim=4)
    1399            0 :       do i3=1,size(slk_arr5, dim=3)
    1400            0 :         do i2=1,size(slk_arr5, dim=2)
    1401            0 :           do i1=1,size(slk_arr5, dim=1)
    1402            0 :             call slk_arr5(i1, i2, i3, i4, i5)%free()
    1403              :           end do
    1404              :         end do
    1405              :       end do
    1406              :     end do
    1407              :   end do
    1408            0 : end subroutine slk_array5_free
    1409              : !!***
    1410              : 
    1411              : !----------------------------------------------------------------------
    1412              : 
    1413              : !!****f* m_slk/slk_array_set_zero
    1414              : !! NAME
    1415              : !!  slk_array_set_zero
    1416              : !!
    1417              : !! FUNCTION
    1418              : !!  Elemental routine to set the value of the PBLAS buffer to zero
    1419              : !!
    1420              : !! SOURCE
    1421              : 
    1422            0 : elemental subroutine slk_array_set_zero(mat)
    1423              : 
    1424              : !Arguments ------------------------------------
    1425              :  class(basemat_t),intent(inout) :: mat
    1426              : 
    1427              :  select type (mat)
    1428              :  class is (slkmat_dp_t)
    1429            0 :    if (allocated(mat%buffer_cplx)) mat%buffer_cplx = zero
    1430            0 :    if (allocated(mat%buffer_real)) mat%buffer_real = zero
    1431              :  class is (slkmat_sp_t)
    1432            0 :    if (allocated(mat%buffer_cplx)) mat%buffer_cplx = cmplx(zero, kind=sp)
    1433            0 :    if (allocated(mat%buffer_real)) mat%buffer_real = real(zero, kind=sp)
    1434              :  end select
    1435              : 
    1436            0 : end subroutine slk_array_set_zero
    1437              : !!***
    1438              : 
    1439              : !----------------------------------------------------------------------
    1440              : 
    1441              : !!****f* m_slk/basemat_gpu_set_zero
    1442              : !! NAME
    1443              : !!  basemat_gpu_set_zero
    1444              : !!
    1445              : !! FUNCTION
    1446              : !!  Elemental routine to set the value of the PBLAS buffer to zero
    1447              : !!
    1448              : !! SOURCE
    1449              : 
    1450            0 : subroutine basemat_gpu_set_zero(mat)
    1451              : 
    1452              : !Arguments ------------------------------------
    1453              :  class(basemat_t),target,intent(inout) :: mat
    1454              : 
    1455              : #ifdef HAVE_OPENMP_OFFLOAD
    1456              : !Local variables-------------------------------
    1457              :  type(c_ptr) :: gpu_ptr
    1458              : ! *********************************************************************
    1459              : 
    1460              :  select type (mat)
    1461              :  class is (slkmat_dp_t)
    1462              :    if (allocated(mat%buffer_cplx)) then
    1463              :      gpu_ptr = xomp_get_mapped_ptr(c_loc(mat%buffer_cplx))
    1464              :      ABI_CHECK_CNULL(gpu_ptr, "buffer_cplx not on GPU!")
    1465              :      call gpu_memset(gpu_ptr, 0, mat%bufsize*dp*2)
    1466              :    end if
    1467              :    if (allocated(mat%buffer_real)) then
    1468              :      gpu_ptr = xomp_get_mapped_ptr(c_loc(mat%buffer_real))
    1469              :      ABI_CHECK_CNULL(gpu_ptr, "buffer_real not on GPU!")
    1470              :      call gpu_memset(gpu_ptr, 0, mat%bufsize*dp)
    1471              :    end if
    1472              :  class is (slkmat_sp_t)
    1473              :    if (allocated(mat%buffer_cplx)) then
    1474              :      gpu_ptr = xomp_get_mapped_ptr(c_loc(mat%buffer_cplx))
    1475              :      ABI_CHECK_CNULL(gpu_ptr, "buffer_cplx not on GPU!")
    1476              :      call gpu_memset(gpu_ptr, 0, mat%bufsize*sp*2)
    1477              :    end if
    1478              :    if (allocated(mat%buffer_real)) then
    1479              :      gpu_ptr = xomp_get_mapped_ptr(c_loc(mat%buffer_real))
    1480              :      ABI_CHECK_CNULL(gpu_ptr, "buffer_real not on GPU!")
    1481              :      call gpu_memset(gpu_ptr, 0, mat%bufsize*sp)
    1482              :    end if
    1483              :  end select
    1484              : #else
    1485            0 :  ABI_ERROR("basemat_gpu_set cannot be used if HAVE_OPENMP_OFFLOAD is not defined!")
    1486              :  ABI_UNUSED(mat%size_local(1))
    1487              : #endif
    1488              : 
    1489            0 : end subroutine basemat_gpu_set_zero
    1490              : !!***
    1491              : 
    1492              : !!****f* m_slk/slk_array1_gpu_set_zero
    1493              : !! NAME
    1494              : !!  slk_array1_gpu_set_zero
    1495              : !!
    1496              : !! FUNCTION
    1497              : !!
    1498              : !! SOURCE
    1499              : 
    1500            0 : subroutine slk_array1_gpu_set_zero(mat1d)
    1501              : 
    1502              : !Arguments ------------------------------------
    1503              :  class(basemat_t),intent(inout) :: mat1d(:)
    1504              : 
    1505              : !Local variables-------------------------------
    1506              :  integer :: i1
    1507              : ! *********************************************************************
    1508              : 
    1509            0 :  do i1=1,size(mat1d, dim=1)
    1510            0 :    call mat1d(i1)%gpu_set_zero()
    1511              :  end do
    1512              : 
    1513            0 : end subroutine slk_array1_gpu_set_zero
    1514              : !!***
    1515              : 
    1516              : !!****f* m_slk/slk_array2_gpu_set_zero
    1517              : !! NAME
    1518              : !!  slk_array2_gpu_set_zero
    1519              : !!
    1520              : !! FUNCTION
    1521              : !!
    1522              : !! SOURCE
    1523              : 
    1524            0 : subroutine slk_array2_gpu_set_zero(mat2d)
    1525              : 
    1526              : !Arguments ------------------------------------
    1527              :  class(basemat_t),intent(inout) :: mat2d(:,:)
    1528              : 
    1529              : !Local variables-------------------------------
    1530              :  integer :: i1, i2
    1531              : ! *********************************************************************
    1532              : 
    1533            0 :  do i2=1,size(mat2d, dim=2)
    1534            0 :    do i1=1,size(mat2d, dim=1)
    1535            0 :      call mat2d(i1, i2)%gpu_set_zero()
    1536              :    end do
    1537              :  end do
    1538              : 
    1539            0 : end subroutine slk_array2_gpu_set_zero
    1540              : !!***
    1541              : 
    1542              : !!****f* m_slk/slk_array3_gpu_set_zero
    1543              : !! NAME
    1544              : !!  slk_array3_gpu_set_zero
    1545              : !!
    1546              : !! FUNCTION
    1547              : !!
    1548              : !! SOURCE
    1549              : 
    1550            0 : subroutine slk_array3_gpu_set_zero(mat3d)
    1551              : 
    1552              : !Arguments ------------------------------------
    1553              :  class(basemat_t),intent(inout) :: mat3d(:,:,:)
    1554              : 
    1555              : !Local variables-------------------------------
    1556              :  integer :: i1,i2,i3
    1557              : ! *********************************************************************
    1558              : 
    1559            0 :  do i3=1,size(mat3d, dim=3)
    1560            0 :    do i2=1,size(mat3d, dim=2)
    1561            0 :      do i1=1,size(mat3d, dim=1)
    1562            0 :        call mat3d(i1,i2,i3)%gpu_set_zero()
    1563              :      end do
    1564              :    end do
    1565              :  end do
    1566              : 
    1567            0 : end subroutine slk_array3_gpu_set_zero
    1568              : !!***
    1569              : 
    1570              : !!****f* m_slk/slk_array4_gpu_set_zero
    1571              : !! NAME
    1572              : !!  slk_array4_gpu_set_zero
    1573              : !!
    1574              : !! FUNCTION
    1575              : !!
    1576              : !! SOURCE
    1577              : 
    1578            0 : subroutine slk_array4_gpu_set_zero(mat4d)
    1579              : 
    1580              : !Arguments ------------------------------------
    1581              :  class(basemat_t),intent(inout) :: mat4d(:,:,:,:)
    1582              : 
    1583              : !Local variables-------------------------------
    1584              :  integer :: i1, i2, i3, i4
    1585              : ! *********************************************************************
    1586              : 
    1587            0 :  do i4=1,size(mat4d, dim=4)
    1588            0 :    do i3=1,size(mat4d, dim=3)
    1589            0 :      do i2=1,size(mat4d, dim=2)
    1590            0 :        do i1=1,size(mat4d, dim=1)
    1591            0 :          call mat4d(i1, i2, i3, i4)%gpu_set_zero()
    1592              :        end do
    1593              :      end do
    1594              :    end do
    1595              :  end do
    1596              : 
    1597            0 : end subroutine slk_array4_gpu_set_zero
    1598              : !!***
    1599              : 
    1600              : !!****f* m_slk/slk_array5_gpu_set_zero
    1601              : !! NAME
    1602              : !!  slk_array5_gpu_set_zero
    1603              : !!
    1604              : !! FUNCTION
    1605              : !!
    1606              : !! SOURCE
    1607              : 
    1608            0 : subroutine slk_array5_gpu_set_zero(mat5d)
    1609              : 
    1610              : !Arguments ------------------------------------
    1611              :  class(basemat_t),intent(inout) :: mat5d(:,:,:,:,:)
    1612              : 
    1613              : !Local variables-------------------------------
    1614              :  integer :: i1,i2,i3,i4,i5
    1615              : ! *********************************************************************
    1616              : 
    1617            0 :  do i5=1,size(mat5d, dim=5)
    1618            0 :    do i4=1,size(mat5d, dim=4)
    1619            0 :      do i3=1,size(mat5d, dim=3)
    1620            0 :        do i2=1,size(mat5d, dim=2)
    1621            0 :          do i1=1,size(mat5d, dim=1)
    1622            0 :            call mat5d(i1,i2,i3,i4,i5)%gpu_set_zero()
    1623              :          end do
    1624              :        end do
    1625              :      end do
    1626              :    end do
    1627              :  end do
    1628              : 
    1629            0 : end subroutine slk_array5_gpu_set_zero
    1630              : !!***
    1631              : 
    1632              : !----------------------------------------------------------------------
    1633              : 
    1634              : !!****f* m_slk/basemat_is_gpu_mapped
    1635              : !! NAME
    1636              : !!  basemat_is_gpu_mapped
    1637              : !!
    1638              : !! FUNCTION
    1639              : !!  Return True if the local buffer is mapped to the GPU and the pointer on the gpu.
    1640              : !!
    1641              : !! SOURCE
    1642              : 
    1643            0 : logical function basemat_is_gpu_mapped(mat, gpu_ptr) result(is_gpu_mapped)
    1644              : 
    1645              : !Arguments ------------------------------------
    1646              :  class(basemat_t),target,intent(in) :: mat
    1647              :  type(c_ptr),intent(out) :: gpu_ptr
    1648              : 
    1649              : !Local variables-------------------------------
    1650              : #ifdef HAVE_OPENMP_OFFLOAD
    1651              :  real(sp), contiguous, pointer :: buf_real_sp(:,:)
    1652              :  real(dp), contiguous, pointer :: buf_real_dp(:,:)
    1653              :  complex(sp), contiguous, pointer :: buf_cplx_sp(:,:)
    1654              :  complex(dp), contiguous, pointer :: buf_cplx_dp(:,:)
    1655              : #endif
    1656              : ! *********************************************************************
    1657              : 
    1658            0 :  is_gpu_mapped = .False.; gpu_ptr = c_null_ptr
    1659              : #ifdef HAVE_OPENMP_OFFLOAD
    1660              :  select type (mat)
    1661              :  class is (slkmat_dp_t)
    1662              :    if (allocated(mat%buffer_cplx)) then
    1663              :      buf_cplx_dp => mat%buffer_cplx
    1664              :      gpu_ptr = xomp_get_mapped_ptr(c_loc(buf_cplx_dp))
    1665              :      is_gpu_mapped = c_associated(gpu_ptr)
    1666              :    end if
    1667              :    if (allocated(mat%buffer_real)) then
    1668              :      buf_real_dp => mat%buffer_real
    1669              :      gpu_ptr = xomp_get_mapped_ptr(c_loc(buf_real_dp))
    1670              :      is_gpu_mapped = c_associated(gpu_ptr)
    1671              :    end if
    1672              : 
    1673              :  class is (slkmat_sp_t)
    1674              :    if (allocated(mat%buffer_cplx)) then
    1675              :      buf_cplx_sp => mat%buffer_cplx
    1676              :      gpu_ptr= xomp_get_mapped_ptr(c_loc(buf_cplx_sp))
    1677              :      is_gpu_mapped = c_associated(gpu_ptr)
    1678              :    end if
    1679              :    if (allocated(mat%buffer_real)) then
    1680              :      buf_real_sp => mat%buffer_real
    1681              :      gpu_ptr = xomp_get_mapped_ptr(c_loc(buf_real_sp))
    1682              :      is_gpu_mapped = c_associated(gpu_ptr)
    1683              :    end if
    1684              :  end select
    1685              : #else
    1686              :  ABI_UNUSED(mat%size_local(1))
    1687              : #endif
    1688              : 
    1689            0 : end function basemat_is_gpu_mapped
    1690              : !!***
    1691              : 
    1692              : !----------------------------------------------------------------------
    1693              : 
    1694              : !!****f* m_slk/basemat_gpu_map
    1695              : !! NAME
    1696              : !!  basemat_gpu_map
    1697              : !!
    1698              : !! FUNCTION
    1699              : !!  Use Opemp to allocate/delete the local buffer on the GPU.
    1700              : !!
    1701              : !! SOURCE
    1702              : 
    1703            0 : subroutine basemat_gpu_map(mat, gpu_action)
    1704              : 
    1705              : !Arguments ------------------------------------
    1706              :  class(basemat_t),target,intent(inout) :: mat
    1707              :  character(len=*), intent(in) :: gpu_action
    1708              : 
    1709              : !Local variables-------------------------------
    1710              : #ifdef HAVE_OPENMP_OFFLOAD
    1711              :  real(sp), contiguous, pointer :: buf_real_sp(:,:)
    1712              :  real(dp), contiguous, pointer :: buf_real_dp(:,:)
    1713              :  complex(sp), contiguous, pointer :: buf_cplx_sp(:,:)
    1714              :  complex(dp), contiguous, pointer :: buf_cplx_dp(:,:)
    1715              : #endif
    1716              : ! *********************************************************************
    1717              : 
    1718            0 :  if (.not. string_in(gpu_action, "None, alloc, alloc_zero, delete, update_from, update_to")) then
    1719            0 :    ABI_ERROR(sjoin("Invalid gpu_action", gpu_action))
    1720              :    ABI_UNUSED(mat%size_local(1))
    1721              :  end if
    1722              : 
    1723              :  if (gpu_action == "None") return
    1724              : 
    1725              : #ifdef HAVE_OPENMP_OFFLOAD
    1726              :  select type (mat)
    1727              :  class is (slkmat_dp_t)
    1728              :    if (allocated(mat%buffer_cplx)) then
    1729              :      buf_cplx_dp => mat%buffer_cplx
    1730              :      if (string_in(gpu_action, "alloc, alloc_zero")) then
    1731              :        !$OMP TARGET ENTER DATA MAP(alloc:buf_cplx_dp)
    1732              :        if (gpu_action == "alloc_zero") call gpu_set_to_zero_complex(mat%buffer_cplx, mat%bufsize)
    1733              :      else if (gpu_action == "delete") then !.and. c_associated(xomp_get_mapped_ptr(c_loc(buf_cplx_dp))
    1734              :        !$OMP TARGET EXIT DATA MAP(delete:buf_cplx_dp)
    1735              :      else if (gpu_action == "update_from") then
    1736              :        !$OMP TARGET UPDATE FROM(buf_cplx_dp)
    1737              :      else if (gpu_action == "update_to") then
    1738              :        !$OMP TARGET UPDATE TO(buf_cplx_dp)
    1739              :      end if
    1740              :    end if
    1741              :    if (allocated(mat%buffer_real)) then
    1742              :      buf_real_dp => mat%buffer_real
    1743              :      if (string_in(gpu_action, "alloc, alloc_zero")) then
    1744              :        !$OMP TARGET ENTER DATA MAP(alloc:buf_real_dp)
    1745              :        if (gpu_action == "alloc_zero") call gpu_set_to_zero(mat%buffer_real, mat%bufsize)
    1746              :      else if (gpu_action == "delete") then !.and. c_associated(xomp_get_mapped_ptr(c_loc(buf_real_dp))
    1747              :        !$OMP TARGET EXIT DATA MAP(delete:buf_real_dp)
    1748              :      else if (gpu_action == "update_from") then
    1749              :        !$OMP TARGET UPDATE FROM(buf_real_dp)
    1750              :      else if (gpu_action == "update_to") then
    1751              :        !$OMP TARGET UPDATE TO(buf_real_dp)
    1752              :      end if
    1753              :    end if
    1754              : 
    1755              :  class is (slkmat_sp_t)
    1756              :    if (allocated(mat%buffer_cplx)) then
    1757              :      buf_cplx_sp => mat%buffer_cplx
    1758              :      if (string_in(gpu_action, "alloc, alloc_zero")) then
    1759              :        !$OMP TARGET ENTER DATA MAP(alloc:buf_cplx_sp)
    1760              :        if (gpu_action == "alloc_zero") call gpu_set_to_zero_complex_sp(mat%buffer_cplx, mat%bufsize)
    1761              :      else if (gpu_action == "delete") then !.and. c_associated(xomp_get_mapped_ptr(c_loc(buf_cplx_sp))
    1762              :        !$OMP TARGET EXIT DATA MAP(delete:buf_cplx_sp)
    1763              :      else if (gpu_action == "update_from") then
    1764              :        !$OMP TARGET UPDATE FROM(buf_cplx_sp)
    1765              :      else if (gpu_action == "update_to") then
    1766              :        !$OMP TARGET UPDATE TO(buf_cplx_sp)
    1767              :      end if
    1768              :    end if
    1769              :    if (allocated(mat%buffer_real)) then
    1770              :      buf_real_sp => mat%buffer_real
    1771              :      if (string_in(gpu_action, "alloc, alloc_zero")) then
    1772              :        !$OMP TARGET ENTER DATA MAP(alloc:buf_real_sp)
    1773              :        if (gpu_action == "alloc_zero") call gpu_set_to_zero_sp(mat%buffer_real, mat%bufsize)
    1774              :      else if (gpu_action == "delete") then !.and. c_associated(xomp_get_mapped_ptr(c_loc(buf_real_sp))
    1775              :        !$OMP TARGET EXIT DATA MAP(delete:buf_real_sp)
    1776              :      else if (gpu_action == "update_from") then
    1777              :        !$OMP TARGET UPDATE FROM(buf_real_sp)
    1778              :      else if (gpu_action == "update_to") then
    1779              :        !$OMP TARGET UPDATE TO(buf_real_sp)
    1780              :      end if
    1781              :    end if
    1782              :  end select
    1783              : #endif
    1784              : 
    1785              : end subroutine basemat_gpu_map
    1786              : !!***
    1787              : 
    1788              : !----------------------------------------------------------------------
    1789              : 
    1790              : !!****f* m_slk/slk_array_locmem_mb
    1791              : !! NAME
    1792              : !!  slk_array_locmem_mb
    1793              : !!
    1794              : !! FUNCTION
    1795              : !!  Elemental function to compute the memory allocated for an array of slkmat_dp_t elements
    1796              : !!  Usage: mem_mb = sum(mat_array)
    1797              : !!
    1798              : !! SOURCE
    1799              : 
    1800            0 : elemental real(dp) function slk_array_locmem_mb(mat) result(mem_mb)
    1801              :   class(basemat_t),intent(in) :: mat
    1802            0 :   mem_mb = mat%locmem_mb()
    1803            0 : end function slk_array_locmem_mb
    1804              : !!***
    1805              : 
    1806              : !----------------------------------------------------------------------
    1807              : 
    1808              : !!****f* m_slk/block_dist_1d
    1809              : !! NAME
    1810              : !!  block_dist_1d
    1811              : !!
    1812              : !! FUNCTION
    1813              : !!  Return block size for one-dimensional block column (row) distribution.
    1814              : !!  Mainly used to assign blocks of contiguous columns (rows) of a matrix to successive processes
    1815              : !!  when a 1d grid is employed.
    1816              : !!
    1817              : !!  It is usually interfaced with CPP macros, e.g:
    1818              : !!
    1819              : !!    ABI_CHECK(block_dist_1d(mat_size, nproc, block_size, msg), msg)
    1820              : !!
    1821              : !! INPUTS
    1822              : !!  mat_size=Size of the matrix (either number of rows or number of colums)
    1823              : !!  nproc=Number of processoes in the 1D scalapack grid
    1824              : !!
    1825              : !! OUTPUT
    1826              : !!  ok= Boolean flag with exit status (idle processes are not allowed).
    1827              : !!  block_size=Size of the block along this axis needed for one-dimensional block distribution
    1828              : !!  msg=Error message (if not ok)
    1829              : !!
    1830              : !! SOURCE
    1831              : 
    1832            0 : logical function block_dist_1d(mat_size, nproc, block_size, msg) result (ok)
    1833              : 
    1834              : !Arguments ------------------------------------
    1835              :  integer, intent(in) :: mat_size, nproc
    1836              :  integer,intent(out) :: block_size
    1837              :  character(len=*),intent(out) :: msg
    1838              : ! *********************************************************************
    1839              : 
    1840            0 :  ok = .True.; msg = ""
    1841              :  !block_size = 1; return
    1842              : 
    1843            0 :  block_size = mat_size / nproc
    1844            0 :  if (block_size == 0) then
    1845            0 :    ok = .False.
    1846              :    write(msg, "(2(a,i0), 2a)") &
    1847            0 :      "The number of MPI processors: ", nproc, " exceeeds the number of rows (columms) of the matrix: ", mat_size, ch10, &
    1848            0 :      "Decrease the number of MPI processes for the scalapack level."
    1849            0 :    return
    1850              :  end if
    1851              : 
    1852            0 :  if (mod(mat_size, nproc) /= 0) block_size = block_size + 1
    1853              : 
    1854            0 : end function block_dist_1d
    1855              : !!***
    1856              : 
    1857              : !----------------------------------------------------------------------
    1858              : 
    1859              : !!****f* m_slk/slk_has_elpa
    1860              : !! NAME
    1861              : !!  slk_has_elpa
    1862              : !!
    1863              : !! FUNCTION
    1864              : !!  Return True if ELPA support is activated
    1865              : !!
    1866              : !! SOURCE
    1867              : 
    1868            0 : pure logical function slk_has_elpa() result (ans)
    1869              : 
    1870            0 :  ans = .False.
    1871              : #ifdef HAVE_LINALG_ELPA
    1872              :  ans = .True.
    1873              : #endif
    1874              : 
    1875            0 : end function slk_has_elpa
    1876              : !!***
    1877              : 
    1878              : !----------------------------------------------------------------------
    1879              : 
    1880              : !!****f* m_slk/matrix_get_local_cplx
    1881              : !! NAME
    1882              : !!  matrix_get_local_cplx
    1883              : !!
    1884              : !! FUNCTION
    1885              : !!  Returns a local matrix coefficient of complex type.
    1886              : !!  Access to a component thanks to its local indices
    1887              : !!
    1888              : !! INPUTS
    1889              : !!  matrix= the matrix to process
    1890              : !!  i= row in the matrix
    1891              : !!  j= column in the matrix
    1892              : !!
    1893              : !! OUTPUT
    1894              : !!  The value of the local matrix.
    1895              : !!
    1896              : !! SOURCE
    1897              : 
    1898            0 : pure complex(dpc) function matrix_get_local_cplx(matrix, i, j)
    1899              : 
    1900              : !Arguments ------------------------------------
    1901              :  class(slkmat_dp_t),intent(in) :: matrix
    1902              :  integer, intent(in) :: i,j
    1903              : ! *********************************************************************
    1904              : 
    1905            0 :  matrix_get_local_cplx = matrix%buffer_cplx(i,j)
    1906              : 
    1907            0 : end function matrix_get_local_cplx
    1908              : !!***
    1909              : 
    1910              : !----------------------------------------------------------------------
    1911              : 
    1912              : !!****f* m_slk/matrix_get_local_real
    1913              : !! NAME
    1914              : !!  matrix_get_local_real
    1915              : !!
    1916              : !! FUNCTION
    1917              : !!  Returns a local matrix coefficient of double precision type.
    1918              : !!
    1919              : !! INPUTS
    1920              : !!  matrix= the matrix to process
    1921              : !!  i= row in the matrix
    1922              : !!  j= column in the matrix
    1923              : !!
    1924              : !! SOURCE
    1925              : 
    1926            0 : pure real(dp) function matrix_get_local_real(matrix,i,j)
    1927              : 
    1928              : !Arguments ------------------------------------
    1929              :  class(slkmat_dp_t),intent(in) :: matrix
    1930              :  integer, intent(in) :: i,j
    1931              : ! *********************************************************************
    1932              : 
    1933            0 :  matrix_get_local_real = matrix%buffer_real(i,j)
    1934              : 
    1935            0 : end function matrix_get_local_real
    1936              : !!***
    1937              : 
    1938              : !----------------------------------------------------------------------
    1939              : 
    1940              : !!****f* m_slk/matrix_set_local_cplx
    1941              : !! NAME
    1942              : !!  matrix_set_local_cplx
    1943              : !!
    1944              : !! FUNCTION
    1945              : !!  Sets a local matrix coefficient of complex type.
    1946              : !! -------------------------------------------------------
    1947              : !!  Positioning of a component of a matrix thanks to its local indices
    1948              : !! -------------------------------------------------------
    1949              : !!
    1950              : !! INPUTS
    1951              : !!  i= row in the matrix
    1952              : !!  j= column in the matrix
    1953              : !!  value= the value to set
    1954              : !!
    1955              : !! SIDE EFFECTS
    1956              : !!  matrix%buffer_cplx(i,j) filled with value
    1957              : !!
    1958              : !! SOURCE
    1959              : 
    1960            0 : pure subroutine matrix_set_local_cplx(matrix,i,j,value)
    1961              : 
    1962              : !Arguments ------------------------------------
    1963              :  class(slkmat_dp_t),intent(inout) :: matrix
    1964              :  integer, intent(in) :: i,j
    1965              :  complex(dp), intent(in) :: value
    1966              : ! *********************************************************************
    1967              : 
    1968            0 :  matrix%buffer_cplx(i,j) = value
    1969              : 
    1970            0 : end subroutine matrix_set_local_cplx
    1971              : !!***
    1972              : 
    1973              : !----------------------------------------------------------------------
    1974              : 
    1975              : !!****f* m_slk/matrix_set_local_real
    1976              : !! NAME
    1977              : !!  matrix_set_local_real
    1978              : !!
    1979              : !! FUNCTION
    1980              : !!  Sets a local matrix coefficient of double precision type.
    1981              : !!
    1982              : !! INPUTS
    1983              : !!  i= row in the matrix
    1984              : !!  j= column in the matrix
    1985              : !!  value= the value to set
    1986              : !!
    1987              : !! SIDE EFFECTS
    1988              : !!  matrix%buffer_real(i,j) set to value
    1989              : !!
    1990              : !! SOURCE
    1991              : 
    1992            0 : pure subroutine matrix_set_local_real(matrix, i, j, value)
    1993              : 
    1994              : !Arguments ------------------------------------
    1995              :  class(slkmat_dp_t),intent(inout) :: matrix
    1996              :  integer, intent(in) :: i,j
    1997              :  real(dp), intent(in) :: value
    1998              : ! *********************************************************************
    1999              : 
    2000            0 :  matrix%buffer_real(i,j) = value
    2001              : 
    2002            0 : end subroutine matrix_set_local_real
    2003              : !!***
    2004              : 
    2005              : !----------------------------------------------------------------------
    2006              : 
    2007              : !!****f* m_slk/idx_loc
    2008              : !! NAME
    2009              : !!  idx_loc
    2010              : !!
    2011              : !! FUNCTION
    2012              : !!  Return local indices from global indices, **independently** of the processor.
    2013              : !!
    2014              : !! INPUTS
    2015              : !!  matrix= the matrix to process
    2016              : !!  i= row in the matrix
    2017              : !!  j= column in the matrix
    2018              : !!
    2019              : !! OUTPUT
    2020              : !!  iloc= local row of the coefficient
    2021              : !!  jloc= local column of the coefficient
    2022              : !!
    2023              : !! SOURCE
    2024              : 
    2025            0 : subroutine basemat_idx_loc(matrix, i, j, iloc, jloc)
    2026              : 
    2027              : !Arguments ------------------------------------
    2028              :  class(basemat_t),intent(in) :: matrix
    2029              :  integer, intent(in) :: i,j
    2030              :  integer, intent(out) :: iloc,jloc
    2031              : ! *********************************************************************
    2032              : 
    2033            0 :  iloc = glob_loc__(matrix, i, 1)
    2034            0 :  jloc = glob_loc__(matrix, j, 2)
    2035              : 
    2036            0 : end subroutine basemat_idx_loc
    2037              : !!***
    2038              : 
    2039              : !----------------------------------------------------------------------
    2040              : 
    2041              : !!****f* m_slk/glob_loc__
    2042              : !! NAME
    2043              : !!  glob_loc__
    2044              : !!
    2045              : !! FUNCTION
    2046              : !!  Returns the global location of a matrix coefficient.
    2047              : !!
    2048              : !! INPUTS
    2049              : !!  matrix= the matrix to process
    2050              : !!  idx= number of rows in the distributed matrix
    2051              : !!  lico= block size index
    2052              : !!
    2053              : !! SOURCE
    2054              : 
    2055              : integer function glob_loc__(matrix, idx, lico)
    2056              : 
    2057              : !Arguments ------------------------------------
    2058              :  class(basemat_t),intent(in) :: matrix
    2059              :  integer, intent(in) :: idx, lico
    2060              : ! *********************************************************************
    2061              : 
    2062              : #ifdef HAVE_LINALG_SCALAPACK
    2063              :  glob_loc__ = NUMROC(idx, matrix%size_blocs(lico), &
    2064              :                      matrix%processor%coords(lico), 0, matrix%processor%grid%dims(lico))
    2065              : #endif
    2066              : 
    2067              : end function glob_loc__
    2068              : !!***
    2069              : 
    2070              : !----------------------------------------------------------------------
    2071              : 
    2072              : !!****f* m_slk/basemat_glob2loc
    2073              : !! NAME
    2074              : !!  basemat_glob2loc
    2075              : !!
    2076              : !! FUNCTION
    2077              : !!  Determine the local indices of an element from its global indices and return haveit bool flag.
    2078              : !!
    2079              : !! INPUTS
    2080              : !!  iloc= local row index.
    2081              : !!  jloc= local column index.
    2082              : !!
    2083              : !! OUTPUT
    2084              : !!  iloc= row in the matrix
    2085              : !!  jloc= column in the matrix
    2086              : !!  haveit= True if (iglob, jglob) is stored on this proc
    2087              : !!
    2088              : !! SOURCE
    2089              : 
    2090            0 : subroutine basemat_glob2loc(mat, iglob, jglob, iloc, jloc, haveit)
    2091              : 
    2092              : !Arguments ------------------------------------
    2093              :  class(basemat_t),intent(in) :: mat
    2094              :  integer, intent(in) :: iglob, jglob
    2095              :  integer, intent(out) :: iloc, jloc
    2096              :  logical,intent(out) :: haveit
    2097              : 
    2098              : !Local variables-------------------------------
    2099              :  integer :: row_src, col_src
    2100              : ! *********************************************************************
    2101              : 
    2102              : #ifdef HAVE_LINALG_SCALAPACK
    2103              :  ! SUBROUTINE INFOG2L( GRINDX, GCINDX, DESC, NPROW, NPCOL, MYROW, MYCOL, LRINDX, LCINDX, RSRC, CSRC)
    2104              : 
    2105              :  call INFOG2L(iglob, jglob, mat%desc, mat%processor%grid%dims(1), mat%processor%grid%dims(2), &
    2106              :    mat%processor%coords(1), mat%processor%coords(2), iloc, jloc, row_src, col_src)
    2107              : 
    2108              :  haveit = all(mat%processor%coords == [row_src, col_src])
    2109              : #endif
    2110              : 
    2111            0 : end subroutine basemat_glob2loc
    2112              : !!***
    2113              : 
    2114              : !----------------------------------------------------------------------
    2115              : 
    2116              : !!****f* m_slk/basemat_loc2glob
    2117              : !! NAME
    2118              : !!  basemat_loc2glob
    2119              : !!
    2120              : !! FUNCTION
    2121              : !!  Determine the global indices of an element from its local indices.
    2122              : !!
    2123              : !! INPUTS
    2124              : !!  matrix= the matrix to process.
    2125              : !!  iloc= local row index.
    2126              : !!  jloc= local column index.
    2127              : !!
    2128              : !! OUTPUT
    2129              : !!  i= row in the matrix
    2130              : !!  j= column in the matrix
    2131              : !!
    2132              : !! SOURCE
    2133              : 
    2134            0 : pure subroutine basemat_loc2glob(matrix, iloc, jloc, i, j)
    2135              : 
    2136              : !Arguments ------------------------------------
    2137              :  class(basemat_t),intent(in) :: matrix
    2138              :  integer, intent(in) :: iloc,jloc
    2139              :  integer, intent(out) :: i,j
    2140              : ! *********************************************************************
    2141              : 
    2142            0 :  i = loc_glob__(matrix, matrix%processor, iloc, 1)
    2143            0 :  j = loc_glob__(matrix, matrix%processor, jloc, 2)
    2144              : 
    2145            0 : end subroutine basemat_loc2glob
    2146              : !!***
    2147              : 
    2148              : !!****f* m_slk/basemat_loc2grow
    2149              : !! NAME
    2150              : !!  basemat_loc2grow
    2151              : !!
    2152              : !! FUNCTION
    2153              : !!  Determine the global row index from the local index
    2154              : !!
    2155              : !! INPUTS
    2156              : !!  matrix= the matrix to process.
    2157              : !!  iloc= local row index.
    2158              : !!
    2159              : !! SOURCE
    2160              : 
    2161            0 : integer pure function basemat_loc2grow(matrix, iloc) result(iglob)
    2162              : 
    2163              : !Arguments ------------------------------------
    2164              :  class(basemat_t),intent(in) :: matrix
    2165              :  integer, intent(in) :: iloc
    2166              : ! *********************************************************************
    2167              : 
    2168            0 :  iglob = loc_glob__(matrix, matrix%processor, iloc, 1)
    2169              : 
    2170            0 : end function basemat_loc2grow
    2171              : !!***
    2172              : 
    2173              : !!****f* m_slk/basemat_loc2col
    2174              : !! NAME
    2175              : !!  basemat_loc2col
    2176              : !!
    2177              : !! FUNCTION
    2178              : !!  Determine the global column index of an element from the local index
    2179              : !!
    2180              : !! INPUTS
    2181              : !!  matrix= the matrix to process.
    2182              : !!  jloc= local column index.
    2183              : !!
    2184              : !! SOURCE
    2185              : 
    2186            0 : integer pure function basemat_loc2col(matrix, jloc) result(jglob)
    2187              : 
    2188              : !Arguments ------------------------------------
    2189              :  class(basemat_t),intent(in) :: matrix
    2190              :  integer, intent(in) :: jloc
    2191              : ! *********************************************************************
    2192              : 
    2193            0 :  jglob = loc_glob__(matrix, matrix%processor, jloc, 2)
    2194              : 
    2195            0 : end function basemat_loc2col
    2196              : !!***
    2197              : 
    2198              : !----------------------------------------------------------------------
    2199              : 
    2200              : !!****f* m_slk/loc_glob__
    2201              : !! NAME
    2202              : !!  loc_glob__
    2203              : !!
    2204              : !! FUNCTION
    2205              : !!  Determine the global index from a local index (row or column) as a function of a given processor
    2206              : !!
    2207              : !! INPUTS
    2208              : !!  matrix= the matrix to process
    2209              : !!  proc= descriptor of a processor
    2210              : !!  idx= number of rows in the distributed matrix
    2211              : !!  lico= block size index. 1 for rows. 2 for columns
    2212              : !!
    2213              : !! SOURCE
    2214              : 
    2215            0 : integer pure function loc_glob__(matrix, proc, idx, lico)
    2216              : 
    2217              : !Arguments ------------------------------------
    2218              :  class(basemat_t),intent(in) :: matrix
    2219              :  class(slk_processor_t),intent(in) :: proc
    2220              :  integer, intent(in) :: idx,lico
    2221              : 
    2222              : !Local variables-------------------------------
    2223              :  integer :: nbcyc, rest, nblocs
    2224              : ! *********************************************************************
    2225              : 
    2226            0 :  nbcyc = INT((idx-1) / matrix%size_blocs(lico))
    2227            0 :  rest = MOD(idx-1, matrix%size_blocs(lico))
    2228            0 :  nblocs = nbcyc * proc%grid%dims(lico) + proc%coords(lico)
    2229              : 
    2230            0 :  loc_glob__ = nblocs * matrix%size_blocs(lico) + rest + 1
    2231              : 
    2232            0 : end function loc_glob__
    2233              : !!***
    2234              : 
    2235              : !----------------------------------------------------------------------
    2236              : 
    2237              : !!****f* m_slk/slkmat_dp_from_global_pack
    2238              : !! NAME
    2239              : !!  slkmat_dp_from_global_pack
    2240              : !!
    2241              : !! FUNCTION
    2242              : !!  Routine to fill a SCALAPACK matrix from a global PACKED matrix.
    2243              : !!
    2244              : !! INPUTS
    2245              : !!  istwf_k= 2 if we have a real matrix else complex.
    2246              : !!  glob_mat_pack= one-dimensional array with packed matrix.
    2247              : !!
    2248              : !! SIDE EFFECTS
    2249              : !!  matrix= the matrix to process
    2250              : !!
    2251              : !! SOURCE
    2252              : 
    2253            0 : subroutine slkmat_dp_from_global_pack(matrix, glob_mat_pack, istwf_k)
    2254              : 
    2255              : !Arguments ------------------------------------
    2256              :  class(slkmat_dp_t),intent(inout) :: matrix
    2257              :  integer,intent(in) :: istwf_k
    2258              :  real(dp),intent(in) :: glob_mat_pack(*)
    2259              : 
    2260              : !Local variables-------------------------------
    2261              :  integer :: i,j,iglob,jglob,ind
    2262              :  real(dp) :: val_real
    2263              :  complex(dp) :: val_cplx
    2264              : ! *********************************************************************
    2265              : 
    2266            0 :  do i=1,matrix%size_local(1)
    2267            0 :    do j=1,matrix%size_local(2)
    2268            0 :      call matrix%loc2glob(i, j, iglob, jglob)
    2269              : 
    2270            0 :      if (istwf_k/=2) then
    2271            0 :        ind = jglob*(jglob-1)+2*iglob-1
    2272            0 :        val_cplx = dcmplx(glob_mat_pack(ind),glob_mat_pack(ind+1))
    2273            0 :        call matrix_set_local_cplx(matrix,i,j,val_cplx)
    2274              :      else
    2275            0 :        ind = (jglob*(jglob-1))/2 + iglob
    2276            0 :        val_real = glob_mat_pack(ind)
    2277            0 :        call matrix_set_local_real(matrix,i,j,val_real)
    2278              :      end if
    2279              : 
    2280              :    end do
    2281              :  end do
    2282              : 
    2283            0 : end subroutine slkmat_dp_from_global_pack
    2284              : !!***
    2285              : 
    2286              : !----------------------------------------------------------------------
    2287              : 
    2288              : !!****f* m_slk/slkmat_dp_from_global_sym
    2289              : !! NAME
    2290              : !!  slkmat_dp_from_global_sym
    2291              : !!
    2292              : !! FUNCTION
    2293              : !!
    2294              : !! INPUTS
    2295              : !!  istwf_k= 2 if we have a real matrix else complex.
    2296              : !!  glob_vec_pack= one-dimensional array in packed form
    2297              : !!
    2298              : !! SIDE EFFECTS
    2299              : !!  matrix= the matrix to process
    2300              : !!
    2301              : !! SOURCE
    2302              : 
    2303            0 : subroutine slkmat_dp_from_global_sym(matrix, glob_vec_pack, istwf_k)
    2304              : 
    2305              : !Arguments ------------------------------------
    2306              :  class(slkmat_dp_t),intent(inout)  :: matrix
    2307              :  real(dp),intent(in) :: glob_vec_pack(:)
    2308              :  integer,intent(in) :: istwf_k
    2309              : 
    2310              : !Local variables-------------------------------
    2311              :  integer :: i,j,iglob,jglob,ind
    2312              :  complex(dp):: val_cplx
    2313              :  real(dp) ::val_real
    2314              : ! *********************************************************************
    2315              : 
    2316            0 :  do i=1,matrix%size_local(1)
    2317            0 :    do j=1,matrix%size_local(2)
    2318            0 :      call matrix%loc2glob(i,j,iglob,jglob)
    2319            0 :      if (jglob < iglob) then
    2320            0 :        ind = iglob*(iglob-1)+2*jglob-1
    2321              :      else
    2322            0 :        ind = jglob*(jglob-1)+2*iglob-1
    2323              :      end if
    2324            0 :      if (istwf_k /= 2) then
    2325            0 :        val_cplx = dcmplx(glob_vec_pack(ind),glob_vec_pack(ind+1))
    2326            0 :        if (jglob < iglob) then
    2327            0 :          call matrix_set_local_cplx(matrix,i,j,conjg(val_cplx))
    2328              :        else
    2329            0 :          call matrix_set_local_cplx(matrix,i,j,val_cplx)
    2330              :        end if
    2331              :      else
    2332            0 :        ind = (ind + 1) / 2
    2333            0 :        val_real = glob_vec_pack(ind)
    2334            0 :        call matrix_set_local_real(matrix,i,j,val_real)
    2335              :      end if
    2336              :    end do
    2337              :  end do
    2338              : 
    2339            0 : end subroutine slkmat_dp_from_global_sym
    2340              : !!***
    2341              : 
    2342              : !----------------------------------------------------------------------
    2343              : 
    2344              : !!****f* m_slk/slkmat_dp_from_real_glob
    2345              : !! NAME
    2346              : !!  slkmat_dp_from_real_glob
    2347              : !!
    2348              : !! FUNCTION
    2349              : !!  Routine to fill a SCALAPACK matrix from a real global matrix (FULL STORAGE MODE)
    2350              : !!
    2351              : !! INPUTS
    2352              : !!  istwf_k= 2 if we have a real matrix else complex.
    2353              : !!  glob_mat= a real matrix
    2354              : !!
    2355              : !! SIDE EFFECTS
    2356              : !!  matrix= the matrix to process
    2357              : !!
    2358              : !! SOURCE
    2359              : 
    2360            0 : subroutine slkmat_dp_from_real_glob(matrix, glob_mat, istwf_k)
    2361              : 
    2362              : !Arguments ------------------------------------
    2363              :  class(slkmat_dp_t),intent(inout) :: matrix
    2364              :  integer,intent(in) :: istwf_k
    2365              : !arrays
    2366              :  real(dp),intent(in) :: glob_mat(:,:)
    2367              : 
    2368              : !Local variables-------------------------------
    2369              :  integer :: i,j,iglob,jglob
    2370              :  real(dp) :: val
    2371              : ! *********************************************************************
    2372              : 
    2373              :  ABI_UNUSED(istwf_k)
    2374              : 
    2375            0 :  do i=1,matrix%size_local(1)
    2376            0 :    do j=1,matrix%size_local(2)
    2377            0 :      call matrix%loc2glob(i, j, iglob, jglob)
    2378            0 :      val = glob_mat(iglob, jglob)
    2379            0 :      call matrix_set_local_real(matrix,i,j,val)
    2380              :    end do
    2381              :  end do
    2382              : 
    2383            0 : end subroutine slkmat_dp_from_real_glob
    2384              : !!***
    2385              : 
    2386              : !----------------------------------------------------------------------
    2387              : 
    2388              : !!****f* m_slk/slkmat_dp_from_complex_glob
    2389              : !! NAME
    2390              : !!  slkmat_dp_from_complex_glob
    2391              : !!
    2392              : !! FUNCTION
    2393              : !!  Routine to fill a SCALAPACK matrix from a global matrix (FULL STORAGE MODE)
    2394              : !!
    2395              : !! INPUTS
    2396              : !!  istwf_k= 2 if we have a real matrix else complex.
    2397              : !!  glob_mat= a complex matrix
    2398              : !!
    2399              : !! SIDE EFFECTS
    2400              : !!  matrix= the matrix to process
    2401              : !!
    2402              : !! SOURCE
    2403              : 
    2404            0 : subroutine slkmat_dp_from_complex_glob(matrix, glob_mat, istwf_k)
    2405              : 
    2406              : !Arguments ------------------------------------
    2407              :  class(slkmat_dp_t),intent(inout) :: matrix
    2408              :  integer,intent(in) :: istwf_k
    2409              : !arrays
    2410              :  real(dp),intent(in) :: glob_mat(:,:)
    2411              : 
    2412              : !Local variables-------------------------------
    2413              :  integer :: i,j,iglob,jglob
    2414              :  complex(dpc) :: val
    2415              : ! *********************************************************************
    2416              : 
    2417              :  ABI_UNUSED(istwf_k)
    2418              : 
    2419            0 :  do i=1,matrix%size_local(1)
    2420            0 :    do j=1,matrix%size_local(2)
    2421            0 :      call matrix%loc2glob(i, j, iglob, jglob)
    2422            0 :      val = dcmplx(glob_mat(2*iglob-1, jglob),glob_mat(2*iglob, jglob))
    2423            0 :      call matrix_set_local_cplx(matrix,i,j,val)
    2424              :    end do
    2425              :  end do
    2426              : 
    2427            0 : end subroutine slkmat_dp_from_complex_glob
    2428              : !!***
    2429              : 
    2430              : !----------------------------------------------------------------------
    2431              : 
    2432              : !!****f* m_slk/slkmat_dp_to_global_pack
    2433              : !! NAME
    2434              : !!  slkmat_dp_to_global_pack
    2435              : !!
    2436              : !! FUNCTION
    2437              : !!  Inserts a ScaLAPACK matrix into a global one in PACKED storage mode.
    2438              : !!
    2439              : !! INPUTS
    2440              : !!  matrix= the matrix to process
    2441              : !!  istwf_k= 2 if we have a real matrix else complex.
    2442              : !!  nband_k= number of bands at this k point for that spin polarization
    2443              : !!
    2444              : !! SIDE EFFECTS
    2445              : !!  glob_pack= one-dimensional array
    2446              : !!
    2447              : !! SOURCE
    2448              : 
    2449            0 : subroutine slkmat_dp_to_global_pack(matrix, glob_pack, istwf_k)
    2450              : 
    2451              : !Arguments ------------------------------------
    2452              :  class(slkmat_dp_t),intent(in) :: matrix
    2453              :  integer,intent(in) :: istwf_k          !,nband_k
    2454              :  real(dp),intent(inout) :: glob_pack(*) !(nband_k*(nband_k+1))
    2455              : 
    2456              : !Local variables-------------------------------
    2457              :  integer  :: i,j,iglob,jglob,ind
    2458              : ! *********************************************************************
    2459              : 
    2460            0 :  do i=1,matrix%size_local(1)
    2461            0 :    do j=1,matrix%size_local(2)
    2462            0 :      call matrix%loc2glob(i, j, iglob, jglob)
    2463              : 
    2464            0 :      ind = jglob*(jglob-1)+2*iglob-1
    2465            0 :      if (ind <= matrix%size_global(2)*(matrix%size_global(2)+1)) then
    2466            0 :         if (istwf_k/=2) then
    2467            0 :          glob_pack(ind)   = real(matrix_get_local_cplx(matrix,i,j))
    2468            0 :          glob_pack(ind+1) = aimag(matrix_get_local_cplx(matrix,i,j))
    2469              :        else
    2470            0 :           ind=(ind+1)/2 !real packed storage
    2471            0 :           glob_pack(ind) = matrix_get_local_real(matrix,i,j)
    2472              :        end if
    2473              :      end if
    2474              :    end do
    2475              :  end do
    2476              : 
    2477            0 : end subroutine slkmat_dp_to_global_pack
    2478              : !!***
    2479              : 
    2480              : !----------------------------------------------------------------------
    2481              : 
    2482              : !!****f* m_slk/slkmat_dp_to_real_glob
    2483              : !! NAME
    2484              : !!  slkmat_dp_to_real_glob
    2485              : !!
    2486              : !! FUNCTION
    2487              : !!  Inserts a ScaLAPACK matrix into a real matrix in FULL STORAGE MODE.
    2488              : !!
    2489              : !! INPUTS
    2490              : !!  matrix= the matrix to process
    2491              : !!  istwf_k= 2 if we have a real matrix else complex.
    2492              : !!
    2493              : !! SIDE EFFECTS
    2494              : !!  glob_mat= the matrix to fill
    2495              : !!
    2496              : !! SOURCE
    2497              : 
    2498            0 : subroutine slkmat_dp_to_real_glob(matrix, glob_mat, istwf_k)
    2499              : 
    2500              : !Arguments ------------------------------------
    2501              :  class(slkmat_dp_t),intent(in) :: matrix
    2502              :  integer,intent(in) :: istwf_k
    2503              : !arrays
    2504              :  real(dp),intent(inout) :: glob_mat(:,:)
    2505              : 
    2506              : !Local variables-------------------------------
    2507              :  integer :: i,j,iglob,jglob
    2508              : ! *********************************************************************
    2509              : 
    2510              :  ABI_UNUSED(istwf_k)
    2511              : 
    2512            0 :  do i=1,matrix%size_local(1)
    2513            0 :    do j=1,matrix%size_local(2)
    2514            0 :      call matrix%loc2glob(i, j, iglob, jglob)
    2515            0 :      glob_mat(iglob,jglob) = matrix_get_local_real(matrix,i,j)
    2516              :    end do
    2517              :  end do
    2518              : 
    2519            0 : end subroutine slkmat_dp_to_real_glob
    2520              : !!***
    2521              : 
    2522              : !----------------------------------------------------------------------
    2523              : 
    2524              : !!****f* m_slk/slkmat_dp_to_complex_glob
    2525              : !! NAME
    2526              : !!  slkmat_dp_to_complex_glob
    2527              : !!
    2528              : !! FUNCTION
    2529              : !!  Inserts a ScaLAPACK matrix into a complex matrix in FULL STORAGE MODE.
    2530              : !!
    2531              : !! INPUTS
    2532              : !!  matrix= the matrix to process
    2533              : !!  istwf_k= 2 if we have a real matrix else complex.
    2534              : !!
    2535              : !! SIDE EFFECTS
    2536              : !!  glob_cmat= the matrix to fill
    2537              : !!
    2538              : !! SOURCE
    2539              : 
    2540            0 : subroutine slkmat_dp_to_complex_glob(matrix, glob_cmat, istwf_k)
    2541              : 
    2542              : !Arguments ------------------------------------
    2543              :  integer,intent(in) :: istwf_k
    2544              :  class(slkmat_dp_t),intent(in) :: matrix
    2545              : !arrays
    2546              :  complex(dpc),intent(inout) :: glob_cmat(:,:)
    2547              : 
    2548              : !Local variables-------------------------------
    2549              :  integer  :: i,j,iglob,jglob
    2550              : ! *********************************************************************
    2551              : 
    2552              :  ABI_UNUSED(istwf_k)
    2553              : 
    2554            0 :  do i=1,matrix%size_local(1)
    2555            0 :    do j=1,matrix%size_local(2)
    2556            0 :      call matrix%loc2glob(i, j, iglob, jglob)
    2557            0 :      glob_cmat(iglob,jglob) = matrix_get_local_cplx(matrix,i,j)
    2558              :    end do
    2559              :  end do
    2560              : 
    2561            0 : end subroutine slkmat_dp_to_complex_glob
    2562              : !!***
    2563              : 
    2564              : !----------------------------------------------------------------------
    2565              : 
    2566              : !!****f* m_slk/slkmat_dp_to_global
    2567              : !! NAME
    2568              : !!  slkmat_dp_to_global
    2569              : !!
    2570              : !! FUNCTION
    2571              : !!  Routine to fill a full matrix with respect to a SCALAPACK matrix.
    2572              : !!
    2573              : !! INPUTS
    2574              : !!  matrix= the matrix to process
    2575              : !!  istwf_k= 2 if we have a real matrix else complex.
    2576              : !!
    2577              : !! SIDE EFFECTS
    2578              : !!  glob_mat= one-dimensional array
    2579              : !!
    2580              : !! SOURCE
    2581              : 
    2582            0 : subroutine slkmat_dp_to_global(matrix, glob_mat, istwf_k)
    2583              : 
    2584              : !Arguments ------------------------------------
    2585              :  class(slkmat_dp_t),intent(in) :: matrix
    2586              :  integer,intent(in) :: istwf_k
    2587              : !arrays
    2588              :  real(dp),intent(inout) :: glob_mat(:,:)
    2589              : 
    2590              : !Local variables-------------------------------
    2591              :  integer  :: i,j,iglob,jglob,ind
    2592              : ! *********************************************************************
    2593              : 
    2594            0 :  do i=1,matrix%size_local(1)
    2595            0 :    do j=1,matrix%size_local(2)
    2596            0 :      call matrix%loc2glob(i, j, iglob, jglob)
    2597              : 
    2598            0 :      if (istwf_k/=2) then
    2599            0 :        ind=(iglob-1)*2+1
    2600            0 :        glob_mat(ind,  jglob) = real(matrix_get_local_cplx(matrix,i,j))
    2601            0 :        glob_mat(ind+1,jglob) = aimag(matrix_get_local_cplx(matrix,i,j))
    2602              :      else
    2603            0 :         ind=iglob
    2604            0 :         glob_mat(ind,jglob) = matrix_get_local_real(matrix,i,j)
    2605              :         !glob_mat(ind+1,jglob) = 0._dp
    2606              :      end if
    2607              : 
    2608              :    end do
    2609              :  end do
    2610              : 
    2611            0 : end subroutine slkmat_dp_to_global
    2612              : !!***
    2613              : 
    2614              : !----------------------------------------------------------------------
    2615              : 
    2616              : !!****f* m_slk/slk_matrix_from_global_dpc_2D
    2617              : !! NAME
    2618              : !!  slk_matrix_from_global_dpc_2D
    2619              : !!
    2620              : !! FUNCTION
    2621              : !!  Routine to fill a complex SCALAPACK matrix with respect to a global matrix.
    2622              : !!  target: Two-dimensional double precision complex matrix
    2623              : !!
    2624              : !! INPUTS
    2625              : !!  glob_cmat=Two-dimensional array containing the global matrix.
    2626              : !!  uplo=String specifying whether only the upper or lower triangular part of the global matrix is used:
    2627              : !!    = "U":  Upper triangular
    2628              : !!    = "L":  Lower triangular
    2629              : !!    = "A":  Full matrix (used for general complex matrices)
    2630              : !!
    2631              : !! SIDE EFFECTS
    2632              : !!  mat<slkmat_dp_t>=The distributed matrix.
    2633              : !!    %buffer_cplx=Local buffer containg the value this node is dealing with.
    2634              : !!
    2635              : !! SOURCE
    2636              : 
    2637            0 : subroutine slk_matrix_from_global_dpc_2D(mat, uplo, glob_cmat)
    2638              : 
    2639              : !Arguments ------------------------------------
    2640              : !scalars
    2641              :  class(slkmat_dp_t),intent(inout)  :: mat
    2642              :  character(len=*),intent(in) :: uplo
    2643              : !array
    2644              :  complex(dpc),intent(in) :: glob_cmat(:,:)
    2645              : 
    2646              : !Local variables-------------------------------
    2647              :  integer :: ii, jj, iglob, jglob
    2648              : !************************************************************************
    2649              : 
    2650            0 :  ABI_CHECK(allocated(mat%buffer_cplx), "%buffer_cplx not allocated")
    2651              : 
    2652            0 :  select case (uplo(1:1))
    2653              : 
    2654              :  case ("A", "a")
    2655              :    ! Full global matrix is used.
    2656            0 :    do jj=1,mat%size_local(2)
    2657            0 :      do ii=1,mat%size_local(1)
    2658            0 :        call mat%loc2glob(ii, jj, iglob, jglob)
    2659            0 :        mat%buffer_cplx(ii,jj) = glob_cmat(iglob,jglob)
    2660              :      end do
    2661              :    end do
    2662              : 
    2663              :  case ("U", "u")
    2664              :    ! Only the upper triangle of the global matrix is used.
    2665            0 :    do jj=1,mat%size_local(2)
    2666            0 :      do ii=1,mat%size_local(1)
    2667            0 :        call mat%loc2glob(ii, jj, iglob, jglob)
    2668            0 :        if (jglob>=iglob) then
    2669            0 :          mat%buffer_cplx(ii,jj) =        glob_cmat(iglob,jglob)
    2670              :        else
    2671            0 :          mat%buffer_cplx(ii,jj) = DCONJG(glob_cmat(jglob,iglob))
    2672              :        end if
    2673              :      end do
    2674              :    end do
    2675              : 
    2676              :  case ("L", "l")
    2677              :    ! Only the lower triangle of the global matrix is used.
    2678            0 :    do jj=1,mat%size_local(2)
    2679            0 :      do ii=1,mat%size_local(1)
    2680            0 :        call mat%loc2glob(ii, jj, iglob, jglob)
    2681            0 :        if (jglob<=iglob) then
    2682            0 :          mat%buffer_cplx(ii,jj) = glob_cmat(iglob,jglob)
    2683              :        else
    2684            0 :          mat%buffer_cplx(ii,jj) = DCONJG(glob_cmat(jglob,iglob))
    2685              :        end if
    2686              :      end do
    2687              :    end do
    2688              : 
    2689              :  case default
    2690            0 :    ABI_BUG(" Wrong uplo: "//TRIM(uplo))
    2691              :  end select
    2692              : 
    2693            0 : end subroutine slk_matrix_from_global_dpc_2D
    2694              : !!***
    2695              : 
    2696              : !----------------------------------------------------------------------
    2697              : 
    2698              : !!****f* m_slk/slk_matrix_from_global_dpc_1Dp
    2699              : !! NAME
    2700              : !!  slk_matrix_from_global_dpc_1Dp
    2701              : !!
    2702              : !! FUNCTION
    2703              : !!  Routine to fill a complex SCALAPACK matrix with respect to a global matrix.
    2704              : !!  target: double precision complex matrix in packed form.
    2705              : !!
    2706              : !! INPUTS
    2707              : !!  glob_cmat_pack(n*(n+1)/2)=One-dimensional array containing the global matrix A packed columnwise in a linear array.
    2708              : !!    The j-th column of A is stored in the array glob_cmat_pack as follows:
    2709              : !!      if uplo = "U", glob_cmat_pack(i + (j-1)*j/2)       = A(i,j) for 1<=i<=j;
    2710              : !!      if uplo = "L", glob_cmat_pack(i + (j-1)*(2*n-j)/2) = A(i,j) for j<=i<=n.
    2711              : !!      where n is the number of rows or columns in the global matrix.
    2712              : !!  uplo=String specifying whether only the upper or lower triangular part of the global matrix is used:
    2713              : !!    = "U":  Upper triangular
    2714              : !!    = "L":  Lower triangular
    2715              : !!
    2716              : !! SIDE EFFECTS
    2717              : !!  mat<slkmat_dp_t>=The distributed matrix.
    2718              : !!    %buffer_cplx=Local buffer containg the value this node is dealing with.
    2719              : !!
    2720              : !! SOURCE
    2721              : 
    2722            0 : subroutine slk_matrix_from_global_dpc_1Dp(mat,uplo, glob_cmat_pack)
    2723              : 
    2724              : !Arguments ------------------------------------
    2725              : !scalars
    2726              :  class(slkmat_dp_t),intent(inout)  :: mat
    2727              :  character(len=*),intent(in) :: uplo
    2728              : !array
    2729              :  complex(dpc),intent(in) :: glob_cmat_pack(:)
    2730              : 
    2731              : !Local variables-------------------------------
    2732              :  integer :: ii,jj,iglob,jglob,ind,n
    2733              :  real(dp) :: szm
    2734              : !************************************************************************
    2735              : 
    2736            0 :  ABI_CHECK(allocated(mat%buffer_cplx), "%buffer_cplx not allocated")
    2737              : 
    2738            0 :  szm = SIZE(glob_cmat_pack)
    2739            0 :  n = NINT( (-1 + SQRT(one+8*szm) )*half )
    2740            0 :  if (n*(n+1)/2 /= SIZE(glob_cmat_pack)) then
    2741            0 :    ABI_ERROR("Buggy compiler")
    2742              :  end if
    2743              : 
    2744            0 :  select case (uplo(1:1))
    2745              : 
    2746              :  case ("U", "u")
    2747              :    ! Only the upper triangle of the global matrix is used.
    2748            0 :    do jj=1,mat%size_local(2)
    2749            0 :      do ii=1,mat%size_local(1)
    2750            0 :        call mat%loc2glob(ii, jj, iglob, jglob)
    2751              : 
    2752            0 :        if (jglob>=iglob) then
    2753            0 :          ind = iglob + jglob*(jglob-1)/2
    2754            0 :          mat%buffer_cplx(ii,jj) = glob_cmat_pack(ind)
    2755              :        else
    2756            0 :          ind = jglob + iglob*(iglob-1)/2
    2757            0 :          mat%buffer_cplx(ii,jj) = DCONJG( glob_cmat_pack(ind) )
    2758              :        end if
    2759              : 
    2760              :      end do
    2761              :    end do
    2762              : 
    2763              :  case ("L", "l")
    2764              :    ! Only the lower triangle of the global matrix is used.
    2765            0 :    do jj=1,mat%size_local(2)
    2766            0 :      do ii=1,mat%size_local(1)
    2767            0 :        call mat%loc2glob(ii, jj, iglob, jglob)
    2768              : 
    2769            0 :        if (jglob<=iglob) then
    2770            0 :          ind = iglob + (jglob-1)*(2*n-jglob)/2
    2771            0 :          mat%buffer_cplx(ii,jj) = glob_cmat_pack(ind)
    2772              :        else
    2773            0 :          ind = jglob + (iglob-1)*(2*n-iglob)/2
    2774            0 :          mat%buffer_cplx(ii,jj) = DCONJG( glob_cmat_pack(ind) )
    2775              :        end if
    2776              :      end do
    2777              :    end do
    2778              : 
    2779              :  case default
    2780            0 :    ABI_BUG(" Wrong uplo: "//TRIM(uplo))
    2781              :  end select
    2782              : 
    2783            0 : end subroutine slk_matrix_from_global_dpc_1Dp
    2784              : !!***
    2785              : 
    2786              : !----------------------------------------------------------------------
    2787              : 
    2788              : !!****f* m_slk/slk_matrix_to_global_dpc_2D
    2789              : !! NAME
    2790              : !!  slk_matrix_to_global_dpc_2D
    2791              : !!
    2792              : !! FUNCTION
    2793              : !!  Fill a global matrix with respect to a SCALAPACK matrix.
    2794              : !!  target: Two-dimensional double precision complex matrix.
    2795              : !!
    2796              : !! INPUTS
    2797              : !!  Slk_mat<slkmat_dp_t>=The distributed matrix.
    2798              : !!  uplo=String specifying whether the upper or lower triangular part of the global matrix has to be filled:
    2799              : !!    = "U":  Upper triangular
    2800              : !!    = "L":  Lower triangular
    2801              : !!    = "A":  Full matrix is filled (used for general complex matrices)
    2802              : !!
    2803              : !! SIDE EFFECTS
    2804              : !!  glob_cmat=The global matrix where the entries owned by this processors have been overwritten.
    2805              : !!  Note that the remaing entries not treated by this node are not changed.
    2806              : !!
    2807              : !! SOURCE
    2808              : 
    2809            0 : subroutine slk_matrix_to_global_dpc_2D(mat, uplo, glob_cmat)
    2810              : 
    2811              : !Arguments ------------------------------------
    2812              : !scalaras
    2813              :  class(slkmat_dp_t),intent(in) :: mat
    2814              :  character(len=*),intent(in) :: uplo
    2815              : !arrays
    2816              :  complex(dpc),intent(inout) :: glob_cmat(:,:)
    2817              : 
    2818              : !Local variables-------------------------------
    2819              :  integer :: ii,jj,iglob,jglob
    2820              : !************************************************************************
    2821              : 
    2822            0 :  select case (uplo(1:1))
    2823              :  case ("A", "a")
    2824              :    ! Full global matrix has to be filled.
    2825            0 :    do jj=1,mat%size_local(2)
    2826            0 :      do ii=1,mat%size_local(1)
    2827            0 :        call mat%loc2glob(ii, jj, iglob, jglob)
    2828            0 :        glob_cmat(iglob,jglob) = mat%buffer_cplx(ii,jj)
    2829              :      end do
    2830              :    end do
    2831              : 
    2832              :  case ("U", "u")
    2833              :    ! Only the upper triangle of the global matrix is filled.
    2834            0 :    do jj=1,mat%size_local(2)
    2835            0 :      do ii=1,mat%size_local(1)
    2836            0 :        call mat%loc2glob(ii, jj, iglob, jglob)
    2837            0 :        if (jglob>=iglob) glob_cmat(iglob,jglob) = mat%buffer_cplx(ii,jj)
    2838              :      end do
    2839              :    end do
    2840              : 
    2841              :  case ("L", "l")
    2842              :    ! Only the lower triangle of the global matrix is filled.
    2843            0 :    do jj=1,mat%size_local(2)
    2844            0 :      do ii=1,mat%size_local(1)
    2845            0 :        call mat%loc2glob(ii, jj, iglob, jglob)
    2846            0 :        if (jglob<=iglob) glob_cmat(iglob,jglob) = mat%buffer_cplx(ii,jj)
    2847              :      end do
    2848              :    end do
    2849              : 
    2850              :  case default
    2851            0 :    ABI_BUG(" Wrong uplo: "//TRIM(uplo))
    2852              :  end select
    2853              : 
    2854            0 : end subroutine slk_matrix_to_global_dpc_2D
    2855              : !!***
    2856              : 
    2857              : !----------------------------------------------------------------------
    2858              : 
    2859              : !!****f* m_slk/my_locr
    2860              : !! NAME
    2861              : !! my_locr
    2862              : !!
    2863              : !! FUNCTION
    2864              : !!  Method of slkmat_dp_t wrapping the scaLAPACK tool LOCr.
    2865              : !!
    2866              : !! OUTPUT
    2867              : !!  my_locr= For the meaning see NOTES below.
    2868              : !!
    2869              : !! NOTES
    2870              : !!  Let K be the number of rows or columns of a distributed matrix, and assume that its process grid has dimension p x q.
    2871              : !!  LOCr( K ) denotes the number of elements of K that a process would receive if K were distributed over the p
    2872              : !!  processes of its process column.
    2873              : !!  Similarly,  LOCc(  K  ) denotes the number of elements of K that a process would receive if K were distributed over
    2874              : !!  the q processes of its process row.
    2875              : !!  The values of LOCr() and LOCc() may be determined via a call to the ScaLAPACK tool function, NUMROC:
    2876              : !!          LOCr( M ) = NUMROC( M, MB_A, MYROW, RSRC_A, NPROW ),
    2877              : !!          LOCc( N ) = NUMROC( N, NB_A, MYCOL, CSRC_A, NPCOL ).  An upper bound for these quantities may  be  computed
    2878              : !!  by:
    2879              : !!          LOCr( M ) <= ceil( ceil(M/MB_A)/NPROW )*MB_A
    2880              : !!          LOCc( N ) <= ceil( ceil(N/NB_A)/NPCOL )*NB_A
    2881              : !!
    2882              : !! SOURCE
    2883              : 
    2884              : integer function my_locr(mat)
    2885              : 
    2886              : !Arguments ------------------------------------
    2887              :  class(basemat_t),intent(in) :: mat
    2888              : 
    2889              : !Local variables-------------------------------
    2890              : #ifdef HAVE_LINALG_SCALAPACK
    2891              :  integer :: M, MB_A, MYROW, RSRC_A, NPROW
    2892              : ! *************************************************************************
    2893              : 
    2894              :  M      = mat%desc(M_ )      ! The number of rows in the global matrix.
    2895              :  MB_A   = mat%desc(MB_)      ! The number of rows in a block.
    2896              :  MYROW  = mat%processor%coords(1)    ! The row index of my processor
    2897              :  RSRC_A = mat%desc(RSRC_)    ! The row of the processors at the beginning.
    2898              :  NPROW  = mat%processor%grid%dims(1) ! The number of processors per row in the Scalapack grid.
    2899              : 
    2900              :  my_locr = NUMROC( M, MB_A, MYROW, RSRC_A, NPROW )
    2901              : #endif
    2902              : 
    2903              : end function my_locr
    2904              : !!***
    2905              : 
    2906              : !----------------------------------------------------------------------
    2907              : 
    2908              : !!****f* m_slk/my_locc
    2909              : !! NAME
    2910              : !! my_locc
    2911              : !!
    2912              : !! FUNCTION
    2913              : !!  Method of slkmat_dp_t wrapping the scaLAPACK tool LOCc.
    2914              : !!
    2915              : !! OUTPUT
    2916              : !!  my_locc= For the meaning see NOTES below.
    2917              : !!
    2918              : !! NOTES
    2919              : !!  Let K be the number of rows or columns of a distributed matrix, and assume that its process grid has dimension p x q.
    2920              : !!  LOCr( K ) denotes the number of elements of K that a process would receive if K were distributed over the p
    2921              : !!  processes of its process column.
    2922              : !!  Similarly,  LOCc(  K  ) denotes the number of elements of K that a process would receive if K were distributed over
    2923              : !!  the q processes of its process row.
    2924              : !!  The values of LOCr() and LOCc() may be determined via a call to the ScaLAPACK tool function, NUMROC:
    2925              : !!          LOCr( M ) = NUMROC( M, MB_A, MYROW, RSRC_A, NPROW ),
    2926              : !!          LOCc( N ) = NUMROC( N, NB_A, MYCOL, CSRC_A, NPCOL ).  An upper bound for these quantities may  be  computed
    2927              : !!  by:
    2928              : !!          LOCr( M ) <= ceil( ceil(M/MB_A)/NPROW )*MB_A
    2929              : !!          LOCc( N ) <= ceil( ceil(N/NB_A)/NPCOL )*NB_A
    2930              : !!
    2931              : !! SOURCE
    2932              : 
    2933              : integer function my_locc(mat)
    2934              : 
    2935              : !Arguments ------------------------------------
    2936              :  class(basemat_t),intent(in) :: mat
    2937              : 
    2938              : #ifdef HAVE_LINALG_SCALAPACK
    2939              : !Local variables-------------------------------
    2940              :  integer :: N, NB_A, MYCOL, CSRC_A, NPCOL
    2941              : ! *************************************************************************
    2942              : 
    2943              :  N      = mat%desc(N_ )              ! The number of columns in the global matrix.
    2944              :  NB_A   = mat%desc(NB_)              ! The number of columns in a block.
    2945              :  MYCOL  = mat%processor%coords(2)    ! The column index of my processor
    2946              :  CSRC_A = mat%desc(CSRC_)            ! The column of the processors at the beginning.
    2947              :  NPCOL  = mat%processor%grid%dims(2) ! The number of processors per column in the Scalapack grid.
    2948              : 
    2949              :  my_locc = NUMROC( N, NB_A, MYCOL, CSRC_A, NPCOL )
    2950              : #endif
    2951              : 
    2952              : end function my_locc
    2953              : !!***
    2954              : 
    2955              : !----------------------------------------------------------------------
    2956              : 
    2957              : !!****f* m_slk/slk_pgemm_dp
    2958              : !! NAME
    2959              : !!  slk_pgemm_dp
    2960              : !!
    2961              : !! FUNCTION
    2962              : !!  Extended matrix * matrix product: C := alpha*A*B + beta*C
    2963              : !!  For a simple matrix vector product, one can simply pass alpha = cone and beta = czero
    2964              : !!
    2965              : !! INPUTS
    2966              : !!  matrix1= first ScaLAPACK matrix (matrix A)
    2967              : !!  matrix2= second ScaLAPACK matrix (matrix B)
    2968              : !!  alpha= scalar multiplicator for the A*B product
    2969              : !!  beta= scalar multiplicator for the C matrix
    2970              : !!  [ija, ijb, ijc]:  (global). The row and column indices in the distributed matrix A/B/C
    2971              : !!    indicating the first row and the first column of the submatrix, respectively. Default [1, 1]
    2972              : !!
    2973              : !! OUTPUT
    2974              : !!  results= ScaLAPACK matrix coming out of the operation
    2975              : !!
    2976              : !! NOTES
    2977              : !! The ESLL manual says that
    2978              : !!      "matrices matrix1 and matrix2 must have no common elements otherwise, results are unpredictable."
    2979              : !! However the official scaLAPACK documentation does not report this (severe) limitation.
    2980              : !!
    2981              : !! SOURCE
    2982              : 
    2983            0 : subroutine slk_pgemm_dp(transa, transb, matrix1, alpha, matrix2, beta, results, &
    2984              :                         ija, ijb, ijc) ! optional
    2985              : 
    2986              : !Arguments ------------------------------------
    2987              :  character(len=1),intent(in) :: transa, transb
    2988              :  class(slkmat_dp_t),intent(in) :: matrix1, matrix2
    2989              :  class(slkmat_dp_t),intent(inout) :: results
    2990              :  complex(dpc),intent(in) :: alpha, beta
    2991              :  integer,optional,intent(in) :: ija(2), ijb(2), ijc(2)
    2992              : 
    2993              : !Local variables-------------------------------
    2994              :  integer :: mm, nn, kk, ija__(2), ijb__(2), ijc__(2)
    2995              : !************************************************************************
    2996              : 
    2997            0 :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    2998            0 :  ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
    2999            0 :  ijc__ = [1, 1]; if (present(ijc)) ijc__ = ijc
    3000              : 
    3001            0 :  mm  = matrix1%size_global(1)
    3002            0 :  nn  = matrix2%size_global(2)
    3003            0 :  kk  = matrix1%size_global(2)
    3004              : 
    3005            0 :  if (toupper(transa) /= 'N') then
    3006              :    mm = matrix1%size_global(2)
    3007            0 :    kk = matrix1%size_global(1)
    3008              :  end if
    3009            0 :  if (toupper(transb) /= 'N') nn = matrix2%size_global(1)
    3010              : 
    3011              : #ifdef HAVE_LINALG_SCALAPACK
    3012              :  ! pzgemm(transa, transb, m, n, k, alpha, a, ia, ja, desca, b, ib, jb, descb, beta, c, ic, jc, descc)
    3013              :  if (matrix1%istwf_k /= 2) then
    3014              :    call PZGEMM(transa, transb, mm, nn, kk, alpha, &
    3015              :                matrix1%buffer_cplx, ija__(1), ija__(2), matrix1%desc, &
    3016              :                matrix2%buffer_cplx, ijb__(1), ijb__(2), matrix2%desc, &
    3017              :                beta, results%buffer_cplx, ijc__(1), ijc__(2), results%desc)
    3018              :  else
    3019              :    call PDGEMM(transa, transb, mm, nn, kk, real(alpha, kind=dp), &
    3020              :                matrix1%buffer_cplx, ija__(1), ija__(2), matrix1%desc, &
    3021              :                matrix2%buffer_cplx, ijb__(1), ijb__(2), matrix2%desc, &
    3022              :                real(beta, kind=dp), results%buffer_cplx, ijc__(1), ijc__(2), results%desc)
    3023              :  end if
    3024              : #endif
    3025              : 
    3026            0 : end subroutine slk_pgemm_dp
    3027              : !!***
    3028              : 
    3029              : !----------------------------------------------------------------------
    3030              : 
    3031              : !!****f* m_slk/slk_pgemm_sp
    3032              : !! NAME
    3033              : !!  slk_pgemm_sp
    3034              : !!
    3035              : !! FUNCTION
    3036              : !!  Extended matrix * matrix product: C := alpha*A*B + beta*C
    3037              : !!  For a simple matrix vector product, one can simply pass alpha = cone and beta = czero
    3038              : !!
    3039              : !! INPUTS
    3040              : !!  matrix1= first ScaLAPACK matrix (matrix A)
    3041              : !!  matrix2= second ScaLAPACK matrix (matrix B)
    3042              : !!  alpha= scalar multiplicator for the A*B product
    3043              : !!  beta= scalar multiplicator for the C matrix
    3044              : !!  [ija, ijb, ijc]:  (global). The row and column indices in the distributed matrix A/B/C
    3045              : !!    indicating the first row and the first column of the submatrix, respectively. Default [1, 1]
    3046              : !!
    3047              : !! OUTPUT
    3048              : !!  results= ScaLAPACK matrix coming out of the operation
    3049              : !!
    3050              : !! NOTES
    3051              : !! The ESLL manual says that
    3052              : !!      "matrices matrix1 and matrix2 must have no common elements otherwise, results are unpredictable."
    3053              : !! However the official scaLAPACK documentation does not report this (severe) limitation.
    3054              : !!
    3055              : !! SOURCE
    3056              : 
    3057            0 : subroutine slk_pgemm_sp(transa, transb, matrix1, alpha, matrix2, beta, results, &
    3058              :                         ija, ijb, ijc) ! optional
    3059              : 
    3060              : !Arguments ------------------------------------
    3061              :  character(len=1),intent(in) :: transa, transb
    3062              :  class(slkmat_sp_t),intent(in) :: matrix1, matrix2
    3063              :  class(slkmat_sp_t),intent(inout) :: results
    3064              :  complex(sp),intent(in) :: alpha, beta
    3065              :  integer,optional,intent(in) :: ija(2), ijb(2), ijc(2)
    3066              : 
    3067              : !Local variables-------------------------------
    3068              :  integer :: mm, nn, kk, ija__(2), ijb__(2), ijc__(2)
    3069              : !************************************************************************
    3070              : 
    3071            0 :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    3072            0 :  ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
    3073            0 :  ijc__ = [1, 1]; if (present(ijc)) ijc__ = ijc
    3074              : 
    3075            0 :  mm  = matrix1%size_global(1)
    3076            0 :  nn  = matrix2%size_global(2)
    3077            0 :  kk  = matrix1%size_global(2)
    3078              : 
    3079            0 :  if (toupper(transa) /= 'N') then
    3080              :    mm = matrix1%size_global(2)
    3081            0 :    kk = matrix1%size_global(1)
    3082              :  end if
    3083            0 :  if (toupper(transb) /= 'N') nn = matrix2%size_global(1)
    3084              : 
    3085              : #ifdef HAVE_LINALG_SCALAPACK
    3086              :  ! pzgemm(transa, transb, m, n, k, alpha, a, ia, ja, desca, b, ib, jb, descb, beta, c, ic, jc, descc)
    3087              :  if (matrix1%istwf_k /= 2) then
    3088              :    call PCGEMM(transa, transb, mm, nn, kk, alpha, &
    3089              :                matrix1%buffer_cplx, ija__(1), ija__(2), matrix1%desc, &
    3090              :                matrix2%buffer_cplx, ijb__(1), ijb__(2), matrix2%desc, &
    3091              :                beta, results%buffer_cplx, ijc__(1), ijc__(2), results%desc)
    3092              :  else
    3093              :    call PSGEMM(transa, transb, mm, nn, kk, real(alpha, kind=sp), &
    3094              :                matrix1%buffer_cplx, ija__(1), ija__(2), matrix1%desc, &
    3095              :                matrix2%buffer_cplx, ijb__(1), ijb__(2), matrix2%desc, &
    3096              :                real(beta, kind=sp), results%buffer_cplx, ijc__(1), ijc__(2), results%desc)
    3097              :  end if
    3098              : #endif
    3099              : 
    3100            0 : end subroutine slk_pgemm_sp
    3101              : !!***
    3102              : 
    3103              : !----------------------------------------------------------------------
    3104              : 
    3105              : !!****f* m_slk/compute_eigen_problem
    3106              : !! NAME
    3107              : !!  compute_eigen_problem
    3108              : !!
    3109              : !! FUNCTION
    3110              : !!  Calculation of eigenvalues and eigenvectors: A * X = lambda * X, complex and real cases.
    3111              : !!
    3112              : !! INPUTS
    3113              : !!  processor= descriptor of a processor
    3114              : !!  matrix= the matrix to process
    3115              : !!  comm= MPI communicator
    3116              : !!  istwf_k= 2 if we have a real matrix else complex.
    3117              : !!  [nev]= Number of eigenvalues needed. Default: full set
    3118              : !!  [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
    3119              : !!
    3120              : !! OUTPUT
    3121              : !!  results= ScaLAPACK matrix coming out of the operation (global dimensions must be equal to matrix
    3122              : !!         even if only a part of the eigenvectors is needed.
    3123              : !!  eigen= eigenvalues of the matrix dimensioned as the global size of the square matrix
    3124              : !!         even if only a part of the eigenvalues is needed.
    3125              : !!
    3126              : !! SOURCE
    3127              : 
    3128            0 : subroutine compute_eigen_problem(processor, matrix, results, eigen, comm, istwf_k, &
    3129              :                                  nev, use_gpu_elpa) ! Optional arguments
    3130              : 
    3131              : !Arguments ------------------------------------
    3132              :  class(slk_processor_t),intent(in) :: processor
    3133              :  class(slkmat_dp_t),intent(inout) :: matrix
    3134              :  class(slkmat_dp_t),intent(inout) :: results
    3135              :  DOUBLE PRECISION,intent(inout) :: eigen(:)
    3136              :  integer,intent(in)  :: comm,istwf_k
    3137              :  integer,optional,intent(in) :: nev
    3138              :  integer,optional,intent(in) :: use_gpu_elpa
    3139              : 
    3140              : #if defined(HAVE_LINALG_ELPA)
    3141              :  !Local variables ------------------------------
    3142              :  type(elpa_hdl_t) :: elpa_hdl
    3143              :  integer :: nev__,use_gpu_elpa_
    3144              : !************************************************************************
    3145              : 
    3146              :   nev__ = matrix%size_global(1); if (present(nev)) nev__ = nev
    3147              :   use_gpu_elpa_=0; if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
    3148              : 
    3149              :   call elpa_func_allocate(elpa_hdl,gpu=use_gpu_elpa_)
    3150              :   call elpa_func_set_matrix(elpa_hdl,matrix%size_global(1),matrix%size_blocs(1),nev__,&
    3151              :                             matrix%size_local(1),matrix%size_local(2))
    3152              :   call elpa_func_get_communicators(elpa_hdl,processor%comm,processor%coords(1),processor%coords(2))
    3153              : 
    3154              :   if (istwf_k/=2) then
    3155              :     call elpa_func_solve_evp_1stage(elpa_hdl,matrix%buffer_cplx,results%buffer_cplx,eigen,nev__)
    3156              :   else
    3157              :     call elpa_func_solve_evp_1stage(elpa_hdl,matrix%buffer_real,results%buffer_real,eigen,nev__)
    3158              :   end if
    3159              : 
    3160              :   call elpa_func_deallocate(elpa_hdl)
    3161              : 
    3162              : 
    3163              : #elif defined(HAVE_LINALG_SLATE)
    3164              :  !Local variables-------------------------------
    3165              :  integer :: nev__,use_gpu_elpa_, info_slate
    3166              : ! *************************************************************************
    3167              : 
    3168              :   if (istwf_k /= 2) then
    3169              :     call slate_zheev_c(                        &
    3170              :       matrix%size_global(1),                   &  ! n
    3171              :       matrix%size_blocs(1),                    &  ! nb  (mb==nb required)
    3172              :       processor%grid%dims(1),                  &  ! nprow
    3173              :       processor%grid%dims(2),                  &  ! npcol
    3174              :       processor%comm,                          &  ! Fortran MPI comm handle
    3175              :       matrix%size_local(1),                    &  ! lda
    3176              :       matrix%buffer_cplx,                      &  ! a_data (destroyed on exit)
    3177              :       results%size_local(1),                   &  ! ldz
    3178              :       results%buffer_cplx,                     &  ! z_data (eigenvectors out)
    3179              :       eigen,                                   &  ! w (eigenvalues out)
    3180              :       nev__,                                   &  ! nev
    3181              :       use_gpu_elpa_,                           &  ! use_gpu (reuse existing flag)
    3182              :       info_slate)
    3183              :     ABI_CHECK(info_slate == 0, "slate_zheev_c failed")
    3184              :   else
    3185              :     call slate_dsyev_c(                        &
    3186              :       matrix%size_global(1),                   &  ! n
    3187              :       matrix%size_blocs(1),                    &  ! nb  (mb==nb required)
    3188              :       processor%grid%dims(1),                  &  ! nprow
    3189              :       processor%grid%dims(2),                  &  ! npcol
    3190              :       processor%comm,                          &  ! Fortran MPI comm handle
    3191              :       matrix%size_local(1),                    &  ! lda
    3192              :       matrix%buffer_real,                      &  ! a_data (destroyed on exit)
    3193              :       results%size_local(1),                   &  ! ldz
    3194              :       results%buffer_real,                     &  ! z_data (eigenvectors out)
    3195              :       eigen,                                   &  ! w (eigenvalues out)
    3196              :       nev__,                                   &  ! nev
    3197              :       use_gpu_elpa_,                           &  ! use_gpu (reuse existing flag)
    3198              :       info_slate)
    3199              :     ABI_CHECK(info_slate == 0, "slate_dsyev_c failed")
    3200              :   end if
    3201              : 
    3202              : 
    3203              : #elif defined(HAVE_LINALG_SCALAPACK)
    3204              :  !Local variables-------------------------------
    3205              :  integer            :: LRWORK,LIWORK,LCWORK,INFO
    3206              :  !character(len=500) :: msg
    3207              :  integer         , dimension(1) :: IWORK_tmp
    3208              :  DOUBLE PRECISION, dimension(1) :: RWORK_tmp
    3209              :  complex(dpc)     , dimension(1) :: CWORK_tmp
    3210              :  integer         , allocatable  :: IWORK(:)
    3211              :  DOUBLE PRECISION, allocatable  :: RWORK(:)
    3212              :  complex(dpc)     , allocatable  :: CWORK(:)
    3213              :  integer,          allocatable :: ICLUSTR(:)
    3214              :  integer,          allocatable :: IFAIL(:)
    3215              :  DOUBLE PRECISION, allocatable :: GAP(:)
    3216              :  DOUBLE PRECISION            :: ABSTOL,ORFAC
    3217              :  integer,          parameter :: IZERO=0
    3218              :  integer ::  M,NZ,ierr,TWORK_tmp(3),TWORK(3) ! IA,JA,IZ,JZ,
    3219              :  integer :: nev__, il, iu, use_gpu_elpa_
    3220              :  character(len=1) :: range
    3221              : ! *************************************************************************
    3222              : 
    3223              :   ABI_UNUSED(use_gpu_elpa) ! No GPU implementation if using scaLAPACK
    3224              :   use_gpu_elpa_=0; if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
    3225              :   nev__ = matrix%size_global(1); range = "A"; il = 0; iu = 0
    3226              :   if (present(nev)) then
    3227              :     nev__ = nev; range = "I"; il = 1; iu = nev
    3228              :   end if
    3229              : 
    3230              :   ! Initialisation
    3231              :   INFO   = 0
    3232              :   ABSTOL = zero
    3233              :   ORFAC  = -1.D+0
    3234              : 
    3235              :   ! Allocation of the variables for the results of the calculations
    3236              :   ABI_MALLOC(IFAIL,(matrix%size_global(2)))
    3237              :   ABI_MALLOC(ICLUSTR,(2*processor%grid%dims(1)*processor%grid%dims(2)))
    3238              :   ABI_MALLOC(GAP,(processor%grid%dims(1)*processor%grid%dims(2)))
    3239              : 
    3240              :   CWORK_tmp = (0.0_DP,0.0_DP)
    3241              :   RWORK_tmp = 0.0_DP
    3242              :   ! Get the size of the work arrays
    3243              :   if (istwf_k/=2) then
    3244              :      call PZHEEVX('V', range, 'U',&
    3245              :       matrix%size_global(2),&
    3246              :       matrix%buffer_cplx,1,1,matrix%desc, &
    3247              :       ZERO,ZERO,il,iu,ABSTOL,&
    3248              :       m,nz,eigen,ORFAC, &
    3249              :       results%buffer_cplx,1,1,results%desc, &
    3250              :       CWORK_tmp,-1,RWORK_tmp,-1,IWORK_tmp,-1,&
    3251              :       IFAIL,ICLUSTR,GAP,INFO)
    3252              :   else
    3253              :      call PDSYEVX('V', range, 'U',&
    3254              :       matrix%size_global(2),&
    3255              :       matrix%buffer_real,1,1,matrix%desc, &
    3256              :       ZERO,ZERO,il,iu,ABSTOL,&
    3257              :       m,nz,eigen,ORFAC, &
    3258              :       results%buffer_real,1,1,results%desc, &
    3259              :       RWORK_tmp,-1,IWORK_tmp,-1,&
    3260              :       IFAIL,ICLUSTR,GAP,INFO)
    3261              :   end if
    3262              : 
    3263              :   if (INFO/=0) then
    3264              :     ABI_ERROR(sjoin("Problem to compute workspace to use ScaLAPACK, INFO: ", itoa(INFO)))
    3265              :   endif
    3266              :   !write(std_out, *)"First call to compute workspace OK"
    3267              : 
    3268              :   TWORK_tmp(1) = IWORK_tmp(1)
    3269              :   TWORK_tmp(2) = INT(RWORK_tmp(1))
    3270              :   TWORK_tmp(3) = INT(real(CWORK_tmp(1)))
    3271              : 
    3272              :   ! Get the maximum of the size of the work arrays processor%comm
    3273              :   call MPI_ALLREDUCE(TWORK_tmp,TWORK,3,MPI_integer,MPI_MAX,comm,ierr)
    3274              : 
    3275              :   LIWORK = TWORK(1)
    3276              :   LRWORK = TWORK(2) + matrix%size_global(2) *(matrix%size_global(2)-1)
    3277              :   LCWORK = TWORK(3)
    3278              : 
    3279              :   ! Allocation of the work arrays
    3280              :   if (LIWORK>0) then
    3281              :     ABI_MALLOC(IWORK,(LIWORK))
    3282              :     IWORK(:) = 0
    3283              :   else
    3284              :     ABI_MALLOC(IWORK,(1))
    3285              :   end if
    3286              :   if (LRWORK>0) then
    3287              :     ! This workspace scales with the number of processors but not always in a simple linear way.
    3288              :     ! and the optimal LRWORK might be pretty big (~1 Gb) even for small systems.
    3289              :     ABI_MALLOC(RWORK,(LRWORK))
    3290              :     RWORK(:) = 0._dp
    3291              :   else
    3292              :     ABI_MALLOC(RWORK,(1))
    3293              :   end if
    3294              :   if (LCWORK>0) then
    3295              :     ABI_MALLOC(CWORK,(LCWORK))
    3296              :     CWORK(:) = (0._dp,0._dp)
    3297              :   else
    3298              :     ABI_MALLOC(CWORK,(1))
    3299              :   end if
    3300              : 
    3301              :   ! prototype
    3302              :   !call pzheevx(jobz, range, uplo, n, a, ia, ja, desca, vl, vu, il, iu, abstol, m, nz, w,
    3303              :   !             orfac, z, iz, jz, descz, work, lwork, rwork, lrwork, iwork, liwork, ifail, iclustr, gap, info)
    3304              : 
    3305              :   ! Call the calculation routine
    3306              :   if (istwf_k/=2) then
    3307              :     ! write(std_out,*) 'I am using PZHEEVX'
    3308              :     call PZHEEVX('V', range, 'U',&
    3309              :      matrix%size_global(2),&
    3310              :      matrix%buffer_cplx,1,1,matrix%desc, &
    3311              :      ZERO,ZERO,il,iu,ABSTOL,&
    3312              :      m,nz,eigen,ORFAC, &
    3313              :      results%buffer_cplx,1,1,results%desc, &
    3314              :      CWORK,LCWORK,RWORK,LRWORK,IWORK,LIWORK,&
    3315              :      IFAIL,ICLUSTR,GAP,INFO)
    3316              :   else
    3317              :     ! write(std_out,*) ' I am using PDSYEVX'
    3318              :     call PDSYEVX('V', range, 'U',&
    3319              :      matrix%size_global(2),&
    3320              :      matrix%buffer_real,1,1,matrix%desc, &
    3321              :      ZERO,ZERO,il,iu,ABSTOL,&
    3322              :      m,nz,eigen,ORFAC, &
    3323              :      results%buffer_real,1,1,results%desc, &
    3324              :      RWORK,LRWORK,IWORK,LIWORK,&
    3325              :      IFAIL,ICLUSTR,GAP,INFO)
    3326              :   endif
    3327              : 
    3328              :   ! MG: TODO: Recheck the computation of the workspace as I got INFO 2 with a 5x5x5 si supercell.
    3329              :   if (INFO/=0) then
    3330              :     ABI_ERROR(sjoin("Problem to compute eigenvalues and eigenvectors with ScaLAPACK, INFO:", itoa(INFO)))
    3331              :   endif
    3332              : 
    3333              :   ABI_FREE(IFAIl)
    3334              :   ABI_FREE(ICLUSTR)
    3335              :   ABI_FREE(GAP)
    3336              :   ABI_SFREE(IWORK)
    3337              :   ABI_SFREE(RWORK)
    3338              :   ABI_SFREE(CWORK)
    3339              : #endif
    3340            0 :   return
    3341              : 
    3342              : end subroutine compute_eigen_problem
    3343              : !!***
    3344              : 
    3345              : !----------------------------------------------------------------------
    3346              : 
    3347              : !!****f* m_slk/solve_gevp_complex
    3348              : !! NAME
    3349              : !!  solve_gevp_complex
    3350              : !!
    3351              : !! FUNCTION
    3352              : !!  Calculation of eigenvalues and eigenvectors: A * X = lambda * B * X
    3353              : !!  complex and real cases.
    3354              : !!
    3355              : !! INPUTS
    3356              : !!  processor= descriptor of a processor
    3357              : !!  matrix1= first ScaLAPACK matrix (matrix A)
    3358              : !!  matrix2= second ScaLAPACK matrix (matrix B)
    3359              : !!  comm= MPI communicator
    3360              : !!  istwf_k= 2 if we have a real matrix else complex.
    3361              : !!  [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
    3362              : !!
    3363              : !! SIDE EFFECTS
    3364              : !!  results= ScaLAPACK matrix coming out of the operation
    3365              : !!  eigen= eigenvalues of the matrix
    3366              : !!
    3367              : !! SOURCE
    3368              : 
    3369              : #ifdef HAVE_LINALG_ELPA
    3370              : 
    3371              : subroutine solve_gevp_complex(na,nev,na_rows,na_cols,nblk,a,b,ev,z,tmp1,tmp2, &
    3372              :                               my_prow,my_pcol,np_rows,np_cols,sc_desc,comm,&
    3373              :                               use_gpu_elpa) ! Optional parameter
    3374              : 
    3375              :   !-Arguments
    3376              :   integer,intent(in) :: na
    3377              :   integer,intent(in) :: nev
    3378              :   integer,intent(in) :: na_rows,na_cols
    3379              :   integer,intent(in) :: nblk
    3380              :   integer,intent(in) :: my_pcol,my_prow
    3381              :   integer,intent(in) :: np_cols,np_rows
    3382              :   integer,intent(in) :: sc_desc(9)
    3383              :   integer,intent(in) :: comm
    3384              :   integer,optional,intent(in) :: use_gpu_elpa
    3385              :   real*8 :: ev(na)
    3386              :   complex*16 :: a(na_rows,na_cols),b(na_rows,na_cols),z(na_rows,na_cols)
    3387              :   complex*16 :: tmp1(na_rows,na_cols),tmp2(na_rows,na_cols)
    3388              :   !-Local variables
    3389              :   integer :: i, n_col, n_row, use_gpu_elpa_
    3390              :   complex*16, parameter :: CZERO = (0.d0,0.d0), CONE = (1.d0,0.d0)
    3391              :   type(elpa_hdl_t) :: elpa_hdl
    3392              : ! *************************************************************************
    3393              : 
    3394              :   use_gpu_elpa_=0
    3395              : #ifdef HAVE_LINALG_ELPA
    3396              :   if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
    3397              : #endif
    3398              : 
    3399              : ! Allocate ELPA handle
    3400              :   call elpa_func_allocate(elpa_hdl,blacs_ctx=sc_desc(CTXT_),gpu=use_gpu_elpa_)
    3401              :   call elpa_func_set_matrix(elpa_hdl,na,nblk,nev,na_rows,na_cols)
    3402              :   call elpa_func_get_communicators(elpa_hdl,comm,my_prow,my_pcol)
    3403              :   call elpa_func_solve_gevp_2stage(elpa_hdl,a,b,z,ev,nev)
    3404              :   call elpa_func_deallocate(elpa_hdl)
    3405              : 
    3406              : end subroutine solve_gevp_complex
    3407              : 
    3408              : !----------------------------------------------------------------------
    3409              : 
    3410              : subroutine solve_gevp_real(na,nev,na_rows,na_cols,nblk,a,b,ev,z,tmp1,tmp2, &
    3411              :                            my_prow,my_pcol,np_rows,np_cols,sc_desc,comm, &
    3412              :                            use_gpu_elpa) ! Optional argument
    3413              : 
    3414              :   !-Arguments
    3415              :   integer,intent(in) :: na
    3416              :   integer,intent(in) :: nev
    3417              :   integer,intent(in) :: na_rows,na_cols
    3418              :   integer,intent(in) :: nblk
    3419              :   integer,intent(in) :: my_pcol,my_prow
    3420              :   integer,intent(in) :: np_cols,np_rows
    3421              :   integer,intent(in) :: sc_desc(9)
    3422              :   integer,intent(in) :: comm
    3423              :   integer,optional,intent(in) :: use_gpu_elpa
    3424              :   real*8 :: ev(na)
    3425              :   real*8 :: a(na_rows,na_cols),b(na_rows,na_cols),z(na_rows,na_cols)
    3426              :   real*8 :: tmp1(na_rows,na_cols),tmp2(na_rows,na_cols)
    3427              :   !-Local variables
    3428              :   integer :: i, n_col, n_row, use_gpu_elpa_
    3429              :   type(elpa_hdl_t) :: elpa_hdl
    3430              : ! *************************************************************************
    3431              : 
    3432              :   use_gpu_elpa_=0
    3433              : #ifdef HAVE_LINALG_ELPA
    3434              :   if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
    3435              : #endif
    3436              : 
    3437              : ! Allocate ELPA handle
    3438              :   call elpa_func_allocate(elpa_hdl,blacs_ctx=sc_desc(CTXT_),gpu=use_gpu_elpa_)
    3439              :   call elpa_func_set_matrix(elpa_hdl,na,nblk,nev,na_rows,na_cols)
    3440              :   call elpa_func_get_communicators(elpa_hdl,comm,my_prow,my_pcol)
    3441              : 
    3442              :   !FIXME Need to figure out why generalized_eigenvectors doesn't work in this real case
    3443              :   !      while it is fine with complex case
    3444              :   if(.false.) then
    3445              :     call elpa_func_solve_gevp_2stage(elpa_hdl,a,b,tmp1,ev,nev)
    3446              :   else
    3447              :     ! 1. Calculate Cholesky factorization of Matrix B = U**T * U
    3448              :     !    and invert triangular matrix U
    3449              :     call elpa_func_cholesky(elpa_hdl,b)
    3450              :     call elpa_func_invert_triangular(elpa_hdl,b)
    3451              :     ! 2. Calculate U**-T * A * U**-1
    3452              :     ! 2a. tmp1 = U**-T * A
    3453              :     call elpa_func_hermitian_multiply(elpa_hdl,'U','L',na,b,a,na_rows,na_cols,tmp1,na_rows,na_cols)
    3454              :     ! 2b. tmp2 = tmp1**T
    3455              :     call pdtran(na,na,1.d0,tmp1,1,1,sc_desc,0.d0,tmp2,1,1,sc_desc)
    3456              :     ! 2c. A =  U**-T * tmp2 ( = U**-T * Aorig * U**-1 )
    3457              :     call elpa_func_hermitian_multiply(elpa_hdl,'U','U',na,b,tmp2,na_rows,na_cols,a,na_rows,na_cols)
    3458              :     ! A is only set in the upper half, solve_evp_real needs a full matrix
    3459              :     ! Set lower half from upper half
    3460              :     call pdtran(na,na,1.d0,a,1,1,sc_desc,0.d0,tmp1,1,1,sc_desc)
    3461              :     do i=1,na_cols
    3462              :        ! Get global column corresponding to i and number of local rows up to
    3463              :        ! and including the diagonal, these are unchanged in A
    3464              :        n_col = indxl2g(i,     nblk, my_pcol, 0, np_cols)
    3465              :        n_row = numroc (n_col, nblk, my_prow, 0, np_rows)
    3466              :        a(n_row+1:na_rows,i) = tmp1(n_row+1:na_rows,i)
    3467              :     enddo
    3468              :     ! 3. Calculate eigenvalues/eigenvectors of U**-T * A * U**-1
    3469              :     !    Eigenvectors go to tmp1
    3470              :     call elpa_func_solve_evp_1stage(elpa_hdl,a,tmp1,ev,nev)
    3471              :     ! 4. Backtransform eigenvectors: Z = U**-1 * tmp1
    3472              :     !    hermitian_multiply needs the transpose of U**-1, thus tmp2 = (U**-1)**T
    3473              :     call pdtran(na,na,1.d0,b,1,1,sc_desc,0.d0,tmp2,1,1,sc_desc)
    3474              :     call elpa_func_hermitian_multiply(elpa_hdl,'L','N',nev,tmp2,tmp1,na_rows,na_cols,z,na_rows,na_cols)
    3475              :   end if
    3476              : 
    3477              :   call elpa_func_deallocate(elpa_hdl)
    3478              : 
    3479              :  end subroutine solve_gevp_real
    3480              : !!***
    3481              : #endif
    3482              : 
    3483              : !----------------------------------------------------------------------
    3484              : 
    3485              : !!****f* m_slk/compute_generalized_eigen_problem
    3486              : !! NAME
    3487              : !!  compute_generalized_eigen_problem
    3488              : !!
    3489              : !! FUNCTION
    3490              : !!  Calculation of eigenvalues and eigenvectors of the generalized eigenvalue problem: A * X = lambda * B X
    3491              : !!  complex and real cases.
    3492              : !!
    3493              : !! INPUTS
    3494              : !!  processor= descriptor of a processor
    3495              : !!  matrix1=  A matrix
    3496              : !!  matrix2=  B matrix
    3497              : !!  comm= MPI communicator
    3498              : !!  istwf_k= 2 if we have a real matrix else complex.
    3499              : !!  [nev]= Number of eigenvalues needed. Default: full set
    3500              : !!  [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
    3501              : !!
    3502              : !! OUTPUT
    3503              : !!  results= ScaLAPACK matrix coming out of the operation (global dimensions must be equal to matrix
    3504              : !!         even if only a part of the eigenvectors is needed.
    3505              : !!  eigen= eigenvalues of the matrix dimensioned as the global size of the square matrix
    3506              : !!         even if only a part of the eigenvalues is needed.
    3507              : !!
    3508              : !! SOURCE
    3509              : 
    3510            0 : subroutine compute_generalized_eigen_problem(processor,matrix1,matrix2,results,eigen,comm,istwf_k,&
    3511              :                                              nev,use_gpu_elpa) ! Optional arguments
    3512              : 
    3513              : !Arguments ------------------------------------
    3514              :   class(slk_processor_t),intent(in)       :: processor
    3515              :   class(slkmat_dp_t),intent(inout)        :: matrix1,matrix2
    3516              :   class(slkmat_dp_t),intent(inout)       :: results
    3517              :   DOUBLE PRECISION,intent(inout) :: eigen(:)
    3518              :   integer,intent(in)  :: comm,istwf_k
    3519              :   integer,optional,intent(in) :: nev
    3520              :   integer,optional,intent(in) :: use_gpu_elpa
    3521              : #ifdef HAVE_LINALG_ELPA
    3522              : !Local
    3523              :   type(slkmat_dp_t) :: tmp1, tmp2
    3524              :   integer :: i,n_col, n_row, nev__,use_gpu_elpa__
    3525              : 
    3526              :   nev__ = matrix1%size_global(2); if (present(nev)) nev__ = nev
    3527              :   use_gpu_elpa__ = 0; if (present(use_gpu_elpa)) use_gpu_elpa__ = use_gpu_elpa
    3528              : 
    3529              :   call tmp1%init(matrix1%size_global(1),matrix1%size_global(2),processor,istwf_k)
    3530              :   call tmp2%init(matrix1%size_global(1),matrix1%size_global(2),processor,istwf_k)
    3531              : 
    3532              :   if (istwf_k/=2) then
    3533              :      call solve_gevp_complex(matrix1%size_global(1), nev__, &
    3534              :            matrix1%size_local(1),matrix1%size_local(2),matrix1%size_blocs(1), &
    3535              :            matrix1%buffer_cplx,matrix2%buffer_cplx,eigen,results%buffer_cplx, &
    3536              :            tmp1%buffer_cplx,tmp2%buffer_cplx, &
    3537              :            processor%coords(1),processor%coords(2), &
    3538              :            processor%grid%dims(1),processor%grid%dims(2), &
    3539              :            matrix1%desc,processor%comm,use_gpu_elpa=use_gpu_elpa__)
    3540              :   else
    3541              :      call solve_gevp_real(matrix1%size_global(1), nev__, &
    3542              :            matrix1%size_local(1),matrix1%size_local(2),matrix1%size_blocs(1), &
    3543              :            matrix1%buffer_real,matrix2%buffer_real,eigen,results%buffer_real, &
    3544              :            tmp1%buffer_real,tmp2%buffer_real, &
    3545              :            processor%coords(1),processor%coords(2), &
    3546              :            processor%grid%dims(1),processor%grid%dims(2), &
    3547              :            matrix1%desc,processor%comm,use_gpu_elpa=use_gpu_elpa__)
    3548              :   end if
    3549              :   call tmp1%free()
    3550              :   call tmp2%free()
    3551              : 
    3552              : 
    3553              : !#elif defined HAVE_LINALG_SLATE
    3554              : !!FIXME Doesn't work. slate_zheev/syev is fine though so we fall back to scaLAPACK instead
    3555              : !!Local variables-------------------------------
    3556              : !  integer :: nev__, use_gpu_elpa_, info_slate
    3557              : !! *************************************************************************
    3558              : !
    3559              : !  nev__ = matrix1%size_global(2); if (present(nev)) nev__ = nev
    3560              : !  use_gpu_elpa__ = 0; if (present(use_gpu_elpa)) use_gpu_elpa__ = use_gpu_elpa
    3561              : !
    3562              : !  if (istwf_k /= 2) then
    3563              : !    call slate_zhegv_c(                        &
    3564              : !      matrix1%size_global(1),                  &  ! n
    3565              : !      matrix1%size_blocs(1),                   &  ! nb  (mb==nb required)
    3566              : !      processor%grid%dims(1),                  &  ! nprow
    3567              : !      processor%grid%dims(2),                  &  ! npcol
    3568              : !      processor%comm,                          &  ! Fortran MPI comm handle
    3569              : !      matrix1%size_local(1),                   &  ! lda
    3570              : !      matrix1%buffer_cplx,                     &  ! a_data (destroyed on exit)
    3571              : !      matrix2%size_local(1),                   &  ! ldb
    3572              : !      matrix2%buffer_cplx,                     &  ! b_data (destroyed on exit)
    3573              : !      results%size_local(1),                   &  ! ldz
    3574              : !      results%buffer_cplx,                     &  ! z_data (eigenvectors out)
    3575              : !      eigen,                                   &  ! w (eigenvalues out)
    3576              : !      nev__,                                   &  ! nev
    3577              : !      use_gpu_elpa_,                           &  ! use_gpu (reuse existing flag)
    3578              : !      info_slate)
    3579              : !    ABI_CHECK(info_slate == 0, "slate_zhgev_c failed")
    3580              : !  else
    3581              : !    call slate_dsygv_c(                        &
    3582              : !      matrix1%size_global(1),                  &  ! n
    3583              : !      matrix1%size_blocs(1),                   &  ! nb  (mb==nb required)
    3584              : !      processor%grid%dims(1),                  &  ! nprow
    3585              : !      processor%grid%dims(2),                  &  ! npcol
    3586              : !      processor%comm,                          &  ! Fortran MPI comm handle
    3587              : !      matrix1%size_local(1),                   &  ! lda
    3588              : !      matrix1%buffer_real,                     &  ! a_data (destroyed on exit)
    3589              : !      matrix2%size_local(1),                   &  ! ldb
    3590              : !      matrix2%buffer_real,                     &  ! b_data (destroyed on exit)
    3591              : !      results%size_local(1),                   &  ! ldz
    3592              : !      results%buffer_real,                     &  ! z_data (eigenvectors out)
    3593              : !      eigen,                                   &  ! w (eigenvalues out)
    3594              : !      nev__,                                   &  ! nev
    3595              : !      use_gpu_elpa_,                           &  ! use_gpu (reuse existing flag)
    3596              : !      info_slate)
    3597              : !    ABI_CHECK(info_slate == 0, "slate_dsygv_c failed")
    3598              : !  end if
    3599              : 
    3600              : 
    3601              : #elif defined(HAVE_LINALG_SCALAPACK)
    3602              : !Local variables-------------------------------
    3603              :   integer            :: LRWORK,LIWORK,LCWORK,INFO
    3604              :   !character(len=500) :: msg
    3605              :   integer         , dimension(1) :: IWORK_tmp
    3606              :   DOUBLE PRECISION, dimension(1) :: RWORK_tmp
    3607              :   complex(dpc)     , dimension(1) :: CWORK_tmp
    3608              : 
    3609              :   integer         , allocatable  :: IWORK(:)
    3610              :   DOUBLE PRECISION, allocatable  :: RWORK(:)
    3611              :   complex(dpc)     , allocatable  :: CWORK(:)
    3612              :   integer,          allocatable :: ICLUSTR(:)
    3613              :   integer,          allocatable :: IFAIL(:)
    3614              :   DOUBLE PRECISION, allocatable :: GAP(:)
    3615              :   DOUBLE PRECISION            :: ABSTOL,ORFAC
    3616              :   integer         , parameter :: IZERO=0
    3617              :   integer ::  M,NZ,ierr,TWORK_tmp(3),TWORK(3) ! IA,JA,IZ,JZ,
    3618              :   character(len=1) :: range
    3619              :   integer :: nev__, il, iu
    3620              : ! *************************************************************************
    3621              : 
    3622              :   ABI_UNUSED(use_gpu_elpa) ! No GPU implementation is using scaLAPACK
    3623              :   nev__ = matrix1%size_global(2); range = "A"; il = 0; iu = 0
    3624              :   if (present(nev)) then
    3625              :     nev__ = nev; range = "I"; il = 1; iu = nev
    3626              :   end if
    3627              : 
    3628              :   ! Initialisation
    3629              :   INFO   = 0
    3630              :   ABSTOL = zero
    3631              :   ORFAC  = -1.D+0
    3632              : 
    3633              :   ! Allocate the arrays for the results of the calculation
    3634              :   ABI_MALLOC(IFAIL  ,(matrix1%size_global(2)))
    3635              :   ABI_MALLOC(ICLUSTR,(2*processor%grid%dims(1)*processor%grid%dims(2)))
    3636              :   ABI_MALLOC(GAP    ,(  processor%grid%dims(1)*processor%grid%dims(2)))
    3637              : 
    3638              :   CWORK_tmp = (0.0_DP,0.0_DP)
    3639              :   RWORK_tmp = 0.0_DP
    3640              :   ! Get the size of the work arrays
    3641              :   if (istwf_k /= 2) then
    3642              :      call PZHEGVX(1, 'V', range, 'U',&
    3643              :        matrix1%size_global(2),&
    3644              :        matrix1%buffer_cplx,1,1,matrix1%desc, &
    3645              :        matrix2%buffer_cplx,1,1,matrix2%desc, &
    3646              :        ZERO,ZERO,il,iu,ABSTOL,&
    3647              :        m,nz,eigen,ORFAC, &
    3648              :        results%buffer_cplx,1,1,results%desc, &
    3649              :        CWORK_tmp,-1,RWORK_tmp,-1,IWORK_tmp,-1,&
    3650              :        IFAIL,ICLUSTR,GAP,INFO)
    3651              :   else
    3652              :      call PDSYGVX(1,'V',range,'U',&
    3653              :        matrix1%size_global(2),&
    3654              :        matrix1%buffer_real,1,1,matrix1%desc, &
    3655              :        matrix2%buffer_real,1,1,matrix2%desc, &
    3656              :        ZERO,ZERO,il,iu,ABSTOL,&
    3657              :        m,nz,eigen,ORFAC, &
    3658              :        results%buffer_real,1,1,results%desc, &
    3659              :        RWORK_tmp,-1,IWORK_tmp,-1,&
    3660              :        IFAIL,ICLUSTR,GAP,INFO)
    3661              :   endif
    3662              : 
    3663              :   if (INFO/=0) then
    3664              :      ABI_ERROR(sjoin("Problem to compute workspace to use ScaLAPACK, INFO=",itoa(INFO)))
    3665              :   endif
    3666              : 
    3667              :   TWORK_tmp(1) = IWORK_tmp(1)
    3668              :   TWORK_tmp(2) = INT(RWORK_tmp(1)) + matrix1%size_global(2) *(matrix1%size_global(2)-1)
    3669              :   TWORK_tmp(3) = INT(real(CWORK_tmp(1)))
    3670              : 
    3671              :  ! Get the maximum of sizes of the work arrays processor%comm
    3672              :   call MPI_ALLREDUCE(TWORK_tmp,TWORK,3,MPI_integer,MPI_MAX,comm,ierr)
    3673              : 
    3674              :   LIWORK = TWORK(1)
    3675              :   LRWORK = TWORK(2)
    3676              :   LCWORK = TWORK(3)
    3677              : 
    3678              :  ! Allocate the work arrays
    3679              :   if (LIWORK>0) then
    3680              :     ABI_MALLOC(IWORK,(LIWORK))
    3681              :     IWORK(:) = 0
    3682              :   else
    3683              :     ABI_MALLOC(IWORK,(1))
    3684              :   end if
    3685              :   if (LRWORK>0) then
    3686              :     ABI_MALLOC(RWORK,(LRWORK))
    3687              :     RWORK(:) = 0._dp
    3688              :   else
    3689              :     ABI_MALLOC(RWORK,(1))
    3690              :   end if
    3691              :   if (LCWORK>0) then
    3692              :     ABI_MALLOC(CWORK,(LCWORK))
    3693              :     CWORK(:) = (0._dp,0._dp)
    3694              :   else
    3695              :     ABI_MALLOC(CWORK,(1))
    3696              :   end if
    3697              : 
    3698              :   ! Call the calculation routine
    3699              :   if (istwf_k/=2) then
    3700              :      ! write(std_out,*) 'I am using PZHEGVX'
    3701              :      call PZHEGVX(1,'V',range,'U',&
    3702              :        matrix1%size_global(2),&
    3703              :        matrix1%buffer_cplx,1,1,matrix1%desc, &
    3704              :        matrix2%buffer_cplx,1,1,matrix2%desc, &
    3705              :        ZERO,ZERO,il,iu,ABSTOL,&
    3706              :        m,nz,eigen,ORFAC, &
    3707              :        results%buffer_cplx,1,1,results%desc, &
    3708              :        CWORK,LCWORK,RWORK,LRWORK,IWORK,LIWORK,&
    3709              :        IFAIL,ICLUSTR,GAP,INFO)
    3710              :   else
    3711              :      ! write(std_out,*) 'I am using PDSYGVX'
    3712              :      call PDSYGVX(1,'V',range,'U',&
    3713              :        matrix1%size_global(2),&
    3714              :        matrix1%buffer_real,1,1,matrix1%desc, &
    3715              :        matrix2%buffer_real,1,1,matrix2%desc, &
    3716              :        ZERO,ZERO,il,iu,ABSTOL,&
    3717              :        m,nz,eigen,ORFAC, &
    3718              :        results%buffer_real,1,1,results%desc, &
    3719              :        RWORK,LRWORK,IWORK,LIWORK,&
    3720              :        IFAIL,ICLUSTR,GAP,INFO)
    3721              :   endif
    3722              : 
    3723              :   if (INFO/=0) then
    3724              :      ABI_ERROR(sjoin("Problem to compute eigen problem with ScaLAPACK, INFO=", itoa(INFO)))
    3725              :   endif
    3726              : 
    3727              :   ABI_FREE(IFAIl)
    3728              :   ABI_FREE(ICLUSTR)
    3729              :   ABI_FREE(GAP)
    3730              :   ABI_SFREE(IWORK)
    3731              :   ABI_SFREE(RWORK)
    3732              :   ABI_SFREE(CWORK)
    3733              : #endif
    3734            0 :   return
    3735              : 
    3736              : end subroutine compute_generalized_eigen_problem
    3737              : !!***
    3738              : 
    3739              : !----------------------------------------------------------------------
    3740              : 
    3741              : !!****f* m_slk/compute_eigen1
    3742              : !! NAME
    3743              : !!  compute_eigen1
    3744              : !!
    3745              : !! FUNCTION
    3746              : !!  Calculation of eigenvalues and eigenvectors. complex and real cases.
    3747              : !!
    3748              : !! INPUTS
    3749              : !!  comm= MPI communicator
    3750              : !!  cplex=1 if matrix is real, 2 if complex
    3751              : !!  nbli_global number of lines
    3752              : !!  nbco_global number of columns
    3753              : !!  matrix= the matrix to process
    3754              : !!  vector= eigenvalues of the matrix
    3755              : !!  istwf_k= 2 if we have a real matrix else complex.
    3756              : !!  [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
    3757              : !!
    3758              : !! OUTPUT
    3759              : !!  vector
    3760              : !!
    3761              : !! SIDE EFFECTS
    3762              : !!  results= ScaLAPACK matrix coming out of the operation
    3763              : !!  eigen= eigenvalues of the matrix
    3764              : !!
    3765              : !! SOURCE
    3766              : 
    3767            0 : subroutine compute_eigen1(comm,processor,cplex,nbli_global,nbco_global,matrix,vector,istwf_k,&
    3768              :                           use_gpu_elpa) ! Optional argument
    3769              : 
    3770              : !Arguments ------------------------------------
    3771              : !scalaras
    3772              :  integer,intent(in) :: comm
    3773              :  integer,intent(in) :: cplex,nbli_global,nbco_global
    3774              :  integer,intent(in) :: istwf_k
    3775              :  class(slk_processor_t),intent(in) :: processor
    3776              :  integer,intent(in),optional :: use_gpu_elpa
    3777              : !arrays
    3778              :  real(dp),intent(inout) :: matrix(cplex*nbli_global,nbco_global)
    3779              :  real(dp),intent(inout) :: vector(:)
    3780              : 
    3781              : !Local variables-------------------------------
    3782              : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
    3783              :  integer :: i,j
    3784              : #endif
    3785              :  integer :: ierr,use_gpu_elpa_
    3786            0 :  type(slkmat_dp_t) :: sca_matrix1
    3787            0 :  type(slkmat_dp_t) :: sca_matrix2
    3788            0 :  real(dp),allocatable :: r_tmp_evec(:,:)
    3789            0 :  complex(dpc),allocatable :: z_tmp_evec(:,:)
    3790              : ! *************************************************************************
    3791              : 
    3792            0 :  use_gpu_elpa_=0
    3793              : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
    3794              :  if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
    3795              : #endif
    3796              : 
    3797              :  ! ================================
    3798              :  ! INITIALISATION SCALAPACK MATRIX
    3799              :  ! ================================
    3800            0 :  call sca_matrix1%init(nbli_global,nbco_global,processor,istwf_k)
    3801            0 :  call sca_matrix2%init(nbli_global,nbco_global,processor,istwf_k)
    3802              : 
    3803              :  ! ==============================
    3804              :  ! FILLING SCALAPACK MATRIX
    3805              :  ! ==============================
    3806            0 :  if ( istwf_k /= 2 ) then
    3807            0 :    ABI_CHECK_IEQ(cplex, 2, "cplex != 2")
    3808            0 :    ABI_MALLOC(z_tmp_evec,(nbli_global,nbco_global))
    3809            0 :    z_tmp_evec=cmplx(0._DP,0._DP)
    3810              : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
    3811              :    ! The full matrix must be set (not only one half like in scalapack).
    3812              :    do j=1,nbco_global
    3813              :       do i=j+1,nbli_global
    3814              :          matrix(2*(i-1)+1,j) = matrix(2*(j-1)+1,i)
    3815              :          matrix(2*(i-1)+2,j) = -matrix(2*(j-1)+2,i)
    3816              :       end do
    3817              :    end do
    3818              : #endif
    3819            0 :    call sca_matrix1%from_complex_glob(matrix,istwf_k)
    3820              :  else
    3821            0 :    ABI_CHECK_IEQ(cplex, 1, "cplex != 2")
    3822            0 :    ABI_MALLOC(r_tmp_evec,(nbli_global,nbco_global))
    3823            0 :    r_tmp_evec(:,:)=0._DP
    3824              : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
    3825              :    ! The full matrix must be set (not only one half like in scalapack).
    3826              :    do j=1,nbco_global
    3827              :       do i=j+1,nbli_global
    3828              :          matrix(i,j) = matrix(j,i)
    3829              :       end do
    3830              :    end do
    3831              : #endif
    3832            0 :    call sca_matrix1%from_real_glob(matrix, istwf_k)
    3833              :  endif
    3834              : 
    3835              :  ! ================================
    3836              :  ! COMPUTE EIGEN VALUES AND VECTORS : A * X = lambda  * X
    3837              :  ! ================================
    3838              :  call compute_eigen_problem(processor,sca_matrix1, sca_matrix2,vector, comm,istwf_k, &
    3839            0 :                             use_gpu_elpa=use_gpu_elpa_)
    3840              : 
    3841              :  ! ==============================
    3842              :  ! CONCATENATE EIGEN VECTORS
    3843              :  ! ==============================
    3844              : #ifdef HAVE_MPI
    3845            0 :  if (istwf_k /= 2) then
    3846            0 :    call sca_matrix2%to_complex_glob(z_tmp_evec, istwf_k)
    3847            0 :    call MPI_ALLREDUCE(z_tmp_evec, matrix, nbli_global*nbco_global, MPI_DOUBLE_complex, MPI_SUM,comm,ierr)
    3848              :  else
    3849            0 :    call sca_matrix2%to_real_glob(r_tmp_evec, istwf_k)
    3850            0 :    call MPI_ALLREDUCE(r_tmp_evec, matrix, nbli_global*nbco_global, MPI_DOUBLE_PRECISION, MPI_SUM,comm,ierr)
    3851              :  endif
    3852              : #endif
    3853              : 
    3854              :  ! ====================================
    3855              :  ! DESTRUCTION SCALAPACK AND TMP MATRICES
    3856              :  ! ====================================
    3857            0 :  call sca_matrix1%free()
    3858            0 :  call sca_matrix2%free()
    3859              : 
    3860            0 :  ABI_SFREE(z_tmp_evec)
    3861            0 :  ABI_SFREE(r_tmp_evec)
    3862              : 
    3863              : #ifndef HAVE_LINALG_ELPA
    3864              :  ABI_UNUSED(use_gpu_elpa)
    3865              : #endif
    3866              : 
    3867            0 : end subroutine compute_eigen1
    3868              : !!***
    3869              : 
    3870              : !----------------------------------------------------------------------
    3871              : 
    3872              : !!****f* m_slk/compute_eigen2
    3873              : !! NAME
    3874              : !!  compute_eigen2
    3875              : !!
    3876              : !! FUNCTION
    3877              : !!  Calculation of eigenvalues and eigenvectors: A * X = lambda * B * X
    3878              : !!  complex and real cases.
    3879              : !!
    3880              : !! INPUTS
    3881              : !!  comm= MPI communicator
    3882              : !!  cplex=1 if matrix is real, 2 if complex
    3883              : !!  nbli_global number of lines
    3884              : !!  nbco_global number of columns
    3885              : !!  matrix1= first ScaLAPACK matrix (matrix A)
    3886              : !!  matrix2= second ScaLAPACK matrix (matrix B)
    3887              : !!  vector=
    3888              : !!  istwf_k= 2 if we have a real matrix else complex.
    3889              : !!  [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
    3890              : !!
    3891              : !! SIDE EFFECTS
    3892              : !!  results= ScaLAPACK matrix coming out of the operation
    3893              : !!  eigen= eigenvalues of the matrix
    3894              : !!
    3895              : !! SOURCE
    3896              : 
    3897            0 : subroutine compute_eigen2(comm,processor,cplex,nbli_global,nbco_global,matrix1,matrix2,vector,istwf_k, &
    3898              :                           use_gpu_elpa) ! Optional argument
    3899              : 
    3900              : !Arguments ------------------------------------
    3901              : !scalars
    3902              :  integer,intent(in) :: cplex,nbli_global,nbco_global
    3903              :  integer,intent(in) :: comm
    3904              :  integer,intent(in) :: istwf_k
    3905              :  class(slk_processor_t),intent(in) :: processor
    3906              :  integer,optional,intent(in) :: use_gpu_elpa
    3907              : !arrays
    3908              :  real(dp),intent(inout) :: matrix1(cplex*nbli_global,nbco_global)
    3909              :  real(dp),intent(inout) :: matrix2(cplex*nbli_global,nbco_global)
    3910              :  real(dp),intent(inout) :: vector(:)
    3911              : 
    3912              : !Local variables-------------------------------
    3913              : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
    3914              :  integer :: i,j
    3915              : #endif
    3916              :  integer :: ierr,use_gpu_elpa_
    3917            0 :  type(slkmat_dp_t) :: sca_matrix1, sca_matrix2, sca_matrix3
    3918            0 :  real(dp),allocatable :: r_tmp_evec(:,:)
    3919            0 :  complex(dpc),allocatable :: z_tmp_evec(:,:)
    3920              : ! *************************************************************************
    3921              : 
    3922            0 :  use_gpu_elpa_=0
    3923              : #if defined HAVE_LINALG_ELPA
    3924              :  if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
    3925              : #endif
    3926              : 
    3927              :  ! ================================
    3928              :  ! INITIALISATION SCALAPACK MATRIX
    3929              :  ! ================================
    3930            0 :  call sca_matrix1%init(nbli_global,nbco_global,processor,istwf_k)
    3931            0 :  call sca_matrix2%init(nbli_global,nbco_global,processor,istwf_k)
    3932            0 :  call sca_matrix3%init(nbli_global,nbco_global,processor,istwf_k)
    3933              : 
    3934              :  ! ==============================
    3935              :  ! FILLING SCALAPACK MATRIX
    3936              :  ! ==============================
    3937            0 :  if ( istwf_k /= 2 ) then
    3938            0 :    ABI_CHECK_IEQ(cplex, 2, "cplex != 2")
    3939            0 :    ABI_MALLOC(z_tmp_evec,(nbli_global,nbco_global))
    3940            0 :    z_tmp_evec=cmplx(0._DP,0._DP)
    3941              : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
    3942              :    ! The full matrix must be set (not only one half like in scalapack).
    3943              :    do j=1,nbco_global
    3944              :       do i=j+1,nbli_global
    3945              :          matrix1(2*(i-1)+1,j) = matrix1(2*(j-1)+1,i)
    3946              :          matrix1(2*(i-1)+2,j) = -matrix1(2*(j-1)+2,i)
    3947              :          matrix2(2*(i-1)+1,j) = matrix2(2*(j-1)+1,i)
    3948              :          matrix2(2*(i-1)+2,j) = -matrix2(2*(j-1)+2,i)
    3949              :       end do
    3950              :    end do
    3951              : #endif
    3952            0 :    call sca_matrix1%from_complex_glob(matrix1, istwf_k)
    3953            0 :    call sca_matrix2%from_complex_glob(matrix2, istwf_k)
    3954              :  else
    3955            0 :    ABI_CHECK_IEQ(cplex, 1, "cplex != 1")
    3956            0 :    ABI_MALLOC(r_tmp_evec,(nbli_global,nbco_global))
    3957            0 :    r_tmp_evec(:,:)=0._DP
    3958              : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
    3959              :    ! The full matrix must be set (not only one half like in scalapack).
    3960              :    do j=1,nbco_global
    3961              :       do i=j+1,nbli_global
    3962              :          matrix1(i,j) = matrix1(j,i)
    3963              :          matrix2(i,j) = matrix2(j,i)
    3964              :       end do
    3965              :    end do
    3966              : #endif
    3967            0 :    call sca_matrix1%from_real_glob(matrix1,istwf_k)
    3968            0 :    call sca_matrix2%from_real_glob(matrix2,istwf_k)
    3969              :  endif
    3970              : 
    3971              :  ! ================================
    3972              :  ! COMPUTE EIGEN VALUES AND VECTORS : A * X = lambda * B * X
    3973              :  ! ================================
    3974              :  call compute_generalized_eigen_problem(processor,sca_matrix1,sca_matrix2,&
    3975            0 :               sca_matrix3,vector,comm,istwf_k,use_gpu_elpa=use_gpu_elpa_)
    3976              : 
    3977              :  ! ==============================
    3978              :  ! CONCATENATE EIGEN VECTORS
    3979              :  ! ==============================
    3980              : #ifdef HAVE_MPI
    3981            0 :  if ( istwf_k /= 2 ) then
    3982            0 :    call sca_matrix3%to_complex_glob(z_tmp_evec,istwf_k)
    3983            0 :    call MPI_ALLREDUCE(z_tmp_evec, matrix1, nbli_global*nbco_global, MPI_DOUBLE_complex, MPI_SUM,comm,ierr)
    3984            0 :    ABI_FREE(z_tmp_evec)
    3985              :  else
    3986            0 :    call sca_matrix3%to_real_glob(r_tmp_evec, istwf_k)
    3987            0 :    call MPI_ALLREDUCE(r_tmp_evec, matrix1, nbli_global*nbco_global, MPI_DOUBLE_PRECISION, MPI_SUM,comm,ierr)
    3988            0 :    ABI_FREE(r_tmp_evec)
    3989              :  endif
    3990              : #endif
    3991              : 
    3992              :  ! ====================================
    3993              :  ! DESTRUCTION SCALAPACK AND TMP MATRICES
    3994              :  ! ====================================
    3995            0 :  call sca_matrix1%free()
    3996            0 :  call sca_matrix2%free()
    3997            0 :  call sca_matrix3%free()
    3998              : 
    3999              : #ifndef HAVE_LINALG_ELPA
    4000              :  ABI_UNUSED(use_gpu_elpa)
    4001              : #endif
    4002              : 
    4003            0 : end subroutine compute_eigen2
    4004              : !!***
    4005              : 
    4006              : !----------------------------------------------------------------------
    4007              : 
    4008              : !!****f* m_slk/slkmat_dp_heev
    4009              : !! NAME
    4010              : !! slkmat_dp_heev
    4011              : !!
    4012              : !! FUNCTION
    4013              : !!  slkmat_dp_heev computes selected eigenvalues and, optionally, eigenvectors of an Hermitian matrix A.
    4014              : !!   A * X = lambda * X
    4015              : !!
    4016              : !! INPUTS
    4017              : !!  JOBZ    (global input) CHARACTER*1
    4018              : !!          Specifies whether or not to compute the eigenvectors:
    4019              : !!          = "N":  Compute eigenvalues only.
    4020              : !!          = "V":  Compute eigenvalues and eigenvectors.
    4021              : !!  UPLO    (global input) CHARACTER*1
    4022              : !!          Specifies whether the upper or lower triangular part of the symmetric matrix A is stored:
    4023              : !!          = "U":  Upper triangular
    4024              : !!          = "L":  Lower triangular
    4025              : !!
    4026              : !!  mat=The object storing the local buffer in DOUBLE PRECISION, the array descriptor, the PBLAS context.
    4027              : !!  vec=The distributed eigenvectors. Not referenced if JOBZ="N"
    4028              : !!
    4029              : !! OUTPUT
    4030              : !!  W       (global output) array, dimension (N) where N is the rank of the global matrix.
    4031              : !!          On normal exit, the first M entries contain the selected eigenvalues in ascending order.
    4032              : !!
    4033              : !! SIDE EFFECTS
    4034              : !!  If JOBZ="V", the local buffer vec%buffer_cplx will contain part of the distributed eigenvectors.
    4035              : !!  On exit, the lower triangle (if UPLO='L') or the upper triangle (if UPLO='U') of A, including the diagonal, is destroyed.
    4036              : !!
    4037              : !! SOURCE
    4038              : 
    4039            0 : subroutine slkmat_dp_heev(mat, jobz, uplo, vec, w, &
    4040              :                     mat_size, ija, ijz) ! Optional
    4041              : 
    4042              : !Arguments ------------------------------------
    4043              : !scalars
    4044              :  class(slkmat_dp_t),intent(inout) :: mat
    4045              :  character(len=*),intent(in) :: jobz, uplo
    4046              :  class(slkmat_dp_t),intent(inout) :: vec
    4047              : !arrays
    4048              :  real(dp),intent(out) :: w(:)
    4049              :  integer,optional,intent(in) :: mat_size, ija(2), ijz(2)
    4050              : 
    4051              : #ifdef HAVE_LINALG_SCALAPACK
    4052              : !Local variables ------------------------------
    4053              : !scalars
    4054              :  integer :: lwork, lrwork, info, nn
    4055              :  !character(len=500) :: msg
    4056              : !arrays
    4057              :  integer :: ija__(2), ijz__(2)
    4058              :  real(dp),allocatable :: rwork_dp(:)
    4059              :  complex(dp),allocatable :: work_dp(:)
    4060              : !************************************************************************
    4061              : 
    4062              :  ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx not allocated")
    4063              : 
    4064              :  nn = mat%size_global(2); if (present(mat_size)) nn = mat_size
    4065              :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    4066              :  ijz__ = [1, 1]; if (present(ijz)) ijz__ = ijz
    4067              : 
    4068              :  ! Get optimal size of workspace.
    4069              :  lwork = - 1; lrwork = -1
    4070              :  ABI_MALLOC(work_dp, (1))
    4071              :  ABI_MALLOC(rwork_dp, (1))
    4072              : 
    4073              :  !call pzheev(jobz, uplo, n, a, ia, ja, desca, w, z, iz, jz, descz, work, lwork, rwork, lrwork, info)
    4074              : 
    4075              :  call PZHEEV(jobz, uplo, nn, mat%buffer_cplx, ija__(1), ija__(2), mat%desc, &
    4076              :              w, vec%buffer_cplx, ijz__(1), ijz__(2), vec%desc, work_dp, lwork, rwork_dp, lrwork, info)
    4077              :  ABI_CHECK(info == 0, sjoin("Error in the calculation of the workspace size, info:", itoa(info)))
    4078              : 
    4079              :  lwork = NINT(real(work_dp(1))); lrwork= NINT(rwork_dp(1)) !*2
    4080              :  ABI_FREE(work_dp)
    4081              :  ABI_FREE(rwork_dp)
    4082              : 
    4083              :  ! MG: Nov 23 2011. On my mac with the official scalapack package, rwork(1) is not large enough and causes a SIGFAULT.
    4084              :  if (firstchar(jobz, ['V'])) then
    4085              :    if (lrwork < 2*nn + 2*nn-2) lrwork = 2*nn + 2*nn-2
    4086              :  else if (firstchar(jobz, ['N'])) then
    4087              :    if (lrwork < 2*nn) lrwork = 2*nn
    4088              :  end if
    4089              :  !write(std_out,*)lwork,lrwork
    4090              : 
    4091              :  ! Solve the problem.
    4092              :  ABI_MALLOC(work_dp, (lwork))
    4093              :  ABI_MALLOC(rwork_dp, (lrwork))
    4094              : 
    4095              :  call PZHEEV(jobz, uplo, nn, mat%buffer_cplx, ija__(1), ija__(2), mat%desc, &
    4096              :              w, vec%buffer_cplx, ijz__(1), ijz__(2), vec%desc, work_dp, lwork, rwork_dp, lrwork, info)
    4097              :  ABI_CHECK(info == 0, sjoin("PZHEEV returned info:", itoa(info)))
    4098              :  ABI_FREE(work_dp)
    4099              :  ABI_FREE(rwork_dp)
    4100              : #endif
    4101              : 
    4102            0 : end subroutine slkmat_dp_heev
    4103              : !!***
    4104              : 
    4105              : !----------------------------------------------------------------------
    4106              : 
    4107              : !!****f* m_slk/slkmat_sp_heev
    4108              : !! NAME
    4109              : !! slkmat_sp_heev
    4110              : !!
    4111              : !! FUNCTION
    4112              : !!  slkmat_sp_heev computes selected eigenvalues and, optionally, eigenvectors of an Hermitian matrix A.
    4113              : !!   A * X = lambda * X
    4114              : !!
    4115              : !! INPUTS
    4116              : !!  JOBZ    (global input) CHARACTER*1
    4117              : !!          Specifies whether or not to compute the eigenvectors:
    4118              : !!          = "N":  Compute eigenvalues only.
    4119              : !!          = "V":  Compute eigenvalues and eigenvectors.
    4120              : !!  UPLO    (global input) CHARACTER*1
    4121              : !!          Specifies whether the upper or lower triangular part of the symmetric matrix A is stored:
    4122              : !!          = "U":  Upper triangular
    4123              : !!          = "L":  Lower triangular
    4124              : !!
    4125              : !!  mat=The object storing the local buffer in SINGLE PRECISION, the array descriptor, the PBLAS context.
    4126              : !!  vec=The distributed eigenvectors. Not referenced if JOBZ="N"
    4127              : !!
    4128              : !! OUTPUT
    4129              : !!  W       (global output) array, dimension (N) where N is the rank of the global matrix.
    4130              : !!          On normal exit, the first M entries contain the selected eigenvalues in ascending order.
    4131              : !!
    4132              : !! SIDE EFFECTS
    4133              : !!  If JOBZ="V", the local buffer vec%buffer_cplx will contain part of the distributed eigenvectors.
    4134              : !!  On exit, the lower triangle (if UPLO='L') or the upper triangle (if UPLO='U') of A, including the diagonal, is destroyed.
    4135              : !!
    4136              : !! SOURCE
    4137              : 
    4138            0 : subroutine slkmat_sp_heev(mat, jobz, uplo, vec, w, &
    4139              :                           mat_size, ija, ijz) ! Optional
    4140              : 
    4141              : !Arguments ------------------------------------
    4142              : !scalars
    4143              :  class(slkmat_sp_t),intent(inout) :: mat
    4144              :  character(len=*),intent(in) :: jobz, uplo
    4145              :  class(slkmat_sp_t),intent(inout) :: vec
    4146              : !arrays
    4147              :  real(sp),intent(out) :: w(:)
    4148              :  integer,optional,intent(in) :: mat_size, ija(2), ijz(2)
    4149              : 
    4150              : #ifdef HAVE_LINALG_SCALAPACK
    4151              : !Local variables ------------------------------
    4152              : !scalars
    4153              :  integer :: lwork, lrwork, info, nn
    4154              : !arrays
    4155              :  integer :: ija__(2), ijz__(2)
    4156              :  real(sp),allocatable :: rwork_sp(:)
    4157              :  complex(sp),allocatable :: work_sp(:)
    4158              : !************************************************************************
    4159              : 
    4160              :  ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx not allocated")
    4161              : 
    4162              :  nn = mat%size_global(2); if (present(mat_size)) nn = mat_size
    4163              :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    4164              :  ijz__ = [1, 1]; if (present(ijz)) ijz__ = ijz
    4165              : 
    4166              :  ! Get optimal size of workspace.
    4167              :  lwork = - 1; lrwork = -1
    4168              :  ABI_MALLOC(work_sp, (1))
    4169              :  ABI_MALLOC(rwork_sp, (1))
    4170              : 
    4171              :  !call pzheev(jobz, uplo, n, a, ia, ja, desca, w, z, iz, jz, descz, work, lwork, rwork, lrwork, info)
    4172              : 
    4173              :  call PCHEEV(jobz, uplo, nn, mat%buffer_cplx, ija__(1), ija__(2), mat%desc, &
    4174              :              w, vec%buffer_cplx, ijz__(1), ijz__(2), vec%desc, work_sp, lwork, rwork_sp, lrwork, info)
    4175              :  ABI_CHECK(info == 0, sjoin("Error in the calculation of the workspace size, info:", itoa(info)))
    4176              : 
    4177              :  lwork = NINT(real(work_sp(1))); lrwork= NINT(rwork_sp(1)) !*2
    4178              :  ABI_FREE(work_sp)
    4179              :  ABI_FREE(rwork_sp)
    4180              : 
    4181              :  ! MG: Nov 23 2011. On my mac with the official scalapack package, rwork(1) is not large enough and causes a SIGFAULT.
    4182              :  if (firstchar(jobz, ['V'])) then
    4183              :    if (lrwork < 2*nn + 2*nn-2) lrwork = 2*nn + 2*nn-2
    4184              :  else if (firstchar(jobz, ['N'])) then
    4185              :    if (lrwork < 2*nn) lrwork = 2*nn
    4186              :  end if
    4187              :  !write(std_out,*)lwork,lrwork
    4188              : 
    4189              :  ! Solve the problem.
    4190              :  ABI_MALLOC(work_sp, (lwork))
    4191              :  ABI_MALLOC(rwork_sp, (lrwork))
    4192              : 
    4193              :  call PCHEEV(jobz, uplo, nn, mat%buffer_cplx, ija__(1), ija__(2), mat%desc, &
    4194              :              w, vec%buffer_cplx, ijz__(1), ijz__(2), vec%desc, work_sp, lwork, rwork_sp, lrwork, info)
    4195              :  ABI_CHECK(info == 0, sjoin("PCHEEV returned info:", itoa(info)))
    4196              :  ABI_FREE(work_sp)
    4197              :  ABI_FREE(rwork_sp)
    4198              : #endif
    4199              : 
    4200            0 : end subroutine slkmat_sp_heev
    4201              : !!***
    4202              : 
    4203              : !----------------------------------------------------------------------
    4204              : 
    4205              : !!****f* m_slk/slkmat_dp_pzheevx
    4206              : !! NAME
    4207              : !!  slkmat_dp_pzheevx
    4208              : !!
    4209              : !! FUNCTION
    4210              : !!  slkmat_dp_pzheevx computes selected eigenvalues and, optionally, eigenvectors of a complex Hermitian matrix A.
    4211              : !!  A * X = lambda * X
    4212              : !!
    4213              : !! INPUTS
    4214              : !!  mat=ScaLAPACK matrix (matrix A)
    4215              : !!
    4216              : !!  vec=The distributed eigenvectors X. Not referenced if JOBZ="N"
    4217              : !!
    4218              : !!  JOBZ    (global input) CHARACTER*1
    4219              : !!          Specifies whether or not to compute the eigenvectors:
    4220              : !!          = "N":  Compute eigenvalues only.
    4221              : !!          = "V":  Compute eigenvalues and eigenvectors.
    4222              : !!
    4223              : !!  RANGE   (global input) CHARACTER*1
    4224              : !!          = "A": all eigenvalues will be found.
    4225              : !!          = "V": all eigenvalues in the interval [VL,VU] will be found.
    4226              : !!          = "I": the IL-th through IU-th eigenvalues will be found.
    4227              : !!
    4228              : !!  UPLO    (global input) CHARACTER*1
    4229              : !!          Specifies whether the upper or lower triangular part of the Hermitian matrix A is stored:
    4230              : !!          = "U":  Upper triangular
    4231              : !!          = "L":  Lower triangular
    4232              : !!
    4233              : !!  VL      (global input) DOUBLE PRECISION
    4234              : !!          If  RANGE="V",the lower bound of the interval to be searched for eigenvalues. Not referenced if RANGE =
    4235              : !!          "A" or "I"
    4236              : !!
    4237              : !!  VU      (global input) DOUBLE PRECISION
    4238              : !!          If RANGE="V", the upper bound of the interval to be searched for eigenvalues.  Not referenced  if  RANGE  =
    4239              : !!          "A" or "I".
    4240              : !!
    4241              : !!  IL     (global input) integer
    4242              : !!         If  RANGE="I",  the  index  (from smallest to largest) of the smallest eigenvalue to be returned.  IL >= 1.
    4243              : !!         Not referenced if RANGE = "A" or "V".
    4244              : !!
    4245              : !!  IU     (global input) integer
    4246              : !!         If RANGE="I", the index (from smallest to largest) of the largest eigenvalue to be returned.  min(IL,N)  <=
    4247              : !!         IU <= N.  Not referenced if RANGE = "A" or "V"
    4248              : !!
    4249              : !!  ABSTOL  (global input) DOUBLE PRECISION
    4250              : !!          If JOBZ="V", setting ABSTOL to PDLAMCH( CONTEXT, "U") yields the most orthogonal eigenvectors.
    4251              : !!          The  absolute error tolerance for the eigenvalues.  An approximate eigenvalue is accepted as converged when
    4252              : !!          it is determined to lie in an interval [a,b] of width less than or equal to
    4253              : !!
    4254              : !!           ABSTOL + EPS *   max( |a|,|b| ) ,
    4255              : !!
    4256              : !!          where EPS is the machine precision.  If ABSTOL is less than or equal to zero, then EPS*norm(T) will be used
    4257              : !!          in  its  place, where norm(T) is the 1-norm of the tridiagonal matrix obtained by reducing A to tridiagonal form.
    4258              : !!          Eigenvalues will be computed  most  accurately  when  ABSTOL  is  set  to  twice  the  underflow  threshold
    4259              : !!          2*PDLAMCH("S")  not  zero.   If  this routine returns with ((MOD(INFO,2).NE.0) .OR.  (MOD(INFO/8,2).NE.0)),
    4260              : !!          indicating that some eigenvalues or eigenvectors did not converge, try setting ABSTOL to 2*PDLAMCH("S").
    4261              : !!
    4262              : !! OUTPUT
    4263              : !!  mene_found= (global output) Total number of eigenvalues found.  0 <= mene_found <= N.
    4264              : !!  eigen(N)= (global output) Eigenvalues of A where N is the dimension of M
    4265              : !!            On normal exit, the first mene_found entries contain the selected eigenvalues in ascending order.
    4266              : !!
    4267              : !! SIDE EFFECTS
    4268              : !!  If JOBZ="V", the local buffer vec%buffer_cplx will contain part of the distributed eigenvectors.
    4269              : !!  Slk%mat%buffer_cplx is destroyed when the routine returns
    4270              : !!
    4271              : !! SOURCE
    4272              : 
    4273            0 : subroutine slkmat_dp_pzheevx(mat, jobz, range, uplo, vl, vu, il, iu, abstol, vec, mene_found, eigen)
    4274              : 
    4275              : !Arguments ------------------------------------
    4276              :  class(slkmat_dp_t),intent(inout) :: mat
    4277              :  integer,intent(in) :: il, iu
    4278              :  integer,intent(out) :: mene_found
    4279              :  real(dp),intent(in) :: abstol,vl,vu
    4280              :  character(len=*),intent(in) :: jobz,range,uplo
    4281              :  class(slkmat_dp_t),intent(inout) :: vec
    4282              : !arrays
    4283              :  real(dp),intent(out) :: eigen(*)
    4284              : 
    4285              : #ifdef HAVE_LINALG_SCALAPACK
    4286              : !Local variables-------------------------------
    4287              : !scalars
    4288              :  integer  :: lwork,lrwork,liwork,info,nvec_calc !,ierr
    4289              :  real(dp) :: orfac
    4290              :  character(len=500) :: msg
    4291              : !arrays
    4292              :  !integer :: ibuff(3),max_ibuff(3)
    4293              :  integer,allocatable  :: iwork(:),iclustr(:),ifail(:)
    4294              :  real(dp),allocatable  :: rwork(:),gap(:)
    4295              :  complex(dpc),allocatable :: work(:)
    4296              : !************************************************************************
    4297              : 
    4298              :  ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx is not allocated!")
    4299              : 
    4300              :  ! abstol = PDLAMCH(vec%processor%grid%comm,'U')
    4301              : 
    4302              :  orfac  = -one ! Only for eigenvectors: use default value 10d-3.
    4303              :  ! Vectors within orfac*norm(A) will be reorthogonalized.
    4304              : 
    4305              :  ! Allocate the arrays for the results of the calculation
    4306              :  ABI_MALLOC(gap, (mat%processor%grid%dims(1) * mat%processor%grid%dims(2)))
    4307              : 
    4308              :  if (firstchar(jobz, ["V","v"])) then
    4309              :    ABI_MALLOC(ifail, (mat%size_global(2)))
    4310              :    ABI_MALLOC(iclustr, (2*mat%processor%grid%dims(1) * mat%processor%grid%dims(2)))
    4311              :  end if
    4312              : 
    4313              :  ! Get the optimal size of the work arrays.
    4314              :  lwork=-1; lrwork=-1; liwork=-1
    4315              :  ABI_MALLOC(work, (1))
    4316              :  ABI_MALLOC(iwork, (1))
    4317              :  ABI_MALLOC(rwork, (3))
    4318              :  ! This is clearly seen in the source in which rwork(1:3) is accessed
    4319              :  ! in the calculation of the workspace size.
    4320              : 
    4321              :   ! prototype
    4322              :   !call pzheevx(jobz, range, uplo, n, a, ia, ja, desca, vl, vu, il, iu, abstol, m, nz, w,
    4323              :   !             orfac, z, iz, jz, descz, work, lwork, rwork, lrwork, iwork, liwork, ifail, iclustr, gap, info)
    4324              : 
    4325              :   call PZHEEVX(jobz,range,uplo, mat%size_global(2),mat%buffer_cplx,1,1,mat%desc,&
    4326              :     vl,vu,il,iu,abstol,mene_found,nvec_calc,eigen,orfac,&
    4327              :     vec%buffer_cplx,1,1,vec%desc,&
    4328              :     work,lwork,rwork,lrwork,iwork,liwork,ifail,iclustr,gap,info)
    4329              : 
    4330              :   ABI_CHECK(info == 0, sjoin("Problem to compute workspace, info:", itoa(info)))
    4331              : 
    4332              :   lwork  = NINT(real(work(1)),kind=dp)
    4333              :   lrwork = NINT(rwork(1))
    4334              :   liwork = iwork(1)
    4335              : 
    4336              :   ABI_FREE(work)
    4337              :   ABI_FREE(rwork)
    4338              :   ABI_FREE(iwork)
    4339              :   !
    4340              :   ! FROM THE SCALAPACK MAN PAGE:
    4341              :   ! The computed eigenvectors may not be orthogonal if the minimal workspace is supplied and ORFAC is too
    4342              :   ! small. If you want to guarantee orthogonality (at the cost of potentially poor performance) you should
    4343              :   ! add the following to LRWORK: (CLUSTERSIZE-1)*N where CLUSTERSIZE is the number of eigenvalues in the
    4344              :   ! largest cluster, where a cluster is defined as a set of close eigenvalues: { W(K),...,W(K+CLUSTERSIZE-1) |
    4345              :   ! W(J+1) <= W(J) + ORFAC*2*norm(A) }.
    4346              : 
    4347              :   if (firstchar(jobz, ["V","v"])) then
    4348              :     lrwork = INT( lrwork + mat%size_global(2) *(mat%size_global(2)-1) )
    4349              :   end if
    4350              : 
    4351              :   ! ibuff(1) = lwork
    4352              :   ! ibuff(2) = lrwork !INT(lrwork + mat%size_global(2) *(mat%size_global(2)-1)
    4353              :   ! ibuff(3) = liwork
    4354              : 
    4355              :   ! Get the maximum of sizes of the work arrays processor%comm
    4356              :   ! call MPI_ALLREDUCE(ibuff,max_ibuff,3,MPI_integer,MPI_MAX,comm,ierr)
    4357              : 
    4358              :   ! lwork  = max_ibuff(1)
    4359              :   ! lrwork = max_ibuff(2)
    4360              :   ! liwork = max_ibuff(3)
    4361              : 
    4362              :   ABI_MALLOC(work , (lwork ))
    4363              :   ABI_MALLOC(rwork, (lrwork))
    4364              :   ABI_MALLOC(iwork, (liwork))
    4365              : 
    4366              :   ! prototype
    4367              :   !call pzheevx(jobz, range, uplo, n, a, ia, ja, desca, vl, vu, il, iu, abstol, m, nz, w,
    4368              :   !             orfac, z, iz, jz, descz, work, lwork, rwork, lrwork, iwork, liwork, ifail, iclustr, gap, info)
    4369              : 
    4370              :   ! Call the scaLAPACK routine.
    4371              :   ! write(std_out,*) 'I am using PZHEEVX'
    4372              :   call PZHEEVX(jobz,range,uplo, mat%size_global(2),mat%buffer_cplx,1,1,mat%desc,&
    4373              :     vl,vu,il,iu,abstol,mene_found,nvec_calc, eigen,orfac,&
    4374              :     vec%buffer_cplx,1,1,vec%desc,&
    4375              :     work,lwork,rwork,lrwork,iwork,liwork,ifail,iclustr,gap,info)
    4376              : 
    4377              :  ! Handle possible error.
    4378              :  if (info < 0) then
    4379              :    write(msg,'(a,i0,a)')" The ", -info, "-th argument of P?HEEVX had an illegal value."
    4380              :    if (info == -25) msg = " LRWORK is too small to compute all the eigenvectors requested, no computation is performed"
    4381              :    ABI_ERROR(msg)
    4382              :  end if
    4383              : 
    4384              :  if (info > 0) then
    4385              :    write(msg,'(a,i0)') " P?HEEVX returned info: ",info
    4386              :    call wrtout(std_out, msg)
    4387              :    if (MOD(info, 2) /= 0)then
    4388              :      write(msg,'(3a)')&
    4389              :      " One or more eigenvectors failed to converge. ",ch10,&
    4390              :      " Their indices are stored in IFAIL. Ensure ABSTOL=2.0*PDLAMCH('U')"
    4391              :      call wrtout(std_out, msg)
    4392              :    end if
    4393              :    if (MOD(info / 2, 2) /= 0) then
    4394              :      write(msg,'(5a)')&
    4395              :      " Eigenvectors corresponding to one or more clusters of eigenvalues ",ch10,&
    4396              :      " could not be reorthogonalized because of insufficient workspace. ",ch10,&
    4397              :      " The indices of the clusters are stored in the array ICLUSTR."
    4398              :      call wrtout(std_out, msg)
    4399              :    end if
    4400              :    if (MOD(info / 4, 2) /= 0) then
    4401              :      write(msg,'(3a)')" Space limit prevented PZHEEVX from computing all of the eigenvectors between VL and VU. ",ch10,&
    4402              :       " The number of eigenvectors computed is returned in NZ."
    4403              :      call wrtout(std_out, msg)
    4404              :    end if
    4405              :    if (MOD(info / 8, 2) /= 0) then
    4406              :      call wrtout(std_out, "PZSTEBZ failed to compute eigenvalues. Ensure ABSTOL=2.0*PDLAMCH('U')")
    4407              :    end if
    4408              :    ABI_ERROR("Cannot continue")
    4409              :  end if
    4410              : 
    4411              :  ! Check the number of eigenvalues found wrt to the number of vectors calculated.
    4412              :  if (firstchar(jobz, ['V','v']) .and. mene_found /= nvec_calc) then
    4413              :    write(msg,'(5a)') &
    4414              :    " The user supplied insufficient space and PZHEEVX is not able to detect this before beginning computation. ",ch10,&
    4415              :    " To get all the eigenvectors requested, the user must supply both sufficient space to hold the ",ch10,&
    4416              :    " eigenvectors in Z (M .LE. DESCZ(N_)) and sufficient workspace to compute them. "
    4417              :    ABI_ERROR(msg)
    4418              :  end if
    4419              : 
    4420              :  ABI_FREE(work)
    4421              :  ABI_FREE(rwork)
    4422              :  ABI_FREE(iwork)
    4423              :  ABI_FREE(gap)
    4424              :  ABI_SFREE(ifail)
    4425              :  ABI_SFREE(iclustr)
    4426              : #endif
    4427              : 
    4428            0 : end subroutine slkmat_dp_pzheevx
    4429              : !!***
    4430              : 
    4431              : !----------------------------------------------------------------------
    4432              : 
    4433              : !!****f* m_slk/slkmat_dp_pzhegvx
    4434              : !! NAME
    4435              : !!  slkmat_dp_pzhegvx
    4436              : !!
    4437              : !! FUNCTION
    4438              : !!  slkmat_dp_pzhegvx provides an object-oriented interface to the ScaLAPACK routine PZHEGVX that
    4439              : !!  computes selected eigenvalues and, optionally, eigenvectors of a complex generalized
    4440              : !!  Hermitian-definite eigenproblem, of the form
    4441              : !!  sub( A )*x=(lambda)*sub( B )*x,  sub( A )*sub( B )x=(lambda)*x,  or sub( B )*sub( A )*x=(lambda)*x.
    4442              : !!  Here sub( A ) denoting A( IA:IA+N-1, JA:JA+N-1 ) is assumed to be
    4443              : !!  Hermitian, and sub( B ) denoting B( IB:IB+N-1, JB:JB+N-1 ) is assumed
    4444              : !!  to be Hermitian positive definite.
    4445              : !!
    4446              : !! INPUTS
    4447              : !!  Slk_matA<slkmat_dp_t>=ScaLAPACK matrix (matrix A)
    4448              : !!  Slk_matB<slkmat_dp_t>=ScaLAPACK matrix (matrix B)
    4449              : !!  Slk_vec<slkmat_dp_t>=The distributed eigenvectors X. Not referenced if JOBZ="N"
    4450              : !!
    4451              : !!  IBtype   (global input) integer
    4452              : !!          Specifies the problem type to be solved:
    4453              : !!          = 1:  sub( A )*x = (lambda)*sub( B )*x
    4454              : !!          = 2:  sub( A )*sub( B )*x = (lambda)*x
    4455              : !!          = 3:  sub( B )*sub( A )*x = (lambda)*x
    4456              : !!
    4457              : !!  JOBZ    (global input) CHARACTER*1
    4458              : !!          Specifies whether or not to compute the eigenvectors:
    4459              : !!          = "N":  Compute eigenvalues only.
    4460              : !!          = "V":  Compute eigenvalues and eigenvectors.
    4461              : !!
    4462              : !!  RANGE   (global input) CHARACTER*1
    4463              : !!          = "A": all eigenvalues will be found.
    4464              : !!          = "V": all eigenvalues in the interval [VL,VU] will be found.
    4465              : !!          = "I": the IL-th through IU-th eigenvalues will be found.
    4466              : !!
    4467              : !!  UPLO    (global input) CHARACTER*1
    4468              : !!          Specifies whether the upper or lower triangular part of the Hermitian matrix sub(A) and sub(B) is stored:
    4469              : !!          = "U":  Upper triangular
    4470              : !!          = "L":  Lower triangular
    4471              : !!
    4472              : !!  VL      (global input) DOUBLE PRECISION
    4473              : !!          If  RANGE="V",the lower bound of the interval to be searched for eigenvalues. Not referenced if RANGE =
    4474              : !!          "A" or "I"
    4475              : !!
    4476              : !!  VU      (global input) DOUBLE PRECISION
    4477              : !!          If RANGE="V", the upper bound of the interval to be searched for eigenvalues.  Not referenced  if  RANGE  =
    4478              : !!          "A" or "I".
    4479              : !!
    4480              : !!  IL     (global input) integer
    4481              : !!         If  RANGE="I",  the  index  (from smallest to largest) of the smallest eigenvalue to be returned.  IL >= 1.
    4482              : !!         Not referenced if RANGE = "A" or "V".
    4483              : !!
    4484              : !!  IU     (global input) integer
    4485              : !!         If RANGE="I", the index (from smallest to largest) of the largest eigenvalue to be returned.  min(IL,N)  <=
    4486              : !!         IU <= N.  Not referenced if RANGE = "A" or "V"
    4487              : !!
    4488              : !!  ABSTOL  (global input) DOUBLE PRECISION
    4489              : !!          If JOBZ="V", setting ABSTOL to PDLAMCH( CONTEXT, "U") yields the most orthogonal eigenvectors.
    4490              : !!          The  absolute error tolerance for the eigenvalues.  An approximate eigenvalue is accepted as converged when
    4491              : !!          it is determined to lie in an interval [a,b] of width less than or equal to
    4492              : !!
    4493              : !!           ABSTOL + EPS *   max( |a|,|b| ) ,
    4494              : !!
    4495              : !!          where EPS is the machine precision.  If ABSTOL is less than or equal to zero, then EPS*norm(T) will be used
    4496              : !!          in  its  place, where norm(T) is the 1-norm of the tridiagonal matrix obtained by reducing A to tridiagonal form.
    4497              : !!          Eigenvalues will be computed  most  accurately  when  ABSTOL  is  set  to  twice  the  underflow  threshold
    4498              : !!          2*PDLAMCH("S")  not  zero.   If  this routine returns with ((MOD(INFO,2).NE.0) .OR.  (MOD(INFO/8,2).NE.0)),
    4499              : !!          indicating that some eigenvalues or eigenvectors did not converge, try setting ABSTOL to 2*PDLAMCH("S").
    4500              : !!
    4501              : !! OUTPUT
    4502              : !!  mene_found= (global output) Total number of eigenvalues found.  0 <= mene_found <= N.
    4503              : !!
    4504              : !!  eigen(N)= (global output) Eigenvalues of A where N is the dimension of M
    4505              : !!            On normal exit, the first mene_found entries contain the selected eigenvalues in ascending order.
    4506              : !!
    4507              : !! SIDE EFFECTS
    4508              : !!  Slk_vec<slkmat_dp_t>:
    4509              : !!   %buffer_cplx local output (global dimension (N,N)
    4510              : !!     If JOBZ = 'V', then on normal exit the first M columns of Z
    4511              : !!     contain the orthonormal eigenvectors of the matrix
    4512              : !!     corresponding to the selected eigenvalues.
    4513              : !!     If JOBZ = 'N', then Z is not referenced.
    4514              : !!
    4515              : !!  Slk_matA<slkmat_dp_t>:
    4516              : !!    %buffer_cplx
    4517              : !!      (local input/local output) complex(DP) pointer into the
    4518              : !!      local memory to an array of dimension (LLD_A, LOCc(JA+N-1)).
    4519              : !!      On entry, this array contains the local pieces of the
    4520              : !!      N-by-N Hermitian distributed matrix sub( A ). If UPLO = 'U',
    4521              : !!      the leading N-by-N upper triangular part of sub( A ) contains
    4522              : !!      the upper triangular part of the matrix.  If UPLO = 'L', the
    4523              : !!      leading N-by-N lower triangular part of sub( A ) contains
    4524              : !!      the lower triangular part of the matrix.
    4525              : !!
    4526              : !!      On exit, if JOBZ = 'V', then if INFO = 0, sub( A ) contains
    4527              : !!      the distributed matrix Z of eigenvectors.  The eigenvectors
    4528              : !!      are normalized as follows:
    4529              : !!      if IBtype = 1 or 2, Z**H*sub( B )*Z = I;
    4530              : !!      if IBtype = 3, Z**H*inv( sub( B ) )*Z = I.
    4531              : !!      If JOBZ = 'N', then on exit the upper triangle (if UPLO='U')
    4532              : !!      or the lower triangle (if UPLO='L') of sub( A ), including
    4533              : !!      the diagonal, is destroyed.
    4534              : !!
    4535              : !!  Slk_matB=
    4536              : !!    %buffer_cplx
    4537              : !!      (local input/local output) complex*(DP) pointer into the
    4538              : !!      local memory to an array of dimension (LLD_B, LOCc(JB+N-1)).
    4539              : !!      On entry, this array contains the local pieces of the
    4540              : !!      N-by-N Hermitian distributed matrix sub( B ). If UPLO = 'U',
    4541              : !!      the leading N-by-N upper triangular part of sub( B ) contains
    4542              : !!      the upper triangular part of the matrix.  If UPLO = 'L', the
    4543              : !!      leading N-by-N lower triangular part of sub( B ) contains
    4544              : !!      the lower triangular part of the matrix.
    4545              : !!
    4546              : !!      On exit, if INFO <= N, the part of sub( B ) containing the
    4547              : !!      matrix is overwritten by the triangular factor U or L from
    4548              : !!      the Cholesky factorization sub( B ) = U**H*U or
    4549              : !!      sub( B ) = L*L**H.
    4550              : !!
    4551              : !! SOURCE
    4552              : 
    4553            0 : subroutine slkmat_dp_pzhegvx(Slk_matA, ibtype, jobz, range, uplo, Slk_matB, vl, vu, il, iu, abstol, Slk_vec, mene_found, eigen)
    4554              : 
    4555              : !Arguments ------------------------------------
    4556              :  class(slkmat_dp_t),intent(inout) :: Slk_matA
    4557              :  integer,intent(in) :: il,iu,ibtype
    4558              :  integer,intent(out) :: mene_found
    4559              :  real(dp),intent(in) :: abstol,vl,vu
    4560              :  character(len=*),intent(in) :: jobz,range,uplo
    4561              :  class(slkmat_dp_t),intent(inout) :: Slk_matB
    4562              :  class(slkmat_dp_t),intent(inout) :: Slk_vec
    4563              : !arrays
    4564              :  real(dp),intent(out) :: eigen(*)
    4565              : 
    4566              : #ifdef HAVE_LINALG_SCALAPACK
    4567              : !Local variables-------------------------------
    4568              : !scalars
    4569              :  integer  :: lwork,lrwork,liwork,info,nvec_calc !,ierr
    4570              :  real(dp) :: orfac
    4571              :  logical :: ltest
    4572              :  character(len=500) :: msg
    4573              : !arrays
    4574              :  !integer :: ibuff(3),max_ibuff(3)
    4575              :  integer :: desca(DLEN_),descb(DLEN_),descz(DLEN_)
    4576              :  integer,allocatable  :: iwork(:),iclustr(:),ifail(:)
    4577              :  real(dp),allocatable  :: rwork(:),gap(:)
    4578              :  complex(dpc),allocatable :: work(:)
    4579              : !************************************************************************
    4580              : 
    4581              :  ABI_CHECK(allocated(Slk_matA%buffer_cplx), "buffer_cplx is not allocated!")
    4582              : 
    4583              :  ! abstol = PDLAMCH(Slk_vecprocessor%grid%comm,'U')
    4584              : 
    4585              :  orfac  = -one ! Only for eigenvectors: use default value 10d-3.
    4586              :  ! Vectors within orfac*norm(A) will be reorthogonalized.
    4587              : 
    4588              :  ! ======================
    4589              :  ! Alignment requirements
    4590              :  ! ======================
    4591              :  ! The distributed submatrices A(IA:*, JA:*), C(IC:IC+M-1,JC:JC+N-1),
    4592              :  ! and B( IB:IB+N-1, JB:JB+N-1 ) must verify some alignment properties,
    4593              : 
    4594              :  desca = Slk_matA%desc
    4595              :  descb = Slk_matB%desc
    4596              :  if (firstchar(jobz, ["V", "v"])) then
    4597              :    descz = Slk_vec%desc
    4598              :  else
    4599              :    descz = Slk_matA%desc
    4600              :  end if
    4601              : 
    4602              :  ltest = .TRUE.
    4603              :  ltest = ltest .and. (DESCA(MB_) == DESCA(NB_))
    4604              :  !IA = IB = IZ
    4605              :  !JA = IB = JZ
    4606              :  ltest = ltest .and.ALL(DESCA(M_   ) == [DESCB(M_   ), DESCZ(M_   )])
    4607              :  ltest = ltest .and.ALL(DESCA(N_   ) == [DESCB(N_   ), DESCZ(N_   )])
    4608              :  ltest = ltest .and.ALL(DESCA(MB_  ) == [DESCB(MB_  ), DESCZ(MB_  )])
    4609              :  ltest = ltest .and.ALL(DESCA(NB_  ) == [DESCB(NB_  ), DESCZ(NB_  )])
    4610              :  ltest = ltest .and.ALL(DESCA(RSRC_) == [DESCB(RSRC_), DESCZ(RSRC_)])
    4611              :  ltest = ltest .and.ALL(DESCA(CSRC_) == [DESCB(CSRC_), DESCZ(CSRC_)])
    4612              :  !MOD( IA-1, DESCA( MB_ ) ) = 0
    4613              :  !MOD( JA-1, DESCA( NB_ ) ) = 0
    4614              :  !MOD( IB-1, DESCB( MB_ ) ) = 0
    4615              :  !MOD( JB-1, DESCB( NB_ ) ) = 0
    4616              : 
    4617              :  if (.not.ltest) then
    4618              :    ABI_ERROR("Alignment requirements not satisfied, check the caller")
    4619              :  end if
    4620              : 
    4621              :  !Allocate the arrays for the results of the calculation
    4622              :  ABI_MALLOC(gap, (Slk_matA%processor%grid%dims(1) * Slk_matA%processor%grid%dims(2)))
    4623              : 
    4624              :  if (firstchar(jobz, ["V","v"])) then
    4625              :    ABI_MALLOC(ifail,(Slk_matA%size_global(2)))
    4626              :    ABI_MALLOC(iclustr,( 2*Slk_matA%processor%grid%dims(1) * Slk_matA%processor%grid%dims(2)))
    4627              :  else
    4628              :    ABI_MALLOC(ifail,(1))
    4629              :  end if
    4630              : 
    4631              :  ! Get the optimal size of the work arrays.
    4632              :  lwork=-1; lrwork=-1; liwork=-1
    4633              :  ABI_MALLOC(work,(1))
    4634              :  ABI_MALLOC(iwork,(1))
    4635              :  ABI_MALLOC(rwork,(3))
    4636              :  ! This is clearly seen in the source in which rwork(1:3) is accessed
    4637              :  ! in the calcuation of the workspace size.
    4638              : 
    4639              :  call pzhegvx(ibtype,jobz,range,uplo, Slk_matA%size_global(2),Slk_matA%buffer_cplx,1,1,Slk_matA%desc,&
    4640              :    Slk_matB%buffer_cplx,1,1,Slk_matB%desc,&
    4641              :    vl,vu,il,iu,abstol,mene_found,nvec_calc,eigen,orfac,&
    4642              :    Slk_vec%buffer_cplx,1,1,Slk_vec%desc,&
    4643              :    work,lwork,rwork,lrwork,iwork,liwork,ifail,iclustr,gap,info)
    4644              : 
    4645              :  ABI_CHECK(info == 0, sjoin("Problem to compute workspace, info:", itoa(info)))
    4646              : 
    4647              :  lwork  = NINT(real(work(1)),kind=dp)
    4648              :  lrwork = NINT(rwork(1))
    4649              :  liwork = iwork(1)
    4650              : 
    4651              :  ABI_FREE(work)
    4652              :  ABI_FREE(rwork)
    4653              :  ABI_FREE(iwork)
    4654              : 
    4655              :  !FROM THE SCALAPACK MAN PAGE:
    4656              :  !The computed eigenvectors may not be orthogonal if the minimal workspace is supplied and ORFAC is too
    4657              :  !small. If you  want to guarantee orthogonality (at the cost of potentially poor performance) you should
    4658              :  !add the following to LRWORK: (CLUSTERSIZE-1)*N where CLUSTERSIZE is  the  number  of  eigenvalues  in  the
    4659              :  !largest cluster, where a cluster is defined as a set of close eigenvalues: { W(K),...,W(K+CLUSTERSIZE-1) |
    4660              :  !W(J+1) <= W(J) + ORFAC*2*norm(A) }.
    4661              : 
    4662              :  if (firstchar(jobz, ["V","v"])) then
    4663              :    lrwork = INT( lrwork + Slk_matA%size_global(2) *(Slk_matA%size_global(2)-1) )
    4664              :  end if
    4665              : 
    4666              :  !ibuff(1) = lwork
    4667              :  !ibuff(2) = lrwork !INT(lrwork + Slk_matA%size_global(2) *(Slk_matA%size_global(2)-1)
    4668              :  !ibuff(3) = liwork
    4669              : 
    4670              :  !Get the maximum of sizes of the work arrays processor%comm
    4671              :  !call MPI_ALLREDUCE(ibuff,max_ibuff,3,MPI_integer,MPI_MAX,comm,ierr)
    4672              : 
    4673              :  !lwork  = max_ibuff(1)
    4674              :  !lrwork = max_ibuff(2)
    4675              :  !liwork = max_ibuff(3)
    4676              : 
    4677              :  ABI_MALLOC(work , (lwork ))
    4678              :  ABI_MALLOC(rwork, (lrwork))
    4679              :  ABI_MALLOC(iwork, (liwork))
    4680              : 
    4681              :  ! Call the scaLAPACK routine.
    4682              :  ! write(std_out,*) 'I am using PZHEGVX'
    4683              :  call pzhegvx(ibtype,jobz,range,uplo, Slk_matA%size_global(2),Slk_matA%buffer_cplx,1,1,Slk_matA%desc,&
    4684              :     Slk_matB%buffer_cplx,1,1,Slk_matB%desc,&
    4685              :     vl,vu,il,iu,abstol,mene_found,nvec_calc, eigen,orfac,&
    4686              :     Slk_vec%buffer_cplx,1,1,Slk_vec%desc,&
    4687              :     work,lwork,rwork,lrwork,iwork,liwork,ifail,iclustr,gap,info)
    4688              : 
    4689              :  ! Handle the possible error.
    4690              :  if (info < 0) then
    4691              :    write(msg,'(a,i0,a)')" The ",-info,"-th argument of PZHEGVX had an illegal value."
    4692              :    if (info==-25) msg = " LRWORK is too small to compute all the eigenvectors requested, no computation is performed"
    4693              :    ABI_ERROR(msg)
    4694              :  end if
    4695              : 
    4696              :  if (info > 0) then
    4697              :    write(msg,'(a,i0)') " PZHEGVX returned info: ",info
    4698              :    call wrtout(std_out, msg)
    4699              :    if (MOD(info,2)/=0)then
    4700              :      write(msg,'(3a)')&
    4701              :      " One or more eigenvectors failed to converge. ",ch10,&
    4702              :      " Their indices are stored in IFAIL. Ensure ABSTOL=2.0*PDLAMCH('U')"
    4703              :      call wrtout(std_out, msg)
    4704              :    end if
    4705              :    if (MOD(info / 2, 2) /= 0) then
    4706              :      write(msg,'(5a)')&
    4707              :      " Eigenvectors corresponding to one or more clusters of eigenvalues ",ch10,&
    4708              :      " could not be reorthogonalized because of insufficient workspace. ",ch10,&
    4709              :      " The indices of the clusters are stored in the array ICLUSTR."
    4710              :      call wrtout(std_out, msg)
    4711              :    end if
    4712              :    if (MOD(info / 4, 2) /= 0) then
    4713              :      write(msg,'(3a)')&
    4714              :      " Space limit prevented PZHEGVX from computing all of the eigenvectors between VL and VU. ",ch10,&
    4715              :      " The number of eigenvectors  computed  is returned in NZ."
    4716              :      call wrtout(std_out, msg)
    4717              :    end if
    4718              :    if (MOD(info / 8, 2) /= 0) then
    4719              :      msg = " PZSTEBZ  failed to compute eigenvalues. Ensure ABSTOL=2.0*PDLAMCH('U')"
    4720              :      call wrtout(std_out, msg)
    4721              :    end if
    4722              :    if (MOD(info / 16, 2) /= 0) then
    4723              :      write(msg,'(3a)')&
    4724              :      " B was not positive definite.",ch10,&
    4725              :      " IFAIL(1) indicates the order of the smallest minor which is not positive definite."
    4726              :      call wrtout(std_out, msg)
    4727              :    end if
    4728              :    ABI_ERROR("Cannot continue")
    4729              :  end if
    4730              : 
    4731              :  ! Check the number of eigenvalues found wrt to the number of vectors calculated.
    4732              :  if ( firstchar(jobz, ['V','v']) .and. mene_found/=nvec_calc) then
    4733              :    write(msg,'(5a)')&
    4734              :    " The user supplied insufficient space and PZHEGVX is not able to detect this before beginning computation. ",ch10,&
    4735              :    " To get all the  eigenvectors requested, the user must supply both sufficient space to hold the ",ch10,&
    4736              :    " eigenvectors in Z (M .LE. DESCZ(N_)) and sufficient workspace to compute them. "
    4737              :    ABI_ERROR(msg)
    4738              :  end if
    4739              : 
    4740              :  ABI_FREE(work)
    4741              :  ABI_FREE(rwork)
    4742              :  ABI_FREE(iwork)
    4743              :  ABI_FREE(gap)
    4744              :  ABI_FREE(ifail)
    4745              :  ABI_SFREE(iclustr)
    4746              : #endif
    4747              : 
    4748            0 : end subroutine slkmat_dp_pzhegvx
    4749              : !!***
    4750              : 
    4751              : !----------------------------------------------------------------------
    4752              : 
    4753              : !!****f* m_slk/basemat_invert
    4754              : !! NAME
    4755              : !! basemat_invert
    4756              : !!
    4757              : !! FUNCTION
    4758              : !!  Compute the inverse of a complex matrix.
    4759              : !!
    4760              : !! SIDE EFFECTS
    4761              : !! mat
    4762              : !!    In input, the matrix to invert.
    4763              : !!    In output the matrix inverted and distributed among the nodes.
    4764              : !!
    4765              : !! SOURCE
    4766              : 
    4767            0 : subroutine basemat_invert(mat)
    4768              : 
    4769              : !Arguments ------------------------------------
    4770              :  class(basemat_t),intent(inout) :: mat
    4771              : 
    4772              : #ifdef HAVE_LINALG_SCALAPACK
    4773              : !Local variables ------------------------------
    4774              : !scalars
    4775              :  integer :: lwork,info,ipiv_size,liwork
    4776              : !array
    4777              :  integer,allocatable :: ipiv(:), iwork(:)
    4778              :  complex(dp),allocatable :: work_dp(:)
    4779              :  complex(sp),allocatable :: work_sp(:)
    4780              : !************************************************************************
    4781              : 
    4782              :  if (mat%desc(MB_) /= mat%desc(NB_)) then
    4783              :    ABI_ERROR("PZGETRF requires square block decomposition i.e MB_A = NB_A.")
    4784              :  end if
    4785              : 
    4786              :  ipiv_size = my_locr(mat) + mat%desc(MB_)
    4787              :  ABI_MALLOC(ipiv, (ipiv_size))
    4788              : 
    4789              :  select type (mat)
    4790              :  class is (slkmat_dp_t)
    4791              :    if (allocated(mat%buffer_cplx)) then
    4792              :      ! P * L * U  Factorization.
    4793              :      call PZGETRF(mat%size_global(1), mat%size_global(2), mat%buffer_cplx, 1, 1, mat%desc,ipiv, info)
    4794              :      ABI_CHECK(info == 0, sjoin(" PZGETRF returned info:", itoa(info)))
    4795              : 
    4796              :      ! Get optimal size of workspace for PZGETRI.
    4797              :      lwork = -1; liwork = -1
    4798              :      ABI_MALLOC(work_dp,(1))
    4799              :      ABI_MALLOC(iwork,(1))
    4800              : 
    4801              :      call PZGETRI(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc, ipiv, work_dp, lwork, iwork, liwork, info)
    4802              :      ABI_CHECK(info == 0, "PZGETRI: Error while computing workspace size")
    4803              : 
    4804              :      lwork = nint(real(work_dp(1))); liwork=iwork(1)
    4805              :      ABI_FREE(work_dp)
    4806              :      ABI_FREE(iwork)
    4807              : 
    4808              :      ! Solve the problem.
    4809              :      ABI_MALLOC(work_dp, (lwork))
    4810              :      ABI_MALLOC(iwork, (liwork))
    4811              : 
    4812              :      call PZGETRI(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc, ipiv, work_dp, lwork, iwork, liwork, info)
    4813              :      ABI_CHECK(info == 0, sjoin("PZGETRI returned info:", itoa(info)))
    4814              :      ABI_FREE(work_dp)
    4815              : 
    4816              :    else if (allocated(mat%buffer_real)) then
    4817              :      ABI_ERROR("Inversion for real matrices not coded!")
    4818              :    end if
    4819              : 
    4820              :  class is (slkmat_sp_t)
    4821              :    if (allocated(mat%buffer_cplx)) then
    4822              :      ! P * L * U  Factorization.
    4823              :      call PCGETRF(mat%size_global(1), mat%size_global(2), mat%buffer_cplx, 1, 1, mat%desc,ipiv, info)
    4824              :      ABI_CHECK(info == 0, sjoin(" PCGETRF returned info:", itoa(info)))
    4825              : 
    4826              :      ! Get optimal size of workspace for PZGETRI.
    4827              :      lwork = -1; liwork = -1
    4828              :      ABI_MALLOC(work_sp,(1))
    4829              :      ABI_MALLOC(iwork,(1))
    4830              : 
    4831              :      call PCGETRI(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc, ipiv, work_sp, lwork, iwork, liwork, info)
    4832              :      ABI_CHECK(info == 0, "PZGETRI: Error while computing workspace size")
    4833              : 
    4834              :      lwork = nint(real(work_sp(1))); liwork=iwork(1)
    4835              :      ABI_FREE(work_sp)
    4836              :      ABI_FREE(iwork)
    4837              : 
    4838              :      ! Solve the problem.
    4839              :      ABI_MALLOC(work_sp, (lwork))
    4840              :      ABI_MALLOC(iwork, (liwork))
    4841              : 
    4842              :      call PCGETRI(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc, ipiv, work_sp, lwork, iwork, liwork, info)
    4843              :      ABI_CHECK(info == 0, sjoin("PZGETRI returned info:", itoa(info)))
    4844              :      ABI_FREE(work_sp)
    4845              : 
    4846              :    else if (allocated(mat%buffer_real)) then
    4847              :      ABI_ERROR("Inversion for real matrices not coded!")
    4848              :    end if
    4849              : 
    4850              :  class default
    4851              :    ABI_ERROR("Wrong class")
    4852              :  end select
    4853              : 
    4854              :  ABI_FREE(iwork)
    4855              :  ABI_FREE(ipiv)
    4856              : #endif
    4857              : 
    4858            0 : end subroutine basemat_invert
    4859              : !!***
    4860              : 
    4861              : !----------------------------------------------------------------------
    4862              : 
    4863              : !!****f* m_slk/slkmat_dp_hpd_invert
    4864              : !! NAME
    4865              : !! slkmat_dp_hpd_invert
    4866              : !!
    4867              : !! FUNCTION
    4868              : !!  Compute the inverse of an Hermitian positive definite matrix.
    4869              : !!
    4870              : !! INPUTS
    4871              : !!  uplo: global input
    4872              : !!    = 'U':  Upper triangle of sub( A ) is stored;
    4873              : !!    = 'L':  Lower triangle of sub( A ) is stored.
    4874              : !!  [full]: If full PBLAS matrix is neeeded. Default: True
    4875              : !!
    4876              : !! SIDE EFFECTS
    4877              : !!  mat= The object storing the local buffer, the array descriptor, the context, etc.
    4878              : !!    On entry, this array contains the local pieces of the N-by-N Hermitian distributed matrix sub( A ) to be factored.
    4879              : !!    If UPLO = 'U', the leading N-by-N upper triangular part of sub( A ) contains the upper triangular part of the matrix,
    4880              : !!    and its strictly lower triangular part is not referenced.
    4881              : !!    If UPLO = 'L', the leading N-by-N lower triangular part of sub( A ) contains the lower triangular part of the distribu-
    4882              : !!    ted matrix, and its strictly upper triangular part is not referenced.
    4883              : !!    On exit, the local pieces of the upper or lower triangle of the (Hermitian) inverse of sub( A )
    4884              : !!
    4885              : !! SOURCE
    4886              : 
    4887            0 : subroutine slkmat_dp_hpd_invert(mat, uplo, full)
    4888              : 
    4889              : !Arguments ------------------------------------
    4890              :  class(slkmat_dp_t),intent(inout) :: mat
    4891              :  character(len=*),intent(in) :: uplo
    4892              :  logical,optional,intent(in) :: full
    4893              : 
    4894              : #ifdef HAVE_LINALG_SCALAPACK
    4895              : !Local variables ------------------------------
    4896              : !scalars
    4897              :  integer :: info, mm, il1, il2, iglob1, iglob2
    4898              :  type(slkmat_dp_t) :: work_mat
    4899              :  logical :: full__
    4900              : !************************************************************************
    4901              : 
    4902              :  ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx not allocated")
    4903              : 
    4904              :  ! ZPOTRF computes the Cholesky factorization of a complex Hermitian positive definite.
    4905              :  !  A = U**H * U,   if UPLO = 'U', or
    4906              :  !  A = L  * L**H,  if UPLO = 'L',
    4907              :  mm = mat%size_global(1)
    4908              :  call PZPOTRF(uplo, mm, mat%buffer_cplx, 1, 1, mat%desc, info)
    4909              :  ABI_CHECK(info == 0, sjoin("PZPOTRF returned info:", itoa(info)))
    4910              : 
    4911              :  ! PZPOTRI computes the inverse of a complex Hermitian positive definite
    4912              :  ! distributed matrix sub( A ) = A(IA:IA+N-1,JA:JA+N-1) using the
    4913              :  ! Cholesky factorization sub( A ) = U**H*U or L*L**H computed by PZPOTRF.
    4914              :  call PZPOTRI(uplo, mm, mat%buffer_cplx, 1, 1, mat%desc, info)
    4915              :  ABI_CHECK(info == 0, sjoin("PZPOTRI returned info:", itoa(info)))
    4916              : 
    4917              :  full__ = .True.; if (present(full)) full__ = full
    4918              :  if (full__) then
    4919              :    ! Only the uplo part contains the inverse so we need to fill the other triangular part.
    4920              :    !     1)  Fill the missing triangle with zeros and copy results to work_mat
    4921              :    !     2)  Call pzgeadd to compute: sub(C) := beta*sub(C) + alpha*op(sub(A))
    4922              :    !     3)  Divide diagonal elements by two.
    4923              : 
    4924              :    do il2=1,mat%size_local(2)
    4925              :      iglob2 = mat%loc2gcol(il2)
    4926              :      do il1=1,mat%size_local(1)
    4927              :        iglob1 = mat%loc2grow(il1)
    4928              :        if (uplo == "L" .and. iglob2 > iglob1) mat%buffer_cplx(il1, il2) = zero
    4929              :        if (uplo == "U" .and. iglob2 < iglob1) mat%buffer_cplx(il1, il2) = zero
    4930              :      end do
    4931              :    end do
    4932              : 
    4933              :    call mat%copy(work_mat, empty=.False.)
    4934              : 
    4935              :    ! call pzgeadd(trans, m, n, alpha, a, ia, ja, desca, beta, c, ic, jc, descc)
    4936              :    ! sub(C) := beta*sub(C) + alpha*op(sub(A))
    4937              :    call pzgeadd("C", mm, mm, cone, work_mat%buffer_cplx, 1, 1, work_mat%desc, &
    4938              :          cone, mat%buffer_cplx, 1, 1, mat%desc)
    4939              :    call work_mat%free()
    4940              : 
    4941              :    do il2=1,mat%size_local(2)
    4942              :      iglob2 = mat%loc2gcol(il2)
    4943              :      do il1=1,mat%size_local(1)
    4944              :        iglob1 = mat%loc2grow(il1)
    4945              :        if (iglob2 == iglob1) mat%buffer_cplx(il1, il2) = half * mat%buffer_cplx(il1, il2)
    4946              :      end do
    4947              :    end do
    4948              :  end if ! full__
    4949              : #endif
    4950              : 
    4951            0 : end subroutine slkmat_dp_hpd_invert
    4952              : !!***
    4953              : 
    4954              : !----------------------------------------------------------------------
    4955              : 
    4956              : !!****f* m_slk/slkmat_sp_hpd_invert
    4957              : !! NAME
    4958              : !! slkmat_sp_hpd_invert
    4959              : !!
    4960              : !! FUNCTION
    4961              : !!  Compute the inverse of an Hermitian positive definite matrix.
    4962              : !!
    4963              : !! INPUTS
    4964              : !!  uplo: global input
    4965              : !!    = 'U':  Upper triangle of sub( A ) is stored;
    4966              : !!    = 'L':  Lower triangle of sub( A ) is stored.
    4967              : !!  [full]: If full PBLAS matrix is neeeded. Default: True
    4968              : !!
    4969              : !! SIDE EFFECTS
    4970              : !!  mat= The object storing the local buffer, the array descriptor, the context, etc.
    4971              : !!    On entry, this array contains the local pieces of the N-by-N Hermitian distributed matrix sub( A ) to be factored.
    4972              : !!    If UPLO = 'U', the leading N-by-N upper triangular part of sub( A ) contains the upper triangular part of the matrix,
    4973              : !!    and its strictly lower triangular part is not referenced.
    4974              : !!    If UPLO = 'L', the leading N-by-N lower triangular part of sub( A ) contains the lower triangular part of the distribu-
    4975              : !!    ted matrix, and its strictly upper triangular part is not referenced.
    4976              : !!    On exit, the local pieces of the upper or lower triangle of the (Hermitian) inverse of sub( A )
    4977              : !!
    4978              : !! SOURCE
    4979              : 
    4980            0 : subroutine slkmat_sp_hpd_invert(mat, uplo, full)
    4981              : 
    4982              : !Arguments ------------------------------------
    4983              :  class(slkmat_sp_t),intent(inout) :: mat
    4984              :  character(len=*),intent(in) :: uplo
    4985              :  logical,optional,intent(in) :: full
    4986              : 
    4987              : #ifdef HAVE_LINALG_SCALAPACK
    4988              : !Local variables ------------------------------
    4989              : !scalars
    4990              :  integer :: info, mm, il1, il2, iglob1, iglob2
    4991              :  type(slkmat_sp_t) :: work_mat
    4992              :  logical :: full__
    4993              : !************************************************************************
    4994              : 
    4995              :  ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx not allocated")
    4996              : 
    4997              :  ! ZPOTRF computes the Cholesky factorization of a complex Hermitian positive definite.
    4998              :  !  A = U**H * U,   if UPLO = 'U', or
    4999              :  !  A = L  * L**H,  if UPLO = 'L',
    5000              :  mm = mat%size_global(1)
    5001              :  call PCPOTRF(uplo, mm, mat%buffer_cplx, 1, 1, mat%desc, info)
    5002              :  ABI_CHECK(info == 0, sjoin("PCPOTRF returned info:", itoa(info)))
    5003              : 
    5004              :  ! PZPOTRI computes the inverse of a complex Hermitian positive definite
    5005              :  ! distributed matrix sub( A ) = A(IA:IA+N-1,JA:JA+N-1) using the
    5006              :  ! Cholesky factorization sub( A ) = U**H*U or L*L**H computed by PZPOTRF.
    5007              :  call PCPOTRI(uplo, mm, mat%buffer_cplx, 1, 1, mat%desc, info)
    5008              :  ABI_CHECK(info == 0, sjoin("PCPOTRI returned info:", itoa(info)))
    5009              : 
    5010              :  full__ = .True.; if (present(full)) full__ = full
    5011              :  if (full__) then
    5012              :    ! Only the uplo part contains the inverse so we need to fill the other triangular part.
    5013              :    !     1)  Fill the missing triangle with zeros and copy results to work_mat
    5014              :    !     2)  Call pzgeadd to compute: sub(C) := beta*sub(C) + alpha*op(sub(A))
    5015              :    !     3)  Divide diagonal elements by two.
    5016              : 
    5017              :    do il2=1,mat%size_local(2)
    5018              :      iglob2 = mat%loc2gcol(il2)
    5019              :      do il1=1,mat%size_local(1)
    5020              :        iglob1 = mat%loc2grow(il1)
    5021              :        if (uplo == "L" .and. iglob2 > iglob1) mat%buffer_cplx(il1, il2) = zero_sp
    5022              :        if (uplo == "U" .and. iglob2 < iglob1) mat%buffer_cplx(il1, il2) = zero_sp
    5023              :      end do
    5024              :    end do
    5025              : 
    5026              :    call mat%copy(work_mat, empty=.False.)
    5027              : 
    5028              :    ! call pzgeadd(trans, m, n, alpha, a, ia, ja, desca, beta, c, ic, jc, descc)
    5029              :    ! sub(C) := beta*sub(C) + alpha*op(sub(A))
    5030              :    call pcgeadd("C", mm, mm, cone_sp, work_mat%buffer_cplx, 1, 1, work_mat%desc, &
    5031              :          cone_sp, mat%buffer_cplx, 1, 1, mat%desc)
    5032              :    call work_mat%free()
    5033              : 
    5034              :    do il2=1,mat%size_local(2)
    5035              :      iglob2 = mat%loc2gcol(il2)
    5036              :      do il1=1,mat%size_local(1)
    5037              :        iglob1 = mat%loc2grow(il1)
    5038              :        if (iglob2 == iglob1) mat%buffer_cplx(il1, il2) = 0.5_sp * mat%buffer_cplx(il1, il2)
    5039              :      end do
    5040              :    end do
    5041              :  end if ! full__
    5042              : #endif
    5043              : 
    5044            0 : end subroutine slkmat_sp_hpd_invert
    5045              : !!***
    5046              : 
    5047              : !----------------------------------------------------------------------
    5048              : 
    5049              : !!****f* m_slk/slkmat_dp_ptrans
    5050              : !! NAME
    5051              : !!  slkmat_dp_ptrans
    5052              : !!
    5053              : !! FUNCTION
    5054              : !! Transposes a matrix
    5055              : !!
    5056              : !!    sub( C ) := beta*sub( C ) + alpha*op( sub( A ) )
    5057              : !!
    5058              : !! where
    5059              : !!
    5060              : !!    sub( C ) denotes C(IC:IC+M-1,JC:JC+N-1),
    5061              : !!    sub( A ) denotes A(IA:IA+N-1,JA:JA+M-1), and, op( X ) = X'.
    5062              : !!
    5063              : !! Thus, op( sub( A ) ) denotes A(IA:IA+N-1,JA:JA+M-1)'.
    5064              : !! Beta is a scalar, sub( C ) is an m by n submatrix, and sub( A ) is an n by m submatrix.
    5065              : !!
    5066              : !! INPUTS
    5067              : !!  [ija(2)]: (global) The row and column indices in the distributed matrix in_mat indicating
    5068              : !!       the first row and the first column of the submatrix sub(A), respectively.
    5069              : !!  [ijc(2)]: (global) The row and column indices in the distributed matrix out_mat
    5070              : !!   indicating the first row and the first column of the submatrix sub(C), respectively.
    5071              : !!  [free]: True to deallocate in_mat. Default: False
    5072              : !!
    5073              : !! SOURCE
    5074              : 
    5075            0 : subroutine slkmat_dp_ptrans(in_mat, trans, out_mat, &
    5076              :                             out_gshape, ija, ijc, size_blocs, alpha, beta, free) ! optional
    5077              : 
    5078              : !Arguments ------------------------------------
    5079              :  class(slkmat_dp_t),intent(inout) :: in_mat
    5080              :  character(len=1),intent(in) :: trans
    5081              :  class(slkmat_dp_t),intent(inout) :: out_mat
    5082              :  integer,optional,intent(in) :: out_gshape(2), size_blocs(2), ija(2), ijc(2)
    5083              :  complex(dp),optional,intent(in) :: alpha, beta
    5084              :  logical,optional,intent(in) :: free
    5085              : 
    5086              : !Local variables-------------------------------
    5087              :  integer :: sb, mm, nn, size_blocs__(2)
    5088              :  real(dp) :: ralpha__, rbeta__
    5089              :  integer :: ija__(2), ijc__(2)
    5090              :  complex(dp) :: calpha__, cbeta__
    5091              : ! *************************************************************************
    5092              : 
    5093            0 :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    5094            0 :  ijc__ = [1, 1]; if (present(ijc)) ijc__ = ijc
    5095              : 
    5096              :  ! transposed output (sub)matrix has shape (nn, mm)
    5097            0 :  if (present(out_gshape)) then
    5098            0 :    nn = out_gshape(1)
    5099            0 :    mm = out_gshape(2)
    5100              :  else
    5101            0 :    nn = in_mat%size_global(2)
    5102            0 :    mm = in_mat%size_global(1)
    5103              :  end if
    5104              : 
    5105            0 :  if (present(size_blocs)) then
    5106            0 :    size_blocs__ = size_blocs
    5107              :  else
    5108              :    ! FIXME: This can cause problems if I start to use round-robin distribution in GWR!!!!!
    5109            0 :    size_blocs__(1) = in_mat%size_global(2)
    5110            0 :    sb = in_mat%size_global(1) / in_mat%processor%grid%dims(2)
    5111            0 :    if (mod(in_mat%size_global(1), in_mat%processor%grid%dims(2)) /= 0) sb = sb + 1
    5112            0 :    size_blocs__(2) = sb
    5113              :    !size_blocs__(2) = in_mat%size_blocs(1); size_blocs__(1) = in_mat%size_blocs(2)
    5114              :  end if
    5115              : 
    5116            0 :  call out_mat%init(nn, mm, in_mat%processor, in_mat%istwf_k, size_blocs=size_blocs__)
    5117              : 
    5118              :  ! prototype: call pdtran(m, n, alpha, a, ia, ja, desca, beta, c, ic, jc, descc)
    5119              : 
    5120            0 :  if (allocated(in_mat%buffer_cplx)) then
    5121              : #ifdef HAVE_LINALG_SCALAPACK
    5122              :    select case (trans)
    5123              :    case ("N")
    5124              :      ! sub(C) := beta*sub(C) + alpha*sub(A)',
    5125              :      calpha__ = cone; if (present(alpha)) calpha__ = alpha
    5126              :      cbeta__ = czero; if (present(beta)) cbeta__ = beta
    5127              :      call pztranu(nn, mm, calpha__, in_mat%buffer_cplx, ija__(1), ija__(2), &
    5128              :                   in_mat%desc, cbeta__, out_mat%buffer_cplx, ijc__(1), ijc__(2), out_mat%desc)
    5129              : 
    5130              :    case ("C")
    5131              :      ! sub(C) := beta * sub(C) + alpha * conjg(sub(A)')
    5132              :      calpha__ = cone; if (present(alpha)) calpha__ = alpha
    5133              :      cbeta__ = czero; if (present(beta)) cbeta__ = beta
    5134              :      call pztranc(nn, mm, calpha__, in_mat%buffer_cplx, ija__(1), ija__(2), &
    5135              :                   in_mat%desc, cbeta__, out_mat%buffer_cplx, ijc__(1), ijc__(2), out_mat%desc)
    5136              : 
    5137              :    case default
    5138              :      ABI_ERROR(sjoin("Invalid value for trans:", trans))
    5139              :    end select
    5140              : 
    5141              :  else if (allocated(in_mat%buffer_real)) then
    5142              :      ralpha__ = one; if (present(alpha)) ralpha__ = real(alpha)
    5143              :      rbeta__ = zero; if (present(beta)) rbeta__ = real(beta)
    5144              :      call pdtran(nn, mm, ralpha__, in_mat%buffer_real, ija__(1), ija__(2), &
    5145              :                  in_mat%desc, rbeta__, out_mat%buffer_real, ijc__(1), ijc__(2), out_mat%desc)
    5146              : #endif
    5147              :  else
    5148            0 :    ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5149              :  end if
    5150              : 
    5151            0 :  if (present(free)) then
    5152            0 :    if (free) call in_mat%free()
    5153              :  end if
    5154              : 
    5155            0 : end subroutine slkmat_dp_ptrans
    5156              : !!***
    5157              : 
    5158              : !!****f* m_slk/slkmat_sp_ptrans
    5159              : !! NAME
    5160              : !!  slkmat_sp_ptrans
    5161              : !!
    5162              : !! FUNCTION
    5163              : !! Transposes a matrix
    5164              : !!
    5165              : !!    sub( C ) := beta*sub( C ) + alpha*op( sub( A ) )
    5166              : !!
    5167              : !! where
    5168              : !!
    5169              : !!    sub( C ) denotes C(IC:IC+M-1,JC:JC+N-1),
    5170              : !!    sub( A ) denotes A(IA:IA+N-1,JA:JA+M-1), and, op( X ) = X'.
    5171              : !!
    5172              : !! Thus, op( sub( A ) ) denotes A(IA:IA+N-1,JA:JA+M-1)'.
    5173              : !! Beta is a scalar, sub( C ) is an m by n submatrix, and sub( A ) is an n by m submatrix.
    5174              : !!
    5175              : !! INPUTS
    5176              : !!  [ija(2)]: (global) The row and column indices in the distributed matrix in_mat indicating
    5177              : !!       the first row and the first column of the submatrix sub(A), respectively.
    5178              : !!  [ijc(2)]: (global) The row and column indices in the distributed matrix out_mat
    5179              : !!   indicating the first row and the first column of the submatrix sub(C), respectively.
    5180              : !!  [free]: True to deallocate in_mat. Default: False
    5181              : !!
    5182              : !! SOURCE
    5183              : 
    5184            0 : subroutine slkmat_sp_ptrans(in_mat, trans, out_mat, &
    5185              :                             out_gshape, ija, ijc, size_blocs, alpha, beta, free) ! optional
    5186              : 
    5187              : !Arguments ------------------------------------
    5188              :  class(slkmat_sp_t),intent(inout) :: in_mat
    5189              :  character(len=1),intent(in) :: trans
    5190              :  class(slkmat_sp_t),intent(inout) :: out_mat
    5191              :  integer,optional,intent(in) :: out_gshape(2), size_blocs(2), ija(2), ijc(2)
    5192              :  complex(sp),optional,intent(in) :: alpha, beta
    5193              :  logical,optional,intent(in) :: free
    5194              : 
    5195              : !Local variables-------------------------------
    5196              :  integer :: sb, mm, nn, size_blocs__(2)
    5197              :  real(sp) :: ralpha__, rbeta__
    5198              :  integer :: ija__(2), ijc__(2)
    5199              :  complex(sp) :: calpha__, cbeta__
    5200              : ! *************************************************************************
    5201              : 
    5202            0 :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    5203            0 :  ijc__ = [1, 1]; if (present(ijc)) ijc__ = ijc
    5204              : 
    5205              :  ! transposed output (sub)matrix has shape (nn, mm)
    5206            0 :  if (present(out_gshape)) then
    5207            0 :    nn = out_gshape(1)
    5208            0 :    mm = out_gshape(2)
    5209              :  else
    5210            0 :    nn = in_mat%size_global(2)
    5211            0 :    mm = in_mat%size_global(1)
    5212              :  end if
    5213              : 
    5214            0 :  if (present(size_blocs)) then
    5215            0 :    size_blocs__ = size_blocs
    5216              :  else
    5217              :    ! FIXME: This can cause problems if I start to use round-robin distribution in GWR!!!!!
    5218            0 :    size_blocs__(1) = in_mat%size_global(2)
    5219            0 :    sb = in_mat%size_global(1) / in_mat%processor%grid%dims(2)
    5220            0 :    if (mod(in_mat%size_global(1), in_mat%processor%grid%dims(2)) /= 0) sb = sb + 1
    5221            0 :    size_blocs__(2) = sb
    5222              :    !size_blocs__(2) = in_mat%size_blocs(1); size_blocs__(1) = in_mat%size_blocs(2)
    5223              :  end if
    5224              : 
    5225            0 :  call out_mat%init(nn, mm, in_mat%processor, in_mat%istwf_k, size_blocs=size_blocs__)
    5226              : 
    5227              :  ! prototype: call pdtran(m, n, alpha, a, ia, ja, desca, beta, c, ic, jc, descc)
    5228              : 
    5229            0 :  if (allocated(in_mat%buffer_cplx)) then
    5230              : #ifdef HAVE_LINALG_SCALAPACK
    5231              :    select case (trans)
    5232              :    case ("N")
    5233              :      ! sub(C) := beta*sub(C) + alpha*sub(A)',
    5234              :      calpha__ = cone_sp; if (present(alpha)) calpha__ = alpha
    5235              :      cbeta__ = czero_sp; if (present(beta)) cbeta__ = beta
    5236              :      call pctranu(nn, mm, calpha__, in_mat%buffer_cplx, ija__(1), ija__(2), &
    5237              :                   in_mat%desc, cbeta__, out_mat%buffer_cplx, ijc__(1), ijc__(2), out_mat%desc)
    5238              : 
    5239              :    case ("C")
    5240              :      ! sub(C) := beta * sub(C) + alpha * conjg(sub(A)')
    5241              :      calpha__ = cone_sp; if (present(alpha)) calpha__ = alpha
    5242              :      cbeta__ = czero_sp; if (present(beta)) cbeta__ = beta
    5243              :      call pctranc(nn, mm, calpha__, in_mat%buffer_cplx, ija__(1), ija__(2), &
    5244              :                   in_mat%desc, cbeta__, out_mat%buffer_cplx, ijc__(1), ijc__(2), out_mat%desc)
    5245              : 
    5246              :    case default
    5247              :      ABI_ERROR(sjoin("Invalid value for trans:", trans))
    5248              :    end select
    5249              : 
    5250              :  else if (allocated(in_mat%buffer_real)) then
    5251              :      ralpha__ = one_sp; if (present(alpha)) ralpha__ = real(alpha)
    5252              :      rbeta__ = zero_sp; if (present(beta)) rbeta__ = real(beta)
    5253              :      call pstran(nn, mm, ralpha__, in_mat%buffer_real, ija__(1), ija__(2), &
    5254              :                  in_mat%desc, rbeta__, out_mat%buffer_real, ijc__(1), ijc__(2), out_mat%desc)
    5255              : #endif
    5256              :  else
    5257            0 :    ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5258              :  end if
    5259              : 
    5260            0 :  if (present(free)) then
    5261            0 :    if (free) call in_mat%free()
    5262              :  end if
    5263              : 
    5264            0 : end subroutine slkmat_sp_ptrans
    5265              : !!***
    5266              : 
    5267              : !----------------------------------------------------------------------
    5268              : 
    5269              : !!****f* m_slk/basemat_change_size_blocs
    5270              : !! NAME
    5271              : !!  basemat_change_size_blocs
    5272              : !!
    5273              : !! FUNCTION
    5274              : !!  Change the block sizes, return new matrix in out_mat
    5275              : !!
    5276              : !! INPUTS
    5277              : !!  [free]: True if `in_mat` should be deallocated. Default: False
    5278              : !!
    5279              : !! OUTPUT
    5280              : !!
    5281              : !! SOURCE
    5282              : 
    5283            0 : subroutine basemat_change_size_blocs(in_mat, out_mat, &
    5284              :                                      size_blocs, processor, free)  ! Optional
    5285              : 
    5286              : !Arguments ------------------------------------
    5287              :  class(basemat_t),target,intent(inout) :: in_mat
    5288              :  class(basemat_t),intent(out) :: out_mat
    5289              :  integer,optional,intent(in) :: size_blocs(2)
    5290              :  class(slk_processor_t), target, optional,intent(in) :: processor
    5291              :  logical,optional,intent(in) :: free
    5292              : 
    5293              : !Local variables-------------------------------
    5294              :  type(slk_processor_t), pointer :: processor__
    5295              : ! *************************************************************************
    5296              : 
    5297            0 :  processor__ => in_mat%processor; if (present(processor)) processor__ => processor
    5298              : 
    5299            0 :  if (present(size_blocs)) then
    5300            0 :    call out_mat%init(in_mat%size_global(1), in_mat%size_global(2), processor__, in_mat%istwf_k, size_blocs=size_blocs)
    5301              :  else
    5302            0 :    call out_mat%init(in_mat%size_global(1), in_mat%size_global(2), processor__, in_mat%istwf_k)
    5303              :  end if
    5304              : 
    5305              :  !write(std_out, *)"out_mat%size_global", out_mat%size_global
    5306              :  !call in_mat%print(header="input matrix in by basemat_change_size_blocs")
    5307              :  !call out_mat%print(header="output matrix generated by basemat_change_size_blocs")
    5308              : 
    5309            0 :  ABI_CHECK(same_type_as(in_mat, out_mat), "in_mat and out_mat should have same type!")
    5310              : 
    5311              :  ! p?gemr2d: Copies a submatrix from one general rectangular matrix to another.
    5312              :  ! prototype
    5313              :  !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
    5314              : 
    5315              : #ifdef HAVE_LINALG_SCALAPACK
    5316              :  select type (in_mat)
    5317              :  class is (slkmat_dp_t)
    5318              :    select type (out_mat)
    5319              :    class is (slkmat_dp_t)
    5320              :    if (allocated(in_mat%buffer_cplx)) then
    5321              :      ABI_CHECK_IEQ(kind(in_mat%buffer_cplx), kind(out_mat%buffer_cplx), "Different kind")
    5322              :      ABI_CHECK(allocated(out_mat%buffer_cplx), "out_mat%buffer_cplx should be allocated")
    5323              :      call pzgemr2d(in_mat%size_global(1), in_mat%size_global(2),  &
    5324              :                    in_mat%buffer_cplx, 1, 1, in_mat%desc,   &
    5325              :                    out_mat%buffer_cplx, 1, 1, out_mat%desc, &
    5326              :                    processor__%grid%comm)
    5327              : 
    5328              :    else if (allocated(in_mat%buffer_real)) then
    5329              :      ABI_CHECK_IEQ(kind(in_mat%buffer_real), kind(out_mat%buffer_real), "Different kind")
    5330              :      ABI_CHECK(allocated(out_mat%buffer_real), "out_mat%buffer_real should be allocated")
    5331              :      call pdgemr2d(in_mat%size_global(1), in_mat%size_global(2),  &
    5332              :                    in_mat%buffer_real, 1, 1, in_mat%desc,   &
    5333              :                    out_mat%buffer_real, 1, 1, out_mat%desc, &
    5334              :                    processor__%grid%comm)
    5335              :    else
    5336              :      ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5337              :    end if
    5338              :    end select
    5339              : 
    5340              :  class is (slkmat_sp_t)
    5341              :    select type (out_mat)
    5342              :    class is (slkmat_sp_t)
    5343              :    if (allocated(in_mat%buffer_cplx)) then
    5344              :      ABI_CHECK_IEQ(kind(in_mat%buffer_cplx), kind(out_mat%buffer_cplx), "Different kind")
    5345              : 
    5346              :      ABI_CHECK(allocated(out_mat%buffer_cplx), "out_mat%buffer should be allocated")
    5347              :      call pcgemr2d(in_mat%size_global(1), in_mat%size_global(2),  &
    5348              :                    in_mat%buffer_cplx, 1, 1, in_mat%desc,   &
    5349              :                    out_mat%buffer_cplx, 1, 1, out_mat%desc, &
    5350              :                    processor__%grid%comm)
    5351              : 
    5352              :    else if (allocated(in_mat%buffer_real)) then
    5353              :      ABI_CHECK_IEQ(kind(in_mat%buffer_real), kind(out_mat%buffer_real), "Different kind")
    5354              :      ABI_CHECK(allocated(out_mat%buffer_real), "out_mat%buffer_real should be allocated")
    5355              :      call psgemr2d(in_mat%size_global(1), in_mat%size_global(2),  &
    5356              :                    in_mat%buffer_real, 1, 1, in_mat%desc,   &
    5357              :                    out_mat%buffer_real, 1, 1, out_mat%desc, &
    5358              :                    processor__%grid%comm)
    5359              :    else
    5360              :      ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5361              :    end if
    5362              :    end select
    5363              : 
    5364              :  class default
    5365              :    ABI_ERROR("Wrong class")
    5366              :  end select
    5367              : #endif
    5368              : 
    5369            0 :  if (present(free)) then
    5370            0 :    if (free) call in_mat%free()
    5371              :  end if
    5372              : 
    5373            0 : end subroutine basemat_change_size_blocs
    5374              : !!***
    5375              : 
    5376              : !----------------------------------------------------------------------
    5377              : 
    5378              : !!****f* m_slk/slkmat_dp_cut
    5379              : !! NAME
    5380              : !!  slkmat_dp_cut
    5381              : !!
    5382              : !! FUNCTION
    5383              : !!  Extract submatrix of shape (glob_nrows, glob_ncols) starting at `ija` from `in_mat`
    5384              : !!  and create new matrix with `size_blocs` and `processor`
    5385              : !!
    5386              : !! INPUTS
    5387              : !!  [free]: True if `in_mat` should be deallocated. Default: False
    5388              : !!
    5389              : !! OUTPUT
    5390              : !!
    5391              : !! SOURCE
    5392              : 
    5393            0 : subroutine slkmat_dp_cut(in_mat, glob_nrows, glob_ncols, out_mat, &
    5394              :                          size_blocs, processor, ija, ijb, free)  ! Optional
    5395              : 
    5396              : !Arguments ------------------------------------
    5397              :  class(slkmat_dp_t),target,intent(inout) :: in_mat
    5398              :  integer,intent(in) :: glob_nrows, glob_ncols
    5399              :  class(slkmat_dp_t),intent(out) :: out_mat
    5400              :  integer,optional,intent(in) :: size_blocs(2)
    5401              :  class(slk_processor_t), target, optional,intent(in) :: processor
    5402              :  integer,optional,intent(in) :: ija(2), ijb(2)
    5403              :  logical,optional,intent(in) :: free
    5404              : 
    5405              : !Local variables-------------------------------
    5406              :  type(slk_processor_t), pointer :: processor__
    5407              :  integer :: ija__(2), ijb__(2)
    5408              : ! *************************************************************************
    5409              : 
    5410            0 :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    5411            0 :  ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
    5412              : 
    5413            0 :  processor__ => in_mat%processor; if (present(processor)) processor__ => processor
    5414              : 
    5415            0 :  if (present(size_blocs)) then
    5416            0 :    call out_mat%init(glob_nrows, glob_ncols, processor__, in_mat%istwf_k, size_blocs=size_blocs)
    5417              :  else
    5418            0 :    call out_mat%init(glob_nrows, glob_ncols, processor__, in_mat%istwf_k)
    5419              :  end if
    5420              :  !call out_mat%print(header="output matrix generated by slkmat_dp_cut")
    5421              : 
    5422              :  ! p?gemr2d: Copies a submatrix from one general rectangular matrix to another.
    5423              :  ! prototype
    5424              :  !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
    5425              : 
    5426            0 :  if (allocated(in_mat%buffer_cplx)) then
    5427              : #ifdef HAVE_LINALG_SCALAPACK
    5428              :    call pzgemr2d(glob_nrows, glob_ncols, &
    5429              :                  in_mat%buffer_cplx, ija__(1), ija__(2), in_mat%desc,   &
    5430              :                  out_mat%buffer_cplx, ijb__(1), ijb__(2), out_mat%desc, &
    5431              :                  processor__%grid%comm)
    5432              : 
    5433              :  else if (allocated(in_mat%buffer_real)) then
    5434              :    call pdgemr2d(glob_nrows, glob_ncols, &
    5435              :                  in_mat%buffer_real, ija__(1), ija__(2), in_mat%desc,   &
    5436              :                  out_mat%buffer_real, ijb__(1), ijb__(2), out_mat%desc, &
    5437              :                  processor__%grid%comm)
    5438              : #endif
    5439              :  else
    5440            0 :    ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5441              :  end if
    5442              : 
    5443            0 :  if (present(free)) then
    5444            0 :    if (free) call in_mat%free()
    5445              :  end if
    5446              : 
    5447            0 : end subroutine slkmat_dp_cut
    5448              : !!***
    5449              : 
    5450              : !----------------------------------------------------------------------
    5451              : 
    5452              : !!****f* m_slk/slkmat_sp_cut
    5453              : !! NAME
    5454              : !!  slkmat_sp_cut
    5455              : !!
    5456              : !! FUNCTION
    5457              : !!  Extract submatrix of shape (glob_nrows, glob_ncols) starting at `ija` from `in_mat`
    5458              : !!  and create new matrix with `size_blocs` and `processor`
    5459              : !!
    5460              : !! INPUTS
    5461              : !!  [free]: True if `in_mat` should be deallocated. Default: False
    5462              : !!
    5463              : !! OUTPUT
    5464              : !!
    5465              : !! SOURCE
    5466              : 
    5467            0 : subroutine slkmat_sp_cut(in_mat, glob_nrows, glob_ncols, out_mat, &
    5468              :                          size_blocs, processor, ija, ijb, free)  ! Optional
    5469              : 
    5470              : !Arguments ------------------------------------
    5471              :  class(slkmat_sp_t),target,intent(inout) :: in_mat
    5472              :  integer,intent(in) :: glob_nrows, glob_ncols
    5473              :  class(slkmat_sp_t),intent(out) :: out_mat
    5474              :  integer,optional,intent(in) :: size_blocs(2)
    5475              :  class(slk_processor_t), target, optional,intent(in) :: processor
    5476              :  integer,optional,intent(in) :: ija(2), ijb(2)
    5477              :  logical,optional,intent(in) :: free
    5478              : 
    5479              : !Local variables-------------------------------
    5480              :  type(slk_processor_t), pointer :: processor__
    5481              :  integer :: ija__(2), ijb__(2)
    5482              : ! *************************************************************************
    5483              : 
    5484            0 :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    5485            0 :  ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
    5486              : 
    5487            0 :  processor__ => in_mat%processor; if (present(processor)) processor__ => processor
    5488              : 
    5489            0 :  if (present(size_blocs)) then
    5490            0 :    call out_mat%init(glob_nrows, glob_ncols, processor__, in_mat%istwf_k, size_blocs=size_blocs)
    5491              :  else
    5492            0 :    call out_mat%init(glob_nrows, glob_ncols, processor__, in_mat%istwf_k)
    5493              :  end if
    5494              :  !call out_mat%print(header="output matrix generated by slkmat_dp_cut")
    5495              : 
    5496              :  ! p?gemr2d: Copies a submatrix from one general rectangular matrix to another.
    5497              :  ! prototype
    5498              :  !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
    5499              : 
    5500            0 :  if (allocated(in_mat%buffer_cplx)) then
    5501              : #ifdef HAVE_LINALG_SCALAPACK
    5502              :    call pcgemr2d(glob_nrows, glob_ncols, &
    5503              :                  in_mat%buffer_cplx, ija__(1), ija__(2), in_mat%desc,   &
    5504              :                  out_mat%buffer_cplx, ijb__(1), ijb__(2), out_mat%desc, &
    5505              :                  processor__%grid%comm)
    5506              : 
    5507              :  else if (allocated(in_mat%buffer_real)) then
    5508              :    call psgemr2d(glob_nrows, glob_ncols, &
    5509              :                  in_mat%buffer_real, ija__(1), ija__(2), in_mat%desc,   &
    5510              :                  out_mat%buffer_real, ijb__(1), ijb__(2), out_mat%desc, &
    5511              :                  processor__%grid%comm)
    5512              : #endif
    5513              :  else
    5514            0 :    ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5515              :  end if
    5516              : 
    5517            0 :  if (present(free)) then
    5518            0 :    if (free) call in_mat%free()
    5519              :  end if
    5520              : 
    5521            0 : end subroutine slkmat_sp_cut
    5522              : !!***
    5523              : 
    5524              : !----------------------------------------------------------------------
    5525              : 
    5526              : !!****f* m_slk/slkmat_dp_take_from
    5527              : !! NAME
    5528              : !!  slkmat_dp_take_from
    5529              : !!
    5530              : !! FUNCTION
    5531              : !!  Take values from source
    5532              : !!  NB: This routine should be called by all procs owning mat and source.
    5533              : !!
    5534              : !! INPUTS
    5535              : !!  [free]: True if source should be deallocated. Default: False
    5536              : !!
    5537              : !! OUTPUT
    5538              : !!
    5539              : !! SOURCE
    5540              : 
    5541            0 : subroutine slkmat_dp_take_from(out_mat, source, &
    5542              :                          ija, ijb, free) ! optional
    5543              : 
    5544              : !Arguments ------------------------------------
    5545              :  class(slkmat_dp_t),intent(inout) :: out_mat
    5546              :  class(slkmat_dp_t),intent(inout) :: source
    5547              :  integer,optional,intent(in) :: ija(2), ijb(2)
    5548              :  logical,optional,intent(in) :: free
    5549              : 
    5550              : !Local variables-------------------------------
    5551              :  integer :: mm, nn
    5552              :  character(len=500) :: msg
    5553              :  integer :: ija__(2), ijb__(2)
    5554              : ! *************************************************************************
    5555              : 
    5556              :  ! prototype
    5557              :  !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
    5558              : 
    5559              :  ! Take care when context A is disjoint from context B. The general rules for which parameters need to be set are:
    5560              :  !
    5561              :  !   - All calling processes must have the correct m and n.
    5562              :  !   - Processes in context A must correctly define all parameters describing A.
    5563              :  !   - Processes in context B must correctly define all parameters describing B.
    5564              :  !   - Processes which are not members of context A must pass ctxt_a = -1 and need not set other parameters describing A.
    5565              :  !   - Processes which are not members of contextB must pass ctxt_b = -1 and need not set other parameters describing B.
    5566              : 
    5567            0 :  mm = source%size_global(1)
    5568            0 :  nn = source%size_global(2)
    5569              : 
    5570            0 :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    5571            0 :  ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
    5572              : 
    5573            0 :  if (all(out_mat%size_global == -1)) then
    5574            0 :    out_mat%desc(CTXT_) = -1
    5575              :  else
    5576            0 :    ABI_CHECK_IEQ(out_mat%istwf_k, source%istwf_k, "istwfk_mat /= istwfk_source")
    5577            0 :    if (any(out_mat%size_global /= source%size_global)) then
    5578              :      msg = sjoin("Matrices should have same global shape but out_mat:", ltoa(out_mat%size_global), &
    5579            0 :                  "source:", ltoa(source%size_global))
    5580            0 :      ABI_ERROR(msg)
    5581              :    end if
    5582              :  end if
    5583              : 
    5584            0 :  if (allocated(source%buffer_cplx)) then
    5585              : #ifdef HAVE_LINALG_SCALAPACK
    5586              :    call pzgemr2d(mm, nn,  &
    5587              :                  source%buffer_cplx, ija__(1), ija__(2), source%desc,   &
    5588              :                  out_mat%buffer_cplx, ijb__(1), ijb__(2), out_mat%desc, &
    5589              :                  source%processor%grid%comm)
    5590              : 
    5591              :  else if (allocated(source%buffer_real)) then
    5592              :    call pdgemr2d(mm, nn,  &
    5593              :                  source%buffer_real, ija__(1), ija__(2), source%desc,   &
    5594              :                  out_mat%buffer_real,ijb__(1), ijb__(2), out_mat%desc, &
    5595              :                  source%processor%grid%comm)
    5596              : #endif
    5597              :  else
    5598            0 :    ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5599              :  end if
    5600              : 
    5601            0 :  if (present(free)) then
    5602            0 :    if (free) call source%free()
    5603              :  end if
    5604              : 
    5605            0 : end subroutine slkmat_dp_take_from
    5606              : !!***
    5607              : 
    5608              : !----------------------------------------------------------------------
    5609              : 
    5610              : !!****f* m_slk/slkmat_sp_take_from
    5611              : !! NAME
    5612              : !!  slkmat_sp_take_from
    5613              : !!
    5614              : !! FUNCTION
    5615              : !!  Take values from source
    5616              : !!  NB: This routine should be called by all procs owning mat and source.
    5617              : !!
    5618              : !! INPUTS
    5619              : !!  [free]: True if source should be deallocated. Default: False
    5620              : !!
    5621              : !! OUTPUT
    5622              : !!
    5623              : !! SOURCE
    5624              : 
    5625            0 : subroutine slkmat_sp_take_from(out_mat, source, &
    5626              :                                ija, ijb, free) ! optional
    5627              : 
    5628              : !Arguments ------------------------------------
    5629              :  class(slkmat_sp_t),intent(inout) :: out_mat
    5630              :  class(slkmat_sp_t),intent(inout) :: source
    5631              :  integer,optional,intent(in) :: ija(2), ijb(2)
    5632              :  logical,optional,intent(in) :: free
    5633              : 
    5634              : !Local variables-------------------------------
    5635              :  integer :: mm, nn
    5636              :  character(len=500) :: msg
    5637              :  integer :: ija__(2), ijb__(2)
    5638              : ! *************************************************************************
    5639              : 
    5640              :  ! prototype
    5641              :  !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
    5642              : 
    5643              :  ! Take care when context A is disjoint from context B. The general rules for which parameters need to be set are:
    5644              :  !
    5645              :  !   - All calling processes must have the correct m and n.
    5646              :  !   - Processes in context A must correctly define all parameters describing A.
    5647              :  !   - Processes in context B must correctly define all parameters describing B.
    5648              :  !   - Processes which are not members of context A must pass ctxt_a = -1 and need not set other parameters describing A.
    5649              :  !   - Processes which are not members of contextB must pass ctxt_b = -1 and need not set other parameters describing B.
    5650              : 
    5651            0 :  mm = source%size_global(1)
    5652            0 :  nn = source%size_global(2)
    5653              : 
    5654            0 :  ija__ = [1, 1]; if (present(ija)) ija__ = ija
    5655            0 :  ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
    5656              : 
    5657            0 :  if (all(out_mat%size_global == -1)) then
    5658            0 :    out_mat%desc(CTXT_) = -1
    5659              :  else
    5660            0 :    ABI_CHECK_IEQ(out_mat%istwf_k, source%istwf_k, "istwfk_mat /= istwfk_source")
    5661            0 :    if (any(out_mat%size_global /= source%size_global)) then
    5662              :      msg = sjoin("Matrices should have same global shape but out_mat:", ltoa(out_mat%size_global), &
    5663            0 :                  "source:", ltoa(source%size_global))
    5664            0 :      ABI_ERROR(msg)
    5665              :    end if
    5666              :  end if
    5667              : 
    5668            0 :  if (allocated(source%buffer_cplx)) then
    5669              : #ifdef HAVE_LINALG_SCALAPACK
    5670              :    call pcgemr2d(mm, nn,  &
    5671              :                  source%buffer_cplx, ija__(1), ija__(2), source%desc,   &
    5672              :                  out_mat%buffer_cplx, ijb__(1), ijb__(2), out_mat%desc, &
    5673              :                  source%processor%grid%comm)
    5674              : 
    5675              :  else if (allocated(source%buffer_real)) then
    5676              :    call psgemr2d(mm, nn,  &
    5677              :                  source%buffer_real, ija__(1), ija__(2), source%desc,   &
    5678              :                  out_mat%buffer_real,ijb__(1), ijb__(2), out_mat%desc, &
    5679              :                  source%processor%grid%comm)
    5680              : #endif
    5681              :  else
    5682            0 :    ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5683              :  end if
    5684              : 
    5685            0 :  if (present(free)) then
    5686            0 :    if (free) call source%free()
    5687              :  end if
    5688              : 
    5689            0 : end subroutine slkmat_sp_take_from
    5690              : !!***
    5691              : 
    5692              : !----------------------------------------------------------------------
    5693              : 
    5694              : !!****f* m_slk/slkmat_dp_collect_cplx
    5695              : !! NAME
    5696              : !!  slkmat_dp_collect_cplx
    5697              : !!
    5698              : !! FUNCTION
    5699              : !!  Return on all processors the complex submatrix of shape (mm, nn) starting at position ija.
    5700              : !!  NB: `out_carr` is allocated by the routine.
    5701              : !!  If optional argument request is use, the routine uses non-blocking BCAST and client code is
    5702              : !!  supposed to wait before accessing out_carr.
    5703              : !!
    5704              : !! SOURCE
    5705              : 
    5706            0 : subroutine slkmat_dp_collect_cplx(in_mat, mm, nn, ija, out_carr, request)
    5707              : 
    5708              : !Arguments ------------------------------------
    5709              :  class(slkmat_dp_t),intent(in) :: in_mat
    5710              :  integer,intent(in) :: mm, nn, ija(2)
    5711              :  complex(dp) ABI_ASYNC, allocatable,intent(out) :: out_carr(:,:)
    5712              :  integer ABI_ASYNC, optional,intent(out) :: request
    5713              : 
    5714              : !Local variables-------------------------------
    5715              :  integer,parameter :: master = 0
    5716              :  integer :: ierr
    5717              :  type(slk_processor_t) :: self_processor
    5718            0 :  type(slkmat_dp_t) :: out_mat
    5719              : ! *************************************************************************
    5720              : 
    5721            0 :  ABI_CHECK(allocated(in_mat%buffer_cplx), "buffer_cplx is not allocated")
    5722              : 
    5723            0 :  if (in_mat%processor%grid%nprocs == 1) then
    5724              :    ! Copy buffer and return
    5725            0 :    ABI_MALLOC(out_carr, (mm, nn))
    5726            0 :    out_carr(:,:) = in_mat%buffer_cplx(ija(1):ija(1)+mm-1, ija(2):ija(2)+nn-1); return
    5727              :  end if
    5728              : 
    5729              :  ! Two-step algorithm:
    5730              :  !     1) Use pzgemr2d to collect submatrix on master.
    5731              :  !     2) Master brodacasts submatrix.
    5732              : 
    5733            0 :  if (in_mat%processor%my_rank == master) then
    5734            0 :    call self_processor%init(xmpi_comm_self)
    5735            0 :    call out_mat%init(mm, nn, self_processor, in_mat%istwf_k, size_blocs=[mm, nn])
    5736              :  else
    5737            0 :    out_mat%desc(CTXT_) = -1
    5738              :  end if
    5739              : 
    5740              : #ifdef HAVE_LINALG_SCALAPACK
    5741              :  call pzgemr2d(mm, nn,  &
    5742              :                in_mat%buffer_cplx, ija(1), ija(2), in_mat%desc,   &
    5743              :                out_mat%buffer_cplx, 1, 1, out_mat%desc, &
    5744              :                in_mat%processor%grid%comm)
    5745              : #endif
    5746              : 
    5747            0 :  if (in_mat%processor%my_rank == master) then
    5748            0 :    ABI_MOVE_ALLOC(out_mat%buffer_cplx, out_carr)
    5749            0 :    call out_mat%free()
    5750            0 :    call self_processor%free()
    5751              :  else
    5752            0 :    ABI_MALLOC(out_carr, (mm, nn))
    5753              :  end if
    5754              : 
    5755            0 :  if (present(request)) then
    5756            0 :    call xmpi_ibcast(out_carr, master, in_mat%processor%comm, request, ierr)
    5757              :  else
    5758            0 :    call xmpi_bcast(out_carr, master, in_mat%processor%comm, ierr)
    5759              :  end if
    5760              : 
    5761            0 : end subroutine slkmat_dp_collect_cplx
    5762              : !!***
    5763              : 
    5764              : !----------------------------------------------------------------------
    5765              : 
    5766              : !!****f* m_slk/slkmat_sp_collect_cplx
    5767              : !! NAME
    5768              : !!  slkmat_sp_collect_cplx
    5769              : !!
    5770              : !! FUNCTION
    5771              : !!  Return on all processors the complex submatrix of shape (mm, nn) starting at position ija.
    5772              : !!  NB: `out_carr` is allocated by the routine.
    5773              : !!  If optional argument request is use, the routine uses non-blocking BCAST and client code is
    5774              : !!  supposed to wait before accessing out_carr.
    5775              : !!
    5776              : !! SOURCE
    5777              : 
    5778            0 : subroutine slkmat_sp_collect_cplx(in_mat, mm, nn, ija, out_carr, request)
    5779              : 
    5780              : !Arguments ------------------------------------
    5781              :  class(slkmat_sp_t),intent(in) :: in_mat
    5782              :  integer,intent(in) :: mm, nn, ija(2)
    5783              :  complex(sp) ABI_ASYNC, allocatable,intent(out) :: out_carr(:,:)
    5784              :  integer ABI_ASYNC, optional,intent(out) :: request
    5785              : 
    5786              : !Local variables-------------------------------
    5787              :  integer,parameter :: master = 0
    5788              :  integer :: ierr
    5789              :  type(slk_processor_t) :: self_processor
    5790            0 :  type(slkmat_sp_t) :: out_mat
    5791              : ! *************************************************************************
    5792              : 
    5793            0 :  ABI_CHECK(allocated(in_mat%buffer_cplx), "buffer_cplx is not allocated")
    5794              : 
    5795            0 :  if (in_mat%processor%grid%nprocs == 1) then
    5796              :    ! Copy buffer and return
    5797            0 :    ABI_MALLOC(out_carr, (mm, nn))
    5798            0 :    out_carr(:,:) = in_mat%buffer_cplx(ija(1):ija(1)+mm-1, ija(2):ija(2)+nn-1); return
    5799              :  end if
    5800              : 
    5801              :  ! Two-step algorithm:
    5802              :  !     1) Use pzgemr2d to collect submatrix on master.
    5803              :  !     2) Master brodacasts submatrix.
    5804              : 
    5805            0 :  if (in_mat%processor%my_rank == master) then
    5806            0 :    call self_processor%init(xmpi_comm_self)
    5807            0 :    call out_mat%init(mm, nn, self_processor, in_mat%istwf_k, size_blocs=[mm, nn])
    5808              :  else
    5809            0 :    out_mat%desc(CTXT_) = -1
    5810              :  end if
    5811              : 
    5812              : #ifdef HAVE_LINALG_SCALAPACK
    5813              :  call pcgemr2d(mm, nn,  &
    5814              :                in_mat%buffer_cplx, ija(1), ija(2), in_mat%desc,   &
    5815              :                out_mat%buffer_cplx, 1, 1, out_mat%desc, &
    5816              :                in_mat%processor%grid%comm)
    5817              : #endif
    5818              : 
    5819            0 :  if (in_mat%processor%my_rank == master) then
    5820            0 :    ABI_MOVE_ALLOC(out_mat%buffer_cplx, out_carr)
    5821            0 :    call out_mat%free()
    5822            0 :    call self_processor%free()
    5823              :  else
    5824            0 :    ABI_MALLOC(out_carr, (mm, nn))
    5825              :  end if
    5826              : 
    5827            0 :  if (present(request)) then
    5828            0 :    call xmpi_ibcast(out_carr, master, in_mat%processor%comm, request, ierr)
    5829              :  else
    5830            0 :    call xmpi_bcast(out_carr, master, in_mat%processor%comm, ierr)
    5831              :  end if
    5832              : 
    5833            0 : end subroutine slkmat_sp_collect_cplx
    5834              : !!***
    5835              : 
    5836              : !----------------------------------------------------------------------
    5837              : 
    5838              : !!****f* m_slk/basemat_get_trace
    5839              : !! NAME
    5840              : !!  basemat_get_trace
    5841              : !!
    5842              : !! FUNCTION
    5843              : !!  Compute the trace of an N-by-N distributed matrix.
    5844              : !!
    5845              : !! SOURCE
    5846              : 
    5847            0 : complex(dp) function basemat_get_trace(mat) result(ctrace)
    5848              : 
    5849              : !Arguments ------------------------------------
    5850              :  class(basemat_t), intent(in) :: mat
    5851              : 
    5852              : !Local variables-------------------------------
    5853              : #ifdef HAVE_LINALG_SCALAPACK
    5854              :  integer :: iloc, jloc, iglob, jglob, ierr
    5855              :  real(dp) :: rtrace
    5856              :  real(sp) :: rtrace_sp
    5857              :  complex(sp) :: ctrace_sp
    5858              : #endif
    5859              : ! *************************************************************************
    5860              : 
    5861            0 :  ABI_CHECK_IEQ(mat%size_global(1), mat%size_global(2), "get_trace assumes square matrix!")
    5862              : 
    5863              :  ! prototype for complex version.
    5864              :  ! COMPLEX*16 FUNCTION PZLATRA( N, A, IA, JA, DESCA )
    5865              :  !
    5866              :  ! NOTE: We don't use PZLATRA and friends as these calls can lead to SIGSEGV when
    5867              :  ! we compile with gcc and link with intel scalack (very likely there's an ABI mismatch
    5868              :  ! even when the compatibily layer is used --> we implement our own version.
    5869              : 
    5870              : #ifdef HAVE_LINALG_SCALAPACK
    5871              :  select type (mat)
    5872              :  class is (slkmat_dp_t)
    5873              :    if (allocated(mat%buffer_cplx)) then
    5874              :      !ctrace = PZLATRA(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc)
    5875              :      ctrace = zero
    5876              :      do jloc=1,mat%size_local(2)
    5877              :        do iloc=1,mat%size_local(1)
    5878              :          call mat%loc2glob(iloc, jloc, iglob, jglob)
    5879              :          if (iglob == jglob) ctrace = ctrace + mat%buffer_cplx(iloc, jloc)
    5880              :        end do
    5881              :      end do
    5882              : 
    5883              :    else if (allocated(mat%buffer_real)) then
    5884              :      !rtrace = PDLATRA(mat%size_global(1), mat%buffer_real, 1, 1, mat%desc)
    5885              :      rtrace = zero
    5886              :      do jloc=1,mat%size_local(2)
    5887              :        do iloc=1,mat%size_local(1)
    5888              :          call mat%loc2glob(iloc, jloc, iglob, jglob)
    5889              :          if (iglob == jglob) rtrace = rtrace + mat%buffer_real(iloc, jloc)
    5890              :        end do
    5891              :      end do
    5892              :      ctrace = rtrace
    5893              : 
    5894              :    else
    5895              :      ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5896              :    end if
    5897              : 
    5898              :  class is (slkmat_sp_t)
    5899              :     if (allocated(mat%buffer_cplx)) then
    5900              :      !ctrace_sp = PCLATRA(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc)
    5901              :      ctrace_sp = zero
    5902              :      do jloc=1,mat%size_local(2)
    5903              :        do iloc=1,mat%size_local(1)
    5904              :          call mat%loc2glob(iloc, jloc, iglob, jglob)
    5905              :          if (iglob == jglob) ctrace_sp = ctrace_sp + mat%buffer_cplx(iloc, jloc)
    5906              :        end do
    5907              :      end do
    5908              :      ctrace = ctrace_sp
    5909              : 
    5910              :     else if (allocated(mat%buffer_real)) then
    5911              :       !rtrace_sp = PSLATRA(mat%size_global(1), mat%buffer_real, 1, 1, mat%desc)
    5912              :       rtrace_sp = zero
    5913              :       do jloc=1,mat%size_local(2)
    5914              :         do iloc=1,mat%size_local(1)
    5915              :           call mat%loc2glob(iloc, jloc, iglob, jglob)
    5916              :           if (iglob == jglob) rtrace_sp = rtrace_sp + mat%buffer_real(iloc, jloc)
    5917              :         end do
    5918              :       end do
    5919              :       ctrace = rtrace_sp
    5920              : 
    5921              :     else
    5922              :       ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
    5923              :     end if
    5924              : 
    5925              :  class default
    5926              :    ABI_ERROR("Wrong class")
    5927              :  end select
    5928              : 
    5929              :  call xmpi_sum(ctrace, mat%processor%comm, ierr)
    5930              : #endif
    5931              : 
    5932            0 : end function basemat_get_trace
    5933              : !!***
    5934              : 
    5935              : !----------------------------------------------------------------------
    5936              : 
    5937              : !!****f* m_slk/basemat_set_imag_diago_to_zero
    5938              : !! NAME
    5939              : !!  basemat_set_imag_diago_to_zero
    5940              : !!
    5941              : !! FUNCTION
    5942              : !!  Set the imaginary part of the diagonal to zero.
    5943              : !!  Return in local_max the max of the imaginar part in the local buffer.
    5944              : !!  No MPI communication is performed inside the routine. Client code can easily reduce
    5945              : !!  local_max within the PBLAS communicator if needed.
    5946              : !!
    5947              : !! INPUTS
    5948              : !!
    5949              : !! OUTPUT
    5950              : !!
    5951              : !! SOURCE
    5952              : 
    5953            0 : subroutine basemat_set_imag_diago_to_zero(mat, local_max)
    5954              : 
    5955              : !Arguments ------------------------------------
    5956              :  class(basemat_t), intent(inout) :: mat
    5957              :  real(dp),intent(out) :: local_max
    5958              : 
    5959              : !Local variables-------------------------------
    5960              :  integer :: il1, iglob1, il2, iglob2
    5961              : ! *************************************************************************
    5962              : 
    5963            0 :  local_max = -huge(one)
    5964              : 
    5965              :  select type (mat)
    5966              :  class is (slkmat_dp_t)
    5967            0 :    if (allocated(mat%buffer_real)) return
    5968            0 :    do il2=1,mat%size_local(2)
    5969            0 :      iglob2 = mat%loc2gcol(il2)
    5970            0 :      do il1=1,mat%size_local(1)
    5971            0 :        iglob1 = mat%loc2grow(il1)
    5972            0 :        if (iglob1 == iglob2) then
    5973            0 :          local_max = max(local_max, aimag(mat%buffer_cplx(il1, il2)))
    5974            0 :          mat%buffer_cplx(il1, il2) = real(mat%buffer_cplx(il1, il2))
    5975              :        end if
    5976              :      end do
    5977              :    end do
    5978              : 
    5979              :  class is (slkmat_sp_t)
    5980            0 :    if (allocated(mat%buffer_real)) return
    5981            0 :    do il2=1,mat%size_local(2)
    5982            0 :      iglob2 = mat%loc2gcol(il2)
    5983            0 :      do il1=1,mat%size_local(1)
    5984            0 :        iglob1 = mat%loc2grow(il1)
    5985            0 :        if (iglob1 == iglob2) then
    5986            0 :          local_max = max(local_max, aimag(mat%buffer_cplx(il1, il2)))
    5987            0 :          mat%buffer_cplx(il1, il2) = real(mat%buffer_cplx(il1, il2))
    5988              :        end if
    5989              :      end do
    5990              :    end do
    5991              : 
    5992              :  class default
    5993            0 :    ABI_ERROR("Wrong class")
    5994              :  end select
    5995              : 
    5996              : end subroutine basemat_set_imag_diago_to_zero
    5997              : !!***
    5998              : 
    5999              : !----------------------------------------------------------------------
    6000              : 
    6001              : !!****f* m_slk/slk_write
    6002              : !! NAME
    6003              : !!  slk_write
    6004              : !!
    6005              : !! FUNCTION
    6006              : !!  Routine to write a square scaLAPACK-distributed matrix to an external file using MPI-IO.
    6007              : !!
    6008              : !! INPUTS
    6009              : !!  Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer
    6010              : !!    containing the distributed matrix.
    6011              : !!  uplo=String specifying whether only the upper or lower triangular part of the global matrix is used:
    6012              : !!    = "U":  Upper triangular
    6013              : !!    = "L":  Lower triangular
    6014              : !!    = "A":  Full matrix (used for general complex matrices)
    6015              : !!  is_fortran_file=.FALSE. is C stream is used. .TRUE. for writing Fortran binary files.
    6016              : !!  [fname]= Mutually exclusive with mpi_fh. The name of the external file on which the matrix will be written.
    6017              : !!           The file is open and closed inside the routine with MPI flags specified by flags.
    6018              : !!  [mpi_fh]=File handler associated to the file (already open in the caller). Not compatible with fname.
    6019              : !!  [flags]=MPI-IO flags used to open the file in MPI_FILE_OPEN.
    6020              : !!    Default is MPI_MODE_CREATE + MPI_MODE_WRONLY + MPI_MODE_EXCL.
    6021              : !!  [glob_subarray(2,2)] = Used to select the subarray of the global matrix. Used only when uplo="All"
    6022              : !!     NOTE that each node should call the routine with the same value.
    6023              : !!     glob_subarray(:,1)=starting global coordinates of the subarray in each dimension
    6024              : !!       (array of nonnegative integers >=1, <=array_of_sizes)
    6025              : !!     glob_subarray(:,2)=Number of elements in each dimension of the subarray (array of positive integers)
    6026              : !!
    6027              : !! OUTPUT
    6028              : !!  Only writing. The global scaLAPACK matrix is written to file fname.
    6029              : !!  If fname is present then the file is open and closed inside the routine. Any exception is fatal.
    6030              : !!
    6031              : !! SIDE EFFECTS
    6032              : !!  [offset]=
    6033              : !!    input:  Offset used to access the content of the file. Default is zero.
    6034              : !!    output: New offset incremented with the byte size of the matrix that has been read (Fortran
    6035              : !!            markers are included if is_fortran_file=.TRUE.)
    6036              : !! TODO
    6037              : !!  - Generalize the implementation adding the writing the real buffer.
    6038              : !!  - This routine should be removed and replaced by hdf5 + mpi-io
    6039              : !!
    6040              : !! SOURCE
    6041              : 
    6042            0 : subroutine slk_write(Slk_mat, uplo, is_fortran_file, fname,mpi_fh, offset, flags, glob_subarray)
    6043              : 
    6044              : !Arguments ------------------------------------
    6045              : !scalars
    6046              :  integer,optional,intent(in) :: flags
    6047              :  integer,optional,intent(inout) :: mpi_fh
    6048              :  integer(XMPI_OFFSET_KIND),optional,intent(inout) :: offset
    6049              :  logical,intent(in) :: is_fortran_file
    6050              :  character(len=*),optional,intent(in) :: fname
    6051              :  character(len=*),intent(in) :: uplo
    6052              :  class(slkmat_dp_t),intent(in) :: Slk_mat
    6053              : !array
    6054              :  integer,optional,intent(in) :: glob_subarray(2,2)
    6055              : 
    6056              : !Local variables ------------------------------
    6057              : !scalars
    6058              : #if defined HAVE_LINALG_SCALAPACK && defined HAVE_MPI_IO
    6059              :  integer :: jloc,iloc,nrows_glob,ncols_glob,elw,nrows_w,ncols_w ! iglob,jglob,
    6060              :  integer :: slk_type,offset_err,etype,nfrec,bsize_elm,mpi_type_elm
    6061              :  integer(XMPI_OFFSET_KIND) :: my_offset
    6062              :  logical :: do_open
    6063              :  integer :: comm,my_flags,my_fh,buffer_size
    6064              :  integer :: ierr,nelw,col_glob ! ij_loc,
    6065              : !arrays
    6066              :  integer(XMPI_OFFSET_KIND),allocatable :: bsize_frecord(:)
    6067              :  integer,pointer :: elw2slk(:,:)
    6068              :  complex(dpc),allocatable :: buffer1_cplx(:)
    6069              :  character(len=500) :: msg
    6070              : !************************************************************************
    6071              : 
    6072              :  ABI_CHECK(allocated(Slk_mat%buffer_cplx), "buffer_cplx not allocated")
    6073              : 
    6074              :  if (firstchar(uplo, ["U","L"]) .and. Slk_mat%size_global(1) /= Slk_mat%size_global(2) ) then
    6075              :    ABI_ERROR("rectangular matrices are not compatible with the specified uplo")
    6076              :  end if
    6077              : 
    6078              :  if (PRESENT(glob_subarray).and. .not. firstchar(uplo, ["A"])) then
    6079              :    ABI_ERROR("glob_subarray should not be used when uplo/=All")
    6080              :  end if
    6081              : 
    6082              :  do_open = PRESENT(fname)
    6083              :  if (do_open) then
    6084              :    ABI_CHECK(.not.PRESENT(fname),"fname should not be present")
    6085              :  else
    6086              :    ABI_CHECK(PRESENT(mpi_fh),"mpi_fh should be present")
    6087              :  end if
    6088              : 
    6089              :  my_offset=0; if (PRESENT(offset)) my_offset=offset
    6090              : 
    6091              :  comm = Slk_mat%processor%comm
    6092              : 
    6093              :  nrows_glob=Slk_mat%size_global(1)
    6094              :  ncols_glob=Slk_mat%size_global(1)
    6095              :  buffer_size= PRODUCT(Slk_mat%size_local(1:2))
    6096              : 
    6097              :  call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
    6098              : 
    6099              :  if (do_open) then !Open the file.
    6100              :    my_flags=MPI_MODE_CREATE + MPI_MODE_WRONLY + MPI_MODE_APPEND
    6101              :    if (PRESENT(flags)) my_flags = flags
    6102              : 
    6103              :    call MPI_FILE_OPEN(comm, fname, my_flags, MPI_INFO_NULL, my_fh, ierr)
    6104              :    ABI_CHECK_MPI(ierr, "MPI_FILE_OPEN "//TRIM(fname))
    6105              :  else
    6106              :    my_fh = mpi_fh
    6107              :  end if
    6108              : 
    6109              :  if (PRESENT(glob_subarray)) then
    6110              :    call slk_single_fview_write(Slk_mat,uplo,nelw,elw2slk,etype,slk_type,offset_err,&
    6111              :      is_fortran_file=is_fortran_file,glob_subarray=glob_subarray)
    6112              :  else
    6113              :    call slk_single_fview_write(Slk_mat,uplo,nelw,elw2slk,etype,slk_type,offset_err,&
    6114              :      is_fortran_file=is_fortran_file)
    6115              :  end if
    6116              : 
    6117              :  if (offset_err /= 0) then
    6118              :    write(msg,"(3a)")&
    6119              :     " Global position index cannot be stored in standard Fortran integer ",ch10,&
    6120              :     " scaLAPACK matrix cannot be read with a single MPI-IO call."
    6121              :    ABI_ERROR(msg)
    6122              :  end if
    6123              : 
    6124              :  call MPI_FILE_SET_VIEW(my_fh, my_offset, etype, slk_type, 'native', MPI_INFO_NULL, ierr)
    6125              :  ABI_CHECK_MPI(ierr,"SET_VIEW")
    6126              : 
    6127              :  call MPI_TYPE_FREE(slk_type,ierr)
    6128              :  ABI_CHECK_MPI(ierr,"MPI_type_FREE")
    6129              : 
    6130              :  if (nelw == buffer_size) then
    6131              :    ! Dump Slk_mat% immediately.
    6132              :    call MPI_FILE_WRITE_ALL(my_fh, Slk_mat%buffer_cplx, buffer_size, MPI_DOUBLE_complex, MPI_STATUS_IGNORE, ierr)
    6133              :    ABI_CHECK_MPI(ierr,"WRITE_ALL")
    6134              :  else
    6135              :    ! Have to extract the data to be written.
    6136              :    ABI_MALLOC(buffer1_cplx,(nelw))
    6137              :    do elw=1,nelw
    6138              :      iloc = elw2slk(1,elw)
    6139              :      jloc = elw2slk(2,elw)
    6140              :      buffer1_cplx(elw) = Slk_mat%buffer_cplx(iloc,jloc)
    6141              :    end do
    6142              :    call MPI_FILE_WRITE_ALL(my_fh, buffer1_cplx, nelw, MPI_DOUBLE_complex, MPI_STATUS_IGNORE, ierr)
    6143              :    ABI_CHECK_MPI(ierr,"WRITE_ALL")
    6144              :    ABI_FREE(buffer1_cplx)
    6145              :  end if
    6146              : 
    6147              :  ABI_FREE(elw2slk)
    6148              :  !
    6149              :  ! Number of columns and rows that have been written.
    6150              :  ! Used to write the Fortran markers and to increment the offset.
    6151              :  nrows_w = nrows_glob
    6152              :  ncols_w = ncols_glob
    6153              :  if (PRESENT(glob_subarray)) then
    6154              :    nrows_w = glob_subarray(1,2) - glob_subarray(1,1) + 1
    6155              :    ncols_w = glob_subarray(2,2) - glob_subarray(2,1) + 1
    6156              :    if (.not.firstchar(uplo, ["A"])) then
    6157              :      ABI_ERROR("glob_subarray should not be used when uplo/=All")
    6158              :    end if
    6159              :  end if
    6160              : 
    6161              :  !TODO check whether slk_single_fview_write can report an offset to reduce the extent.
    6162              :  if (is_fortran_file) then ! Collective writing of the Fortran markers.
    6163              :    nfrec = ncols_w
    6164              :    ABI_MALLOC(bsize_frecord,(nfrec))
    6165              :    if (firstchar(uplo, ["A"])) then
    6166              :      bsize_frecord = nrows_w * bsize_elm
    6167              :    else if (firstchar(uplo, ["U"])) then
    6168              :      bsize_frecord = (/(col_glob * bsize_elm, col_glob=1,nfrec)/)
    6169              :    else if (firstchar(uplo, ["L"])) then
    6170              :      bsize_frecord = (/(col_glob * bsize_elm, col_glob=nfrec,1,-1)/)
    6171              :    else
    6172              :      ABI_ERROR("Wrong uplo")
    6173              :    end if
    6174              :    call xmpio_write_frmarkers(mpi_fh,my_offset,xmpio_collective,nfrec,bsize_frecord,ierr)
    6175              :    ABI_CHECK(ierr==0,"Error while writing Fortran markers")
    6176              :    ABI_FREE(bsize_frecord)
    6177              :  end if
    6178              : 
    6179              :  if (do_open) then
    6180              :    ! Close the file.
    6181              :    call MPI_FILE_CLOSE(my_fh, ierr)
    6182              :    ABI_CHECK_MPI(ierr,"FILE_CLOSE")
    6183              :  end if
    6184              : 
    6185              :  ! Increment the offset
    6186              :  if (PRESENT(offset)) then
    6187              :    if (firstchar(uplo, ["A"])) then
    6188              :      offset = offset + nrows_w*ncols_w*bsize_elm
    6189              :      if (is_fortran_file) offset = offset + ncols_w*2*xmpio_bsize_frm
    6190              :    else if (firstchar(uplo, ["U","L"])) then
    6191              :      offset = offset + ( (Slk_mat%size_global(2) * (Slk_mat%size_global(2))+1)/2 ) * bsize_elm
    6192              :      if (is_fortran_file) offset = offset + Slk_mat%size_global(2)*2*xmpio_bsize_frm
    6193              :    else
    6194              :      ABI_ERROR("Wrong uplo")
    6195              :    end if
    6196              :  end if
    6197              : 
    6198              :  call xmpi_barrier(comm)
    6199              :  RETURN
    6200              : 
    6201              : #else
    6202            0 :   ABI_ERROR("MPI-IO support not activated")
    6203              : #endif
    6204              : 
    6205            0 : end subroutine slk_write
    6206              : !!***
    6207              : 
    6208              : !----------------------------------------------------------------------
    6209              : 
    6210              : !!****f* m_slk/slk_read
    6211              : !! NAME
    6212              : !!  slk_read
    6213              : !!
    6214              : !! FUNCTION
    6215              : !!  Routine to read a square scaLAPACK distributed matrix from an external file using MPI-IO.
    6216              : !!
    6217              : !! INPUTS
    6218              : !!  uplo=String specifying whether only the upper or lower triangular part of the global matrix is stored on disk:
    6219              : !!    = "U":  Upper triangular is stored
    6220              : !!    = "L":  Lower triangular is stored
    6221              : !!    = "A":  Full matrix (used for general complex matrices)
    6222              : !!  symtype=Symmetry type of the matrix stored on disk (used only if uplo = "L" or "A").
    6223              : !!    = "H" for Hermitian matrix
    6224              : !!    = "S" for symmetric matrix.
    6225              : !!    = "N" if matrix has no symmetry (not compatible with uplo="L" or uplo="U".
    6226              : !!  is_fortran_file=.FALSE. is C stream is used. .TRUE. for writing Fortran binary files.
    6227              : !!  [fname]= Mutually exclusive with mpi_fh. The name of the external file from which the matrix will be read.
    6228              : !!           The file is open and closed inside the routine with MPI flags specified by flags.
    6229              : !!  [mpi_fh]=File handler associated to the file (already open in the caller). Not compatible with fname.
    6230              : !!  [flags]=MPI-IO flags used to open the file in MPI_FILE_OPEN. Default is MPI_MODE_RDONLY. Referenced only when fname is used.
    6231              : !!
    6232              : !! SIDE EFFECTS
    6233              : !!  Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer
    6234              : !!    supposed to be allocated.
    6235              : !!    %buffer_cplx=Local buffer containg the distributed matrix stored on the external file.
    6236              : !!  If fname is present then the file is opened and closed inside the routine. Any exception is fatal.
    6237              : !!  [offset]=
    6238              : !!    input:  Offset used to access the content of the file. Default is zero.
    6239              : !!    output: New offset incremented with the byte size of the matrix that has been read (Fortran
    6240              : !!            markers are included if is_fortran_file=.TRUE.)
    6241              : !!
    6242              : !! TODO
    6243              : !!  - Generalize the implementation adding the reading of the real buffer.
    6244              : !!
    6245              : !!  - This routine is not portable as this kind of access pattern is not supported by all MPI implementations
    6246              : !!    E.g. with MPICH we have
    6247              : !!
    6248              : !! --- !ERROR
    6249              : !! src_file: m_slk.F90
    6250              : !! src_line: 3780
    6251              : !! mpi_rank: 1
    6252              : !! message: |
    6253              : !!     SET_VIEW
    6254              : !!     Other I/O error , error stack:
    6255              : !!     ADIO_Set_view(48):  **iobadoverlap displacements of filetype must be in a monotonically nondecreasing order
    6256              : !! ...
    6257              : !!
    6258              : !! - This routine should be removed and replaced by hdf5 + mpi-io
    6259              : !!
    6260              : !! SOURCE
    6261              : 
    6262            0 : subroutine slk_read(Slk_mat,uplo,symtype,is_fortran_file,fname,mpi_fh,offset,flags)
    6263              : 
    6264              : !Arguments ------------------------------------
    6265              : !scalars
    6266              :  integer,optional,intent(in) :: flags,mpi_fh
    6267              :  integer(XMPI_OFFSET_KIND),optional,intent(inout) :: offset
    6268              :  character(len=*),optional,intent(in) :: fname
    6269              :  character(len=*),intent(in) :: uplo,symtype
    6270              :  logical,intent(in) :: is_fortran_file
    6271              :  class(slkmat_dp_t),intent(inout) :: Slk_mat
    6272              : 
    6273              : !Local variables ------------------------------
    6274              : #if defined HAVE_LINALG_SCALAPACK && defined HAVE_MPI_IO
    6275              : !scalars
    6276              :  integer :: nrows_glob,offset_err,slk_type,etype
    6277              :  integer(XMPI_OFFSET_KIND) :: my_offset
    6278              :  logical :: do_open
    6279              :  integer :: comm,my_flags,my_fh,buffer_size,ierr,col_glob
    6280              :  integer :: nfrec,bsize_elm,mpi_type_elm
    6281              :  !complex(dpc) :: ctest
    6282              :  logical,parameter :: check_frm=.TRUE.
    6283              :  integer(XMPI_OFFSET_KIND),allocatable :: bsize_frecord(:)
    6284              : !arrays
    6285              :  character(len=500) :: msg
    6286              : !************************************************************************
    6287              : 
    6288              :  do_open = PRESENT(fname)
    6289              :  if (do_open) then
    6290              :    ABI_CHECK(.not.PRESENT(fname), "fname should not be present")
    6291              :  else
    6292              :    ABI_CHECK(PRESENT(mpi_fh), "mpi_fh should be present")
    6293              :  end if
    6294              : 
    6295              :  my_offset=0; if (PRESENT(offset)) my_offset=offset
    6296              : 
    6297              :  ABI_CHECK(allocated(Slk_mat%buffer_cplx), "%buffer_cplx not allocated")
    6298              :  if (firstchar(uplo, ["U","L"]) .and. Slk_mat%size_global(1) /= Slk_mat%size_global(2) ) then
    6299              :    ABI_ERROR("rectangular matrices are not compatible with the specified uplo")
    6300              :  end if
    6301              : 
    6302              :  nrows_glob = Slk_mat%size_global(1)
    6303              : 
    6304              :  buffer_size= PRODUCT(Slk_mat%size_local(1:2))
    6305              : 
    6306              :  call wrtout(std_out, "slk_read: Using MPI-IO")
    6307              : 
    6308              :  comm = Slk_mat%processor%comm
    6309              : 
    6310              :  if (do_open) then ! Open the file.
    6311              :    my_flags=MPI_MODE_RDONLY; if (PRESENT(flags)) my_flags = flags
    6312              :    call MPI_FILE_OPEN(comm, fname, my_flags, MPI_INFO_NULL, my_fh, ierr)
    6313              :    ABI_CHECK_MPI(ierr,"FILE_OPEN "//TRIM(fname))
    6314              :  else
    6315              :    my_fh = mpi_fh
    6316              :  end if
    6317              : 
    6318              :  call slk_single_fview_read(Slk_mat,uplo,etype,slk_type,offset_err,is_fortran_file=is_fortran_file)
    6319              : 
    6320              :  if (offset_err/=0) then
    6321              :    write(msg,"(3a)")&
    6322              :     "Global position index cannot be stored in standard Fortran integer ",ch10,&
    6323              :     "scaLAPACK matrix cannot be read with a single MPI-IO call."
    6324              :    ABI_ERROR(msg)
    6325              :  end if
    6326              : 
    6327              :  call MPI_FILE_SET_VIEW(my_fh, my_offset, etype, slk_type, 'native', MPI_INFO_NULL, ierr)
    6328              :  ABI_CHECK_MPI(ierr,"SET_VIEW")
    6329              : 
    6330              :  call MPI_FILE_READ_ALL(my_fh, Slk_mat%buffer_cplx, buffer_size, MPI_DOUBLE_complex, MPI_STATUS_IGNORE, ierr)
    6331              :  ABI_CHECK_MPI(ierr,"READ_ALL")
    6332              : 
    6333              :  ! Symmetrize local buffer if uplo /= "All"
    6334              :  call Slk_mat%symmetrize(uplo, symtype)
    6335              : 
    6336              : !BEGINDEBUG
    6337              : !call MPI_FILE_READ_AT(mpi_fh,my_offset+xmpio_bsize_frm,ctest,1,MPI_DOUBLE_complex,MPI_STATUS_IGNORE,ierr)
    6338              : !write(std_out,*)"ctest",ctest
    6339              : !call MPI_FILE_READ_AT(mpi_fh,my_offset+2*xmpio_bsize_frm,ctest,1,MPI_DOUBLE_complex,MPI_STATUS_IGNORE,ierr)
    6340              : !write(std_out,*)"ctest",ctest
    6341              : !ENDDEBUG
    6342              : 
    6343              :  !call print_arr(Slk_mat%buffer_cplx,max_r=10,max_c=10,unit=std_out)
    6344              :  !
    6345              :  ! Close the file and release the MPI filetype.
    6346              :  call MPI_type_FREE(slk_type,ierr)
    6347              :  ABI_CHECK_MPI(ierr,"MPI_type_FREE")
    6348              : 
    6349              :  call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
    6350              : 
    6351              : !It seems that personal call makes the code stuck
    6352              : !if (is_fortran_file .and. check_frm .and. Slk_mat%Processor%my_rank==0) then ! Master checks the Fortran markers.
    6353              :  if (is_fortran_file .and. check_frm) then ! Master checks the Fortran markers.
    6354              :    call wrtout(std_out,"Checking Fortran record markers...", do_flush=.True.)
    6355              :    nfrec = Slk_mat%size_global(2)
    6356              :    ABI_MALLOC(bsize_frecord,(nfrec))
    6357              :    if (firstchar(uplo, ["A"])) then
    6358              :      bsize_frecord = Slk_mat%size_global(1) * bsize_elm
    6359              :    else if (firstchar(uplo, ["U"])) then
    6360              :      bsize_frecord = (/(col_glob * bsize_elm, col_glob=1,nfrec)/)
    6361              :    else if (firstchar(uplo, ["L"])) then
    6362              :      bsize_frecord = (/(col_glob * bsize_elm, col_glob=nfrec,1,-1)/)
    6363              :    else
    6364              :      ABI_ERROR("Wrong uplo")
    6365              :    end if
    6366              :    call xmpio_check_frmarkers(my_fh,my_offset,xmpio_collective,nfrec,bsize_frecord,ierr)
    6367              :    ABI_CHECK(ierr==0,"Wrong Fortran record markers")
    6368              :    ABI_FREE(bsize_frecord)
    6369              :  end if
    6370              : 
    6371              :  if (do_open) then ! Close the file.
    6372              :    call MPI_FILE_CLOSE(my_fh, ierr)
    6373              :    ABI_CHECK_MPI(ierr,"FILE_CLOSE")
    6374              :  end if
    6375              : 
    6376              : !Increment the offset
    6377              :  if (PRESENT(offset)) then
    6378              :    if (firstchar(uplo, ["A"])) then
    6379              :      offset = offset + PRODUCT(Slk_mat%size_global(1:2)) * bsize_elm
    6380              :      if (is_fortran_file) offset = offset + Slk_mat%size_global(2)*2*xmpio_bsize_frm
    6381              :    else if (firstchar(uplo, ["U","L"])) then
    6382              :      offset = offset + ( (Slk_mat%size_global(2) * (Slk_mat%size_global(2))+1)/2 ) * bsize_elm
    6383              :      if (is_fortran_file) offset = offset + Slk_mat%size_global(2)*2*xmpio_bsize_frm
    6384              :    else
    6385              :      ABI_ERROR("Wrong uplo")
    6386              :    end if
    6387              :  end if
    6388              : 
    6389              :  call xmpi_barrier(comm)
    6390              :  RETURN
    6391              : 
    6392              : #else
    6393            0 :  ABI_ERROR("MPI-IO support not enabled")
    6394              : #endif
    6395              : 
    6396            0 : end subroutine slk_read
    6397              : !!***
    6398              : 
    6399              : !----------------------------------------------------------------------
    6400              : 
    6401              : !!****f* m_slk/slk_single_fview_read_mask
    6402              : !! NAME
    6403              : !!  slk_single_fview_read_mask
    6404              : !!
    6405              : !! FUNCTION
    6406              : !!  Return an MPI datatype that can be used to read a scaLAPACK distributed matrix from
    6407              : !!  a binary file using MPI-IO. The view is created using the user-defined mask function
    6408              : !!  mask_of_glob. The storage of the data on file is described via the user-defined function offset_of_glob.
    6409              : !!
    6410              : !! INPUTS
    6411              : !!  Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK matrix.
    6412              : !!  mask_of_glob(row_glob,col_glob,size_glob) is an integer function that accepts in input
    6413              : !!     the global indices of the matrix size_glob(1:2) are the global dimensions.
    6414              : !!     Return 0 if (row_glob,col_glob) should not be read.
    6415              : !!  offset_of_glob(row_glob,col_glob,size_glob,nsblocks,sub_block,bsize_elm,bsize_frm)
    6416              : !!  nsblocks=Number of sub-blocks (will be passed to offset_of_glob)
    6417              : !!  sub_block(2,2,nsblocks)=Global coordinates of the extremal points delimiting the sub-blocs
    6418              : !!   e.g. sub_block(:,1,1) gives the coordinates of the left upper corner of the first block.
    6419              : !!        sub_block(:,2,1) gives the coordinates of the right lower corner of the first block.
    6420              : !!  [is_fortran_file]=.FALSE. is C stream is used. Set to .TRUE. for writing Fortran binary files
    6421              : !!    with record marker.
    6422              : !!
    6423              : !! OUTPUT
    6424              : !!  my_nel=Number of elements that will be read by this node.
    6425              : !!  etype=Elementary data type (handle) defining the elementary unit used to access the file.
    6426              : !!    This is the elementary type that must be used to creae the view (MPI_BYTE is used).
    6427              : !!  slk_type=New MPI type that can be used to instantiate the MPI-IO view for the Fortran file.
    6428              : !!     Note that the view assumes that the file pointer points to the FIRST Fortran record marker.
    6429              : !!  offset_err=Error code. A returned non-zero value signals that the global matrix is too large
    6430              : !!    for a single MPI-IO access. See notes in other slk_single_fview_* routines.
    6431              : !!
    6432              : !! SIDE EFFECTS
    6433              : !!  myel2loc(:,:)
    6434              : !!    input: pointer to NULL
    6435              : !!    output: myel2loc(2,my_nel):  myel2loc(:,el) gives (iloc,jloc) for el=1,my_nel.
    6436              : !!
    6437              : !! SOURCE
    6438              : 
    6439            0 : subroutine slk_single_fview_read_mask(Slk_mat,mask_of_glob,offset_of_glob,nsblocks,sub_block,&
    6440              :                                       my_nel,myel2loc,etype,slk_type,offset_err,is_fortran_file)
    6441              : 
    6442              : !Arguments ------------------------------------
    6443              : !scalars
    6444              :  integer,intent(in) :: nsblocks
    6445              :  integer,intent(out) :: my_nel,offset_err,slk_type,etype
    6446              :  logical,optional,intent(in) :: is_fortran_file
    6447              :  class(slkmat_dp_t),intent(in) :: Slk_mat
    6448              : !arrays
    6449              :  integer,intent(in) :: sub_block(2,2,nsblocks)
    6450              :  integer,pointer :: myel2loc(:,:)
    6451              : 
    6452              :  interface
    6453              :    function mask_of_glob(row_glob,col_glob,size_glob)
    6454              :      use defs_basis
    6455              :      integer :: mask_of_glob
    6456              :      integer,intent(in) :: row_glob,col_glob
    6457              :      integer,intent(in) :: size_glob(2)
    6458              :    end function mask_of_glob
    6459              :  end interface
    6460              : 
    6461              :  interface
    6462              :    function offset_of_glob(row_glob,col_glob,size_glob,nsblocks,sub_block,bsize_elm,bsize_frm)
    6463              :      use defs_basis
    6464              :      use m_xmpi
    6465              :      integer(XMPI_OFFSET_KIND) :: offset_of_glob
    6466              :      integer,intent(in) :: row_glob,col_glob,bsize_elm,bsize_frm,nsblocks
    6467              :      integer,intent(in) :: size_glob(2),sub_block(2,2,nsblocks)
    6468              :    end function offset_of_glob
    6469              :  end interface
    6470              : 
    6471              : !Local variables ------------------------------
    6472              : !scalars
    6473              :  integer :: el,jloc,iloc,iglob,jglob,mpi_err,sweep
    6474              :  integer :: bsize_frm,mpi_type_elm,bsize_elm
    6475              :  integer(XMPI_OFFSET_KIND) :: tmp_off,max_displ
    6476              : !arrays
    6477              :  character(len=500) :: msg
    6478            0 :  integer,allocatable :: block_length(:),block_type(:)
    6479            0 :  integer(XMPI_ADDRESS_KIND),allocatable :: block_displ(:)
    6480              : !************************************************************************
    6481              : 
    6482              : #ifdef HAVE_MPI_IO
    6483            0 :  bsize_frm = xmpio_bsize_frm  ! Byte size of the Fortran record marker.
    6484            0 :  if (PRESENT(is_fortran_file)) then
    6485            0 :    if (.not.is_fortran_file) bsize_frm = 0
    6486              :  end if
    6487              : 
    6488              :  ! Byte size of the matrix element.
    6489            0 :  call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
    6490              : 
    6491              :  ! Find the number of local matrix elements to be read, then create the table myel2loc.
    6492            0 :  do sweep=1,2
    6493            0 :    if (sweep==2) then
    6494            0 :       ABI_MALLOC(myel2loc,(2,my_nel))
    6495              :    end if
    6496            0 :    my_nel=0
    6497              : 
    6498            0 :    do jloc=1,Slk_mat%size_local(2)
    6499            0 :      do iloc=1,Slk_mat%size_local(1)
    6500            0 :        call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    6501            0 :        if ( mask_of_glob(iglob,jglob,Slk_mat%size_global)/= 0) then ! Will fill this entry.
    6502            0 :          my_nel  = my_nel+1
    6503            0 :          if (sweep==2) myel2loc(:,my_nel) = (/iloc,jloc/)
    6504              :        end if
    6505              :      end do
    6506              :    end do
    6507              :  end do
    6508              : 
    6509            0 :  etype = MPI_BYTE
    6510              : 
    6511              :  ! Define the mapping between scaLAPACK buffer and the storage on file.
    6512              :  ! Note that the view assumes that the file pointer points to the first Fortran record marker.
    6513            0 :  ABI_MALLOC(block_length,(my_nel+2))
    6514            0 :  ABI_MALLOC(block_displ,(my_nel+2))
    6515            0 :  ABI_MALLOC(block_type,(my_nel+2))
    6516            0 :  block_length(1)=1
    6517            0 :  block_displ (1)=0
    6518            0 :  block_type  (1)=MPI_LB
    6519              : 
    6520            0 :  offset_err=0; max_displ=0
    6521            0 :  do el=1,my_nel
    6522            0 :    iloc = myel2loc(1,el)
    6523            0 :    jloc = myel2loc(2,el)
    6524            0 :    call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    6525            0 :    tmp_off = offset_of_glob(iglob,jglob,Slk_mat%size_global,nsblocks,sub_block,bsize_elm,bsize_frm)
    6526            0 :    if (xmpio_max_address(tmp_off)) offset_err=1   ! Test for possible wraparounds.
    6527            0 :    max_displ = MAX(max_displ,tmp_off)
    6528            0 :    block_displ (el+1) = tmp_off
    6529            0 :    block_type  (el+1) = mpi_type_elm
    6530            0 :    block_length(el+1) = 1
    6531              :    !write(std_out,*)" iglob, jglob, tmp_off ",iglob, jglob, tmp_off
    6532              :  end do
    6533              :  !write(std_out,*)" MAX displ is ",MAXVAL(block_displ)
    6534              : 
    6535            0 :  if (offset_err/=0) then  ! just warn, let the caller handle the exception.
    6536              :    write(msg,"(3a)")&
    6537            0 :     " Global position index cannot be stored in standard Fortran integer ",ch10,&
    6538            0 :     " scaLAPACK matrix cannot be read with a single MPI-IO call ."
    6539            0 :    ABI_WARNING(msg)
    6540              :  end if
    6541              : 
    6542            0 :  block_length(my_nel+2) = 1
    6543            0 :  block_displ (my_nel+2) = max_displ
    6544            0 :  block_type  (my_nel+2) = MPI_UB
    6545              : 
    6546            0 :  call xmpio_type_struct(my_nel+2,block_length,block_displ,block_type,slk_type,mpi_err)
    6547            0 :  ABI_CHECK_MPI(mpi_err,"MPI_type_STRUCT")
    6548              : 
    6549            0 :  ABI_FREE(block_length)
    6550            0 :  ABI_FREE(block_displ)
    6551            0 :  ABI_FREE(block_type)
    6552              : 
    6553            0 :  call MPI_type_COMMIT(slk_type,mpi_err)
    6554            0 :  ABI_CHECK_MPI(mpi_err,"MPI_type_COMMIT")
    6555              : 
    6556              : #else
    6557              :  ABI_ERROR("MPI-IO is mandatatory in slk_single_fview_read_mask")
    6558              : #endif
    6559              : 
    6560            0 : end subroutine slk_single_fview_read_mask
    6561              : !!***
    6562              : 
    6563              : !----------------------------------------------------------------------
    6564              : 
    6565              : !!****f* m_slk/slkmat_dp_symmetrize
    6566              : !! NAME
    6567              : !!  slkmat_dp_symmetrize
    6568              : !!
    6569              : !! FUNCTION
    6570              : !!  Symmetrize a square scaLAPACK matrix.
    6571              : !!
    6572              : !! INPUTS
    6573              : !!  uplo=String specifying whether only the upper or lower triangular part of the global matrix has been read
    6574              : !!    = "U":  Upper triangular has been read.
    6575              : !!    = "L":  Lower triangular has been read.
    6576              : !!    = "A":  Full matrix (used for general complex matrices)
    6577              : !!  symtype=Symmetry type of the matrix (used only if uplo = "L" or "A").
    6578              : !!    = "H" for Hermitian matrix
    6579              : !!    = "S" for symmetric matrix.
    6580              : !!    = "N" if matrix has no symmetry (not compatible with uplo="L" or uplo="U".
    6581              : !!
    6582              : !! SIDE EFFECTS
    6583              : !!  Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer
    6584              : !!    supposed to be allocated.
    6585              : !!    %buffer_cplx=Local buffer containg the distributed matrix stored on the external file.
    6586              : !!
    6587              : !! SOURCE
    6588              : 
    6589            0 : subroutine slkmat_dp_symmetrize(Slk_mat, uplo, symtype)
    6590              : 
    6591              : !Arguments ------------------------------------
    6592              : !scalars
    6593              :  class(slkmat_dp_t),intent(inout) :: Slk_mat
    6594              :  character(len=*),intent(in) :: uplo, symtype
    6595              : 
    6596              : !Local variables ------------------------------
    6597              : !scalars
    6598              :  integer :: jloc,iloc,iglob,jglob,ij_loc
    6599              :  logical :: is_hermitian,is_real,is_cplx,is_symmetric
    6600              :  character(len=500) :: msg
    6601              : !************************************************************************
    6602              : 
    6603            0 :  is_cplx = (allocated(Slk_mat%buffer_cplx))
    6604            0 :  is_real = (allocated(Slk_mat%buffer_real))
    6605              : 
    6606              :  ! One and only one buffer should be allocated.
    6607            0 :  if (is_real .and. is_cplx) then
    6608            0 :    write(msg,'(a,2l1)')" ScaLAPACK buffers are not allocated correctly, is_real, is_cplx ",is_real,is_cplx
    6609            0 :    ABI_ERROR(msg)
    6610              :  end if
    6611              : 
    6612            0 :  if (is_real) RETURN
    6613              : 
    6614            0 :  is_hermitian=.FALSE.; is_symmetric=.FALSE.
    6615              :  select case (symtype(1:1))
    6616              :  case ("H", "h")
    6617              :    is_hermitian = .TRUE.
    6618              :  case ("S","s")
    6619            0 :    is_symmetric = .TRUE.
    6620              :  case("N","n")
    6621            0 :    if (ALL(uplo(1:1) /= ["A","a"])) then
    6622            0 :      msg = " Found symtype= "//TRIM(symtype)//", but uplo= "//TRIM(uplo)
    6623            0 :      ABI_ERROR(msg)
    6624              :    end if
    6625            0 :    RETURN  ! Nothing to do.
    6626              :  case default
    6627            0 :    ABI_ERROR("Wrong symtype "//TRIM(symtype))
    6628              :  end select
    6629              : 
    6630              :  !write(std_out,*)"is_cplx",is_cplx
    6631              :  !write(std_out,*)"is_hermitian",is_hermitian
    6632              : 
    6633              :  select case (uplo(1:1))
    6634              :  case ("A","a")
    6635              :    ! Full global matrix has been read, nothing to do.
    6636            0 :    return
    6637              : 
    6638              :  case ("U", "u")
    6639              :    ! Only the upper triangle of the global matrix was read.
    6640            0 :    if (is_cplx .and. is_hermitian) then
    6641            0 :      ij_loc=0
    6642            0 :      do jloc=1,Slk_mat%size_local(2)
    6643            0 :        do iloc=1,Slk_mat%size_local(1)
    6644            0 :          call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    6645            0 :          ij_loc = ij_loc+1
    6646            0 :          if (jglob < iglob) then
    6647              :            ! Diagonal elements are not forced to be real.
    6648            0 :            Slk_mat%buffer_cplx(iloc,jloc) = DCONJG(Slk_mat%buffer_cplx(iloc,jloc))
    6649              :          end if
    6650              :          !if (iglob==jglob) Slk_mat%buffer_cplx(iloc,jloc) =  real(Slk_mat%buffer_cplx(iloc,jloc))
    6651              :        end do
    6652              :      end do
    6653              :    end if
    6654              : 
    6655              :  case ("L", "l")
    6656              :    ! Only the lower triangle of the global matrix was read.
    6657            0 :    if (is_cplx .and. is_hermitian) then
    6658            0 :      ij_loc=0
    6659            0 :      do jloc=1,Slk_mat%size_local(2)
    6660            0 :        do iloc=1,Slk_mat%size_local(1)
    6661            0 :          call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    6662            0 :          ij_loc = ij_loc+1
    6663            0 :          if (jglob>iglob) then ! diagonal elements are not forced to be real.
    6664            0 :            Slk_mat%buffer_cplx(iloc,jloc) =  DCONJG(Slk_mat%buffer_cplx(iloc,jloc))
    6665              :          end if
    6666              :          !if (iglob==jglob) Slk_mat%buffer_cplx(iloc,jloc) =  real(Slk_mat%buffer_cplx(iloc,jloc))
    6667              :        end do
    6668              :      end do
    6669              :    end if
    6670              : 
    6671              :  case default
    6672            0 :    ABI_BUG(" Wrong uplo: "//TRIM(uplo))
    6673              :  end select
    6674              : 
    6675              : end subroutine slkmat_dp_symmetrize
    6676              : !!***
    6677              : 
    6678              : !----------------------------------------------------------------------
    6679              : 
    6680              : !!****f* m_slk/slk_single_fview_read
    6681              : !! NAME
    6682              : !!  slk_single_fview_read
    6683              : !!
    6684              : !! FUNCTION
    6685              : !!  Return an MPI datatype that can be used to read a scaLAPACK distributed matrix from
    6686              : !!  a binary file using MPI-IO.
    6687              : !!
    6688              : !! INPUTS
    6689              : !!  Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer.
    6690              : !!  uplo=String specifying whether only the upper or lower triangular part of the global matrix is stored on disk:
    6691              : !!    = "U":  Upper triangular is stored
    6692              : !!    = "L":  Lower triangular is stored
    6693              : !!    = "A":  Full matrix (used for general complex matrices)
    6694              : !!  [is_fortran_file]=.FALSE. is C stream is used. .TRUE. for writing Fortran binary files
    6695              : !!    with record markers. In this case etype is set xmpio_mpi_type_frm provided that
    6696              : !!    the mpi_type of the matrix element is commensurate with xmpio_mpi_type_frm. Defaults to .TRUE.
    6697              : !!
    6698              : !! OUTPUT
    6699              : !!  etype=Elementary data type (handle) defining the elementary unit used to access the file.
    6700              : !!  slk_type=New MPI type that can be used to instantiate the MPI-IO view for the Fortran file.
    6701              : !!     Note that the view assumes that the file pointer points to the FIRST Fortran record marker.
    6702              : !!  offset_err=Error code. A non-zero value signals that the global matrix is too large
    6703              : !!    for a single MPI-IO access (see notes below).
    6704              : !!
    6705              : !! NOTES
    6706              : !!  With (signed) Fortran integers, the maximum size of the file that
    6707              : !!  that can be read in one-shot is around 2Gb when etype is set to byte.
    6708              : !!  Using a larger etype might create portability problems (real data on machines using
    6709              : !!  integer*16 for the marker) since etype must be a multiple of the Fortran record marker
    6710              : !!  Due to the above reason, block_displ is given in bytes and must be stored in a integer
    6711              : !!  of kind XMPI_ADDRESS_KIND. If the displacement is too large, the routine returns
    6712              : !!  offset_err=1 so that the caller will know that several MPI-IO reads are needed to
    6713              : !!  read the local buffer.
    6714              : !!
    6715              : !! SOURCE
    6716              : 
    6717            0 : subroutine slk_single_fview_read(Slk_mat,uplo,etype,slk_type,offset_err,is_fortran_file)
    6718              : 
    6719              : !Arguments ------------------------------------
    6720              : !scalars
    6721              :  class(slkmat_dp_t),intent(in) :: Slk_mat
    6722              :  integer,intent(out) :: offset_err,slk_type,etype
    6723              :  character(len=*),intent(in) :: uplo
    6724              :  logical,optional,intent(in) :: is_fortran_file
    6725              : 
    6726              : !Local variables ------------------------------
    6727              : !scalars
    6728              :  integer :: jloc,iloc,iglob,jglob,nrows_glob,ncols_glob,mpi_err,nel
    6729              :  integer :: bsize_frm,mpi_type_elm,ij_loc,bsize_etype,bsize_elm
    6730              :  integer(XMPI_OFFSET_KIND) :: ijp_glob,my_offset,cpad_frm
    6731              : !arrays
    6732              :  character(len=500) :: msg
    6733            0 :  integer,allocatable :: block_length(:),block_type(:)
    6734            0 :  integer(XMPI_ADDRESS_KIND),allocatable :: block_displ(:)
    6735              : !************************************************************************
    6736              : 
    6737              : #ifdef HAVE_MPI_IO
    6738              : !@slkmat_dp_t
    6739            0 :  bsize_frm = xmpio_bsize_frm    ! Byte size of the Fortran record marker.
    6740            0 :  if (PRESENT(is_fortran_file)) then
    6741            0 :    if (.not.is_fortran_file) bsize_frm = 0
    6742              :  end if
    6743              : 
    6744            0 :  call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
    6745              : 
    6746              :  ! Global dimensions.
    6747            0 :  nrows_glob=Slk_mat%size_global(1)
    6748            0 :  ncols_glob=Slk_mat%size_global(2)
    6749              : 
    6750              :  ! Number of matrix elements treated by this node.
    6751            0 :  nel = PRODUCT(Slk_mat%size_local(1:2))
    6752              : 
    6753              :  !Cannot use MPI_type_CREATE_INDEXED_BLOCK since it is not correctly implemented in several MPI libraries.
    6754              :  !etype has to be set to MPI_BYTE, since the displacement in MPI structures is always in byte.
    6755              :  ! ABI_WARNING("Using MPI_type_STRUCT for the MPI-IO file view")
    6756              : 
    6757            0 :  etype = MPI_BYTE
    6758            0 :  call MPI_type_SIZE(etype,bsize_etype,mpi_err)
    6759              : 
    6760              :  ! Define the mapping between scaLAPACK buffer and the storage on file.
    6761            0 :  ABI_MALLOC(block_length, (nel+2))
    6762            0 :  ABI_MALLOC(block_displ, (nel+2))
    6763            0 :  ABI_MALLOC(block_type, (nel+2))
    6764            0 :  block_length(1)=1
    6765            0 :  block_displ (1)=0
    6766            0 :  block_type  (1)=MPI_LB
    6767              : 
    6768              :  ! Note that the view assumes that the file pointer points to the first Fortran record marker.
    6769            0 :  offset_err=0
    6770            0 :  select case (uplo(1:1))
    6771              :  case ("A","a")
    6772              :    ! The entire global matrix is stored on disk.
    6773              :    ! TODO can use contigous vectors for better access.
    6774            0 :    ij_loc=0
    6775            0 :    do jloc=1,Slk_mat%size_local(2)
    6776            0 :      do iloc=1,Slk_mat%size_local(1)
    6777            0 :        call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    6778            0 :        ij_loc  = ij_loc+1
    6779            0 :        my_offset = 2*(jglob-1)*bsize_frm + bsize_frm + (jglob-1)*nrows_glob*bsize_elm + (iglob-1) * bsize_elm
    6780            0 :        my_offset = my_offset / bsize_etype
    6781            0 :        if (xmpio_max_address(my_offset)) offset_err=1   ! Test for possible wraparounds
    6782            0 :        block_displ (ij_loc+1) = my_offset
    6783            0 :        block_type  (ij_loc+1) = mpi_type_elm
    6784            0 :        block_length(ij_loc+1) = 1
    6785              :      end do
    6786              :    end do
    6787              : 
    6788              :  case ("U","u")
    6789              :    ! Only the upper triangle of the global matrix is stored on disk.
    6790            0 :    ij_loc=0
    6791            0 :    do jloc=1,Slk_mat%size_local(2)
    6792            0 :      do iloc=1,Slk_mat%size_local(1)
    6793            0 :        call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    6794            0 :        if (jglob>=iglob) then
    6795            0 :          ijp_glob = iglob + jglob*(jglob-1)/2  ! Index for packed form
    6796            0 :          cpad_frm = 2*(jglob-1)*bsize_frm
    6797              :        else
    6798            0 :          ijp_glob = jglob + iglob*(iglob-1)/2  ! Index for packed form
    6799            0 :          cpad_frm = 2*(iglob-1)*bsize_frm
    6800              :        end if
    6801            0 :        ij_loc = ij_loc+1
    6802            0 :        my_offset = cpad_frm + bsize_frm + (ijp_glob-1) * bsize_elm
    6803            0 :        my_offset = my_offset / bsize_etype
    6804            0 :        if (xmpio_max_address(my_offset)) offset_err=1  ! Test for possible wraparounds
    6805            0 :        block_displ (ij_loc+1) = my_offset
    6806            0 :        block_type  (ij_loc+1) = mpi_type_elm
    6807            0 :        block_length(ij_loc+1) = 1
    6808              :      end do
    6809              :    end do
    6810              : 
    6811              :  case ("L","l")
    6812              :    ! Only the lower triangle of the global matrix is stored on disk.
    6813            0 :    ij_loc=0
    6814            0 :    do jloc=1,Slk_mat%size_local(2)
    6815            0 :      do iloc=1,Slk_mat%size_local(1)
    6816            0 :        call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    6817            0 :        if (jglob<=iglob) then
    6818            0 :          ijp_glob = iglob + (jglob-1)*(2*nrows_glob-jglob)/2 ! Index for packed form
    6819            0 :          cpad_frm = 2*(jglob-1)*bsize_frm
    6820              :        else
    6821            0 :          ijp_glob = jglob + (iglob-1)*(2*nrows_glob-iglob)/2 ! Index for packed form
    6822            0 :          cpad_frm = 2*(iglob-1)*bsize_frm
    6823              :        end if
    6824            0 :        ij_loc = ij_loc+1
    6825            0 :        my_offset = cpad_frm + bsize_frm + (ijp_glob-1) * bsize_elm
    6826            0 :        my_offset = my_offset / bsize_etype
    6827            0 :        if (xmpio_max_address(my_offset)) offset_err=1   ! block_displ is usually integer*4. Test for possible wraparounds
    6828            0 :        block_displ  (ij_loc+1) = my_offset
    6829            0 :        block_type  (ij_loc+1) = mpi_type_elm
    6830            0 :        block_length(ij_loc+1) = 1
    6831              :      end do
    6832              :    end do
    6833              : 
    6834            0 :    if (offset_err/=0) then  ! just warn, let the caller handle the exception.
    6835              :      write(msg,"(3a)")&
    6836            0 :       " Global position index cannot be stored in standard Fortran integer ",ch10,&
    6837            0 :       " scaLAPACK matrix cannot be read with a single MPI-IO call ."
    6838            0 :      ABI_WARNING(msg)
    6839              :    end if
    6840              : 
    6841              :  case default
    6842            0 :    ABI_BUG(" Wrong uplo: "//TRIM(uplo))
    6843              :  end select
    6844              : 
    6845            0 :  block_length(nel+2)= 1
    6846            0 :  block_displ (nel+2)= ncols_glob * (nrows_glob*bsize_elm + 2*bsize_frm) / bsize_etype
    6847            0 :  block_type  (nel+2)= MPI_UB
    6848              : 
    6849            0 :  call xmpio_type_struct(nel+2,block_length,block_displ,block_type,slk_type,mpi_err)
    6850            0 :  ABI_CHECK_MPI(mpi_err,"MPI_type_STRUCT")
    6851              : 
    6852            0 :  ABI_FREE(block_length)
    6853            0 :  ABI_FREE(block_displ)
    6854            0 :  ABI_FREE(block_type)
    6855              : 
    6856            0 :  call MPI_type_COMMIT(slk_type,mpi_err)
    6857            0 :  ABI_CHECK_MPI(mpi_err,"MPI_type_COMMIT")
    6858              : 
    6859              : #else
    6860              :  ABI_ERROR("MPI-IO is mandatatory in slk_single_fview_read")
    6861              : #endif
    6862              : 
    6863            0 : end subroutine slk_single_fview_read
    6864              : !!***
    6865              : 
    6866              : !----------------------------------------------------------------------
    6867              : 
    6868              : !!****f* m_slk/slk_single_fview_write
    6869              : !! NAME
    6870              : !!  slk_single_fview_write
    6871              : !!
    6872              : !! FUNCTION
    6873              : !!  Returns an MPI datatype that can be used to write a scaLAPACK distributed matrix to
    6874              : !!  a binary file using MPI-IO.
    6875              : !!
    6876              : !! INPUTS
    6877              : !!  Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer.
    6878              : !!  uplo=String specifying whether only the upper or lower triangular part of the global matrix is stored on disk:
    6879              : !!    = "U":  Upper triangular is stored
    6880              : !!    = "L":  Lower triangular is stored
    6881              : !!    = "A":  Full matrix (used for general complex matrices)
    6882              : !!  [is_fortran_file]=.FALSE. is C stream is used. .TRUE. for writing Fortran binary files
    6883              : !!    with record marker. In this case etype is set xmpio_mpi_type_frm provided that
    6884              : !!    the mpi_type of the matrix element is commensurate with xmpio_mpi_type_frm. Defaults to .TRUE.
    6885              : !!  glob_subarray(2,2) = Used to select the subarray of the global matrix. Used only when uplo="All"
    6886              : !!     glob_subarray(:,1)=starting global coordinates of the subarray in each dimension
    6887              : !!     (array of nonnegative integers >=1, <=array_of_sizes)
    6888              : !!     glob_subarray(:,2)=Number of elements in each dimension of the subarray (array of positive integers)
    6889              : !!
    6890              : !! OUTPUT
    6891              : !!  nelw=Number of elements to be written.
    6892              : !!  etype=Elementary data type (handle) defining the elementary unit used to access the file.
    6893              : !!  slk_type=New MPI type that can be used to instantiate the MPI-IO view for the Fortran file.
    6894              : !!     Note that the view assumes that the file pointer points to the FIRST Fortran record marker.
    6895              : !!  offset_err=Error code. A non-zero value signals that the global matrix is too large
    6896              : !!    for a single MPI-IO access (see notes below).
    6897              : !!
    6898              : !! SIDE EFFECTS
    6899              : !!  elw2slk(:,:) =
    6900              : !!    input:  pointer to null().
    6901              : !!    output: elw2slk(2,nelw) contains the local coordinates of the matrix elements to be written.
    6902              : !!     (useful only if the upper or lower triangle of the global matrix has to be written or when
    6903              : !!      uplo="all" but a global subarray is written.
    6904              : !!
    6905              : !! NOTES
    6906              : !!  With (signed) Fortran integers, the maximum size of the file that
    6907              : !!  that can be read in one-shot is around 2Gb when etype is set to byte.
    6908              : !!  Using a larger etype might create portability problems (real data on machines using
    6909              : !!  integer*16 for the marker) since etype must be a multiple of the Fortran record marker
    6910              : !!  Due to the above reason, block_displ is given in bytes and must be stored in a Fortran
    6911              : !!  integer of kind XMPI_ADDRESS_KIND. If the displacement is too large, the routine returns
    6912              : !!  offset_err=1 so that the caller will know that several MPI-IO reads are needed to
    6913              : !!  write the local buffer.
    6914              : !!
    6915              : !! SOURCE
    6916              : 
    6917            0 : subroutine slk_single_fview_write(Slk_mat,uplo,nelw,elw2slk,etype,slk_type,offset_err,is_fortran_file,glob_subarray)
    6918              : 
    6919              : !Arguments ------------------------------------
    6920              : !scalars
    6921              :  class(slkmat_dp_t),intent(in) :: Slk_mat
    6922              :  integer,intent(out) :: offset_err,slk_type,etype,nelw
    6923              :  character(len=*),intent(in) :: uplo
    6924              :  logical,optional,intent(in) :: is_fortran_file
    6925              : !arrays
    6926              :  integer,pointer :: elw2slk(:,:)
    6927              :  integer,optional,intent(in) :: glob_subarray(2,2)
    6928              : 
    6929              : !Local variables ------------------------------
    6930              : !scalars
    6931              :  integer :: jloc,iloc,iglob,jglob,nrows_glob,ncols_glob,mpi_err,nel_max
    6932              :  integer :: grow_min,grow_max,gcol_min,gcol_max
    6933              :  integer :: bsize_frm,mpi_type_elm,ij_loc,bsize_elm
    6934              :  integer(XMPI_OFFSET_KIND) :: ijp_glob,my_offset,cpad_frm
    6935              : !arrays
    6936              :  character(len=500) :: msg
    6937            0 :  integer,allocatable :: block_length(:),block_type(:)
    6938            0 :  integer(XMPI_ADDRESS_KIND),allocatable :: block_displ(:)
    6939              : !************************************************************************
    6940              : 
    6941              : #ifdef HAVE_MPI_IO
    6942              : !@slkmat_dp_t
    6943            0 :  bsize_frm = xmpio_bsize_frm    ! Byte size of the Fortran record marker.
    6944            0 :  if (PRESENT(is_fortran_file)) then
    6945            0 :    if (.not.is_fortran_file) bsize_frm = 0
    6946              :  end if
    6947              : 
    6948            0 :  if (PRESENT(glob_subarray).and..not.firstchar(uplo, ["A"])) then
    6949            0 :    ABI_ERROR("glob_subarray should not be used when uplo/=All")
    6950              :  end if
    6951              : 
    6952            0 :  call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
    6953              : 
    6954              :  ! Global dimensions.
    6955            0 :  nrows_glob=Slk_mat%size_global(1)
    6956            0 :  ncols_glob=Slk_mat%size_global(2)
    6957              : 
    6958              :  ! Number of matrix elements treated by this node.
    6959            0 :  nel_max = PRODUCT(Slk_mat%size_local(1:2))
    6960              : 
    6961            0 :  ABI_MALLOC(elw2slk,(2,nel_max))
    6962            0 :  elw2slk=0
    6963              : 
    6964              :  ! Cannot use MPI_type_CREATE_INDEXED_BLOCK since it is not correctly implemented in several MPI libraries.
    6965              :  ! etype has to be set to MPI_BYTE, since the displacement in MPI structures is always in byte.
    6966            0 :  etype = MPI_BYTE
    6967              : 
    6968              :  ! Define the mapping between scaLAPACK buffer and the storage on file.
    6969            0 :  ABI_MALLOC(block_length, (nel_max+2))
    6970            0 :  ABI_MALLOC(block_displ, (nel_max+2))
    6971            0 :  ABI_MALLOC(block_type, (nel_max+2))
    6972            0 :  block_length(1)=1
    6973            0 :  block_displ (1)=0
    6974            0 :  block_type  (1)=MPI_LB
    6975              : 
    6976              :  ! Note that the view assumes that the file pointer points to the first Fortran record marker.
    6977            0 :  offset_err=0
    6978              : 
    6979            0 :  select case (uplo(1:1))
    6980              :  case ("A","a")
    6981              :    ! The entire global matrix is written on disk. TODO can use contigous vectors for better access.
    6982            0 :    grow_min=1; grow_max=nrows_glob
    6983            0 :    gcol_min=1; gcol_max=ncols_glob
    6984            0 :    if (PRESENT(glob_subarray)) then ! subarray access.
    6985            0 :      grow_min = glob_subarray(1,1)
    6986            0 :      gcol_min = glob_subarray(2,1)
    6987            0 :      grow_max = grow_min + glob_subarray(1,2) -1
    6988            0 :      gcol_max = gcol_min + glob_subarray(2,2) -1
    6989              :    end if
    6990              : 
    6991            0 :    ij_loc=0
    6992            0 :    do jloc=1,Slk_mat%size_local(2)
    6993            0 :      do iloc=1,Slk_mat%size_local(1)
    6994            0 :        call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    6995              :        if (iglob>=grow_min.and.iglob<=grow_max .and. &  ! glob_subarray element.
    6996            0 :            jglob>=gcol_min.and.jglob<=gcol_max) then
    6997            0 :          ij_loc  = ij_loc+1
    6998            0 :          my_offset = 2*(jglob-1)*bsize_frm + bsize_frm + (jglob-1)*nrows_glob*bsize_elm + (iglob-1) * bsize_elm
    6999            0 :          if (xmpio_max_address(my_offset)) offset_err=1   ! Test for possible wraparounds
    7000            0 :          block_displ (ij_loc+1) = my_offset
    7001            0 :          block_type  (ij_loc+1) = mpi_type_elm
    7002            0 :          block_length(ij_loc+1) = 1
    7003            0 :          elw2slk(:,ij_loc) = (/iloc,jloc/) ! useless when subarray are not used but oh well!
    7004              :        end if
    7005              :      end do
    7006              :    end do
    7007              : 
    7008              :  case ("U","u")
    7009              :    ! Only the upper triangle of the global matrix is stored on disk.
    7010            0 :    ij_loc=0
    7011            0 :    do jloc=1,Slk_mat%size_local(2)
    7012            0 :      do iloc=1,Slk_mat%size_local(1)
    7013            0 :        call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    7014            0 :        if (jglob>=iglob) then
    7015            0 :          ijp_glob = iglob + jglob*(jglob-1)/2  ! Index for packed form
    7016            0 :          cpad_frm = 2*(jglob-1)*bsize_frm
    7017            0 :          ij_loc = ij_loc+1
    7018            0 :          my_offset = cpad_frm + bsize_frm + (ijp_glob-1) * bsize_elm
    7019            0 :          if (xmpio_max_address(my_offset)) offset_err=1   ! Test for possible wraparounds
    7020            0 :          block_displ (ij_loc+1) = my_offset
    7021            0 :          block_type  (ij_loc+1) = mpi_type_elm
    7022            0 :          block_length(ij_loc+1) = 1
    7023            0 :          elw2slk(:,ij_loc) = (/iloc,jloc/)
    7024              :        end if
    7025              :      end do
    7026              :    end do
    7027              : 
    7028              :  case ("L","l")
    7029              :    ! Only the lower triangle of the global matrix is stored on disk.
    7030            0 :    ij_loc=0
    7031            0 :    do jloc=1,Slk_mat%size_local(2)
    7032            0 :      do iloc=1,Slk_mat%size_local(1)
    7033            0 :        call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
    7034            0 :        if (jglob<=iglob) then
    7035            0 :          ijp_glob = iglob + (jglob-1)*(2*nrows_glob-jglob)/2 ! Index for packed form
    7036            0 :          cpad_frm = 2*(jglob-1)*bsize_frm
    7037            0 :          ij_loc = ij_loc+1
    7038            0 :          my_offset = cpad_frm + bsize_frm + (ijp_glob-1) * bsize_elm
    7039            0 :          if (xmpio_max_address(my_offset)) offset_err=1   ! block_displ is usually integer*4. Test for possible wraparounds
    7040            0 :          block_displ (ij_loc+1) = my_offset
    7041            0 :          block_type  (ij_loc+1) = mpi_type_elm
    7042            0 :          block_length(ij_loc+1) = 1
    7043            0 :          elw2slk(:,ij_loc) = (/iloc,jloc/)
    7044              :        end if
    7045              :      end do
    7046              :    end do
    7047              : 
    7048              :  case default
    7049            0 :    ABI_BUG(" Wrong uplo: "//TRIM(uplo))
    7050              :  end select
    7051              : 
    7052            0 :  if (offset_err/=0) then  ! just warn, let the caller handle the exception.
    7053              :    write(msg,"(3a)")&
    7054            0 :     "Global position index cannot be stored in standard Fortran integer ",ch10,&
    7055            0 :     "scaLAPACK matrix cannot be read with a single MPI-IO call ."
    7056            0 :    ABI_WARNING(msg)
    7057              :  end if
    7058              : 
    7059              :  ! Final number of matrix elements that will be written by this node.
    7060            0 :  nelw = ij_loc
    7061              : 
    7062            0 :  block_length(nelw+2)= 1
    7063            0 :  block_displ (nelw+2)= ncols_glob * (nrows_glob*bsize_elm + 2*bsize_frm)
    7064            0 :  block_type  (nelw+2)= MPI_UB
    7065              : 
    7066            0 :  call xmpio_type_struct(nelw+2,block_length,block_displ,block_type,slk_type,mpi_err)
    7067            0 :  ABI_CHECK_MPI(mpi_err, "MPI_type_STRUCT")
    7068              : 
    7069            0 :  ABI_FREE(block_length)
    7070            0 :  ABI_FREE(block_displ)
    7071            0 :  ABI_FREE(block_type)
    7072              : 
    7073            0 :  call MPI_type_COMMIT(slk_type,mpi_err)
    7074            0 :  ABI_CHECK_MPI(mpi_err, "MPI_type_COMMIT")
    7075              : 
    7076              : #else
    7077              :  ABI_ERROR("MPI-IO is mandatatory in slk_single_fview_read")
    7078              : #endif
    7079              : 
    7080            0 : end subroutine slk_single_fview_write
    7081              : !!***
    7082              : 
    7083              : !----------------------------------------------------------------------
    7084              : 
    7085              : !!****f* m_slk/slkmat_dp_bsize_and_type
    7086              : !! NAME
    7087              : !!  slkmat_dp_bsize_and_type
    7088              : !!
    7089              : !! FUNCTION
    7090              : !!  Returns the byte size and the MPI datatype associated to the matrix elements
    7091              : !!  that are stored in the ScaLAPACK_matrix
    7092              : !!
    7093              : !! INPUTS
    7094              : !!  Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer
    7095              : !!
    7096              : !! OUTPUT
    7097              : !!  bsize_elm=Byte size of the matrix element.
    7098              : !!  mpi_type_elm=MPI datatype of the matrix element.
    7099              : !!
    7100              : !! SOURCE
    7101              : 
    7102            0 : subroutine slkmat_dp_bsize_and_type(Slk_mat, bsize_elm, mpi_type_elm)
    7103              : 
    7104              : !Arguments ------------------------------------
    7105              : !scalars
    7106              :  class(slkmat_dp_t),intent(in) :: Slk_mat
    7107              :  integer,intent(out) :: bsize_elm,mpi_type_elm
    7108              : 
    7109              : !Local variables ------------------------------
    7110              : !scalars
    7111              :  integer :: ierr
    7112              :  character(len=500) :: msg
    7113              : ! ************************************************************************
    7114              : 
    7115            0 :  ierr=0
    7116              : #ifdef HAVE_MPI
    7117            0 :  if (allocated(Slk_mat%buffer_cplx)) then
    7118            0 :    ierr = ierr + 1
    7119            0 :    mpi_type_elm = MPI_DOUBLE_COMPLEX
    7120            0 :    bsize_elm    = xmpi_bsize_dpc
    7121              :  end if
    7122              : 
    7123            0 :  if (allocated(Slk_mat%buffer_real)) then
    7124            0 :    ierr = ierr + 1
    7125            0 :    mpi_type_elm = MPI_DOUBLE_PRECISION
    7126            0 :    bsize_elm    = xmpi_bsize_dp
    7127              :  end if
    7128              : #endif
    7129              : 
    7130              :  ! One and only one buffer should be allocated.
    7131            0 :  if (ierr /= 1) then
    7132            0 :    write(msg,'(a,i0)')" ScaLAPACK buffers are not allocated correctly, ierr= ",ierr
    7133            0 :    ABI_ERROR(msg)
    7134              :  end if
    7135              : 
    7136            0 : end subroutine slkmat_dp_bsize_and_type
    7137              : !!***
    7138              : 
    7139              : !----------------------------------------------------------------------
    7140              : 
    7141              : !!****f* m_slk/slkmat_sp_svd
    7142              : !! NAME
    7143              : !! slkmat_sp_svd
    7144              : !!
    7145              : !! FUNCTION
    7146              : !!  Compute Singular Value Decomposition of matrix in_mat (single precision version).
    7147              : !!
    7148              : !! SIDE EFFECTS
    7149              : !!  in_mat: matrix is destroyed in output.
    7150              : !!
    7151              : !! SOURCE
    7152              : 
    7153            0 : subroutine slkmat_sp_svd(in_mat, jobu, jobvt, u_mat, s_vals, vt_mat)
    7154              : 
    7155              : !Arguments ------------------------------------
    7156              :  class(slkmat_sp_t),intent(inout) :: in_mat
    7157              :  character(len=1),intent(in) :: jobu, jobvt
    7158              :  class(slkmat_sp_t),intent(out) :: u_mat, vt_mat
    7159              :  real(sp),allocatable, intent(out) :: s_vals(:)
    7160              : 
    7161              : #ifdef HAVE_LINALG_SCALAPACK
    7162              : !Local variables ------------------------------
    7163              : !scalars
    7164              :  integer :: info, lwork, lrwork, isize, mm, nn
    7165              : !array
    7166              :  real(sp),allocatable :: rwork_sp(:)
    7167              :  complex(sp),allocatable :: cwork_sp(:)
    7168              : !************************************************************************
    7169              : 
    7170              :  if (in_mat%desc(MB_) /= in_mat%desc(NB_)) then
    7171              :    ABI_ERROR("PCGESVD requires square block decomposition i.e MB_A = NB_A.")
    7172              :  end if
    7173              : 
    7174              :  mm = in_mat%size_global(1); nn = in_mat%size_global(2); isize = min(mm, nn)
    7175              : 
    7176              :  ! Allocate singular values and output matrices.
    7177              : 
    7178              :  ABI_CALLOC(s_vals, (isize))
    7179              :  if (jobu == "V") call u_mat%init(mm, isize, in_mat%processor, in_mat%istwf_k)
    7180              :  if (jobvt == "V") call vt_mat%init(isize, nn, in_mat%processor, in_mat%istwf_k)
    7181              : 
    7182              :  if (allocated(in_mat%buffer_cplx)) then
    7183              :    ! Query optimal workspace size
    7184              :    lwork = -1
    7185              :    ABI_MALLOC(cwork_sp, (1))
    7186              :    ABI_MALLOC(rwork_sp, (1))
    7187              : 
    7188              :    call PCGESVD(jobu, jobvt, &
    7189              :                 mm, nn, in_mat%buffer_cplx, 1, 1, in_mat%desc, s_vals, &
    7190              :                 u_mat%buffer_cplx, 1, 1, u_mat%desc, &
    7191              :                 vt_mat%buffer_cplx, 1, 1, vt_mat%desc, &
    7192              :                 cwork_sp, lwork, rwork_sp, info)
    7193              : 
    7194              :    ABI_CHECK(info == 0, sjoin("CZGESVD returned info:", itoa(info)))
    7195              : 
    7196              :    ! Allocate optimal workspace
    7197              :    lwork = nint(real(cwork_sp(1)))
    7198              :    lrwork = nint(rwork_sp(1))
    7199              :    ABI_FREE(cwork_sp)
    7200              :    ABI_FREE(rwork_sp)
    7201              : 
    7202              :    ABI_MALLOC(cwork_sp, (lwork))
    7203              :    ABI_MALLOC(rwork_sp, (lrwork))
    7204              : 
    7205              :    ! Perform SVD
    7206              :    call PCGESVD(jobu, jobvt, &
    7207              :                 mm, nn, in_mat%buffer_cplx, 1, 1, in_mat%desc, s_vals, &
    7208              :                 u_mat%buffer_cplx, 1, 1, u_mat%desc, &
    7209              :                 vt_mat%buffer_cplx, 1, 1, vt_mat%desc, &
    7210              :                 cwork_sp, lwork, rwork_sp, info)
    7211              : 
    7212              :    ABI_FREE(cwork_sp)
    7213              :    ABI_FREE(rwork_sp)
    7214              :    ABI_CHECK(info == 0, sjoin("PCGESVD returned info:", itoa(info)))
    7215              : 
    7216              :  else if (allocated(in_mat%buffer_real)) then
    7217              :    ABI_ERROR("SVD for real matrices not coded!")
    7218              :  else
    7219              :    ABI_ERROR("Neither complex nor real buffer are allocated!")
    7220              :  end if
    7221              : #endif
    7222              : 
    7223            0 : end subroutine slkmat_sp_svd
    7224              : !!***
    7225              : 
    7226              : !----------------------------------------------------------------------
    7227              : 
    7228              : !!****f* m_slk/slkmat_dp_svd
    7229              : !! NAME
    7230              : !! slkmat_dp_svd
    7231              : !!
    7232              : !! FUNCTION
    7233              : !!  Compute Singular Value Decomposition of matrix in_mat (double precision version).
    7234              : !!
    7235              : !! SIDE EFFECTS
    7236              : !!  in_mat: matrix is destroyed in output.
    7237              : !!
    7238              : !! SOURCE
    7239              : 
    7240            0 : subroutine slkmat_dp_svd(in_mat, jobu, jobvt, u_mat, s_vals, vt_mat)
    7241              : 
    7242              : !Arguments ------------------------------------
    7243              :  class(slkmat_dp_t),intent(inout) :: in_mat
    7244              :  character(len=1),intent(in) :: jobu, jobvt
    7245              :  class(slkmat_dp_t),intent(out) :: u_mat, vt_mat
    7246              :  real(dp),allocatable, intent(out) :: s_vals(:)
    7247              : 
    7248              : #ifdef HAVE_LINALG_SCALAPACK
    7249              : !Local variables ------------------------------
    7250              : !scalars
    7251              :  integer :: info, lwork, lrwork, isize, mm, nn
    7252              : !array
    7253              :  real(dp),allocatable :: rwork_dp(:)
    7254              :  complex(dp),allocatable :: cwork_dp(:)
    7255              : !************************************************************************
    7256              : 
    7257              :  if (in_mat%desc(MB_) /= in_mat%desc(NB_)) then
    7258              :    ABI_ERROR("PZGESVD requires square block decomposition i.e MB_A = NB_A.")
    7259              :  end if
    7260              : 
    7261              :  mm = in_mat%size_global(1); nn = in_mat%size_global(2); isize = min(mm, nn)
    7262              : 
    7263              :  ! Allocate singular values and output matrices.
    7264              :  ABI_CALLOC(s_vals, (isize))
    7265              : 
    7266              :  if (jobu == "V") call u_mat%init(mm, isize, in_mat%processor, in_mat%istwf_k)
    7267              :  if (jobvt == "V") call vt_mat%init(isize, nn, in_mat%processor, in_mat%istwf_k)
    7268              : 
    7269              :  if (allocated(in_mat%buffer_cplx)) then
    7270              :    ! Query optimal workspace size
    7271              :    lwork = -1
    7272              :    ABI_MALLOC(cwork_dp, (1))
    7273              :    ABI_MALLOC(rwork_dp, (1))
    7274              : 
    7275              :    call PZGESVD(jobu, jobvt, &
    7276              :                 mm, nn, in_mat%buffer_cplx, 1, 1, in_mat%desc, s_vals, &
    7277              :                 u_mat%buffer_cplx, 1, 1, u_mat%desc, &
    7278              :                 vt_mat%buffer_cplx, 1, 1, vt_mat%desc, &
    7279              :                 cwork_dp, lwork, rwork_dp, info)
    7280              : 
    7281              :    ABI_CHECK(info == 0, sjoin("CZGESVD returned info:", itoa(info)))
    7282              : 
    7283              :    ! Allocate optimal workspace
    7284              :    lwork = nint(real(cwork_dp(1)))
    7285              :    lrwork = nint(rwork_dp(1))
    7286              :    ABI_FREE(cwork_dp)
    7287              :    ABI_FREE(rwork_dp)
    7288              : 
    7289              :    ABI_MALLOC(cwork_dp, (lwork))
    7290              :    ABI_MALLOC(rwork_dp, (lrwork))
    7291              : 
    7292              :    ! Perform SVD
    7293              :    call PZGESVD(jobu, jobvt, &
    7294              :                 mm, nn, in_mat%buffer_cplx, 1, 1, in_mat%desc, s_vals, &
    7295              :                 u_mat%buffer_cplx, 1, 1, u_mat%desc, &
    7296              :                 vt_mat%buffer_cplx, 1, 1, vt_mat%desc, &
    7297              :                 cwork_dp, lwork, rwork_dp, info)
    7298              : 
    7299              :    ABI_FREE(cwork_dp)
    7300              :    ABI_FREE(rwork_dp)
    7301              :    ABI_CHECK(info == 0, sjoin("CZGESVD returned info:", itoa(info)))
    7302              : 
    7303              :  else if (allocated(in_mat%buffer_real)) then
    7304              :    ABI_ERROR("SVD for real matrices not coded!")
    7305              :  else
    7306              :    ABI_ERROR("Neither complex nor real buffer are allocated!")
    7307              :  end if
    7308              : #endif
    7309              : 
    7310            0 : end subroutine slkmat_dp_svd
    7311              : !!***
    7312              : 
    7313            0 : end module m_slk
    7314              : !!***
        

Generated by: LCOV version 2.3-1