LCOV - code coverage report
Current view: top level - src/fm - cp_cfm_basic_linalg.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 88.9 % 512 455
Test Date: 2026-11-04 00:58:17 Functions: 95.7 % 23 22

            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 complex full matrices.
      10              : !> \note
      11              : !>      - not all functionality implemented
      12              : !> \par History
      13              : !>      Nearly literal copy of Fawzi's routines
      14              : !> \author Joost VandeVondele
      15              : ! **************************************************************************************************
      16              : MODULE cp_cfm_basic_linalg
      17              :    USE OMP_LIB,                         ONLY: omp_get_max_threads
      18              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_type
      19              :    USE cp_cfm_types,                    ONLY: cp_cfm_create,&
      20              :                                               cp_cfm_get_info,&
      21              :                                               cp_cfm_release,&
      22              :                                               cp_cfm_to_cfm,&
      23              :                                               cp_cfm_type
      24              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_equivalent
      25              :    USE cp_fm_types,                     ONLY: cp_fm_type
      26              :    USE cp_log_handling,                 ONLY: cp_to_string
      27              :    USE kahan_sum,                       ONLY: accurate_dot_product
      28              :    USE kinds,                           ONLY: dp
      29              :    USE mathconstants,                   ONLY: z_one,&
      30              :                                               z_zero
      31              : #include "../base/base_uses.f90"
      32              : 
      33              :    IMPLICIT NONE
      34              :    PRIVATE
      35              : 
      36              :    LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .TRUE.
      37              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_cfm_basic_linalg'
      38              : 
      39              :    PUBLIC :: cp_cfm_add_on_diag, &
      40              :              cp_cfm_column_scale, &
      41              :              cp_cfm_gemm, &
      42              :              cp_cfm_get_diag, &
      43              :              cp_cfm_lu_decompose, &
      44              :              cp_cfm_lu_invert, &
      45              :              cp_cfm_norm, &
      46              :              cp_cfm_scale, &
      47              :              cp_cfm_scale_and_add, &
      48              :              cp_cfm_scale_and_add_fm, &
      49              :              cp_cfm_schur_product, &
      50              :              cp_cfm_schur_product_cc, &
      51              :              cp_cfm_solve, &
      52              :              cp_cfm_trace, &
      53              :              cp_cfm_transpose, &
      54              :              cp_cfm_triangular_invert, &
      55              :              cp_cfm_triangular_multiply, &
      56              :              cp_cfm_rot_rows, &
      57              :              cp_cfm_rot_cols, &
      58              :              cp_cfm_det, & ! determinant of a complex matrix with correct sign
      59              :              cp_cfm_uplo_to_full, &
      60              :              cp_cfm_vectorsnorm
      61              : 
      62              :    REAL(kind=dp), EXTERNAL :: zlange, pzlange
      63              : 
      64              :    INTERFACE cp_cfm_scale
      65              :       MODULE PROCEDURE cp_cfm_dscale, cp_cfm_zscale
      66              :    END INTERFACE cp_cfm_scale
      67              : 
      68              : ! **************************************************************************************************
      69              : 
      70              : CONTAINS
      71              : 
      72              : ! **************************************************************************************************
      73              : !> \brief Computes the determinant (with a correct sign even in parallel environment!) of a complex square matrix
      74              : !> \param matrix_a ...
      75              : !> \param det_a ...
      76              : !> \author A. Sinyavskiy (andrey.sinyavskiy@chem.uzh.ch)
      77              : ! **************************************************************************************************
      78         1536 :    SUBROUTINE cp_cfm_det(matrix_a, det_a)
      79              : 
      80              :       TYPE(cp_cfm_type), INTENT(IN)            :: matrix_a
      81              :       COMPLEX(KIND=dp), INTENT(OUT)            :: det_a
      82              :       COMPLEX(KIND=dp)                         :: determinant
      83              :       TYPE(cp_cfm_type)                        :: matrix_lu
      84         1536 :       COMPLEX(KIND=dp), DIMENSION(:, :), POINTER  :: a
      85              :       INTEGER                                  :: n, i, info, P
      86         1536 :       INTEGER, ALLOCATABLE, DIMENSION(:)       :: ipivot
      87         1536 :       COMPLEX(KIND=dp), DIMENSION(:), POINTER  :: diag
      88              : 
      89              : #if defined(__parallel)
      90              :       INTEGER                                  :: myprow, nprow, npcol, nrow_local, irow_local, &
      91              :                                                   mypcol, ncol_local, icol_local, j
      92              :       INTEGER, DIMENSION(9)                    :: desca
      93              : #endif
      94              : 
      95              :       CALL cp_cfm_create(matrix=matrix_lu, &
      96              :                          matrix_struct=matrix_a%matrix_struct, &
      97         1536 :                          name="A_lu"//TRIM(ADJUSTL(cp_to_string(1)))//"MATRIX")
      98         1536 :       CALL cp_cfm_to_cfm(matrix_a, matrix_lu)
      99              : 
     100         1536 :       a => matrix_lu%local_data
     101         1536 :       n = matrix_lu%matrix_struct%nrow_global
     102         4608 :       ALLOCATE (ipivot(n))
     103         1536 :       ipivot(:) = 0
     104         1536 :       P = 0
     105         4608 :       ALLOCATE (diag(n))
     106         8622 :       diag(:) = 0.0_dp
     107              : #if defined(__parallel)
     108              :       ! Use LU decomposition
     109        15360 :       desca(:) = matrix_lu%matrix_struct%descriptor(:)
     110         1536 :       CALL pzgetrf(n, n, a(1, 1), 1, 1, desca, ipivot, info)
     111         1536 :       myprow = matrix_lu%matrix_struct%context%mepos(1)
     112         1536 :       mypcol = matrix_lu%matrix_struct%context%mepos(2)
     113         1536 :       nprow = matrix_lu%matrix_struct%context%num_pe(1)
     114         1536 :       npcol = matrix_lu%matrix_struct%context%num_pe(2)
     115         1536 :       nrow_local = matrix_lu%matrix_struct%nrow_locals(myprow)
     116         1536 :       ncol_local = matrix_lu%matrix_struct%ncol_locals(mypcol)
     117              : 
     118         5199 :       DO irow_local = 1, nrow_local
     119         3663 :          i = matrix_lu%matrix_struct%row_indices(irow_local)
     120        41874 :          DO icol_local = 1, ncol_local
     121        36675 :             j = matrix_lu%matrix_struct%col_indices(icol_local)
     122        40338 :             IF (i == j) diag(i) = matrix_lu%local_data(irow_local, icol_local)
     123              :          END DO
     124              :       END DO
     125        15708 :       CALL matrix_lu%matrix_struct%para_env%sum(diag)
     126         8622 :       determinant = PRODUCT(diag)
     127         5199 :       DO irow_local = 1, nrow_local
     128         3663 :          i = matrix_lu%matrix_struct%row_indices(irow_local)
     129         5199 :          IF (ipivot(irow_local) /= i) P = P + 1
     130              :       END DO
     131         1536 :       CALL matrix_lu%matrix_struct%para_env%sum(P)
     132              :       ! very important fix
     133         1536 :       P = P/npcol
     134              : #else
     135              :       CALL zgetrf(n, n, a(1, 1), n, ipivot, info)
     136              :       DO i = 1, n
     137              :          diag(i) = matrix_lu%local_data(i, i)
     138              :       END DO
     139              :       determinant = PRODUCT(diag)
     140              :       DO i = 1, n
     141              :          IF (ipivot(i) /= i) P = P + 1
     142              :       END DO
     143              : #endif
     144         1536 :       DEALLOCATE (ipivot)
     145         1536 :       DEALLOCATE (diag)
     146         1536 :       CALL cp_cfm_release(matrix_lu)
     147         1536 :       det_a = determinant*(-2*MOD(P, 2) + 1.0_dp)
     148         1536 :    END SUBROUTINE cp_cfm_det
     149              : 
     150              : ! **************************************************************************************************
     151              : !> \brief Computes the element-wise (Schur) product of two matrices: C = A \circ B .
     152              : !> \param matrix_a the first input matrix
     153              : !> \param matrix_b the second input matrix
     154              : !> \param matrix_c matrix to store the result
     155              : ! **************************************************************************************************
     156          154 :    SUBROUTINE cp_cfm_schur_product(matrix_a, matrix_b, matrix_c)
     157              : 
     158              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_a, matrix_b, matrix_c
     159              : 
     160              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_schur_product'
     161              : 
     162          154 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a, b, c
     163              :       INTEGER                                            :: handle, icol_local, irow_local, mypcol, &
     164              :                                                             myprow, ncol_local, nrow_local
     165              : 
     166          154 :       CALL timeset(routineN, handle)
     167              : 
     168          154 :       myprow = matrix_a%matrix_struct%context%mepos(1)
     169          154 :       mypcol = matrix_a%matrix_struct%context%mepos(2)
     170              : 
     171          154 :       a => matrix_a%local_data
     172          154 :       b => matrix_b%local_data
     173          154 :       c => matrix_c%local_data
     174              : 
     175          154 :       nrow_local = matrix_a%matrix_struct%nrow_locals(myprow)
     176          154 :       ncol_local = matrix_a%matrix_struct%ncol_locals(mypcol)
     177              : 
     178          462 :       DO icol_local = 1, ncol_local
     179          770 :          DO irow_local = 1, nrow_local
     180          616 :             c(irow_local, icol_local) = a(irow_local, icol_local)*b(irow_local, icol_local)
     181              :          END DO
     182              :       END DO
     183              : 
     184          154 :       CALL timestop(handle)
     185              : 
     186          154 :    END SUBROUTINE cp_cfm_schur_product
     187              : 
     188              : ! **************************************************************************************************
     189              : !> \brief Computes the element-wise (Schur) product of two matrices: C = A \circ conjg(B) .
     190              : !> \param matrix_a the first input matrix
     191              : !> \param matrix_b the second input matrix
     192              : !> \param matrix_c matrix to store the result
     193              : ! **************************************************************************************************
     194          760 :    SUBROUTINE cp_cfm_schur_product_cc(matrix_a, matrix_b, matrix_c)
     195              : 
     196              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_a, matrix_b, matrix_c
     197              : 
     198              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_schur_product_cc'
     199              : 
     200          760 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a, b, c
     201              :       INTEGER                                            :: handle, icol_local, irow_local, mypcol, &
     202              :                                                             myprow, ncol_local, nrow_local
     203              : 
     204          760 :       CALL timeset(routineN, handle)
     205              : 
     206          760 :       myprow = matrix_a%matrix_struct%context%mepos(1)
     207          760 :       mypcol = matrix_a%matrix_struct%context%mepos(2)
     208              : 
     209          760 :       a => matrix_a%local_data
     210          760 :       b => matrix_b%local_data
     211          760 :       c => matrix_c%local_data
     212              : 
     213          760 :       nrow_local = matrix_a%matrix_struct%nrow_locals(myprow)
     214          760 :       ncol_local = matrix_a%matrix_struct%ncol_locals(mypcol)
     215              : 
     216         5233 :       DO icol_local = 1, ncol_local
     217       190760 :          DO irow_local = 1, nrow_local
     218       190000 :             c(irow_local, icol_local) = a(irow_local, icol_local)*CONJG(b(irow_local, icol_local))
     219              :          END DO
     220              :       END DO
     221              : 
     222          760 :       CALL timestop(handle)
     223              : 
     224          760 :    END SUBROUTINE cp_cfm_schur_product_cc
     225              : 
     226              : ! **************************************************************************************************
     227              : !> \brief Scale and add two BLACS matrices (a = alpha*a + beta*b).
     228              : !> \param alpha ...
     229              : !> \param matrix_a ...
     230              : !> \param beta ...
     231              : !> \param matrix_b ...
     232              : !> \date    11.06.2001
     233              : !> \author  Matthias Krack
     234              : !> \version 1.0
     235              : !> \note
     236              : !>    Use explicit loops to avoid temporary arrays, as a compiler reasonably assumes that arrays
     237              : !>    matrix_a%local_data and matrix_b%local_data may overlap (they are referenced by pointers).
     238              : !>    In general case (alpha*a + beta*b) explicit loops appears to be up to two times more efficient
     239              : !>    than equivalent LAPACK calls (zscale, zaxpy). This is because using LAPACK calls implies
     240              : !>    two passes through each array, so data need to be retrieved twice if arrays are large
     241              : !>    enough to not fit into the processor's cache.
     242              : ! **************************************************************************************************
     243       664574 :    SUBROUTINE cp_cfm_scale_and_add(alpha, matrix_a, beta, matrix_b)
     244              :       COMPLEX(kind=dp), INTENT(in)                       :: alpha
     245              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_a
     246              :       COMPLEX(kind=dp), INTENT(in), OPTIONAL             :: beta
     247              :       TYPE(cp_cfm_type), INTENT(IN), OPTIONAL            :: matrix_b
     248              : 
     249              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_scale_and_add'
     250              : 
     251              :       COMPLEX(kind=dp)                                   :: my_beta
     252       664574 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a, b
     253              :       INTEGER                                            :: handle, icol_local, irow_local, mypcol, &
     254              :                                                             myprow, ncol_local, nrow_local
     255              : 
     256       664574 :       CALL timeset(routineN, handle)
     257              : 
     258       664574 :       my_beta = z_zero
     259       664574 :       IF (PRESENT(beta)) my_beta = beta
     260       664574 :       NULLIFY (a, b)
     261              : 
     262              :       ! to do: use dscal,dcopy,daxp
     263       664574 :       myprow = matrix_a%matrix_struct%context%mepos(1)
     264       664574 :       mypcol = matrix_a%matrix_struct%context%mepos(2)
     265              : 
     266       664574 :       nrow_local = matrix_a%matrix_struct%nrow_locals(myprow)
     267       664574 :       ncol_local = matrix_a%matrix_struct%ncol_locals(mypcol)
     268              : 
     269       664574 :       a => matrix_a%local_data
     270              : 
     271       664574 :       IF (my_beta == z_zero) THEN
     272              : 
     273        65984 :          IF (alpha == z_zero) THEN
     274            0 :             a(:, :) = z_zero
     275        65984 :          ELSE IF (alpha == z_one) THEN
     276        65984 :             CALL timestop(handle)
     277        65984 :             RETURN
     278              :          ELSE
     279            0 :             a(:, :) = alpha*a(:, :)
     280              :          END IF
     281              : 
     282              :       ELSE
     283       598590 :          CPASSERT(PRESENT(matrix_b))
     284       598590 :          IF (matrix_a%matrix_struct%context /= matrix_b%matrix_struct%context) &
     285            0 :             CPABORT("matrixes must be in the same blacs context")
     286              : 
     287       598590 :          IF (cp_fm_struct_equivalent(matrix_a%matrix_struct, &
     288              :                                      matrix_b%matrix_struct)) THEN
     289              : 
     290       598590 :             b => matrix_b%local_data
     291              : 
     292       598590 :             IF (alpha == z_zero) THEN
     293           20 :                IF (my_beta == z_one) THEN
     294              :                   !a(:, :) = b(:, :)
     295          724 :                   DO icol_local = 1, ncol_local
     296        49172 :                      DO irow_local = 1, nrow_local
     297        49152 :                         a(irow_local, icol_local) = b(irow_local, icol_local)
     298              :                      END DO
     299              :                   END DO
     300              :                ELSE
     301              :                   !a(:, :) = my_beta*b(:, :)
     302            0 :                   DO icol_local = 1, ncol_local
     303            0 :                      DO irow_local = 1, nrow_local
     304            0 :                         a(irow_local, icol_local) = my_beta*b(irow_local, icol_local)
     305              :                      END DO
     306              :                   END DO
     307              :                END IF
     308       598570 :             ELSE IF (alpha == z_one) THEN
     309       578021 :                IF (my_beta == z_one) THEN
     310              :                   !a(:, :) = a(:, :)+b(:, :)
     311      4221307 :                   DO icol_local = 1, ncol_local
     312     60727771 :                      DO irow_local = 1, nrow_local
     313     60309708 :                         a(irow_local, icol_local) = a(irow_local, icol_local) + b(irow_local, icol_local)
     314              :                      END DO
     315              :                   END DO
     316              :                ELSE
     317              :                   !a(:, :) = a(:, :)+my_beta*b(:, :)
     318      2484807 :                   DO icol_local = 1, ncol_local
     319     72666645 :                      DO irow_local = 1, nrow_local
     320     72506687 :                         a(irow_local, icol_local) = a(irow_local, icol_local) + my_beta*b(irow_local, icol_local)
     321              :                      END DO
     322              :                   END DO
     323              :                END IF
     324              :             ELSE
     325              :                !a(:, :) = alpha*a(:, :)+my_beta*b(:, :)
     326       305196 :                DO icol_local = 1, ncol_local
     327      8082612 :                   DO irow_local = 1, nrow_local
     328      8062063 :                      a(irow_local, icol_local) = alpha*a(irow_local, icol_local) + my_beta*b(irow_local, icol_local)
     329              :                   END DO
     330              :                END DO
     331              :             END IF
     332              :          ELSE
     333              :             CALL cp_abort(__LOCATION__, &
     334              :                           "cp_cfm_scale_and_add is not yet implemented for cases "// &
     335            0 :                           "where input two matrix structures are not equivalent")
     336              :          END IF
     337              :       END IF
     338       598590 :       CALL timestop(handle)
     339       664574 :    END SUBROUTINE cp_cfm_scale_and_add
     340              : 
     341              : ! **************************************************************************************************
     342              : !> \brief Scale and add two BLACS matrices (a = alpha*a + beta*b).
     343              : !>        where b is a real matrix (adapted from cp_cfm_scale_and_add).
     344              : !> \param alpha ...
     345              : !> \param matrix_a ...
     346              : !> \param beta ...
     347              : !> \param matrix_b ...
     348              : !> \date    01.08.2014
     349              : !> \author  JGH
     350              : !> \version 1.0
     351              : ! **************************************************************************************************
     352        96016 :    SUBROUTINE cp_cfm_scale_and_add_fm(alpha, matrix_a, beta, matrix_b)
     353              :       COMPLEX(kind=dp), INTENT(in)                       :: alpha
     354              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_a
     355              :       COMPLEX(kind=dp), INTENT(in)                       :: beta
     356              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix_b
     357              : 
     358              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_scale_and_add_fm'
     359              : 
     360        96016 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a
     361              :       INTEGER                                            :: handle, icol_local, irow_local, mypcol, &
     362              :                                                             myprow, ncol_local, nrow_local
     363        96016 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: b
     364              : 
     365        96016 :       CALL timeset(routineN, handle)
     366              : 
     367        96016 :       NULLIFY (a, b)
     368              : 
     369        96016 :       myprow = matrix_a%matrix_struct%context%mepos(1)
     370        96016 :       mypcol = matrix_a%matrix_struct%context%mepos(2)
     371              : 
     372        96016 :       nrow_local = matrix_a%matrix_struct%nrow_locals(myprow)
     373        96016 :       ncol_local = matrix_a%matrix_struct%ncol_locals(mypcol)
     374              : 
     375        96016 :       a => matrix_a%local_data
     376              : 
     377        96016 :       IF (beta == z_zero) THEN
     378              : 
     379            0 :          IF (alpha == z_zero) THEN
     380            0 :             a(:, :) = z_zero
     381            0 :          ELSE IF (alpha == z_one) THEN
     382            0 :             CALL timestop(handle)
     383            0 :             RETURN
     384              :          ELSE
     385            0 :             a(:, :) = alpha*a(:, :)
     386              :          END IF
     387              : 
     388              :       ELSE
     389        96016 :          IF (matrix_a%matrix_struct%context /= matrix_b%matrix_struct%context) &
     390            0 :             CPABORT("matrices must be in the same blacs context")
     391              : 
     392        96016 :          IF (cp_fm_struct_equivalent(matrix_a%matrix_struct, &
     393              :                                      matrix_b%matrix_struct)) THEN
     394              : 
     395        96016 :             b => matrix_b%local_data
     396              : 
     397        96016 :             IF (alpha == z_zero) THEN
     398         7426 :                IF (beta == z_one) THEN
     399              :                   !a(:, :) = b(:, :)
     400        62302 :                   DO icol_local = 1, ncol_local
     401       960076 :                      DO irow_local = 1, nrow_local
     402       952686 :                         a(irow_local, icol_local) = b(irow_local, icol_local)
     403              :                      END DO
     404              :                   END DO
     405              :                ELSE
     406              :                   !a(:, :) = beta*b(:, :)
     407          684 :                   DO icol_local = 1, ncol_local
     408         6516 :                      DO irow_local = 1, nrow_local
     409         6480 :                         a(irow_local, icol_local) = beta*b(irow_local, icol_local)
     410              :                      END DO
     411              :                   END DO
     412              :                END IF
     413        88590 :             ELSE IF (alpha == z_one) THEN
     414        23844 :                IF (beta == z_one) THEN
     415              :                   !a(:, :) = a(:, :)+b(:, :)
     416       178296 :                   DO icol_local = 1, ncol_local
     417      2273536 :                      DO irow_local = 1, nrow_local
     418      2264592 :                         a(irow_local, icol_local) = a(irow_local, icol_local) + b(irow_local, icol_local)
     419              :                      END DO
     420              :                   END DO
     421              :                ELSE
     422              :                   !a(:, :) = a(:, :)+beta*b(:, :)
     423       402432 :                   DO icol_local = 1, ncol_local
     424     11836780 :                      DO irow_local = 1, nrow_local
     425     11821880 :                         a(irow_local, icol_local) = a(irow_local, icol_local) + beta*b(irow_local, icol_local)
     426              :                      END DO
     427              :                   END DO
     428              :                END IF
     429              :             ELSE
     430              :                !a(:, :) = alpha*a(:, :)+beta*b(:, :)
     431       562290 :                DO icol_local = 1, ncol_local
     432      5184146 :                   DO irow_local = 1, nrow_local
     433      5119400 :                      a(irow_local, icol_local) = alpha*a(irow_local, icol_local) + beta*b(irow_local, icol_local)
     434              :                   END DO
     435              :                END DO
     436              :             END IF
     437              :          ELSE
     438              :             CALL cp_abort(__LOCATION__, &
     439              :                           "cp_cfm_scale_and_add_fm is not yet implemented for cases "// &
     440            0 :                           "where two input matrix structures are not equivalent")
     441              :          END IF
     442              :       END IF
     443        96016 :       CALL timestop(handle)
     444        96016 :    END SUBROUTINE cp_cfm_scale_and_add_fm
     445              : 
     446              : ! **************************************************************************************************
     447              : !> \brief Computes LU decomposition of a given matrix.
     448              : !> \param matrix_a     full matrix
     449              : !> \param determinant  determinant
     450              : !> \date    11.06.2001
     451              : !> \author  Matthias Krack
     452              : !> \version 1.0
     453              : !> \note
     454              : !>    The actual purpose right now is to efficiently compute the determinant of a given matrix.
     455              : !>    The original content of the matrix is destroyed.
     456              : ! **************************************************************************************************
     457            0 :    SUBROUTINE cp_cfm_lu_decompose(matrix_a, determinant)
     458              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_a
     459              :       COMPLEX(kind=dp), INTENT(out)                      :: determinant
     460              : 
     461              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_lu_decompose'
     462              : 
     463            0 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a
     464              :       INTEGER                                            :: counter, handle, info, irow, nrow_global
     465            0 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: ipivot
     466              : 
     467              : #if defined(__parallel)
     468              :       INTEGER                                            :: icol, ncol_local, nrow_local
     469              :       INTEGER, DIMENSION(9)                              :: desca
     470            0 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     471              : #else
     472              :       INTEGER                                            :: lda
     473              : #endif
     474              : 
     475            0 :       CALL timeset(routineN, handle)
     476              : 
     477            0 :       nrow_global = matrix_a%matrix_struct%nrow_global
     478            0 :       a => matrix_a%local_data
     479              : 
     480            0 :       ALLOCATE (ipivot(nrow_global))
     481              : #if defined(__parallel)
     482              :       CALL cp_cfm_get_info(matrix_a, nrow_local=nrow_local, ncol_local=ncol_local, &
     483            0 :                            row_indices=row_indices, col_indices=col_indices)
     484              : 
     485            0 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
     486            0 :       CALL pzgetrf(nrow_global, nrow_global, a(1, 1), 1, 1, desca, ipivot, info)
     487              : 
     488            0 :       counter = 0
     489            0 :       DO irow = 1, nrow_local
     490            0 :          IF (ipivot(irow) /= row_indices(irow)) counter = counter + 1
     491              :       END DO
     492              : 
     493            0 :       IF (MOD(counter, 2) == 0) THEN
     494            0 :          determinant = z_one
     495              :       ELSE
     496            0 :          determinant = -z_one
     497              :       END IF
     498              : 
     499              :       ! compute product of diagonal elements
     500              :       irow = 1
     501              :       icol = 1
     502            0 :       DO WHILE (irow <= nrow_local .AND. icol <= ncol_local)
     503            0 :          IF (row_indices(irow) < col_indices(icol)) THEN
     504            0 :             irow = irow + 1
     505            0 :          ELSE IF (row_indices(irow) > col_indices(icol)) THEN
     506            0 :             icol = icol + 1
     507              :          ELSE ! diagonal element
     508            0 :             determinant = determinant*a(irow, icol)
     509            0 :             irow = irow + 1
     510            0 :             icol = icol + 1
     511              :          END IF
     512              :       END DO
     513            0 :       CALL matrix_a%matrix_struct%para_env%prod(determinant)
     514              : #else
     515              :       lda = SIZE(a, 1)
     516              :       CALL zgetrf(nrow_global, nrow_global, a(1, 1), lda, ipivot, info)
     517              :       counter = 0
     518              :       determinant = z_one
     519              :       DO irow = 1, nrow_global
     520              :          IF (ipivot(irow) /= irow) counter = counter + 1
     521              :          determinant = determinant*a(irow, irow)
     522              :       END DO
     523              :       IF (MOD(counter, 2) == 1) determinant = -1.0_dp*determinant
     524              : #endif
     525              : 
     526              :       ! info is allowed to be zero
     527              :       ! this does just signal a zero diagonal element
     528            0 :       DEALLOCATE (ipivot)
     529              : 
     530            0 :       CALL timestop(handle)
     531            0 :    END SUBROUTINE cp_cfm_lu_decompose
     532              : 
     533              : ! **************************************************************************************************
     534              : !> \brief Performs one of the matrix-matrix operations:
     535              : !>        matrix_c = alpha * op1( matrix_a ) * op2( matrix_b ) + beta*matrix_c.
     536              : !> \param transa       form of op1( matrix_a ):
     537              : !>                     op1( matrix_a ) = matrix_a,   when transa == 'N' ,
     538              : !>                     op1( matrix_a ) = matrix_a^T, when transa == 'T' ,
     539              : !>                     op1( matrix_a ) = matrix_a^H, when transa == 'C' ,
     540              : !> \param transb       form of op2( matrix_b )
     541              : !> \param m            number of rows of the matrix op1( matrix_a )
     542              : !> \param n            number of columns of the matrix op2( matrix_b )
     543              : !> \param k            number of columns of the matrix op1( matrix_a ) as well as
     544              : !>                     number of rows of the matrix op2( matrix_b )
     545              : !> \param alpha        scale factor
     546              : !> \param matrix_a     matrix A
     547              : !> \param matrix_b     matrix B
     548              : !> \param beta         scale factor
     549              : !> \param matrix_c     matrix C
     550              : !> \param a_first_col  (optional) the first column of the matrix_a to multiply
     551              : !> \param a_first_row  (optional) the first row of the matrix_a to multiply
     552              : !> \param b_first_col  (optional) the first column of the matrix_b to multiply
     553              : !> \param b_first_row  (optional) the first row of the matrix_b to multiply
     554              : !> \param c_first_col  (optional) the first column of the matrix_c
     555              : !> \param c_first_row  (optional) the first row of the matrix_c
     556              : !> \date    07.06.2001
     557              : !> \author  Matthias Krack
     558              : !> \version 1.0
     559              : ! **************************************************************************************************
     560      1260922 :    SUBROUTINE cp_cfm_gemm(transa, transb, m, n, k, alpha, matrix_a, matrix_b, beta, &
     561              :                           matrix_c, a_first_col, a_first_row, b_first_col, b_first_row, c_first_col, &
     562              :                           c_first_row)
     563              :       CHARACTER(len=1), INTENT(IN)                       :: transa, transb
     564              :       INTEGER, INTENT(IN)                                :: m, n, k
     565              :       COMPLEX(kind=dp), INTENT(IN)                       :: alpha
     566              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_a, matrix_b
     567              :       COMPLEX(kind=dp), INTENT(IN)                       :: beta
     568              :       TYPE(cp_cfm_type), INTENT(INOUT)                   :: matrix_c
     569              :       INTEGER, INTENT(IN), OPTIONAL                      :: a_first_col, a_first_row, b_first_col, &
     570              :                                                             b_first_row, c_first_col, c_first_row
     571              : 
     572              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_gemm'
     573              : 
     574      1260922 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a, b, c
     575              :       INTEGER                                            :: handle, i_a, i_b, i_c, j_a, j_b, j_c
     576              : #if defined(__parallel)
     577              :       INTEGER, DIMENSION(9)                              :: desca, descb, descc
     578              : #else
     579              :       INTEGER                                            :: lda, ldb, ldc
     580              : #endif
     581              : 
     582      1260922 :       CALL timeset(routineN, handle)
     583      1260922 :       a => matrix_a%local_data
     584      1260922 :       b => matrix_b%local_data
     585      1260922 :       c => matrix_c%local_data
     586              : 
     587      1260922 :       i_a = 1
     588      1260922 :       IF (PRESENT(a_first_row)) i_a = a_first_row
     589              : 
     590      1260922 :       j_a = 1
     591      1260922 :       IF (PRESENT(a_first_col)) j_a = a_first_col
     592              : 
     593      1260922 :       i_b = 1
     594      1260922 :       IF (PRESENT(b_first_row)) i_b = b_first_row
     595              : 
     596      1260922 :       j_b = 1
     597      1260922 :       IF (PRESENT(b_first_col)) j_b = b_first_col
     598              : 
     599      1260922 :       i_c = 1
     600      1260922 :       IF (PRESENT(c_first_row)) i_c = c_first_row
     601              : 
     602      1260922 :       j_c = 1
     603      1260922 :       IF (PRESENT(c_first_col)) j_c = c_first_col
     604              : 
     605              : #if defined(__parallel)
     606     12609220 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
     607     12609220 :       descb(:) = matrix_b%matrix_struct%descriptor(:)
     608     12609220 :       descc(:) = matrix_c%matrix_struct%descriptor(:)
     609              : 
     610              :       CALL pzgemm(transa, transb, m, n, k, alpha, a(1, 1), i_a, j_a, desca, &
     611      1260922 :                   b(1, 1), i_b, j_b, descb, beta, c(1, 1), i_c, j_c, descc)
     612              : #else
     613              :       lda = SIZE(a, 1)
     614              :       ldb = SIZE(b, 1)
     615              :       ldc = SIZE(c, 1)
     616              : 
     617              :       ! consider zgemm3m
     618              :       CALL zgemm(transa, transb, m, n, k, alpha, a(i_a, j_a), &
     619              :                  lda, b(i_b, j_b), ldb, beta, c(i_c, j_c), ldc)
     620              : #endif
     621      1260922 :       CALL timestop(handle)
     622      1260922 :    END SUBROUTINE cp_cfm_gemm
     623              : 
     624              : ! **************************************************************************************************
     625              : !> \brief Scales columns of the full matrix by corresponding factors.
     626              : !> \param matrix_a target matrix; columns outside the selected window are unchanged
     627              : !> \param scaling factors for consecutive columns starting at first_col;
     628              : !>        the window is truncated at the last global column of the matrix
     629              : !> \param first_col first global column to scale (defaults to 1)
     630              : !> \param source optional source with the same shape and distribution as matrix_a;
     631              : !>        if omitted, matrix_a is scaled in place
     632              : !> \author Joost VandeVondele
     633              : ! **************************************************************************************************
     634       199318 :    SUBROUTINE cp_cfm_column_scale(matrix_a, scaling, first_col, source)
     635              :       TYPE(cp_cfm_type), INTENT(INOUT)                   :: matrix_a
     636              :       COMPLEX(kind=dp), DIMENSION(:), INTENT(IN)         :: scaling
     637              :       INTEGER, INTENT(IN), OPTIONAL                      :: first_col
     638              :       TYPE(cp_cfm_type), INTENT(IN), OPTIONAL            :: source
     639              : 
     640              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_column_scale'
     641              : 
     642       199318 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a, b
     643              :       INTEGER                                            :: first, handle, i_factor, icol_local, &
     644              :                                                             ncol_local, nrow_local
     645       199318 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices
     646              :       LOGICAL                                            :: compatible_layout
     647              : 
     648       199318 :       CALL timeset(routineN, handle)
     649              : 
     650       199318 :       a => matrix_a%local_data
     651       199318 :       b => a
     652       199318 :       IF (PRESENT(source)) THEN
     653         6912 :          compatible_layout = cp_fm_struct_equivalent(matrix_a%matrix_struct, source%matrix_struct)
     654         6912 :          CPASSERT(compatible_layout)
     655         6912 :          b => source%local_data
     656              :       END IF
     657       199318 :       first = 1
     658       199318 :       IF (PRESENT(first_col)) first = first_col
     659         6912 :       CPASSERT(first >= 1)
     660       199318 :       CALL cp_cfm_get_info(matrix_a, nrow_local=nrow_local, ncol_local=ncol_local, col_indices=col_indices)
     661              : 
     662      2708815 :       DO icol_local = 1, ncol_local
     663      2509497 :          i_factor = col_indices(icol_local) - first + 1
     664      2509497 :          IF (i_factor < 1 .OR. i_factor > SIZE(scaling)) CYCLE
     665    192746744 :          a(1:nrow_local, icol_local) = scaling(i_factor)*b(1:nrow_local, icol_local)
     666              :       END DO
     667              : 
     668       199318 :       CALL timestop(handle)
     669       199318 :    END SUBROUTINE cp_cfm_column_scale
     670              : 
     671              : ! **************************************************************************************************
     672              : !> \brief Scales a complex matrix by a real number.
     673              : !>      matrix_a = alpha * matrix_b
     674              : !> \param alpha    scale factor
     675              : !> \param matrix_a complex matrix to scale
     676              : ! **************************************************************************************************
     677        22213 :    SUBROUTINE cp_cfm_dscale(alpha, matrix_a)
     678              :       REAL(kind=dp), INTENT(IN)                          :: alpha
     679              :       TYPE(cp_cfm_type), INTENT(INOUT)                   :: matrix_a
     680              : 
     681              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_cfm_dscale'
     682              : 
     683        22213 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a
     684              :       INTEGER                                            :: handle
     685              : 
     686        22213 :       CALL timeset(routineN, handle)
     687              : 
     688        22213 :       NULLIFY (a)
     689              : 
     690        22213 :       a => matrix_a%local_data
     691              : 
     692        66639 :       CALL zdscal(SIZE(a), alpha, a(1, 1), 1)
     693              : 
     694        22213 :       CALL timestop(handle)
     695        22213 :    END SUBROUTINE cp_cfm_dscale
     696              : 
     697              : ! **************************************************************************************************
     698              : !> \brief Scales a complex matrix by a complex number.
     699              : !>      matrix_a = alpha * matrix_b
     700              : !> \param alpha    scale factor
     701              : !> \param matrix_a complex matrix to scale
     702              : !> \note
     703              : !>      use cp_fm_set_all to zero (avoids problems with nan)
     704              : ! **************************************************************************************************
     705        39645 :    SUBROUTINE cp_cfm_zscale(alpha, matrix_a)
     706              :       COMPLEX(kind=dp), INTENT(IN)                       :: alpha
     707              :       TYPE(cp_cfm_type), INTENT(INOUT)                   :: matrix_a
     708              : 
     709              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_cfm_zscale'
     710              : 
     711        39645 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a
     712              :       INTEGER                                            :: handle
     713              : 
     714        39645 :       CALL timeset(routineN, handle)
     715              : 
     716        39645 :       NULLIFY (a)
     717              : 
     718        39645 :       a => matrix_a%local_data
     719              : 
     720      6460567 :       a(:, :) = alpha*a(:, :)
     721              : 
     722        39645 :       CALL timestop(handle)
     723        39645 :    END SUBROUTINE cp_cfm_zscale
     724              : 
     725              : ! **************************************************************************************************
     726              : !> \brief Solve the system of linear equations A*b=A_general using LU decomposition.
     727              : !>        Pay attention that both matrices are overwritten on exit and that
     728              : !>        the result is stored into the matrix 'general_a'.
     729              : !> \param matrix_a     matrix A (overwritten on exit)
     730              : !> \param general_a    (input) matrix A_general, (output) matrix B
     731              : !> \param determinant  (optional) determinant
     732              : !> \author Florian Schiffmann
     733              : ! **************************************************************************************************
     734         7144 :    SUBROUTINE cp_cfm_solve(matrix_a, general_a, determinant)
     735              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_a, general_a
     736              :       COMPLEX(kind=dp), OPTIONAL                         :: determinant
     737              : 
     738              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_solve'
     739              : 
     740         7144 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a, a_general
     741              :       INTEGER                                            :: counter, handle, info, irow, nrow_global
     742         7144 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: ipivot
     743              : 
     744              : #if defined(__parallel)
     745              :       INTEGER                                            :: icol, ncol_local, nrow_local
     746              :       INTEGER, DIMENSION(9)                              :: desca, descb
     747         7144 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     748              : #else
     749              :       INTEGER                                            :: lda, ldb
     750              : #endif
     751              : 
     752         7144 :       CALL timeset(routineN, handle)
     753              : 
     754         7144 :       a => matrix_a%local_data
     755         7144 :       a_general => general_a%local_data
     756         7144 :       nrow_global = matrix_a%matrix_struct%nrow_global
     757        21432 :       ALLOCATE (ipivot(nrow_global))
     758              : 
     759              : #if defined(__parallel)
     760        71440 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
     761        71440 :       descb(:) = general_a%matrix_struct%descriptor(:)
     762         7144 :       CALL pzgetrf(nrow_global, nrow_global, a(1, 1), 1, 1, desca, ipivot, info)
     763         7144 :       IF (PRESENT(determinant)) THEN
     764              :          CALL cp_cfm_get_info(matrix_a, nrow_local=nrow_local, ncol_local=ncol_local, &
     765         6416 :                               row_indices=row_indices, col_indices=col_indices)
     766              : 
     767         6416 :          counter = 0
     768        19296 :          DO irow = 1, nrow_local
     769        19296 :             IF (ipivot(irow) /= row_indices(irow)) counter = counter + 1
     770              :          END DO
     771              : 
     772         6416 :          IF (MOD(counter, 2) == 0) THEN
     773         6406 :             determinant = z_one
     774              :          ELSE
     775           10 :             determinant = -z_one
     776              :          END IF
     777              : 
     778              :          ! compute product of diagonal elements
     779              :          irow = 1
     780              :          icol = 1
     781        28932 :          DO WHILE (irow <= nrow_local .AND. icol <= ncol_local)
     782        28932 :             IF (row_indices(irow) < col_indices(icol)) THEN
     783            0 :                irow = irow + 1
     784        22516 :             ELSE IF (row_indices(irow) > col_indices(icol)) THEN
     785         9636 :                icol = icol + 1
     786              :             ELSE ! diagonal element
     787        12880 :                determinant = determinant*a(irow, icol)
     788        12880 :                irow = irow + 1
     789        12880 :                icol = icol + 1
     790              :             END IF
     791              :          END DO
     792         6416 :          CALL matrix_a%matrix_struct%para_env%prod(determinant)
     793              :       END IF
     794              : 
     795              :       CALL pzgetrs("N", nrow_global, nrow_global, a(1, 1), 1, 1, desca, &
     796         7144 :                    ipivot, a_general(1, 1), 1, 1, descb, info)
     797              : #else
     798              :       lda = SIZE(a, 1)
     799              :       ldb = SIZE(a_general, 1)
     800              :       CALL zgetrf(nrow_global, nrow_global, a(1, 1), lda, ipivot, info)
     801              :       IF (PRESENT(determinant)) THEN
     802              :          counter = 0
     803              :          determinant = z_one
     804              :          DO irow = 1, nrow_global
     805              :             IF (ipivot(irow) /= irow) counter = counter + 1
     806              :             determinant = determinant*a(irow, irow)
     807              :          END DO
     808              :          IF (MOD(counter, 2) == 1) determinant = -1.0_dp*determinant
     809              :       END IF
     810              :       CALL zgetrs("N", nrow_global, nrow_global, a(1, 1), lda, ipivot, a_general(1, 1), ldb, info)
     811              : #endif
     812              : 
     813              :       ! info is allowed to be zero
     814              :       ! this does just signal a zero diagonal element
     815         7144 :       DEALLOCATE (ipivot)
     816         7144 :       CALL timestop(handle)
     817              : 
     818         7144 :    END SUBROUTINE cp_cfm_solve
     819              : 
     820              : ! **************************************************************************************************
     821              : !> \brief Inverts a matrix using LU decomposition. The input matrix will be overwritten.
     822              : !> \param matrix     input a general square non-singular matrix, outputs its inverse
     823              : !> \param info_out   optional, if present outputs the info from (p)zgetri
     824              : !> \author Lianheng Tong
     825              : ! **************************************************************************************************
     826       120050 :    SUBROUTINE cp_cfm_lu_invert(matrix, info_out)
     827              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
     828              :       INTEGER, INTENT(out), OPTIONAL                     :: info_out
     829              : 
     830              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_lu_invert'
     831              : 
     832       120050 :       COMPLEX(kind=dp), ALLOCATABLE, DIMENSION(:)        :: work
     833              :       COMPLEX(kind=dp), DIMENSION(1)                     :: work1
     834       120050 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: mat
     835              :       INTEGER                                            :: handle, info, lwork, nrows_global
     836       120050 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: ipivot
     837              : 
     838              : #if defined(__parallel)
     839              :       INTEGER                                            :: liwork
     840       120050 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: iwork
     841              :       INTEGER, DIMENSION(1)                              :: iwork1
     842              :       INTEGER, DIMENSION(9)                              :: desca
     843              : #else
     844              :       INTEGER                                            :: lda
     845              : #endif
     846              : 
     847       120050 :       CALL timeset(routineN, handle)
     848              : 
     849       120050 :       mat => matrix%local_data
     850       120050 :       nrows_global = matrix%matrix_struct%nrow_global
     851       120050 :       CPASSERT(nrows_global == matrix%matrix_struct%ncol_global)
     852       360150 :       ALLOCATE (ipivot(nrows_global))
     853              : 
     854              :       ! do LU decomposition
     855              : #if defined(__parallel)
     856      1200500 :       desca = matrix%matrix_struct%descriptor
     857              :       CALL pzgetrf(nrows_global, nrows_global, &
     858       120050 :                    mat(1, 1), 1, 1, desca, ipivot, info)
     859              : #else
     860              :       lda = SIZE(mat, 1)
     861              :       CALL zgetrf(nrows_global, nrows_global, &
     862              :                   mat(1, 1), lda, ipivot, info)
     863              : #endif
     864       120050 :       IF (info /= 0) THEN
     865            0 :          CALL cp_abort(__LOCATION__, "LU decomposition has failed")
     866              :       END IF
     867              : 
     868              :       ! do inversion
     869              : #if defined(__parallel)
     870              :       CALL pzgetri(nrows_global, mat(1, 1), 1, 1, desca, &
     871       120050 :                    ipivot, work1, -1, iwork1, -1, info)
     872       120050 :       lwork = INT(work1(1))
     873       120050 :       liwork = INT(iwork1(1))
     874       360150 :       ALLOCATE (work(lwork))
     875       360150 :       ALLOCATE (iwork(liwork))
     876              :       CALL pzgetri(nrows_global, mat(1, 1), 1, 1, desca, &
     877       120050 :                    ipivot, work, lwork, iwork, liwork, info)
     878       120050 :       DEALLOCATE (iwork)
     879              : #else
     880              :       CALL zgetri(nrows_global, mat(1, 1), lda, ipivot, work1, -1, info)
     881              :       lwork = INT(work1(1))
     882              :       ALLOCATE (work(lwork))
     883              :       CALL zgetri(nrows_global, mat(1, 1), lda, ipivot, work, lwork, info)
     884              : #endif
     885       120050 :       DEALLOCATE (work)
     886       120050 :       DEALLOCATE (ipivot)
     887              : 
     888       120050 :       IF (PRESENT(info_out)) THEN
     889            0 :          info_out = info
     890              :       ELSE
     891       120050 :          IF (info /= 0) &
     892            0 :             CALL cp_abort(__LOCATION__, "LU inversion has failed")
     893              :       END IF
     894              : 
     895       120050 :       CALL timestop(handle)
     896              : 
     897       120050 :    END SUBROUTINE cp_cfm_lu_invert
     898              : 
     899              : ! **************************************************************************************************
     900              : !> \brief By default, returns the trace of matrix_a^T matrix_b, i.e
     901              : !>      sum_{i,j}(matrix_a(i,j)*matrix_b(i,j)) .
     902              : !> \param matrix_a a complex matrix
     903              : !> \param matrix_b another complex matrix
     904              : !> \param trace    value of the trace operator
     905              : !> \param conjugate_a conjugate the first matrix for a Hermitian Frobenius inner product (default: false)
     906              : !> \param local return only the local contribution, leaving the reduction to the caller (default: false)
     907              : !> \par History
     908              : !>    * 09.2017 created [Sergey Chulkov]
     909              : !> \author Sergey Chulkov
     910              : !> \note
     911              : !>      Based on the subroutine cp_fm_trace(). Note the transposition of matrix_a!
     912              : ! **************************************************************************************************
     913       167358 :    SUBROUTINE cp_cfm_trace(matrix_a, matrix_b, trace, conjugate_a, local)
     914              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_a, matrix_b
     915              :       COMPLEX(kind=dp), INTENT(out)                      :: trace
     916              :       LOGICAL, INTENT(IN), OPTIONAL                      :: conjugate_a, local
     917              : 
     918              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_cfm_trace'
     919              : 
     920              :       INTEGER                                            :: handle, mypcol, myprow, ncol_local, &
     921              :                                                             npcol, nprow, nrow_local
     922              :       LOGICAL                                            :: reduce
     923              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     924              : 
     925       167358 :       CALL timeset(routineN, handle)
     926              : 
     927       167358 :       context => matrix_a%matrix_struct%context
     928       167358 :       myprow = context%mepos(1)
     929       167358 :       mypcol = context%mepos(2)
     930       167358 :       nprow = context%num_pe(1)
     931       167358 :       npcol = context%num_pe(2)
     932              : 
     933       167358 :       nrow_local = MIN(matrix_a%matrix_struct%nrow_locals(myprow), matrix_b%matrix_struct%nrow_locals(myprow))
     934       167358 :       ncol_local = MIN(matrix_a%matrix_struct%ncol_locals(mypcol), matrix_b%matrix_struct%ncol_locals(mypcol))
     935              : 
     936              :       ! compute an accurate dot-product
     937              :       trace = accurate_dot_product(matrix_a%local_data(1:nrow_local, 1:ncol_local), &
     938       167358 :                                    matrix_b%local_data(1:nrow_local, 1:ncol_local), conjugate_a=conjugate_a)
     939              : 
     940       167358 :       reduce = .TRUE.
     941       167358 :       IF (PRESENT(local)) reduce = .NOT. local
     942       167358 :       IF (reduce) CALL matrix_a%matrix_struct%para_env%sum(trace)
     943              : 
     944       167358 :       CALL timestop(handle)
     945              : 
     946       167358 :    END SUBROUTINE cp_cfm_trace
     947              : 
     948              : ! **************************************************************************************************
     949              : !> \brief Multiplies in place by a triangular matrix:
     950              : !>       matrix_b = alpha op(triangular_matrix) matrix_b
     951              : !>      or (if side='R')
     952              : !>       matrix_b = alpha matrix_b op(triangular_matrix)
     953              : !>      op(triangular_matrix) is:
     954              : !>       triangular_matrix (if transa="N" and invert_tr=.false.)
     955              : !>       triangular_matrix^T (if transa="T" and invert_tr=.false.)
     956              : !>       triangular_matrix^H (if transa="C" and invert_tr=.false.)
     957              : !>       triangular_matrix^(-1) (if transa="N" and invert_tr=.true.)
     958              : !>       triangular_matrix^(-T) (if transa="T" and invert_tr=.true.)
     959              : !>       triangular_matrix^(-H) (if transa="C" and invert_tr=.true.)
     960              : !> \param triangular_matrix the triangular matrix that multiplies the other
     961              : !> \param matrix_b the matrix that gets multiplied and stores the result
     962              : !> \param side on which side of matrix_b stays op(triangular_matrix)
     963              : !>        (defaults to 'L')
     964              : !> \param transa_tr ...
     965              : !> \param invert_tr if the triangular matrix should be inverted
     966              : !>        (defaults to false)
     967              : !> \param uplo_tr if triangular_matrix is stored in the upper ('U') or
     968              : !>        lower ('L') triangle (defaults to 'U')
     969              : !> \param unit_diag_tr if the diagonal elements of triangular_matrix should
     970              : !>        be assumed to be 1 (defaults to false)
     971              : !> \param n_rows the number of rows of the result (defaults to
     972              : !>        size(matrix_b,1))
     973              : !> \param n_cols the number of columns of the result (defaults to
     974              : !>        size(matrix_b,2))
     975              : !> \param alpha ...
     976              : !> \par History
     977              : !>      08.2002 created [fawzi]
     978              : !> \author Fawzi Mohamed
     979              : !> \note
     980              : !>      needs an mpi env
     981              : ! **************************************************************************************************
     982       240866 :    SUBROUTINE cp_cfm_triangular_multiply(triangular_matrix, matrix_b, side, &
     983              :                                          transa_tr, invert_tr, uplo_tr, unit_diag_tr, n_rows, n_cols, &
     984              :                                          alpha)
     985              :       TYPE(cp_cfm_type), INTENT(IN)                      :: triangular_matrix, matrix_b
     986              :       CHARACTER, INTENT(in), OPTIONAL                    :: side, transa_tr
     987              :       LOGICAL, INTENT(in), OPTIONAL                      :: invert_tr
     988              :       CHARACTER, INTENT(in), OPTIONAL                    :: uplo_tr
     989              :       LOGICAL, INTENT(in), OPTIONAL                      :: unit_diag_tr
     990              :       INTEGER, INTENT(in), OPTIONAL                      :: n_rows, n_cols
     991              :       COMPLEX(kind=dp), INTENT(in), OPTIONAL             :: alpha
     992              : 
     993              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_triangular_multiply'
     994              : 
     995              :       CHARACTER                                          :: side_char, transa, unit_diag, uplo
     996              :       COMPLEX(kind=dp)                                   :: al
     997              :       INTEGER                                            :: handle, m, n
     998              :       LOGICAL                                            :: invert
     999              : 
    1000       120433 :       CALL timeset(routineN, handle)
    1001       120433 :       side_char = 'L'
    1002       120433 :       unit_diag = 'N'
    1003       120433 :       uplo = 'U'
    1004       120433 :       transa = 'N'
    1005       120433 :       invert = .FALSE.
    1006       120433 :       al = z_one
    1007       120433 :       CALL cp_cfm_get_info(matrix_b, nrow_global=m, ncol_global=n)
    1008       120433 :       IF (PRESENT(side)) side_char = side
    1009       120433 :       IF (PRESENT(invert_tr)) invert = invert_tr
    1010       120433 :       IF (PRESENT(uplo_tr)) uplo = uplo_tr
    1011       120433 :       IF (PRESENT(unit_diag_tr)) THEN
    1012            0 :          IF (unit_diag_tr) THEN
    1013            0 :             unit_diag = 'U'
    1014              :          ELSE
    1015              :             unit_diag = 'N'
    1016              :          END IF
    1017              :       END IF
    1018       120433 :       IF (PRESENT(transa_tr)) transa = transa_tr
    1019       120433 :       IF (PRESENT(alpha)) al = alpha
    1020       120433 :       IF (PRESENT(n_rows)) m = n_rows
    1021       120433 :       IF (PRESENT(n_cols)) n = n_cols
    1022              : 
    1023       120433 :       IF (invert) THEN
    1024              : 
    1025              : #if defined(__parallel)
    1026              :          CALL pztrsm(side_char, uplo, transa, unit_diag, m, n, al, &
    1027              :                      triangular_matrix%local_data(1, 1), 1, 1, &
    1028              :                      triangular_matrix%matrix_struct%descriptor, &
    1029              :                      matrix_b%local_data(1, 1), 1, 1, &
    1030        13982 :                      matrix_b%matrix_struct%descriptor(1))
    1031              : #else
    1032              :          CALL ztrsm(side_char, uplo, transa, unit_diag, m, n, al, &
    1033              :                     triangular_matrix%local_data(1, 1), &
    1034              :                     SIZE(triangular_matrix%local_data, 1), &
    1035              :                     matrix_b%local_data(1, 1), SIZE(matrix_b%local_data, 1))
    1036              : #endif
    1037              : 
    1038              :       ELSE
    1039              : 
    1040              : #if defined(__parallel)
    1041              :          CALL pztrmm(side_char, uplo, transa, unit_diag, m, n, al, &
    1042              :                      triangular_matrix%local_data(1, 1), 1, 1, &
    1043              :                      triangular_matrix%matrix_struct%descriptor, &
    1044              :                      matrix_b%local_data(1, 1), 1, 1, &
    1045       106451 :                      matrix_b%matrix_struct%descriptor(1))
    1046              : #else
    1047              :          CALL ztrmm(side_char, uplo, transa, unit_diag, m, n, al, &
    1048              :                     triangular_matrix%local_data(1, 1), &
    1049              :                     SIZE(triangular_matrix%local_data, 1), &
    1050              :                     matrix_b%local_data(1, 1), SIZE(matrix_b%local_data, 1))
    1051              : #endif
    1052              : 
    1053              :       END IF
    1054              : 
    1055       120433 :       CALL timestop(handle)
    1056              : 
    1057       120433 :    END SUBROUTINE cp_cfm_triangular_multiply
    1058              : 
    1059              : ! **************************************************************************************************
    1060              : !> \brief Inverts a triangular matrix.
    1061              : !> \param matrix_a ...
    1062              : !> \param uplo ...
    1063              : !> \param info_out ...
    1064              : !> \author MI
    1065              : ! **************************************************************************************************
    1066        35229 :    SUBROUTINE cp_cfm_triangular_invert(matrix_a, uplo, info_out)
    1067              :       TYPE(cp_cfm_type), INTENT(IN)            :: matrix_a
    1068              :       CHARACTER, INTENT(in), OPTIONAL          :: uplo
    1069              :       INTEGER, INTENT(out), OPTIONAL           :: info_out
    1070              : 
    1071              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_triangular_invert'
    1072              : 
    1073              :       CHARACTER                                :: unit_diag, my_uplo
    1074              :       INTEGER                                  :: handle, info, ncol_global
    1075              :       COMPLEX(kind=dp), DIMENSION(:, :), &
    1076        35229 :          POINTER                               :: a
    1077              : #if defined(__parallel)
    1078              :       INTEGER, DIMENSION(9)                    :: desca
    1079              : #endif
    1080              : 
    1081        35229 :       CALL timeset(routineN, handle)
    1082              : 
    1083        35229 :       unit_diag = 'N'
    1084        35229 :       my_uplo = 'U'
    1085        35229 :       IF (PRESENT(uplo)) my_uplo = uplo
    1086              : 
    1087        35229 :       ncol_global = matrix_a%matrix_struct%ncol_global
    1088              : 
    1089        35229 :       a => matrix_a%local_data
    1090              : 
    1091              : #if defined(__parallel)
    1092       352290 :       desca(:) = matrix_a%matrix_struct%descriptor(:)
    1093        35229 :       CALL pztrtri(my_uplo, unit_diag, ncol_global, a(1, 1), 1, 1, desca, info)
    1094              : #else
    1095              :       CALL ztrtri(my_uplo, unit_diag, ncol_global, a(1, 1), ncol_global, info)
    1096              : #endif
    1097              : 
    1098        35229 :       IF (PRESENT(info_out)) THEN
    1099            0 :          info_out = info
    1100              :       ELSE
    1101        35229 :          IF (info /= 0) &
    1102              :             CALL cp_abort(__LOCATION__, &
    1103            0 :                           "triangular invert failed: matrix is not positive definite  or ill-conditioned")
    1104              :       END IF
    1105              : 
    1106        35229 :       CALL timestop(handle)
    1107        35229 :    END SUBROUTINE cp_cfm_triangular_invert
    1108              : 
    1109              : ! **************************************************************************************************
    1110              : !> \brief Transposes a BLACS distributed complex matrix.
    1111              : !> \param matrix    input matrix
    1112              : !> \param trans     'T' for transpose, 'C' for Hermitian conjugate
    1113              : !> \param matrixt   output matrix
    1114              : !> \author Lianheng Tong
    1115              : ! **************************************************************************************************
    1116        33393 :    SUBROUTINE cp_cfm_transpose(matrix, trans, matrixt)
    1117              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
    1118              :       CHARACTER, INTENT(in)                              :: trans
    1119              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrixt
    1120              : 
    1121              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_transpose'
    1122              : 
    1123        33393 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: aa, cc
    1124              :       INTEGER                                            :: handle, ncol_global, nrow_global
    1125              : #if defined(__parallel)
    1126              :       INTEGER, DIMENSION(9)                              :: desca, descc
    1127              : #elif !defined(__MKL)
    1128              :       INTEGER                                            :: ii, jj
    1129              : #endif
    1130              : 
    1131        33393 :       CALL timeset(routineN, handle)
    1132              : 
    1133        33393 :       nrow_global = matrix%matrix_struct%nrow_global
    1134        33393 :       ncol_global = matrix%matrix_struct%ncol_global
    1135              : 
    1136        33393 :       CPASSERT(matrixt%matrix_struct%nrow_global == ncol_global)
    1137        33393 :       CPASSERT(matrixt%matrix_struct%ncol_global == nrow_global)
    1138              : 
    1139        33393 :       aa => matrix%local_data
    1140        33393 :       cc => matrixt%local_data
    1141              : 
    1142              : #if defined(__parallel)
    1143       333930 :       desca = matrix%matrix_struct%descriptor
    1144       333930 :       descc = matrixt%matrix_struct%descriptor
    1145        13338 :       SELECT CASE (trans)
    1146              :       CASE ('T')
    1147              :          CALL pztranu(nrow_global, ncol_global, &
    1148              :                       z_one, aa(1, 1), 1, 1, desca, &
    1149        13338 :                       z_zero, cc(1, 1), 1, 1, descc)
    1150              :       CASE ('C')
    1151              :          CALL pztranc(nrow_global, ncol_global, &
    1152              :                       z_one, aa(1, 1), 1, 1, desca, &
    1153        20055 :                       z_zero, cc(1, 1), 1, 1, descc)
    1154              :       CASE DEFAULT
    1155        33393 :          CPABORT("trans only accepts 'T' or 'C'")
    1156              :       END SELECT
    1157              : #elif defined(__MKL)
    1158              :       CALL mkl_zomatcopy('C', trans, nrow_global, ncol_global, 1.0_dp, aa(1, 1), nrow_global, cc(1, 1), ncol_global)
    1159              : #else
    1160              :       SELECT CASE (trans)
    1161              :       CASE ('T')
    1162              :          DO jj = 1, ncol_global
    1163              :             DO ii = 1, nrow_global
    1164              :                cc(ii, jj) = aa(jj, ii)
    1165              :             END DO
    1166              :          END DO
    1167              :       CASE ('C')
    1168              :          DO jj = 1, ncol_global
    1169              :             DO ii = 1, nrow_global
    1170              :                cc(ii, jj) = CONJG(aa(jj, ii))
    1171              :             END DO
    1172              :          END DO
    1173              :       CASE DEFAULT
    1174              :          CPABORT("trans only accepts 'T' or 'C'")
    1175              :       END SELECT
    1176              : #endif
    1177              : 
    1178        33393 :       CALL timestop(handle)
    1179        33393 :    END SUBROUTINE cp_cfm_transpose
    1180              : 
    1181              : ! **************************************************************************************************
    1182              : !> \brief Norm of matrix using (p)zlange.
    1183              : !> \param matrix     input a general matrix
    1184              : !> \param mode       'M' max abs element value,
    1185              : !>                   '1' or 'O' one norm, i.e. maximum column sum,
    1186              : !>                   'I' infinity norm, i.e. maximum row sum,
    1187              : !>                   'F' or 'E' Frobenius norm, i.e. sqrt of sum of all squares of elements
    1188              : !> \return the norm according to mode
    1189              : !> \author Lianheng Tong
    1190              : ! **************************************************************************************************
    1191       237266 :    FUNCTION cp_cfm_norm(matrix, mode) RESULT(res)
    1192              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
    1193              :       CHARACTER, INTENT(IN)                              :: mode
    1194              :       REAL(kind=dp)                                      :: res
    1195              : 
    1196              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_norm'
    1197              : 
    1198       237266 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: aa
    1199              :       INTEGER                                            :: handle, lwork, ncols, ncols_local, &
    1200              :                                                             nrows, nrows_local
    1201       237266 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: work
    1202              : 
    1203              : #if defined(__parallel)
    1204              :       INTEGER, DIMENSION(9)                              :: desca
    1205              : #else
    1206              :       INTEGER                                            :: lda
    1207              : #endif
    1208              : 
    1209       237266 :       CALL timeset(routineN, handle)
    1210              : 
    1211              :       CALL cp_cfm_get_info(matrix=matrix, &
    1212              :                            nrow_global=nrows, &
    1213              :                            ncol_global=ncols, &
    1214              :                            nrow_local=nrows_local, &
    1215       237266 :                            ncol_local=ncols_local)
    1216       237266 :       aa => matrix%local_data
    1217              : 
    1218              :       SELECT CASE (mode)
    1219              :       CASE ('M', 'm')
    1220            0 :          lwork = 1
    1221              :       CASE ('1', 'O', 'o')
    1222              : #if defined(__parallel)
    1223            0 :          lwork = ncols_local
    1224              : #else
    1225              :          lwork = 1
    1226              : #endif
    1227              :       CASE ('I', 'i')
    1228              : #if defined(__parallel)
    1229            8 :          lwork = nrows_local
    1230              : #else
    1231              :          lwork = nrows
    1232              : #endif
    1233              :       CASE ('F', 'f', 'E', 'e')
    1234            0 :          lwork = 1
    1235              :       CASE DEFAULT
    1236       237266 :          CPABORT("mode input is not valid")
    1237              :       END SELECT
    1238              : 
    1239       711798 :       ALLOCATE (work(lwork))
    1240              : 
    1241              : #if defined(__parallel)
    1242      2372660 :       desca = matrix%matrix_struct%descriptor
    1243       237266 :       res = pzlange(mode, nrows, ncols, aa(1, 1), 1, 1, desca, work)
    1244              : #else
    1245              :       lda = SIZE(aa, 1)
    1246              :       res = zlange(mode, nrows, ncols, aa(1, 1), lda, work)
    1247              : #endif
    1248              : 
    1249       237266 :       DEALLOCATE (work)
    1250       237266 :       CALL timestop(handle)
    1251       237266 :    END FUNCTION cp_cfm_norm
    1252              : 
    1253              : ! **************************************************************************************************
    1254              : !> \brief Applies a planar rotation defined by cs and sn to the i'th and j'th rows.
    1255              : !> \param matrix ...
    1256              : !> \param irow ...
    1257              : !> \param jrow ...
    1258              : !> \param cs cosine of the rotation angle
    1259              : !> \param sn sinus of the rotation angle
    1260              : !> \author Ole Schuett
    1261              : ! **************************************************************************************************
    1262       375120 :    SUBROUTINE cp_cfm_rot_rows(matrix, irow, jrow, cs, sn)
    1263              :       TYPE(cp_cfm_type), INTENT(IN)            :: matrix
    1264              :       INTEGER, INTENT(IN)                      :: irow, jrow
    1265              :       REAL(dp), INTENT(IN)                     :: cs, sn
    1266              : 
    1267              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_rot_rows'
    1268              :       INTEGER                                  :: handle, ncol
    1269              :       COMPLEX(KIND=dp)                         :: sn_cmplx
    1270              : 
    1271              : #if defined(__parallel)
    1272              :       INTEGER                                  :: info, lwork
    1273              :       INTEGER, DIMENSION(9)                    :: desc
    1274       375120 :       REAL(dp), DIMENSION(:), ALLOCATABLE      :: work
    1275              : #endif
    1276       375120 :       CALL timeset(routineN, handle)
    1277       375120 :       CALL cp_cfm_get_info(matrix, ncol_global=ncol)
    1278       375120 :       sn_cmplx = CMPLX(sn, 0.0_dp, dp)
    1279              : #if defined(__parallel)
    1280       375120 :       IF (1 /= matrix%matrix_struct%context%n_pid) THEN
    1281       375120 :          lwork = 2*ncol + 1
    1282      1125360 :          ALLOCATE (work(lwork))
    1283      3751200 :          desc(:) = matrix%matrix_struct%descriptor(:)
    1284       375120 :          info = 0
    1285              :          CALL pzrot(ncol, &
    1286              :                     matrix%local_data(1, 1), irow, 1, desc, ncol, &
    1287              :                     matrix%local_data(1, 1), jrow, 1, desc, ncol, &
    1288       375120 :                     cs, sn_cmplx, work, lwork, info)
    1289       375120 :          CPASSERT(info == 0)
    1290       375120 :          DEALLOCATE (work)
    1291              :       ELSE
    1292              : #endif
    1293            0 :          CALL zrot(ncol, matrix%local_data(irow, 1), ncol, matrix%local_data(jrow, 1), ncol, cs, sn_cmplx)
    1294              : #if defined(__parallel)
    1295              :       END IF
    1296              : #endif
    1297       375120 :       CALL timestop(handle)
    1298       375120 :    END SUBROUTINE cp_cfm_rot_rows
    1299              : 
    1300              : ! **************************************************************************************************
    1301              : !> \brief Applies a planar rotation defined by cs and sn to the i'th and j'th columnns.
    1302              : !> \param matrix ...
    1303              : !> \param icol ...
    1304              : !> \param jcol ...
    1305              : !> \param cs cosine of the rotation angle
    1306              : !> \param sn sinus of the rotation angle
    1307              : !> \author Ole Schuett
    1308              : ! **************************************************************************************************
    1309       422760 :    SUBROUTINE cp_cfm_rot_cols(matrix, icol, jcol, cs, sn)
    1310              :       TYPE(cp_cfm_type), INTENT(IN)            :: matrix
    1311              :       INTEGER, INTENT(IN)                      :: icol, jcol
    1312              :       REAL(dp), INTENT(IN)                     :: cs, sn
    1313              : 
    1314              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_rot_cols'
    1315              :       INTEGER                                  :: handle, nrow
    1316              :       COMPLEX(KIND=dp)                         :: sn_cmplx
    1317              : 
    1318              : #if defined(__parallel)
    1319              :       INTEGER                                  :: info, lwork
    1320              :       INTEGER, DIMENSION(9)                    :: desc
    1321       422760 :       REAL(dp), DIMENSION(:), ALLOCATABLE      :: work
    1322              : #endif
    1323       422760 :       CALL timeset(routineN, handle)
    1324       422760 :       CALL cp_cfm_get_info(matrix, nrow_global=nrow)
    1325       422760 :       sn_cmplx = CMPLX(sn, 0.0_dp, dp)
    1326              : #if defined(__parallel)
    1327       422760 :       IF (1 /= matrix%matrix_struct%context%n_pid) THEN
    1328       422760 :          lwork = 2*nrow + 1
    1329      1268280 :          ALLOCATE (work(lwork))
    1330      4227600 :          desc(:) = matrix%matrix_struct%descriptor(:)
    1331       422760 :          info = 0
    1332              :          CALL pzrot(nrow, &
    1333              :                     matrix%local_data(1, 1), 1, icol, desc, 1, &
    1334              :                     matrix%local_data(1, 1), 1, jcol, desc, 1, &
    1335       422760 :                     cs, sn_cmplx, work, lwork, info)
    1336       422760 :          CPASSERT(info == 0)
    1337       422760 :          DEALLOCATE (work)
    1338              :       ELSE
    1339              : #endif
    1340            0 :          CALL zrot(nrow, matrix%local_data(1, icol), 1, matrix%local_data(1, jcol), 1, cs, sn_cmplx)
    1341              : #if defined(__parallel)
    1342              :       END IF
    1343              : #endif
    1344       422760 :       CALL timestop(handle)
    1345       422760 :    END SUBROUTINE cp_cfm_rot_cols
    1346              : 
    1347              : ! **************************************************************************************************
    1348              : !> \brief ...
    1349              : !> \param matrix ...
    1350              : !> \param workspace ...
    1351              : !> \param uplo triangular format; defaults to 'U'
    1352              : !> \par History
    1353              : !>      12.2024 Added optional workspace as input [Rocco Meli]
    1354              : !> \author Jan Wilhelm
    1355              : ! **************************************************************************************************
    1356        15122 :    SUBROUTINE cp_cfm_uplo_to_full(matrix, workspace, uplo)
    1357              : 
    1358              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
    1359              :       TYPE(cp_cfm_type), INTENT(IN), OPTIONAL            :: workspace
    1360              :       CHARACTER, INTENT(IN), OPTIONAL                    :: uplo
    1361              : 
    1362              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_cfm_uplo_to_full'
    1363              : 
    1364              :       CHARACTER                                          :: myuplo
    1365              :       INTEGER                                            :: handle, i_global, iiB, j_global, jjB, &
    1366              :                                                             ncol_local, nrow_local
    1367         7561 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
    1368              :       TYPE(cp_cfm_type)                                  :: work
    1369              : 
    1370         7561 :       CALL timeset(routineN, handle)
    1371              : 
    1372         7561 :       IF (.NOT. PRESENT(workspace)) THEN
    1373         6910 :          CALL cp_cfm_create(work, matrix%matrix_struct)
    1374              :       ELSE
    1375          651 :          work = workspace
    1376              :       END IF
    1377              : 
    1378         7561 :       myuplo = 'U'
    1379         7561 :       IF (PRESENT(uplo)) myuplo = uplo
    1380              : 
    1381              :       ! get info of fm_mat_Q
    1382              :       CALL cp_cfm_get_info(matrix=matrix, &
    1383              :                            nrow_local=nrow_local, &
    1384              :                            ncol_local=ncol_local, &
    1385              :                            row_indices=row_indices, &
    1386         7561 :                            col_indices=col_indices)
    1387              : 
    1388       278263 :       DO jjB = 1, ncol_local
    1389       270702 :          j_global = col_indices(jjB)
    1390      9086380 :          DO iiB = 1, nrow_local
    1391      8808117 :             i_global = row_indices(iiB)
    1392      9078819 :             IF (MERGE(j_global < i_global, j_global > i_global, (myuplo == "U") .OR. (myuplo == "u"))) THEN
    1393      4331539 :                matrix%local_data(iiB, jjB) = z_zero
    1394      4476578 :             ELSE IF (j_global == i_global) THEN
    1395       145039 :                matrix%local_data(iiB, jjB) = matrix%local_data(iiB, jjB)/(2.0_dp, 0.0_dp)
    1396              :             END IF
    1397              :          END DO
    1398              :       END DO
    1399              : 
    1400         7561 :       CALL cp_cfm_transpose(matrix, 'C', work)
    1401              : 
    1402         7561 :       CALL cp_cfm_scale_and_add(z_one, matrix, z_one, work)
    1403              : 
    1404         7561 :       IF (.NOT. PRESENT(workspace)) THEN
    1405         6910 :          CALL cp_cfm_release(work)
    1406              :       END IF
    1407              : 
    1408         7561 :       CALL timestop(handle)
    1409              : 
    1410         7561 :    END SUBROUTINE cp_cfm_uplo_to_full
    1411              : 
    1412              : ! **************************************************************************************************
    1413              : !> \brief find the norm of each column norm_{j}= sqrt( \sum_{i} A_{ij}*conjg(A_{ij}) )
    1414              : !>        Complex-valued mirror of cp_fm_vectorsnorm.
    1415              : !> \param matrix ...
    1416              : !> \param norm_array ...
    1417              : !> \param squared return squared norms instead (default: false)
    1418              : ! **************************************************************************************************
    1419       190410 :    SUBROUTINE cp_cfm_vectorsnorm(matrix, norm_array, squared)
    1420              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
    1421              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: norm_array
    1422              :       LOGICAL, INTENT(IN), OPTIONAL                      :: squared
    1423              : 
    1424              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_cfm_vectorsnorm'
    1425              : 
    1426              :       INTEGER                                            :: handle, i, j, ncol_local, nrow_local
    1427              : 
    1428              : !$    INTEGER                                            :: nworkers
    1429       190410 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices
    1430              :       LOGICAL                                            :: take_sqrt
    1431              :       REAL(KIND=dp)                                      :: norm_sq
    1432              :       COMPLEX(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
    1433       190410 :          POINTER                                         :: local_data
    1434              : 
    1435       190410 :       CALL timeset(routineN, handle)
    1436              : 
    1437       190410 :       local_data => matrix%local_data
    1438              :       CALL cp_cfm_get_info(matrix, col_indices=col_indices, nrow_local=nrow_local, &
    1439       190410 :                            ncol_local=ncol_local)
    1440              : 
    1441       993886 :       norm_array = 0.0_dp
    1442              :       ! Own whole columns and keep at least 8192 elements per worker to amortize team startup.
    1443       190410 : !$    nworkers = MAX(1, MIN(omp_get_max_threads(), ncol_local))
    1444       190410 : !$    nworkers = MAX(1, MIN(nworkers, (nrow_local*ncol_local + 1)/8192))
    1445              : !$OMP PARALLEL DO DEFAULT(NONE) SCHEDULE(STATIC) &
    1446              : !$OMP SHARED(local_data, col_indices, nrow_local, ncol_local, norm_array) PRIVATE(i, j, norm_sq) &
    1447       190410 : !$OMP NUM_THREADS(nworkers) IF(nworkers > 1)
    1448              :       DO j = 1, ncol_local
    1449              :          norm_sq = 0.0_dp
    1450              : !$OMP SIMD REDUCTION(+:norm_sq)
    1451              :          DO i = 1, nrow_local
    1452              :             norm_sq = norm_sq + REAL(local_data(i, j), KIND=dp)**2 + AIMAG(local_data(i, j))**2
    1453              :          END DO
    1454              : !$OMP END SIMD
    1455              :          norm_array(col_indices(j)) = norm_sq
    1456              :       END DO
    1457              : !$OMP END PARALLEL DO
    1458      1797362 :       CALL matrix%matrix_struct%para_env%sum(norm_array)
    1459       190410 :       take_sqrt = .TRUE.
    1460       190410 :       IF (PRESENT(squared)) take_sqrt = .NOT. squared
    1461       991692 :       IF (take_sqrt) norm_array = SQRT(norm_array)
    1462              : 
    1463       190410 :       CALL timestop(handle)
    1464       190410 :    END SUBROUTINE cp_cfm_vectorsnorm
    1465              : 
    1466              : ! **************************************************************************************************
    1467              : !> \brief Adds a scalar to the diagonal of a distributed complex full matrix.
    1468              : !> \param matrix input/output matrix
    1469              : !> \param alpha scalar added to each diagonal element
    1470              : !> \param n_active optional number of physical rows/columns
    1471              : ! **************************************************************************************************
    1472        23512 :    SUBROUTINE cp_cfm_add_on_diag(matrix, alpha, n_active)
    1473              :       TYPE(cp_cfm_type), INTENT(INOUT)                   :: matrix
    1474              :       COMPLEX(KIND=dp), INTENT(IN)                       :: alpha
    1475              :       INTEGER, INTENT(IN), OPTIONAL                      :: n_active
    1476              : 
    1477              :       INTEGER                                            :: i_global, n_diag, ncol_global, nrow_global
    1478              : #if defined(__parallel)
    1479              :       INTEGER                                            :: icol_local, ipcol, iprow, irow, irow_local, &
    1480              :                                                             mypcol, myprow, npcol, nprow, nrow_local
    1481              :       INTEGER, DIMENSION(9)                              :: desca
    1482        11756 :       INTEGER, DIMENSION(:), POINTER                     :: row_indices
    1483              :       TYPE(cp_blacs_env_type), POINTER                   :: context
    1484              : #endif
    1485              : 
    1486        11756 :       CALL cp_cfm_get_info(matrix, nrow_global=nrow_global, ncol_global=ncol_global)
    1487              : 
    1488        11756 :       n_diag = MIN(nrow_global, ncol_global)
    1489        11756 :       IF (PRESENT(n_active)) n_diag = n_active
    1490        11756 :       CPASSERT(n_diag >= 0 .AND. n_diag <= MIN(nrow_global, ncol_global))
    1491              : 
    1492              : #if defined(__parallel)
    1493        11756 :       CALL cp_cfm_get_info(matrix, nrow_local=nrow_local, row_indices=row_indices)
    1494        11756 :       context => matrix%matrix_struct%context
    1495        11756 :       myprow = context%mepos(1)
    1496        11756 :       mypcol = context%mepos(2)
    1497        11756 :       nprow = context%num_pe(1)
    1498        11756 :       npcol = context%num_pe(2)
    1499       117560 :       desca(:) = matrix%matrix_struct%descriptor(:)
    1500              : 
    1501       258286 :       DO irow = 1, nrow_local
    1502       246530 :          i_global = row_indices(irow)
    1503       246530 :          IF (i_global > n_diag) EXIT
    1504              :          CALL infog2l(i_global, i_global, desca, nprow, npcol, myprow, mypcol, &
    1505       246530 :                       irow_local, icol_local, iprow, ipcol)
    1506       258286 :          IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
    1507       246530 :             matrix%local_data(irow_local, icol_local) = matrix%local_data(irow_local, icol_local) + alpha
    1508              :          END IF
    1509              :       END DO
    1510              : #else
    1511              :       DO i_global = 1, n_diag
    1512              :          matrix%local_data(i_global, i_global) = matrix%local_data(i_global, i_global) + alpha
    1513              :       END DO
    1514              : #endif
    1515              : 
    1516        11756 :    END SUBROUTINE cp_cfm_add_on_diag
    1517              : 
    1518              : ! **************************************************************************************************
    1519              : !> \brief returns the diagonal of a complex full matrix: diag(i)= A_{ii}.
    1520              : !>        Each diagonal entry is owned by one process. The sum over the
    1521              : !>        process grid collects the entries.
    1522              : !> \param matrix ...
    1523              : !> \param diag ...
    1524              : !> \param n_active optional number of leading diagonal elements to return
    1525              : ! **************************************************************************************************
    1526        28716 :    SUBROUTINE cp_cfm_get_diag(matrix, diag, n_active)
    1527              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
    1528              :       COMPLEX(KIND=dp), DIMENSION(:), INTENT(OUT)        :: diag
    1529              :       INTEGER, INTENT(IN), OPTIONAL                      :: n_active
    1530              : 
    1531              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'cp_cfm_get_diag'
    1532              : 
    1533              :       INTEGER                                            :: handle, i_global, n_diag, ncol_global, &
    1534              :                                                             nrow_global
    1535              : #if defined(__parallel)
    1536              :       INTEGER                                            :: icol_local, ipcol, iprow, irow, irow_local, &
    1537              :                                                             mypcol, myprow, npcol, nprow, nrow_local
    1538              :       INTEGER, DIMENSION(9)                              :: desca
    1539        28716 :       INTEGER, DIMENSION(:), POINTER                     :: row_indices
    1540              :       TYPE(cp_blacs_env_type), POINTER                   :: context
    1541              : #endif
    1542              : 
    1543        28716 :       CALL timeset(routineN, handle)
    1544              : 
    1545        28716 :       CALL cp_cfm_get_info(matrix, nrow_global=nrow_global, ncol_global=ncol_global)
    1546              : 
    1547        28716 :       n_diag = MIN(nrow_global, ncol_global)
    1548        28716 :       IF (PRESENT(n_active)) n_diag = n_active
    1549        28716 :       CPASSERT(n_diag >= 0 .AND. n_diag <= MIN(nrow_global, ncol_global))
    1550        28716 :       CPASSERT(SIZE(diag) >= n_diag)
    1551              : 
    1552       715063 :       diag = z_zero
    1553              : #if defined(__parallel)
    1554        28716 :       CALL cp_cfm_get_info(matrix, nrow_local=nrow_local, row_indices=row_indices)
    1555        28716 :       context => matrix%matrix_struct%context
    1556        28716 :       myprow = context%mepos(1)
    1557        28716 :       mypcol = context%mepos(2)
    1558        28716 :       nprow = context%num_pe(1)
    1559        28716 :       npcol = context%num_pe(2)
    1560       287160 :       desca(:) = matrix%matrix_struct%descriptor(:)
    1561              : 
    1562       395124 :       DO irow = 1, nrow_local
    1563       366408 :          i_global = row_indices(irow)
    1564       366408 :          IF (i_global > n_diag) EXIT
    1565              :          CALL infog2l(i_global, i_global, desca, nprow, npcol, myprow, mypcol, &
    1566       366408 :                       irow_local, icol_local, iprow, ipcol)
    1567       395124 :          IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
    1568       366408 :             diag(i_global) = matrix%local_data(irow_local, icol_local)
    1569              :          END IF
    1570              :       END DO
    1571              : #else
    1572              :       DO i_global = 1, n_diag
    1573              :          diag(i_global) = matrix%local_data(i_global, i_global)
    1574              :       END DO
    1575              : #endif
    1576      1401410 :       CALL matrix%matrix_struct%para_env%sum(diag)
    1577              : 
    1578        28716 :       CALL timestop(handle)
    1579        28716 :    END SUBROUTINE cp_cfm_get_diag
    1580              : 
    1581              : END MODULE cp_cfm_basic_linalg
        

Generated by: LCOV version 2.0-1