Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2025 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief Original matrix exponential parametrization
10 : !> \author Ole Schuett
11 : ! **************************************************************************************************
12 : MODULE pao_param_exp
13 : USE basis_set_types, ONLY: gto_basis_set_type
14 : USE cp_dbcsr_api, ONLY: &
15 : dbcsr_create, dbcsr_get_block_p, dbcsr_get_info, dbcsr_iterator_blocks_left, &
16 : dbcsr_iterator_next_block, dbcsr_iterator_start, dbcsr_iterator_stop, dbcsr_iterator_type, &
17 : dbcsr_p_type, dbcsr_release, dbcsr_reserve_diag_blocks, dbcsr_set, dbcsr_type
18 : USE dm_ls_scf_types, ONLY: ls_scf_env_type
19 : USE kinds, ONLY: dp
20 : USE mathlib, ONLY: diag_antisym,&
21 : diamat_all
22 : USE pao_param_methods, ONLY: pao_calc_AB_from_U,&
23 : pao_calc_grad_lnv_wrt_U
24 : USE pao_potentials, ONLY: pao_guess_initial_potential
25 : USE pao_types, ONLY: pao_env_type
26 : USE qs_environment_types, ONLY: get_qs_env,&
27 : qs_environment_type
28 : USE qs_kind_types, ONLY: get_qs_kind,&
29 : qs_kind_type
30 : #include "./base/base_uses.f90"
31 :
32 : IMPLICIT NONE
33 :
34 : PRIVATE
35 :
36 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'pao_param_exp'
37 :
38 : PUBLIC :: pao_param_init_exp, pao_param_finalize_exp, pao_calc_AB_exp
39 : PUBLIC :: pao_param_count_exp, pao_param_initguess_exp
40 :
41 : CONTAINS
42 :
43 : ! **************************************************************************************************
44 : !> \brief Initialize matrix exponential parametrization
45 : !> \param pao ...
46 : !> \param qs_env ...
47 : ! **************************************************************************************************
48 24 : SUBROUTINE pao_param_init_exp(pao, qs_env)
49 : TYPE(pao_env_type), POINTER :: pao
50 : TYPE(qs_environment_type), POINTER :: qs_env
51 :
52 : CHARACTER(len=*), PARAMETER :: routineN = 'pao_param_init_exp'
53 :
54 : INTEGER :: acol, arow, handle, iatom, N
55 : LOGICAL :: found
56 24 : REAL(dp), DIMENSION(:), POINTER :: H_evals
57 24 : REAL(dp), DIMENSION(:, :), POINTER :: block_H, block_H0, block_N, block_U0, &
58 24 : block_V0, H_evecs
59 : TYPE(dbcsr_iterator_type) :: iter
60 24 : TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: matrix_s
61 :
62 24 : CALL timeset(routineN, handle)
63 :
64 24 : CALL get_qs_env(qs_env, matrix_s=matrix_s)
65 :
66 : ! allocate matrix_U0
67 : CALL dbcsr_create(pao%matrix_U0, &
68 : name="PAO matrix_U0", &
69 : matrix_type="N", &
70 : dist=pao%diag_distribution, &
71 24 : template=matrix_s(1)%matrix)
72 24 : CALL dbcsr_reserve_diag_blocks(pao%matrix_U0)
73 :
74 : ! diagonalize each block of H0 and store eigenvectors in U0
75 : !$OMP PARALLEL DEFAULT(NONE) SHARED(pao,qs_env) &
76 24 : !$OMP PRIVATE(iter,arow,acol,iatom,N,found,block_H0,block_V0,block_N,block_H,block_U0,H_evecs,H_evals)
77 : CALL dbcsr_iterator_start(iter, pao%matrix_U0)
78 : DO WHILE (dbcsr_iterator_blocks_left(iter))
79 : CALL dbcsr_iterator_next_block(iter, arow, acol, block_U0)
80 : iatom = arow; CPASSERT(arow == acol)
81 : CALL dbcsr_get_block_p(matrix=pao%matrix_H0, row=iatom, col=iatom, block=block_H0, found=found)
82 : CALL dbcsr_get_block_p(matrix=pao%matrix_N_diag, row=iatom, col=iatom, block=block_N, found=found)
83 : CPASSERT(ASSOCIATED(block_H0) .AND. ASSOCIATED(block_N))
84 : N = SIZE(block_U0, 1)
85 :
86 : ALLOCATE (block_V0(N, N))
87 : CALL pao_guess_initial_potential(qs_env, iatom, block_V0)
88 :
89 : ! construct H
90 : ALLOCATE (block_H(N, N))
91 : block_H = MATMUL(MATMUL(block_N, block_H0 + block_V0), block_N) ! transform into orthonormal basis
92 :
93 : ! diagonalize H
94 : ALLOCATE (H_evecs(N, N), H_evals(N))
95 : H_evecs = block_H
96 : CALL diamat_all(H_evecs, H_evals)
97 :
98 : ! use eigenvectors as initial guess
99 : block_U0 = H_evecs
100 :
101 : DEALLOCATE (block_H, H_evecs, H_evals, block_V0)
102 : END DO
103 : CALL dbcsr_iterator_stop(iter)
104 : !$OMP END PARALLEL
105 :
106 24 : IF (pao%precondition) &
107 0 : CPABORT("PAO preconditioning not supported for selected parametrization.")
108 :
109 24 : CALL timestop(handle)
110 24 : END SUBROUTINE pao_param_init_exp
111 :
112 : ! **************************************************************************************************
113 : !> \brief Finalize exponential parametrization
114 : !> \param pao ...
115 : ! **************************************************************************************************
116 24 : SUBROUTINE pao_param_finalize_exp(pao)
117 : TYPE(pao_env_type), POINTER :: pao
118 :
119 24 : CALL dbcsr_release(pao%matrix_U0)
120 :
121 24 : END SUBROUTINE pao_param_finalize_exp
122 :
123 : ! **************************************************************************************************
124 : !> \brief Returns the number of parameters for given atomic kind
125 : !> \param qs_env ...
126 : !> \param ikind ...
127 : !> \param nparams ...
128 : ! **************************************************************************************************
129 128 : SUBROUTINE pao_param_count_exp(qs_env, ikind, nparams)
130 : TYPE(qs_environment_type), POINTER :: qs_env
131 : INTEGER, INTENT(IN) :: ikind
132 : INTEGER, INTENT(OUT) :: nparams
133 :
134 : INTEGER :: cols, pao_basis_size, pri_basis_size, &
135 : rows
136 : TYPE(gto_basis_set_type), POINTER :: basis_set
137 64 : TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
138 :
139 64 : CALL get_qs_env(qs_env, qs_kind_set=qs_kind_set)
140 : CALL get_qs_kind(qs_kind_set(ikind), &
141 : basis_set=basis_set, &
142 64 : pao_basis_size=pao_basis_size)
143 64 : pri_basis_size = basis_set%nsgf
144 :
145 : ! we only consider rotations between occupied and virtuals
146 64 : rows = pao_basis_size
147 64 : cols = pri_basis_size - pao_basis_size
148 64 : nparams = rows*cols
149 :
150 64 : END SUBROUTINE pao_param_count_exp
151 :
152 : ! **************************************************************************************************
153 : !> \brief Fills matrix_X with an initial guess
154 : !> \param pao ...
155 : ! **************************************************************************************************
156 14 : SUBROUTINE pao_param_initguess_exp(pao)
157 : TYPE(pao_env_type), POINTER :: pao
158 :
159 14 : CALL dbcsr_set(pao%matrix_X, 0.0_dp) ! actual initial guess is matrix_U0
160 :
161 14 : END SUBROUTINE pao_param_initguess_exp
162 :
163 : ! **************************************************************************************************
164 : !> \brief Takes current matrix_X and calculates the matrices A and B.
165 : !> \param pao ...
166 : !> \param qs_env ...
167 : !> \param ls_scf_env ...
168 : !> \param gradient ...
169 : ! **************************************************************************************************
170 2710 : SUBROUTINE pao_calc_AB_exp(pao, qs_env, ls_scf_env, gradient)
171 : TYPE(pao_env_type), POINTER :: pao
172 : TYPE(qs_environment_type), POINTER :: qs_env
173 : TYPE(ls_scf_env_type), TARGET :: ls_scf_env
174 : LOGICAL, INTENT(IN) :: gradient
175 :
176 : CHARACTER(len=*), PARAMETER :: routineN = 'pao_calc_AB_exp'
177 :
178 : INTEGER :: handle
179 2710 : TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: matrix_s
180 : TYPE(dbcsr_type) :: matrix_M, matrix_U
181 :
182 2710 : CALL timeset(routineN, handle)
183 2710 : CALL get_qs_env(qs_env, matrix_s=matrix_s)
184 2710 : CALL dbcsr_create(matrix_U, matrix_type="N", dist=pao%diag_distribution, template=matrix_s(1)%matrix)
185 2710 : CALL dbcsr_reserve_diag_blocks(matrix_U)
186 :
187 : !TODO: move this condition into pao_calc_U, use matrix_N as template
188 2710 : IF (gradient) THEN
189 488 : CALL pao_calc_grad_lnv_wrt_U(qs_env, ls_scf_env, matrix_M)
190 488 : CALL pao_calc_U_exp(pao, matrix_U, matrix_M, pao%matrix_G)
191 488 : CALL dbcsr_release(matrix_M)
192 : ELSE
193 2222 : CALL pao_calc_U_exp(pao, matrix_U)
194 : END IF
195 :
196 2710 : CALL pao_calc_AB_from_U(pao, qs_env, ls_scf_env, matrix_U)
197 2710 : CALL dbcsr_release(matrix_U)
198 2710 : CALL timestop(handle)
199 2710 : END SUBROUTINE pao_calc_AB_exp
200 :
201 : ! **************************************************************************************************
202 : !> \brief Calculate new matrix U and optionally its gradient G
203 : !> \param pao ...
204 : !> \param matrix_U ...
205 : !> \param matrix_M ...
206 : !> \param matrix_G ...
207 : ! **************************************************************************************************
208 2710 : SUBROUTINE pao_calc_U_exp(pao, matrix_U, matrix_M, matrix_G)
209 : TYPE(pao_env_type), POINTER :: pao
210 : TYPE(dbcsr_type) :: matrix_U
211 : TYPE(dbcsr_type), OPTIONAL :: matrix_M, matrix_G
212 :
213 : CHARACTER(len=*), PARAMETER :: routineN = 'pao_calc_U_exp'
214 :
215 : COMPLEX(dp) :: denom
216 2710 : COMPLEX(dp), DIMENSION(:), POINTER :: evals
217 2710 : COMPLEX(dp), DIMENSION(:, :), POINTER :: block_D, evecs
218 : INTEGER :: acol, arow, handle, i, iatom, j, k, M, &
219 : N, nparams
220 2710 : INTEGER, DIMENSION(:), POINTER :: blk_sizes_pao, blk_sizes_pri
221 : LOGICAL :: found
222 2710 : REAL(dp), DIMENSION(:, :), POINTER :: block_G, block_G_full, block_M, &
223 2710 : block_tmp, block_U, block_U0, block_X, &
224 2710 : block_X_full
225 : TYPE(dbcsr_iterator_type) :: iter
226 :
227 2710 : CALL timeset(routineN, handle)
228 :
229 2710 : CALL dbcsr_get_info(pao%matrix_Y, row_blk_size=blk_sizes_pri, col_blk_size=blk_sizes_pao)
230 :
231 : !$OMP PARALLEL DEFAULT(NONE) SHARED(pao,matrix_U,matrix_M,matrix_G,blk_sizes_pri,blk_sizes_pao) &
232 : !$OMP PRIVATE(iter,arow,acol,iatom,N,M,nparams,i,j,k,found) &
233 : !$OMP PRIVATE(block_X,block_U,block_U0,block_X_full,evals,evecs) &
234 2710 : !$OMP PRIVATE(block_M,block_G,block_D,block_tmp,block_G_full,denom)
235 : CALL dbcsr_iterator_start(iter, pao%matrix_X)
236 : DO WHILE (dbcsr_iterator_blocks_left(iter))
237 : CALL dbcsr_iterator_next_block(iter, arow, acol, block_X)
238 : iatom = arow; CPASSERT(arow == acol)
239 : CALL dbcsr_get_block_p(matrix=matrix_U, row=iatom, col=iatom, block=block_U, found=found)
240 : CPASSERT(ASSOCIATED(block_U))
241 : CALL dbcsr_get_block_p(matrix=pao%matrix_U0, row=iatom, col=iatom, block=block_U0, found=found)
242 : CPASSERT(ASSOCIATED(block_U0))
243 :
244 : N = blk_sizes_pri(iatom) ! size of primary basis
245 : M = blk_sizes_pao(iatom) ! size of pao basis
246 : nparams = SIZE(block_X, 1)
247 :
248 : ! block_X stores only rotations between occupied and virtuals
249 : ! hence, we first have to build the full anti-symmetric exponent block
250 : ALLOCATE (block_X_full(N, N))
251 : block_X_full(:, :) = 0.0_dp
252 : DO i = 1, nparams
253 : block_X_full(MOD(i - 1, M) + 1, M + (i - 1)/M + 1) = +block_X(i, 1)
254 : block_X_full(M + (i - 1)/M + 1, MOD(i - 1, M) + 1) = -block_X(i, 1)
255 : END DO
256 :
257 : ! diagonalize block_X_full
258 : ALLOCATE (evals(N), evecs(N, N))
259 : CALL diag_antisym(block_X_full, evecs, evals)
260 :
261 : ! construct rotation matrix
262 : block_U(:, :) = 0.0_dp
263 : DO k = 1, N
264 : DO i = 1, N
265 : DO j = 1, N
266 : block_U(i, j) = block_U(i, j) + REAL(EXP(evals(k))*evecs(i, k)*CONJG(evecs(j, k)), dp)
267 : END DO
268 : END DO
269 : END DO
270 :
271 : block_U = MATMUL(block_U0, block_U) ! prepend initial guess rotation
272 :
273 : ! TURNING POINT (if calc grad) ------------------------------------------
274 : IF (PRESENT(matrix_G)) THEN
275 : CPASSERT(PRESENT(matrix_M))
276 :
277 : CALL dbcsr_get_block_p(matrix=pao%matrix_G, row=iatom, col=iatom, block=block_G, found=found)
278 : CPASSERT(ASSOCIATED(block_G))
279 : CALL dbcsr_get_block_p(matrix=matrix_M, row=iatom, col=iatom, block=block_M, found=found)
280 : ! don't check ASSOCIATED(block_M), it might have been filtered out.
281 :
282 : ALLOCATE (block_D(N, N), block_tmp(N, N), block_G_full(N, N))
283 : DO i = 1, N
284 : DO j = 1, N
285 : denom = evals(i) - evals(j)
286 : IF (i == j) THEN
287 : block_D(i, i) = EXP(evals(i)) ! diagonal elements
288 : ELSE IF (ABS(denom) > 1e-10_dp) THEN
289 : block_D(i, j) = (EXP(evals(i)) - EXP(evals(j)))/denom
290 : ELSE
291 : block_D(i, j) = 1.0_dp ! limit according to L'Hospital's rule
292 : END IF
293 : END DO
294 : END DO
295 :
296 : IF (ASSOCIATED(block_M)) THEN
297 : block_tmp = MATMUL(TRANSPOSE(block_U0), block_M)
298 : ELSE
299 : block_tmp = 0.0_dp
300 : END IF
301 : block_G_full = fold_derivatives(block_tmp, block_D, evecs)
302 :
303 : ! return only gradient for rotations between occupied and virtuals
304 : DO i = 1, nparams
305 : block_G(i, 1) = 2.0_dp*block_G_full(MOD(i - 1, M) + 1, M + (i - 1)/M + 1)
306 : END DO
307 :
308 : DEALLOCATE (block_D, block_tmp, block_G_full)
309 : END IF
310 :
311 : DEALLOCATE (block_X_full, evals, evecs)
312 :
313 : END DO
314 : CALL dbcsr_iterator_stop(iter)
315 : !$OMP END PARALLEL
316 :
317 2710 : CALL timestop(handle)
318 2710 : END SUBROUTINE pao_calc_U_exp
319 :
320 : ! **************************************************************************************************
321 : !> \brief Helper routine, for calculating derivatives
322 : !> \param M ...
323 : !> \param D ...
324 : !> \param R ...
325 : !> \return ...
326 : ! **************************************************************************************************
327 683 : FUNCTION fold_derivatives(M, D, R) RESULT(G)
328 : REAL(dp), DIMENSION(:, :), INTENT(IN) :: M
329 : COMPLEX(dp), DIMENSION(:, :), INTENT(IN) :: D, R
330 : REAL(dp), DIMENSION(SIZE(M, 1), SIZE(M, 1)) :: G
331 :
332 683 : COMPLEX(dp), DIMENSION(:, :), POINTER :: F, RF, RM, RMR
333 : INTEGER :: n
334 683 : REAL(dp), DIMENSION(:, :), POINTER :: RFR
335 :
336 683 : n = SIZE(M, 1)
337 :
338 8879 : ALLOCATE (RM(n, n), RMR(n, n), F(n, n), RF(n, n), RFR(n, n))
339 :
340 642537 : RM = MATMUL(TRANSPOSE(CONJG(R)), TRANSPOSE(M))
341 1133318 : RMR = MATMUL(RM, R)
342 101626 : F = RMR*D !Hadamard product
343 641854 : RF = MATMUL(R, F)
344 1133318 : RFR = REAL(MATMUL(RF, TRANSPOSE(CONJG(R))), dp)
345 :
346 : ! gradient dE/dX has to be anti-symmetric
347 50813 : G = 0.5_dp*(TRANSPOSE(RFR) - RFR)
348 :
349 683 : DEALLOCATE (RM, RMR, F, RF, RFR)
350 683 : END FUNCTION fold_derivatives
351 :
352 683 : END MODULE pao_param_exp
|