LCOV - code coverage report
Current view: top level - src/common - mathlib.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:85b8a9b) Lines: 78.4 % 728 571
Test Date: 2026-06-14 06:48:14 Functions: 91.3 % 46 42

            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 Collection of simple mathematical functions and subroutines
      10              : !> \par History
      11              : !>      FUNCTION angle updated and FUNCTION dihedral angle added; cleaned
      12              : !>      (13.03.2004,MK)
      13              : !> \author MK (15.11.1998)
      14              : ! **************************************************************************************************
      15              : MODULE mathlib
      16              : 
      17              :    USE kinds,                           ONLY: default_string_length,&
      18              :                                               dp
      19              :    USE mathconstants,                   ONLY: euler,&
      20              :                                               fac,&
      21              :                                               oorootpi,&
      22              :                                               z_one,&
      23              :                                               z_zero
      24              : #include "../base/base_uses.f90"
      25              : 
      26              :    IMPLICIT NONE
      27              :    PRIVATE
      28              : 
      29              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'mathlib'
      30              :    REAL(KIND=dp), PARAMETER             :: eps_geo = 1.0E-6_dp
      31              : 
      32              :    ! Public subroutines
      33              : 
      34              :    PUBLIC :: build_rotmat, &
      35              :              jacobi, &
      36              :              diamat_all, &
      37              :              invmat, &
      38              :              invmat_symm, &
      39              :              invert_matrix, &
      40              :              get_pseudo_inverse_svd, &
      41              :              get_pseudo_inverse_diag, &
      42              :              symmetrize_matrix, &
      43              :              unit_matrix, diag, &
      44              :              erfc_cutoff, &
      45              :              diag_antisym, &
      46              :              diag_complex, &
      47              :              gemm_square, &
      48              :              geeig_right
      49              : 
      50              :    ! Public functions
      51              : 
      52              :    PUBLIC :: angle, &
      53              :              binomial, &
      54              :              binomial_gen, &
      55              :              multinomial, &
      56              :              det_3x3, &
      57              :              dihedral_angle, &
      58              :              gcd, &
      59              :              inv_3x3, &
      60              :              lcm, &
      61              :              vector_product, &
      62              :              pswitch, &
      63              :              rotate_vector, &
      64              :              reflect_vector, &
      65              :              expint, abnormal_value, &
      66              :              get_diag, &
      67              :              set_diag
      68              : 
      69              :    INTERFACE det_3x3
      70              :       MODULE PROCEDURE det_3x3_1, det_3x3_2
      71              :    END INTERFACE
      72              : 
      73              :    INTERFACE invert_matrix
      74              :       MODULE PROCEDURE invert_matrix_d, invert_matrix_z
      75              :    END INTERFACE
      76              : 
      77              :    INTERFACE set_diag
      78              :       MODULE PROCEDURE set_diag_scalar_d, set_diag_scalar_z
      79              :    END INTERFACE
      80              : 
      81              :    INTERFACE swap
      82              :       MODULE PROCEDURE swap_scalar, swap_vector
      83              :    END INTERFACE
      84              : 
      85              :    INTERFACE unit_matrix
      86              :       MODULE PROCEDURE unit_matrix_d, unit_matrix_z
      87              :    END INTERFACE
      88              : 
      89              :    INTERFACE gemm_square
      90              :       MODULE PROCEDURE zgemm_square_2, zgemm_square_3, dgemm_square_2, dgemm_square_3
      91              :    END INTERFACE
      92              : 
      93              : CONTAINS
      94              : 
      95              : ! **************************************************************************************************
      96              : !> \brief Polynomial (5th degree) switching function
      97              : !>        f(a) = 1 .... f(b) = 0 with f'(a) = f"(a) = f'(b) = f"(b) = 0
      98              : !> \param x ...
      99              : !> \param a ...
     100              : !> \param b ...
     101              : !> \param order ...
     102              : !> \return =0 : f(x)
     103              : !> \return =1 : f'(x)
     104              : !> \return =2 : f"(x)
     105              : ! **************************************************************************************************
     106        29870 :    FUNCTION pswitch(x, a, b, order) RESULT(fx)
     107              :       REAL(KIND=dp)                                      :: x, a, b
     108              :       INTEGER                                            :: order
     109              :       REAL(KIND=dp)                                      :: fx
     110              : 
     111              :       REAL(KIND=dp)                                      :: u, u2, u3
     112              : 
     113        29870 :       CPASSERT(b > a)
     114        29870 :       IF (x < a .OR. x > b) THEN
     115              :          ! outside switching intervall
     116        27934 :          IF (order > 0) THEN
     117              :             ! derivatives are 0
     118              :             fx = 0.0_dp
     119              :          ELSE
     120        13967 :             IF (x < a) THEN
     121              :                ! x < a => f(x) = 1
     122              :                fx = 1.0_dp
     123              :             ELSE
     124              :                ! x > b => f(x) = 0
     125        13640 :                fx = 0.0_dp
     126              :             END IF
     127              :          END IF
     128              :       ELSE
     129              :          ! renormalized coordinate
     130         1936 :          u = (x - a)/(b - a)
     131         2904 :          SELECT CASE (order)
     132              :          CASE (0)
     133          968 :             u2 = u*u
     134          968 :             u3 = u2*u
     135          968 :             fx = 1._dp - 10._dp*u3 + 15._dp*u2*u2 - 6._dp*u2*u3
     136              :          CASE (1)
     137          968 :             u2 = u*u
     138          968 :             fx = -30._dp*u2 + 60._dp*u*u2 - 30._dp*u2*u2
     139          968 :             fx = fx/(b - a)
     140              :          CASE (2)
     141            0 :             u2 = u*u
     142            0 :             fx = -60._dp*u + 180._dp*u2 - 120._dp*u*u2
     143            0 :             fx = fx/(b - a)**2
     144              :          CASE DEFAULT
     145         1936 :             CPABORT('order not defined')
     146              :          END SELECT
     147              :       END IF
     148              : 
     149        29870 :    END FUNCTION pswitch
     150              : 
     151              : ! **************************************************************************************************
     152              : !> \brief determines if a value is not normal (e.g. for Inf and Nan)
     153              : !>        based on IO to work also under optimization.
     154              : !> \param a input value
     155              : !> \return TRUE for NaN and Inf
     156              : ! **************************************************************************************************
     157       367551 :    LOGICAL FUNCTION abnormal_value(a)
     158              :       REAL(KIND=dp)                                      :: a
     159              : 
     160              :       CHARACTER(LEN=32)                                  :: buffer
     161              : 
     162       367551 :       abnormal_value = .FALSE.
     163              :       ! the function should work when compiled with -ffast-math and similar
     164              :       ! unfortunately, that option asserts that all numbers are normals,
     165              :       ! which the compiler uses to optimize the function to .FALSE. if based on the IEEE module
     166              :       ! therefore, pass this to the Fortran runtime/printf, if things are NaN or Inf, error out.
     167       367551 :       WRITE (buffer, *) a
     168       367551 :       IF (INDEX(buffer, "N") /= 0 .OR. INDEX(buffer, "n") /= 0) abnormal_value = .TRUE.
     169              : 
     170       367551 :    END FUNCTION abnormal_value
     171              : 
     172              : ! **************************************************************************************************
     173              : !> \brief  Calculation of the angle between the vectors a and b.
     174              : !>         The angle is returned in radians.
     175              : !> \param a ...
     176              : !> \param b ...
     177              : !> \return ...
     178              : !> \date    14.10.1998
     179              : !> \author  MK
     180              : !> \version 1.0
     181              : ! **************************************************************************************************
     182       754822 :    PURE FUNCTION angle(a, b) RESULT(angle_ab)
     183              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: a, b
     184              :       REAL(KIND=dp)                                      :: angle_ab
     185              : 
     186              :       REAL(KIND=dp)                                      :: length_of_a, length_of_b
     187       754822 :       REAL(KIND=dp), DIMENSION(SIZE(a, 1))               :: a_norm, b_norm
     188              : 
     189      3019288 :       length_of_a = SQRT(DOT_PRODUCT(a, a))
     190      3019288 :       length_of_b = SQRT(DOT_PRODUCT(b, b))
     191              : 
     192       754822 :       IF ((length_of_a > eps_geo) .AND. (length_of_b > eps_geo)) THEN
     193      3019288 :          a_norm(:) = a(:)/length_of_a
     194      3019288 :          b_norm(:) = b(:)/length_of_b
     195      3019288 :          angle_ab = ACOS(MIN(MAX(DOT_PRODUCT(a_norm, b_norm), -1.0_dp), 1.0_dp))
     196              :       ELSE
     197              :          angle_ab = 0.0_dp
     198              :       END IF
     199              : 
     200       754822 :    END FUNCTION angle
     201              : 
     202              : ! **************************************************************************************************
     203              : !> \brief   The binomial coefficient n over k for 0 <= k <= n is calculated,
     204              : !>            otherwise zero is returned.
     205              : !> \param n ...
     206              : !> \param k ...
     207              : !> \return ...
     208              : !> \date    08.03.1999
     209              : !> \author  MK
     210              : !> \version 1.0
     211              : ! **************************************************************************************************
     212    529061863 :    ELEMENTAL FUNCTION binomial(n, k) RESULT(n_over_k)
     213              :       INTEGER, INTENT(IN)                                :: n, k
     214              :       REAL(KIND=dp)                                      :: n_over_k
     215              : 
     216    529061863 :       IF ((k >= 0) .AND. (k <= n)) THEN
     217    525977677 :          n_over_k = fac(n)/(fac(n - k)*fac(k))
     218              :       ELSE
     219              :          n_over_k = 0.0_dp
     220              :       END IF
     221              : 
     222    529061863 :    END FUNCTION binomial
     223              : 
     224              : ! **************************************************************************************************
     225              : !> \brief   The generalized binomial coefficient z over k for 0 <= k <= n is calculated.
     226              : !>            (z)   z*(z-1)*...*(z-k+2)*(z-k+1)
     227              : !>            ( ) = ---------------------------
     228              : !>            (k)                 k!
     229              : !> \param z ...
     230              : !> \param k ...
     231              : !> \return ...
     232              : !> \date    11.11.2019
     233              : !> \author  FS
     234              : !> \version 1.0
     235              : ! **************************************************************************************************
     236       171640 :    ELEMENTAL FUNCTION binomial_gen(z, k) RESULT(z_over_k)
     237              :       REAL(KIND=dp), INTENT(IN)                          :: z
     238              :       INTEGER, INTENT(IN)                                :: k
     239              :       REAL(KIND=dp)                                      :: z_over_k
     240              : 
     241              :       INTEGER                                            :: i
     242              : 
     243       171640 :       IF (k >= 0) THEN
     244              :          z_over_k = 1.0_dp
     245       257460 :          DO i = 1, k
     246       257460 :             z_over_k = z_over_k*(z - i + 1)/REAL(i, dp)
     247              :          END DO
     248              :       ELSE
     249              :          z_over_k = 0.0_dp
     250              :       END IF
     251              : 
     252       171640 :    END FUNCTION binomial_gen
     253              : 
     254              : ! **************************************************************************************************
     255              : !> \brief Calculates the multinomial coefficients
     256              : !> \param n ...
     257              : !> \param k ...
     258              : !> \return ...
     259              : !> \author Ole Schuett
     260              : ! **************************************************************************************************
     261         8982 :    PURE FUNCTION multinomial(n, k) RESULT(res)
     262              :       INTEGER, INTENT(IN)                                :: n
     263              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: k
     264              :       REAL(KIND=dp)                                      :: res
     265              : 
     266              :       INTEGER                                            :: i
     267              :       REAL(KIND=dp)                                      :: denom
     268              : 
     269        71856 :       IF (ALL(k >= 0) .AND. SUM(k) == n) THEN
     270         5280 :          denom = 1.0_dp
     271        21120 :          DO i = 1, SIZE(k)
     272        21120 :             denom = denom*fac(k(i))
     273              :          END DO
     274         5280 :          res = fac(n)/denom
     275              :       ELSE
     276              :          res = 0.0_dp
     277              :       END IF
     278              : 
     279         8982 :    END FUNCTION multinomial
     280              : 
     281              : ! **************************************************************************************************
     282              : !> \brief   The rotation matrix rotmat which rotates a vector about a
     283              : !>          rotation axis defined by the vector a is build up.
     284              : !>          The rotation angle is phi (radians).
     285              : !> \param phi ...
     286              : !> \param a ...
     287              : !> \param rotmat ...
     288              : !> \date    16.10.1998
     289              : !> \author  MK
     290              : !> \version 1.0
     291              : ! **************************************************************************************************
     292        15759 :    PURE SUBROUTINE build_rotmat(phi, a, rotmat)
     293              :       REAL(KIND=dp), INTENT(IN)                          :: phi
     294              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a
     295              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(OUT)        :: rotmat
     296              : 
     297              :       REAL(KIND=dp)                                      :: cosp, cost, length_of_a, sinp
     298              :       REAL(KIND=dp), DIMENSION(3)                        :: d
     299              : 
     300        15759 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
     301              :       ! Check the length of the vector a
     302        15759 :       IF (length_of_a > eps_geo) THEN
     303              : 
     304        63036 :          d(:) = a(:)/length_of_a
     305              : 
     306        15759 :          cosp = COS(phi)
     307        15759 :          sinp = SIN(phi)
     308        15759 :          cost = 1.0_dp - cosp
     309              : 
     310        15759 :          rotmat(1, 1) = d(1)*d(1)*cost + cosp
     311        15759 :          rotmat(1, 2) = d(1)*d(2)*cost - d(3)*sinp
     312        15759 :          rotmat(1, 3) = d(1)*d(3)*cost + d(2)*sinp
     313        15759 :          rotmat(2, 1) = d(2)*d(1)*cost + d(3)*sinp
     314        15759 :          rotmat(2, 2) = d(2)*d(2)*cost + cosp
     315        15759 :          rotmat(2, 3) = d(2)*d(3)*cost - d(1)*sinp
     316        15759 :          rotmat(3, 1) = d(3)*d(1)*cost - d(2)*sinp
     317        15759 :          rotmat(3, 2) = d(3)*d(2)*cost + d(1)*sinp
     318        15759 :          rotmat(3, 3) = d(3)*d(3)*cost + cosp
     319              :       ELSE
     320            0 :          CALL unit_matrix(rotmat)
     321              :       END IF
     322              : 
     323        15759 :    END SUBROUTINE build_rotmat
     324              : 
     325              : ! **************************************************************************************************
     326              : !> \brief   Returns the determinante of the 3x3 matrix a.
     327              : !> \param a ...
     328              : !> \return ...
     329              : !> \date    13.03.2004
     330              : !> \author  MK
     331              : !> \version 1.0
     332              : ! **************************************************************************************************
     333     19082358 :    PURE FUNCTION det_3x3_1(a) RESULT(det_a)
     334              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN)         :: a
     335              :       REAL(KIND=dp)                                      :: det_a
     336              : 
     337              :       det_a = a(1, 1)*(a(2, 2)*a(3, 3) - a(2, 3)*a(3, 2)) + &
     338              :               a(1, 2)*(a(2, 3)*a(3, 1) - a(2, 1)*a(3, 3)) + &
     339     19082358 :               a(1, 3)*(a(2, 1)*a(3, 2) - a(2, 2)*a(3, 1))
     340              : 
     341     19082358 :    END FUNCTION det_3x3_1
     342              : 
     343              : ! **************************************************************************************************
     344              : !> \brief   Returns the determinante of the 3x3 matrix a given by its columns.
     345              : !> \param a1 ...
     346              : !> \param a2 ...
     347              : !> \param a3 ...
     348              : !> \return ...
     349              : !> \date    13.03.2004
     350              : !> \author  MK
     351              : !> \version 1.0
     352              : ! **************************************************************************************************
     353         5460 :    PURE FUNCTION det_3x3_2(a1, a2, a3) RESULT(det_a)
     354              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a1, a2, a3
     355              :       REAL(KIND=dp)                                      :: det_a
     356              : 
     357              :       det_a = a1(1)*(a2(2)*a3(3) - a3(2)*a2(3)) + &
     358              :               a2(1)*(a3(2)*a1(3) - a1(2)*a3(3)) + &
     359         5460 :               a3(1)*(a1(2)*a2(3) - a2(2)*a1(3))
     360              : 
     361         5460 :    END FUNCTION det_3x3_2
     362              : 
     363              : ! **************************************************************************************************
     364              : !> \brief Diagonalize the symmetric n by n matrix a using the LAPACK
     365              : !>        library. Only the upper triangle of matrix a is used.
     366              : !>        Externals (LAPACK 3.0)
     367              : !> \param a ...
     368              : !> \param eigval ...
     369              : !> \param dac ...
     370              : !> \date    29.03.1999
     371              : !> \par Variables
     372              : !>      - a       : Symmetric matrix to be diagonalized (input; upper triangle) ->
     373              : !>      -           eigenvectors of the matrix a (output).
     374              : !>      - dac     : If true, then the divide-and-conquer algorithm is applied.
     375              : !>      - eigval  : Eigenvalues of the matrix a (output).
     376              : !> \author  MK
     377              : !> \version 1.0
     378              : ! **************************************************************************************************
     379        85030 :    SUBROUTINE diamat_all(a, eigval, dac)
     380              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: a
     381              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigval
     382              :       LOGICAL, INTENT(IN), OPTIONAL                      :: dac
     383              : 
     384              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'diamat_all'
     385              : 
     386              :       INTEGER                                            :: handle, info, liwork, lwork, n, nb
     387        85030 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: iwork
     388              :       INTEGER, EXTERNAL                                  :: ilaenv
     389              :       LOGICAL                                            :: divide_and_conquer
     390        85030 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: work
     391              : 
     392              :       EXTERNAL dsyev, dsyevd
     393              : 
     394        85030 :       CALL timeset(routineN, handle)
     395              : 
     396              :       ! Get the size of the matrix a
     397        85030 :       n = SIZE(a, 1)
     398              : 
     399              :       ! Check the size of matrix a
     400        85030 :       IF (SIZE(a, 2) /= n) THEN
     401            0 :          CPABORT("Check the size of matrix a (parameter #1)")
     402              :       END IF
     403              : 
     404              :       ! Check the size of vector eigval
     405        85030 :       IF (SIZE(eigval) /= n) THEN
     406            0 :          CPABORT("The dimension of vector eigval is too small")
     407              :       END IF
     408              : 
     409              :       ! Check, if the divide-and-conquer algorithm is requested
     410              : 
     411        85030 :       IF (PRESENT(dac)) THEN
     412          217 :          divide_and_conquer = dac
     413              :       ELSE
     414              :          divide_and_conquer = .FALSE.
     415              :       END IF
     416              : 
     417              :       ! Get the optimal work storage size
     418              : 
     419          217 :       IF (divide_and_conquer) THEN
     420          217 :          lwork = 2*n**2 + 6*n + 1
     421          217 :          liwork = 5*n + 3
     422              :       ELSE
     423        84813 :          nb = ilaenv(1, "DSYTRD", "U", n, -1, -1, -1)
     424        84813 :          lwork = (nb + 2)*n
     425              :       END IF
     426              : 
     427              :       ! Allocate work storage
     428              : 
     429       255090 :       ALLOCATE (work(lwork))
     430        85030 :       IF (divide_and_conquer) THEN
     431          651 :          ALLOCATE (iwork(liwork))
     432              :       END IF
     433              : 
     434              :       ! Diagonalize the matrix a
     435              : 
     436        85030 :       info = 0
     437              :       IF (divide_and_conquer) THEN
     438          217 :          CALL dsyevd("V", "U", n, a, n, eigval, work, lwork, iwork, liwork, info)
     439              :       ELSE
     440        85009 :          CALL dsyev("V", "U", n, a, n, eigval, work, lwork, info)
     441              :       END IF
     442              : 
     443        85030 :       IF (info /= 0) THEN
     444            0 :          IF (divide_and_conquer) THEN
     445            0 :             CPABORT("The matrix diagonalization with dsyevd failed")
     446              :          ELSE
     447            0 :             CPABORT("The matrix diagonalization with dsyev failed")
     448              :          END IF
     449              :       END IF
     450              : 
     451              :       ! Release work storage
     452        85030 :       DEALLOCATE (work)
     453              : 
     454        85030 :       IF (divide_and_conquer) THEN
     455          217 :          DEALLOCATE (iwork)
     456              :       END IF
     457              : 
     458        85030 :       CALL timestop(handle)
     459              : 
     460       170060 :    END SUBROUTINE diamat_all
     461              : 
     462              : ! **************************************************************************************************
     463              : !> \brief   Returns the dihedral angle, i.e. the angle between the planes
     464              : !>          defined by the vectors (-ab,bc) and (cd,-bc).
     465              : !>          The dihedral angle is returned in radians.
     466              : !> \param ab ...
     467              : !> \param bc ...
     468              : !> \param cd ...
     469              : !> \return ...
     470              : !> \date    13.03.2004
     471              : !> \author  MK
     472              : !> \version 1.0
     473              : ! **************************************************************************************************
     474          116 :    PURE FUNCTION dihedral_angle(ab, bc, cd) RESULT(dihedral_angle_abcd)
     475              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: ab, bc, cd
     476              :       REAL(KIND=dp)                                      :: dihedral_angle_abcd
     477              : 
     478              :       REAL(KIND=dp)                                      :: det_abcd
     479              :       REAL(KIND=dp), DIMENSION(3)                        :: abc, bcd
     480              : 
     481          464 :       abc = vector_product(bc, -ab)
     482          464 :       bcd = vector_product(cd, -bc)
     483              :       ! Calculate the normal vectors of the planes
     484              :       ! defined by the points a,b,c and b,c,d
     485              : 
     486          464 :       det_abcd = det_3x3(abc, bcd, -bc)
     487          116 :       dihedral_angle_abcd = SIGN(1.0_dp, det_abcd)*angle(abc, bcd)
     488              : 
     489          116 :    END FUNCTION dihedral_angle
     490              : 
     491              : ! **************************************************************************************************
     492              : !> \brief   Return the diagonal elements of matrix a as a vector.
     493              : !> \param a ...
     494              : !> \return ...
     495              : !> \date    20.11.1998
     496              : !> \author  MK
     497              : !> \version 1.0
     498              : ! **************************************************************************************************
     499        63538 :    PURE FUNCTION get_diag(a) RESULT(a_diag)
     500              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: a
     501              :       REAL(KIND=dp), &
     502              :          DIMENSION(MIN(SIZE(a, 1), SIZE(a, 2)))          :: a_diag
     503              : 
     504              :       INTEGER                                            :: i, n
     505              : 
     506        63538 :       n = MIN(SIZE(a, 1), SIZE(a, 2))
     507              : 
     508      6369658 :       DO i = 1, n
     509      6369658 :          a_diag(i) = a(i, i)
     510              :       END DO
     511              : 
     512        63538 :    END FUNCTION get_diag
     513              : 
     514              : ! **************************************************************************************************
     515              : !> \brief   Returns the inverse of the 3 x 3 matrix a.
     516              : !> \param a ...
     517              : !> \return ...
     518              : !> \date    13.03.2004
     519              : !> \author  MK
     520              : !> \version 1.0
     521              : ! **************************************************************************************************
     522     18318003 :    PURE FUNCTION inv_3x3(a) RESULT(a_inv)
     523              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN)         :: a
     524              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: a_inv
     525              : 
     526              :       REAL(KIND=dp)                                      :: det_a
     527              : 
     528     18318003 :       det_a = 1.0_dp/det_3x3(a)
     529              : 
     530     18318003 :       a_inv(1, 1) = (a(2, 2)*a(3, 3) - a(3, 2)*a(2, 3))*det_a
     531     18318003 :       a_inv(2, 1) = (a(2, 3)*a(3, 1) - a(3, 3)*a(2, 1))*det_a
     532     18318003 :       a_inv(3, 1) = (a(2, 1)*a(3, 2) - a(3, 1)*a(2, 2))*det_a
     533              : 
     534     18318003 :       a_inv(1, 2) = (a(1, 3)*a(3, 2) - a(3, 3)*a(1, 2))*det_a
     535     18318003 :       a_inv(2, 2) = (a(1, 1)*a(3, 3) - a(3, 1)*a(1, 3))*det_a
     536     18318003 :       a_inv(3, 2) = (a(1, 2)*a(3, 1) - a(3, 2)*a(1, 1))*det_a
     537              : 
     538     18318003 :       a_inv(1, 3) = (a(1, 2)*a(2, 3) - a(2, 2)*a(1, 3))*det_a
     539     18318003 :       a_inv(2, 3) = (a(1, 3)*a(2, 1) - a(2, 3)*a(1, 1))*det_a
     540     18318003 :       a_inv(3, 3) = (a(1, 1)*a(2, 2) - a(2, 1)*a(1, 2))*det_a
     541              : 
     542     18318003 :    END FUNCTION inv_3x3
     543              : 
     544              : ! **************************************************************************************************
     545              : !> \brief returns inverse of matrix using the lapack routines DGETRF and DGETRI
     546              : !> \param a ...
     547              : !> \param info ...
     548              : ! **************************************************************************************************
     549         8443 :    SUBROUTINE invmat(a, info)
     550              :       REAL(KIND=dp), INTENT(INOUT)                       :: a(:, :)
     551              :       INTEGER, INTENT(OUT)                               :: info
     552              : 
     553              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'invmat'
     554              : 
     555              :       INTEGER                                            :: handle, lwork, n
     556         8443 :       INTEGER, ALLOCATABLE                               :: ipiv(:)
     557         8443 :       REAL(KIND=dp), ALLOCATABLE                         :: work(:)
     558              : 
     559         8443 :       CALL timeset(routineN, handle)
     560              : 
     561         8443 :       n = SIZE(a, 1)
     562         8443 :       lwork = 20*n
     563        25329 :       ALLOCATE (ipiv(n))
     564        25329 :       ALLOCATE (work(lwork))
     565         8443 :       ipiv = 0
     566         8443 :       work = 0._dp
     567         8443 :       info = 0
     568       551377 :       CALL dgetrf(n, n, a, n, ipiv, info)
     569         8443 :       IF (info == 0) THEN
     570       550793 :          CALL dgetri(n, a, n, ipiv, work, lwork, info)
     571              :       END IF
     572         8443 :       DEALLOCATE (ipiv, work)
     573              : 
     574         8443 :       CALL timestop(handle)
     575              : 
     576         8443 :    END SUBROUTINE invmat
     577              : 
     578              : ! **************************************************************************************************
     579              : !> \brief returns inverse of real symmetric, positive definite matrix
     580              : !> \param a matrix
     581              : !> \param potrf if cholesky decomposition of a was already done using dpotrf.
     582              : !>        If not given, cholesky decomposition of a will be done before inversion.
     583              : !> \param uplo indicating if the upper or lower triangle of a is stored.
     584              : !> \author Dorothea Golze [02.2015]
     585              : ! **************************************************************************************************
     586         3026 :    SUBROUTINE invmat_symm(a, potrf, uplo)
     587              :       REAL(KIND=dp), INTENT(INOUT)                       :: a(:, :)
     588              :       LOGICAL, INTENT(IN), OPTIONAL                      :: potrf
     589              :       CHARACTER(LEN=1), INTENT(IN), OPTIONAL             :: uplo
     590              : 
     591              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'invmat_symm'
     592              : 
     593              :       CHARACTER(LEN=1)                                   :: myuplo
     594              :       INTEGER                                            :: handle, info, n
     595              :       LOGICAL                                            :: do_potrf
     596              : 
     597         3026 :       CALL timeset(routineN, handle)
     598              : 
     599         3026 :       do_potrf = .TRUE.
     600         3026 :       IF (PRESENT(potrf)) do_potrf = potrf
     601              : 
     602         3026 :       myuplo = 'U'
     603         3026 :       IF (PRESENT(uplo)) myuplo = uplo
     604              : 
     605         3026 :       n = SIZE(a, 1)
     606         3026 :       info = 0
     607              : 
     608              :       ! do cholesky decomposition
     609         3026 :       IF (do_potrf) THEN
     610         4009 :          CALL dpotrf(myuplo, n, a, n, info)
     611         2809 :          IF (info /= 0) CPABORT("DPOTRF failed")
     612              :       END IF
     613              : 
     614              :       ! do inversion using the cholesky decomposition
     615         4226 :       CALL dpotri(myuplo, n, a, n, info)
     616         3026 :       IF (info /= 0) CPABORT("Matrix inversion failed")
     617              : 
     618              :       ! complete the matrix
     619         3026 :       IF ((myuplo == "U") .OR. (myuplo == "u")) THEN
     620         3026 :          CALL symmetrize_matrix(a, "upper_to_lower")
     621              :       ELSE
     622            0 :          CALL symmetrize_matrix(a, "lower_to_upper")
     623              :       END IF
     624              : 
     625         3026 :       CALL timestop(handle)
     626              : 
     627         3026 :    END SUBROUTINE invmat_symm
     628              : 
     629              : ! **************************************************************************************************
     630              : !> \brief  Compute the inverse of the n by n real matrix a using the LAPACK
     631              : !>         library
     632              : !> \param a ...
     633              : !> \param a_inverse ...
     634              : !> \param eval_error ...
     635              : !> \param option ...
     636              : !> \param improve ...
     637              : !> \date   23.03.1999
     638              : !> \par Variables
     639              : !>       - a        : Real matrix to be inverted (input).
     640              : !>       - a_inverse: Inverse of the matrix a (output).
     641              : !>       - a_lu     : LU factorization of matrix a.
     642              : !>       - a_norm   : Norm of matrix a.
     643              : !>       - error    : Estimated error of the inversion.
     644              : !>       - r_cond   : Reciprocal condition number of the matrix a.
     645              : !>       - trans    : "N" => invert a
     646              : !>       -            "T" => invert transpose(a)
     647              : !> \author MK
     648              : !> \version 1.0
     649              : !> \note NB add improve argument, used to disable call to dgerfs
     650              : ! **************************************************************************************************
     651         4676 :    SUBROUTINE invert_matrix_d(a, a_inverse, eval_error, option, improve)
     652              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: a
     653              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: a_inverse
     654              :       REAL(KIND=dp), INTENT(OUT)                         :: eval_error
     655              :       CHARACTER(LEN=1), INTENT(IN), OPTIONAL             :: option
     656              :       LOGICAL, INTENT(IN), OPTIONAL                      :: improve
     657              : 
     658              :       CHARACTER(LEN=1)                                   :: norm, trans
     659              :       CHARACTER(LEN=default_string_length)               :: message
     660              :       INTEGER                                            :: info, iter, n
     661         4676 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: ipiv, iwork
     662              :       LOGICAL                                            :: do_improve
     663              :       REAL(KIND=dp)                                      :: a_norm, old_eval_error, r_cond
     664         4676 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: berr, ferr, work
     665         4676 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: a_lu, b
     666              :       REAL(KIND=dp), EXTERNAL                            :: dlange
     667              : 
     668              :       EXTERNAL dgecon, dgerfs, dgetrf, dgetrs
     669              : 
     670              :       ! Check for optional parameter
     671         4676 :       IF (PRESENT(option)) THEN
     672          524 :          trans = option
     673              :       ELSE
     674         4152 :          trans = "N"
     675              :       END IF
     676              : 
     677         4676 :       IF (PRESENT(improve)) THEN
     678          648 :          do_improve = improve
     679              :       ELSE
     680              :          do_improve = .TRUE.
     681              :       END IF
     682              : 
     683              :       ! Get the dimension of matrix a
     684         4676 :       n = SIZE(a, 1)
     685              : 
     686              :       ! Check array dimensions
     687         4676 :       IF (n == 0) THEN
     688            0 :          CPABORT("Matrix to be inverted of zero size")
     689              :       END IF
     690              : 
     691         4676 :       IF (n /= SIZE(a, 2)) THEN
     692            0 :          CPABORT("Check the array bounds of parameter #1")
     693              :       END IF
     694              : 
     695         4676 :       IF ((n /= SIZE(a_inverse, 1)) .OR. &
     696              :           (n /= SIZE(a_inverse, 2))) THEN
     697            0 :          CPABORT("Check the array bounds of parameter #2")
     698              :       END IF
     699              : 
     700              :       ! Allocate work storage
     701        18704 :       ALLOCATE (a_lu(n, n))
     702        14028 :       ALLOCATE (b(n, n))
     703        14028 :       ALLOCATE (berr(n))
     704         9352 :       ALLOCATE (ferr(n))
     705        14028 :       ALLOCATE (ipiv(n))
     706         9352 :       ALLOCATE (iwork(n))
     707        14028 :       ALLOCATE (work(4*n))
     708              : 
     709      2932192 :       a_lu(1:n, 1:n) = a(1:n, 1:n)
     710              : 
     711              :       ! Compute the LU factorization of the matrix a
     712         4676 :       CALL dgetrf(n, n, a_lu, n, ipiv, info)
     713              : 
     714         4676 :       IF (info /= 0) THEN
     715            0 :          CPABORT("The LU factorization in dgetrf failed")
     716              :       END IF
     717              : 
     718              :       ! Compute the norm of the matrix a
     719              : 
     720         4676 :       IF (trans == "N") THEN
     721         4426 :          norm = '1'
     722              :       ELSE
     723          250 :          norm = 'I'
     724              :       END IF
     725              : 
     726         4676 :       a_norm = dlange(norm, n, n, a, n, work)
     727              : 
     728              :       ! Compute the reciprocal of the condition number of a
     729              : 
     730         4676 :       CALL dgecon(norm, n, a_lu, n, a_norm, r_cond, work, iwork, info)
     731              : 
     732         4676 :       IF (info /= 0) THEN
     733            0 :          CPABORT("The computation of the condition number in dgecon failed")
     734              :       END IF
     735              : 
     736         4676 :       IF (r_cond < EPSILON(0.0_dp)) THEN
     737            0 :          WRITE (message, "(A,ES10.3)") "R_COND =", r_cond
     738              :          CALL cp_abort(__LOCATION__, &
     739              :                        "Bad condition number "//TRIM(message)//" (smaller than the machine "// &
     740            0 :                        "working precision)")
     741              :       END IF
     742              : 
     743              :       ! Solve a system of linear equations using the LU factorization computed by dgetrf
     744              : 
     745         4676 :       CALL unit_matrix(a_inverse)
     746              : 
     747         4676 :       CALL dgetrs(trans, n, n, a_lu, n, ipiv, a_inverse, n, info)
     748              : 
     749         4676 :       IF (info /= 0) THEN
     750            0 :          CPABORT("Solving the system of linear equations in dgetrs failed")
     751              :       END IF
     752              : 
     753              :       ! Improve the computed solution iteratively
     754         4676 :       CALL unit_matrix(b) ! Initialize right-hand sides
     755              : 
     756         4676 :       eval_error = 0.0_dp
     757              : 
     758         4676 :       IF (do_improve) THEN
     759        11948 :          DO iter = 1, 10
     760              : 
     761              :             CALL dgerfs(trans, n, n, a, n, a_lu, n, ipiv, b, n, a_inverse, n, ferr, berr, &
     762        11676 :                         work, iwork, info)
     763              : 
     764        11676 :             IF (info /= 0) THEN
     765            0 :                CPABORT("Improving the computed solution in dgerfs failed")
     766              :             END IF
     767              : 
     768        11676 :             old_eval_error = eval_error
     769       230818 :             eval_error = MAXVAL(ferr)
     770              : 
     771        11948 :             IF (ABS(eval_error - old_eval_error) <= EPSILON(1.0_dp)) EXIT
     772              : 
     773              :          END DO
     774              :       END IF
     775              : 
     776              :       ! Release work storage
     777         4676 :       DEALLOCATE (work)
     778         4676 :       DEALLOCATE (iwork)
     779         4676 :       DEALLOCATE (ipiv)
     780         4676 :       DEALLOCATE (ferr)
     781         4676 :       DEALLOCATE (berr)
     782         4676 :       DEALLOCATE (b)
     783         4676 :       DEALLOCATE (a_lu)
     784              : 
     785         4676 :    END SUBROUTINE invert_matrix_d
     786              : 
     787              : ! **************************************************************************************************
     788              : !> \brief  Compute the inverse of the n by n complex matrix a using the LAPACK
     789              : !>         library
     790              : !> \param a ...
     791              : !> \param a_inverse ...
     792              : !> \param eval_error ...
     793              : !> \param option ...
     794              : !> \date   08.06.2009
     795              : !> \par Variables
     796              : !>       - a        : Complex matrix to be inverted (input).
     797              : !>       - a_inverse: Inverse of the matrix a (output).
     798              : !>       - a_lu     : LU factorization of matrix a.
     799              : !>       - a_norm   : Norm of matrix a.
     800              : !>       - error    : Estimated error of the inversion.
     801              : !>       - r_cond   : Reciprocal condition number of the matrix a.
     802              : !>       - trans    : "N" => invert a
     803              : !>       -            "T" => invert transpose(a)
     804              : !> \author MK
     805              : !> \version 1.0
     806              : ! **************************************************************************************************
     807            0 :    SUBROUTINE invert_matrix_z(a, a_inverse, eval_error, option)
     808              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: a
     809              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: a_inverse
     810              :       REAL(KIND=dp), INTENT(OUT)                         :: eval_error
     811              :       CHARACTER(LEN=1), INTENT(IN), OPTIONAL             :: option
     812              : 
     813              :       CHARACTER(LEN=1)                                   :: norm, trans
     814              :       CHARACTER(LEN=default_string_length)               :: message
     815            0 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:)        :: work
     816            0 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: a_lu, b
     817              :       INTEGER                                            :: info, iter, n
     818            0 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: ipiv
     819              :       REAL(KIND=dp)                                      :: a_norm, old_eval_error, r_cond
     820            0 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: berr, ferr, rwork
     821              :       REAL(KIND=dp), EXTERNAL                            :: zlange
     822              : 
     823              :       EXTERNAL zgecon, zgerfs, zgetrf, zgetrs
     824              : 
     825              :       ! Check for optional parameter
     826            0 :       IF (PRESENT(option)) THEN
     827            0 :          trans = option
     828              :       ELSE
     829            0 :          trans = "N"
     830              :       END IF
     831              : 
     832              :       ! Get the dimension of matrix a
     833            0 :       n = SIZE(a, 1)
     834              : 
     835              :       ! Check array dimensions
     836            0 :       IF (n == 0) THEN
     837            0 :          CPABORT("Matrix to be inverted of zero size")
     838              :       END IF
     839              : 
     840            0 :       IF (n /= SIZE(a, 2)) THEN
     841            0 :          CPABORT("Check the array bounds of parameter #1")
     842              :       END IF
     843              : 
     844            0 :       IF ((n /= SIZE(a_inverse, 1)) .OR. &
     845              :           (n /= SIZE(a_inverse, 2))) THEN
     846            0 :          CPABORT("Check the array bounds of parameter #2")
     847              :       END IF
     848              : 
     849              :       ! Allocate work storage
     850            0 :       ALLOCATE (a_lu(n, n))
     851            0 :       ALLOCATE (b(n, n))
     852            0 :       ALLOCATE (berr(n))
     853            0 :       ALLOCATE (ferr(n))
     854            0 :       ALLOCATE (ipiv(n))
     855            0 :       ALLOCATE (rwork(2*n))
     856            0 :       ALLOCATE (work(2*n))
     857              : 
     858            0 :       a_lu(1:n, 1:n) = a(1:n, 1:n)
     859              : 
     860              :       ! Compute the LU factorization of the matrix a
     861            0 :       CALL zgetrf(n, n, a_lu, n, ipiv, info)
     862              : 
     863            0 :       IF (info /= 0) THEN
     864            0 :          CPABORT("The LU factorization in dgetrf failed")
     865              :       END IF
     866              : 
     867              :       ! Compute the norm of the matrix a
     868              : 
     869            0 :       IF (trans == "N") THEN
     870            0 :          norm = '1'
     871              :       ELSE
     872            0 :          norm = 'I'
     873              :       END IF
     874              : 
     875            0 :       a_norm = zlange(norm, n, n, a, n, work)
     876              : 
     877              :       ! Compute the reciprocal of the condition number of a
     878              : 
     879            0 :       CALL zgecon(norm, n, a_lu, n, a_norm, r_cond, work, rwork, info)
     880              : 
     881            0 :       IF (info /= 0) THEN
     882            0 :          CPABORT("The computation of the condition number in dgecon failed")
     883              :       END IF
     884              : 
     885            0 :       IF (r_cond < EPSILON(0.0_dp)) THEN
     886            0 :          WRITE (message, "(A,ES10.3)") "R_COND =", r_cond
     887              :          CALL cp_abort(__LOCATION__, &
     888              :                        "Bad condition number "//TRIM(message)//" (smaller than the machine "// &
     889            0 :                        "working precision)")
     890              :       END IF
     891              : 
     892              :       ! Solve a system of linear equations using the LU factorization computed by dgetrf
     893              : 
     894            0 :       CALL unit_matrix(a_inverse)
     895              : 
     896            0 :       CALL zgetrs(trans, n, n, a_lu, n, ipiv, a_inverse, n, info)
     897              : 
     898            0 :       IF (info /= 0) THEN
     899            0 :          CPABORT("Solving the system of linear equations in dgetrs failed")
     900              :       END IF
     901              : 
     902              :       ! Improve the computed solution iteratively
     903            0 :       CALL unit_matrix(b) ! Initialize right-hand sides
     904              : 
     905            0 :       eval_error = 0.0_dp
     906              : 
     907            0 :       DO iter = 1, 10
     908              : 
     909              :          CALL zgerfs(trans, n, n, a, n, a_lu, n, ipiv, b, n, a_inverse, n, ferr, berr, &
     910            0 :                      work, rwork, info)
     911              : 
     912            0 :          IF (info /= 0) THEN
     913            0 :             CPABORT("Improving the computed solution in dgerfs failed")
     914              :          END IF
     915              : 
     916            0 :          old_eval_error = eval_error
     917            0 :          eval_error = MAXVAL(ferr)
     918              : 
     919            0 :          IF (ABS(eval_error - old_eval_error) <= EPSILON(1.0_dp)) EXIT
     920              : 
     921              :       END DO
     922              : 
     923              :       ! Release work storage
     924            0 :       DEALLOCATE (work)
     925            0 :       DEALLOCATE (rwork)
     926            0 :       DEALLOCATE (ipiv)
     927            0 :       DEALLOCATE (ferr)
     928            0 :       DEALLOCATE (berr)
     929            0 :       DEALLOCATE (b)
     930            0 :       DEALLOCATE (a_lu)
     931              : 
     932            0 :    END SUBROUTINE invert_matrix_z
     933              : 
     934              : ! **************************************************************************************************
     935              : !> \brief returns the pseudoinverse of a real, square matrix using singular
     936              : !>         value decomposition
     937              : !> \param a matrix a
     938              : !> \param a_pinverse pseudoinverse of matrix a
     939              : !> \param rskip parameter for setting small singular values to zero
     940              : !> \param determinant determinant of matrix a (optional output)
     941              : !> \param sval array holding singular values of matrix a (optional output)
     942              : !> \author Dorothea Golze [02.2015]
     943              : ! **************************************************************************************************
     944          861 :    SUBROUTINE get_pseudo_inverse_svd(a, a_pinverse, rskip, determinant, sval)
     945              :       REAL(KIND=dp), DIMENSION(:, :)                     :: a, a_pinverse
     946              :       REAL(KIND=dp), INTENT(IN)                          :: rskip
     947              :       REAL(KIND=dp), INTENT(OUT), OPTIONAL               :: determinant
     948              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT), &
     949              :          OPTIONAL, POINTER                               :: sval
     950              : 
     951              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'get_pseudo_inverse_svd'
     952              : 
     953              :       INTEGER                                            :: handle, i, info, lwork, n
     954          861 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: iwork
     955          861 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: sig, work
     956          861 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: sig_plus, temp_mat, u, vt
     957              : 
     958          861 :       CALL timeset(routineN, handle)
     959              : 
     960          861 :       n = SIZE(a, 1)
     961        12054 :       ALLOCATE (u(n, n), vt(n, n), sig(n), sig_plus(n, n), iwork(8*n), work(1), temp_mat(n, n))
     962          861 :       u(:, :) = 0.0_dp
     963          861 :       vt(:, :) = 0.0_dp
     964          861 :       sig(:) = 0.0_dp
     965          861 :       sig_plus = 0.0_dp
     966          861 :       work = 0.0_dp
     967          861 :       iwork = 0
     968          861 :       IF (PRESENT(determinant)) determinant = 1.0_dp
     969              : 
     970              :       ! work size query
     971          861 :       lwork = -1
     972              :       CALL dgesdd('A', n, n, a(1, 1), n, sig(1), u(1, 1), n, vt(1, 1), n, work(1), &
     973          861 :                   lwork, iwork(1), info)
     974              : 
     975          861 :       IF (info /= 0) THEN
     976            0 :          CPABORT("ERROR in DGESDD: Could not retrieve work array sizes")
     977              :       END IF
     978          861 :       lwork = INT(work(1))
     979          861 :       DEALLOCATE (work)
     980         2583 :       ALLOCATE (work(lwork))
     981              : 
     982              :       ! do SVD
     983              :       CALL dgesdd('A', n, n, a(1, 1), n, sig(1), u(1, 1), n, vt(1, 1), n, work(1), &
     984          861 :                   lwork, iwork(1), info)
     985              : 
     986          861 :       IF (info /= 0) THEN
     987            0 :          CPABORT("SVD failed")
     988              :       END IF
     989              : 
     990          861 :       IF (PRESENT(sval)) THEN
     991            0 :          CPASSERT(.NOT. ASSOCIATED(sval))
     992            0 :          ALLOCATE (sval(n))
     993            0 :          sval(:) = sig
     994              :       END IF
     995              : 
     996              :       ! set singular values that are too small to zero
     997         5141 :       DO i = 1, n
     998        63417 :          IF (sig(i) > rskip*MAXVAL(sig)) THEN
     999         4280 :             IF (PRESENT(determinant)) &
    1000            0 :                determinant = determinant*sig(i)
    1001         4280 :             sig_plus(i, i) = 1._dp/sig(i)
    1002              :          ELSE
    1003            0 :             sig_plus(i, i) = 0.0_dp
    1004              :          END IF
    1005              :       END DO
    1006              : 
    1007              :       ! build pseudoinverse: V*sig_plus*UT
    1008          861 :       CALL dgemm("N", "T", n, n, n, 1._dp, sig_plus, n, u, n, 0._dp, temp_mat, n)
    1009          861 :       CALL dgemm("T", "N", n, n, n, 1._dp, vt, n, temp_mat, n, 0._dp, a_pinverse, n)
    1010              : 
    1011          861 :       DEALLOCATE (u, vt, sig, iwork, work, sig_plus, temp_mat)
    1012              : 
    1013          861 :       CALL timestop(handle)
    1014              : 
    1015          861 :    END SUBROUTINE get_pseudo_inverse_svd
    1016              : 
    1017              : ! **************************************************************************************************
    1018              : !> \brief returns the pseudoinverse of a real, symmetric and positive definite
    1019              : !>        matrix using diagonalization.
    1020              : !> \param a matrix a
    1021              : !> \param a_pinverse pseudoinverse of matrix a
    1022              : !> \param rskip parameter for setting small eigenvalues to zero
    1023              : !> \author Dorothea Golze [02.2015]
    1024              : ! **************************************************************************************************
    1025         1177 :    SUBROUTINE get_pseudo_inverse_diag(a, a_pinverse, rskip)
    1026              :       REAL(KIND=dp), DIMENSION(:, :)                     :: a, a_pinverse
    1027              :       REAL(KIND=dp), INTENT(IN)                          :: rskip
    1028              : 
    1029              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'get_pseudo_inverse_diag'
    1030              : 
    1031              :       INTEGER                                            :: handle, i, info, lwork, n
    1032         1177 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eig, work
    1033         1177 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: dinv, temp_mat
    1034              : 
    1035         1177 :       CALL timeset(routineN, handle)
    1036              : 
    1037         1177 :       info = 0
    1038         1177 :       n = SIZE(a, 1)
    1039         9416 :       ALLOCATE (dinv(n, n), eig(n), work(1), temp_mat(n, n))
    1040         1177 :       dinv(:, :) = 0.0_dp
    1041         1177 :       eig(:) = 0.0_dp
    1042         1177 :       work(:) = 0.0_dp
    1043         1177 :       temp_mat = 0.0_dp
    1044              : 
    1045              :       ! work size query
    1046         1177 :       lwork = -1
    1047         1177 :       CALL dsyev('V', 'U', n, a, n, eig(1), work(1), lwork, info)
    1048         1177 :       IF (info /= 0) THEN
    1049            0 :          CPABORT("ERROR in DSYEV: Could not retrieve work array sizes")
    1050              :       END IF
    1051         1177 :       lwork = INT(work(1))
    1052         1177 :       DEALLOCATE (work)
    1053         3531 :       ALLOCATE (work(lwork))
    1054         1177 :       work = 0.0_dp
    1055              : 
    1056              :       ! get eigenvalues and eigenvectors
    1057         1177 :       CALL dsyev('V', 'U', n, a, n, eig(1), work(1), lwork, info)
    1058              : 
    1059         1177 :       IF (info /= 0) THEN
    1060            0 :          CPABORT("Matrix diagonalization failed")
    1061              :       END IF
    1062              : 
    1063              :       ! set eigenvalues that are too small to zero
    1064        15283 :       DO i = 1, n
    1065       189031 :          IF (eig(i) > rskip*MAXVAL(eig)) THEN
    1066        12616 :             dinv(i, i) = 1.0_dp/eig(i)
    1067              :          ELSE
    1068         1490 :             dinv(i, i) = 0._dp
    1069              :          END IF
    1070              :       END DO
    1071              : 
    1072              :       ! build pseudoinverse: U*dinv*UT
    1073         1177 :       CALL dgemm("N", "T", n, n, n, 1._dp, dinv, n, a, n, 0._dp, temp_mat, n)
    1074         1177 :       CALL dgemm("N", "N", n, n, n, 1._dp, a, n, temp_mat, n, 0._dp, a_pinverse, n)
    1075              : 
    1076         1177 :       DEALLOCATE (eig, work, dinv, temp_mat)
    1077              : 
    1078         1177 :       CALL timestop(handle)
    1079              : 
    1080         1177 :    END SUBROUTINE get_pseudo_inverse_diag
    1081              : 
    1082              : ! **************************************************************************************************
    1083              : !> \brief  Reflection of the vector a through a mirror plane defined by the
    1084              : !>         normal vector b. The reflected vector a is stored in a_mirror.
    1085              : !> \param a ...
    1086              : !> \param b ...
    1087              : !> \return ...
    1088              : !> \date    16.10.1998
    1089              : !> \author  MK
    1090              : !> \version 1.0
    1091              : ! **************************************************************************************************
    1092        19487 :    PURE FUNCTION reflect_vector(a, b) RESULT(a_mirror)
    1093              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a, b
    1094              :       REAL(KIND=dp), DIMENSION(3)                        :: a_mirror
    1095              : 
    1096              :       REAL(KIND=dp)                                      :: length_of_b, scapro
    1097              :       REAL(KIND=dp), DIMENSION(3)                        :: d
    1098              : 
    1099        19487 :       length_of_b = SQRT(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
    1100              : 
    1101        19487 :       IF (length_of_b > eps_geo) THEN
    1102              : 
    1103        77948 :          d(:) = b(:)/length_of_b
    1104              : 
    1105              :          ! Calculate the mirror image a_mirror of the vector a
    1106        19487 :          scapro = a(1)*d(1) + a(2)*d(2) + a(3)*d(3)
    1107              : 
    1108        77948 :          a_mirror(:) = a(:) - 2.0_dp*scapro*d(:)
    1109              : 
    1110              :       ELSE
    1111              : 
    1112            0 :          a_mirror(:) = 0.0_dp
    1113              : 
    1114              :       END IF
    1115              : 
    1116        19487 :    END FUNCTION reflect_vector
    1117              : 
    1118              : ! **************************************************************************************************
    1119              : !> \brief   Rotation of the vector a about an rotation axis defined by the
    1120              : !>          vector b. The rotation angle is phi (radians). The rotated vector
    1121              : !>          a is stored in a_rot.
    1122              : !> \param a ...
    1123              : !> \param phi ...
    1124              : !> \param b ...
    1125              : !> \return ...
    1126              : !> \date    16.10.1998
    1127              : !> \author  MK
    1128              : !> \version 1.0
    1129              : ! **************************************************************************************************
    1130         4098 :    PURE FUNCTION rotate_vector(a, phi, b) RESULT(a_rot)
    1131              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a
    1132              :       REAL(KIND=dp), INTENT(IN)                          :: phi
    1133              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: b
    1134              :       REAL(KIND=dp), DIMENSION(3)                        :: a_rot
    1135              : 
    1136              :       REAL(KIND=dp)                                      :: length_of_b
    1137              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: rotmat
    1138              : 
    1139         4098 :       length_of_b = SQRT(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
    1140         4098 :       IF (length_of_b > eps_geo) THEN
    1141              : 
    1142              :          ! Build up the rotation matrix rotmat
    1143         3964 :          CALL build_rotmat(phi, b, rotmat)
    1144              : 
    1145              :          ! Rotate the vector a by phi about the axis defined by vector b
    1146        63424 :          a_rot(:) = MATMUL(rotmat, a)
    1147              : 
    1148              :       ELSE
    1149              : 
    1150          536 :          a_rot(:) = 0.0_dp
    1151              : 
    1152              :       END IF
    1153              : 
    1154         4098 :    END FUNCTION rotate_vector
    1155              : 
    1156              : ! **************************************************************************************************
    1157              : !> \brief   Set the diagonal elements of matrix a to b.
    1158              : !> \param a ...
    1159              : !> \param b ...
    1160              : !> \date    20.11.1998
    1161              : !> \author  MK
    1162              : !> \version 1.0
    1163              : ! **************************************************************************************************
    1164        43888 :    PURE SUBROUTINE set_diag_scalar_d(a, b)
    1165              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: a
    1166              :       REAL(KIND=dp), INTENT(IN)                          :: b
    1167              : 
    1168              :       INTEGER                                            :: i, n
    1169              : 
    1170        43888 :       n = MIN(SIZE(a, 1), SIZE(a, 2))
    1171       244259 :       DO i = 1, n
    1172       244259 :          a(i, i) = b
    1173              :       END DO
    1174              : 
    1175        43888 :    END SUBROUTINE set_diag_scalar_d
    1176              : 
    1177              : ! **************************************************************************************************
    1178              : !> \brief ...
    1179              : !> \param a ...
    1180              : !> \param b ...
    1181              : ! **************************************************************************************************
    1182            0 :    PURE SUBROUTINE set_diag_scalar_z(a, b)
    1183              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(INOUT)   :: a
    1184              :       COMPLEX(KIND=dp), INTENT(IN)                       :: b
    1185              : 
    1186              :       INTEGER                                            :: i, n
    1187              : 
    1188            0 :       n = MIN(SIZE(a, 1), SIZE(a, 2))
    1189            0 :       DO i = 1, n
    1190            0 :          a(i, i) = b
    1191              :       END DO
    1192              : 
    1193            0 :    END SUBROUTINE set_diag_scalar_z
    1194              : 
    1195              : ! **************************************************************************************************
    1196              : !> \brief   Symmetrize the matrix a.
    1197              : !> \param a ...
    1198              : !> \param option ...
    1199              : !> \date    16.10.1998
    1200              : !> \author  MK
    1201              : !> \version 1.0
    1202              : ! **************************************************************************************************
    1203        19696 :    SUBROUTINE symmetrize_matrix(a, option)
    1204              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: a
    1205              :       CHARACTER(LEN=*), INTENT(IN)                       :: option
    1206              : 
    1207              :       INTEGER                                            :: i, n
    1208              : 
    1209        19696 :       n = MIN(SIZE(a, 1), SIZE(a, 2))
    1210              : 
    1211        19696 :       IF (option == "lower_to_upper") THEN
    1212            0 :          DO i = 1, n - 1
    1213            0 :             a(i, i + 1:n) = a(i + 1:n, i)
    1214              :          END DO
    1215        19696 :       ELSE IF (option == "upper_to_lower") THEN
    1216       522697 :          DO i = 1, n - 1
    1217     29908250 :             a(i + 1:n, i) = a(i, i + 1:n)
    1218              :          END DO
    1219           36 :       ELSE IF (option == "anti_lower_to_upper") THEN
    1220            0 :          DO i = 1, n - 1
    1221            0 :             a(i, i + 1:n) = -a(i + 1:n, i)
    1222              :          END DO
    1223           36 :       ELSE IF (option == "anti_upper_to_lower") THEN
    1224          564 :          DO i = 1, n - 1
    1225         4716 :             a(i + 1:n, i) = -a(i, i + 1:n)
    1226              :          END DO
    1227              :       ELSE
    1228            0 :          CPABORT("Invalid option <"//TRIM(option)//"> was specified for parameter #2")
    1229              :       END IF
    1230              : 
    1231        19696 :    END SUBROUTINE symmetrize_matrix
    1232              : 
    1233              : ! **************************************************************************************************
    1234              : !> \brief   Set the matrix a to be a unit matrix.
    1235              : !> \param a ...
    1236              : !> \date    16.10.1998
    1237              : !> \author  MK
    1238              : !> \version 1.0
    1239              : ! **************************************************************************************************
    1240        43888 :    PURE SUBROUTINE unit_matrix_d(a)
    1241              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: a
    1242              : 
    1243      7727648 :       a(:, :) = 0.0_dp
    1244        43888 :       CALL set_diag(a, 1.0_dp)
    1245              : 
    1246        43888 :    END SUBROUTINE unit_matrix_d
    1247              : 
    1248              : ! **************************************************************************************************
    1249              : !> \brief ...
    1250              : !> \param a ...
    1251              : ! **************************************************************************************************
    1252            0 :    PURE SUBROUTINE unit_matrix_z(a)
    1253              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(INOUT)   :: a
    1254              : 
    1255            0 :       a(:, :) = (0.0_dp, 0.0_dp)
    1256            0 :       CALL set_diag(a, (1.0_dp, 0.0_dp))
    1257              : 
    1258            0 :    END SUBROUTINE unit_matrix_z
    1259              : 
    1260              : ! **************************************************************************************************
    1261              : !> \brief   Calculation of the vector product c = a x b.
    1262              : !> \param a ...
    1263              : !> \param b ...
    1264              : !> \return ...
    1265              : !> \date    16.10.1998
    1266              : !> \author  MK
    1267              : !> \version 1.0
    1268              : ! **************************************************************************************************
    1269         8011 :    PURE FUNCTION vector_product(a, b) RESULT(c)
    1270              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a, b
    1271              :       REAL(KIND=dp), DIMENSION(3)                        :: c
    1272              : 
    1273         8011 :       c(1) = a(2)*b(3) - a(3)*b(2)
    1274         8011 :       c(2) = a(3)*b(1) - a(1)*b(3)
    1275         8011 :       c(3) = a(1)*b(2) - a(2)*b(1)
    1276              : 
    1277         8011 :    END FUNCTION vector_product
    1278              : 
    1279              : ! **************************************************************************************************
    1280              : !> \brief computes the greatest common divisor of two number
    1281              : !> \param a ...
    1282              : !> \param b ...
    1283              : !> \return ...
    1284              : !> \author Joost VandeVondele
    1285              : ! **************************************************************************************************
    1286      1070975 :    ELEMENTAL FUNCTION gcd(a, b)
    1287              :       INTEGER, INTENT(IN)                                :: a, b
    1288              :       INTEGER                                            :: gcd
    1289              : 
    1290              :       INTEGER                                            :: aa, ab, l, rem, s
    1291              : 
    1292      1070975 :       aa = ABS(a)
    1293      1070975 :       ab = ABS(b)
    1294      1070975 :       IF (aa < ab) THEN
    1295              :          s = aa
    1296              :          l = ab
    1297              :       ELSE
    1298      1047227 :          s = ab
    1299      1047227 :          l = aa
    1300              :       END IF
    1301      1070975 :       IF (s /= 0) THEN
    1302              :          DO
    1303      1071113 :             rem = MOD(l, s)
    1304      1071113 :             IF (rem == 0) EXIT
    1305              :             l = s
    1306      1070975 :             s = rem
    1307              :          END DO
    1308              :          GCD = s
    1309              :       ELSE
    1310              :          GCD = l
    1311              :       END IF
    1312      1070975 :    END FUNCTION gcd
    1313              : 
    1314              : ! **************************************************************************************************
    1315              : !> \brief computes the least common multiplier of two numbers
    1316              : !> \param a ...
    1317              : !> \param b ...
    1318              : !> \return ...
    1319              : !> \author Joost VandeVondele
    1320              : ! **************************************************************************************************
    1321       511454 :    ELEMENTAL FUNCTION lcm(a, b)
    1322              :       INTEGER, INTENT(IN)                                :: a, b
    1323              :       INTEGER                                            :: lcm
    1324              : 
    1325              :       INTEGER                                            :: tmp
    1326              : 
    1327       511454 :       tmp = gcd(a, b)
    1328       511454 :       IF (tmp == 0) THEN
    1329              :          lcm = 0
    1330              :       ELSE
    1331              :          ! could still overflow if the true lcm is larger than maxint
    1332       511454 :          lcm = ABS((a/tmp)*b)
    1333              :       END IF
    1334       511454 :    END FUNCTION lcm
    1335              : 
    1336              : ! **************************************************************************************************
    1337              : !> \brief computes the exponential integral
    1338              : !>      Ei(x) = Int(exp(-x*t)/t,t=1..infinity)  x>0
    1339              : !> \param x ...
    1340              : !> \return ...
    1341              : !> \author JGH (adapted from Numerical recipies)
    1342              : ! **************************************************************************************************
    1343            0 :    FUNCTION ei(x)
    1344              :       REAL(dp)                                           :: x, ei
    1345              : 
    1346              :       INTEGER, PARAMETER                                 :: maxit = 100
    1347              :       REAL(dp), PARAMETER                                :: eps = EPSILON(0.0_dp), &
    1348              :                                                             fpmin = TINY(0.0_dp)
    1349              : 
    1350              :       INTEGER                                            :: k
    1351              :       REAL(dp)                                           :: fact, prev, sum1, term
    1352              : 
    1353            0 :       IF (x <= 0._dp) THEN
    1354            0 :          CPABORT("Invalid argument")
    1355              :       END IF
    1356              : 
    1357            0 :       IF (x < fpmin) THEN
    1358            0 :          ei = LOG(x) + euler
    1359            0 :       ELSE IF (x <= -LOG(EPS)) THEN
    1360              :          sum1 = 0._dp
    1361              :          fact = 1._dp
    1362            0 :          DO k = 1, maxit
    1363            0 :             fact = fact*x/REAL(k, dp)
    1364            0 :             term = fact/REAL(k, dp)
    1365            0 :             sum1 = sum1 + term
    1366            0 :             IF (term < eps*sum1) EXIT
    1367              :          END DO
    1368            0 :          ei = sum1 + LOG(x) + euler
    1369              :       ELSE
    1370              :          sum1 = 0._dp
    1371              :          term = 1._dp
    1372            0 :          DO k = 1, maxit
    1373            0 :             prev = term
    1374            0 :             term = term*REAL(k, dp)/x
    1375            0 :             IF (term < eps) EXIT
    1376            0 :             IF (term < prev) THEN
    1377            0 :                sum1 = sum1 + term
    1378              :             ELSE
    1379            0 :                sum1 = sum1 - prev
    1380            0 :                EXIT
    1381              :             END IF
    1382              :          END DO
    1383            0 :          ei = EXP(x)*(1._dp + sum1)/x
    1384              :       END IF
    1385              : 
    1386            0 :    END FUNCTION ei
    1387              : 
    1388              : ! **************************************************************************************************
    1389              : !> \brief computes the exponential integral
    1390              : !>      En(x) = Int(exp(-x*t)/t^n,t=1..infinity)  x>0, n=0,1,..
    1391              : !>      Note: Ei(-x) = -E1(x)
    1392              : !> \param n ...
    1393              : !> \param x ...
    1394              : !> \return ...
    1395              : !> \par History
    1396              : !>      05.2007 Created
    1397              : !> \author Manuel Guidon (adapted from Numerical recipies)
    1398              : ! **************************************************************************************************
    1399    298362910 :    ELEMENTAL IMPURE FUNCTION expint(n, x)
    1400              :       INTEGER, INTENT(IN)                                :: n
    1401              :       REAL(dp), INTENT(IN)                               :: x
    1402              :       REAL(dp)                                           :: expint
    1403              : 
    1404              :       INTEGER, PARAMETER                                 :: maxit = 100
    1405              :       REAL(dp), PARAMETER :: eps = 6.e-14_dp, euler = 0.5772156649015328606065120_dp, &
    1406              :          fpmin = TINY(0.0_dp)
    1407              : 
    1408              :       INTEGER                                            :: i, ii, nm1
    1409              :       REAL(dp)                                           :: a, b, c, d, del, fact, h, psi
    1410              : 
    1411    298362910 :       nm1 = n - 1
    1412              : 
    1413    298362910 :       IF (n < 0 .OR. x < 0.0_dp .OR. (x == 0.0_dp .AND. (n == 0 .OR. n == 1))) THEN
    1414            0 :          CPABORT("Invalid argument")
    1415    298362910 :       ELSE IF (n == 0) THEN !Special case.
    1416            0 :          expint = EXP(-x)/x
    1417    298362910 :       ELSE IF (x == 0.0_dp) THEN !Another special case.
    1418            0 :          expint = 1.0_dp/nm1
    1419    298362910 :       ELSE IF (x > 1.0_dp) THEN !Lentz's algorithm (5.2).
    1420    240863916 :          b = x + n
    1421    240863916 :          c = 1.0_dp/FPMIN
    1422    240863916 :          d = 1.0_dp/b
    1423    240863916 :          h = d
    1424   6974042655 :          DO i = 1, MAXIT
    1425   6974042655 :             a = -i*(nm1 + i)
    1426   6974042655 :             b = b + 2.0_dp
    1427   6974042655 :             d = 1.0_dp/(a*d + b)
    1428   6974042655 :             c = b + a/c
    1429   6974042655 :             del = c*d
    1430   6974042655 :             h = h*del
    1431   6974042655 :             IF (ABS(del - 1.0_dp) < EPS) THEN
    1432    240863916 :                expint = h*EXP(-x)
    1433    240863916 :                RETURN
    1434              :             END IF
    1435              :          END DO
    1436            0 :          CPABORT("continued fraction failed in expint")
    1437              :       ELSE !Evaluate series.
    1438     57498994 :          IF (nm1 /= 0) THEN !Set first term.
    1439            0 :             expint = 1.0_dp/nm1
    1440              :          ELSE
    1441     57498994 :             expint = -LOG(x) - euler
    1442              :          END IF
    1443              :          fact = 1.0_dp
    1444    491523421 :          DO i = 1, MAXIT
    1445    491523421 :             fact = -fact*x/i
    1446    491523421 :             IF (i /= nm1) THEN
    1447    491523421 :                del = -fact/(i - nm1)
    1448              :             ELSE
    1449              :                psi = -euler !Compute I(n).
    1450            0 :                DO ii = 1, nm1
    1451            0 :                   psi = psi + 1.0_dp/ii
    1452              :                END DO
    1453            0 :                del = fact*(-LOG(x) + psi)
    1454              :             END IF
    1455    491523421 :             expint = expint + del
    1456    491523421 :             IF (ABS(del) < ABS(expint)*EPS) RETURN
    1457              :          END DO
    1458            0 :          CPABORT("series failed in expint")
    1459              :       END IF
    1460              : 
    1461              :    END FUNCTION expint
    1462              : 
    1463              : ! **************************************************************************************************
    1464              : !> \brief  Jacobi matrix diagonalization. The eigenvalues are returned in
    1465              : !>         vector d and the eigenvectors are returned in matrix v in ascending
    1466              : !>         order.
    1467              : !>
    1468              : !> \param a ...
    1469              : !> \param d ...
    1470              : !> \param v ...
    1471              : !> \par History
    1472              : !>         - Creation (20.11.98, Matthias Krack)
    1473              : ! **************************************************************************************************
    1474        34478 :    SUBROUTINE jacobi(a, d, v)
    1475              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: a
    1476              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: d
    1477              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: v
    1478              : 
    1479              :       INTEGER                                            :: n
    1480              : 
    1481        34478 :       n = SIZE(d(:))
    1482              : 
    1483              :       ! Diagonalize matrix a
    1484        34478 :       CALL diag(n, a, d, v)
    1485              : 
    1486              :       ! Sort eigenvalues and eigenvector in ascending order
    1487        34478 :       CALL eigsrt(n, d, v)
    1488              : 
    1489        34478 :    END SUBROUTINE jacobi
    1490              : 
    1491              : ! **************************************************************************************************
    1492              : !> \brief  Diagonalize matrix a. The eigenvalues are returned in vector d
    1493              : !>         and the eigenvectors are returned in matrix v.
    1494              : !>
    1495              : !> \param n matrix/vector extent (problem size)
    1496              : !> \param a matrix to be diagonalised
    1497              : !> \param d vector of eigenvalues
    1498              : !> \param v matrix of eigenvectors
    1499              : !> \par History
    1500              : !>         - Creation (20.11.98, Matthias Krack)
    1501              : ! **************************************************************************************************
    1502        34478 :    SUBROUTINE diag(n, a, d, v)
    1503              :       INTEGER, INTENT(IN)                                :: n
    1504              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: a
    1505              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: d
    1506              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: v
    1507              : 
    1508              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'diag'
    1509              :       REAL(KIND=dp), PARAMETER                           :: a_eps = 1.0E-10_dp, d_eps = 1.0E-3_dp
    1510              : 
    1511              :       INTEGER                                            :: handle, i, ip, iq
    1512              :       REAL(KIND=dp)                                      :: a_max, apq, c, d_min, dip, diq, g, h, s, &
    1513              :                                                             t, tau, theta, tresh
    1514        34478 :       REAL(KIND=dp), DIMENSION(n)                        :: b, z
    1515              : 
    1516        34478 :       CALL timeset(routineN, handle)
    1517              : 
    1518        34478 :       a_max = 0.0_dp
    1519       121765 :       DO ip = 1, n - 1
    1520       879538 :          a_max = MAX(a_max, MAXVAL(ABS(a(ip, ip + 1:n))))
    1521       121765 :          b(ip) = a(ip, ip) ! get_diag(a)
    1522              :       END DO
    1523        34478 :       b(n) = a(n, n)
    1524              : 
    1525        34478 :       CALL unit_matrix(v)
    1526              : 
    1527              :       ! Go for 50 iterations
    1528       160078 :       DO i = 1, 50
    1529       975669 :          d = b
    1530       975669 :          d_min = MAX(d_eps, MINVAL(ABS(b)))
    1531       160078 :          IF (a_max < a_eps*d_min) THEN
    1532        34478 :             CALL timestop(handle)
    1533              :             RETURN
    1534              :          END IF
    1535       125600 :          tresh = MERGE(a_max, 0.0_dp, (i < 4))
    1536       819426 :          z = 0.0_dp
    1537       693826 :          DO ip = 1, n - 1
    1538      6401612 :             DO iq = ip + 1, n
    1539      5707786 :                dip = d(ip)
    1540      5707786 :                diq = d(iq)
    1541      5707786 :                apq = a(ip, iq)
    1542      5707786 :                g = 100.0_dp*ABS(apq)
    1543      6276012 :                IF (tresh < ABS(apq)) THEN
    1544      3334999 :                   h = diq - dip
    1545      3334999 :                   IF ((ABS(h) + g) /= ABS(h)) THEN
    1546      2523614 :                      theta = 0.5_dp*h/apq
    1547      2523614 :                      t = 1.0_dp/(ABS(theta) + SQRT(1.0_dp + theta**2))
    1548      2523614 :                      IF (theta < 0.0_dp) t = -t
    1549              :                   ELSE
    1550       811385 :                      t = apq/h
    1551              :                   END IF
    1552      3334999 :                   c = 1.0_dp/SQRT(1.0_dp + t**2)
    1553      3334999 :                   s = t*c
    1554      3334999 :                   tau = s/(1.0_dp + c)
    1555      3334999 :                   h = t*apq
    1556      3334999 :                   z(ip) = z(ip) - h
    1557      3334999 :                   z(iq) = z(iq) + h
    1558      3334999 :                   d(ip) = dip - h
    1559      3334999 :                   d(iq) = diq + h
    1560      3334999 :                   a(ip, iq) = 0.0_dp
    1561      3334999 :                   CALL jrotate(a(1:ip - 1, ip), a(1:ip - 1, iq), s, tau)
    1562      3334999 :                   CALL jrotate(a(ip, ip + 1:iq - 1), a(ip + 1:iq - 1, iq), s, tau)
    1563      3334999 :                   CALL jrotate(a(ip, iq + 1:n), a(iq, iq + 1:n), s, tau)
    1564      3334999 :                   CALL jrotate(v(:, ip), v(:, iq), s, tau)
    1565              :                ELSE IF ((4 < i) .AND. &
    1566            0 :                         ((ABS(dip) + g) == ABS(dip)) .AND. &
    1567      2372787 :                         ((ABS(diq) + g) == ABS(diq))) THEN
    1568            0 :                   a(ip, iq) = 0.0_dp
    1569              :                END IF
    1570              :             END DO
    1571              :          END DO
    1572       819426 :          b = b + z
    1573              :          a_max = 0.0_dp
    1574       693826 :          DO ip = 1, n - 1
    1575      6401612 :             a_max = MAX(a_max, MAXVAL(ABS(a(ip, ip + 1:n))))
    1576              :          END DO
    1577              :       END DO
    1578            0 :       WRITE (*, '(/,T2,A,/)') 'Too many iterations in jacobi'
    1579              : 
    1580            0 :       CALL timestop(handle)
    1581              : 
    1582              :    END SUBROUTINE diag
    1583              : 
    1584              : ! **************************************************************************************************
    1585              : !> \brief  Perform a Jacobi rotation of the vectors a and b.
    1586              : !>
    1587              : !> \param a ...
    1588              : !> \param b ...
    1589              : !> \param ss ...
    1590              : !> \param tt ...
    1591              : !> \par History
    1592              : !>         - Creation (20.11.98, Matthias Krack)
    1593              : ! **************************************************************************************************
    1594     13339996 :    PURE SUBROUTINE jrotate(a, b, ss, tt)
    1595              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: a, b
    1596              :       REAL(KIND=dp), INTENT(IN)                          :: ss, tt
    1597              : 
    1598              :       REAL(KIND=dp)                                      :: u, v
    1599              : 
    1600     13339996 :       u = 1.0_dp - ss*tt
    1601     13339996 :       v = ss/u
    1602              : 
    1603    228402348 :       a = a*u - b*ss
    1604    228402348 :       b = b*(u + ss*v) + a*v
    1605              : 
    1606     13339996 :    END SUBROUTINE jrotate
    1607              : 
    1608              : ! **************************************************************************************************
    1609              : !> \brief Sort the values in vector d in ascending order and swap the
    1610              : !>        corresponding columns of matrix v.
    1611              : !>
    1612              : !> \param n ...
    1613              : !> \param d ...
    1614              : !> \param v ...
    1615              : !> \par History
    1616              : !>         - Creation (20.11.98, Matthias Krack)
    1617              : ! **************************************************************************************************
    1618        34478 :    SUBROUTINE eigsrt(n, d, v)
    1619              :       INTEGER, INTENT(IN)                                :: n
    1620              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: d
    1621              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: v
    1622              : 
    1623              :       INTEGER                                            :: i, j
    1624              : 
    1625       121765 :       DO i = 1, n - 1
    1626      1054112 :          j = SUM(MINLOC(d(i:n))) + i - 1
    1627       121765 :          IF (j /= i) THEN
    1628        42828 :             CALL swap(d(i), d(j))
    1629        42828 :             CALL swap(v(:, i), v(:, j))
    1630              :          END IF
    1631              :       END DO
    1632              : 
    1633        34478 :    END SUBROUTINE eigsrt
    1634              : 
    1635              : ! **************************************************************************
    1636              : !> \brief Swap two scalars
    1637              : !>
    1638              : !> \param a ...
    1639              : !> \param b ...
    1640              : !> \par History
    1641              : !>         - Creation (20.11.98, Matthias Krack)
    1642              : ! **************************************************************************************************
    1643        42828 :    ELEMENTAL SUBROUTINE swap_scalar(a, b)
    1644              :       REAL(KIND=dp), INTENT(INOUT)                       :: a, b
    1645              : 
    1646              :       REAL(KIND=dp)                                      :: c
    1647              : 
    1648        42828 :       c = a
    1649        42828 :       a = b
    1650        42828 :       b = c
    1651              : 
    1652        42828 :    END SUBROUTINE swap_scalar
    1653              : 
    1654              : ! **************************************************************************
    1655              : !> \brief Swap two vectors
    1656              : !>
    1657              : !> \param a ...
    1658              : !> \param b ...
    1659              : !> \par History
    1660              : !>         - Creation (20.11.98, Matthias Krack)
    1661              : ! **************************************************************************************************
    1662        42828 :    SUBROUTINE swap_vector(a, b)
    1663              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: a, b
    1664              : 
    1665              :       INTEGER                                            :: i, n
    1666              :       REAL(KIND=dp)                                      :: c
    1667              : 
    1668        42828 :       n = SIZE(a)
    1669              : 
    1670        42828 :       IF (n /= SIZE(b)) THEN
    1671            0 :          CPABORT("Check the array bounds of the parameters")
    1672              :       END IF
    1673              : 
    1674       956900 :       DO i = 1, n
    1675       914072 :          c = a(i)
    1676       914072 :          a(i) = b(i)
    1677       956900 :          b(i) = c
    1678              :       END DO
    1679              : 
    1680        42828 :    END SUBROUTINE swap_vector
    1681              : 
    1682              : ! **************************************************************************************************
    1683              : !> \brief - compute a truncation radius for the shortrange operator
    1684              : !> \param eps target accuracy!> \param omg screening parameter
    1685              : !> \param omg ...
    1686              : !> \param r_cutoff cutoff radius
    1687              : !> \par History
    1688              : !>      10.2012 created [Hossein Banihashemian]
    1689              : !>      05.2019 moved here from hfx_types (A. Bussy)
    1690              : !> \author Hossein Banihashemian
    1691              : ! **************************************************************************************************
    1692           66 :    SUBROUTINE erfc_cutoff(eps, omg, r_cutoff)
    1693              :       REAL(dp), INTENT(in)  :: eps, omg
    1694              :       REAL(dp), INTENT(out) :: r_cutoff
    1695              : 
    1696              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'erfc_cutoff'
    1697              : 
    1698              :       REAL(dp), PARAMETER :: abstol = 1E-10_dp, soltol = 1E-16_dp
    1699              :       REAL(dp) :: r0, f0, fprime0, delta_r
    1700              :       INTEGER :: iter, handle
    1701              :       INTEGER, PARAMETER :: iterMAX = 1000
    1702              : 
    1703           66 :       CALL timeset(routineN, handle)
    1704              : 
    1705              :       ! initial guess assuming that we are in the asymptotic regime of the erf, and the solution is about 10.
    1706           66 :       r0 = SQRT(-LOG(eps*omg*10**2))/omg
    1707           66 :       CALL eval_transc_func(r0, eps, omg, f0, fprime0)
    1708              : 
    1709          638 :       DO iter = 1, iterMAX
    1710          638 :          delta_r = f0/fprime0
    1711          638 :          r0 = r0 - delta_r
    1712          638 :          CALL eval_transc_func(r0, eps, omg, f0, fprime0)
    1713          638 :          IF (ABS(delta_r) < abstol .OR. ABS(f0) < soltol) EXIT
    1714              :       END DO
    1715           66 :       CPASSERT(iter <= itermax)
    1716           66 :       r_cutoff = r0
    1717              : 
    1718           66 :       CALL timestop(handle)
    1719              :    CONTAINS
    1720              : ! **************************************************************************************************
    1721              : !> \brief ...
    1722              : !> \param r ...
    1723              : !> \param eps ...
    1724              : !> \param omega ...
    1725              : !> \param fn ...
    1726              : !> \param df ...
    1727              : ! **************************************************************************************************
    1728          704 :       ELEMENTAL SUBROUTINE eval_transc_func(r, eps, omega, fn, df)
    1729              :       REAL(dp), INTENT(in)                               :: r, eps, omega
    1730              :       REAL(dp), INTENT(out)                              :: fn, df
    1731              : 
    1732              :       REAL(dp)                                           :: qr
    1733              : 
    1734          704 :          qr = omega*r
    1735          704 :          fn = ERFC(qr) - r*eps
    1736          704 :          df = -2.0_dp*oorootpi*omega*EXP(-qr**2) - eps
    1737          704 :       END SUBROUTINE eval_transc_func
    1738              :    END SUBROUTINE erfc_cutoff
    1739              : 
    1740              : ! **************************************************************************************************
    1741              : !> \brief Diagonalizes a local complex Hermitian matrix using LAPACK. Based on cp_cfm_heevd
    1742              : !> \param matrix Hermitian matrix is preserved
    1743              : !> \param eigenvectors ...
    1744              : !> \param eigenvalues ...
    1745              : !> \author A. Bussy
    1746              : ! **************************************************************************************************
    1747        68919 :    SUBROUTINE diag_complex(matrix, eigenvectors, eigenvalues)
    1748              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: matrix
    1749              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: eigenvectors
    1750              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
    1751              : 
    1752              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'diag_complex'
    1753              : 
    1754        68919 :       COMPLEX(KIND=dp), DIMENSION(:), ALLOCATABLE        :: work
    1755              :       INTEGER                                            :: handle, info, liwork, lrwork, lwork, n
    1756        68919 :       INTEGER, DIMENSION(:), ALLOCATABLE                 :: iwork
    1757        68919 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE           :: rwork
    1758              : 
    1759        68919 :       CALL timeset(routineN, handle)
    1760              : 
    1761        68919 :       IF (SIZE(matrix, 1) /= SIZE(matrix, 2)) CPABORT("Expected square matrix")
    1762              :       ! IF (MAXVAL(ABS(matrix - CONJG(TRANSPOSE(matrix)))) > 1e-14_dp) CPABORT("Expected hermitian matrix")
    1763              : 
    1764        68919 :       n = SIZE(matrix, 1)
    1765        68919 :       ALLOCATE (iwork(1), rwork(1), work(1))
    1766              : 
    1767              :       ! work space query
    1768        68919 :       lwork = -1
    1769        68919 :       lrwork = -1
    1770        68919 :       liwork = -1
    1771              : 
    1772        68919 :       CALL zheevd('V', 'U', n, eigenvectors, n, eigenvalues, work, lwork, rwork, lrwork, iwork, liwork, info)
    1773              : 
    1774        68919 :       lwork = CEILING(REAL(work(1), KIND=dp))
    1775        68919 :       lrwork = CEILING(rwork(1))
    1776        68919 :       liwork = iwork(1)
    1777              : 
    1778        68919 :       DEALLOCATE (iwork, rwork, work)
    1779       482433 :       ALLOCATE (iwork(liwork), rwork(lrwork), work(lwork))
    1780      2507197 :       eigenvectors(:, :) = matrix(:, :)
    1781              : 
    1782              :       ! final diagonalization
    1783        68919 :       CALL zheevd('V', 'U', n, eigenvectors, n, eigenvalues, work, lwork, rwork, lrwork, iwork, liwork, info)
    1784              : 
    1785        68919 :       DEALLOCATE (iwork, rwork, work)
    1786              : 
    1787        68919 :       IF (info /= 0) CPABORT("Diagonalisation of a complex matrix failed")
    1788              : 
    1789        68919 :       CALL timestop(handle)
    1790              : 
    1791        68919 :    END SUBROUTINE diag_complex
    1792              : 
    1793              : ! **************************************************************************************************
    1794              : !> \brief Helper routine for diagonalizing anti symmetric matrices
    1795              : !> \param matrix ...
    1796              : !> \param evecs ...
    1797              : !> \param evals ...
    1798              : ! **************************************************************************************************
    1799         3539 :    SUBROUTINE diag_antisym(matrix, evecs, evals)
    1800              :       REAL(dp), DIMENSION(:, :)                          :: matrix
    1801              :       COMPLEX(dp), DIMENSION(:, :)                       :: evecs
    1802              :       COMPLEX(dp), DIMENSION(:)                          :: evals
    1803              : 
    1804              :       COMPLEX(dp), ALLOCATABLE, DIMENSION(:, :)          :: matrix_c
    1805              :       INTEGER                                            :: n
    1806              :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: eigenvalues
    1807              : 
    1808         3539 :       IF (SIZE(matrix, 1) /= SIZE(matrix, 2)) CPABORT("Expected square matrix")
    1809              :       ! IF (MAXVAL(ABS(matrix + TRANSPOSE(matrix))) > 1e-14_dp) CPABORT("Expected anti-symmetric matrix")
    1810              : 
    1811         3539 :       n = SIZE(matrix, 1)
    1812        21234 :       ALLOCATE (matrix_c(n, n), eigenvalues(n))
    1813              : 
    1814       235869 :       matrix_c(:, :) = CMPLX(0.0_dp, -matrix, kind=dp)
    1815         3539 :       CALL diag_complex(matrix_c, evecs, eigenvalues)
    1816        27874 :       evals = CMPLX(0.0_dp, eigenvalues, kind=dp)
    1817              : 
    1818         3539 :       DEALLOCATE (matrix_c, eigenvalues)
    1819         3539 :    END SUBROUTINE diag_antisym
    1820              : ! **************************************************************************************************
    1821              : !> \brief Square array multiplication via LAPACK routines, leaves inputs unchanged
    1822              : !> \param A_in Input matrix 1
    1823              : !> \param A_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 1
    1824              : !> \param B_in Input matrix 2
    1825              : !> \param B_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 2
    1826              : !> \param C_out Output matrix
    1827              : !> \par History
    1828              : !>    11.2025 created [Stepan Marek]
    1829              : ! **************************************************************************************************
    1830         2570 :    SUBROUTINE zgemm_square_2(A_in, A_trans, B_in, B_trans, C_out)
    1831              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN)      :: A_in
    1832              :       CHARACTER, INTENT(IN)                              :: A_trans
    1833              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN)      :: B_in
    1834              :       CHARACTER, INTENT(IN)                              :: B_trans
    1835              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(INOUT)   :: C_out
    1836              : 
    1837              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'zgemm_square_2'
    1838              : 
    1839              :       INTEGER                                            :: handle, n
    1840              : 
    1841         2570 :       n = SIZE(A_in, 1)
    1842         2570 :       IF (n /= SIZE(A_in, 2)) CPABORT("Non-square array 1 (A).")
    1843         2570 :       IF (n /= SIZE(B_in, 1)) CPABORT("Incompatible (rows) array 2 (B).")
    1844         2570 :       IF (n /= SIZE(B_in, 2)) CPABORT("Non-square array 2 (B).")
    1845         2570 :       IF (n /= SIZE(C_out, 1)) CPABORT("Incompatible (rows) result array 3 (C).")
    1846         2570 :       IF (n /= SIZE(C_out, 2)) CPABORT("Incompatible (cols) result array 3 (C).")
    1847         2570 :       IF (.NOT. (A_trans == 'N' .OR. A_trans == 'n' .OR. &
    1848              :                  A_trans == 'T' .OR. A_trans == 't' .OR. &
    1849              :                  A_trans == 'C' .OR. A_trans == 'c')) &
    1850            0 :          CPABORT("Unknown transpose character for array 1 (A).")
    1851         2570 :       IF (.NOT. (B_trans == 'N' .OR. B_trans == 'n' .OR. &
    1852              :                  B_trans == 'T' .OR. B_trans == 't' .OR. &
    1853              :                  B_trans == 'C' .OR. B_trans == 'c')) &
    1854            0 :          CPABORT("Unknown transpose character for array 2 (B).")
    1855              : 
    1856         2570 :       CALL timeset(routineN, handle)
    1857              : 
    1858         2570 :       CALL ZGEMM(A_trans, B_trans, n, n, n, z_one, A_in, n, B_in, n, z_zero, C_out, n)
    1859              : 
    1860         2570 :       CALL timestop(handle)
    1861              : 
    1862         2570 :    END SUBROUTINE zgemm_square_2
    1863              : ! **************************************************************************************************
    1864              : !> \brief Square array multiplication via LAPACK routines, leaves inputs unchanged, real matrices
    1865              : !> \param A_in Input matrix 1
    1866              : !> \param A_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 1
    1867              : !> \param B_in Input matrix 2
    1868              : !> \param B_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 2
    1869              : !> \param C_out Output matrix
    1870              : !> \par History
    1871              : !>    11.2025 created [Stepan Marek]
    1872              : ! **************************************************************************************************
    1873            2 :    SUBROUTINE dgemm_square_2(A_in, A_trans, B_in, B_trans, C_out)
    1874              :       REAL(kind=dp), DIMENSION(:, :), INTENT(IN)         :: A_in
    1875              :       CHARACTER, INTENT(IN)                              :: A_trans
    1876              :       REAL(kind=dp), DIMENSION(:, :), INTENT(IN)         :: B_in
    1877              :       CHARACTER, INTENT(IN)                              :: B_trans
    1878              :       REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT)      :: C_out
    1879              : 
    1880              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'dgemm_square_2'
    1881              : 
    1882              :       INTEGER                                            :: handle, n
    1883              : 
    1884            2 :       n = SIZE(A_in, 1)
    1885            2 :       IF (n /= SIZE(A_in, 2)) CPABORT("Non-square array 1 (A).")
    1886            2 :       IF (n /= SIZE(B_in, 1)) CPABORT("Incompatible (rows) array 2 (B).")
    1887            2 :       IF (n /= SIZE(B_in, 2)) CPABORT("Non-square array 2 (B).")
    1888            2 :       IF (n /= SIZE(C_out, 1)) CPABORT("Incompatible (rows) result array 3 (C).")
    1889            2 :       IF (n /= SIZE(C_out, 2)) CPABORT("Incompatible (cols) result array 3 (C).")
    1890            2 :       IF (.NOT. (A_trans == 'N' .OR. A_trans == 'n' .OR. &
    1891              :                  A_trans == 'T' .OR. A_trans == 't' .OR. &
    1892              :                  A_trans == 'C' .OR. A_trans == 'c')) &
    1893            0 :          CPABORT("Unknown transpose character for array 1 (A).")
    1894            2 :       IF (.NOT. (B_trans == 'N' .OR. B_trans == 'n' .OR. &
    1895              :                  B_trans == 'T' .OR. B_trans == 't' .OR. &
    1896              :                  B_trans == 'C' .OR. B_trans == 'c')) &
    1897            0 :          CPABORT("Unknown transpose character for array 2 (B).")
    1898              : 
    1899            2 :       CALL timeset(routineN, handle)
    1900              : 
    1901            2 :       CALL DGEMM(A_trans, B_trans, n, n, n, 1.0_dp, A_in, n, B_in, n, 0.0_dp, C_out, n)
    1902              : 
    1903            2 :       CALL timestop(handle)
    1904              : 
    1905            2 :    END SUBROUTINE dgemm_square_2
    1906              : ! **************************************************************************************************
    1907              : !> \brief Square array multiplication via LAPACK routines, leaves inputs unchanged, for 3 matrices
    1908              : !> \param A_in Input matrix 1
    1909              : !> \param A_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 1
    1910              : !> \param B_in Input matrix 2
    1911              : !> \param B_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 2
    1912              : !> \param C_in Input matrix 3
    1913              : !> \param C_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 3
    1914              : !> \param D_out Output matrix
    1915              : !> \par History
    1916              : !>    11.2025 created [Stepan Marek]
    1917              : ! **************************************************************************************************
    1918        36386 :    SUBROUTINE zgemm_square_3(A_in, A_trans, B_in, B_trans, C_in, C_trans, D_out)
    1919              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN)      :: A_in
    1920              :       CHARACTER, INTENT(IN)                              :: A_trans
    1921              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN)      :: B_in
    1922              :       CHARACTER, INTENT(IN)                              :: B_trans
    1923              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN)      :: C_in
    1924              :       CHARACTER, INTENT(IN)                              :: C_trans
    1925              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(INOUT)   :: D_out
    1926              : 
    1927              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'zgemm_square_3'
    1928              : 
    1929        36386 :       COMPLEX(kind=dp), ALLOCATABLE, DIMENSION(:, :)     :: work
    1930              :       INTEGER                                            :: handle, n
    1931              : 
    1932        36386 :       n = SIZE(A_in, 1)
    1933        36386 :       IF (n /= SIZE(A_in, 2)) CPABORT("Non-square array 1 (A).")
    1934        36386 :       IF (n /= SIZE(B_in, 1)) CPABORT("Incompatible (rows) array 2 (B).")
    1935        36386 :       IF (n /= SIZE(B_in, 2)) CPABORT("Non-square array 2 (B).")
    1936        36386 :       IF (n /= SIZE(C_in, 1)) CPABORT("Incompatible (rows) array 3 (C).")
    1937        36386 :       IF (n /= SIZE(C_in, 2)) CPABORT("Non-square array 3 (C).")
    1938        36386 :       IF (n /= SIZE(D_out, 1)) CPABORT("Incompatible (rows) result array 4 (D).")
    1939        36386 :       IF (n /= SIZE(D_out, 2)) CPABORT("Incompatible (cols) result array 4 (D).")
    1940        36386 :       IF (.NOT. (A_trans == 'N' .OR. A_trans == 'n' .OR. &
    1941              :                  A_trans == 'T' .OR. A_trans == 't' .OR. &
    1942              :                  A_trans == 'C' .OR. A_trans == 'c')) &
    1943            0 :          CPABORT("Unknown transpose character for array 1 (A).")
    1944        36386 :       IF (.NOT. (B_trans == 'N' .OR. B_trans == 'n' .OR. &
    1945              :                  B_trans == 'T' .OR. B_trans == 't' .OR. &
    1946              :                  B_trans == 'C' .OR. B_trans == 'c')) &
    1947            0 :          CPABORT("Unknown transpose character for array 2 (B).")
    1948        36386 :       IF (.NOT. (C_trans == 'N' .OR. C_trans == 'n' .OR. &
    1949              :                  C_trans == 'T' .OR. C_trans == 't' .OR. &
    1950              :                  C_trans == 'C' .OR. C_trans == 'c')) &
    1951            0 :          CPABORT("Unknown transpose character for array 3 (C).")
    1952              : 
    1953        36386 :       CALL timeset(routineN, handle)
    1954              : 
    1955       145544 :       ALLOCATE (work(n, n), source=z_zero)
    1956              : 
    1957        36386 :       CALL ZGEMM(A_trans, B_trans, n, n, n, z_one, A_in, n, B_in, n, z_zero, work, n)
    1958        36386 :       CALL ZGEMM('N', C_trans, n, n, n, z_one, work, n, C_in, n, z_zero, D_out, n)
    1959              : 
    1960        36386 :       DEALLOCATE (work)
    1961              : 
    1962        36386 :       CALL timestop(handle)
    1963              : 
    1964        36386 :    END SUBROUTINE zgemm_square_3
    1965              : ! **************************************************************************************************
    1966              : !> \brief Square array multiplication via LAPACK routines, leaves inputs unchanged, for 3 matrices
    1967              : !> \param A_in Input matrix 1
    1968              : !> \param A_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 1
    1969              : !> \param B_in Input matrix 2
    1970              : !> \param B_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 2
    1971              : !> \param C_in Input matrix 3
    1972              : !> \param C_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 3
    1973              : !> \param D_out Output matrix
    1974              : !> \par History
    1975              : !>    11.2025 created [Stepan Marek]
    1976              : ! **************************************************************************************************
    1977            4 :    SUBROUTINE dgemm_square_3(A_in, A_trans, B_in, B_trans, C_in, C_trans, D_out)
    1978              :       REAL(kind=dp), DIMENSION(:, :), INTENT(IN)         :: A_in
    1979              :       CHARACTER, INTENT(IN)                              :: A_trans
    1980              :       REAL(kind=dp), DIMENSION(:, :), INTENT(IN)         :: B_in
    1981              :       CHARACTER, INTENT(IN)                              :: B_trans
    1982              :       REAL(kind=dp), DIMENSION(:, :), INTENT(IN)         :: C_in
    1983              :       CHARACTER, INTENT(IN)                              :: C_trans
    1984              :       REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT)      :: D_out
    1985              : 
    1986              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'dgemm_square_3'
    1987              : 
    1988              :       INTEGER                                            :: handle, n
    1989            4 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :)        :: work
    1990              : 
    1991            4 :       n = SIZE(A_in, 1)
    1992            4 :       IF (n /= SIZE(A_in, 2)) CPABORT("Non-square array 1 (A).")
    1993            4 :       IF (n /= SIZE(B_in, 1)) CPABORT("Incompatible (rows) array 2 (B).")
    1994            4 :       IF (n /= SIZE(B_in, 2)) CPABORT("Non-square array 2 (B).")
    1995            4 :       IF (n /= SIZE(C_in, 1)) CPABORT("Incompatible (rows) array 3 (C).")
    1996            4 :       IF (n /= SIZE(C_in, 2)) CPABORT("Non-square array 3 (C).")
    1997            4 :       IF (n /= SIZE(D_out, 1)) CPABORT("Incompatible (rows) result array 4 (D).")
    1998            4 :       IF (n /= SIZE(D_out, 2)) CPABORT("Incompatible (cols) result array 4 (D).")
    1999            4 :       IF (.NOT. (A_trans == 'N' .OR. A_trans == 'n' .OR. &
    2000              :                  A_trans == 'T' .OR. A_trans == 't' .OR. &
    2001              :                  A_trans == 'C' .OR. A_trans == 'c')) &
    2002            0 :          CPABORT("Unknown transpose character for array 1 (A).")
    2003            4 :       IF (.NOT. (B_trans == 'N' .OR. B_trans == 'n' .OR. &
    2004              :                  B_trans == 'T' .OR. B_trans == 't' .OR. &
    2005              :                  B_trans == 'C' .OR. B_trans == 'c')) &
    2006            0 :          CPABORT("Unknown transpose character for array 2 (B).")
    2007            4 :       IF (.NOT. (C_trans == 'N' .OR. C_trans == 'n' .OR. &
    2008              :                  C_trans == 'T' .OR. C_trans == 't' .OR. &
    2009              :                  C_trans == 'C' .OR. C_trans == 'c')) &
    2010            0 :          CPABORT("Unknown transpose character for array 3 (C).")
    2011              : 
    2012            4 :       CALL timeset(routineN, handle)
    2013              : 
    2014           16 :       ALLOCATE (work(n, n), source=0.0_dp)
    2015              : 
    2016            4 :       CALL DGEMM(A_trans, B_trans, n, n, n, 1.0_dp, A_in, n, B_in, n, 0.0_dp, work, n)
    2017            4 :       CALL DGEMM('N', C_trans, n, n, n, 1.0_dp, work, n, C_in, n, 0.0_dp, D_out, n)
    2018              : 
    2019            4 :       DEALLOCATE (work)
    2020              : 
    2021            4 :       CALL timestop(handle)
    2022              : 
    2023            4 :    END SUBROUTINE dgemm_square_3
    2024              : 
    2025              : ! **************************************************************************************************
    2026              : !> \brief Solve the generalized eigenvalue equation for complex matrices A*v = B*v*λ
    2027              : !> \param A_in ...
    2028              : !> \param B_in ...
    2029              : !> \param eigenvalues ...
    2030              : !> \param eigenvectors ...
    2031              : !> \author Shridhar Shanbhag
    2032              : ! **************************************************************************************************
    2033            4 :    SUBROUTINE geeig_right(A_in, B_in, eigenvalues, eigenvectors)
    2034              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN)      :: A_in, B_in
    2035              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
    2036              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: eigenvectors
    2037              : 
    2038              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'geeig_right'
    2039              :       COMPLEX(KIND=dp), PARAMETER :: cone = CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
    2040              :          czero = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2041              : 
    2042            4 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:)        :: cevals
    2043              :       COMPLEX(kind=dp), ALLOCATABLE, DIMENSION(:, :)     :: A, B, work
    2044              :       INTEGER                                            :: handle, i, icol, irow, lda, ldb, ldc, &
    2045              :                                                             nao, nc, ncol, nmo, nx
    2046              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: evals
    2047              : 
    2048            4 :       CALL timeset(routineN, handle)
    2049              : 
    2050              :       ! Test sizes
    2051            4 :       nao = SIZE(A_in, 1)
    2052            4 :       nmo = SIZE(eigenvalues)
    2053           20 :       ALLOCATE (evals(nao), cevals(nao))
    2054           32 :       ALLOCATE (work(nao, nao), B(nao, nao), A(nao, nao))
    2055        21988 :       A(:, :) = A_in(:, :)
    2056        21988 :       B(:, :) = B_in(:, :)
    2057              : 
    2058              :       ! Diagonalize -S matrix, this way the NULL space is at the end of the spectrum
    2059        21988 :       B = -B
    2060            4 :       CALL diag_complex(B, work, evals)
    2061          228 :       evals(:) = -evals(:)
    2062            4 :       nc = nao
    2063          228 :       DO i = 1, nao
    2064          228 :          IF (evals(i) < -1.0_dp) THEN
    2065            0 :             nc = i - 1
    2066            0 :             EXIT
    2067              :          END IF
    2068              :       END DO
    2069            4 :       CPASSERT(nc /= 0)
    2070              : 
    2071            4 :       IF (nc /= nao) THEN
    2072            0 :          IF (nc < nmo) THEN
    2073              :             ! Copy NULL space definition to last vectors of eigenvectors (if needed)
    2074            0 :             ncol = nmo - nc
    2075            0 :             CALL zcopy(ncol*nao, work(1, nc + 1), 1, eigenvectors(1, nc + 1), 1)
    2076              :          END IF
    2077              :          ! Set NULL space in eigenvector matrix of S to zero
    2078            0 :          DO icol = nc + 1, nao
    2079            0 :             DO irow = 1, nao
    2080            0 :                work(irow, icol) = czero
    2081              :             END DO
    2082              :          END DO
    2083              :          ! Set small eigenvalues to a dummy save value
    2084            0 :          evals(nc + 1:nao) = 1.0_dp
    2085              :       END IF
    2086              :       ! Calculate U*s**(-1/2)
    2087          228 :       cevals(:) = CMPLX(1.0_dp/SQRT(evals(:)), 0.0_dp, KIND=dp)
    2088          228 :       DO i = 1, MIN(SIZE(work, 2), SIZE(cevals))
    2089          228 :          CALL zscal(MIN(SIZE(work, 2), SIZE(cevals)), cevals(i), work(1, i), 1)
    2090              :       END DO
    2091              :       ! Reduce to get U^(-C) * H * U^(-1)
    2092            4 :       CALL gemm_square(work, 'C', A, 'N', B)
    2093            4 :       CALL gemm_square(B, 'N', work, 'N', A)
    2094            4 :       IF (nc /= nao) THEN
    2095              :          ! set diagonal values to save large value
    2096            0 :          DO icol = nc + 1, nao
    2097            0 :             A(icol, icol) = 10000*cone
    2098              :          END DO
    2099              :       END IF
    2100              :       ! Diagonalize
    2101            4 :       CALL diag_complex(A, B, evals)
    2102          228 :       eigenvalues(1:nmo) = evals(1:nmo)
    2103            4 :       nx = MIN(nc, nmo)
    2104              :       ! Restore vectors C = U^(-1) * C*
    2105            4 :       lda = SIZE(work, 1)
    2106            4 :       ldb = SIZE(B, 1)
    2107            4 :       ldc = SIZE(eigenvectors, 1)
    2108              :       CALL ZGEMM("N", "N", nao, nx, nc, cone, work, &
    2109            4 :                  lda, B, ldb, czero, eigenvectors, ldc)
    2110              : 
    2111            4 :       DEALLOCATE (evals, cevals, work, B, A)
    2112              : 
    2113            4 :       CALL timestop(handle)
    2114            4 :    END SUBROUTINE geeig_right
    2115              : 
    2116              : END MODULE mathlib
        

Generated by: LCOV version 2.0-1