Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief used for collecting diagonalization schemes available for cp_cfm_type
10 : !> \note
11 : !> first version : only one routine right now
12 : !> \author Joost VandeVondele (2003-09)
13 : ! **************************************************************************************************
14 : MODULE cp_cfm_diag
15 : USE cp_cfm_basic_linalg, ONLY: cp_cfm_cholesky_decompose, &
16 : cp_cfm_gemm, &
17 : cp_cfm_column_scale, &
18 : cp_cfm_scale, &
19 : cp_cfm_triangular_invert, &
20 : cp_cfm_triangular_multiply
21 : USE cp_cfm_types, ONLY: cp_cfm_get_info, &
22 : cp_cfm_set_element, &
23 : cp_cfm_to_cfm, &
24 : cp_cfm_type
25 : USE kinds, ONLY: dp
26 : #if defined (__HAS_IEEE_EXCEPTIONS)
27 : USE ieee_exceptions, ONLY: ieee_get_halting_mode, &
28 : ieee_set_halting_mode, &
29 : ieee_all
30 : #endif
31 :
32 : #include "../base/base_uses.f90"
33 :
34 : IMPLICIT NONE
35 : PRIVATE
36 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_cfm_diag'
37 :
38 : PUBLIC :: cp_cfm_heevd, cp_cfm_geeig, cp_cfm_geeig_canon
39 :
40 : CONTAINS
41 :
42 : ! **************************************************************************************************
43 : !> \brief Perform a diagonalisation of a complex matrix
44 : !> \param matrix ...
45 : !> \param eigenvectors ...
46 : !> \param eigenvalues ...
47 : !> \par History
48 : !> - (De)Allocation checks updated (15.02.2011,MK)
49 : !> \author Joost VandeVondele
50 : ! **************************************************************************************************
51 25126 : SUBROUTINE cp_cfm_heevd(matrix, eigenvectors, eigenvalues)
52 :
53 : TYPE(cp_cfm_type), INTENT(IN) :: matrix, eigenvectors
54 : REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigenvalues
55 :
56 : CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_heevd'
57 :
58 25126 : COMPLEX(KIND=dp), DIMENSION(:), POINTER :: work
59 : COMPLEX(KIND=dp), DIMENSION(:, :), &
60 25126 : POINTER :: m
61 : INTEGER :: handle, info, liwork, &
62 : lrwork, lwork, n
63 25126 : INTEGER, DIMENSION(:), POINTER :: iwork
64 25126 : REAL(KIND=dp), DIMENSION(:), POINTER :: rwork
65 : #if defined(__parallel)
66 : INTEGER, DIMENSION(9) :: descm, descv
67 : COMPLEX(KIND=dp), DIMENSION(:, :), &
68 25126 : POINTER :: v
69 : #if defined (__HAS_IEEE_EXCEPTIONS)
70 : LOGICAL, DIMENSION(5) :: halt
71 : #endif
72 : #endif
73 :
74 25126 : CALL timeset(routineN, handle)
75 :
76 25126 : n = matrix%matrix_struct%nrow_global
77 25126 : m => matrix%local_data
78 25126 : ALLOCATE (iwork(1), rwork(1), work(1))
79 : ! work space query
80 25126 : lwork = -1
81 25126 : lrwork = -1
82 25126 : liwork = -1
83 :
84 : #if defined(__parallel)
85 25126 : v => eigenvectors%local_data
86 251260 : descm(:) = matrix%matrix_struct%descriptor(:)
87 251260 : descv(:) = eigenvectors%matrix_struct%descriptor(:)
88 : CALL pzheevd('V', 'U', n, m(1, 1), 1, 1, descm, eigenvalues(1), v(1, 1), 1, 1, descv, &
89 25126 : work(1), lwork, rwork(1), lrwork, iwork(1), liwork, info)
90 : ! The work space query for lwork does not return always sufficiently large values.
91 : ! Let's add some margin to avoid crashes.
92 25126 : lwork = CEILING(REAL(work(1), KIND=dp)) + 1000
93 : ! needed to correct for a bug in scalapack, unclear how much the right number is
94 25126 : lrwork = CEILING(REAL(rwork(1), KIND=dp)) + 1000000
95 25126 : liwork = iwork(1)
96 : #else
97 : CALL zheevd('V', 'U', n, m(1, 1), SIZE(m, 1), eigenvalues(1), &
98 : work(1), lwork, rwork(1), lrwork, iwork(1), liwork, info)
99 : lwork = CEILING(REAL(work(1), KIND=dp))
100 : lrwork = CEILING(REAL(rwork(1), KIND=dp))
101 : liwork = iwork(1)
102 : #endif
103 :
104 25126 : DEALLOCATE (iwork, rwork, work)
105 175882 : ALLOCATE (iwork(liwork), rwork(lrwork), work(lwork))
106 :
107 : #if defined(__parallel)
108 : ! Scalapack takes advantage of IEEE754 exceptions for speedup.
109 : ! Therefore, we disable floating point traps temporarily.
110 : #if defined (__HAS_IEEE_EXCEPTIONS)
111 : CALL ieee_get_halting_mode(IEEE_ALL, halt)
112 : CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
113 : #endif
114 :
115 : CALL pzheevd('V', 'U', n, m(1, 1), 1, 1, descm, eigenvalues(1), v(1, 1), 1, 1, descv, &
116 25126 : work(1), lwork, rwork(1), lrwork, iwork(1), liwork, info)
117 :
118 : #if defined (__HAS_IEEE_EXCEPTIONS)
119 : CALL ieee_set_halting_mode(IEEE_ALL, halt)
120 : #endif
121 : #else
122 : CALL ZHEEVD('V', 'U', n, m(1, 1), SIZE(m, 1), eigenvalues(1), &
123 : work(1), lwork, rwork(1), lrwork, iwork(1), liwork, info)
124 : eigenvectors%local_data = matrix%local_data
125 : #endif
126 :
127 25126 : DEALLOCATE (iwork, rwork, work)
128 25126 : IF (info /= 0) &
129 0 : CPABORT("Diagonalisation of a complex matrix failed")
130 :
131 25126 : CALL timestop(handle)
132 :
133 25126 : END SUBROUTINE cp_cfm_heevd
134 :
135 : ! **************************************************************************************************
136 : !> \brief General Eigenvalue Problem AX = BXE
137 : !> Single option version: Cholesky decomposition of B
138 : !> \param amatrix ...
139 : !> \param bmatrix ...
140 : !> \param eigenvectors ...
141 : !> \param eigenvalues ...
142 : !> \param work ...
143 : ! **************************************************************************************************
144 15148 : SUBROUTINE cp_cfm_geeig(amatrix, bmatrix, eigenvectors, eigenvalues, work)
145 :
146 : TYPE(cp_cfm_type), INTENT(IN) :: amatrix, bmatrix, eigenvectors
147 : REAL(KIND=dp), DIMENSION(:) :: eigenvalues
148 : TYPE(cp_cfm_type), INTENT(IN) :: work
149 :
150 : CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_geeig'
151 :
152 : INTEGER :: handle, nao, nmo
153 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: evals
154 :
155 15148 : CALL timeset(routineN, handle)
156 :
157 15148 : CALL cp_cfm_get_info(amatrix, nrow_global=nao)
158 45444 : ALLOCATE (evals(nao))
159 : ! Cholesky decompose S=U(T)U
160 15148 : CALL cp_cfm_cholesky_decompose(bmatrix)
161 : ! Invert to get U^(-1)
162 15148 : CALL cp_cfm_triangular_invert(bmatrix)
163 : ! Reduce to get U^(-T) * H * U^(-1)
164 15148 : CALL cp_cfm_triangular_multiply(bmatrix, amatrix, side="R")
165 15148 : CALL cp_cfm_triangular_multiply(bmatrix, amatrix, transa_tr="C")
166 : ! Diagonalize
167 15148 : CALL cp_cfm_heevd(matrix=amatrix, eigenvectors=work, eigenvalues=evals)
168 : ! Restore vectors C = U^(-1) * C*
169 15148 : CALL cp_cfm_triangular_multiply(bmatrix, work)
170 15148 : nmo = SIZE(eigenvalues)
171 15148 : CALL cp_cfm_to_cfm(work, eigenvectors, nmo)
172 234266 : eigenvalues(1:nmo) = evals(1:nmo)
173 :
174 15148 : DEALLOCATE (evals)
175 :
176 15148 : CALL timestop(handle)
177 :
178 15148 : END SUBROUTINE cp_cfm_geeig
179 :
180 : ! **************************************************************************************************
181 : !> \brief General Eigenvalue Problem AX = BXE
182 : !> Use canonical orthogonalization
183 : !> \param amatrix ...
184 : !> \param bmatrix ...
185 : !> \param eigenvectors ...
186 : !> \param eigenvalues ...
187 : !> \param work ...
188 : !> \param epseig ...
189 : ! **************************************************************************************************
190 494 : SUBROUTINE cp_cfm_geeig_canon(amatrix, bmatrix, eigenvectors, eigenvalues, work, epseig)
191 :
192 : TYPE(cp_cfm_type), INTENT(IN) :: amatrix, bmatrix, eigenvectors
193 : REAL(KIND=dp), DIMENSION(:) :: eigenvalues
194 : TYPE(cp_cfm_type), INTENT(IN) :: work
195 : REAL(KIND=dp), INTENT(IN) :: epseig
196 :
197 : CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_geeig_canon'
198 : COMPLEX(KIND=dp), PARAMETER :: cone = CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
199 : czero = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
200 :
201 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:) :: cevals
202 : INTEGER :: handle, i, icol, irow, nao, nc, ncol, &
203 : nmo, nx
204 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: evals
205 :
206 494 : CALL timeset(routineN, handle)
207 :
208 : ! Test sizes
209 494 : CALL cp_cfm_get_info(amatrix, nrow_global=nao)
210 494 : nmo = SIZE(eigenvalues)
211 2470 : ALLOCATE (evals(nao), cevals(nao))
212 :
213 : ! Diagonalize -S matrix, this way the NULL space is at the end of the spectrum
214 494 : CALL cp_cfm_scale(-cone, bmatrix)
215 494 : CALL cp_cfm_heevd(bmatrix, work, evals)
216 19962 : evals(:) = -evals(:)
217 494 : nc = nao
218 19398 : DO i = 1, nao
219 19398 : IF (evals(i) < epseig) THEN
220 72 : nc = i - 1
221 72 : EXIT
222 : END IF
223 : END DO
224 494 : CPASSERT(nc /= 0)
225 :
226 494 : IF (nc /= nao) THEN
227 72 : IF (nc < nmo) THEN
228 : ! Copy NULL space definition to last vectors of eigenvectors (if needed)
229 0 : ncol = nmo - nc
230 0 : CALL cp_cfm_to_cfm(work, eigenvectors, ncol, nc + 1, nc + 1)
231 : END IF
232 : ! Set NULL space in eigenvector matrix of S to zero
233 636 : DO icol = nc + 1, nao
234 80028 : DO irow = 1, nao
235 79956 : CALL cp_cfm_set_element(work, irow, icol, czero)
236 : END DO
237 : END DO
238 : ! Set small eigenvalues to a dummy save value
239 636 : evals(nc + 1:nao) = 1.0_dp
240 : END IF
241 : ! calculate U*s**(-1/2)
242 19962 : cevals(:) = CMPLX(1.0_dp/SQRT(evals(:)), 0.0_dp, KIND=dp)
243 494 : CALL cp_cfm_column_scale(work, cevals)
244 : ! Reduce to get U^(-C) * H * U^(-1)
245 494 : CALL cp_cfm_gemm("C", "N", nao, nao, nao, cone, work, amatrix, czero, bmatrix)
246 494 : CALL cp_cfm_gemm("N", "N", nao, nao, nao, cone, bmatrix, work, czero, amatrix)
247 494 : IF (nc /= nao) THEN
248 : ! set diagonal values to save large value
249 636 : DO icol = nc + 1, nao
250 636 : CALL cp_cfm_set_element(amatrix, icol, icol, CMPLX(10000.0_dp, 0.0_dp, KIND=dp))
251 : END DO
252 : END IF
253 : ! Diagonalize
254 494 : CALL cp_cfm_heevd(amatrix, bmatrix, evals)
255 7138 : eigenvalues(1:nmo) = evals(1:nmo)
256 494 : nx = MIN(nc, nmo)
257 : ! Restore vectors C = U^(-1) * C*
258 494 : CALL cp_cfm_gemm("N", "N", nao, nx, nc, cone, work, bmatrix, czero, eigenvectors)
259 :
260 494 : DEALLOCATE (evals)
261 :
262 494 : CALL timestop(handle)
263 :
264 988 : END SUBROUTINE cp_cfm_geeig_canon
265 :
266 : END MODULE cp_cfm_diag
|