LCOV - code coverage report
Current view: top level - src/fm - cp_fm_basic_linalg.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:b310db4) Lines: 76.6 % 755 578
Test Date: 2026-10-23 01:10:43 Functions: 70.5 % 44 31

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief Basic linear algebra operations for full matrices.
      10              : !> \par History
      11              : !>      08.2002 split out of qs_blacs [fawzi]
      12              : !> \author Fawzi Mohamed
      13              : ! **************************************************************************************************
      14              : MODULE cp_fm_basic_linalg
      15              :    USE cp_blacs_env, ONLY: cp_blacs_env_type
      16              :    USE cp_fm_struct, ONLY: cp_fm_struct_equivalent
      17              :    USE cp_fm_types, ONLY: &
      18              :       cp_fm_create, cp_fm_get_diag, cp_fm_get_info, cp_fm_get_submatrix, cp_fm_p_type, &
      19              :       cp_fm_release, cp_fm_set_all, cp_fm_set_element, cp_fm_set_submatrix, cp_fm_to_fm, &
      20              :       cp_fm_type
      21              :    USE cp_log_handling, ONLY: cp_logger_get_default_unit_nr, &
      22              :                               cp_to_string
      23              :    USE kahan_sum, ONLY: accurate_dot_product, &
      24              :                         accurate_sum
      25              :    USE kinds, ONLY: dp, &
      26              :                     int_8
      27              :    USE machine, ONLY: m_memory
      28              :    USE mathlib, ONLY: get_pseudo_inverse_svd, &
      29              :                       invert_matrix
      30              :    USE message_passing, ONLY: mp_comm_type
      31              : #if defined (__HAS_IEEE_EXCEPTIONS)
      32              :    USE ieee_exceptions, ONLY: ieee_get_halting_mode, &
      33              :                               ieee_set_halting_mode, &
      34              :                               IEEE_ALL
      35              : #endif
      36              : #include "../base/base_uses.f90"
      37              : 
      38              :    IMPLICIT NONE
      39              :    PRIVATE
      40              : 
      41              :    LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .TRUE.
      42              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_fm_basic_linalg'
      43              : 
      44              :    PUBLIC :: cp_fm_scale, & ! scale a matrix
      45              :              cp_fm_scale_and_add, & ! scale and add two matrices
      46              :              cp_fm_geadd, & ! general addition
      47              :              cp_fm_add_columns, & ! daxpy type column add
      48              :              cp_fm_column_scale, & ! scale columns of a matrix
      49              :              cp_fm_row_scale, & ! scale rows of a matrix
      50              :              cp_fm_trace, & ! trace of the transpose(A)*B
      51              :              cp_fm_contracted_trace, & ! sum_{i,...,k} Tr [A(i,...,k)^T * B(i,...,k)]
      52              :              cp_fm_norm, & ! different norms of A
      53              :              cp_fm_schur_product, & ! schur product
      54              :              cp_fm_transpose, & ! transpose a matrix
      55              :              cp_fm_uplo_to_full, & ! symmetrise a triangular matrix
      56              :              cp_fm_syrk, & ! rank k update
      57              :              cp_fm_triangular_multiply, & ! triangular matrix multiply / solve
      58              :              cp_fm_symm, & ! multiply a symmetric with a non-symmetric matrix
      59              :              cp_fm_gemm, & ! multiply two matrices
      60              :              cp_complex_fm_gemm, & ! multiply two complex matrices, represented by non_complex fm matrices
      61              :              cp_fm_invert, & ! computes the inverse and determinant
      62              :              cp_fm_frobenius_norm, & ! frobenius norm
      63              :              cp_fm_triangular_invert, & ! compute the reciprocal of a triangular matrix
      64              :              cp_fm_qr_factorization, & ! compute the QR factorization of a rectangular matrix
      65              :              cp_fm_solve, & ! solves the equation  A*B=C A and C are input
      66              :              cp_fm_pdgeqpf, & ! compute a QR factorization with column pivoting of a M-by-N distributed matrix
      67              :              cp_fm_pdorgqr, & ! generates an M-by-N as first N columns of a product of K elementary reflectors
      68              :              cp_fm_potrf, & ! Cholesky decomposition
      69              :              cp_fm_potri, & ! Invert triangular matrix
      70              :              cp_fm_rot_rows, & ! rotates two rows
      71              :              cp_fm_rot_cols, & ! rotates two columns
      72              :              cp_fm_Gram_Schmidt_orthonorm, & ! Gram-Schmidt orthonormalization of columns of a full matrix, &
      73              :              cp_fm_det, & ! determinant of a real matrix with correct sign
      74              :              cp_fm_matvec, & ! matrix-vector multiplication (vector replicated)
      75              :              cp_fm_add_on_diag ! add a scalar to the diagonal
      76              : 
      77              :    REAL(KIND=dp), EXTERNAL :: dlange, pdlange, pdlatra
      78              : 
      79              :    INTERFACE cp_fm_trace
      80              :       MODULE PROCEDURE cp_fm_trace_a0b0t0
      81              :       MODULE PROCEDURE cp_fm_trace_a1b0t1_a
      82              :       MODULE PROCEDURE cp_fm_trace_a1b0t1_p
      83              :       MODULE PROCEDURE cp_fm_trace_a1b1t1_aa
      84              :       MODULE PROCEDURE cp_fm_trace_a1b1t1_ap
      85              :       MODULE PROCEDURE cp_fm_trace_a1b1t1_pa
      86              :       MODULE PROCEDURE cp_fm_trace_a1b1t1_pp
      87              :    END INTERFACE cp_fm_trace
      88              : 
      89              :    INTERFACE cp_fm_contracted_trace
      90              :       MODULE PROCEDURE cp_fm_contracted_trace_a2b2t2_aa
      91              :       MODULE PROCEDURE cp_fm_contracted_trace_a2b2t2_ap
      92              :       MODULE PROCEDURE cp_fm_contracted_trace_a2b2t2_pa
      93              :       MODULE PROCEDURE cp_fm_contracted_trace_a2b2t2_pp
      94              :    END INTERFACE cp_fm_contracted_trace
      95              : CONTAINS
      96              : 
      97              : ! **************************************************************************************************
      98              : !> \brief Computes the determinant (with a correct sign even in parallel environment!) of a real square matrix
      99              : !> \author A. Sinyavskiy (andrey.sinyavskiy@chem.uzh.ch)
     100              : ! **************************************************************************************************
     101            0 :    SUBROUTINE cp_fm_det(matrix_a, det_a)
     102              : 
     103              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a
     104              :       REAL(KIND=dp), INTENT(OUT)               :: det_a
     105              :       REAL(KIND=dp)                            :: determinant
     106              :       TYPE(cp_fm_type)                         :: matrix_lu
     107              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
     108              :       INTEGER                                  :: n, i, info, P
     109            0 :       INTEGER, ALLOCATABLE, DIMENSION(:)       :: ipivot
     110              :       REAL(KIND=dp), DIMENSION(:), POINTER     :: diag
     111              : 
     112              : #if defined(__parallel)
     113              :       INTEGER                                  :: myprow, nprow, npcol, nrow_local, nrow_block, irow_local
     114              :       INTEGER, DIMENSION(9)                    :: desca
     115              : #endif
     116              : 
     117              :       CALL cp_fm_create(matrix=matrix_lu, &
     118              :                         matrix_struct=matrix_a%matrix_struct, &
     119            0 :                         name="A_lu"//TRIM(ADJUSTL(cp_to_string(1)))//"MATRIX")
     120            0 :       CALL cp_fm_to_fm(matrix_a, matrix_lu)
     121              : 
     122            0 :       a => matrix_lu%local_data
     123            0 :       n = matrix_lu%matrix_struct%nrow_global
     124            0 :       ALLOCATE (ipivot(n))
     125            0 :       ipivot(:) = 0
     126            0 :       P = 0
     127            0 :       ALLOCATE (diag(n))
     128            0 :       diag(:) = 0.0_dp
     129              : #if defined(__parallel)
     130              :       ! Use LU decomposition
     131            0 :       desca(:) = matrix_lu%matrix_struct%descriptor(:)
     132            0 :       CALL pdgetrf(n, n, a, 1, 1, desca, ipivot, info)
     133            0 :       CALL cp_fm_get_diag(matrix_lu, diag)
     134            0 :       determinant = PRODUCT(diag)
     135            0 :       myprow = matrix_lu%matrix_struct%context%mepos(1)
     136            0 :       nprow = matrix_lu%matrix_struct%context%num_pe(1)
     137            0 :       npcol = matrix_lu%matrix_struct%context%num_pe(2)
     138            0 :       nrow_local = matrix_lu%matrix_struct%nrow_locals(myprow)
     139            0 :       nrow_block = matrix_lu%matrix_struct%nrow_block
     140            0 :       DO irow_local = 1, nrow_local
     141            0 :          i = matrix_lu%matrix_struct%row_indices(irow_local)
     142            0 :          IF (ipivot(irow_local) /= i) P = P + 1
     143              :       END DO
     144            0 :       CALL matrix_lu%matrix_struct%para_env%sum(P)
     145              :       ! very important fix
     146            0 :       P = P/npcol
     147              : #else
     148              :       CALL dgetrf(n, n, a, n, ipivot, info)
     149              :       CALL cp_fm_get_diag(matrix_lu, diag)
     150              :       determinant = PRODUCT(diag)
     151              :       DO i = 1, n
     152              :          IF (ipivot(i) /= i) P = P + 1
     153              :       END DO
     154              : #endif
     155            0 :       DEALLOCATE (ipivot)
     156            0 :       DEALLOCATE (diag)
     157            0 :       CALL cp_fm_release(matrix_lu)
     158            0 :       det_a = determinant*(-2*MOD(P, 2) + 1.0_dp)
     159            0 :    END SUBROUTINE cp_fm_det
     160              : 
     161              : ! **************************************************************************************************
     162              : !> \brief calc A <- alpha*A + beta*B
     163              : !>      optimized for alpha == 1.0 (just add beta*B) and beta == 0.0 (just
     164              : !>      scale A)
     165              : !> \param alpha ...
     166              : !> \param matrix_a ...
     167              : !> \param beta ...
     168              : !> \param matrix_b ...
     169              : ! **************************************************************************************************
     170      1397586 :    SUBROUTINE cp_fm_scale_and_add(alpha, matrix_a, beta, matrix_b)
     171              : 
     172              :       REAL(KIND=dp), INTENT(IN)                          :: alpha
     173              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix_a
     174              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: beta
     175              :       TYPE(cp_fm_type), INTENT(IN), OPTIONAL             :: matrix_b
     176              : 
     177              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_scale_and_add'
     178              : 
     179              :       INTEGER                                            :: handle, size_a, size_b
     180              :       REAL(KIND=dp)                                      :: my_beta
     181              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: a, b
     182              : 
     183      1397586 :       CALL timeset(routineN, handle)
     184              : 
     185      1397586 :       my_beta = 0.0_dp
     186      1397586 :       IF (PRESENT(matrix_b)) my_beta = 1.0_dp
     187      1397586 :       IF (PRESENT(beta)) my_beta = beta
     188              :       NULLIFY (a, b)
     189              : 
     190      1397586 :       IF (PRESENT(beta)) THEN
     191      1397584 :          CPASSERT(PRESENT(matrix_b))
     192      1397584 :          IF (ASSOCIATED(matrix_a%local_data, matrix_b%local_data)) THEN
     193            0 :             CPWARN("Bad use of routine. Call cp_fm_scale instead")
     194            0 :             CALL cp_fm_scale(alpha + beta, matrix_a)
     195            0 :             CALL timestop(handle)
     196            0 :             RETURN
     197              :          END IF
     198              :       END IF
     199              : 
     200      1397586 :       a => matrix_a%local_data
     201              : 
     202      1397586 :       size_a = SIZE(a, 1)*SIZE(a, 2)
     203              : 
     204      1397586 :       IF (alpha /= 1.0_dp) THEN
     205        85308 :          CALL dscal(size_a, alpha, a, 1)
     206              :       END IF
     207      1397586 :       IF (my_beta /= 0.0_dp) THEN
     208      1389650 :          IF (matrix_a%matrix_struct%context /= matrix_b%matrix_struct%context) &
     209            0 :             CPABORT("Matrices must be in the same blacs context")
     210              : 
     211      1389650 :          IF (cp_fm_struct_equivalent(matrix_a%matrix_struct, &
     212              :                                      matrix_b%matrix_struct)) THEN
     213              : 
     214      1389650 :             b => matrix_b%local_data
     215      1389650 :             size_b = SIZE(b, 1)*SIZE(b, 2)
     216      1389650 :             IF (size_a /= size_b) &
     217            0 :                CPABORT("Matrices must have same local sizes")
     218              : 
     219      1389650 :             CALL daxpy(size_a, my_beta, b, 1, a, 1)
     220              : 
     221              :          ELSE
     222              :             CALL cp_abort(__LOCATION__, &
     223              :                           "cp_fm_scale_and_add is not yet implemented for cases "// &
     224            0 :                           "where two input matrix structures are not equivalent")
     225              :          END IF
     226              : 
     227              :       END IF
     228              : 
     229      1397586 :       CALL timestop(handle)
     230              : 
     231              :    END SUBROUTINE cp_fm_scale_and_add
     232              : 
     233              : ! **************************************************************************************************
     234              : !> \brief interface to BLACS geadd:
     235              : !>                matrix_b = beta*matrix_b + alpha*opt(matrix_a)
     236              : !>        where opt(matrix_a) can be either:
     237              : !>              'N':  matrix_a
     238              : !>              'T':  matrix_a^T
     239              : !>              'C':  matrix_a^H (Hermitian conjugate)
     240              : !>        note that this is a level three routine, use cp_fm_scale_and_add if that
     241              : !>        is sufficient for your needs
     242              : !> \param alpha  : complex scalar
     243              : !> \param trans  : 'N' normal, 'T' transposed
     244              : !> \param matrix_a : input matrix_a
     245              : !> \param beta   : complex scalar
     246              : !> \param matrix_b : input matrix_b, upon out put the updated matrix_b
     247              : !> \author  Lianheng Tong
     248              : ! **************************************************************************************************
     249          650 :    SUBROUTINE cp_fm_geadd(alpha, trans, matrix_a, beta, matrix_b)
     250              :       REAL(KIND=dp), INTENT(IN) :: alpha, beta
     251              :       CHARACTER, INTENT(IN) :: trans
     252              :       TYPE(cp_fm_type), INTENT(IN) :: matrix_a, matrix_b
     253              : 
     254              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_geadd'
     255              : 
     256              :       INTEGER :: nrow_global, ncol_global, handle
     257              :       REAL(KIND=dp), DIMENSION(:, :), POINTER :: aa, bb
     258              : #if defined(__parallel)
     259              :       INTEGER, DIMENSION(9) :: desca, descb
     260              : #elif !defined(__MKL)
     261              :       INTEGER :: ii, jj
     262              : #endif
     263              : 
     264          650 :       CALL timeset(routineN, handle)
     265              : 
     266          650 :       nrow_global = matrix_a%matrix_struct%nrow_global
     267          650 :       ncol_global = matrix_a%matrix_struct%ncol_global
     268          650 :       CPASSERT(nrow_global == matrix_b%matrix_struct%nrow_global)
     269          650 :       CPASSERT(ncol_global == matrix_b%matrix_struct%ncol_global)
     270              : 
     271          650 :       aa => matrix_a%local_data
     272          650 :       bb => matrix_b%local_data
     273              : 
     274              : #if defined(__parallel)
     275         6500 :       desca = matrix_a%matrix_struct%descriptor
     276         6500 :       descb = matrix_b%matrix_struct%descriptor
     277              :       CALL pdgeadd(trans, &
     278              :                    nrow_global, &
     279              :                    ncol_global, &
     280              :                    alpha, &
     281              :                    aa, &
     282              :                    1, 1, &
     283              :                    desca, &
     284              :                    beta, &
     285              :                    bb, &
     286              :                    1, 1, &
     287          650 :                    descb)
     288              : #elif defined(__MKL)
     289              :       CALL mkl_domatadd('C', trans, 'N', nrow_global, ncol_global, &
     290              :                         alpha, aa, nrow_global, beta, bb, nrow_global, bb, nrow_global)
     291              : #else
     292              :       ! dgeadd is not a standard BLAS function, although it is implemented
     293              :       ! in some libraries like OpenBLAS, so not going to use it here
     294              :       SELECT CASE (trans)
     295              :       CASE ('T')
     296              :          DO jj = 1, ncol_global
     297              :             DO ii = 1, nrow_global
     298              :                bb(ii, jj) = beta*bb(ii, jj) + alpha*aa(jj, ii)
     299              :             END DO
     300              :          END DO
     301              :       CASE DEFAULT
     302              :          DO jj = 1, ncol_global
     303              :             DO ii = 1, nrow_global
     304              :                bb(ii, jj) = beta*bb(ii, jj) + alpha*aa(ii, jj)
     305              :             END DO
     306              :          END DO
     307              :       END SELECT
     308              : #endif
     309              : 
     310          650 :       CALL timestop(handle)
     311              : 
     312          650 :    END SUBROUTINE cp_fm_geadd
     313              : 
     314              : ! **************************************************************************************************
     315              : !> \brief Add (and scale) a subset of columns of a fm to a fm
     316              : !>        b = alpha*a + b
     317              : !> \param msource ...
     318              : !> \param mtarget ...
     319              : !> \param ncol ...
     320              : !> \param alpha ...
     321              : !> \param source_start ...
     322              : !> \param target_start ...
     323              : ! **************************************************************************************************
     324            0 :    SUBROUTINE cp_fm_add_columns(msource, mtarget, ncol, alpha, source_start, target_start)
     325              : 
     326              :       TYPE(cp_fm_type), INTENT(IN)             :: msource, mtarget
     327              :       INTEGER, INTENT(IN)                      :: ncol
     328              :       REAL(KIND=dp), INTENT(IN), OPTIONAL      :: alpha
     329              :       INTEGER, INTENT(IN), OPTIONAL            :: source_start, target_start
     330              : 
     331              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_add_columns'
     332              : 
     333              :       INTEGER                                  :: handle, n, ss, ts, i
     334              :       REAL(KIND=dp)                            :: fscale
     335              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, b
     336              : #if defined(__parallel)
     337              :       INTEGER, DIMENSION(9)                    :: desca, descb
     338              : #endif
     339              : 
     340            0 :       CALL timeset(routineN, handle)
     341              : 
     342            0 :       ss = 1
     343            0 :       ts = 1
     344              : 
     345            0 :       IF (PRESENT(source_start)) ss = source_start
     346            0 :       IF (PRESENT(target_start)) ts = target_start
     347              : 
     348            0 :       fscale = 1.0_dp
     349            0 :       IF (PRESENT(alpha)) fscale = alpha
     350              : 
     351            0 :       n = msource%matrix_struct%nrow_global
     352              : 
     353            0 :       a => msource%local_data
     354            0 :       b => mtarget%local_data
     355              : 
     356              : #if defined(__parallel)
     357              :       MARK_USED(i)
     358            0 :       desca(:) = msource%matrix_struct%descriptor(:)
     359            0 :       descb(:) = mtarget%matrix_struct%descriptor(:)
     360            0 :       CALL pdgeadd("N", n, ncol, fscale, a, 1, ss, desca, 1.0_dp, b, 1, ts, descb)
     361              : #else
     362              :       DO i = 0, ncol - 1
     363              :          b(1:n, ts + i) = b(1:n, ts + i) + fscale*a(1:n, ss + i)
     364              :       END DO
     365              : #endif
     366              : 
     367            0 :       CALL timestop(handle)
     368              : 
     369            0 :    END SUBROUTINE cp_fm_add_columns
     370              : 
     371              : ! **************************************************************************************************
     372              : !> \brief Computes the LU-decomposition of the matrix, and the determinant of the matrix
     373              : !>      IMPORTANT : the sign of the determinant is not defined correctly yet ....
     374              : !> \param matrix_a ...
     375              : !> \param almost_determinant ...
     376              : !> \param correct_sign ...
     377              : !> \par History
     378              : !>      added correct_sign 02.07 (fschiff)
     379              : !> \author Joost VandeVondele
     380              : !> \note
     381              : !>      - matrix_a is overwritten
     382              : !>      - the sign of the determinant might be wrong
     383              : !>      - SERIOUS WARNING (KNOWN BUG) : the sign of the determinant depends on ipivot
     384              : !>      - one should be able to find out if ipivot is an even or an odd permutation...
     385              : !>        if you need the correct sign, just add correct_sign==.TRUE. (fschiff)
     386              : !>      - Use cp_fm_get_diag instead of n times cp_fm_get_element (A. Bussy)
     387              : ! **************************************************************************************************
     388            0 :    SUBROUTINE cp_fm_lu_decompose(matrix_a, almost_determinant, correct_sign)
     389              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a
     390              :       REAL(KIND=dp), INTENT(OUT)               :: almost_determinant
     391              :       LOGICAL, INTENT(IN), OPTIONAL            :: correct_sign
     392              : 
     393              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_lu_decompose'
     394              : 
     395              :       INTEGER                                  :: handle, i, info, n
     396            0 :       INTEGER, ALLOCATABLE, DIMENSION(:)       :: ipivot
     397              :       REAL(KIND=dp)                            :: determinant
     398              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
     399              : #if defined(__parallel)
     400              :       INTEGER, DIMENSION(9)                    :: desca
     401            0 :       REAL(KIND=dp), DIMENSION(:), POINTER     :: diag
     402              : #else
     403              :       INTEGER                                  :: lda
     404              : #endif
     405              : 
     406            0 :       CALL timeset(routineN, handle)
     407              : 
     408            0 :       a => matrix_a%local_data
     409            0 :       n = matrix_a%matrix_struct%nrow_global
     410            0 :       ALLOCATE (ipivot(n + matrix_a%matrix_struct%nrow_block))
     411              : 
     412              : #if defined(__parallel)
     413              :       MARK_USED(correct_sign)
     414            0 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
     415            0 :       CALL pdgetrf(n, n, a, 1, 1, desca, ipivot, info)
     416              : 
     417            0 :       ALLOCATE (diag(n))
     418            0 :       diag(:) = 0.0_dp
     419            0 :       CALL cp_fm_get_diag(matrix_a, diag)
     420            0 :       determinant = 1.0_dp
     421            0 :       DO i = 1, n
     422            0 :          determinant = determinant*diag(i)
     423              :       END DO
     424            0 :       DEALLOCATE (diag)
     425              : #else
     426              :       lda = SIZE(a, 1)
     427              :       CALL dgetrf(n, n, a, lda, ipivot, info)
     428              :       determinant = 1.0_dp
     429              :       IF (correct_sign) THEN
     430              :          DO i = 1, n
     431              :             IF (ipivot(i) /= i) THEN
     432              :                determinant = -determinant*a(i, i)
     433              :             ELSE
     434              :                determinant = determinant*a(i, i)
     435              :             END IF
     436              :          END DO
     437              :       ELSE
     438              :          DO i = 1, n
     439              :             determinant = determinant*a(i, i)
     440              :          END DO
     441              :       END IF
     442              : #endif
     443              :       ! info is allowed to be zero
     444              :       ! this does just signal a zero diagonal element
     445            0 :       DEALLOCATE (ipivot)
     446            0 :       almost_determinant = determinant ! notice that the sign is random
     447            0 :       CALL timestop(handle)
     448            0 :    END SUBROUTINE cp_fm_lu_decompose
     449              : 
     450              : ! **************************************************************************************************
     451              : !> \brief computes matrix_c = beta * matrix_c + alpha * ( matrix_a  ** transa ) * ( matrix_b ** transb )
     452              : !> \param transa : 'N' -> normal   'T' -> transpose
     453              : !>      alpha,beta :: can be 0.0_dp and 1.0_dp
     454              : !> \param transb same operation choices as transa
     455              : !> \param m ...
     456              : !> \param n ...
     457              : !> \param k ...
     458              : !> \param alpha ...
     459              : !> \param matrix_a : m x k matrix ( ! for transa = 'N')
     460              : !> \param matrix_b : k x n matrix ( ! for transb = 'N')
     461              : !> \param beta ...
     462              : !> \param matrix_c : m x n matrix
     463              : !> \param a_first_col ...
     464              : !> \param a_first_row ...
     465              : !> \param b_first_col : the k x n matrix starts at col b_first_col of matrix_b (avoid usage)
     466              : !> \param b_first_row ...
     467              : !> \param c_first_col ...
     468              : !> \param c_first_row ...
     469              : !> \author Matthias Krack
     470              : !> \note
     471              : !>      matrix_c should have no overlap with matrix_a, matrix_b
     472              : ! **************************************************************************************************
     473        19996 :    SUBROUTINE cp_fm_gemm(transa, transb, m, n, k, alpha, matrix_a, matrix_b, beta, &
     474              :                          matrix_c, a_first_col, a_first_row, b_first_col, b_first_row, &
     475              :                          c_first_col, c_first_row)
     476              : 
     477              :       CHARACTER(LEN=1), INTENT(IN)             :: transa, transb
     478              :       INTEGER, INTENT(IN)                      :: m, n, k
     479              :       REAL(KIND=dp), INTENT(IN)                :: alpha
     480              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a, matrix_b
     481              :       REAL(KIND=dp), INTENT(IN)                :: beta
     482              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_c
     483              :       INTEGER, INTENT(IN), OPTIONAL            :: a_first_col, a_first_row, &
     484              :                                                   b_first_col, b_first_row, &
     485              :                                                   c_first_col, c_first_row
     486              : 
     487              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_gemm'
     488              : 
     489              :       INTEGER                                  :: handle, i_a, i_b, i_c, j_a, &
     490              :                                                   j_b, j_c
     491              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, b, c
     492              : #if defined(__parallel)
     493              :       INTEGER, DIMENSION(9)                    :: desca, descb, descc
     494              : #else
     495              :       INTEGER                                  :: lda, ldb, ldc
     496              : #endif
     497              : 
     498        19996 :       CALL timeset(routineN, handle)
     499              : 
     500              :       !sample peak memory
     501        19996 :       CALL m_memory()
     502              : 
     503        19996 :       a => matrix_a%local_data
     504        19996 :       b => matrix_b%local_data
     505        19996 :       c => matrix_c%local_data
     506              : 
     507        19996 :       i_a = 1
     508        19996 :       IF (PRESENT(a_first_row)) i_a = a_first_row
     509              : 
     510        19996 :       j_a = 1
     511        19996 :       IF (PRESENT(a_first_col)) j_a = a_first_col
     512              : 
     513        19996 :       i_b = 1
     514        19996 :       IF (PRESENT(b_first_row)) i_b = b_first_row
     515              : 
     516        19996 :       j_b = 1
     517        19996 :       IF (PRESENT(b_first_col)) j_b = b_first_col
     518              : 
     519        19996 :       i_c = 1
     520        19996 :       IF (PRESENT(c_first_row)) i_c = c_first_row
     521              : 
     522        19996 :       j_c = 1
     523        19996 :       IF (PRESENT(c_first_col)) j_c = c_first_col
     524              : 
     525              : #if defined(__parallel)
     526              : 
     527       199960 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
     528       199960 :       descb(:) = matrix_b%matrix_struct%descriptor(:)
     529       199960 :       descc(:) = matrix_c%matrix_struct%descriptor(:)
     530              : 
     531              :       CALL pdgemm(transa, transb, m, n, k, alpha, a, i_a, j_a, desca, b, i_b, j_b, &
     532        19996 :                   descb, beta, c, i_c, j_c, descc)
     533              : 
     534              : #else
     535              : 
     536              :       lda = SIZE(a, 1)
     537              :       ldb = SIZE(b, 1)
     538              :       ldc = SIZE(c, 1)
     539              : 
     540              :       CALL dgemm(transa, transb, m, n, k, alpha, a(i_a, j_a), lda, b(i_b, j_b), ldb, beta, c(i_c, j_c), ldc)
     541              : 
     542              : #endif
     543        19996 :       CALL timestop(handle)
     544              : 
     545        19996 :    END SUBROUTINE cp_fm_gemm
     546              : 
     547              : ! **************************************************************************************************
     548              : !> \brief computes matrix_c = beta * matrix_c + alpha *  matrix_a  *  matrix_b
     549              : !>      computes matrix_c = beta * matrix_c + alpha *  matrix_b  *  matrix_a
     550              : !>      where matrix_a is symmetric
     551              : !> \param side : 'L' -> matrix_a is on the left 'R' -> matrix_a is on the right
     552              : !>      alpha,beta :: can be 0.0_dp and 1.0_dp
     553              : !> \param uplo triangular format
     554              : !> \param m ...
     555              : !> \param n ...
     556              : !> \param alpha ...
     557              : !> \param matrix_a : m x m matrix
     558              : !> \param matrix_b : m x n matrix
     559              : !> \param beta ...
     560              : !> \param matrix_c : m x n matrix
     561              : !> \author Matthias Krack
     562              : !> \note
     563              : !>      matrix_c should have no overlap with matrix_a, matrix_b
     564              : !>      all matrices in QS are triangular according to uplo
     565              : !>      matrix_a is always an m x m matrix
     566              : !>      typically slower than cp_fm_gemm (especially in parallel easily 50 percent)
     567              : ! **************************************************************************************************
     568       217224 :    SUBROUTINE cp_fm_symm(side, uplo, m, n, alpha, matrix_a, matrix_b, beta, matrix_c)
     569              : 
     570              :       CHARACTER(LEN=1), INTENT(IN)             :: side, uplo
     571              :       INTEGER, INTENT(IN)                      :: m, n
     572              :       REAL(KIND=dp), INTENT(IN)                :: alpha
     573              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a, matrix_b
     574              :       REAL(KIND=dp), INTENT(IN)                :: beta
     575              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_c
     576              : 
     577              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_symm'
     578              : 
     579              :       INTEGER                                  :: handle
     580       217224 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, b, c
     581              : #if defined(__parallel)
     582              :       INTEGER, DIMENSION(9)                    :: desca, descb, descc
     583              : #else
     584              :       INTEGER                                  :: lda, ldb, ldc
     585              : #endif
     586              : 
     587       217224 :       CALL timeset(routineN, handle)
     588              : 
     589       217224 :       a => matrix_a%local_data
     590       217224 :       b => matrix_b%local_data
     591       217224 :       c => matrix_c%local_data
     592              : 
     593              : #if defined(__parallel)
     594              : 
     595      2172240 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
     596      2172240 :       descb(:) = matrix_b%matrix_struct%descriptor(:)
     597      2172240 :       descc(:) = matrix_c%matrix_struct%descriptor(:)
     598              : 
     599       217224 :       CALL pdsymm(side, uplo, m, n, alpha, a(1, 1), 1, 1, desca, b(1, 1), 1, 1, descb, beta, c(1, 1), 1, 1, descc)
     600              : 
     601              : #else
     602              : 
     603              :       lda = matrix_a%matrix_struct%local_leading_dimension
     604              :       ldb = matrix_b%matrix_struct%local_leading_dimension
     605              :       ldc = matrix_c%matrix_struct%local_leading_dimension
     606              : 
     607              :       CALL dsymm(side, uplo, m, n, alpha, a(1, 1), lda, b(1, 1), ldb, beta, c(1, 1), ldc)
     608              : 
     609              : #endif
     610       217224 :       CALL timestop(handle)
     611              : 
     612       217224 :    END SUBROUTINE cp_fm_symm
     613              : 
     614              : ! **************************************************************************************************
     615              : !> \brief computes the Frobenius norm of matrix_a
     616              : !> \brief computes the Frobenius norm of matrix_a
     617              : !> \param matrix_a : m x n matrix
     618              : !> \return ...
     619              : !> \author VW
     620              : ! **************************************************************************************************
     621         7926 :    FUNCTION cp_fm_frobenius_norm(matrix_a) RESULT(norm)
     622              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a
     623              :       REAL(KIND=dp)                            :: norm
     624              : 
     625              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_frobenius_norm'
     626              : 
     627              :       INTEGER                                  :: handle, size_a
     628         7926 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
     629              :       REAL(KIND=dp), EXTERNAL                  :: DDOT
     630              : #if defined(__parallel)
     631              :       TYPE(mp_comm_type)                       :: group
     632              : #endif
     633              : 
     634         7926 :       CALL timeset(routineN, handle)
     635              : 
     636              :       norm = 0.0_dp
     637         7926 :       a => matrix_a%local_data
     638         7926 :       size_a = SIZE(a, 1)*SIZE(a, 2)
     639         7926 :       norm = DDOT(size_a, a(1, 1), 1, a(1, 1), 1)
     640              : #if defined(__parallel)
     641         7926 :       group = matrix_a%matrix_struct%para_env
     642         7926 :       CALL group%sum(norm)
     643              : #endif
     644         7926 :       norm = SQRT(norm)
     645              : 
     646         7926 :       CALL timestop(handle)
     647              : 
     648         7926 :    END FUNCTION cp_fm_frobenius_norm
     649              : 
     650              : ! **************************************************************************************************
     651              : !> \brief performs a rank-k update of a symmetric matrix_c
     652              : !>         matrix_c = beta * matrix_c + alpha * matrix_a * transpose ( matrix_a )
     653              : !> \param uplo : 'U'   ('L')
     654              : !> \param trans : 'N'  ('T')
     655              : !> \param k : number of cols to use in matrix_a
     656              : !>      ia,ja ::  1,1 (could be used for selecting subblock of a)
     657              : !> \param alpha ...
     658              : !> \param matrix_a ...
     659              : !> \param ia ...
     660              : !> \param ja ...
     661              : !> \param beta ...
     662              : !> \param matrix_c ...
     663              : !> \author Matthias Krack
     664              : ! **************************************************************************************************
     665        11778 :    SUBROUTINE cp_fm_syrk(uplo, trans, k, alpha, matrix_a, ia, ja, beta, matrix_c)
     666              :       CHARACTER(LEN=1), INTENT(IN)             :: uplo, trans
     667              :       INTEGER, INTENT(IN)                      :: k
     668              :       REAL(KIND=dp), INTENT(IN)                :: alpha
     669              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a
     670              :       INTEGER, INTENT(IN)                      :: ia, ja
     671              :       REAL(KIND=dp), INTENT(IN)                :: beta
     672              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_c
     673              : 
     674              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_syrk'
     675              : 
     676              :       INTEGER                                  :: handle, n
     677        11778 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, c
     678              : #if defined(__parallel)
     679              :       INTEGER, DIMENSION(9)                    :: desca, descc
     680              : #else
     681              :       INTEGER                                  :: lda, ldc
     682              : #endif
     683              : #if defined (__HAS_IEEE_EXCEPTIONS)
     684              :       LOGICAL, DIMENSION(5)                    :: halt
     685              : #endif
     686              : 
     687        11778 :       CALL timeset(routineN, handle)
     688              : 
     689        11778 :       n = matrix_c%matrix_struct%nrow_global
     690              : 
     691        11778 :       a => matrix_a%local_data
     692        11778 :       c => matrix_c%local_data
     693              : 
     694              : #if defined (__HAS_IEEE_EXCEPTIONS)
     695              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     696              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     697              : #endif
     698              : #if defined(__parallel)
     699       117780 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
     700       117780 :       descc(:) = matrix_c%matrix_struct%descriptor(:)
     701              : 
     702        11778 :       CALL pdsyrk(uplo, trans, n, k, alpha, a(1, 1), ia, ja, desca, beta, c(1, 1), 1, 1, descc)
     703              : #else
     704              :       lda = SIZE(a, 1)
     705              :       ldc = SIZE(c, 1)
     706              : 
     707              :       CALL dsyrk(uplo, trans, n, k, alpha, a(ia, ja), lda, beta, c(1, 1), ldc)
     708              : #endif
     709              : #if defined (__HAS_IEEE_EXCEPTIONS)
     710              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     711              : #endif
     712        11778 :       CALL timestop(handle)
     713              : 
     714        11778 :    END SUBROUTINE cp_fm_syrk
     715              : 
     716              : ! **************************************************************************************************
     717              : !> \brief computes the schur product of two matrices
     718              : !>       c_ij = a_ij * b_ij
     719              : !> \param matrix_a ...
     720              : !> \param matrix_b ...
     721              : !> \param matrix_c ...
     722              : !> \author Joost VandeVondele
     723              : ! **************************************************************************************************
     724        13066 :    SUBROUTINE cp_fm_schur_product(matrix_a, matrix_b, matrix_c)
     725              : 
     726              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix_a, matrix_b, matrix_c
     727              : 
     728              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_schur_product'
     729              : 
     730              :       INTEGER                                            :: handle, icol_local, irow_local, mypcol, &
     731              :                                                             myprow, ncol_local, &
     732              :                                                             nrow_local
     733        13066 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: a, b, c
     734              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     735              : 
     736        13066 :       CALL timeset(routineN, handle)
     737              : 
     738        13066 :       context => matrix_a%matrix_struct%context
     739        13066 :       myprow = context%mepos(1)
     740        13066 :       mypcol = context%mepos(2)
     741              : 
     742        13066 :       a => matrix_a%local_data
     743        13066 :       b => matrix_b%local_data
     744        13066 :       c => matrix_c%local_data
     745              : 
     746        13066 :       nrow_local = matrix_a%matrix_struct%nrow_locals(myprow)
     747        13066 :       ncol_local = matrix_a%matrix_struct%ncol_locals(mypcol)
     748              : 
     749       117646 :       DO icol_local = 1, ncol_local
     750      6743576 :          DO irow_local = 1, nrow_local
     751      6730510 :             c(irow_local, icol_local) = a(irow_local, icol_local)*b(irow_local, icol_local)
     752              :          END DO
     753              :       END DO
     754              : 
     755        13066 :       CALL timestop(handle)
     756              : 
     757        13066 :    END SUBROUTINE cp_fm_schur_product
     758              : 
     759              : ! **************************************************************************************************
     760              : !> \brief returns the trace of matrix_a^T matrix_b, i.e
     761              : !>      sum_{i,j}(matrix_a(i,j)*matrix_b(i,j))
     762              : !> \param matrix_a a matrix
     763              : !> \param matrix_b another matrix
     764              : !> \param trace ...
     765              : !> \par History
     766              : !>      11.06.2001 Creation (Matthias Krack)
     767              : !>      12.2002 added doc [fawzi]
     768              : !> \author Matthias Krack
     769              : !> \note
     770              : !>      note the transposition of matrix_a!
     771              : ! **************************************************************************************************
     772       286702 :    SUBROUTINE cp_fm_trace_a0b0t0(matrix_a, matrix_b, trace)
     773              : 
     774              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix_a, matrix_b
     775              :       REAL(KIND=dp), INTENT(OUT)                         :: trace
     776              : 
     777              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_trace_a0b0t0'
     778              : 
     779              :       INTEGER                                            :: handle, mypcol, myprow, ncol_local, &
     780              :                                                             nrow_local
     781       286702 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: a, b
     782              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     783              :       TYPE(mp_comm_type)                                 :: group
     784              : 
     785       286702 :       CALL timeset(routineN, handle)
     786              : 
     787       286702 :       context => matrix_a%matrix_struct%context
     788       286702 :       myprow = context%mepos(1)
     789       286702 :       mypcol = context%mepos(2)
     790              : 
     791       286702 :       group = matrix_a%matrix_struct%para_env
     792              : 
     793       286702 :       a => matrix_a%local_data
     794       286702 :       b => matrix_b%local_data
     795              : 
     796       286702 :       nrow_local = MIN(matrix_a%matrix_struct%nrow_locals(myprow), matrix_b%matrix_struct%nrow_locals(myprow))
     797       286702 :       ncol_local = MIN(matrix_a%matrix_struct%ncol_locals(mypcol), matrix_b%matrix_struct%ncol_locals(mypcol))
     798              : 
     799              :       ! cries for an accurate_dot_product
     800              :       trace = accurate_dot_product(a(1:nrow_local, 1:ncol_local), &
     801       286702 :                                    b(1:nrow_local, 1:ncol_local))
     802              : 
     803       286702 :       CALL group%sum(trace)
     804              : 
     805       286702 :       CALL timestop(handle)
     806              : 
     807       286702 :    END SUBROUTINE cp_fm_trace_a0b0t0
     808              : 
     809              :    #:mute
     810              :       #:set types = [("cp_fm_type", "a", ""), ("cp_fm_p_type", "p","%matrix")]
     811              :    #:endmute
     812              : 
     813              : ! **************************************************************************************************
     814              : !> \brief Compute trace(k) = Tr (matrix_a(k)^T matrix_b) for each pair of matrices A_k and B.
     815              : !> \param matrix_a list of A matrices
     816              : !> \param matrix_b B matrix
     817              : !> \param trace    computed traces
     818              : !> \par History
     819              : !>    * 08.2018 forked from cp_fm_trace() [Sergey Chulkov]
     820              : !> \note \parblock
     821              : !>      Computing the trace requires collective communication between involved MPI processes
     822              : !>      that implies a synchronisation point between them. The aim of this subroutine is to reduce
     823              : !>      the amount of time wasted in such synchronisation by performing one large collective
     824              : !>      operation which involves all the matrices in question.
     825              : !>
     826              : !>      The subroutine's suffix reflects dimensionality of dummy arrays; 'a1b0t1' means that
     827              : !>      the dummy variables 'matrix_a' and 'trace' are 1-dimensional arrays, while the variable
     828              : !>      'matrix_b' is a single matrix.
     829              : !>      \endparblock
     830              : ! **************************************************************************************************
     831              :    #:for longname, shortname, appendix in types
     832        92473 :       SUBROUTINE cp_fm_trace_a1b0t1_${shortname}$ (matrix_a, matrix_b, trace)
     833              :          TYPE(${longname}$), DIMENSION(:), INTENT(IN)       :: matrix_a
     834              :          TYPE(cp_fm_type), INTENT(IN)                       :: matrix_b
     835              :          REAL(kind=dp), DIMENSION(:), INTENT(OUT)           :: trace
     836              : 
     837              :          CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_trace_a1b0t1_${shortname}$'
     838              : 
     839              :          INTEGER                                            :: handle, imatrix, n_matrices, &
     840              :                                                                ncols_local, nrows_local
     841        92473 :          REAL(kind=dp), DIMENSION(:, :), POINTER            :: ldata_a, ldata_b
     842              :          TYPE(mp_comm_type)                                 :: group
     843              : 
     844        92473 :          CALL timeset(routineN, handle)
     845              : 
     846        92473 :          n_matrices = SIZE(trace)
     847        92473 :          CPASSERT(SIZE(matrix_a) == n_matrices)
     848              : 
     849        92473 :          CALL cp_fm_get_info(matrix_b, nrow_local=nrows_local, ncol_local=ncols_local)
     850              : 
     851        92473 :          ldata_b => matrix_b%local_data(1:nrows_local, 1:ncols_local)
     852              : 
     853              : !$OMP PARALLEL DO DEFAULT(NONE), &
     854              : !$OMP             PRIVATE(imatrix, ldata_a), &
     855              : !$OMP             SHARED(ldata_b, matrix_a, matrix_b), &
     856        92473 : !$OMP             SHARED(ncols_local, nrows_local, n_matrices, trace)
     857              : 
     858              :          DO imatrix = 1, n_matrices
     859              : 
     860              :             ! assume that the matrices A(i) and B have identical shapes and distribution schemes
     861              :             ldata_a => matrix_a(imatrix) ${appendix}$%local_data(1:nrows_local, 1:ncols_local)
     862              :             trace(imatrix) = accurate_dot_product(ldata_a, ldata_b)
     863              :          END DO
     864              : !$OMP END PARALLEL DO
     865              : 
     866        92473 :          group = matrix_b%matrix_struct%para_env
     867       743077 :          CALL group%sum(trace)
     868              : 
     869        92473 :          CALL timestop(handle)
     870        92473 :       END SUBROUTINE cp_fm_trace_a1b0t1_${shortname}$
     871              :    #:endfor
     872              : 
     873              : ! **************************************************************************************************
     874              : !> \brief Compute trace(k) = Tr (matrix_a(k)^T matrix_b(k)) for each pair of matrices A_k and B_k.
     875              : !> \param matrix_a list of A matrices
     876              : !> \param matrix_b list of B matrices
     877              : !> \param trace    computed traces
     878              : !> \param accurate ...
     879              : !> \par History
     880              : !>    * 11.2016 forked from cp_fm_trace() [Sergey Chulkov]
     881              : !> \note \parblock
     882              : !>      Computing the trace requires collective communication between involved MPI processes
     883              : !>      that implies a synchronisation point between them. The aim of this subroutine is to reduce
     884              : !>      the amount of time wasted in such synchronisation by performing one large collective
     885              : !>      operation which involves all the matrices in question.
     886              : !>
     887              : !>      The subroutine's suffix reflects dimensionality of dummy arrays; 'a1b1t1' means that
     888              : !>      all dummy variables (matrix_a, matrix_b, and trace) are 1-dimensional arrays.
     889              : !>      \endparblock
     890              : ! **************************************************************************************************
     891              :    #:for longname1, shortname1, appendix1 in types
     892              :       #:for longname2, shortname2, appendix2 in types
     893       187268 :          SUBROUTINE cp_fm_trace_a1b1t1_${shortname1}$${shortname2}$ (matrix_a, matrix_b, trace, accurate)
     894              :             TYPE(${longname1}$), DIMENSION(:), INTENT(IN)      :: matrix_a
     895              :             TYPE(${longname2}$), DIMENSION(:), INTENT(IN)      :: matrix_b
     896              :             REAL(kind=dp), DIMENSION(:), INTENT(OUT)           :: trace
     897              :             LOGICAL, INTENT(IN), OPTIONAL                      :: accurate
     898              : 
     899              :             CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_trace_a1b1t1_${shortname1}$${shortname2}$'
     900              : 
     901              :             INTEGER                                            :: handle, imatrix, n_matrices, &
     902              :                                                                   ncols_local, nrows_local
     903              :             LOGICAL                                            :: use_accurate_sum
     904       187268 :             REAL(kind=dp), DIMENSION(:, :), POINTER            :: ldata_a, ldata_b
     905              :             TYPE(mp_comm_type)                                 :: group
     906              : 
     907       187268 :             CALL timeset(routineN, handle)
     908              : 
     909       187268 :             n_matrices = SIZE(trace)
     910       187268 :             CPASSERT(SIZE(matrix_a) == n_matrices)
     911       187268 :             CPASSERT(SIZE(matrix_b) == n_matrices)
     912              : 
     913       187268 :             use_accurate_sum = .TRUE.
     914       187268 :             IF (PRESENT(accurate)) use_accurate_sum = accurate
     915              : 
     916              : !$OMP PARALLEL DO DEFAULT(NONE), &
     917              : !$OMP             PRIVATE(imatrix, ldata_a, ldata_b, ncols_local), &
     918              : !$OMP             PRIVATE(nrows_local), &
     919       187268 : !$OMP             SHARED(matrix_a, matrix_b, n_matrices, trace, use_accurate_sum)
     920              :             DO imatrix = 1, n_matrices
     921              :                CALL cp_fm_get_info(matrix_a(imatrix) ${appendix1}$, nrow_local=nrows_local, ncol_local=ncols_local)
     922              : 
     923              :                ! assume that the matrices A(i) and B(i) have identical shapes and distribution schemes
     924              :                ldata_a => matrix_a(imatrix) ${appendix1}$%local_data(1:nrows_local, 1:ncols_local)
     925              :                ldata_b => matrix_b(imatrix) ${appendix2}$%local_data(1:nrows_local, 1:ncols_local)
     926              :                IF (use_accurate_sum) THEN
     927              :                   trace(imatrix) = accurate_dot_product(ldata_a, ldata_b)
     928              :                ELSE
     929              :                   trace(imatrix) = SUM(ldata_a*ldata_b)
     930              :                END IF
     931              :             END DO
     932              : !$OMP END PARALLEL DO
     933              : 
     934       187268 :             group = matrix_a(1) ${appendix1}$%matrix_struct%para_env
     935       650832 :             CALL group%sum(trace)
     936              : 
     937       187268 :             CALL timestop(handle)
     938       187268 :          END SUBROUTINE cp_fm_trace_a1b1t1_${shortname1}$${shortname2}$
     939              :       #:endfor
     940              :    #:endfor
     941              : 
     942              : ! **************************************************************************************************
     943              : !> \brief Compute trace(i,j) = \sum_k Tr (matrix_a(k,i)^T matrix_b(k,j)).
     944              : !> \param matrix_a list of A matrices
     945              : !> \param matrix_b list of B matrices
     946              : !> \param trace    computed traces
     947              : !> \param accurate ...
     948              : ! **************************************************************************************************
     949              :    #:for longname1, shortname1, appendix1 in types
     950              :       #:for longname2, shortname2, appendix2 in types
     951        17584 :          SUBROUTINE cp_fm_contracted_trace_a2b2t2_${shortname1}$${shortname2}$ (matrix_a, matrix_b, trace, accurate)
     952              :             TYPE(${longname1}$), DIMENSION(:, :), INTENT(IN)   :: matrix_a
     953              :             TYPE(${longname2}$), DIMENSION(:, :), INTENT(IN)   :: matrix_b
     954              :             REAL(kind=dp), DIMENSION(:, :), INTENT(OUT)        :: trace
     955              :             LOGICAL, INTENT(IN), OPTIONAL                      :: accurate
     956              : 
     957              :             CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_contracted_trace_a2b2t2_${shortname1}$${shortname2}$'
     958              : 
     959              :             INTEGER                                            :: handle, ia, ib, iz, na, nb, ncols_local, &
     960              :                                                                   nrows_local, nz
     961              :             INTEGER(kind=int_8)                                :: ib8, itrace, na8, ntraces
     962              :             LOGICAL                                            :: use_accurate_sum
     963              :             REAL(kind=dp)                                      :: t
     964        17584 :             REAL(kind=dp), DIMENSION(:, :), POINTER            :: ldata_a, ldata_b
     965              :             TYPE(mp_comm_type)                                 :: group
     966              : 
     967        17584 :             CALL timeset(routineN, handle)
     968              : 
     969        17584 :             nz = SIZE(matrix_a, 1)
     970        17584 :             CPASSERT(SIZE(matrix_b, 1) == nz)
     971              : 
     972        17584 :             na = SIZE(matrix_a, 2)
     973        17584 :             nb = SIZE(matrix_b, 2)
     974        17584 :             CPASSERT(SIZE(trace, 1) == na)
     975        17584 :             CPASSERT(SIZE(trace, 2) == nb)
     976              : 
     977        17584 :             use_accurate_sum = .TRUE.
     978        17584 :             IF (PRESENT(accurate)) use_accurate_sum = accurate
     979              : 
     980              :             ! here we use one running index (itrace) instead of two (ia, ib) in order to
     981              :             ! improve load balance between shared-memory threads
     982        17584 :             ntraces = na*nb
     983        17584 :             na8 = INT(na, kind=int_8)
     984              : 
     985              : !$OMP PARALLEL DO DEFAULT(NONE), &
     986              : !$OMP             PRIVATE(ia, ib, ib8, itrace, iz, ldata_a, ldata_b, ncols_local), &
     987              : !$OMP             PRIVATE(nrows_local, t), &
     988        17584 : !$OMP             SHARED(matrix_a, matrix_b, na, na8, nb, ntraces, nz, trace, use_accurate_sum)
     989              :             DO itrace = 1, ntraces
     990              :                ib8 = (itrace - 1)/na8
     991              :                ia = INT(itrace - ib8*na8)
     992              :                ib = INT(ib8) + 1
     993              : 
     994              :                t = 0.0_dp
     995              :                DO iz = 1, nz
     996              :                   CALL cp_fm_get_info(matrix_a(iz, ia) ${appendix1}$, nrow_local=nrows_local, ncol_local=ncols_local)
     997              : 
     998              :                   ! assume that the matrices A(iz, ia) and B(iz, ib) have identical shapes and distribution schemes
     999              :                   ldata_a => matrix_a(iz, ia) ${appendix1}$%local_data(1:nrows_local, 1:ncols_local)
    1000              :                   ldata_b => matrix_b(iz, ib) ${appendix2}$%local_data(1:nrows_local, 1:ncols_local)
    1001              :                   IF (use_accurate_sum) THEN
    1002              :                      t = t + accurate_dot_product(ldata_a, ldata_b)
    1003              :                   ELSE
    1004              :                      t = t + SUM(ldata_a*ldata_b)
    1005              :                   END IF
    1006              :                END DO
    1007              :                trace(ia, ib) = t
    1008              :             END DO
    1009              : !$OMP END PARALLEL DO
    1010              : 
    1011        17584 :             group = matrix_a(1, 1) ${appendix1}$%matrix_struct%para_env
    1012       833876 :             CALL group%sum(trace)
    1013              : 
    1014        17584 :             CALL timestop(handle)
    1015        17584 :          END SUBROUTINE cp_fm_contracted_trace_a2b2t2_${shortname1}$${shortname2}$
    1016              :       #:endfor
    1017              :    #:endfor
    1018              : 
    1019              : ! **************************************************************************************************
    1020              : !> \brief multiplies in place by a triangular matrix:
    1021              : !>       matrix_b = alpha op(triangular_matrix) matrix_b
    1022              : !>      or (if side='R')
    1023              : !>       matrix_b = alpha matrix_b op(triangular_matrix)
    1024              : !>      op(triangular_matrix) is:
    1025              : !>       triangular_matrix (if transpose_tr=.false. and invert_tr=.false.)
    1026              : !>       triangular_matrix^T (if transpose_tr=.true. and invert_tr=.false.)
    1027              : !>       triangular_matrix^(-1) (if transpose_tr=.false. and invert_tr=.true.)
    1028              : !>       triangular_matrix^(-T) (if transpose_tr=.true. and invert_tr=.true.)
    1029              : !> \param triangular_matrix the triangular matrix that multiplies the other
    1030              : !> \param matrix_b the matrix that gets multiplied and stores the result
    1031              : !> \param side on which side of matrix_b stays op(triangular_matrix)
    1032              : !>        (defaults to 'L')
    1033              : !> \param transpose_tr if the triangular matrix should be transposed
    1034              : !>        (defaults to false)
    1035              : !> \param invert_tr if the triangular matrix should be inverted
    1036              : !>        (defaults to false)
    1037              : !> \param uplo_tr if triangular_matrix is stored in the upper ('U') or
    1038              : !>        lower ('L') triangle (defaults to 'U')
    1039              : !> \param unit_diag_tr if the diagonal elements of triangular_matrix should
    1040              : !>        be assumed to be 1 (defaults to false)
    1041              : !> \param n_rows the number of rows of the result (defaults to
    1042              : !>        size(matrix_b,1))
    1043              : !> \param n_cols the number of columns of the result (defaults to
    1044              : !>        size(matrix_b,2))
    1045              : !> \param alpha ...
    1046              : !> \par History
    1047              : !>      08.2002 created [fawzi]
    1048              : !> \author Fawzi Mohamed
    1049              : !> \note
    1050              : !>      needs an mpi env
    1051              : ! **************************************************************************************************
    1052       141232 :    SUBROUTINE cp_fm_triangular_multiply(triangular_matrix, matrix_b, side, &
    1053              :                                         transpose_tr, invert_tr, uplo_tr, unit_diag_tr, n_rows, n_cols, &
    1054              :                                         alpha)
    1055              :       TYPE(cp_fm_type), INTENT(IN)                       :: triangular_matrix, matrix_b
    1056              :       CHARACTER, INTENT(IN), OPTIONAL                    :: side
    1057              :       LOGICAL, INTENT(IN), OPTIONAL                      :: transpose_tr, invert_tr
    1058              :       CHARACTER, INTENT(IN), OPTIONAL                    :: uplo_tr
    1059              :       LOGICAL, INTENT(IN), OPTIONAL                      :: unit_diag_tr
    1060              :       INTEGER, INTENT(IN), OPTIONAL                      :: n_rows, n_cols
    1061              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: alpha
    1062              : 
    1063              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_triangular_multiply'
    1064              : 
    1065              :       CHARACTER                                          :: side_char, transa, unit_diag, uplo
    1066              :       INTEGER                                            :: handle, mdim, m, n
    1067              :       LOGICAL                                            :: invert
    1068              :       REAL(KIND=dp)                                      :: al
    1069              : 
    1070        70616 :       CALL timeset(routineN, handle)
    1071        70616 :       side_char = 'L'
    1072        70616 :       unit_diag = 'N'
    1073        70616 :       uplo = 'U'
    1074        70616 :       transa = 'N'
    1075        70616 :       invert = .FALSE.
    1076        70616 :       al = 1.0_dp
    1077        70616 :       CALL cp_fm_get_info(matrix_b, nrow_global=m, ncol_global=n)
    1078        70616 :       IF (PRESENT(side)) side_char = side
    1079              :       mdim = MERGE(1, 2, 'L' == side_char)
    1080        70616 :       IF (PRESENT(invert_tr)) invert = invert_tr
    1081        70616 :       IF (PRESENT(uplo_tr)) uplo = uplo_tr
    1082        70616 :       IF (PRESENT(unit_diag_tr)) THEN
    1083            0 :          IF (unit_diag_tr) THEN
    1084            0 :             unit_diag = 'U'
    1085              :          ELSE
    1086              :             unit_diag = 'N'
    1087              :          END IF
    1088              :       END IF
    1089        70616 :       IF (PRESENT(transpose_tr)) THEN
    1090         6666 :          IF (transpose_tr) THEN
    1091         3028 :             transa = 'T'
    1092              :          ELSE
    1093              :             transa = 'N'
    1094              :          END IF
    1095              :       END IF
    1096        70616 :       IF (PRESENT(alpha)) al = alpha
    1097        70616 :       IF (PRESENT(n_rows)) m = n_rows
    1098        70616 :       IF (PRESENT(n_cols)) n = n_cols
    1099              : 
    1100        70616 :       IF (invert) THEN
    1101              : 
    1102              : #if defined(__parallel)
    1103              :          CALL pdtrsm(side_char, uplo, transa, unit_diag, m, n, al, &
    1104              :                      triangular_matrix%local_data(1, 1), 1, 1, &
    1105              :                      triangular_matrix%matrix_struct%descriptor, &
    1106              :                      matrix_b%local_data(1, 1), 1, 1, &
    1107        53150 :                      matrix_b%matrix_struct%descriptor(1))
    1108              : #else
    1109              :          CALL dtrsm(side_char, uplo, transa, unit_diag, m, n, al, &
    1110              :                     triangular_matrix%local_data(1, 1), &
    1111              :                     SIZE(triangular_matrix%local_data, mdim), &
    1112              :                     matrix_b%local_data(1, 1), SIZE(matrix_b%local_data, 1))
    1113              : #endif
    1114              : 
    1115              :       ELSE
    1116              : 
    1117              : #if defined(__parallel)
    1118              :          CALL pdtrmm(side_char, uplo, transa, unit_diag, m, n, al, &
    1119              :                      triangular_matrix%local_data(1, 1), 1, 1, &
    1120              :                      triangular_matrix%matrix_struct%descriptor, &
    1121              :                      matrix_b%local_data(1, 1), 1, 1, &
    1122        17466 :                      matrix_b%matrix_struct%descriptor(1))
    1123              : #else
    1124              :          CALL dtrmm(side_char, uplo, transa, unit_diag, m, n, al, &
    1125              :                     triangular_matrix%local_data(1, 1), &
    1126              :                     SIZE(triangular_matrix%local_data, mdim), &
    1127              :                     matrix_b%local_data(1, 1), SIZE(matrix_b%local_data, 1))
    1128              : #endif
    1129              : 
    1130              :       END IF
    1131              : 
    1132        70616 :       CALL timestop(handle)
    1133        70616 :    END SUBROUTINE cp_fm_triangular_multiply
    1134              : 
    1135              : ! **************************************************************************************************
    1136              : !> \brief scales a matrix
    1137              : !>      matrix_a = alpha * matrix_b
    1138              : !> \param alpha ...
    1139              : !> \param matrix_a ...
    1140              : !> \note
    1141              : !>      use cp_fm_set_all to zero (avoids problems with nan)
    1142              : ! **************************************************************************************************
    1143       231780 :    SUBROUTINE cp_fm_scale(alpha, matrix_a)
    1144              :       REAL(KIND=dp), INTENT(IN)                          :: alpha
    1145              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix_a
    1146              : 
    1147              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_fm_scale'
    1148              : 
    1149              :       INTEGER                                            :: handle, size_a
    1150              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: a
    1151              : 
    1152       231780 :       CALL timeset(routineN, handle)
    1153              : 
    1154              :       NULLIFY (a)
    1155              : 
    1156       231780 :       a => matrix_a%local_data
    1157       231780 :       size_a = SIZE(a, 1)*SIZE(a, 2)
    1158              : 
    1159       231780 :       CALL DSCAL(size_a, alpha, a, 1)
    1160              : 
    1161       231780 :       CALL timestop(handle)
    1162              : 
    1163       231780 :    END SUBROUTINE cp_fm_scale
    1164              : 
    1165              : ! **************************************************************************************************
    1166              : !> \brief transposes a matrix
    1167              : !>      matrixt = matrix ^ T
    1168              : !> \param matrix ...
    1169              : !> \param matrixt ...
    1170              : !> \note
    1171              : !>      all matrix elements are transposed (see cp_fm_uplo_to_full to symmetrise a matrix)
    1172              : ! **************************************************************************************************
    1173        40688 :    SUBROUTINE cp_fm_transpose(matrix, matrixt)
    1174              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix, matrixt
    1175              : 
    1176              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_transpose'
    1177              : 
    1178              :       INTEGER                                  :: handle, ncol_global, &
    1179              :                                                   nrow_global, ncol_globalt, nrow_globalt
    1180        20344 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, c
    1181              : #if defined(__parallel)
    1182              :       INTEGER, DIMENSION(9)                    :: desca, descc
    1183              : #elif !defined(__MKL)
    1184              :       INTEGER                                  :: i, j
    1185              : #endif
    1186              : 
    1187        20344 :       nrow_global = matrix%matrix_struct%nrow_global
    1188        20344 :       ncol_global = matrix%matrix_struct%ncol_global
    1189        20344 :       nrow_globalt = matrixt%matrix_struct%nrow_global
    1190        20344 :       ncol_globalt = matrixt%matrix_struct%ncol_global
    1191            0 :       CPASSERT(nrow_global == ncol_globalt)
    1192        20344 :       CPASSERT(nrow_globalt == ncol_global)
    1193              : 
    1194        20344 :       CALL timeset(routineN, handle)
    1195              : 
    1196        20344 :       a => matrix%local_data
    1197        20344 :       c => matrixt%local_data
    1198              : 
    1199              : #if defined(__parallel)
    1200       203440 :       desca(:) = matrix%matrix_struct%descriptor(:)
    1201       203440 :       descc(:) = matrixt%matrix_struct%descriptor(:)
    1202        20344 :       CALL pdtran(ncol_global, nrow_global, 1.0_dp, a(1, 1), 1, 1, desca, 0.0_dp, c(1, 1), 1, 1, descc)
    1203              : #elif defined(__MKL)
    1204              :       CALL mkl_domatcopy('C', 'T', nrow_global, ncol_global, 1.0_dp, a(1, 1), nrow_global, c(1, 1), ncol_global)
    1205              : #else
    1206              :       DO j = 1, ncol_global
    1207              :          DO i = 1, nrow_global
    1208              :             c(j, i) = a(i, j)
    1209              :          END DO
    1210              :       END DO
    1211              : #endif
    1212        20344 :       CALL timestop(handle)
    1213              : 
    1214        20344 :    END SUBROUTINE cp_fm_transpose
    1215              : 
    1216              : ! **************************************************************************************************
    1217              : !> \brief given a triangular matrix according to uplo, computes the corresponding full matrix
    1218              : !> \param matrix the triangular matrix as input, the full matrix as output
    1219              : !> \param work a matrix of the same size as matrix
    1220              : !> \param uplo triangular format; defaults to 'U'
    1221              : !> \author Matthias Krack
    1222              : !> \note
    1223              : !>       the opposite triangular part is irrelevant
    1224              : ! **************************************************************************************************
    1225       387746 :    SUBROUTINE cp_fm_uplo_to_full(matrix, work, uplo)
    1226              : 
    1227              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix, work
    1228              :       CHARACTER, INTENT(IN), OPTIONAL          :: uplo
    1229              : 
    1230              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_uplo_to_full'
    1231              : 
    1232              :       CHARACTER                                :: myuplo
    1233              :       INTEGER                                  :: handle, icol_global, irow_global, &
    1234              :                                                   mypcol, myprow, ncol_global, &
    1235              :                                                   nrow_global
    1236       193873 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
    1237              :       TYPE(cp_blacs_env_type), POINTER         :: context
    1238              : 
    1239              : #if defined(__parallel)
    1240              :       INTEGER                                  :: icol_local, irow_local, &
    1241              :                                                   ncol_block, ncol_local, &
    1242              :                                                   nrow_block, nrow_local
    1243              :       INTEGER, DIMENSION(9)                    :: desca, descc
    1244       193873 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: c
    1245              : #endif
    1246              : 
    1247       193873 :       myuplo = 'U'
    1248            0 :       IF (PRESENT(uplo)) myuplo = uplo
    1249              : 
    1250       193873 :       nrow_global = matrix%matrix_struct%nrow_global
    1251       193873 :       ncol_global = matrix%matrix_struct%ncol_global
    1252       193873 :       CPASSERT(nrow_global == ncol_global)
    1253       193873 :       nrow_global = work%matrix_struct%nrow_global
    1254       193873 :       ncol_global = work%matrix_struct%ncol_global
    1255       193873 :       CPASSERT(nrow_global == ncol_global)
    1256              : 
    1257       193873 :       CALL timeset(routineN, handle)
    1258              : 
    1259       193873 :       context => matrix%matrix_struct%context
    1260       193873 :       myprow = context%mepos(1)
    1261       193873 :       mypcol = context%mepos(2)
    1262              : 
    1263              : #if defined(__parallel)
    1264              : 
    1265       193873 :       nrow_block = matrix%matrix_struct%nrow_block
    1266       193873 :       ncol_block = matrix%matrix_struct%ncol_block
    1267              : 
    1268       193873 :       nrow_local = matrix%matrix_struct%nrow_locals(myprow)
    1269       193873 :       ncol_local = matrix%matrix_struct%ncol_locals(mypcol)
    1270              : 
    1271       193873 :       a => work%local_data
    1272      1938730 :       desca(:) = work%matrix_struct%descriptor(:)
    1273       193873 :       c => matrix%local_data
    1274      1938730 :       descc(:) = matrix%matrix_struct%descriptor(:)
    1275              : 
    1276      6026996 :       DO icol_local = 1, ncol_local
    1277      5833123 :          icol_global = matrix%matrix_struct%col_indices(icol_local)
    1278    245655146 :          DO irow_local = 1, nrow_local
    1279    239628150 :             irow_global = matrix%matrix_struct%row_indices(irow_local)
    1280    245461273 :             IF (MERGE(irow_global > icol_global, irow_global < icol_global, (myuplo == "U") .OR. (myuplo == "u"))) THEN
    1281    118230619 :                c(irow_local, icol_local) = 0.0_dp
    1282    121397531 :             ELSE IF (irow_global == icol_global) THEN
    1283      3166884 :                c(irow_local, icol_local) = 0.5_dp*c(irow_local, icol_local)
    1284              :             END IF
    1285              :          END DO
    1286              :       END DO
    1287              : 
    1288      6026996 :       DO icol_local = 1, ncol_local
    1289    245655146 :       DO irow_local = 1, nrow_local
    1290    245461273 :          a(irow_local, icol_local) = c(irow_local, icol_local)
    1291              :       END DO
    1292              :       END DO
    1293              : 
    1294       193873 :       CALL pdtran(nrow_global, ncol_global, 1.0_dp, a(1, 1), 1, 1, desca, 1.0_dp, c(1, 1), 1, 1, descc)
    1295              : 
    1296              : #else
    1297              : 
    1298              :       a => matrix%local_data
    1299              : 
    1300              :       IF ((myuplo == "U") .OR. (myuplo == "u")) THEN
    1301              :          DO irow_global = 1, nrow_global
    1302              :          DO icol_global = irow_global + 1, ncol_global
    1303              :             a(icol_global, irow_global) = a(irow_global, icol_global)
    1304              :          END DO
    1305              :          END DO
    1306              :       ELSE
    1307              :          DO icol_global = 1, ncol_global
    1308              :          DO irow_global = icol_global + 1, nrow_global
    1309              :             a(irow_global, icol_global) = a(icol_global, irow_global)
    1310              :          END DO
    1311              :          END DO
    1312              :       END IF
    1313              : 
    1314              : #endif
    1315       193873 :       CALL timestop(handle)
    1316              : 
    1317       193873 :    END SUBROUTINE cp_fm_uplo_to_full
    1318              : 
    1319              : ! **************************************************************************************************
    1320              : !> \brief scales column i of matrix a with scaling(i)
    1321              : !> \param matrixa ...
    1322              : !> \param scaling : an array used for scaling the columns,
    1323              : !>                  SIZE(scaling) determines the number of columns to be scaled
    1324              : !> \author Joost VandeVondele
    1325              : !> \note
    1326              : !>      this is very useful as a first step in the computation of C = sum_i alpha_i A_i transpose (A_i)
    1327              : !>      that is a rank-k update (cp_fm_syrk , cp_sm_plus_fm_fm_t)
    1328              : !>      this procedure can be up to 20 times faster than calling cp_fm_syrk n times
    1329              : !>      where every vector has a different prefactor
    1330              : ! **************************************************************************************************
    1331       122474 :    SUBROUTINE cp_fm_column_scale(matrixa, scaling)
    1332              :       TYPE(cp_fm_type), INTENT(IN)             :: matrixa
    1333              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)  :: scaling
    1334              : 
    1335              :       INTEGER                                  :: k, mypcol, myprow, n, ncol_global, &
    1336              :                                                   npcol, nprow
    1337       122474 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
    1338              : #if defined(__parallel)
    1339              :       INTEGER                                  :: icol_global, icol_local, &
    1340              :                                                   ipcol, iprow, irow_local
    1341              : #else
    1342              :       INTEGER                                  :: i
    1343              : #endif
    1344              : 
    1345       122474 :       myprow = matrixa%matrix_struct%context%mepos(1)
    1346       122474 :       mypcol = matrixa%matrix_struct%context%mepos(2)
    1347       122474 :       nprow = matrixa%matrix_struct%context%num_pe(1)
    1348       122474 :       npcol = matrixa%matrix_struct%context%num_pe(2)
    1349              : 
    1350       122474 :       ncol_global = matrixa%matrix_struct%ncol_global
    1351              : 
    1352       122474 :       a => matrixa%local_data
    1353       122474 :       n = SIZE(a, 1)
    1354       122474 :       k = MIN(SIZE(scaling), ncol_global)
    1355              : 
    1356              : #if defined(__parallel)
    1357              : 
    1358      1237438 :       DO icol_global = 1, k
    1359              :          CALL infog2l(1, icol_global, matrixa%matrix_struct%descriptor, &
    1360              :                       nprow, npcol, myprow, mypcol, &
    1361      1114964 :                       irow_local, icol_local, iprow, ipcol)
    1362      1237438 :          IF ((ipcol == mypcol)) THEN
    1363      1114964 :             CALL DSCAL(n, scaling(icol_global), a(:, icol_local), 1)
    1364              :          END IF
    1365              :       END DO
    1366              : #else
    1367              :       DO i = 1, k
    1368              :          CALL DSCAL(n, scaling(i), a(:, i), 1)
    1369              :       END DO
    1370              : #endif
    1371       122474 :    END SUBROUTINE cp_fm_column_scale
    1372              : 
    1373              : ! **************************************************************************************************
    1374              : !> \brief scales row i of matrix a with scaling(i)
    1375              : !> \param matrixa ...
    1376              : !> \param scaling : an array used for scaling the columns,
    1377              : !> \author JGH
    1378              : !> \note
    1379              : ! **************************************************************************************************
    1380         8214 :    SUBROUTINE cp_fm_row_scale(matrixa, scaling)
    1381              :       TYPE(cp_fm_type), INTENT(IN)             :: matrixa
    1382              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)  :: scaling
    1383              : 
    1384              :       INTEGER                                  :: n, m, nrow_global, nrow_local, ncol_local
    1385         8214 :       INTEGER, DIMENSION(:), POINTER           :: row_indices
    1386         8214 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
    1387              : #if defined(__parallel)
    1388              :       INTEGER                                  :: irow_global, icol, irow
    1389              : #else
    1390              :       INTEGER                                  :: j
    1391              : #endif
    1392              : 
    1393              :       CALL cp_fm_get_info(matrixa, row_indices=row_indices, nrow_global=nrow_global, &
    1394         8214 :                           nrow_local=nrow_local, ncol_local=ncol_local)
    1395         8214 :       CPASSERT(SIZE(scaling) == nrow_global)
    1396              : 
    1397         8214 :       a => matrixa%local_data
    1398         8214 :       n = SIZE(a, 1)
    1399         8214 :       m = SIZE(a, 2)
    1400              : 
    1401              : #if defined(__parallel)
    1402        88030 :       DO icol = 1, ncol_local
    1403      6522512 :          DO irow = 1, nrow_local
    1404      6434482 :             irow_global = row_indices(irow)
    1405      6514298 :             a(irow, icol) = scaling(irow_global)*a(irow, icol)
    1406              :          END DO
    1407              :       END DO
    1408              : #else
    1409              :       DO j = 1, m
    1410              :          a(1:n, j) = scaling(1:n)*a(1:n, j)
    1411              :       END DO
    1412              : #endif
    1413         8214 :    END SUBROUTINE cp_fm_row_scale
    1414              : 
    1415              : ! **************************************************************************************************
    1416              : !> \brief Inverts a cp_fm_type matrix, optionally returning the determinant of the input matrix
    1417              : !> \param matrix_a the matrix to invert
    1418              : !> \param matrix_inverse the inverse of matrix_a
    1419              : !> \param det_a the determinant of matrix_a
    1420              : !> \param eps_svd optional parameter to active SVD based inversion, singular values below eps_svd
    1421              : !>                are screened
    1422              : !> \param eigval optionally return matrix eigenvalues/singular values
    1423              : !> \par History
    1424              : !>      note of Jan Wilhelm (12.2015)
    1425              : !>      - computation of determinant corrected
    1426              : !>      - determinant only computed if det_a is present
    1427              : !>      12.2016 added option to use SVD instead of LU [Nico Holmberg]
    1428              : !>      - Use cp_fm_get diag instead of n times cp_fm_get_element (A. Bussy)
    1429              : !> \author Florian Schiffmann(02.2007)
    1430              : ! **************************************************************************************************
    1431         1854 :    SUBROUTINE cp_fm_invert(matrix_a, matrix_inverse, det_a, eps_svd, eigval)
    1432              : 
    1433              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a, matrix_inverse
    1434              :       REAL(KIND=dp), INTENT(OUT), OPTIONAL     :: det_a
    1435              :       REAL(KIND=dp), INTENT(IN), OPTIONAL      :: eps_svd
    1436              :       REAL(KIND=dp), DIMENSION(:), POINTER, &
    1437              :          INTENT(INOUT), OPTIONAL               :: eigval
    1438              : 
    1439              :       INTEGER                                  :: n
    1440         1854 :       INTEGER, ALLOCATABLE, DIMENSION(:)       :: ipivot
    1441              :       REAL(KIND=dp)                            :: determinant, my_eps_svd
    1442              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
    1443              :       TYPE(cp_fm_type)                         :: matrix_lu
    1444              : 
    1445              : #if defined(__parallel)
    1446              :       TYPE(cp_fm_type)                         :: u, vt, sigma, inv_sigma_ut
    1447              :       TYPE(mp_comm_type) :: group
    1448              :       INTEGER                                  :: i, info, liwork, lwork, exponent_of_minus_one
    1449              :       INTEGER, DIMENSION(9)                    :: desca
    1450              :       LOGICAL                                  :: quenched
    1451              :       REAL(KIND=dp)                            :: alpha, beta
    1452         1854 :       REAL(KIND=dp), DIMENSION(:), POINTER     :: diag
    1453         1854 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: work
    1454              : #else
    1455              :       LOGICAL                                  :: sign
    1456              :       REAL(KIND=dp)                            :: eps1
    1457              : #endif
    1458              : 
    1459         1854 :       my_eps_svd = 0.0_dp
    1460          480 :       IF (PRESENT(eps_svd)) my_eps_svd = eps_svd
    1461              : 
    1462              :       CALL cp_fm_create(matrix=matrix_lu, &
    1463              :                         matrix_struct=matrix_a%matrix_struct, &
    1464         1854 :                         name="A_lu"//TRIM(ADJUSTL(cp_to_string(1)))//"MATRIX")
    1465         1854 :       CALL cp_fm_to_fm(matrix_a, matrix_lu)
    1466              : 
    1467         1854 :       a => matrix_lu%local_data
    1468         1854 :       n = matrix_lu%matrix_struct%nrow_global
    1469         5562 :       ALLOCATE (ipivot(n + matrix_a%matrix_struct%nrow_block))
    1470         1854 :       ipivot(:) = 0
    1471              : #if defined(__parallel)
    1472         1854 :       IF (my_eps_svd == 0.0_dp) THEN
    1473              :          ! Use LU decomposition
    1474              :          lwork = 3*n
    1475              :          liwork = 3*n
    1476        18240 :          desca(:) = matrix_lu%matrix_struct%descriptor(:)
    1477         1824 :          CALL pdgetrf(n, n, a, 1, 1, desca, ipivot, info)
    1478              : 
    1479         1824 :          IF (PRESENT(det_a) .OR. PRESENT(eigval)) THEN
    1480              : 
    1481         1134 :             ALLOCATE (diag(n))
    1482          928 :             diag(:) = 0.0_dp
    1483          378 :             CALL cp_fm_get_diag(matrix_lu, diag)
    1484              : 
    1485          378 :             exponent_of_minus_one = 0
    1486          378 :             determinant = 1.0_dp
    1487          928 :             DO i = 1, n
    1488          550 :                determinant = determinant*diag(i)
    1489          928 :                IF (ipivot(i) /= i) THEN
    1490          224 :                   exponent_of_minus_one = exponent_of_minus_one + 1
    1491              :                END IF
    1492              :             END DO
    1493          378 :             IF (PRESENT(eigval)) THEN
    1494            0 :                CPASSERT(.NOT. ASSOCIATED(eigval))
    1495            0 :                ALLOCATE (eigval(n))
    1496            0 :                eigval(:) = diag
    1497              :             END IF
    1498          378 :             DEALLOCATE (diag)
    1499              : 
    1500          378 :             group = matrix_lu%matrix_struct%para_env
    1501          378 :             CALL group%sum(exponent_of_minus_one)
    1502              : 
    1503          378 :             determinant = determinant*(-1.0_dp)**exponent_of_minus_one
    1504              : 
    1505              :          END IF
    1506              : 
    1507         1824 :          alpha = 0.0_dp
    1508         1824 :          beta = 1.0_dp
    1509         1824 :          CALL cp_fm_set_all(matrix_inverse, alpha, beta)
    1510         1824 :          CALL pdgetrs('N', n, n, matrix_lu%local_data, 1, 1, desca, ipivot, matrix_inverse%local_data, 1, 1, desca, info)
    1511              :       ELSE
    1512              :          ! Use singular value decomposition
    1513              :          CALL cp_fm_create(matrix=u, &
    1514              :                            matrix_struct=matrix_a%matrix_struct, &
    1515           30 :                            name="LEFT_SINGULAR_MATRIX")
    1516           30 :          CALL cp_fm_set_all(u, alpha=0.0_dp)
    1517              :          CALL cp_fm_create(matrix=vt, &
    1518              :                            matrix_struct=matrix_a%matrix_struct, &
    1519           30 :                            name="RIGHT_SINGULAR_MATRIX")
    1520           30 :          CALL cp_fm_set_all(vt, alpha=0.0_dp)
    1521           90 :          ALLOCATE (diag(n))
    1522           96 :          diag(:) = 0.0_dp
    1523          300 :          desca(:) = matrix_lu%matrix_struct%descriptor(:)
    1524           30 :          ALLOCATE (work(1))
    1525              :          ! Workspace query
    1526           30 :          lwork = -1
    1527              :          CALL pdgesvd('V', 'V', n, n, matrix_lu%local_data, 1, 1, desca, diag, u%local_data, &
    1528           30 :                       1, 1, desca, vt%local_data, 1, 1, desca, work, lwork, info)
    1529           30 :          lwork = INT(work(1))
    1530           30 :          DEALLOCATE (work)
    1531           90 :          ALLOCATE (work(lwork))
    1532              :          ! SVD
    1533              :          CALL pdgesvd('V', 'V', n, n, matrix_lu%local_data, 1, 1, desca, diag, u%local_data, &
    1534           30 :                       1, 1, desca, vt%local_data, 1, 1, desca, work, lwork, info)
    1535              :          ! info == n+1 implies homogeneity error when the number of procs is large
    1536              :          ! this likely isnt a problem, but maybe we should handle it separately
    1537           30 :          IF (info /= 0 .AND. info /= n + 1) &
    1538            0 :             CPABORT("Singular value decomposition of matrix failed.")
    1539              :          ! (Pseudo)inverse and (pseudo)determinant
    1540              :          CALL cp_fm_create(matrix=sigma, &
    1541              :                            matrix_struct=matrix_a%matrix_struct, &
    1542           30 :                            name="SINGULAR_VALUE_MATRIX")
    1543           30 :          CALL cp_fm_set_all(sigma, alpha=0.0_dp)
    1544           30 :          determinant = 1.0_dp
    1545           30 :          quenched = .FALSE.
    1546           30 :          IF (PRESENT(eigval)) THEN
    1547           28 :             CPASSERT(.NOT. ASSOCIATED(eigval))
    1548           84 :             ALLOCATE (eigval(n))
    1549          156 :             eigval(:) = diag
    1550              :          END IF
    1551           96 :          DO i = 1, n
    1552           66 :             IF (diag(i) < my_eps_svd) THEN
    1553           18 :                diag(i) = 0.0_dp
    1554           18 :                quenched = .TRUE.
    1555              :             ELSE
    1556           48 :                determinant = determinant*diag(i)
    1557           48 :                diag(i) = 1.0_dp/diag(i)
    1558              :             END IF
    1559           96 :             CALL cp_fm_set_element(sigma, i, i, diag(i))
    1560              :          END DO
    1561           30 :          DEALLOCATE (diag)
    1562           30 :          IF (quenched) &
    1563              :             CALL cp_warn(__LOCATION__, &
    1564              :                          "Linear dependencies were detected in the SVD inversion of matrix "//TRIM(ADJUSTL(matrix_a%name))// &
    1565           12 :                          ". At least one singular value has been quenched.")
    1566              :          ! Sigma^-1 * U^T
    1567              :          CALL cp_fm_create(matrix=inv_sigma_ut, &
    1568              :                            matrix_struct=matrix_a%matrix_struct, &
    1569           30 :                            name="SINGULAR_VALUE_MATRIX")
    1570           30 :          CALL cp_fm_set_all(inv_sigma_ut, alpha=0.0_dp)
    1571              :          CALL pdgemm('N', 'T', n, n, n, 1.0_dp, sigma%local_data, 1, 1, desca, &
    1572           30 :                      u%local_data, 1, 1, desca, 0.0_dp, inv_sigma_ut%local_data, 1, 1, desca)
    1573              :          ! A^-1 = V * (Sigma^-1 * U^T)
    1574           30 :          CALL cp_fm_set_all(matrix_inverse, alpha=0.0_dp)
    1575              :          CALL pdgemm('T', 'N', n, n, n, 1.0_dp, vt%local_data, 1, 1, desca, &
    1576           30 :                      inv_sigma_ut%local_data, 1, 1, desca, 0.0_dp, matrix_inverse%local_data, 1, 1, desca)
    1577              :          ! Clean up
    1578           30 :          DEALLOCATE (work)
    1579           30 :          CALL cp_fm_release(u)
    1580           30 :          CALL cp_fm_release(vt)
    1581           30 :          CALL cp_fm_release(sigma)
    1582          150 :          CALL cp_fm_release(inv_sigma_ut)
    1583              :       END IF
    1584              : #else
    1585              :       IF (my_eps_svd == 0.0_dp) THEN
    1586              :          sign = .TRUE.
    1587              :          CALL invert_matrix(matrix_a%local_data, matrix_inverse%local_data, &
    1588              :                             eval_error=eps1)
    1589              :          CALL cp_fm_lu_decompose(matrix_lu, determinant, correct_sign=sign)
    1590              :          IF (PRESENT(eigval)) &
    1591              :             CALL cp_abort(__LOCATION__, &
    1592              :                           "NYI. Eigenvalues not available for return without SCALAPACK.")
    1593              :       ELSE
    1594              :          CALL get_pseudo_inverse_svd(matrix_a%local_data, matrix_inverse%local_data, eps_svd, &
    1595              :                                      determinant, eigval)
    1596              :       END IF
    1597              : #endif
    1598         1854 :       CALL cp_fm_release(matrix_lu)
    1599         1854 :       DEALLOCATE (ipivot)
    1600         1854 :       IF (PRESENT(det_a)) det_a = determinant
    1601         1854 :    END SUBROUTINE cp_fm_invert
    1602              : 
    1603              : ! **************************************************************************************************
    1604              : !> \brief inverts a triangular matrix
    1605              : !> \param matrix_a ...
    1606              : !> \param uplo_tr triangular format; defaults to 'U'
    1607              : !> \author MI
    1608              : ! **************************************************************************************************
    1609         5880 :    SUBROUTINE cp_fm_triangular_invert(matrix_a, uplo_tr)
    1610              : 
    1611              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a
    1612              :       CHARACTER, INTENT(IN), OPTIONAL          :: uplo_tr
    1613              : 
    1614              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_triangular_invert'
    1615              : 
    1616              :       CHARACTER                                :: unit_diag, uplo
    1617              :       INTEGER                                  :: handle, info, ncol_global
    1618         5880 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
    1619              : #if defined(__parallel)
    1620              :       INTEGER, DIMENSION(9)                    :: desca
    1621              : #endif
    1622              : 
    1623         5880 :       CALL timeset(routineN, handle)
    1624              : 
    1625         5880 :       unit_diag = 'N'
    1626         5880 :       uplo = 'U'
    1627         5880 :       IF (PRESENT(uplo_tr)) uplo = uplo_tr
    1628              : 
    1629         5880 :       ncol_global = matrix_a%matrix_struct%ncol_global
    1630              : 
    1631         5880 :       a => matrix_a%local_data
    1632              : 
    1633              : #if defined(__parallel)
    1634        58800 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
    1635              : 
    1636         5880 :       CALL pdtrtri(uplo, unit_diag, ncol_global, a(1, 1), 1, 1, desca, info)
    1637              : 
    1638              : #else
    1639              :       CALL dtrtri(uplo, unit_diag, ncol_global, a(1, 1), ncol_global, info)
    1640              : #endif
    1641              : 
    1642         5880 :       CALL timestop(handle)
    1643         5880 :    END SUBROUTINE cp_fm_triangular_invert
    1644              : 
    1645              : ! **************************************************************************************************
    1646              : !> \brief  performs a QR factorization of the input rectangular matrix A or of a submatrix of A
    1647              : !>         the computed triangular matrix R is in output of the submatrix sub(A) of size NxN
    1648              : !>         M and M give the dimension of the submatrix that has to be factorized (MxN) with M>N
    1649              : !> \param matrix_a ...
    1650              : !> \param matrix_r ...
    1651              : !> \param nrow_fact ...
    1652              : !> \param ncol_fact ...
    1653              : !> \param first_row ...
    1654              : !> \param first_col ...
    1655              : !> \author MI
    1656              : ! **************************************************************************************************
    1657        23296 :    SUBROUTINE cp_fm_qr_factorization(matrix_a, matrix_r, nrow_fact, ncol_fact, first_row, first_col, uplo)
    1658              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a, matrix_r
    1659              :       INTEGER, INTENT(IN), OPTIONAL            :: nrow_fact, ncol_fact, &
    1660              :                                                   first_row, first_col
    1661              :       CHARACTER, INTENT(IN), OPTIONAL          :: uplo
    1662              : 
    1663              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_qr_factorization'
    1664              : 
    1665              :       CHARACTER                                :: myuplo
    1666              :       INTEGER                                  :: handle, i, icol, info, irow, &
    1667              :                                                   j, lda, lwork, ncol, &
    1668              :                                                   ndim, nrow
    1669        23296 :       REAL(dp), ALLOCATABLE, DIMENSION(:)      :: tau, work
    1670        23296 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)   :: r_mat
    1671              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
    1672              : #if defined(__parallel)
    1673              :       INTEGER, DIMENSION(9)                    :: desca
    1674              : #endif
    1675              : 
    1676        23296 :       CALL timeset(routineN, handle)
    1677              : 
    1678        23296 :       myuplo = 'U'
    1679        23296 :       IF (PRESENT(uplo)) myuplo = uplo
    1680              : 
    1681        23296 :       ncol = matrix_a%matrix_struct%ncol_global
    1682        23296 :       nrow = matrix_a%matrix_struct%nrow_global
    1683        23296 :       lda = nrow
    1684              : 
    1685        23296 :       a => matrix_a%local_data
    1686              : 
    1687        23296 :       IF (PRESENT(nrow_fact)) nrow = nrow_fact
    1688        23296 :       IF (PRESENT(ncol_fact)) ncol = ncol_fact
    1689        23296 :       irow = 1
    1690        23296 :       IF (PRESENT(first_row)) irow = first_row
    1691        23296 :       icol = 1
    1692        23296 :       IF (PRESENT(first_col)) icol = first_col
    1693              : 
    1694        23296 :       CPASSERT(nrow >= ncol)
    1695        23296 :       ndim = SIZE(a, 2)
    1696        69888 :       ALLOCATE (tau(ndim))
    1697              : 
    1698              : #if defined(__parallel)
    1699              : 
    1700       232960 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
    1701              : 
    1702        23296 :       lwork = -1
    1703        69888 :       ALLOCATE (work(2*ndim))
    1704        23296 :       CALL pdgeqrf(nrow, ncol, a, irow, icol, desca, tau, work, lwork, info)
    1705        23296 :       lwork = INT(work(1))
    1706        23296 :       DEALLOCATE (work)
    1707        69888 :       ALLOCATE (work(lwork))
    1708        23296 :       CALL pdgeqrf(nrow, ncol, a, irow, icol, desca, tau, work, lwork, info)
    1709              : 
    1710              : #else
    1711              :       lwork = -1
    1712              :       ALLOCATE (work(2*ndim))
    1713              :       CALL dgeqrf(nrow, ncol, a, lda, tau, work, lwork, info)
    1714              :       lwork = INT(work(1))
    1715              :       DEALLOCATE (work)
    1716              :       ALLOCATE (work(lwork))
    1717              :       CALL dgeqrf(nrow, ncol, a, lda, tau, work, lwork, info)
    1718              : 
    1719              : #endif
    1720              : 
    1721        93184 :       ALLOCATE (r_mat(ncol, ncol))
    1722        23296 :       CALL cp_fm_get_submatrix(matrix_a, r_mat, 1, 1, ncol, ncol)
    1723        23296 :       IF ((myuplo == "U") .OR. (myuplo == "u")) THEN
    1724        56840 :          DO i = 1, ncol
    1725        76734 :          DO j = i + 1, ncol
    1726        53438 :             r_mat(j, i) = 0.0_dp
    1727              :          END DO
    1728              :          END DO
    1729              :       ELSE
    1730            0 :          DO j = 1, ncol
    1731            0 :          DO i = j + 1, ncol
    1732            0 :             r_mat(i, j) = 0.0_dp
    1733              :          END DO
    1734              :          END DO
    1735              :       END IF
    1736        23296 :       CALL cp_fm_set_submatrix(matrix_r, r_mat, 1, 1, ncol, ncol)
    1737              : 
    1738        23296 :       DEALLOCATE (tau, work, r_mat)
    1739              : 
    1740        23296 :       CALL timestop(handle)
    1741              : 
    1742        23296 :    END SUBROUTINE cp_fm_qr_factorization
    1743              : 
    1744              : ! **************************************************************************************************
    1745              : !> \brief computes the the solution to A*b=A_general using lu decomposition
    1746              : !> \param matrix_a input matrix; will be overwritten
    1747              : !> \param general_a contains the result
    1748              : !> \author Florian Schiffmann
    1749              : ! **************************************************************************************************
    1750         8094 :    SUBROUTINE cp_fm_solve(matrix_a, general_a)
    1751              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix_a, general_a
    1752              : 
    1753              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_solve'
    1754              : 
    1755              :       INTEGER                                  :: handle, info, n, nrhs
    1756         8094 :       INTEGER, ALLOCATABLE, DIMENSION(:)       :: ipivot
    1757              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, a_general
    1758              : #if defined(__parallel)
    1759              :       INTEGER, DIMENSION(9)                    :: desca, descb
    1760              : #else
    1761              :       INTEGER                                  :: lda, ldb
    1762              : #endif
    1763              : 
    1764         8094 :       CALL timeset(routineN, handle)
    1765              : 
    1766         8094 :       a => matrix_a%local_data
    1767         8094 :       a_general => general_a%local_data
    1768         8094 :       n = matrix_a%matrix_struct%nrow_global
    1769         8094 :       nrhs = general_a%matrix_struct%ncol_global
    1770        24282 :       ALLOCATE (ipivot(n + matrix_a%matrix_struct%nrow_block))
    1771              : 
    1772              : #if defined(__parallel)
    1773        80940 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
    1774        80940 :       descb(:) = general_a%matrix_struct%descriptor(:)
    1775         8094 :       CALL pdgetrf(n, n, a, 1, 1, desca, ipivot, info)
    1776              :       CALL pdgetrs("N", n, nrhs, a, 1, 1, desca, ipivot, a_general, &
    1777         8094 :                    1, 1, descb, info)
    1778              : 
    1779              : #else
    1780              :       lda = SIZE(a, 1)
    1781              :       ldb = SIZE(a_general, 1)
    1782              :       CALL dgetrf(n, n, a, lda, ipivot, info)
    1783              :       CALL dgetrs("N", n, nrhs, a, lda, ipivot, a_general, ldb, info)
    1784              : 
    1785              : #endif
    1786              :       ! info is allowed to be zero
    1787              :       ! this does just signal a zero diagonal element
    1788         8094 :       DEALLOCATE (ipivot)
    1789         8094 :       CALL timestop(handle)
    1790         8094 :    END SUBROUTINE
    1791              : 
    1792              : ! **************************************************************************************************
    1793              : !> \brief Convenience function. Computes the matrix multiplications needed
    1794              : !>        for the multiplication of complex matrices.
    1795              : !>        C = beta * C + alpha * ( A  ** transa ) * ( B ** transb )
    1796              : !> \param transa : 'N' -> normal   'T' -> transpose   'C' -> conjugate transpose
    1797              : !>      alpha,beta :: can be 0.0_dp and 1.0_dp
    1798              : !> \param transb ...
    1799              : !> \param m ...
    1800              : !> \param n ...
    1801              : !> \param k ...
    1802              : !> \param alpha ...
    1803              : !> \param A_re m x k matrix ( ! for transa = 'N'), real part
    1804              : !> \param A_im m x k matrix ( ! for transa = 'N'), imaginary part
    1805              : !> \param B_re k x n matrix ( ! for transa = 'N'), real part
    1806              : !> \param B_im k x n matrix ( ! for transa = 'N'), imaginary part
    1807              : !> \param beta ...
    1808              : !> \param C_re m x n matrix, real part
    1809              : !> \param C_im m x n matrix, imaginary part
    1810              : !> \param a_first_col ...
    1811              : !> \param a_first_row ...
    1812              : !> \param b_first_col : the k x n matrix starts at col b_first_col of matrix_b (avoid usage)
    1813              : !> \param b_first_row ...
    1814              : !> \param c_first_col ...
    1815              : !> \param c_first_row ...
    1816              : !> \author Samuel Andermatt
    1817              : !> \note
    1818              : !>      C should have no overlap with A, B
    1819              : ! **************************************************************************************************
    1820         4748 :    SUBROUTINE cp_complex_fm_gemm(transa, transb, m, n, k, alpha, A_re, A_im, B_re, B_im, beta, &
    1821              :                                  C_re, C_im, a_first_col, a_first_row, b_first_col, b_first_row, c_first_col, &
    1822              :                                  c_first_row)
    1823              :       CHARACTER(LEN=1), INTENT(IN)                       :: transa, transb
    1824              :       INTEGER, INTENT(IN)                                :: m, n, k
    1825              :       REAL(KIND=dp), INTENT(IN)                          :: alpha
    1826              :       TYPE(cp_fm_type), INTENT(IN)                       :: A_re, A_im, B_re, B_im
    1827              :       REAL(KIND=dp), INTENT(IN)                          :: beta
    1828              :       TYPE(cp_fm_type), INTENT(IN)                       :: C_re, C_im
    1829              :       INTEGER, INTENT(IN), OPTIONAL                      :: a_first_col, a_first_row, b_first_col, &
    1830              :                                                             b_first_row, c_first_col, c_first_row
    1831              : 
    1832              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_complex_fm_gemm'
    1833              : 
    1834              :       CHARACTER(LEN=1)                                   :: transa_real, transb_real
    1835              :       INTEGER                                            :: handle
    1836              :       REAL(KIND=dp)                                      :: sign_a, sign_b
    1837              : 
    1838         4748 :       CALL timeset(routineN, handle)
    1839              : 
    1840         4748 :       transa_real = transa
    1841         4748 :       transb_real = transb
    1842         4748 :       sign_a = 1.0_dp
    1843         4748 :       sign_b = 1.0_dp
    1844         4748 :       IF (transa == "C") THEN
    1845         3364 :          transa_real = "T"
    1846         3364 :          sign_a = -1.0_dp
    1847              :       END IF
    1848         4748 :       IF (transb == "C") THEN
    1849          240 :          transb_real = "T"
    1850          240 :          sign_b = -1.0_dp
    1851              :       END IF
    1852              : 
    1853              :       CALL cp_fm_gemm(transa_real, transb_real, m, n, k, alpha, A_re, B_re, beta, C_re, &
    1854              :                       a_first_col=a_first_col, &
    1855              :                       a_first_row=a_first_row, &
    1856              :                       b_first_col=b_first_col, &
    1857              :                       b_first_row=b_first_row, &
    1858              :                       c_first_col=c_first_col, &
    1859         4748 :                       c_first_row=c_first_row)
    1860              :       CALL cp_fm_gemm(transa_real, transb_real, m, n, k, -sign_a*sign_b*alpha, &
    1861              :                       A_im, B_im, 1.0_dp, C_re, &
    1862              :                       a_first_col=a_first_col, &
    1863              :                       a_first_row=a_first_row, &
    1864              :                       b_first_col=b_first_col, &
    1865              :                       b_first_row=b_first_row, &
    1866              :                       c_first_col=c_first_col, &
    1867         4748 :                       c_first_row=c_first_row)
    1868              :       CALL cp_fm_gemm(transa_real, transb_real, m, n, k, sign_b*alpha, A_re, B_im, beta, C_im, &
    1869              :                       a_first_col=a_first_col, &
    1870              :                       a_first_row=a_first_row, &
    1871              :                       b_first_col=b_first_col, &
    1872              :                       b_first_row=b_first_row, &
    1873              :                       c_first_col=c_first_col, &
    1874         4748 :                       c_first_row=c_first_row)
    1875              :       CALL cp_fm_gemm(transa_real, transb_real, m, n, k, sign_a*alpha, A_im, B_re, 1.0_dp, C_im, &
    1876              :                       a_first_col=a_first_col, &
    1877              :                       a_first_row=a_first_row, &
    1878              :                       b_first_col=b_first_col, &
    1879              :                       b_first_row=b_first_row, &
    1880              :                       c_first_col=c_first_col, &
    1881         4748 :                       c_first_row=c_first_row)
    1882              : 
    1883         4748 :       CALL timestop(handle)
    1884              : 
    1885         4748 :    END SUBROUTINE cp_complex_fm_gemm
    1886              : 
    1887              : ! **************************************************************************************************
    1888              : !> \brief inverts a matrix using LU decomposition
    1889              : !>        the input matrix will be overwritten
    1890              : !> \param matrix   : input a general square non-singular matrix, outputs its inverse
    1891              : !> \param info_out : optional, if present outputs the info from (p)zgetri
    1892              : !> \author Lianheng Tong
    1893              : ! **************************************************************************************************
    1894            0 :    SUBROUTINE cp_fm_lu_invert(matrix, info_out)
    1895              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix
    1896              :       INTEGER, INTENT(OUT), OPTIONAL           :: info_out
    1897              : 
    1898              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_lu_invert'
    1899              : 
    1900              :       INTEGER :: nrows_global, handle, info, lwork
    1901            0 :       INTEGER, DIMENSION(:), ALLOCATABLE       :: ipivot
    1902              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: mat
    1903            0 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: work
    1904              : #if defined(__parallel)
    1905              :       INTEGER                                  :: liwork
    1906              :       INTEGER, DIMENSION(9)                    :: desca
    1907            0 :       INTEGER, DIMENSION(:), ALLOCATABLE       :: iwork
    1908              : #else
    1909              :       INTEGER                                  :: lda
    1910              : #endif
    1911              : 
    1912            0 :       CALL timeset(routineN, handle)
    1913              : 
    1914            0 :       mat => matrix%local_data
    1915            0 :       nrows_global = matrix%matrix_struct%nrow_global
    1916            0 :       CPASSERT(nrows_global == matrix%matrix_struct%ncol_global)
    1917            0 :       ALLOCATE (ipivot(nrows_global))
    1918              :       ! do LU decomposition
    1919              : #if defined(__parallel)
    1920            0 :       desca = matrix%matrix_struct%descriptor
    1921              :       CALL pdgetrf(nrows_global, nrows_global, &
    1922            0 :                    mat, 1, 1, desca, ipivot, info)
    1923              : #else
    1924              :       lda = SIZE(mat, 1)
    1925              :       CALL dgetrf(nrows_global, nrows_global, &
    1926              :                   mat, lda, ipivot, info)
    1927              : #endif
    1928            0 :       IF (info /= 0) THEN
    1929            0 :          CALL cp_abort(__LOCATION__, "LU decomposition has failed")
    1930              :       END IF
    1931              :       ! do inversion
    1932              : #if defined(__parallel)
    1933            0 :       ALLOCATE (iwork(1))
    1934              :       CALL pdgetri(nrows_global, mat, 1, 1, desca, &
    1935            0 :                    ipivot, work, -1, iwork, -1, info)
    1936              :       lwork = INT(work(1))
    1937            0 :       DEALLOCATE (work)
    1938              :       ALLOCATE (work(lwork))
    1939              :       liwork = INT(iwork(1))
    1940              :       DEALLOCATE (iwork)
    1941              :       ALLOCATE (iwork(liwork))
    1942              :       CALL pdgetri(nrows_global, mat, 1, 1, desca, &
    1943              :                    ipivot, work, lwork, iwork, liwork, info)
    1944              :       DEALLOCATE (iwork)
    1945              : #else
    1946              :       CALL dgetri(nrows_global, mat, lda, &
    1947              :                   ipivot, work, -1, info)
    1948              :       lwork = INT(work(1))
    1949              :       DEALLOCATE (work)
    1950              :       ALLOCATE (work(lwork))
    1951              :       CALL dgetri(nrows_global, mat, lda, &
    1952              :                   ipivot, work, lwork, info)
    1953              : #endif
    1954              :       DEALLOCATE (work)
    1955              :       DEALLOCATE (ipivot)
    1956              : 
    1957              :       IF (PRESENT(info_out)) THEN
    1958              :          info_out = info
    1959              :       ELSE
    1960              :          IF (info /= 0) &
    1961              :             CALL cp_abort(__LOCATION__, "LU inversion has failed")
    1962              :       END IF
    1963              : 
    1964              :       CALL timestop(handle)
    1965              : 
    1966            0 :    END SUBROUTINE cp_fm_lu_invert
    1967              : 
    1968              : ! **************************************************************************************************
    1969              : !> \brief norm of matrix using (p)dlange
    1970              : !> \param matrix   : input a general matrix
    1971              : !> \param mode     : 'M' max abs element value,
    1972              : !>                   '1' or 'O' one norm, i.e. maximum column sum
    1973              : !>                   'I' infinity norm, i.e. maximum row sum
    1974              : !>                   'F' or 'E' Frobenius norm, i.e. sqrt of sum of all squares of elements
    1975              : !> \return : the norm according to mode
    1976              : !> \author Lianheng Tong
    1977              : ! **************************************************************************************************
    1978         1332 :    FUNCTION cp_fm_norm(matrix, mode) RESULT(res)
    1979              :       TYPE(cp_fm_type), INTENT(IN) :: matrix
    1980              :       CHARACTER, INTENT(IN) :: mode
    1981              :       REAL(KIND=dp) :: res
    1982              : 
    1983              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_norm'
    1984              : 
    1985              :       INTEGER :: nrows, ncols, handle, lwork, nrows_local, ncols_local
    1986              :       REAL(KIND=dp), DIMENSION(:, :), POINTER :: aa
    1987         1332 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: work
    1988              : #if defined(__parallel)
    1989              :       INTEGER, DIMENSION(9) :: desca
    1990              : #else
    1991              :       INTEGER :: lda
    1992              : #endif
    1993              : 
    1994         1332 :       CALL timeset(routineN, handle)
    1995              : 
    1996              :       CALL cp_fm_get_info(matrix=matrix, &
    1997              :                           nrow_global=nrows, &
    1998              :                           ncol_global=ncols, &
    1999              :                           nrow_local=nrows_local, &
    2000         1332 :                           ncol_local=ncols_local)
    2001         1332 :       aa => matrix%local_data
    2002              : 
    2003              : #if defined(__parallel)
    2004        13320 :       desca = matrix%matrix_struct%descriptor
    2005              :       SELECT CASE (mode)
    2006              :       CASE ('M', 'm')
    2007         1332 :          lwork = 1
    2008              :       CASE ('1', 'O', 'o')
    2009         1332 :          lwork = ncols_local
    2010              :       CASE ('I', 'i')
    2011            0 :          lwork = nrows_local
    2012              :       CASE ('F', 'f', 'E', 'e')
    2013            0 :          lwork = 1
    2014              :       CASE DEFAULT
    2015         1332 :          CPABORT("mode input is not valid")
    2016              :       END SELECT
    2017         3996 :       ALLOCATE (work(lwork))
    2018         1332 :       res = pdlange(mode, nrows, ncols, aa, 1, 1, desca, work)
    2019         1332 :       DEALLOCATE (work)
    2020              : #else
    2021              :       SELECT CASE (mode)
    2022              :       CASE ('M', 'm')
    2023              :          lwork = 1
    2024              :       CASE ('1', 'O', 'o')
    2025              :          lwork = 1
    2026              :       CASE ('I', 'i')
    2027              :          lwork = nrows
    2028              :       CASE ('F', 'f', 'E', 'e')
    2029              :          lwork = 1
    2030              :       CASE DEFAULT
    2031              :          CPABORT("mode input is not valid")
    2032              :       END SELECT
    2033              :       ALLOCATE (work(lwork))
    2034              :       lda = SIZE(aa, 1)
    2035              :       res = dlange(mode, nrows, ncols, aa, lda, work)
    2036              :       DEALLOCATE (work)
    2037              : #endif
    2038              : 
    2039         1332 :       CALL timestop(handle)
    2040              : 
    2041         1332 :    END FUNCTION cp_fm_norm
    2042              : 
    2043              : ! **************************************************************************************************
    2044              : !> \brief trace of a matrix using pdlatra
    2045              : !> \param matrix   : input a square matrix
    2046              : !> \return : the trace
    2047              : !> \author Lianheng Tong
    2048              : ! **************************************************************************************************
    2049            0 :    FUNCTION cp_fm_latra(matrix) RESULT(res)
    2050              :       TYPE(cp_fm_type), INTENT(IN) :: matrix
    2051              :       REAL(KIND=dp) :: res
    2052              : 
    2053              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_latra'
    2054              : 
    2055              :       INTEGER :: nrows, ncols, handle
    2056              :       REAL(KIND=dp), DIMENSION(:, :), POINTER :: aa
    2057              : #if defined(__parallel)
    2058              :       INTEGER, DIMENSION(9) :: desca
    2059              : #else
    2060              :       INTEGER :: ii
    2061              : #endif
    2062              : 
    2063            0 :       CALL timeset(routineN, handle)
    2064              : 
    2065            0 :       nrows = matrix%matrix_struct%nrow_global
    2066            0 :       ncols = matrix%matrix_struct%ncol_global
    2067            0 :       CPASSERT(nrows == ncols)
    2068            0 :       aa => matrix%local_data
    2069              : 
    2070              : #if defined(__parallel)
    2071            0 :       desca = matrix%matrix_struct%descriptor
    2072            0 :       res = pdlatra(nrows, aa, 1, 1, desca)
    2073              : #else
    2074              :       res = 0.0_dp
    2075              :       DO ii = 1, nrows
    2076              :          res = res + aa(ii, ii)
    2077              :       END DO
    2078              : #endif
    2079              : 
    2080            0 :       CALL timestop(handle)
    2081              : 
    2082            0 :    END FUNCTION cp_fm_latra
    2083              : 
    2084              : ! **************************************************************************************************
    2085              : !> \brief compute a QR factorization with column pivoting of a M-by-N distributed matrix
    2086              : !>        sub( A ) = A(IA:IA+M-1,JA:JA+N-1)
    2087              : !> \param matrix   : input M-by-N distributed matrix sub( A ) which is to be factored
    2088              : !> \param tau      : scalar factors TAU of the elementary reflectors. TAU is tied to the distributed matrix A
    2089              : !> \param nrow ...
    2090              : !> \param ncol ...
    2091              : !> \param first_row ...
    2092              : !> \param first_col ...
    2093              : !> \author MI
    2094              : ! **************************************************************************************************
    2095           38 :    SUBROUTINE cp_fm_pdgeqpf(matrix, tau, nrow, ncol, first_row, first_col)
    2096              : 
    2097              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
    2098              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: tau
    2099              :       INTEGER, INTENT(IN)                                :: nrow, ncol, first_row, first_col
    2100              : 
    2101              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_pdgeqpf'
    2102              : 
    2103              :       INTEGER                                            :: handle
    2104              :       INTEGER                                            :: info, lwork
    2105           38 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: ipiv
    2106              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: a
    2107              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: work
    2108              : #if defined(__parallel)
    2109              :       INTEGER, DIMENSION(9) :: descc
    2110              : #else
    2111              :       INTEGER :: lda
    2112              : #endif
    2113              : 
    2114           38 :       CALL timeset(routineN, handle)
    2115              : 
    2116           38 :       a => matrix%local_data
    2117           38 :       lwork = -1
    2118          114 :       ALLOCATE (work(2*nrow))
    2119          114 :       ALLOCATE (ipiv(ncol))
    2120           38 :       info = 0
    2121              : 
    2122              : #if defined(__parallel)
    2123          380 :       descc(:) = matrix%matrix_struct%descriptor(:)
    2124              :       ! Call SCALAPACK routine to get optimal work dimension
    2125           38 :       CALL pdgeqpf(nrow, ncol, a, first_row, first_col, descc, ipiv, tau, work, lwork, info)
    2126           38 :       lwork = INT(work(1))
    2127           38 :       DEALLOCATE (work)
    2128          114 :       ALLOCATE (work(lwork))
    2129          254 :       tau = 0.0_dp
    2130           38 :       ipiv = 0
    2131              : 
    2132              :       ! Call SCALAPACK routine to get QR decomposition of CTs
    2133           38 :       CALL pdgeqpf(nrow, ncol, a, first_row, first_col, descc, ipiv, tau, work, lwork, info)
    2134              : #else
    2135              :       CPASSERT(first_row == 1 .AND. first_col == 1)
    2136              :       lda = SIZE(a, 1)
    2137              :       CALL dgeqp3(nrow, ncol, a, lda, ipiv, tau, work, lwork, info)
    2138              :       lwork = INT(work(1))
    2139              :       DEALLOCATE (work)
    2140              :       ALLOCATE (work(lwork))
    2141              :       tau = 0.0_dp
    2142              :       ipiv = 0
    2143              :       CALL dgeqp3(nrow, ncol, a, lda, ipiv, tau, work, lwork, info)
    2144              : #endif
    2145           38 :       CPASSERT(info == 0)
    2146              : 
    2147           38 :       DEALLOCATE (work)
    2148           38 :       DEALLOCATE (ipiv)
    2149              : 
    2150           38 :       CALL timestop(handle)
    2151              : 
    2152           38 :    END SUBROUTINE cp_fm_pdgeqpf
    2153              : 
    2154              : ! **************************************************************************************************
    2155              : !> \brief generates an M-by-N real distributed matrix Q denoting A(IA:IA+M-1,JA:JA+N-1)
    2156              : !>         with orthonormal columns, which is defined as the first N columns of a product of K
    2157              : !>         elementary reflectors of order M
    2158              : !> \param matrix : On entry, the j-th column must contain the vector which defines the elementary reflector
    2159              : !>                  as returned from PDGEQRF
    2160              : !>                 On exit it contains  the M-by-N distributed matrix Q
    2161              : !> \param tau :   contains the scalar factors TAU of elementary reflectors  as returned by PDGEQRF
    2162              : !> \param nrow ...
    2163              : !> \param first_row ...
    2164              : !> \param first_col ...
    2165              : !> \author MI
    2166              : ! **************************************************************************************************
    2167           38 :    SUBROUTINE cp_fm_pdorgqr(matrix, tau, nrow, first_row, first_col)
    2168              : 
    2169              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
    2170              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: tau
    2171              :       INTEGER, INTENT(IN)                                :: nrow, first_row, first_col
    2172              : 
    2173              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_pdorgqr'
    2174              : 
    2175              :       INTEGER                                            :: handle
    2176              :       INTEGER                                            :: info, lwork
    2177              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: a
    2178              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: work
    2179              : #if defined(__parallel)
    2180              :       INTEGER, DIMENSION(9) :: descc
    2181              : #else
    2182              :       INTEGER :: lda
    2183              : #endif
    2184              : 
    2185           38 :       CALL timeset(routineN, handle)
    2186              : 
    2187           38 :       a => matrix%local_data
    2188           38 :       lwork = -1
    2189          114 :       ALLOCATE (work(2*nrow))
    2190           38 :       info = 0
    2191              : 
    2192              : #if defined(__parallel)
    2193          380 :       descc(:) = matrix%matrix_struct%descriptor(:)
    2194              : 
    2195           38 :       CALL pdorgqr(nrow, nrow, nrow, a, first_row, first_col, descc, tau, work, lwork, info)
    2196           38 :       CPASSERT(info == 0)
    2197           38 :       lwork = INT(work(1))
    2198           38 :       DEALLOCATE (work)
    2199          114 :       ALLOCATE (work(lwork))
    2200              : 
    2201              :       ! Call SCALAPACK routine to get Q
    2202           38 :       CALL pdorgqr(nrow, nrow, nrow, a, first_row, first_col, descc, tau, work, lwork, info)
    2203              : #else
    2204              :       CPASSERT(first_row == 1 .AND. first_col == 1)
    2205              :       lda = SIZE(a, 1)
    2206              :       CALL dorgqr(nrow, nrow, nrow, a, lda, tau, work, lwork, info)
    2207              :       lwork = INT(work(1))
    2208              :       DEALLOCATE (work)
    2209              :       ALLOCATE (work(lwork))
    2210              :       CALL dorgqr(nrow, nrow, nrow, a, lda, tau, work, lwork, info)
    2211              : #endif
    2212           38 :       CPASSERT(INFO == 0)
    2213              : 
    2214           38 :       DEALLOCATE (work)
    2215           38 :       CALL timestop(handle)
    2216              : 
    2217           38 :    END SUBROUTINE cp_fm_pdorgqr
    2218              : 
    2219              : ! **************************************************************************************************
    2220              : !> \brief Applies a planar rotation defined by cs and sn to the i'th and j'th rows.
    2221              : !> \param matrix ...
    2222              : !> \param irow ...
    2223              : !> \param jrow ...
    2224              : !> \param cs cosine of the rotation angle
    2225              : !> \param sn sinus of the rotation angle
    2226              : !> \author Ole Schuett
    2227              : ! **************************************************************************************************
    2228        31928 :    SUBROUTINE cp_fm_rot_rows(matrix, irow, jrow, cs, sn)
    2229              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix
    2230              :       INTEGER, INTENT(IN)                      :: irow, jrow
    2231              :       REAL(dp), INTENT(IN)                     :: cs, sn
    2232              : 
    2233              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_rot_rows'
    2234              :       INTEGER                                  :: handle, ncol
    2235              : 
    2236              : #if defined(__parallel)
    2237              :       INTEGER                                  :: info, lwork
    2238              :       INTEGER, DIMENSION(9)                    :: desc
    2239        31928 :       REAL(dp), DIMENSION(:), ALLOCATABLE      :: work
    2240              : #endif
    2241        31928 :       CALL timeset(routineN, handle)
    2242        31928 :       CALL cp_fm_get_info(matrix, ncol_global=ncol)
    2243              : #if defined(__parallel)
    2244        31928 :       IF (1 /= matrix%matrix_struct%context%n_pid) THEN
    2245        31928 :          lwork = 2*ncol + 1
    2246        95784 :          ALLOCATE (work(lwork))
    2247       319280 :          desc(:) = matrix%matrix_struct%descriptor(:)
    2248              :          CALL pdrot(ncol, &
    2249              :                     matrix%local_data(1, 1), irow, 1, desc, ncol, &
    2250              :                     matrix%local_data(1, 1), jrow, 1, desc, ncol, &
    2251        31928 :                     cs, sn, work, lwork, info)
    2252        31928 :          CPASSERT(info == 0)
    2253        31928 :          DEALLOCATE (work)
    2254              :       ELSE
    2255              : #endif
    2256            0 :          CALL drot(ncol, matrix%local_data(irow, 1), ncol, matrix%local_data(jrow, 1), ncol, cs, sn)
    2257              : #if defined(__parallel)
    2258              :       END IF
    2259              : #endif
    2260        31928 :       CALL timestop(handle)
    2261        31928 :    END SUBROUTINE cp_fm_rot_rows
    2262              : 
    2263              : ! **************************************************************************************************
    2264              : !> \brief Applies a planar rotation defined by cs and sn to the i'th and j'th columnns.
    2265              : !> \param matrix ...
    2266              : !> \param icol ...
    2267              : !> \param jcol ...
    2268              : !> \param cs cosine of the rotation angle
    2269              : !> \param sn sinus of the rotation angle
    2270              : !> \author Ole Schuett
    2271              : ! **************************************************************************************************
    2272        36928 :    SUBROUTINE cp_fm_rot_cols(matrix, icol, jcol, cs, sn)
    2273              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix
    2274              :       INTEGER, INTENT(IN)                      :: icol, jcol
    2275              :       REAL(dp), INTENT(IN)                     :: cs, sn
    2276              : 
    2277              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_rot_cols'
    2278              :       INTEGER                                  :: handle, nrow
    2279              : 
    2280              : #if defined(__parallel)
    2281              :       INTEGER                                  :: info, lwork
    2282              :       INTEGER, DIMENSION(9)                    :: desc
    2283        36928 :       REAL(dp), DIMENSION(:), ALLOCATABLE      :: work
    2284              : #endif
    2285        36928 :       CALL timeset(routineN, handle)
    2286        36928 :       CALL cp_fm_get_info(matrix, nrow_global=nrow)
    2287              : #if defined(__parallel)
    2288        36928 :       IF (1 /= matrix%matrix_struct%context%n_pid) THEN
    2289        36928 :          lwork = 2*nrow + 1
    2290       110784 :          ALLOCATE (work(lwork))
    2291       369280 :          desc(:) = matrix%matrix_struct%descriptor(:)
    2292              :          CALL pdrot(nrow, &
    2293              :                     matrix%local_data(1, 1), 1, icol, desc, 1, &
    2294              :                     matrix%local_data(1, 1), 1, jcol, desc, 1, &
    2295        36928 :                     cs, sn, work, lwork, info)
    2296        36928 :          CPASSERT(info == 0)
    2297        36928 :          DEALLOCATE (work)
    2298              :       ELSE
    2299              : #endif
    2300            0 :          CALL drot(nrow, matrix%local_data(1, icol), 1, matrix%local_data(1, jcol), 1, cs, sn)
    2301              : #if defined(__parallel)
    2302              :       END IF
    2303              : #endif
    2304        36928 :       CALL timestop(handle)
    2305        36928 :    END SUBROUTINE cp_fm_rot_cols
    2306              : 
    2307              : ! **************************************************************************************************
    2308              : !> \brief Orthonormalizes selected rows and columns of a full matrix, matrix_a
    2309              : !> \param matrix_a ...
    2310              : !> \param B ...
    2311              : !> \param nrows number of rows of matrix_a, optional, defaults to size(matrix_a,1)
    2312              : !> \param ncols number of columns of matrix_a, optional, defaults to size(matrix_a, 2)
    2313              : !> \param start_row starting index of rows, optional, defaults to 1
    2314              : !> \param start_col starting index of columns, optional, defaults to 1
    2315              : !> \param do_norm ...
    2316              : !> \param do_print ...
    2317              : ! **************************************************************************************************
    2318            0 :    SUBROUTINE cp_fm_Gram_Schmidt_orthonorm(matrix_a, B, nrows, ncols, start_row, start_col, &
    2319              :                                            do_norm, do_print)
    2320              : 
    2321              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix_a
    2322              :       REAL(kind=dp), DIMENSION(:, :), INTENT(OUT)        :: B
    2323              :       INTEGER, INTENT(IN), OPTIONAL                      :: nrows, ncols, start_row, start_col
    2324              :       LOGICAL, INTENT(IN), OPTIONAL                      :: do_norm, do_print
    2325              : 
    2326              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_Gram_Schmidt_orthonorm'
    2327              : 
    2328              :       INTEGER :: end_col_global, end_col_local, end_row_global, end_row_local, handle, i, j, &
    2329              :                  j_col, ncol_global, ncol_local, nrow_global, nrow_local, start_col_global, &
    2330              :                  start_col_local, start_row_global, start_row_local, this_col, unit_nr
    2331            0 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
    2332              :       LOGICAL                                            :: my_do_norm, my_do_print
    2333              :       REAL(KIND=dp)                                      :: norm
    2334            0 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: a
    2335              : 
    2336            0 :       CALL timeset(routineN, handle)
    2337              : 
    2338            0 :       my_do_norm = .TRUE.
    2339            0 :       IF (PRESENT(do_norm)) my_do_norm = do_norm
    2340              : 
    2341            0 :       my_do_print = .FALSE.
    2342            0 :       IF (PRESENT(do_print) .AND. (my_do_norm)) my_do_print = do_print
    2343              : 
    2344            0 :       unit_nr = -1
    2345            0 :       IF (my_do_print) THEN
    2346            0 :          unit_nr = cp_logger_get_default_unit_nr()
    2347            0 :          IF (unit_nr < 1) my_do_print = .FALSE.
    2348              :       END IF
    2349              : 
    2350            0 :       IF (SIZE(B) /= 0) THEN
    2351            0 :          IF (PRESENT(nrows)) THEN
    2352            0 :             nrow_global = nrows
    2353              :          ELSE
    2354            0 :             nrow_global = SIZE(B, 1)
    2355              :          END IF
    2356              : 
    2357            0 :          IF (PRESENT(ncols)) THEN
    2358            0 :             ncol_global = ncols
    2359              :          ELSE
    2360            0 :             ncol_global = SIZE(B, 2)
    2361              :          END IF
    2362              : 
    2363            0 :          IF (PRESENT(start_row)) THEN
    2364            0 :             start_row_global = start_row
    2365              :          ELSE
    2366              :             start_row_global = 1
    2367              :          END IF
    2368              : 
    2369            0 :          IF (PRESENT(start_col)) THEN
    2370            0 :             start_col_global = start_col
    2371              :          ELSE
    2372              :             start_col_global = 1
    2373              :          END IF
    2374              : 
    2375            0 :          end_row_global = start_row_global + nrow_global - 1
    2376            0 :          end_col_global = start_col_global + ncol_global - 1
    2377              : 
    2378              :          CALL cp_fm_get_info(matrix=matrix_a, &
    2379              :                              nrow_global=nrow_global, ncol_global=ncol_global, &
    2380              :                              nrow_local=nrow_local, ncol_local=ncol_local, &
    2381            0 :                              row_indices=row_indices, col_indices=col_indices)
    2382            0 :          IF (end_row_global > nrow_global) THEN
    2383              :             end_row_global = nrow_global
    2384              :          END IF
    2385            0 :          IF (end_col_global > ncol_global) THEN
    2386              :             end_col_global = ncol_global
    2387              :          END IF
    2388              : 
    2389              :          ! find out row/column indices of locally stored matrix elements that
    2390              :          ! needs to be copied.
    2391              :          ! Arrays row_indices and col_indices are assumed to be sorted in
    2392              :          ! ascending order
    2393            0 :          DO start_row_local = 1, nrow_local
    2394            0 :             IF (row_indices(start_row_local) >= start_row_global) EXIT
    2395              :          END DO
    2396              : 
    2397            0 :          DO end_row_local = start_row_local, nrow_local
    2398            0 :             IF (row_indices(end_row_local) > end_row_global) EXIT
    2399              :          END DO
    2400            0 :          end_row_local = end_row_local - 1
    2401              : 
    2402            0 :          DO start_col_local = 1, ncol_local
    2403            0 :             IF (col_indices(start_col_local) >= start_col_global) EXIT
    2404              :          END DO
    2405              : 
    2406            0 :          DO end_col_local = start_col_local, ncol_local
    2407            0 :             IF (col_indices(end_col_local) > end_col_global) EXIT
    2408              :          END DO
    2409            0 :          end_col_local = end_col_local - 1
    2410              : 
    2411            0 :          a => matrix_a%local_data
    2412              : 
    2413            0 :          this_col = col_indices(start_col_local) - start_col_global + 1
    2414              : 
    2415            0 :          B(:, this_col) = a(:, start_col_local)
    2416              : 
    2417            0 :          IF (my_do_norm) THEN
    2418            0 :             norm = SQRT(accurate_dot_product(B(:, this_col), B(:, this_col)))
    2419            0 :             B(:, this_col) = B(:, this_col)/norm
    2420            0 :             IF (my_do_print) WRITE (unit_nr, '(I3,F8.3)') this_col, norm
    2421              :          END IF
    2422              : 
    2423            0 :          DO i = start_col_local + 1, end_col_local
    2424            0 :             this_col = col_indices(i) - start_col_global + 1
    2425            0 :             B(:, this_col) = a(:, i)
    2426            0 :             DO j = start_col_local, i - 1
    2427            0 :                j_col = col_indices(j) - start_col_global + 1
    2428              :                B(:, this_col) = B(:, this_col) - &
    2429              :                                 accurate_dot_product(B(:, j_col), B(:, this_col))* &
    2430            0 :                                 B(:, j_col)/accurate_dot_product(B(:, j_col), B(:, j_col))
    2431              :             END DO
    2432              : 
    2433            0 :             IF (my_do_norm) THEN
    2434            0 :                norm = SQRT(accurate_dot_product(B(:, this_col), B(:, this_col)))
    2435            0 :                B(:, this_col) = B(:, this_col)/norm
    2436            0 :                IF (my_do_print) WRITE (unit_nr, '(I3,F8.3)') this_col, norm
    2437              :             END IF
    2438              : 
    2439              :          END DO
    2440            0 :          CALL matrix_a%matrix_struct%para_env%sum(B)
    2441              :       END IF
    2442              : 
    2443            0 :       CALL timestop(handle)
    2444              : 
    2445            0 :    END SUBROUTINE cp_fm_Gram_Schmidt_orthonorm
    2446              : 
    2447              : ! **************************************************************************************************
    2448              : !> \brief Cholesky decomposition
    2449              : !> \param fm_matrix ...
    2450              : !> \param n ...
    2451              : !> \param uplo triangular format; defaults to 'U'
    2452              : ! **************************************************************************************************
    2453        11255 :    SUBROUTINE cp_fm_potrf(fm_matrix, n, uplo)
    2454              :       TYPE(cp_fm_type)                         :: fm_matrix
    2455              :       INTEGER, INTENT(IN)                      :: n
    2456              :       CHARACTER, INTENT(IN), OPTIONAL          :: uplo
    2457              : 
    2458              :       CHARACTER                                :: myuplo
    2459              :       INTEGER                                  :: info
    2460        11255 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
    2461              : #if defined(__parallel)
    2462              :       INTEGER, DIMENSION(9)                    :: desca
    2463              : #endif
    2464              : 
    2465        11255 :       myuplo = 'U'
    2466            0 :       IF (PRESENT(uplo)) myuplo = uplo
    2467              : 
    2468        11255 :       a => fm_matrix%local_data
    2469              : #if defined(__parallel)
    2470       112550 :       desca(:) = fm_matrix%matrix_struct%descriptor(:)
    2471        11255 :       CALL pdpotrf(myuplo, n, a(1, 1), 1, 1, desca, info)
    2472              : #else
    2473              :       CALL dpotrf(myuplo, n, a(1, 1), SIZE(a, 1), info)
    2474              : #endif
    2475        11255 :       IF (info /= 0) &
    2476            0 :          CPABORT("Cholesky decomposition failed. Matrix ill-conditioned?")
    2477              : 
    2478        11255 :    END SUBROUTINE cp_fm_potrf
    2479              : 
    2480              : ! **************************************************************************************************
    2481              : !> \brief Invert trianguar matrix
    2482              : !> \param fm_matrix the matrix to invert (triangular matrix according to uplo)
    2483              : !> \param n size of the matrix to invert
    2484              : !> \param uplo triangular format; defaults to 'U'
    2485              : ! **************************************************************************************************
    2486        10432 :    SUBROUTINE cp_fm_potri(fm_matrix, n, uplo)
    2487              :       TYPE(cp_fm_type)                         :: fm_matrix
    2488              :       INTEGER, INTENT(IN)                      :: n
    2489              :       CHARACTER, INTENT(IN), OPTIONAL          :: uplo
    2490              : 
    2491              :       CHARACTER                                :: myuplo
    2492        10432 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
    2493              :       INTEGER                                  :: info
    2494              : #if defined(__parallel)
    2495              :       INTEGER, DIMENSION(9)                    :: desca
    2496              : #endif
    2497              : 
    2498        10432 :       myuplo = 'U'
    2499            0 :       IF (PRESENT(uplo)) myuplo = uplo
    2500              : 
    2501        10432 :       a => fm_matrix%local_data
    2502              : #if defined(__parallel)
    2503       104320 :       desca(:) = fm_matrix%matrix_struct%descriptor(:)
    2504        10432 :       CALL pdpotri(myuplo, n, a(1, 1), 1, 1, desca, info)
    2505              : #else
    2506              :       CALL dpotri(myuplo, n, a(1, 1), SIZE(a, 1), info)
    2507              : #endif
    2508        10432 :       CPASSERT(info == 0)
    2509        10432 :    END SUBROUTINE cp_fm_potri
    2510              : 
    2511              : ! **************************************************************************************************
    2512              : !> \brief Calculates
    2513              : !>        yv = alpha*amat*xv + beta*yv
    2514              : !>        where amat: fm matrix
    2515              : !>              xv  : vector replicated
    2516              : !>              yv  : vector replicated
    2517              : !>        Defaults: alpha = 1, beta = 0
    2518              : ! **************************************************************************************************
    2519        22982 :    SUBROUTINE cp_fm_matvec(amat, xv, yv, alpha, beta)
    2520              :       TYPE(cp_fm_type), INTENT(IN)                   :: amat
    2521              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)        :: xv
    2522              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)     :: yv
    2523              :       REAL(KIND=dp), OPTIONAL, INTENT(IN)            :: alpha, beta
    2524              : 
    2525              :       INTEGER                                        :: na, nc, nx, ny
    2526              :       REAL(KIND=dp)                                  :: aval, bval
    2527              : #if defined(__parallel)
    2528              :       INTEGER                                        :: nrl, ncl, ic, ir
    2529        22982 :       INTEGER, DIMENSION(:), POINTER                 :: rind, cind
    2530        22982 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE       :: xvl, yvl, yvm
    2531              : #endif
    2532              : 
    2533        22982 :       aval = 1.0_dp
    2534        22480 :       IF (PRESENT(alpha)) aval = alpha
    2535        22982 :       bval = 0.0_dp
    2536        22982 :       IF (PRESENT(beta)) bval = beta
    2537              : 
    2538        22982 :       CALL cp_fm_get_info(amat, nrow_global=na, ncol_global=nc)
    2539        22982 :       nx = SIZE(xv)
    2540        22982 :       ny = SIZE(yv)
    2541        22982 :       IF ((nx /= ny) .OR. (nc /= nx)) THEN
    2542            0 :          CPABORT("cp_fm_matvec: incompatible dimensions")
    2543              :       END IF
    2544              : #if defined(__parallel)
    2545              :       CALL cp_fm_get_info(amat, nrow_local=nrl, ncol_local=ncl, &
    2546        22982 :                           row_indices=rind, col_indices=cind)
    2547       160874 :       ALLOCATE (xvl(ncl), yvl(nrl), yvm(ny))
    2548       375484 :       DO ic = 1, ncl
    2549       375484 :          xvl(ic) = xv(cind(ic))
    2550              :       END DO
    2551        22982 :       yvl = 0.0_dp
    2552        22982 :       IF (nrl > 0 .AND. ncl > 0) THEN
    2553     15462576 :          yvl(1:nrl) = MATMUL(amat%local_data(1:nrl, 1:ncl), xvl)
    2554              :       END IF
    2555        22982 :       yvm = 0.0_dp
    2556       199233 :       DO ir = 1, nrl
    2557       199233 :          yvm(rind(ir)) = yvl(ir)
    2558              :       END DO
    2559        22982 :       CALL amat%matrix_struct%para_env%sum(yvm)
    2560        22982 :       IF (bval == 0.0_dp) THEN
    2561       280306 :          yv = aval*yvm
    2562              :       ELSE
    2563        95178 :          yv = bval*yv + aval*yvm
    2564              :       END IF
    2565              : #else
    2566              :       IF (bval == 0.0_dp) THEN
    2567              :          yv = aval*MATMUL(amat%local_data, xv)
    2568              :       ELSE
    2569              :          yv = bval*yv + aval*MATMUL(amat%local_data, xv)
    2570              :       END IF
    2571              : #endif
    2572              : 
    2573        91928 :    END SUBROUTINE cp_fm_matvec
    2574              : 
    2575              : ! **************************************************************************************************
    2576              : !> \brief Adds a scalar to the diagonal of a distributed real full matrix.
    2577              : !> \param matrix input/output matrix
    2578              : !> \param alpha scalar added to each diagonal element
    2579              : !> \param n_active optional number of physical rows/columns
    2580              : ! **************************************************************************************************
    2581        53946 :    SUBROUTINE cp_fm_add_on_diag(matrix, alpha, n_active)
    2582              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
    2583              :       REAL(KIND=dp), INTENT(IN)                          :: alpha
    2584              :       INTEGER, INTENT(IN), OPTIONAL                      :: n_active
    2585              : 
    2586              :       INTEGER                                            :: i_global, n_diag, ncol_global, nrow_global
    2587              : #if defined(__parallel)
    2588              :       INTEGER                                            :: icol_local, ipcol, iprow, irow, irow_local, &
    2589              :                                                             mypcol, myprow, npcol, nprow, nrow_local
    2590              :       INTEGER, DIMENSION(9)                              :: desca
    2591        26973 :       INTEGER, DIMENSION(:), POINTER                     :: row_indices
    2592              :       TYPE(cp_blacs_env_type), POINTER                   :: context
    2593              : #endif
    2594              : 
    2595        26973 :       CALL cp_fm_get_info(matrix, nrow_global=nrow_global, ncol_global=ncol_global)
    2596              : 
    2597        26973 :       n_diag = MIN(nrow_global, ncol_global)
    2598        26973 :       IF (PRESENT(n_active)) n_diag = n_active
    2599        26973 :       CPASSERT(n_diag >= 0 .AND. n_diag <= MIN(nrow_global, ncol_global))
    2600              : 
    2601              : #if defined(__parallel)
    2602        26973 :       CALL cp_fm_get_info(matrix, nrow_local=nrow_local, row_indices=row_indices)
    2603        26973 :       context => matrix%matrix_struct%context
    2604        26973 :       myprow = context%mepos(1)
    2605        26973 :       mypcol = context%mepos(2)
    2606        26973 :       nprow = context%num_pe(1)
    2607        26973 :       npcol = context%num_pe(2)
    2608       269730 :       desca(:) = matrix%matrix_struct%descriptor(:)
    2609              : 
    2610      1169808 :       DO irow = 1, nrow_local
    2611      1142835 :          i_global = row_indices(irow)
    2612      1142835 :          IF (i_global > n_diag) EXIT
    2613              :          CALL infog2l(i_global, i_global, desca, nprow, npcol, myprow, mypcol, &
    2614      1142835 :                       irow_local, icol_local, iprow, ipcol)
    2615      1169808 :          IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
    2616      1142227 :             matrix%local_data(irow_local, icol_local) = matrix%local_data(irow_local, icol_local) + alpha
    2617              :          END IF
    2618              :       END DO
    2619              : #else
    2620              :       DO i_global = 1, n_diag
    2621              :          matrix%local_data(i_global, i_global) = matrix%local_data(i_global, i_global) + alpha
    2622              :       END DO
    2623              : #endif
    2624              : 
    2625        26973 :    END SUBROUTINE cp_fm_add_on_diag
    2626              : 
    2627              : END MODULE cp_fm_basic_linalg
        

Generated by: LCOV version 2.0-1