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 Regressions for complex k-point OT derivatives and preconditioner guards.
10 : ! **************************************************************************************************
11 302 : PROGRAM qs_ot_complex_ref_unittest
12 2 : USE bibliography, ONLY: add_all_references
13 : USE cp_blacs_env, ONLY: cp_blacs_env_create,&
14 : cp_blacs_env_release,&
15 : cp_blacs_env_type
16 : USE cp_cfm_types, ONLY: cp_cfm_create,&
17 : cp_cfm_get_element,&
18 : cp_cfm_release,&
19 : cp_cfm_set_all,&
20 : cp_cfm_set_element,&
21 : cp_cfm_type
22 : USE cp_dbcsr_api, ONLY: &
23 : dbcsr_create, dbcsr_distribution_new, dbcsr_distribution_release, dbcsr_distribution_type, &
24 : dbcsr_finalize, dbcsr_finalize_lib, dbcsr_get_block_p, dbcsr_init_lib, dbcsr_put_block, &
25 : dbcsr_release, dbcsr_reserve_blocks, dbcsr_type, dbcsr_type_no_symmetry
26 : USE cp_fm_struct, ONLY: cp_fm_struct_create,&
27 : cp_fm_struct_release,&
28 : cp_fm_struct_type
29 : USE cp_fm_types, ONLY: cp_fm_create,&
30 : cp_fm_get_submatrix,&
31 : cp_fm_release,&
32 : cp_fm_set_submatrix,&
33 : cp_fm_type
34 : USE cp_log_handling, ONLY: cp_add_default_logger,&
35 : cp_logger_create,&
36 : cp_logger_release,&
37 : cp_logger_type,&
38 : cp_rm_default_logger
39 : USE input_constants, ONLY: &
40 : ot_precond_full_all, ot_precond_full_kinetic, ot_precond_full_single, &
41 : ot_precond_full_single_inverse, ot_precond_none, ot_precond_s_inverse, &
42 : ot_precond_solver_chebyshev, ot_precond_solver_default, ot_precond_solver_direct, &
43 : ot_precond_solver_inv_chol
44 : USE kinds, ONLY: dp
45 : USE kpoint_methods, ONLY: kpoint_ot_energy_weighted_density
46 : USE machine, ONLY: default_output_unit
47 : USE mathlib, ONLY: diag_complex
48 : USE message_passing, ONLY: mp_comm_self,&
49 : mp_comm_type,&
50 : mp_para_env_release,&
51 : mp_para_env_type,&
52 : mp_world_finalize,&
53 : mp_world_init
54 : USE preconditioner, ONLY: apply_preconditioner
55 : USE preconditioner_makes, ONLY: make_complex_full_all,&
56 : make_complex_full_kinetic,&
57 : make_complex_full_s_inverse,&
58 : make_complex_full_single,&
59 : make_complex_full_single_inverse
60 : USE preconditioner_types, ONLY: destroy_preconditioner,&
61 : init_preconditioner,&
62 : preconditioner_type
63 : USE qs_ot, ONLY: &
64 : qs_ot_antihermitian_spectral_norm, qs_ot_apply_complex_frechet_dbcsr, &
65 : qs_ot_complex_exp_frechet_kernel, qs_ot_density_secant_hessian, &
66 : qs_ot_density_secant_orbital_overlaps, qs_ot_density_secant_projected_hessian, &
67 : qs_ot_density_tangent, qs_ot_finite_rotation_response, qs_ot_fixed_n_energy_gradient, &
68 : qs_ot_fixed_n_energy_hessian, qs_ot_fixed_n_projector_frechet, qs_ot_fixed_n_schur_block, &
69 : qs_ot_generate_rotation, qs_ot_get_derivative_complex, qs_ot_get_derivative_ref_complex, &
70 : qs_ot_get_orbitals_ref_complex, qs_ot_get_p_complex, qs_ot_projected_response_update, &
71 : qs_ot_rot_mat_derivative, qs_ot_symmetric_abs_solve, qs_ot_symmetric_sr1_update
72 : USE qs_ot_types, ONLY: qs_ot_kpoint_preconditioner_scale,&
73 : qs_ot_kpoint_preconditioner_solver_supported,&
74 : qs_ot_kpoint_preconditioner_supported,&
75 : qs_ot_type
76 : USE reference_manager, ONLY: remove_all_references
77 :
78 : IMPLICIT NONE
79 :
80 : INTEGER, PARAMETER :: k = 2, n = 3
81 :
82 : COMPLEX(KIND=dp), DIMENSION(n, k) :: direction, gradient, gradient_old, hc, q, x
83 : COMPLEX(KIND=dp), DIMENSION(n, n) :: hamiltonian
84 : COMPLEX(KIND=dp), DIMENSION(k, k) :: b, inverse_sqrt
85 : INTEGER :: io_unit, mynode, nfail
86 4 : INTEGER, DIMENSION(:), POINTER :: col_dist, col_k, row_dist, row_k, row_n
87 2 : INTEGER, DIMENSION(:, :), POINTER :: pgrid
88 : LOGICAL :: found
89 : REAL(KIND=dp) :: error, fd_slope, hstep, old_error, slope
90 2 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: block_p
91 : TYPE(cp_logger_type), POINTER :: logger
92 : TYPE(dbcsr_distribution_type) :: dist_kk, dist_nk
93 : TYPE(dbcsr_type), TARGET :: b_im, b_re, f_im, f_re, g_im, g_re, &
94 : hc_im, hc_re, q_im, q_re, sx_im, sx_re, &
95 : tmp_kk, x_im, x_re
96 : TYPE(dbcsr_type), POINTER :: hc_im_p, hc_re_p
97 : TYPE(mp_comm_type) :: mp_comm
98 : TYPE(mp_para_env_type), POINTER :: para_env
99 320 : TYPE(qs_ot_type) :: ot_env
100 :
101 2 : nfail = 0
102 2 : CALL test_preconditioner_support(nfail)
103 2 : NULLIFY (hc_im_p, hc_re_p, logger, para_env)
104 2 : CALL mp_world_init(mp_comm)
105 2 : mynode = mp_comm%mepos
106 2 : io_unit = -1
107 2 : IF (mynode == 0) io_unit = default_output_unit
108 2 : CALL test_antihermitian_spectral_norm(mynode, nfail)
109 2 : CALL test_fixed_n_mermin_energy(mynode, nfail)
110 2 : CALL test_fixed_n_projector_frechet(mynode, nfail)
111 2 : CALL test_finite_rotation_response(mynode, nfail)
112 2 : CALL test_fixed_n_rotation_schur(mynode, nfail)
113 2 : CALL test_symmetric_abs_solve(mynode, nfail)
114 2 : CALL test_projected_response_update(mynode, nfail)
115 2 : CALL test_symmetric_sr1_update(mynode, nfail)
116 2 : CALL test_density_secant_hessian(mynode, nfail)
117 2 : CALL test_joint_spin_density_secant(mynode, nfail)
118 2 : CALL test_density_tangent(mynode, nfail)
119 2 : CALL test_density_secant_moving_subspace(mynode, nfail)
120 2 : ALLOCATE (para_env)
121 2 : CALL para_env%from_dup(mp_comm)
122 : CALL cp_logger_create(logger, para_env=para_env, default_global_unit_nr=io_unit, &
123 2 : close_global_unit_on_dealloc=.FALSE.)
124 2 : CALL cp_add_default_logger(logger)
125 2 : CALL add_all_references()
126 2 : CALL dbcsr_init_lib(mp_comm%get_handle(), io_unit)
127 2 : CALL test_real_rotation_frechet(para_env, nfail)
128 2 : CALL test_complex_rotation_frechet(para_env, nfail)
129 2 : CALL test_sparse_frechet_patterns(para_env, nfail)
130 2 : CALL test_complex_ref_rotation(para_env, nfail)
131 2 : CALL test_complex_preconditioner_gauge(para_env, nfail)
132 2 : CALL test_kpoint_ot_energy_weighted_density(para_env, nfail)
133 :
134 : x(:, 1) = [CMPLX(1.10_dp, 0.10_dp, KIND=dp), CMPLX(0.20_dp, -0.30_dp, KIND=dp), &
135 8 : CMPLX(-0.10_dp, 0.20_dp, KIND=dp)]
136 : x(:, 2) = [CMPLX(0.30_dp, 0.40_dp, KIND=dp), CMPLX(0.90_dp, -0.20_dp, KIND=dp), &
137 8 : CMPLX(0.25_dp, 0.10_dp, KIND=dp)]
138 : direction(:, 1) = [CMPLX(0.17_dp, -0.09_dp, KIND=dp), CMPLX(-0.21_dp, 0.13_dp, KIND=dp), &
139 8 : CMPLX(0.08_dp, 0.19_dp, KIND=dp)]
140 : direction(:, 2) = [CMPLX(-0.12_dp, 0.23_dp, KIND=dp), CMPLX(0.16_dp, 0.07_dp, KIND=dp), &
141 8 : CMPLX(-0.14_dp, -0.11_dp, KIND=dp)]
142 :
143 : hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
144 2 : hamiltonian(1, 1) = CMPLX(0.70_dp, 0.0_dp, KIND=dp)
145 2 : hamiltonian(2, 2) = CMPLX(-0.40_dp, 0.0_dp, KIND=dp)
146 2 : hamiltonian(3, 3) = CMPLX(1.10_dp, 0.0_dp, KIND=dp)
147 2 : hamiltonian(1, 2) = CMPLX(0.20_dp, 0.10_dp, KIND=dp)
148 2 : hamiltonian(2, 1) = CONJG(hamiltonian(1, 2))
149 2 : hamiltonian(1, 3) = CMPLX(-0.10_dp, 0.05_dp, KIND=dp)
150 2 : hamiltonian(3, 1) = CONJG(hamiltonian(1, 3))
151 2 : hamiltonian(2, 3) = CMPLX(0.15_dp, 0.20_dp, KIND=dp)
152 2 : hamiltonian(3, 2) = CONJG(hamiltonian(2, 3))
153 :
154 38 : CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(x)), x), inverse_sqrt)
155 38 : q = MATMUL(x, inverse_sqrt)
156 70 : hc = 2.0_dp*MATMUL(hamiltonian, q)
157 :
158 2 : ALLOCATE (pgrid(0:0, 0:0), row_dist(1), col_dist(1), row_n(1), row_k(1), col_k(1))
159 6 : pgrid(:, :) = 0
160 4 : row_dist(:) = 0
161 4 : col_dist(:) = 0
162 4 : row_n(:) = n
163 4 : row_k(:) = k
164 4 : col_k(:) = k
165 : CALL dbcsr_distribution_new(dist_nk, group=mp_comm_self%get_handle(), pgrid=pgrid, &
166 2 : row_dist=row_dist, col_dist=col_dist)
167 : CALL dbcsr_distribution_new(dist_kk, group=mp_comm_self%get_handle(), pgrid=pgrid, &
168 2 : row_dist=row_dist, col_dist=col_dist)
169 2 : CALL test_complex_strict_derivative(dist_nk, dist_kk, row_n, row_k, col_k, para_env, nfail)
170 :
171 2 : CALL create_one_block(x_re, "complex_ref_x_re", dist_nk, row_n, col_k)
172 2 : CALL create_one_block(x_im, "complex_ref_x_im", dist_nk, row_n, col_k)
173 2 : CALL create_one_block(sx_re, "complex_ref_sx_re", dist_nk, row_n, col_k)
174 2 : CALL create_one_block(sx_im, "complex_ref_sx_im", dist_nk, row_n, col_k)
175 2 : CALL create_one_block(hc_re, "complex_ref_hc_re", dist_nk, row_n, col_k)
176 2 : CALL create_one_block(hc_im, "complex_ref_hc_im", dist_nk, row_n, col_k)
177 2 : CALL create_one_block(g_re, "complex_ref_g_re", dist_nk, row_n, col_k)
178 2 : CALL create_one_block(g_im, "complex_ref_g_im", dist_nk, row_n, col_k)
179 2 : CALL create_one_block(q_re, "complex_ref_q_re", dist_nk, row_n, col_k)
180 2 : CALL create_one_block(q_im, "complex_ref_q_im", dist_nk, row_n, col_k)
181 2 : CALL create_one_block(f_re, "complex_ref_f_re", dist_kk, row_k, col_k)
182 2 : CALL create_one_block(f_im, "complex_ref_f_im", dist_kk, row_k, col_k)
183 2 : CALL create_one_block(b_re, "complex_ref_b_re", dist_kk, row_k, col_k)
184 2 : CALL create_one_block(b_im, "complex_ref_b_im", dist_kk, row_k, col_k)
185 2 : CALL create_one_block(tmp_kk, "complex_ref_tmp_kk", dist_kk, row_k, col_k)
186 :
187 2 : CALL put_complex_pair(x_re, x_im, x)
188 2 : CALL put_complex_pair(sx_re, sx_im, x)
189 2 : CALL put_complex_pair(hc_re, hc_im, hc)
190 2 : CALL put_complex_pair(f_re, f_im, inverse_sqrt)
191 :
192 2 : ot_env%has_complex_kpoint_state = .TRUE.
193 2 : ot_env%settings%eps_irac_filter_matrix = 0.0_dp
194 2 : ot_env%matrix_x => x_re
195 2 : ot_env%matrix_x_im => x_im
196 2 : ot_env%matrix_sx => sx_re
197 2 : ot_env%matrix_sx_im => sx_im
198 2 : ot_env%matrix_gx => g_re
199 2 : ot_env%matrix_gx_im => g_im
200 2 : ot_env%matrix_ref_inv_sqrt => f_re
201 2 : ot_env%matrix_ref_inv_sqrt_im => f_im
202 2 : ot_env%buf1_k_k_sym => b_re
203 2 : ot_env%buf2_k_k_sym => b_im
204 2 : ot_env%buf3_k_k_sym => tmp_kk
205 2 : ot_env%buf1_n_k => q_re
206 2 : ot_env%buf1_n_k_dp => q_im
207 :
208 2 : hc_re_p => hc_re
209 2 : hc_im_p => hc_im
210 2 : CALL qs_ot_get_derivative_ref_complex(hc_re_p, hc_im_p, ot_env)
211 :
212 2 : gradient(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
213 2 : CALL dbcsr_get_block_p(g_re, 1, 1, block_p, found)
214 18 : IF (found) gradient = gradient + CMPLX(block_p, 0.0_dp, KIND=dp)
215 2 : CALL dbcsr_get_block_p(g_im, 1, 1, block_p, found)
216 18 : IF (found) gradient = gradient + CMPLX(0.0_dp, block_p, KIND=dp)
217 :
218 2 : hstep = 1.0E-6_dp
219 : fd_slope = (polar_energy(x + hstep*direction, hamiltonian) - &
220 34 : polar_energy(x - hstep*direction, hamiltonian))/(2.0_dp*hstep)
221 18 : slope = REAL(SUM(CONJG(gradient)*direction), KIND=dp)
222 2 : error = ABS(slope - fd_slope)
223 :
224 38 : b = MATMUL(CONJG(TRANSPOSE(x)), hc)
225 54 : gradient_old = hc - MATMUL(x, b)
226 18 : old_error = ABS(REAL(SUM(CONJG(gradient_old)*direction), KIND=dp) - fd_slope)
227 2 : IF (error > 5.0E-8_dp .OR. old_error < 1.0E-3_dp) nfail = nfail + 1
228 :
229 2 : IF (mynode == 0) THEN
230 1 : WRITE (io_unit, '(A,3(1X,ES13.6))') "complex finite-REF pullback: fd/predicted/error", &
231 2 : fd_slope, slope, error
232 1 : WRITE (io_unit, '(A,1X,ES13.6)') "discarded projected-gradient error", old_error
233 : END IF
234 :
235 2 : CALL dbcsr_release(b_im)
236 2 : CALL dbcsr_release(b_re)
237 2 : CALL dbcsr_release(tmp_kk)
238 2 : CALL dbcsr_release(f_im)
239 2 : CALL dbcsr_release(f_re)
240 2 : CALL dbcsr_release(q_im)
241 2 : CALL dbcsr_release(q_re)
242 2 : CALL dbcsr_release(g_im)
243 2 : CALL dbcsr_release(g_re)
244 2 : CALL dbcsr_release(hc_im)
245 2 : CALL dbcsr_release(hc_re)
246 2 : CALL dbcsr_release(sx_im)
247 2 : CALL dbcsr_release(sx_re)
248 2 : CALL dbcsr_release(x_im)
249 2 : CALL dbcsr_release(x_re)
250 2 : CALL dbcsr_distribution_release(dist_kk)
251 2 : CALL dbcsr_distribution_release(dist_nk)
252 2 : DEALLOCATE (pgrid, row_dist, col_dist, row_n, row_k, col_k)
253 :
254 2 : CALL dbcsr_finalize_lib()
255 2 : CALL remove_all_references()
256 2 : CALL cp_rm_default_logger()
257 2 : CALL cp_logger_release(logger)
258 2 : CALL mp_para_env_release(para_env)
259 2 : CALL mp_world_finalize()
260 :
261 6 : IF (nfail > 0) ERROR STOP "qs_ot_complex_ref_unittest failed"
262 :
263 : CONTAINS
264 :
265 : ! **************************************************************************************************
266 : !> \brief Check the noncanonical complex OT energy-weighted density and its gauge covariance.
267 : !> \param para_env parallel environment
268 : !> \param nfail accumulated number of failures
269 : ! **************************************************************************************************
270 2 : SUBROUTINE test_kpoint_ot_energy_weighted_density(para_env, nfail)
271 : TYPE(mp_para_env_type), POINTER :: para_env
272 : INTEGER, INTENT(INOUT) :: nfail
273 :
274 : INTEGER, PARAMETER :: nao = 4, nmo = 3
275 :
276 : COMPLEX(KIND=dp), DIMENSION(nao, nao) :: actual, expected, hmat, rotated
277 : COMPLEX(KIND=dp), DIMENSION(nao, nmo) :: coeff, coeff_rot, hc_dense, hc_rot
278 : COMPLEX(KIND=dp), DIMENSION(nmo, nmo) :: hblock, lagrange, rotation
279 : INTEGER :: i, j
280 : REAL(KIND=dp) :: error, gauge_error, hermitian_error, &
281 : phase, pi
282 : REAL(KIND=dp), DIMENSION(nao, nao) :: actual_im, actual_re, rotated_im, &
283 : rotated_re
284 : REAL(KIND=dp), DIMENSION(nmo) :: occupation, uniform_occupation
285 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
286 : TYPE(cp_fm_struct_type), POINTER :: coeff_struct, hmat_struct
287 : TYPE(cp_fm_type) :: coeff_im_fm, coeff_re_fm, hc_im_fm, &
288 : hc_re_fm, wmat_im_fm, wmat_re_fm
289 :
290 2 : NULLIFY (blacs_env, coeff_struct, hmat_struct)
291 2 : pi = ACOS(-1.0_dp)
292 2 : hmat(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
293 2 : hmat(1, 1) = CMPLX(-0.8_dp, 0.0_dp, KIND=dp)
294 2 : hmat(2, 2) = CMPLX(0.1_dp, 0.0_dp, KIND=dp)
295 2 : hmat(3, 3) = CMPLX(0.7_dp, 0.0_dp, KIND=dp)
296 2 : hmat(4, 4) = CMPLX(1.4_dp, 0.0_dp, KIND=dp)
297 2 : hmat(1, 2) = CMPLX(0.21_dp, 0.13_dp, KIND=dp)
298 2 : hmat(1, 3) = CMPLX(-0.17_dp, 0.09_dp, KIND=dp)
299 2 : hmat(1, 4) = CMPLX(0.08_dp, -0.14_dp, KIND=dp)
300 2 : hmat(2, 3) = CMPLX(0.31_dp, -0.11_dp, KIND=dp)
301 2 : hmat(2, 4) = CMPLX(-0.19_dp, -0.07_dp, KIND=dp)
302 2 : hmat(3, 4) = CMPLX(0.27_dp, 0.16_dp, KIND=dp)
303 10 : DO i = 1, nao
304 22 : DO j = i + 1, nao
305 20 : hmat(j, i) = CONJG(hmat(i, j))
306 : END DO
307 : END DO
308 8 : DO j = 1, nmo
309 32 : DO i = 1, nao
310 24 : phase = 2.0_dp*pi*REAL((i - 1)*(j - 1), KIND=dp)/REAL(nao, KIND=dp)
311 30 : coeff(i, j) = CMPLX(COS(phase), SIN(phase), KIND=dp)/SQRT(REAL(nao, KIND=dp))
312 : END DO
313 : END DO
314 128 : hc_dense = MATMUL(hmat, coeff)
315 2 : occupation = [1.0_dp, 0.63_dp, 0.11_dp]
316 :
317 98 : hblock = MATMUL(CONJG(TRANSPOSE(coeff)), hc_dense)
318 8 : DO j = 1, nmo
319 26 : lagrange(:, j) = hblock(:, j)*occupation(j)
320 : END DO
321 50 : lagrange = 0.5_dp*(lagrange + CONJG(TRANSPOSE(lagrange)))
322 226 : expected = MATMUL(MATMUL(coeff, lagrange), CONJG(TRANSPOSE(coeff)))
323 :
324 2 : CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=para_env)
325 : CALL cp_fm_struct_create(hmat_struct, nrow_global=nao, ncol_global=nao, &
326 2 : context=blacs_env, para_env=para_env)
327 : CALL cp_fm_struct_create(coeff_struct, nrow_global=nao, ncol_global=nmo, &
328 2 : context=blacs_env, para_env=para_env)
329 2 : CALL cp_fm_create(coeff_re_fm, coeff_struct)
330 2 : CALL cp_fm_create(coeff_im_fm, coeff_struct)
331 2 : CALL cp_fm_create(hc_re_fm, coeff_struct)
332 2 : CALL cp_fm_create(hc_im_fm, coeff_struct)
333 2 : CALL cp_fm_create(wmat_re_fm, hmat_struct)
334 2 : CALL cp_fm_create(wmat_im_fm, hmat_struct)
335 32 : CALL cp_fm_set_submatrix(coeff_re_fm, REAL(coeff, KIND=dp))
336 32 : CALL cp_fm_set_submatrix(coeff_im_fm, AIMAG(coeff))
337 32 : CALL cp_fm_set_submatrix(hc_re_fm, REAL(hc_dense, KIND=dp))
338 32 : CALL cp_fm_set_submatrix(hc_im_fm, AIMAG(hc_dense))
339 :
340 : CALL kpoint_ot_energy_weighted_density( &
341 : coeff_re_fm, coeff_im_fm, hc_re_fm, hc_im_fm, occupation, &
342 2 : wmat_re_fm, wmat_im_fm)
343 2 : CALL cp_fm_get_submatrix(wmat_re_fm, actual_re)
344 2 : CALL cp_fm_get_submatrix(wmat_im_fm, actual_im)
345 42 : actual = CMPLX(actual_re, actual_im, KIND=dp)
346 42 : error = MAXVAL(ABS(actual - expected))
347 42 : hermitian_error = MAXVAL(ABS(actual - CONJG(TRANSPOSE(actual))))
348 :
349 2 : rotation(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
350 2 : rotation(1, 1) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
351 2 : rotation(1, 2) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
352 2 : rotation(2, 1) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
353 2 : rotation(2, 2) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
354 2 : rotation(3, 3) = CMPLX(0.0_dp, 1.0_dp, KIND=dp)
355 98 : coeff_rot = MATMUL(coeff, rotation)
356 98 : hc_rot = MATMUL(hc_dense, rotation)
357 8 : uniform_occupation(:) = 1.0_dp
358 32 : CALL cp_fm_set_submatrix(coeff_re_fm, REAL(coeff_rot, KIND=dp))
359 32 : CALL cp_fm_set_submatrix(coeff_im_fm, AIMAG(coeff_rot))
360 32 : CALL cp_fm_set_submatrix(hc_re_fm, REAL(hc_rot, KIND=dp))
361 32 : CALL cp_fm_set_submatrix(hc_im_fm, AIMAG(hc_rot))
362 : CALL kpoint_ot_energy_weighted_density( &
363 : coeff_re_fm, coeff_im_fm, hc_re_fm, hc_im_fm, uniform_occupation, &
364 2 : wmat_re_fm, wmat_im_fm)
365 2 : CALL cp_fm_get_submatrix(wmat_re_fm, rotated_re)
366 2 : CALL cp_fm_get_submatrix(wmat_im_fm, rotated_im)
367 42 : rotated = CMPLX(rotated_re, rotated_im, KIND=dp)
368 226 : expected = MATMUL(MATMUL(coeff, hblock), CONJG(TRANSPOSE(coeff)))
369 42 : gauge_error = MAXVAL(ABS(rotated - expected))
370 :
371 2 : IF (error > 5.0E-12_dp .OR. hermitian_error > 5.0E-12_dp .OR. &
372 0 : gauge_error > 5.0E-12_dp) nfail = nfail + 1
373 2 : IF (para_env%is_source()) THEN
374 1 : WRITE (*, '(A,3(1X,ES13.6))') 'complex OT W error/hermitian/gauge', &
375 2 : error, hermitian_error, gauge_error
376 : END IF
377 :
378 2 : CALL cp_fm_release(wmat_im_fm)
379 2 : CALL cp_fm_release(wmat_re_fm)
380 2 : CALL cp_fm_release(hc_im_fm)
381 2 : CALL cp_fm_release(hc_re_fm)
382 2 : CALL cp_fm_release(coeff_im_fm)
383 2 : CALL cp_fm_release(coeff_re_fm)
384 2 : CALL cp_fm_struct_release(coeff_struct)
385 2 : CALL cp_fm_struct_release(hmat_struct)
386 2 : CALL cp_blacs_env_release(blacs_env)
387 :
388 6 : END SUBROUTINE test_kpoint_ot_energy_weighted_density
389 : ! **************************************************************************************************
390 : !> \brief Check the complex rotation norm against an analytic pair and a unitary gauge change.
391 : !> \param mynode MPI rank
392 : !> \param nfail accumulated failures
393 : ! **************************************************************************************************
394 2 : SUBROUTINE test_antihermitian_spectral_norm(mynode, nfail)
395 : INTEGER, INTENT(IN) :: mynode
396 : INTEGER, INTENT(INOUT) :: nfail
397 :
398 : COMPLEX(KIND=dp), DIMENSION(3) :: phase
399 : COMPLEX(KIND=dp), DIMENSION(3, 3) :: generator, transformed
400 : INTEGER :: i, j
401 : REAL(KIND=dp) :: error, norm, transformed_norm
402 :
403 2 : generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
404 2 : generator(1, 2) = CMPLX(0.3_dp, 0.4_dp, KIND=dp)
405 2 : generator(2, 1) = -CONJG(generator(1, 2))
406 2 : phase = EXP(CMPLX(0.0_dp, 1.0_dp, KIND=dp)*[0.37_dp, -0.51_dp, 0.83_dp])
407 8 : DO j = 1, 3
408 26 : DO i = 1, 3
409 24 : transformed(i, j) = CONJG(phase(i))*generator(i, j)*phase(j)
410 : END DO
411 : END DO
412 :
413 2 : norm = qs_ot_antihermitian_spectral_norm(generator)
414 2 : transformed_norm = qs_ot_antihermitian_spectral_norm(transformed)
415 2 : error = MAX(ABS(norm - 0.5_dp), ABS(transformed_norm - norm))
416 2 : IF (mynode == 0) WRITE (default_output_unit, '(A,ES14.6)') &
417 1 : "complex rotation spectral-norm error: ", error
418 2 : IF (error > 1.0E-13_dp) nfail = nfail + 1
419 :
420 2 : END SUBROUTINE test_antihermitian_spectral_norm
421 :
422 : ! **************************************************************************************************
423 : !> \brief Check the fixed-N Mermin gradient, dense gauge projector, and occupation Hessian.
424 : !> \param mynode MPI rank
425 : !> \param nfail accumulated number of failures
426 : ! **************************************************************************************************
427 2 : SUBROUTINE test_fixed_n_mermin_energy(mynode, nfail)
428 : INTEGER, INTENT(IN) :: mynode
429 : INTEGER, INTENT(INOUT) :: nfail
430 :
431 : INTEGER, PARAMETER :: nbands = 4
432 : REAL(KIND=dp), PARAMETER :: fd_step = 1.0E-6_dp, maxocc = 2.0_dp, &
433 : target_electrons = 2.25_dp, &
434 : temperature = 0.07_dp
435 :
436 : REAL(KIND=dp) :: fd_slope, gradient_error, hessian_error, &
437 : mu, predicted_slope, response_sum, &
438 : weighted_residual
439 : REAL(KIND=dp), DIMENSION(nbands) :: direction, energies, gradient, gradient_minus, &
440 : gradient_plus, hessian_action, occupation, rayleigh, response, response_minus, &
441 : response_plus, weights
442 : REAL(KIND=dp), DIMENSION(nbands, nbands) :: hessian
443 :
444 2 : energies(:) = [-0.24_dp, -0.05_dp, 0.08_dp, 0.31_dp]
445 2 : rayleigh(:) = [-0.19_dp, -0.02_dp, 0.04_dp, 0.27_dp]
446 2 : weights(:) = [0.35_dp, 0.35_dp, 0.65_dp, 0.65_dp]
447 2 : direction(:) = [0.17_dp, -0.11_dp, 0.08_dp, -0.05_dp]
448 :
449 : CALL fixed_n_fermi_occupations(energies, weights, target_electrons, temperature, &
450 2 : maxocc, occupation, mu)
451 10 : response(:) = weights(:)*occupation(:)*(maxocc - occupation(:))/(maxocc*temperature)
452 10 : response_sum = SUM(response)
453 10 : weighted_residual = DOT_PRODUCT(response, rayleigh - energies)
454 : CALL qs_ot_fixed_n_energy_gradient(rayleigh, energies, response, response_sum, &
455 2 : weighted_residual, gradient)
456 :
457 : fd_slope = (fixed_n_mermin_value(rayleigh, energies + fd_step*direction, weights, &
458 : target_electrons, temperature, maxocc) - &
459 : fixed_n_mermin_value(rayleigh, energies - fd_step*direction, weights, &
460 18 : target_electrons, temperature, maxocc))/(2.0_dp*fd_step)
461 10 : predicted_slope = DOT_PRODUCT(gradient, direction)
462 10 : gradient_error = MAX(ABS(fd_slope - predicted_slope), ABS(SUM(gradient)))
463 :
464 : ! At the canonical point h_i=e_i, the exact local Hessian is the dense fixed-N projector.
465 2 : rayleigh(:) = energies(:)
466 2 : CALL qs_ot_fixed_n_energy_hessian(response, response_sum, hessian)
467 42 : hessian_action(:) = MATMUL(hessian, direction)
468 :
469 : CALL fixed_n_fermi_occupations(energies + fd_step*direction, weights, target_electrons, &
470 10 : temperature, maxocc, occupation, mu)
471 10 : response_plus(:) = weights(:)*occupation(:)*(maxocc - occupation(:))/(maxocc*temperature)
472 10 : weighted_residual = DOT_PRODUCT(response_plus, rayleigh - energies - fd_step*direction)
473 : CALL qs_ot_fixed_n_energy_gradient(rayleigh, energies + fd_step*direction, response_plus, &
474 18 : SUM(response_plus), weighted_residual, gradient_plus)
475 :
476 : CALL fixed_n_fermi_occupations(energies - fd_step*direction, weights, target_electrons, &
477 10 : temperature, maxocc, occupation, mu)
478 10 : response_minus(:) = weights(:)*occupation(:)*(maxocc - occupation(:))/(maxocc*temperature)
479 10 : weighted_residual = DOT_PRODUCT(response_minus, rayleigh - energies + fd_step*direction)
480 : CALL qs_ot_fixed_n_energy_gradient(rayleigh, energies - fd_step*direction, response_minus, &
481 18 : SUM(response_minus), weighted_residual, gradient_minus)
482 :
483 : hessian_error = MAXVAL(ABS((gradient_plus - gradient_minus)/(2.0_dp*fd_step) - &
484 10 : hessian_action))
485 42 : hessian_error = MAX(hessian_error, MAXVAL(ABS(hessian - TRANSPOSE(hessian))))
486 52 : hessian_error = MAX(hessian_error, MAXVAL(ABS(MATMUL(hessian, SPREAD(1.0_dp, 1, nbands)))))
487 :
488 2 : IF (gradient_error > 2.0E-8_dp .OR. hessian_error > 2.0E-7_dp) nfail = nfail + 1
489 2 : IF (mynode == 0) THEN
490 1 : WRITE (io_unit, '(A,2(1X,ES13.6))') "fixed-N Mermin gradient/Hessian errors:", &
491 2 : gradient_error, hessian_error
492 : END IF
493 :
494 2 : END SUBROUTINE test_fixed_n_mermin_energy
495 :
496 : ! **************************************************************************************************
497 : !> \brief Check the complex fixed-N spectral occupation-projector derivative by finite differences.
498 : !> \param mynode MPI rank
499 : !> \param nfail accumulated number of failures
500 : ! **************************************************************************************************
501 2 : SUBROUTINE test_fixed_n_projector_frechet(mynode, nfail)
502 : INTEGER, INTENT(IN) :: mynode
503 : INTEGER, INTENT(INOUT) :: nfail
504 :
505 : INTEGER, PARAMETER :: nbands = 3
506 : REAL(KIND=dp), PARAMETER :: fd_step = 1.0E-6_dp, kpoint_weight = 0.75_dp, maxocc = 2.0_dp, &
507 : target_electrons = 2.10_dp, temperature = 0.09_dp
508 :
509 : COMPLEX(KIND=dp), DIMENSION(nbands, nbands) :: dchc, derivative, derivative_fd, &
510 : hamiltonian, projector_minus, &
511 : projector_plus, vectors
512 : INTEGER :: i
513 : REAL(KIND=dp) :: error, mu, trace_error
514 : REAL(KIND=dp), DIMENSION(nbands) :: eigenvalues, occupation, response, &
515 : weights
516 :
517 2 : hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
518 2 : hamiltonian(1, 1) = CMPLX(-0.22_dp, 0.0_dp, KIND=dp)
519 2 : hamiltonian(2, 2) = CMPLX(0.03_dp, 0.0_dp, KIND=dp)
520 2 : hamiltonian(3, 3) = CMPLX(0.28_dp, 0.0_dp, KIND=dp)
521 2 : hamiltonian(1, 2) = CMPLX(0.04_dp, -0.03_dp, KIND=dp)
522 2 : hamiltonian(2, 1) = CONJG(hamiltonian(1, 2))
523 2 : hamiltonian(2, 3) = CMPLX(-0.02_dp, 0.05_dp, KIND=dp)
524 2 : hamiltonian(3, 2) = CONJG(hamiltonian(2, 3))
525 :
526 2 : dchc(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
527 2 : dchc(1, 1) = CMPLX(0.13_dp, 0.0_dp, KIND=dp)
528 2 : dchc(2, 2) = CMPLX(-0.08_dp, 0.0_dp, KIND=dp)
529 2 : dchc(3, 3) = CMPLX(0.05_dp, 0.0_dp, KIND=dp)
530 2 : dchc(1, 2) = CMPLX(0.17_dp, -0.11_dp, KIND=dp)
531 2 : dchc(2, 1) = CONJG(dchc(1, 2))
532 2 : dchc(2, 3) = CMPLX(0.12_dp, 0.09_dp, KIND=dp)
533 2 : dchc(3, 2) = CONJG(dchc(2, 3))
534 :
535 2 : CALL diag_complex(hamiltonian, vectors, eigenvalues)
536 8 : weights(:) = kpoint_weight
537 : CALL fixed_n_fermi_occupations(eigenvalues, weights, target_electrons, temperature, &
538 2 : maxocc, occupation, mu)
539 8 : response(:) = weights(:)*occupation(:)*(maxocc - occupation(:))/(maxocc*temperature)
540 : CALL qs_ot_fixed_n_projector_frechet(hamiltonian, dchc, occupation, kpoint_weight, &
541 8 : response, SUM(response), derivative)
542 :
543 : CALL fixed_n_spectral_projector(hamiltonian + fd_step*dchc, weights, target_electrons, &
544 26 : temperature, maxocc, projector_plus)
545 : CALL fixed_n_spectral_projector(hamiltonian - fd_step*dchc, weights, target_electrons, &
546 26 : temperature, maxocc, projector_minus)
547 26 : derivative_fd(:, :) = (projector_plus - projector_minus)/(2.0_dp*fd_step)
548 26 : error = MAXVAL(ABS(derivative - derivative_fd))
549 2 : trace_error = 0.0_dp
550 8 : DO i = 1, nbands
551 8 : trace_error = trace_error + REAL(derivative(i, i), KIND=dp)
552 : END DO
553 2 : error = MAX(error, ABS(trace_error))
554 :
555 2 : IF (error > 3.0E-7_dp) nfail = nfail + 1
556 2 : IF (mynode == 0) WRITE (io_unit, '(A,1X,ES13.6)') &
557 1 : "fixed-N complex occupation-projector error:", error
558 :
559 2 : END SUBROUTINE test_fixed_n_projector_frechet
560 :
561 : ! **************************************************************************************************
562 : !> \brief Check the finite complex REF rotation Hessian against the scalar band energy.
563 : !> \param mynode MPI rank
564 : !> \param nfail accumulated number of failures
565 : ! **************************************************************************************************
566 2 : SUBROUTINE test_finite_rotation_response(mynode, nfail)
567 : INTEGER, INTENT(IN) :: mynode
568 : INTEGER, INTENT(INOUT) :: nfail
569 :
570 : INTEGER, PARAMETER :: nbands = 3, nrotation = 6
571 : REAL(KIND=dp), PARAMETER :: fd_step = 2.0E-4_dp, &
572 : kpoint_weight = 0.625_dp
573 :
574 : COMPLEX(KIND=dp), DIMENSION(nbands, nbands) :: base_hamiltonian, chc, &
575 : direction_generator, generator, &
576 : rotation, rotation_minus, rotation_plus
577 : INTEGER :: i
578 : REAL(KIND=dp) :: energy, energy_minus, energy_plus, &
579 : gradient_error, hessian_error, &
580 : rayleigh_error, symmetry_error
581 : REAL(KIND=dp), DIMENSION(nbands) :: occupation, rayleigh_minus, rayleigh_plus
582 : REAL(KIND=dp), DIMENSION(nbands, nrotation) :: rayleigh_response
583 : REAL(KIND=dp), DIMENSION(nrotation) :: direction, gradient
584 : REAL(KIND=dp), DIMENSION(nrotation, nrotation) :: hessian
585 :
586 2 : base_hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
587 2 : base_hamiltonian(1, 1) = CMPLX(-0.31_dp, 0.0_dp, KIND=dp)
588 2 : base_hamiltonian(2, 2) = CMPLX(0.07_dp, 0.0_dp, KIND=dp)
589 2 : base_hamiltonian(3, 3) = CMPLX(0.42_dp, 0.0_dp, KIND=dp)
590 2 : base_hamiltonian(1, 2) = CMPLX(0.09_dp, -0.06_dp, KIND=dp)
591 2 : base_hamiltonian(1, 3) = CMPLX(-0.04_dp, 0.08_dp, KIND=dp)
592 2 : base_hamiltonian(2, 3) = CMPLX(0.11_dp, 0.05_dp, KIND=dp)
593 8 : DO i = 1, nbands
594 14 : base_hamiltonian(i, 1:i - 1) = CONJG(base_hamiltonian(1:i - 1, i))
595 : END DO
596 :
597 2 : generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
598 2 : generator(1, 2) = CMPLX(0.17_dp, -0.08_dp, KIND=dp)
599 2 : generator(1, 3) = CMPLX(-0.09_dp, 0.04_dp, KIND=dp)
600 2 : generator(2, 3) = CMPLX(0.12_dp, 0.07_dp, KIND=dp)
601 8 : DO i = 1, nbands
602 14 : generator(i, 1:i - 1) = -CONJG(generator(1:i - 1, i))
603 : END DO
604 :
605 2 : direction(:) = [0.14_dp, -0.07_dp, -0.11_dp, 0.09_dp, 0.05_dp, 0.13_dp]
606 2 : direction_generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
607 2 : direction_generator(1, 2) = CMPLX(direction(1), direction(2), KIND=dp)
608 2 : direction_generator(2, 1) = -CONJG(direction_generator(1, 2))
609 2 : direction_generator(1, 3) = CMPLX(direction(3), direction(4), KIND=dp)
610 2 : direction_generator(3, 1) = -CONJG(direction_generator(1, 3))
611 2 : direction_generator(2, 3) = CMPLX(direction(5), direction(6), KIND=dp)
612 2 : direction_generator(3, 2) = -CONJG(direction_generator(2, 3))
613 2 : occupation(:) = [1.73_dp, 0.88_dp, 0.19_dp]
614 :
615 2 : rotation(:, :) = dense_antihermitian_exp(generator)
616 : chc(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), &
617 158 : MATMUL(base_hamiltonian, rotation))
618 : CALL qs_ot_finite_rotation_response(chc, generator, occupation, kpoint_weight, &
619 2 : gradient, hessian, rayleigh_response)
620 :
621 26 : rotation_plus(:, :) = dense_antihermitian_exp(generator + fd_step*direction_generator)
622 26 : rotation_minus(:, :) = dense_antihermitian_exp(generator - fd_step*direction_generator)
623 2 : energy = finite_rotation_energy(rotation, base_hamiltonian, occupation, kpoint_weight)
624 2 : energy_plus = finite_rotation_energy(rotation_plus, base_hamiltonian, occupation, kpoint_weight)
625 2 : energy_minus = finite_rotation_energy(rotation_minus, base_hamiltonian, occupation, kpoint_weight)
626 : gradient_error = ABS((energy_plus - energy_minus)/(2.0_dp*fd_step) - &
627 14 : DOT_PRODUCT(gradient, direction))
628 : hessian_error = ABS((energy_plus - 2.0_dp*energy + energy_minus)/fd_step**2 - &
629 98 : DOT_PRODUCT(direction, MATMUL(hessian, direction)))
630 86 : symmetry_error = MAXVAL(ABS(hessian - TRANSPOSE(hessian)))
631 :
632 8 : DO i = 1, nbands
633 : rayleigh_plus(i) = REAL(DOT_PRODUCT(rotation_plus(:, i), &
634 96 : MATMUL(base_hamiltonian, rotation_plus(:, i))), KIND=dp)
635 : rayleigh_minus(i) = REAL(DOT_PRODUCT(rotation_minus(:, i), &
636 98 : MATMUL(base_hamiltonian, rotation_minus(:, i))), KIND=dp)
637 : END DO
638 : rayleigh_error = MAXVAL(ABS((rayleigh_plus - rayleigh_minus)/(2.0_dp*fd_step) - &
639 56 : MATMUL(rayleigh_response, direction)))
640 :
641 : IF (gradient_error > 5.0E-8_dp .OR. hessian_error > 2.0E-6_dp .OR. &
642 2 : rayleigh_error > 2.0E-7_dp .OR. symmetry_error > 1.0E-12_dp) nfail = nfail + 1
643 2 : IF (mynode == 0) THEN
644 1 : WRITE (io_unit, '(A,4(1X,ES13.6))') "finite complex rotation response errors:", &
645 2 : gradient_error, hessian_error, rayleigh_error, symmetry_error
646 : END IF
647 :
648 2 : END SUBROUTINE test_finite_rotation_response
649 :
650 : ! **************************************************************************************************
651 : !> \brief Check the coupled fixed-N rotation/energy Hessian and its Schur decomposition.
652 : !> \param mynode MPI rank
653 : !> \param nfail accumulated number of failures
654 : ! **************************************************************************************************
655 2 : SUBROUTINE test_fixed_n_rotation_schur(mynode, nfail)
656 : INTEGER, INTENT(IN) :: mynode
657 : INTEGER, INTENT(INOUT) :: nfail
658 :
659 : INTEGER, PARAMETER :: nbands = 3, nrotation = 6
660 : REAL(KIND=dp), PARAMETER :: fd_step = 1.0E-4_dp, &
661 : kpoint_weight = 0.625_dp, &
662 : maxocc = 2.0_dp, temperature = 0.08_dp
663 :
664 : COMPLEX(KIND=dp), DIMENSION(nbands, nbands) :: base_hamiltonian, chc, &
665 : direction_generator, generator, &
666 : rotation, rotation_minus, rotation_plus
667 : INTEGER :: i
668 : REAL(KIND=dp) :: curvature, curvature_error, &
669 : energy_minus, energy_plus, &
670 : energy_zero, mu, response_sum, &
671 : schur_error
672 : REAL(KIND=dp), DIMENSION(nbands) :: energy_coordinate, energy_direction, energy_gradient, &
673 : energy_minus_coordinate, energy_plus_coordinate, occupation, rayleigh, rayleigh_minus, &
674 : rayleigh_plus, response, weights
675 : REAL(KIND=dp), DIMENSION(nbands, nbands) :: energy_hessian
676 : REAL(KIND=dp), DIMENSION(nbands, nrotation) :: rayleigh_response
677 : REAL(KIND=dp), DIMENSION(nrotation) :: coupling, rotation_direction, &
678 : rotation_gradient, schur_rhs
679 : REAL(KIND=dp), DIMENSION(nrotation, nrotation) :: rotation_hessian, schur, schur_block
680 :
681 2 : base_hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
682 2 : base_hamiltonian(1, 1) = CMPLX(-0.31_dp, 0.0_dp, KIND=dp)
683 2 : base_hamiltonian(2, 2) = CMPLX(0.07_dp, 0.0_dp, KIND=dp)
684 2 : base_hamiltonian(3, 3) = CMPLX(0.42_dp, 0.0_dp, KIND=dp)
685 2 : base_hamiltonian(1, 2) = CMPLX(0.09_dp, -0.06_dp, KIND=dp)
686 2 : base_hamiltonian(1, 3) = CMPLX(-0.04_dp, 0.08_dp, KIND=dp)
687 2 : base_hamiltonian(2, 3) = CMPLX(0.11_dp, 0.05_dp, KIND=dp)
688 8 : DO i = 1, nbands
689 14 : base_hamiltonian(i, 1:i - 1) = CONJG(base_hamiltonian(1:i - 1, i))
690 : END DO
691 :
692 2 : generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
693 2 : generator(1, 2) = CMPLX(0.17_dp, -0.08_dp, KIND=dp)
694 2 : generator(1, 3) = CMPLX(-0.09_dp, 0.04_dp, KIND=dp)
695 2 : generator(2, 3) = CMPLX(0.12_dp, 0.07_dp, KIND=dp)
696 8 : DO i = 1, nbands
697 14 : generator(i, 1:i - 1) = -CONJG(generator(1:i - 1, i))
698 : END DO
699 2 : rotation_direction(:) = [0.14_dp, -0.07_dp, -0.11_dp, 0.09_dp, 0.05_dp, 0.13_dp]
700 2 : energy_direction(:) = [0.08_dp, -0.11_dp, 0.03_dp]
701 2 : direction_generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
702 2 : direction_generator(1, 2) = CMPLX(rotation_direction(1), rotation_direction(2), KIND=dp)
703 2 : direction_generator(2, 1) = -CONJG(direction_generator(1, 2))
704 2 : direction_generator(1, 3) = CMPLX(rotation_direction(3), rotation_direction(4), KIND=dp)
705 2 : direction_generator(3, 1) = -CONJG(direction_generator(1, 3))
706 2 : direction_generator(2, 3) = CMPLX(rotation_direction(5), rotation_direction(6), KIND=dp)
707 2 : direction_generator(3, 2) = -CONJG(direction_generator(2, 3))
708 :
709 2 : rotation(:, :) = dense_antihermitian_exp(generator)
710 158 : chc(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), MATMUL(base_hamiltonian, rotation))
711 8 : DO i = 1, nbands
712 8 : rayleigh(i) = REAL(chc(i, i), KIND=dp)
713 : END DO
714 2 : energy_coordinate(:) = rayleigh(:)
715 8 : weights(:) = kpoint_weight
716 : CALL fixed_n_fermi_occupations(energy_coordinate, weights, 1.7_dp, temperature, &
717 2 : maxocc, occupation, mu)
718 8 : response(:) = weights*occupation*(maxocc - occupation)/(maxocc*temperature)
719 8 : response_sum = SUM(response)
720 : CALL qs_ot_finite_rotation_response(chc, generator, occupation, kpoint_weight, &
721 2 : rotation_gradient, rotation_hessian, rayleigh_response)
722 : CALL qs_ot_fixed_n_energy_gradient(rayleigh, energy_coordinate, response, response_sum, &
723 2 : 0.0_dp, energy_gradient)
724 2 : CALL qs_ot_fixed_n_energy_hessian(response, response_sum, energy_hessian)
725 : CALL qs_ot_fixed_n_schur_block( &
726 : rotation_hessian, rayleigh_response, response, rotation_gradient, energy_gradient, &
727 2 : schur_block, coupling, schur_rhs)
728 : schur(:, :) = schur_block + SPREAD(coupling, DIM=2, NCOPIES=nrotation)* &
729 86 : SPREAD(coupling, DIM=1, NCOPIES=nrotation)/response_sum
730 : schur_error = MAXVAL(ABS(schur - rotation_hessian + &
731 : MATMUL(TRANSPOSE(rayleigh_response), &
732 544 : MATMUL(energy_hessian, rayleigh_response))))
733 :
734 26 : rotation_plus(:, :) = dense_antihermitian_exp(generator + fd_step*direction_generator)
735 26 : rotation_minus(:, :) = dense_antihermitian_exp(generator - fd_step*direction_generator)
736 8 : DO i = 1, nbands
737 : rayleigh_plus(i) = REAL(DOT_PRODUCT(rotation_plus(:, i), &
738 96 : MATMUL(base_hamiltonian, rotation_plus(:, i))), KIND=dp)
739 : rayleigh_minus(i) = REAL(DOT_PRODUCT(rotation_minus(:, i), &
740 104 : MATMUL(base_hamiltonian, rotation_minus(:, i))), KIND=dp)
741 : END DO
742 8 : energy_plus_coordinate(:) = energy_coordinate + fd_step*energy_direction
743 8 : energy_minus_coordinate(:) = energy_coordinate - fd_step*energy_direction
744 : energy_zero = fixed_n_mermin_value(rayleigh, energy_coordinate, weights, 1.7_dp, &
745 2 : temperature, maxocc)
746 : energy_plus = fixed_n_mermin_value(rayleigh_plus, energy_plus_coordinate, weights, 1.7_dp, &
747 2 : temperature, maxocc)
748 : energy_minus = fixed_n_mermin_value(rayleigh_minus, energy_minus_coordinate, weights, 1.7_dp, &
749 2 : temperature, maxocc)
750 : curvature = DOT_PRODUCT(rotation_direction, MATMUL(rotation_hessian, rotation_direction)) - &
751 : 2.0_dp*DOT_PRODUCT(energy_direction, &
752 : MATMUL(energy_hessian, &
753 : MATMUL(rayleigh_response, rotation_direction))) + &
754 212 : DOT_PRODUCT(energy_direction, MATMUL(energy_hessian, energy_direction))
755 2 : curvature_error = ABS((energy_plus - 2.0_dp*energy_zero + energy_minus)/fd_step**2 - curvature)
756 :
757 2 : IF (schur_error > 2.0E-11_dp .OR. curvature_error > 3.0E-6_dp) nfail = nfail + 1
758 2 : IF (mynode == 0) THEN
759 1 : WRITE (io_unit, '(A,2(1X,ES13.6))') "fixed-N rotation Schur errors:", &
760 2 : schur_error, curvature_error
761 : END IF
762 :
763 2 : END SUBROUTINE test_fixed_n_rotation_schur
764 :
765 : ! **************************************************************************************************
766 : !> \brief Check that positive and negative response modes retain their spectral magnitude.
767 : !> \param mynode MPI rank
768 : !> \param nfail accumulated number of failures
769 : ! **************************************************************************************************
770 2 : SUBROUTINE test_symmetric_abs_solve(mynode, nfail)
771 : INTEGER, INTENT(IN) :: mynode
772 : INTEGER, INTENT(INOUT) :: nfail
773 :
774 : LOGICAL :: valid
775 : REAL(KIND=dp) :: error
776 : REAL(KIND=dp), DIMENSION(3) :: eigenvalues
777 : REAL(KIND=dp), DIMENSION(3, 2) :: expected, rhs, solution
778 : REAL(KIND=dp), DIMENSION(3, 3) :: absolute_matrix, matrix, vectors
779 :
780 8 : vectors(:, 1) = [0.8_dp, 0.6_dp, 0.0_dp]
781 8 : vectors(:, 2) = [-0.6_dp, 0.8_dp, 0.0_dp]
782 8 : vectors(:, 3) = [0.0_dp, 0.0_dp, 1.0_dp]
783 2 : eigenvalues(:) = [-0.4_dp, 1.7_dp, 0.25_dp]
784 : matrix(:, :) = MATMUL(vectors, MATMUL( &
785 : RESHAPE([eigenvalues(1), 0.0_dp, 0.0_dp, 0.0_dp, eigenvalues(2), 0.0_dp, &
786 176 : 0.0_dp, 0.0_dp, eigenvalues(3)], [3, 3]), TRANSPOSE(vectors)))
787 : absolute_matrix(:, :) = MATMUL(vectors, MATMUL( &
788 : RESHAPE([ABS(eigenvalues(1)), 0.0_dp, 0.0_dp, 0.0_dp, ABS(eigenvalues(2)), 0.0_dp, &
789 176 : 0.0_dp, 0.0_dp, ABS(eigenvalues(3))], [3, 3]), TRANSPOSE(vectors)))
790 8 : rhs(:, 1) = [0.3_dp, -0.5_dp, 0.7_dp]
791 8 : rhs(:, 2) = [-0.2_dp, 0.4_dp, 0.1_dp]
792 2 : CALL qs_ot_symmetric_abs_solve(matrix, rhs, solution, valid)
793 54 : expected(:, :) = MATMUL(absolute_matrix, solution)
794 18 : error = MAXVAL(ABS(expected - rhs))
795 2 : IF (.NOT. valid .OR. error > 2.0E-13_dp) nfail = nfail + 1
796 :
797 2 : eigenvalues(:) = [-0.4_dp, 1.7_dp, 1.0E-12_dp]
798 : matrix(:, :) = MATMUL(vectors, MATMUL( &
799 : RESHAPE([eigenvalues(1), 0.0_dp, 0.0_dp, 0.0_dp, eigenvalues(2), 0.0_dp, &
800 176 : 0.0_dp, 0.0_dp, eigenvalues(3)], [3, 3]), TRANSPOSE(vectors)))
801 8 : rhs(:, 1) = [0.3_dp, -0.5_dp, 0.7_dp]
802 8 : rhs(:, 2) = [-0.2_dp, 0.4_dp, 0.1_dp]
803 2 : CALL qs_ot_symmetric_abs_solve(matrix, rhs, solution, valid)
804 2 : expected(:, :) = rhs(:, :)
805 6 : expected(3, :) = 0.0_dp
806 : absolute_matrix(:, :) = MATMUL(vectors(:, 1:2), MATMUL( &
807 : RESHAPE([ABS(eigenvalues(1)), 0.0_dp, 0.0_dp, ABS(eigenvalues(2))], [2, 2]), &
808 106 : TRANSPOSE(vectors(:, 1:2))))
809 72 : error = MAX(error, MAXVAL(ABS(MATMUL(absolute_matrix, solution) - expected)))
810 6 : IF (.NOT. valid .OR. error > 2.0E-13_dp .OR. MAXVAL(ABS(solution(3, :))) > 2.0E-13_dp) THEN
811 0 : nfail = nfail + 1
812 : END IF
813 2 : IF (mynode == 0) WRITE (io_unit, '(A,1X,ES13.6)') "symmetric absolute solve error:", error
814 :
815 2 : END SUBROUTINE test_symmetric_abs_solve
816 :
817 : ! **************************************************************************************************
818 : !> \brief Check that the projected response update preserves the baseline and rejects indefiniteness.
819 : !> \param mynode MPI rank
820 : !> \param nfail accumulated number of failures
821 : ! **************************************************************************************************
822 2 : SUBROUTINE test_projected_response_update(mynode, nfail)
823 : INTEGER, INTENT(IN) :: mynode
824 : INTEGER, INTENT(INOUT) :: nfail
825 :
826 : LOGICAL :: valid
827 : REAL(KIND=dp) :: error, preservation_error
828 : REAL(KIND=dp), DIMENSION(2) :: coefficients, gradient, residual
829 : REAL(KIND=dp), DIMENSION(2, 2) :: correction, reference, total
830 :
831 2 : reference(:, :) = RESHAPE([4.0_dp, 0.7_dp, 0.7_dp, 2.5_dp], [2, 2])
832 2 : correction(:, :) = 0.0_dp
833 2 : CALL qs_ot_projected_response_update(reference, correction, coefficients, valid)
834 6 : preservation_error = MAXVAL(ABS(coefficients - [1.0_dp, 0.0_dp]))
835 2 : IF (.NOT. valid .OR. preservation_error > 1.0E-12_dp) nfail = nfail + 1
836 :
837 2 : correction(:, :) = RESHAPE([0.8_dp, -0.3_dp, -0.3_dp, 0.4_dp], [2, 2])
838 2 : gradient(:) = [1.2_dp, -0.4_dp]
839 : CALL qs_ot_projected_response_update( &
840 2 : reference, correction, coefficients, valid, projected_gradient=gradient)
841 14 : total(:, :) = reference + correction
842 20 : residual(:) = MATMUL(total, coefficients) - gradient
843 6 : error = MAXVAL(ABS(residual))
844 2 : IF (.NOT. valid .OR. error > 1.0E-12_dp) nfail = nfail + 1
845 :
846 2 : correction(:, :) = RESHAPE([-8.0_dp, 0.0_dp, 0.0_dp, 0.0_dp], [2, 2])
847 2 : CALL qs_ot_projected_response_update(reference, correction, coefficients, valid)
848 2 : IF (valid) nfail = nfail + 1
849 2 : IF (mynode == 0) WRITE (io_unit, '(A,2(1X,ES14.6))') &
850 1 : "projected response preservation/residual errors:", preservation_error, error
851 :
852 2 : END SUBROUTINE test_projected_response_update
853 :
854 : ! **************************************************************************************************
855 : !> \brief Check exact signed SR1 secants and rejection of an unresolved orthogonal residual.
856 : !> \param mynode MPI rank
857 : !> \param nfail accumulated number of failures
858 : ! **************************************************************************************************
859 2 : SUBROUTINE test_symmetric_sr1_update(mynode, nfail)
860 : INTEGER, INTENT(IN) :: mynode
861 : INTEGER, INTENT(INOUT) :: nfail
862 :
863 : LOGICAL :: valid
864 : REAL(KIND=dp) :: rejected_error, secant_error, &
865 : symmetry_error
866 : REAL(KIND=dp), DIMENSION(3) :: orthogonal_residual, response, step
867 : REAL(KIND=dp), DIMENSION(3, 3) :: matrix, updated
868 :
869 2 : matrix(:, :) = 0.0_dp
870 2 : matrix(1, 1) = 1.0_dp
871 2 : matrix(2, 2) = 2.0_dp
872 2 : matrix(3, 3) = 3.0_dp
873 2 : step(:) = [0.3_dp, -0.4_dp, 0.2_dp]
874 2 : response(:) = [0.0_dp, -0.3_dp, 0.8_dp]
875 2 : CALL qs_ot_symmetric_sr1_update(matrix, step, response, updated, valid)
876 34 : secant_error = MAXVAL(ABS(MATMUL(updated, step) - response))
877 26 : symmetry_error = MAXVAL(ABS(updated - TRANSPOSE(updated)))
878 2 : IF (.NOT. valid .OR. secant_error > 2.0E-14_dp .OR. symmetry_error > 2.0E-14_dp) THEN
879 0 : nfail = nfail + 1
880 : END IF
881 :
882 2 : orthogonal_residual(:) = [0.4_dp, 0.3_dp, 0.0_dp]
883 34 : response(:) = MATMUL(matrix, step) + orthogonal_residual
884 2 : CALL qs_ot_symmetric_sr1_update(matrix, step, response, updated, valid)
885 26 : rejected_error = MAXVAL(ABS(updated - matrix))
886 2 : IF (valid .OR. rejected_error > 2.0E-14_dp) nfail = nfail + 1
887 2 : IF (mynode == 0) WRITE (io_unit, '(A,3(1X,ES13.6))') &
888 1 : "symmetric SR1 secant/symmetry/rejection errors:", &
889 2 : secant_error, symmetry_error, rejected_error
890 :
891 2 : END SUBROUTINE test_symmetric_sr1_update
892 :
893 : ! **************************************************************************************************
894 : !> \brief Check the complex gauge-invariant, sign-aware density-space response secant.
895 : !> \param mynode MPI rank
896 : !> \param nfail accumulated number of failures
897 : ! **************************************************************************************************
898 2 : SUBROUTINE test_density_secant_hessian(mynode, nfail)
899 : INTEGER, INTENT(IN) :: mynode
900 : INTEGER, INTENT(INOUT) :: nfail
901 :
902 : INTEGER, PARAMETER :: n = 3, nmode = 2
903 : REAL(KIND=dp), PARAMETER :: secant_position = 0.4_dp
904 :
905 : COMPLEX(KIND=dp), DIMENSION(n, n) :: density_step, hamiltonian_step, unitary
906 : COMPLEX(KIND=dp), DIMENSION(n, n, nmode) :: density_modes, transformed_modes
907 : LOGICAL :: calibrated_valid, rejected_valid, valid
908 : REAL(KIND=dp) :: calibrated_error, density_norm, direct_error, gauge_error, overlap_error, &
909 : rejected_error, response_work, sign_error
910 : REAL(KIND=dp), DIMENSION(nmode) :: density_overlap, response_overlap
911 : REAL(KIND=dp), DIMENSION(nmode, nmode) :: calibrated, correction, &
912 : correction_gauge, correction_overlap, &
913 : expected, rejected
914 :
915 2 : density_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
916 2 : density_step(1, 1) = CMPLX(0.30_dp, 0.0_dp, KIND=dp)
917 2 : density_step(2, 2) = CMPLX(-0.18_dp, 0.0_dp, KIND=dp)
918 2 : density_step(3, 3) = CMPLX(-0.12_dp, 0.0_dp, KIND=dp)
919 2 : density_step(1, 2) = CMPLX(0.07_dp, -0.11_dp, KIND=dp)
920 2 : density_step(2, 1) = CONJG(density_step(1, 2))
921 2 : density_step(1, 3) = CMPLX(-0.04_dp, 0.05_dp, KIND=dp)
922 2 : density_step(3, 1) = CONJG(density_step(1, 3))
923 :
924 26 : hamiltonian_step(:, :) = -0.45_dp*density_step
925 2 : hamiltonian_step(2, 3) = CMPLX(0.06_dp, 0.03_dp, KIND=dp)
926 2 : hamiltonian_step(3, 2) = CONJG(hamiltonian_step(2, 3))
927 26 : density_modes(:, :, 1) = density_step
928 26 : density_modes(:, :, 2) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
929 2 : density_modes(1, 1, 2) = CMPLX(-0.2_dp, 0.0_dp, KIND=dp)
930 2 : density_modes(2, 2, 2) = CMPLX(0.1_dp, 0.0_dp, KIND=dp)
931 2 : density_modes(3, 3, 2) = CMPLX(0.1_dp, 0.0_dp, KIND=dp)
932 2 : density_modes(1, 3, 2) = CMPLX(0.03_dp, 0.08_dp, KIND=dp)
933 2 : density_modes(3, 1, 2) = CONJG(density_modes(1, 3, 2))
934 :
935 : CALL qs_ot_density_secant_hessian( &
936 : density_step, hamiltonian_step, density_modes, correction, valid, &
937 2 : density_norm, response_work)
938 2 : density_overlap(1) = density_norm
939 26 : density_overlap(2) = SUM(REAL(CONJG(density_step)*density_modes(:, :, 2), KIND=dp))
940 2 : response_overlap(1) = response_work
941 26 : response_overlap(2) = SUM(REAL(CONJG(hamiltonian_step)*density_modes(:, :, 2), KIND=dp))
942 : CALL qs_ot_density_secant_projected_hessian( &
943 2 : density_norm, response_work, density_overlap, response_overlap, correction_overlap, valid)
944 :
945 2 : expected(1, 1) = response_work
946 2 : expected(1, 2) = response_overlap(2)
947 2 : expected(2, 1) = response_overlap(2)
948 : expected(2, 2) = (2.0_dp*density_overlap(2)*response_overlap(2) - &
949 2 : response_work*density_overlap(2)**2/density_norm)/density_norm
950 14 : direct_error = MAXVAL(ABS(correction - expected))
951 14 : overlap_error = MAXVAL(ABS(correction_overlap - correction))
952 2 : sign_error = MAX(0.0_dp, correction(1, 1))
953 :
954 : CALL qs_ot_density_secant_projected_hessian( &
955 : density_norm, response_work, density_overlap, response_overlap, calibrated, &
956 2 : calibrated_valid, secant_mode=2, secant_position=secant_position)
957 : expected(1, 1) = response_work
958 2 : expected(1, 2) = response_work/secant_position
959 2 : expected(2, 1) = expected(1, 2)
960 2 : expected(2, 2) = response_work/secant_position**2
961 14 : calibrated_error = MAXVAL(ABS(calibrated - expected))
962 : CALL qs_ot_density_secant_projected_hessian( &
963 : density_norm, response_work, density_overlap, response_overlap, rejected, &
964 2 : rejected_valid, secant_mode=2, secant_position=0.0_dp)
965 14 : rejected_error = MAXVAL(ABS(rejected))
966 :
967 2 : unitary(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
968 2 : unitary(1, 1) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
969 2 : unitary(2, 1) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
970 2 : unitary(1, 2) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
971 2 : unitary(2, 2) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
972 2 : unitary(3, 3) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
973 158 : density_step = MATMUL(CONJG(TRANSPOSE(unitary)), MATMUL(density_step, unitary))
974 158 : hamiltonian_step = MATMUL(CONJG(TRANSPOSE(unitary)), MATMUL(hamiltonian_step, unitary))
975 : transformed_modes(:, :, 1) = &
976 186 : MATMUL(CONJG(TRANSPOSE(unitary)), MATMUL(density_modes(:, :, 1), unitary))
977 : transformed_modes(:, :, 2) = &
978 186 : MATMUL(CONJG(TRANSPOSE(unitary)), MATMUL(density_modes(:, :, 2), unitary))
979 : CALL qs_ot_density_secant_hessian( &
980 2 : density_step, hamiltonian_step, transformed_modes, correction_gauge, valid)
981 14 : gauge_error = MAXVAL(ABS(correction_gauge - correction))
982 :
983 : IF (.NOT. valid .OR. .NOT. calibrated_valid .OR. rejected_valid .OR. &
984 : direct_error > 2.0E-14_dp .OR. overlap_error > 2.0E-14_dp .OR. &
985 : gauge_error > 2.0E-14_dp .OR. sign_error > 2.0E-14_dp .OR. &
986 2 : calibrated_error > 2.0E-14_dp .OR. rejected_error > 2.0E-14_dp) nfail = nfail + 1
987 2 : IF (mynode == 0) WRITE (io_unit, '(A,6(1X,ES13.6))') &
988 1 : "density secant direct/overlap/gauge/sign/calibrated/rejected errors:", &
989 2 : direct_error, overlap_error, gauge_error, sign_error, calibrated_error, rejected_error
990 :
991 2 : END SUBROUTINE test_density_secant_hessian
992 :
993 : ! **************************************************************************************************
994 : !> \brief Check that the Hxc secant is formed in the joint direct sum of both spin channels.
995 : !> \param mynode MPI rank
996 : !> \param nfail accumulated number of failures
997 : ! **************************************************************************************************
998 2 : SUBROUTINE test_joint_spin_density_secant(mynode, nfail)
999 : INTEGER, INTENT(IN) :: mynode
1000 : INTEGER, INTENT(INOUT) :: nfail
1001 :
1002 : INTEGER, PARAMETER :: n = 2, nmode = 2, nspin = 2, &
1003 : ntotal = n*nspin
1004 :
1005 : COMPLEX(KIND=dp), DIMENSION(n, n, nmode, nspin) :: density_modes
1006 : COMPLEX(KIND=dp), DIMENSION(n, n, nspin) :: density_step, hamiltonian_step
1007 : COMPLEX(KIND=dp), DIMENSION(ntotal, ntotal) :: direct_density_step, &
1008 : direct_hamiltonian_step
1009 : COMPLEX(KIND=dp), DIMENSION(ntotal, ntotal, nmode) :: direct_density_modes
1010 : INTEGER :: first, ispin, last, mode
1011 : LOGICAL :: direct_valid, joint_valid, spin_valid
1012 : REAL(KIND=dp) :: coupling_size, density_norm, &
1013 : direct_error, response_work
1014 : REAL(KIND=dp), DIMENSION(nmode) :: density_overlap, response_overlap
1015 : REAL(KIND=dp), DIMENSION(nmode, nmode) :: direct_correction, joint_correction, &
1016 : separate_correction, spin_correction
1017 :
1018 : density_step(:, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1019 2 : density_step(1, 1, 1) = 0.31_dp
1020 2 : density_step(2, 2, 1) = -0.31_dp
1021 2 : density_step(1, 2, 1) = CMPLX(0.08_dp, -0.04_dp, KIND=dp)
1022 2 : density_step(2, 1, 1) = CONJG(density_step(1, 2, 1))
1023 2 : density_step(1, 1, 2) = -0.19_dp
1024 2 : density_step(2, 2, 2) = 0.19_dp
1025 2 : density_step(1, 2, 2) = CMPLX(-0.05_dp, 0.06_dp, KIND=dp)
1026 2 : density_step(2, 1, 2) = CONJG(density_step(1, 2, 2))
1027 :
1028 14 : hamiltonian_step(:, :, 1) = 0.67_dp*density_step(:, :, 1)
1029 2 : hamiltonian_step(1, 2, 1) = hamiltonian_step(1, 2, 1) + CMPLX(-0.03_dp, 0.09_dp, KIND=dp)
1030 2 : hamiltonian_step(2, 1, 1) = CONJG(hamiltonian_step(1, 2, 1))
1031 14 : hamiltonian_step(:, :, 2) = -0.43_dp*density_step(:, :, 2)
1032 2 : hamiltonian_step(1, 2, 2) = hamiltonian_step(1, 2, 2) + CMPLX(0.07_dp, 0.02_dp, KIND=dp)
1033 2 : hamiltonian_step(2, 1, 2) = CONJG(hamiltonian_step(1, 2, 2))
1034 :
1035 : density_modes(:, :, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1036 2 : density_modes(1, 1, 1, 1) = 0.21_dp
1037 2 : density_modes(2, 2, 1, 1) = -0.21_dp
1038 2 : density_modes(1, 2, 1, 1) = CMPLX(0.04_dp, 0.03_dp, KIND=dp)
1039 2 : density_modes(2, 1, 1, 1) = CONJG(density_modes(1, 2, 1, 1))
1040 2 : density_modes(1, 1, 2, 1) = -0.13_dp
1041 2 : density_modes(2, 2, 2, 1) = 0.13_dp
1042 2 : density_modes(1, 2, 2, 1) = CMPLX(-0.02_dp, 0.05_dp, KIND=dp)
1043 2 : density_modes(2, 1, 2, 1) = CONJG(density_modes(1, 2, 2, 1))
1044 2 : density_modes(1, 1, 1, 2) = -0.17_dp
1045 2 : density_modes(2, 2, 1, 2) = 0.17_dp
1046 2 : density_modes(1, 2, 1, 2) = CMPLX(0.06_dp, -0.01_dp, KIND=dp)
1047 2 : density_modes(2, 1, 1, 2) = CONJG(density_modes(1, 2, 1, 2))
1048 2 : density_modes(1, 1, 2, 2) = 0.09_dp
1049 2 : density_modes(2, 2, 2, 2) = -0.09_dp
1050 2 : density_modes(1, 2, 2, 2) = CMPLX(0.03_dp, 0.04_dp, KIND=dp)
1051 2 : density_modes(2, 1, 2, 2) = CONJG(density_modes(1, 2, 2, 2))
1052 :
1053 2 : direct_density_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1054 2 : direct_hamiltonian_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1055 2 : direct_density_modes(:, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1056 2 : density_norm = 0.0_dp
1057 2 : response_work = 0.0_dp
1058 2 : density_overlap(:) = 0.0_dp
1059 2 : response_overlap(:) = 0.0_dp
1060 2 : separate_correction(:, :) = 0.0_dp
1061 2 : spin_valid = .TRUE.
1062 6 : DO ispin = 1, nspin
1063 4 : first = (ispin - 1)*n + 1
1064 4 : last = ispin*n
1065 28 : direct_density_step(first:last, first:last) = density_step(:, :, ispin)
1066 28 : direct_hamiltonian_step(first:last, first:last) = hamiltonian_step(:, :, ispin)
1067 60 : direct_density_modes(first:last, first:last, :) = density_modes(:, :, :, ispin)
1068 : density_norm = density_norm + &
1069 : SUM(REAL(CONJG(density_step(:, :, ispin))* &
1070 28 : density_step(:, :, ispin), KIND=dp))
1071 : response_work = response_work + &
1072 : SUM(REAL(CONJG(density_step(:, :, ispin))* &
1073 28 : hamiltonian_step(:, :, ispin), KIND=dp))
1074 12 : DO mode = 1, nmode
1075 : density_overlap(mode) = density_overlap(mode) + &
1076 : SUM(REAL(CONJG(density_step(:, :, ispin))* &
1077 56 : density_modes(:, :, mode, ispin), KIND=dp))
1078 : response_overlap(mode) = response_overlap(mode) + &
1079 : SUM(REAL(CONJG(hamiltonian_step(:, :, ispin))* &
1080 60 : density_modes(:, :, mode, ispin), KIND=dp))
1081 : END DO
1082 : CALL qs_ot_density_secant_hessian( &
1083 : density_step(:, :, ispin), hamiltonian_step(:, :, ispin), &
1084 4 : density_modes(:, :, :, ispin), spin_correction, direct_valid)
1085 4 : spin_valid = spin_valid .AND. direct_valid
1086 30 : separate_correction = separate_correction + spin_correction
1087 : END DO
1088 :
1089 : CALL qs_ot_density_secant_hessian( &
1090 : direct_density_step, direct_hamiltonian_step, direct_density_modes, &
1091 2 : direct_correction, direct_valid)
1092 : CALL qs_ot_density_secant_projected_hessian( &
1093 : density_norm, response_work, density_overlap, response_overlap, &
1094 2 : joint_correction, joint_valid)
1095 14 : direct_error = MAXVAL(ABS(joint_correction - direct_correction))
1096 14 : coupling_size = MAXVAL(ABS(joint_correction - separate_correction))
1097 :
1098 : IF (.NOT. direct_valid .OR. .NOT. joint_valid .OR. .NOT. spin_valid .OR. &
1099 2 : direct_error > 3.0E-14_dp .OR. coupling_size < 1.0E-5_dp) nfail = nfail + 1
1100 2 : IF (mynode == 0) WRITE (io_unit, '(A,2(1X,ES13.6))') &
1101 1 : "joint-spin density secant direct error/coupling:", direct_error, coupling_size
1102 :
1103 2 : END SUBROUTINE test_joint_spin_density_secant
1104 :
1105 : ! **************************************************************************************************
1106 : !> \brief Check the coupled finite-chart density tangent against a central finite difference.
1107 : !> \param mynode MPI rank
1108 : !> \param nfail accumulated number of failures
1109 : ! **************************************************************************************************
1110 2 : SUBROUTINE test_density_tangent(mynode, nfail)
1111 : INTEGER, INTENT(IN) :: mynode
1112 : INTEGER, INTENT(INOUT) :: nfail
1113 :
1114 : INTEGER, PARAMETER :: nbands = 3, nrotation = 6
1115 : REAL(KIND=dp), PARAMETER :: fd_step = 2.0E-6_dp, &
1116 : kpoint_weight = 0.625_dp
1117 :
1118 : COMPLEX(KIND=dp), DIMENSION(nbands, nbands) :: density_minus, density_plus, density_tangent, &
1119 : direction_generator, generator, rotation, rotation_minus, rotation_plus, weighted_rotation
1120 : INTEGER :: i, j, r
1121 : REAL(KIND=dp) :: error, trace_error
1122 : REAL(KIND=dp), DIMENSION(nbands) :: occupation, weighted_occupation_step
1123 : REAL(KIND=dp), DIMENSION(nrotation) :: rotation_step
1124 :
1125 2 : generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1126 2 : generator(1, 2) = CMPLX(0.17_dp, -0.09_dp, KIND=dp)
1127 2 : generator(2, 1) = -CONJG(generator(1, 2))
1128 2 : generator(1, 3) = CMPLX(-0.06_dp, 0.11_dp, KIND=dp)
1129 2 : generator(3, 1) = -CONJG(generator(1, 3))
1130 2 : generator(2, 3) = CMPLX(0.08_dp, 0.04_dp, KIND=dp)
1131 2 : generator(3, 2) = -CONJG(generator(2, 3))
1132 2 : occupation(:) = [1.72_dp, 0.83_dp, 0.31_dp]
1133 2 : rotation_step(:) = [0.21_dp, -0.14_dp, -0.08_dp, 0.17_dp, 0.11_dp, 0.06_dp]
1134 2 : weighted_occupation_step(:) = [-0.037_dp, 0.052_dp, -0.015_dp]
1135 :
1136 : CALL qs_ot_density_tangent( &
1137 : generator, occupation, kpoint_weight, rotation_step, weighted_occupation_step, &
1138 2 : density_tangent, difference_step=fd_step)
1139 :
1140 2 : direction_generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1141 2 : r = 0
1142 6 : DO i = 1, nbands - 1
1143 12 : DO j = i + 1, nbands
1144 6 : r = r + 1
1145 6 : direction_generator(i, j) = CMPLX(rotation_step(r), 0.0_dp, KIND=dp)
1146 6 : direction_generator(j, i) = -direction_generator(i, j)
1147 6 : r = r + 1
1148 : direction_generator(i, j) = direction_generator(i, j) + &
1149 6 : CMPLX(0.0_dp, rotation_step(r), KIND=dp)
1150 : direction_generator(j, i) = direction_generator(j, i) + &
1151 10 : CMPLX(0.0_dp, rotation_step(r), KIND=dp)
1152 : END DO
1153 : END DO
1154 2 : rotation = dense_antihermitian_exp(generator)
1155 26 : rotation_plus = dense_antihermitian_exp(generator + fd_step*direction_generator)
1156 26 : rotation_minus = dense_antihermitian_exp(generator - fd_step*direction_generator)
1157 2 : weighted_rotation(:, :) = rotation_plus
1158 8 : DO j = 1, nbands
1159 : weighted_rotation(:, j) = &
1160 : (kpoint_weight*occupation(j) + fd_step*weighted_occupation_step(j))* &
1161 26 : weighted_rotation(:, j)
1162 : END DO
1163 80 : density_plus(:, :) = MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation_plus)))
1164 2 : weighted_rotation(:, :) = rotation_minus
1165 8 : DO j = 1, nbands
1166 : weighted_rotation(:, j) = &
1167 : (kpoint_weight*occupation(j) - fd_step*weighted_occupation_step(j))* &
1168 26 : weighted_rotation(:, j)
1169 : END DO
1170 80 : density_minus(:, :) = MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation_minus)))
1171 : density_plus(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), &
1172 182 : MATMUL((density_plus - density_minus)/(2.0_dp*fd_step), rotation))
1173 26 : error = MAXVAL(ABS(density_tangent - density_plus))
1174 : trace_error = ABS(REAL(SUM([(density_tangent(i, i), i=1, nbands)]), KIND=dp) - &
1175 8 : SUM(weighted_occupation_step))
1176 :
1177 2 : IF (error > 3.0E-9_dp .OR. trace_error > 3.0E-10_dp) nfail = nfail + 1
1178 2 : IF (mynode == 0) WRITE (io_unit, '(A,2(1X,ES13.6))') &
1179 1 : "finite complex density tangent errors:", error, trace_error
1180 :
1181 2 : END SUBROUTINE test_density_tangent
1182 :
1183 : ! **************************************************************************************************
1184 : !> \brief Check density-secants when accepted endpoint orbital subspaces differ.
1185 : !> \param mynode MPI rank
1186 : !> \param nfail accumulated number of failures
1187 : ! **************************************************************************************************
1188 2 : SUBROUTINE test_density_secant_moving_subspace(mynode, nfail)
1189 : INTEGER, INTENT(IN) :: mynode
1190 : INTEGER, INTENT(INOUT) :: nfail
1191 :
1192 : INTEGER, PARAMETER :: nao = 4, nbands = 3, nmode = 2
1193 : REAL(KIND=dp), PARAMETER :: weight = 0.625_dp
1194 :
1195 : COMPLEX(KIND=dp), DIMENSION(nao, nao) :: density_step, hamiltonian_step
1196 : COMPLEX(KIND=dp), DIMENSION(nao, nao, nmode) :: density_modes_ao
1197 : COMPLEX(KIND=dp), DIMENSION(nao, nbands) :: c0, c1
1198 : COMPLEX(KIND=dp), DIMENSION(nbands, nbands) :: h0, h1, overlap
1199 : COMPLEX(KIND=dp), DIMENSION(nbands, nbands, nmode) :: density_modes
1200 : INTEGER :: i
1201 : LOGICAL :: valid
1202 : REAL(KIND=dp) :: density_norm, density_norm_ref, error, &
1203 : response_work, response_work_ref
1204 : REAL(KIND=dp), DIMENSION(nbands) :: occupation0, occupation1
1205 : REAL(KIND=dp), DIMENSION(nmode) :: density_overlap, density_overlap_ref, &
1206 : response_overlap, response_overlap_ref
1207 :
1208 2 : c0(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1209 2 : c0(1, 1) = 1.0_dp
1210 2 : c0(2, 2) = 1.0_dp
1211 2 : c0(3, 3) = 1.0_dp
1212 2 : c1(:, :) = c0
1213 2 : c1(1, 1) = COS(0.31_dp)
1214 2 : c1(4, 1) = SIN(0.31_dp)
1215 2 : c1(2, 2) = COS(0.23_dp)
1216 2 : c1(3, 2) = CMPLX(0.0_dp, SIN(0.23_dp), KIND=dp)
1217 2 : c1(2, 3) = CMPLX(0.0_dp, SIN(0.23_dp), KIND=dp)
1218 2 : c1(3, 3) = COS(0.23_dp)
1219 2 : occupation0(:) = [1.73_dp, 0.82_dp, 0.82_dp]
1220 2 : occupation1(:) = [1.61_dp, 0.91_dp, 0.76_dp]
1221 :
1222 2 : hamiltonian_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1223 2 : hamiltonian_step(1, 1) = 0.21_dp
1224 2 : hamiltonian_step(2, 2) = -0.17_dp
1225 2 : hamiltonian_step(3, 3) = 0.09_dp
1226 2 : hamiltonian_step(4, 4) = -0.04_dp
1227 2 : hamiltonian_step(1, 4) = CMPLX(0.07_dp, -0.03_dp, KIND=dp)
1228 2 : hamiltonian_step(4, 1) = CONJG(hamiltonian_step(1, 4))
1229 2 : hamiltonian_step(2, 3) = CMPLX(-0.05_dp, 0.08_dp, KIND=dp)
1230 2 : hamiltonian_step(3, 2) = CONJG(hamiltonian_step(2, 3))
1231 2 : density_modes(:, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1232 2 : density_modes(1, 1, 1) = 0.13_dp
1233 2 : density_modes(2, 2, 1) = -0.08_dp
1234 2 : density_modes(3, 3, 1) = -0.05_dp
1235 2 : density_modes(1, 2, 2) = CMPLX(0.04_dp, -0.06_dp, KIND=dp)
1236 2 : density_modes(2, 1, 2) = CONJG(density_modes(1, 2, 2))
1237 :
1238 98 : overlap(:, :) = MATMUL(CONJG(TRANSPOSE(c0)), c1)
1239 224 : h0(:, :) = MATMUL(CONJG(TRANSPOSE(c0)), MATMUL(hamiltonian_step, c0))
1240 224 : h1(:, :) = MATMUL(CONJG(TRANSPOSE(c1)), MATMUL(hamiltonian_step, c1))
1241 : CALL qs_ot_density_secant_orbital_overlaps( &
1242 : overlap, occupation0, occupation1, h0, h1, density_modes, weight, density_norm, &
1243 2 : response_work, density_overlap, response_overlap, valid)
1244 :
1245 2 : density_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1246 8 : DO i = 1, nbands
1247 : density_step = density_step + weight*occupation1(i)* &
1248 : SPREAD(c1(:, i), DIM=2, NCOPIES=nao)* &
1249 : SPREAD(CONJG(c1(:, i)), DIM=1, NCOPIES=nao) - &
1250 : weight*occupation0(i)* &
1251 : SPREAD(c0(:, i), DIM=2, NCOPIES=nao)* &
1252 176 : SPREAD(CONJG(c0(:, i)), DIM=1, NCOPIES=nao)
1253 : END DO
1254 6 : DO i = 1, nmode
1255 : density_modes_ao(:, :, i) = MATMUL(c1, &
1256 : MATMUL(density_modes(:, :, i), &
1257 552 : CONJG(TRANSPOSE(c1))))
1258 : density_overlap_ref(i) = REAL( &
1259 84 : SUM(CONJG(density_step)*density_modes_ao(:, :, i)), KIND=dp)/weight
1260 : response_overlap_ref(i) = &
1261 86 : REAL(SUM(CONJG(hamiltonian_step)*density_modes_ao(:, :, i)), KIND=dp)
1262 : END DO
1263 42 : density_norm_ref = REAL(SUM(CONJG(density_step)*density_step), KIND=dp)/weight
1264 42 : response_work_ref = REAL(SUM(CONJG(density_step)*hamiltonian_step), KIND=dp)
1265 2 : error = MAX(ABS(density_norm - density_norm_ref), ABS(response_work - response_work_ref))
1266 6 : error = MAX(error, MAXVAL(ABS(density_overlap - density_overlap_ref)))
1267 6 : error = MAX(error, MAXVAL(ABS(response_overlap - response_overlap_ref)))
1268 :
1269 2 : IF (.NOT. valid .OR. error > 3.0E-13_dp) nfail = nfail + 1
1270 2 : IF (mynode == 0) WRITE (io_unit, '(A,1X,ES13.6)') &
1271 1 : "moving-subspace density secant error:", error
1272 :
1273 2 : END SUBROUTINE test_density_secant_moving_subspace
1274 :
1275 : ! **************************************************************************************************
1276 : !> \brief fixed-occupation band energy for a dense finite rotation
1277 : !> \param rotation unitary REF rotation
1278 : !> \param hamiltonian fixed Hermitian Hamiltonian in the REF basis
1279 : !> \param occupation band occupations
1280 : !> \param kpoint_weight irreducible-k-point weight
1281 : !> \return weighted band energy
1282 : ! **************************************************************************************************
1283 6 : FUNCTION finite_rotation_energy(rotation, hamiltonian, occupation, kpoint_weight) RESULT(energy)
1284 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: rotation, hamiltonian
1285 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: occupation
1286 : REAL(KIND=dp), INTENT(IN) :: kpoint_weight
1287 : REAL(KIND=dp) :: energy
1288 :
1289 : INTEGER :: i
1290 :
1291 6 : energy = 0.0_dp
1292 24 : DO i = 1, SIZE(occupation)
1293 : energy = energy + kpoint_weight*occupation(i)* &
1294 : REAL(DOT_PRODUCT(rotation(:, i), &
1295 330 : MATMUL(hamiltonian, rotation(:, i))), KIND=dp)
1296 : END DO
1297 :
1298 6 : END FUNCTION finite_rotation_energy
1299 :
1300 : ! **************************************************************************************************
1301 : !> \brief Solve fixed-N Fermi occupations by bisection.
1302 : !> \param energies band energies
1303 : !> \param weights integration weights
1304 : !> \param target_electrons requested electron number
1305 : !> \param temperature electronic temperature in energy units
1306 : !> \param maxocc maximum band occupation
1307 : !> \param occupation resulting occupations
1308 : !> \param mu resulting chemical potential
1309 : ! **************************************************************************************************
1310 48 : SUBROUTINE fixed_n_fermi_occupations(energies, weights, target_electrons, temperature, &
1311 24 : maxocc, occupation, mu)
1312 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: energies, weights
1313 : REAL(KIND=dp), INTENT(IN) :: target_electrons, temperature, maxocc
1314 : REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: occupation
1315 : REAL(KIND=dp), INTENT(OUT) :: mu
1316 :
1317 : INTEGER :: i, iteration
1318 : REAL(KIND=dp) :: high, low, number
1319 :
1320 106 : low = MINVAL(energies) - 80.0_dp*temperature
1321 106 : high = MAXVAL(energies) + 80.0_dp*temperature
1322 3864 : DO iteration = 1, 160
1323 3840 : mu = 0.5_dp*(low + high)
1324 3840 : number = 0.0_dp
1325 16960 : DO i = 1, SIZE(energies)
1326 13120 : occupation(i) = fermi_occupation(energies(i), mu, temperature, maxocc)
1327 16960 : number = number + weights(i)*occupation(i)
1328 : END DO
1329 3864 : IF (number < target_electrons) THEN
1330 : low = mu
1331 : ELSE
1332 1894 : high = mu
1333 : END IF
1334 : END DO
1335 24 : mu = 0.5_dp*(low + high)
1336 106 : DO i = 1, SIZE(energies)
1337 106 : occupation(i) = fermi_occupation(energies(i), mu, temperature, maxocc)
1338 : END DO
1339 :
1340 24 : END SUBROUTINE fixed_n_fermi_occupations
1341 :
1342 : ! **************************************************************************************************
1343 : !> \brief Numerically stable Fermi occupation.
1344 : !> \param energy band energy
1345 : !> \param mu chemical potential
1346 : !> \param temperature electronic temperature in energy units
1347 : !> \param maxocc maximum band occupation
1348 : !> \return Fermi occupation
1349 : ! **************************************************************************************************
1350 13202 : PURE FUNCTION fermi_occupation(energy, mu, temperature, maxocc) RESULT(occupation)
1351 : REAL(KIND=dp), INTENT(IN) :: energy, mu, temperature, maxocc
1352 : REAL(KIND=dp) :: occupation
1353 :
1354 : REAL(KIND=dp) :: x
1355 :
1356 13202 : x = (energy - mu)/temperature
1357 13202 : IF (x > 40.0_dp) THEN
1358 28 : occupation = maxocc*EXP(-x)
1359 13174 : ELSE IF (x < -40.0_dp) THEN
1360 30 : occupation = maxocc*(1.0_dp - EXP(x))
1361 : ELSE
1362 13144 : occupation = maxocc/(1.0_dp + EXP(x))
1363 : END IF
1364 :
1365 13202 : END FUNCTION fermi_occupation
1366 :
1367 : ! **************************************************************************************************
1368 : !> \brief Fixed-H fixed-N Mermin free energy used by the finite-difference test.
1369 : !> \param rayleigh Hamiltonian expectation values
1370 : !> \param energies auxiliary band energies
1371 : !> \param weights integration weights
1372 : !> \param target_electrons requested electron number
1373 : !> \param temperature electronic temperature in energy units
1374 : !> \param maxocc maximum band occupation
1375 : !> \return fixed-N Mermin free energy
1376 : ! **************************************************************************************************
1377 10 : FUNCTION fixed_n_mermin_value(rayleigh, energies, weights, target_electrons, &
1378 : temperature, maxocc) RESULT(value)
1379 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: rayleigh, energies, weights
1380 : REAL(KIND=dp), INTENT(IN) :: target_electrons, temperature, maxocc
1381 : REAL(KIND=dp) :: value
1382 :
1383 : INTEGER :: i
1384 : REAL(KIND=dp) :: mu, p
1385 20 : REAL(KIND=dp), DIMENSION(SIZE(energies)) :: occupation
1386 :
1387 : CALL fixed_n_fermi_occupations(energies, weights, target_electrons, temperature, &
1388 10 : maxocc, occupation, mu)
1389 10 : value = 0.0_dp
1390 44 : DO i = 1, SIZE(energies)
1391 34 : p = MAX(EPSILON(1.0_dp), MIN(1.0_dp - EPSILON(1.0_dp), occupation(i)/maxocc))
1392 : value = value + weights(i)*(occupation(i)*rayleigh(i) + &
1393 44 : temperature*maxocc*(p*LOG(p) + (1.0_dp - p)*LOG(1.0_dp - p)))
1394 : END DO
1395 :
1396 10 : END FUNCTION fixed_n_mermin_value
1397 :
1398 : ! **************************************************************************************************
1399 : !> \brief Canonical weighted occupation projector used by the finite-difference test.
1400 : !> \param hamiltonian Hermitian Hamiltonian
1401 : !> \param weights integration weights
1402 : !> \param target_electrons requested electron number
1403 : !> \param temperature electronic temperature in energy units
1404 : !> \param maxocc maximum band occupation
1405 : !> \param projector weighted spectral occupation projector
1406 : ! **************************************************************************************************
1407 4 : SUBROUTINE fixed_n_spectral_projector(hamiltonian, weights, target_electrons, temperature, &
1408 4 : maxocc, projector)
1409 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: hamiltonian
1410 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: weights
1411 : REAL(KIND=dp), INTENT(IN) :: target_electrons, temperature, maxocc
1412 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: projector
1413 :
1414 : COMPLEX(KIND=dp), DIMENSION(SIZE(hamiltonian, 1), &
1415 8 : SIZE(hamiltonian, 1)) :: vectors, weighted_vectors
1416 : INTEGER :: i
1417 : REAL(KIND=dp) :: mu
1418 8 : REAL(KIND=dp), DIMENSION(SIZE(hamiltonian, 1)) :: eigenvalues, occupation
1419 :
1420 4 : CALL diag_complex(hamiltonian, vectors, eigenvalues)
1421 : CALL fixed_n_fermi_occupations(eigenvalues, weights, target_electrons, temperature, &
1422 4 : maxocc, occupation, mu)
1423 52 : weighted_vectors(:, :) = vectors(:, :)
1424 16 : DO i = 1, SIZE(eigenvalues)
1425 52 : weighted_vectors(:, i) = weights(i)*occupation(i)*weighted_vectors(:, i)
1426 : END DO
1427 216 : projector(:, :) = MATMUL(weighted_vectors, CONJG(TRANSPOSE(vectors)))
1428 :
1429 4 : END SUBROUTINE fixed_n_spectral_projector
1430 :
1431 : ! **************************************************************************************************
1432 : !> \brief Check the real finite rotation pullback used by Gamma-point REF OT.
1433 : !> \param para_env parallel environment
1434 : !> \param nfail accumulated number of failures
1435 : ! **************************************************************************************************
1436 2 : SUBROUTINE test_real_rotation_frechet(para_env, nfail)
1437 : TYPE(mp_para_env_type), POINTER :: para_env
1438 : INTEGER, INTENT(INOUT) :: nfail
1439 :
1440 : INTEGER, PARAMETER :: m = 3
1441 :
1442 : INTEGER :: i
1443 4 : INTEGER, DIMENSION(:), POINTER :: col_dist, col_size, row_dist, row_size
1444 2 : INTEGER, DIMENSION(:, :), POINTER :: pgrid
1445 : LOGICAL :: found
1446 : REAL(KIND=dp) :: error, fd_slope, hstep, predicted
1447 2 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: block
1448 : REAL(KIND=dp), DIMENSION(m), TARGET :: rotation_evals
1449 : REAL(KIND=dp), DIMENSION(m, m) :: dedu, direction, generator, gradient, &
1450 : rotation_minus, rotation_plus
1451 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
1452 : TYPE(dbcsr_distribution_type) :: dist
1453 16 : TYPE(dbcsr_type), DIMENSION(7), TARGET :: matrices
1454 : TYPE(mp_para_env_type), POINTER :: local_para_env
1455 320 : TYPE(qs_ot_type) :: rotation_env
1456 :
1457 2 : NULLIFY (blacs_env, local_para_env)
1458 2 : generator(:, :) = 0.0_dp
1459 2 : generator(1, 2) = 0.23_dp
1460 2 : generator(2, 1) = -generator(1, 2)
1461 2 : generator(1, 3) = -0.11_dp
1462 2 : generator(3, 1) = -generator(1, 3)
1463 2 : generator(2, 3) = 0.17_dp
1464 2 : generator(3, 2) = -generator(2, 3)
1465 :
1466 2 : direction(:, :) = 0.0_dp
1467 2 : direction(1, 2) = -0.09_dp
1468 2 : direction(2, 1) = -direction(1, 2)
1469 2 : direction(1, 3) = 0.14_dp
1470 2 : direction(3, 1) = -direction(1, 3)
1471 2 : direction(2, 3) = 0.06_dp
1472 2 : direction(3, 2) = -direction(2, 3)
1473 :
1474 : dedu = RESHAPE([0.31_dp, -0.27_dp, 0.08_dp, &
1475 : 0.19_dp, 0.42_dp, -0.16_dp, &
1476 2 : -0.07_dp, 0.21_dp, 0.35_dp], [m, m])
1477 :
1478 0 : ALLOCATE (local_para_env)
1479 2 : CALL local_para_env%from_dup(mp_comm_self)
1480 2 : CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=local_para_env)
1481 2 : ALLOCATE (pgrid(0:0, 0:0), row_dist(1), col_dist(1), row_size(1), col_size(1))
1482 6 : pgrid(:, :) = 0
1483 4 : row_dist(:) = 0
1484 4 : col_dist(:) = 0
1485 4 : row_size(:) = m
1486 4 : col_size(:) = m
1487 : CALL dbcsr_distribution_new(dist, group=mp_comm_self%get_handle(), pgrid=pgrid, &
1488 2 : row_dist=row_dist, col_dist=col_dist)
1489 16 : DO i = 1, SIZE(matrices)
1490 16 : CALL create_one_block(matrices(i), 'real_rotation', dist, row_size, col_size)
1491 : END DO
1492 2 : CALL dbcsr_put_block(matrices(2), 1, 1, generator)
1493 2 : CALL dbcsr_put_block(matrices(3), 1, 1, dedu)
1494 :
1495 2 : rotation_env%para_env => local_para_env
1496 2 : rotation_env%blacs_env => blacs_env
1497 2 : rotation_env%rot_mat_u => matrices(1)
1498 2 : rotation_env%rot_mat_x => matrices(2)
1499 2 : rotation_env%rot_mat_dedu => matrices(3)
1500 2 : rotation_env%rot_mat_gx => matrices(4)
1501 2 : rotation_env%rot_mat_evec_re => matrices(5)
1502 2 : rotation_env%rot_mat_evec_im => matrices(6)
1503 2 : rotation_env%matrix_buf1 => matrices(7)
1504 2 : rotation_env%rot_mat_evals => rotation_evals
1505 :
1506 2 : CALL qs_ot_generate_rotation(rotation_env)
1507 2 : CALL qs_ot_rot_mat_derivative(rotation_env)
1508 2 : gradient(:, :) = 0.0_dp
1509 2 : CALL dbcsr_get_block_p(matrices(4), 1, 1, block, found)
1510 26 : IF (found) gradient = block
1511 :
1512 2 : hstep = 1.0E-6_dp
1513 : rotation_plus = REAL(dense_antihermitian_exp( &
1514 50 : CMPLX(generator + hstep*direction, 0.0_dp, KIND=dp)), KIND=dp)
1515 : rotation_minus = REAL(dense_antihermitian_exp( &
1516 50 : CMPLX(generator - hstep*direction, 0.0_dp, KIND=dp)), KIND=dp)
1517 26 : fd_slope = SUM(dedu*(rotation_plus - rotation_minus))/(2.0_dp*hstep)
1518 26 : predicted = 0.5_dp*SUM(gradient*direction)
1519 2 : error = ABS(predicted - fd_slope)
1520 2 : IF (error > 5.0E-8_dp) nfail = nfail + 1
1521 2 : IF (para_env%is_source()) THEN
1522 1 : WRITE (io_unit, '(A,3(1X,ES13.6))') 'real finite rotation slope fd/predicted/error', &
1523 2 : fd_slope, predicted, error
1524 : END IF
1525 :
1526 16 : DO i = 1, SIZE(matrices)
1527 16 : CALL dbcsr_release(matrices(i))
1528 : END DO
1529 2 : CALL dbcsr_distribution_release(dist)
1530 2 : DEALLOCATE (pgrid, row_dist, col_dist, row_size, col_size)
1531 2 : CALL cp_blacs_env_release(blacs_env)
1532 2 : CALL mp_para_env_release(local_para_env)
1533 :
1534 8 : END SUBROUTINE test_real_rotation_frechet
1535 :
1536 : ! **************************************************************************************************
1537 : !> \brief Check the combined finite complex REF and rotation derivatives.
1538 : !> \param para_env parallel environment
1539 : !> \param nfail accumulated number of failures
1540 : ! **************************************************************************************************
1541 2 : SUBROUTINE test_complex_ref_rotation(para_env, nfail)
1542 : TYPE(mp_para_env_type), POINTER :: para_env
1543 : INTEGER, INTENT(INOUT) :: nfail
1544 :
1545 : COMPLEX(KIND=dp), DIMENSION(k, k) :: direction_a, generator, gradient_a, &
1546 : inverse_sqrt, rotation
1547 : COMPLEX(KIND=dp), DIMENSION(n, k) :: c_dense, c_reference, direction_x, &
1548 : gradient_x, hc_dense, q_dense, x_dense
1549 : COMPLEX(KIND=dp), DIMENSION(n, n) :: hamiltonian, overlap
1550 : INTEGER :: i, j
1551 4 : INTEGER, DIMENSION(:), POINTER :: col_dist, col_k, col_n, row_dist, row_k, &
1552 2 : row_n
1553 2 : INTEGER, DIMENSION(:, :), POINTER :: pgrid
1554 : REAL(KIND=dp) :: error_c, error_ref, error_rot, fd_ref, &
1555 : fd_rot, hstep, predicted_ref, &
1556 : predicted_rot
1557 : REAL(KIND=dp), DIMENSION(k) :: occupation
1558 : REAL(KIND=dp), DIMENSION(k), TARGET :: rotation_evals
1559 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
1560 : TYPE(dbcsr_distribution_type) :: dist_kk, dist_nk, dist_nn
1561 22 : TYPE(dbcsr_type), DIMENSION(10), TARGET :: rot_matrix
1562 6 : TYPE(dbcsr_type), DIMENSION(2), TARGET :: nk_buffer
1563 8 : TYPE(dbcsr_type), DIMENSION(3), TARGET :: nonsym_buffer
1564 10 : TYPE(dbcsr_type), DIMENSION(4), TARGET :: sym_buffer
1565 : TYPE(dbcsr_type), POINTER :: c_im_p, c_re_p, hc_im_p, hc_re_p, &
1566 : s_im_p, s_re_p
1567 : TYPE(dbcsr_type), TARGET :: c_im, c_re, f_im, f_re, g_im, g_re, &
1568 : hc_im, hc_re, s_im, s_re, sx_im, &
1569 : sx_re, x_im, x_re
1570 : TYPE(mp_para_env_type), POINTER :: local_para_env
1571 320 : TYPE(qs_ot_type) :: rotation_env
1572 :
1573 2 : NULLIFY (blacs_env, c_im_p, c_re_p, hc_im_p, hc_re_p, local_para_env, &
1574 2 : s_im_p, s_re_p)
1575 :
1576 : x_dense(:, 1) = [CMPLX(1.05_dp, 0.12_dp, KIND=dp), CMPLX(0.18_dp, -0.27_dp, KIND=dp), &
1577 8 : CMPLX(-0.14_dp, 0.16_dp, KIND=dp)]
1578 : x_dense(:, 2) = [CMPLX(0.26_dp, 0.31_dp, KIND=dp), CMPLX(0.94_dp, -0.17_dp, KIND=dp), &
1579 8 : CMPLX(0.21_dp, 0.13_dp, KIND=dp)]
1580 : direction_x(:, 1) = [CMPLX(0.11_dp, -0.07_dp, KIND=dp), CMPLX(-0.16_dp, 0.09_dp, KIND=dp), &
1581 8 : CMPLX(0.05_dp, 0.14_dp, KIND=dp)]
1582 : direction_x(:, 2) = [CMPLX(-0.08_dp, 0.15_dp, KIND=dp), CMPLX(0.13_dp, 0.04_dp, KIND=dp), &
1583 8 : CMPLX(-0.10_dp, -0.06_dp, KIND=dp)]
1584 :
1585 : generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1586 2 : generator(1, 1) = CMPLX(0.0_dp, 0.19_dp, KIND=dp)
1587 2 : generator(2, 2) = CMPLX(0.0_dp, -0.08_dp, KIND=dp)
1588 2 : generator(1, 2) = CMPLX(0.23_dp, -0.11_dp, KIND=dp)
1589 2 : generator(2, 1) = -CONJG(generator(1, 2))
1590 : direction_a(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1591 2 : direction_a(1, 1) = CMPLX(0.0_dp, -0.07_dp, KIND=dp)
1592 2 : direction_a(2, 2) = CMPLX(0.0_dp, 0.05_dp, KIND=dp)
1593 2 : direction_a(1, 2) = CMPLX(-0.09_dp, 0.06_dp, KIND=dp)
1594 2 : direction_a(2, 1) = -CONJG(direction_a(1, 2))
1595 :
1596 2 : hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1597 2 : hamiltonian(1, 1) = CMPLX(0.65_dp, 0.0_dp, KIND=dp)
1598 2 : hamiltonian(2, 2) = CMPLX(-0.35_dp, 0.0_dp, KIND=dp)
1599 2 : hamiltonian(3, 3) = CMPLX(1.15_dp, 0.0_dp, KIND=dp)
1600 2 : hamiltonian(1, 2) = CMPLX(0.18_dp, 0.12_dp, KIND=dp)
1601 2 : hamiltonian(1, 3) = CMPLX(-0.09_dp, 0.07_dp, KIND=dp)
1602 2 : hamiltonian(2, 3) = CMPLX(0.14_dp, -0.17_dp, KIND=dp)
1603 8 : DO i = 1, n
1604 14 : DO j = i + 1, n
1605 12 : hamiltonian(j, i) = CONJG(hamiltonian(i, j))
1606 : END DO
1607 : END DO
1608 2 : overlap(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1609 8 : DO i = 1, n
1610 8 : overlap(i, i) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
1611 : END DO
1612 2 : occupation = [1.0_dp, 0.37_dp]
1613 :
1614 38 : CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(x_dense)), x_dense), inverse_sqrt)
1615 38 : q_dense = MATMUL(x_dense, inverse_sqrt)
1616 2 : rotation = dense_antihermitian_exp(generator)
1617 38 : c_reference = MATMUL(q_dense, rotation)
1618 :
1619 2 : ALLOCATE (local_para_env)
1620 2 : CALL local_para_env%from_dup(mp_comm_self)
1621 2 : CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=local_para_env)
1622 :
1623 : ALLOCATE (pgrid(0:0, 0:0), row_dist(1), col_dist(1), row_n(1), row_k(1), &
1624 2 : col_n(1), col_k(1))
1625 6 : pgrid(:, :) = 0
1626 4 : row_dist(:) = 0
1627 4 : col_dist(:) = 0
1628 4 : row_n(:) = n
1629 4 : col_n(:) = n
1630 4 : row_k(:) = k
1631 4 : col_k(:) = k
1632 : CALL dbcsr_distribution_new(dist_nk, group=mp_comm_self%get_handle(), pgrid=pgrid, &
1633 2 : row_dist=row_dist, col_dist=col_dist)
1634 : CALL dbcsr_distribution_new(dist_nn, group=mp_comm_self%get_handle(), pgrid=pgrid, &
1635 2 : row_dist=row_dist, col_dist=col_dist)
1636 : CALL dbcsr_distribution_new(dist_kk, group=mp_comm_self%get_handle(), pgrid=pgrid, &
1637 2 : row_dist=row_dist, col_dist=col_dist)
1638 :
1639 2 : CALL create_one_block(x_re, 'rotation_ref_x_re', dist_nk, row_n, col_k)
1640 2 : CALL create_one_block(x_im, 'rotation_ref_x_im', dist_nk, row_n, col_k)
1641 2 : CALL create_one_block(sx_re, 'rotation_ref_sx_re', dist_nk, row_n, col_k)
1642 2 : CALL create_one_block(sx_im, 'rotation_ref_sx_im', dist_nk, row_n, col_k)
1643 2 : CALL create_one_block(c_re, 'rotation_ref_c_re', dist_nk, row_n, col_k)
1644 2 : CALL create_one_block(c_im, 'rotation_ref_c_im', dist_nk, row_n, col_k)
1645 2 : CALL create_one_block(hc_re, 'rotation_ref_hc_re', dist_nk, row_n, col_k)
1646 2 : CALL create_one_block(hc_im, 'rotation_ref_hc_im', dist_nk, row_n, col_k)
1647 2 : CALL create_one_block(g_re, 'rotation_ref_g_re', dist_nk, row_n, col_k)
1648 2 : CALL create_one_block(g_im, 'rotation_ref_g_im', dist_nk, row_n, col_k)
1649 2 : CALL create_one_block(s_re, 'rotation_ref_s_re', dist_nn, row_n, col_n)
1650 2 : CALL create_one_block(s_im, 'rotation_ref_s_im', dist_nn, row_n, col_n)
1651 2 : CALL create_one_block(f_re, 'rotation_ref_f_re', dist_kk, row_k, col_k)
1652 2 : CALL create_one_block(f_im, 'rotation_ref_f_im', dist_kk, row_k, col_k)
1653 10 : DO i = 1, SIZE(sym_buffer)
1654 10 : CALL create_one_block(sym_buffer(i), 'rotation_ref_sym', dist_kk, row_k, col_k)
1655 : END DO
1656 8 : DO i = 1, SIZE(nonsym_buffer)
1657 8 : CALL create_one_block(nonsym_buffer(i), 'rotation_ref_nonsym', dist_kk, row_k, col_k)
1658 : END DO
1659 6 : DO i = 1, SIZE(nk_buffer)
1660 6 : CALL create_one_block(nk_buffer(i), 'rotation_ref_nk', dist_nk, row_n, col_k)
1661 : END DO
1662 22 : DO i = 1, SIZE(rot_matrix)
1663 22 : CALL create_one_block(rot_matrix(i), 'rotation_ref_rot', dist_kk, row_k, col_k)
1664 : END DO
1665 :
1666 2 : CALL put_complex_pair(x_re, x_im, x_dense)
1667 2 : CALL put_complex_pair(s_re, s_im, overlap)
1668 2 : CALL put_complex_pair(rot_matrix(3), rot_matrix(4), generator)
1669 :
1670 2 : rotation_env%has_complex_kpoint_state = .TRUE.
1671 2 : rotation_env%settings%do_rotation = .TRUE.
1672 2 : rotation_env%settings%eps_irac_filter_matrix = 0.0_dp
1673 2 : rotation_env%settings%ot_method = 'LBFG'
1674 2 : rotation_env%para_env => local_para_env
1675 2 : rotation_env%blacs_env => blacs_env
1676 2 : rotation_env%matrix_x => x_re
1677 2 : rotation_env%matrix_x_im => x_im
1678 2 : rotation_env%matrix_sx => sx_re
1679 2 : rotation_env%matrix_sx_im => sx_im
1680 2 : rotation_env%matrix_gx => g_re
1681 2 : rotation_env%matrix_gx_im => g_im
1682 2 : rotation_env%matrix_ref_inv_sqrt => f_re
1683 2 : rotation_env%matrix_ref_inv_sqrt_im => f_im
1684 2 : rotation_env%buf1_k_k_sym => sym_buffer(1)
1685 2 : rotation_env%buf2_k_k_sym => sym_buffer(2)
1686 2 : rotation_env%buf3_k_k_sym => sym_buffer(3)
1687 2 : rotation_env%buf4_k_k_sym => sym_buffer(4)
1688 2 : rotation_env%buf1_k_k_nosym => nonsym_buffer(1)
1689 2 : rotation_env%buf2_k_k_nosym => nonsym_buffer(2)
1690 2 : rotation_env%buf3_k_k_nosym => nonsym_buffer(3)
1691 2 : rotation_env%buf1_n_k => nk_buffer(1)
1692 2 : rotation_env%buf1_n_k_dp => nk_buffer(2)
1693 2 : rotation_env%rot_mat_u => rot_matrix(1)
1694 2 : rotation_env%rot_mat_u_im => rot_matrix(2)
1695 2 : rotation_env%rot_mat_x => rot_matrix(3)
1696 2 : rotation_env%rot_mat_x_im => rot_matrix(4)
1697 2 : rotation_env%rot_mat_dedu => rot_matrix(5)
1698 2 : rotation_env%rot_mat_dedu_im => rot_matrix(6)
1699 2 : rotation_env%rot_mat_gx => rot_matrix(7)
1700 2 : rotation_env%rot_mat_gx_im => rot_matrix(8)
1701 2 : rotation_env%rot_mat_evec_re => rot_matrix(9)
1702 2 : rotation_env%rot_mat_evec_im => rot_matrix(10)
1703 2 : rotation_env%rot_mat_evals => rotation_evals
1704 :
1705 2 : c_re_p => c_re
1706 2 : c_im_p => c_im
1707 2 : s_re_p => s_re
1708 2 : s_im_p => s_im
1709 2 : CALL qs_ot_get_orbitals_ref_complex(c_re_p, c_im_p, s_re_p, s_im_p, rotation_env)
1710 2 : CALL get_complex_pair(c_re, c_im, c_dense)
1711 18 : error_c = MAXVAL(ABS(c_dense - c_reference))
1712 :
1713 70 : hc_dense = 2.0_dp*MATMUL(hamiltonian, c_dense)
1714 6 : DO j = 1, k
1715 18 : hc_dense(:, j) = occupation(j)*hc_dense(:, j)
1716 : END DO
1717 2 : CALL put_complex_pair(hc_re, hc_im, hc_dense)
1718 2 : hc_re_p => hc_re
1719 2 : hc_im_p => hc_im
1720 2 : CALL qs_ot_get_derivative_ref_complex(hc_re_p, hc_im_p, rotation_env)
1721 2 : CALL get_complex_pair(g_re, g_im, gradient_x)
1722 2 : CALL get_complex_pair(rot_matrix(7), rot_matrix(8), gradient_a)
1723 :
1724 2 : hstep = 1.0E-6_dp
1725 : fd_ref = (rotated_polar_energy(x_dense + hstep*direction_x, generator, &
1726 : hamiltonian, occupation) - &
1727 : rotated_polar_energy(x_dense - hstep*direction_x, generator, &
1728 34 : hamiltonian, occupation))/(2.0_dp*hstep)
1729 18 : predicted_ref = REAL(SUM(CONJG(gradient_x)*direction_x), KIND=dp)
1730 2 : error_ref = ABS(predicted_ref - fd_ref)
1731 :
1732 : fd_rot = (rotated_polar_energy(x_dense, generator + hstep*direction_a, &
1733 : hamiltonian, occupation) - &
1734 : rotated_polar_energy(x_dense, generator - hstep*direction_a, &
1735 26 : hamiltonian, occupation))/(2.0_dp*hstep)
1736 : predicted_rot = 0.5_dp*(SUM(REAL(gradient_a, KIND=dp)*REAL(direction_a, KIND=dp)) + &
1737 26 : SUM(AIMAG(gradient_a)*AIMAG(direction_a)))
1738 2 : error_rot = ABS(predicted_rot - fd_rot)
1739 :
1740 2 : IF (error_c > 5.0E-11_dp .OR. error_ref > 5.0E-8_dp .OR. &
1741 0 : error_rot > 5.0E-8_dp) nfail = nfail + 1
1742 2 : IF (para_env%is_source()) THEN
1743 1 : WRITE (io_unit, '(A,1X,ES13.6)') 'complex finite REF rotation orbital error', error_c
1744 1 : WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex finite REF slope fd/predicted/error', &
1745 2 : fd_ref, predicted_ref, error_ref
1746 1 : WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex finite rotation slope fd/predicted/error', &
1747 2 : fd_rot, predicted_rot, error_rot
1748 : END IF
1749 :
1750 22 : DO i = 1, SIZE(rot_matrix)
1751 22 : CALL dbcsr_release(rot_matrix(i))
1752 : END DO
1753 6 : DO i = 1, SIZE(nk_buffer)
1754 6 : CALL dbcsr_release(nk_buffer(i))
1755 : END DO
1756 8 : DO i = 1, SIZE(nonsym_buffer)
1757 8 : CALL dbcsr_release(nonsym_buffer(i))
1758 : END DO
1759 10 : DO i = 1, SIZE(sym_buffer)
1760 10 : CALL dbcsr_release(sym_buffer(i))
1761 : END DO
1762 2 : CALL dbcsr_release(f_im)
1763 2 : CALL dbcsr_release(f_re)
1764 2 : CALL dbcsr_release(s_im)
1765 2 : CALL dbcsr_release(s_re)
1766 2 : CALL dbcsr_release(g_im)
1767 2 : CALL dbcsr_release(g_re)
1768 2 : CALL dbcsr_release(hc_im)
1769 2 : CALL dbcsr_release(hc_re)
1770 2 : CALL dbcsr_release(c_im)
1771 2 : CALL dbcsr_release(c_re)
1772 2 : CALL dbcsr_release(sx_im)
1773 2 : CALL dbcsr_release(sx_re)
1774 2 : CALL dbcsr_release(x_im)
1775 2 : CALL dbcsr_release(x_re)
1776 2 : CALL dbcsr_distribution_release(dist_kk)
1777 2 : CALL dbcsr_distribution_release(dist_nn)
1778 2 : CALL dbcsr_distribution_release(dist_nk)
1779 2 : DEALLOCATE (pgrid, row_dist, col_dist, row_n, row_k, col_n, col_k)
1780 2 : CALL cp_blacs_env_release(blacs_env)
1781 2 : CALL mp_para_env_release(local_para_env)
1782 :
1783 10 : END SUBROUTINE test_complex_ref_rotation
1784 :
1785 : ! **************************************************************************************************
1786 : !> \brief Check the finite complex rotation Frechet derivative against central differences.
1787 : !> \param para_env parallel environment
1788 : !> \param nfail accumulated number of failures
1789 : ! **************************************************************************************************
1790 2 : SUBROUTINE test_complex_rotation_frechet(para_env, nfail)
1791 : TYPE(mp_para_env_type), POINTER :: para_env
1792 : INTEGER, INTENT(INOUT) :: nfail
1793 :
1794 : INTEGER, PARAMETER :: m = 3
1795 :
1796 : COMPLEX(KIND=dp), DIMENSION(m, m) :: direction, fd, generator, predicted
1797 : INTEGER :: i, j
1798 : REAL(KIND=dp) :: error, hstep
1799 : REAL(KIND=dp), DIMENSION(m) :: evals
1800 :
1801 2 : evals = [-0.7_dp, 0.2000003_dp, 1.1_dp]
1802 2 : generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1803 8 : DO i = 1, m
1804 8 : generator(i, i) = CMPLX(0.0_dp, -evals(i), KIND=dp)
1805 : END DO
1806 : direction(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1807 2 : direction(1, 2) = CMPLX(0.17_dp, -0.09_dp, KIND=dp)
1808 2 : direction(2, 1) = -CONJG(direction(1, 2))
1809 2 : direction(1, 3) = CMPLX(-0.06_dp, 0.13_dp, KIND=dp)
1810 2 : direction(3, 1) = -CONJG(direction(1, 3))
1811 2 : direction(2, 3) = CMPLX(0.11_dp, 0.04_dp, KIND=dp)
1812 2 : direction(3, 2) = -CONJG(direction(2, 3))
1813 2 : direction(1, 1) = CMPLX(0.0_dp, 0.08_dp, KIND=dp)
1814 2 : direction(2, 2) = CMPLX(0.0_dp, -0.03_dp, KIND=dp)
1815 2 : direction(3, 3) = CMPLX(0.0_dp, 0.05_dp, KIND=dp)
1816 :
1817 8 : DO i = 1, m
1818 26 : DO j = 1, m
1819 : predicted(i, j) = qs_ot_complex_exp_frechet_kernel(evals(i), evals(j))* &
1820 24 : direction(i, j)
1821 : END DO
1822 : END DO
1823 2 : hstep = 1.0E-6_dp
1824 : fd = (dense_antihermitian_exp(generator + hstep*direction) - &
1825 74 : dense_antihermitian_exp(generator - hstep*direction))/(2.0_dp*hstep)
1826 26 : error = MAXVAL(ABS(fd - predicted))
1827 2 : IF (error > 5.0E-9_dp) nfail = nfail + 1
1828 2 : IF (para_env%is_source()) THEN
1829 1 : WRITE (io_unit, '(A,1X,ES13.6)') 'complex rotation Frechet finite-difference error', error
1830 : END IF
1831 :
1832 2 : END SUBROUTINE test_complex_rotation_frechet
1833 :
1834 : ! **************************************************************************************************
1835 : !> \brief Check union handling for distinct real/imaginary DBCSR block patterns.
1836 : !> \param para_env parallel environment
1837 : !> \param nfail accumulated number of failures
1838 : ! **************************************************************************************************
1839 2 : SUBROUTINE test_sparse_frechet_patterns(para_env, nfail)
1840 : TYPE(mp_para_env_type), POINTER :: para_env
1841 : INTEGER, INTENT(INOUT) :: nfail
1842 :
1843 : COMPLEX(KIND=dp) :: expected, value
1844 2 : INTEGER, DIMENSION(:), POINTER :: col_dist, col_size, row_dist, row_size
1845 : INTEGER, DIMENSION(:, :), POINTER :: pgrid
1846 : LOGICAL :: found
1847 : REAL(KIND=dp) :: error
1848 : REAL(KIND=dp), DIMENSION(2) :: evals
1849 2 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: block
1850 : TYPE(dbcsr_distribution_type) :: dist
1851 : TYPE(dbcsr_type) :: inner_im, inner_re, outer_im, outer_re
1852 :
1853 2 : ALLOCATE (pgrid(0:0, 0:0), row_dist(2), col_dist(2), row_size(2), col_size(2))
1854 6 : pgrid(:, :) = 0
1855 6 : row_dist(:) = 0
1856 6 : col_dist(:) = 0
1857 6 : row_size(:) = 1
1858 6 : col_size(:) = 1
1859 : CALL dbcsr_distribution_new(dist, group=mp_comm_self%get_handle(), pgrid=pgrid, &
1860 2 : row_dist=row_dist, col_dist=col_dist)
1861 2 : CALL dbcsr_create(inner_re, 'frechet_sparse_re', dist, dbcsr_type_no_symmetry, row_size, col_size)
1862 2 : CALL dbcsr_reserve_blocks(inner_re, rows=[1], cols=[2])
1863 2 : CALL dbcsr_finalize(inner_re)
1864 2 : CALL dbcsr_create(inner_im, 'frechet_sparse_im', dist, dbcsr_type_no_symmetry, row_size, col_size)
1865 2 : CALL dbcsr_reserve_blocks(inner_im, rows=[2], cols=[1])
1866 2 : CALL dbcsr_finalize(inner_im)
1867 2 : CALL dbcsr_put_block(inner_re, 1, 2, RESHAPE([0.37_dp], [1, 1]))
1868 2 : CALL dbcsr_put_block(inner_im, 2, 1, RESHAPE([-0.21_dp], [1, 1]))
1869 :
1870 2 : evals = [-0.4_dp, 0.9_dp]
1871 2 : CALL qs_ot_apply_complex_frechet_dbcsr(evals, inner_re, inner_im, outer_re, outer_im)
1872 : error = 0.0_dp
1873 2 : expected = qs_ot_complex_exp_frechet_kernel(evals(1), evals(2))*CMPLX(0.37_dp, 0.0_dp, KIND=dp)
1874 2 : value = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1875 2 : CALL dbcsr_get_block_p(outer_re, 1, 2, block, found)
1876 2 : IF (found) value = value + CMPLX(block(1, 1), 0.0_dp, KIND=dp)
1877 2 : CALL dbcsr_get_block_p(outer_im, 1, 2, block, found)
1878 2 : IF (found) value = value + CMPLX(0.0_dp, block(1, 1), KIND=dp)
1879 2 : error = MAX(error, ABS(value - expected))
1880 :
1881 2 : expected = qs_ot_complex_exp_frechet_kernel(evals(2), evals(1))*CMPLX(0.0_dp, -0.21_dp, KIND=dp)
1882 2 : value = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1883 2 : CALL dbcsr_get_block_p(outer_re, 2, 1, block, found)
1884 2 : IF (found) value = value + CMPLX(block(1, 1), 0.0_dp, KIND=dp)
1885 2 : CALL dbcsr_get_block_p(outer_im, 2, 1, block, found)
1886 2 : IF (found) value = value + CMPLX(0.0_dp, block(1, 1), KIND=dp)
1887 2 : error = MAX(error, ABS(value - expected))
1888 2 : IF (error > 5.0E-14_dp) nfail = nfail + 1
1889 2 : IF (para_env%is_source()) THEN
1890 1 : WRITE (io_unit, '(A,1X,ES13.6)') 'sparse complex Frechet block-union error', error
1891 : END IF
1892 :
1893 2 : CALL dbcsr_release(outer_im)
1894 2 : CALL dbcsr_release(outer_re)
1895 2 : CALL dbcsr_release(inner_im)
1896 2 : CALL dbcsr_release(inner_re)
1897 2 : CALL dbcsr_distribution_release(dist)
1898 2 : DEALLOCATE (pgrid, row_dist, col_dist, row_size, col_size)
1899 :
1900 2 : END SUBROUTINE test_sparse_frechet_patterns
1901 : ! **************************************************************************************************
1902 : !> \brief Check the finite complex STRICT chart and its analytic derivative.
1903 : !> \param dist_nk distribution for orbital-shaped matrices
1904 : !> \param dist_kk distribution for occupied-space matrices
1905 : !> \param row_n AO block sizes
1906 : !> \param row_k occupied-space row block sizes
1907 : !> \param col_k occupied-space column block sizes
1908 : !> \param para_env parallel environment
1909 : !> \param nfail accumulated number of failures
1910 : ! **************************************************************************************************
1911 2 : SUBROUTINE test_complex_strict_derivative(dist_nk, dist_kk, row_n, row_k, col_k, para_env, nfail)
1912 : TYPE(dbcsr_distribution_type), INTENT(IN) :: dist_nk, dist_kk
1913 : INTEGER, DIMENSION(:), INTENT(INOUT), POINTER :: row_n, row_k, col_k
1914 : TYPE(mp_para_env_type), POINTER :: para_env
1915 : INTEGER, INTENT(INOUT) :: nfail
1916 :
1917 : CHARACTER(LEN=32) :: name
1918 : COMPLEX(KIND=dp), DIMENSION(k, k) :: direction_a, generator, gradient_a, &
1919 : inverse_sqrt, rotation
1920 : COMPLEX(KIND=dp), DIMENSION(n, k) :: c0, direction, gradient, hc, orbitals, &
1921 : orbitals_base, sc0, sx, x
1922 : COMPLEX(KIND=dp), DIMENSION(n, n) :: hamiltonian, overlap
1923 : INTEGER :: i
1924 : LOGICAL :: found
1925 : REAL(KIND=dp) :: error, error_rot, fd_slope, &
1926 : fd_slope_rot, hstep, ortho_error, &
1927 : slope, slope_rot
1928 2 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: block
1929 : REAL(KIND=dp), DIMENSION(k) :: occupation
1930 : REAL(KIND=dp), DIMENSION(k), TARGET :: rotation_evals
1931 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
1932 22 : TYPE(dbcsr_type), DIMENSION(10), TARGET :: rot_matrix
1933 32 : TYPE(dbcsr_type), DIMENSION(15), TARGET :: nk
1934 52 : TYPE(dbcsr_type), DIMENSION(25), TARGET :: kk
1935 : TYPE(dbcsr_type), POINTER :: hc_im_p, hc_re_p, sx_im_p, sx_re_p, &
1936 : x_im_p, x_re_p
1937 : TYPE(mp_para_env_type), POINTER :: strict_para_env
1938 320 : TYPE(qs_ot_type) :: strict_env
1939 :
1940 2 : NULLIFY (blacs_env, hc_im_p, hc_re_p, strict_para_env, x_im_p, x_re_p, sx_im_p, sx_re_p)
1941 2 : ALLOCATE (strict_para_env)
1942 2 : CALL strict_para_env%from_dup(mp_comm_self)
1943 2 : CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=strict_para_env)
1944 32 : DO i = 1, SIZE(nk)
1945 30 : WRITE (name, '(A,I0)') 'complex_strict_nk_', i
1946 32 : CALL create_one_block(nk(i), TRIM(name), dist_nk, row_n, col_k)
1947 : END DO
1948 52 : DO i = 1, SIZE(kk)
1949 50 : WRITE (name, '(A,I0)') 'complex_strict_kk_', i
1950 52 : CALL create_one_block(kk(i), TRIM(name), dist_kk, row_k, col_k)
1951 : END DO
1952 22 : DO i = 1, SIZE(rot_matrix)
1953 20 : WRITE (name, '(A,I0)') 'complex_strict_rot_', i
1954 22 : CALL create_one_block(rot_matrix(i), TRIM(name), dist_kk, row_k, col_k)
1955 : END DO
1956 :
1957 : overlap(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1958 2 : overlap(1, 1) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
1959 2 : overlap(2, 2) = CMPLX(1.2_dp, 0.0_dp, KIND=dp)
1960 2 : overlap(3, 3) = CMPLX(1.4_dp, 0.0_dp, KIND=dp)
1961 2 : overlap(1, 2) = CMPLX(0.06_dp, 0.03_dp, KIND=dp)
1962 2 : overlap(2, 1) = CONJG(overlap(1, 2))
1963 2 : overlap(1, 3) = CMPLX(-0.04_dp, 0.02_dp, KIND=dp)
1964 2 : overlap(3, 1) = CONJG(overlap(1, 3))
1965 2 : overlap(2, 3) = CMPLX(0.05_dp, -0.01_dp, KIND=dp)
1966 2 : overlap(3, 2) = CONJG(overlap(2, 3))
1967 :
1968 : c0(:, 1) = [CMPLX(1.0_dp, 0.10_dp, KIND=dp), CMPLX(0.12_dp, -0.08_dp, KIND=dp), &
1969 8 : CMPLX(-0.07_dp, 0.04_dp, KIND=dp)]
1970 : c0(:, 2) = [CMPLX(0.09_dp, 0.05_dp, KIND=dp), CMPLX(0.85_dp, -0.12_dp, KIND=dp), &
1971 8 : CMPLX(0.11_dp, 0.06_dp, KIND=dp)]
1972 90 : CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(c0)), MATMUL(overlap, c0)), inverse_sqrt)
1973 38 : c0 = MATMUL(c0, inverse_sqrt)
1974 :
1975 2 : x(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1976 2 : x(3, 1) = CMPLX(0.23_dp, 0.17_dp, KIND=dp)
1977 2 : x(3, 2) = CMPLX(-0.14_dp, 0.21_dp, KIND=dp)
1978 144 : x = x - MATMUL(c0, MATMUL(CONJG(TRANSPOSE(c0)), MATMUL(overlap, x)))
1979 2 : direction(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1980 2 : direction(3, 1) = CMPLX(0.11_dp, -0.09_dp, KIND=dp)
1981 2 : direction(3, 2) = CMPLX(0.07_dp, 0.13_dp, KIND=dp)
1982 144 : direction = direction - MATMUL(c0, MATMUL(CONJG(TRANSPOSE(c0)), MATMUL(overlap, direction)))
1983 :
1984 : hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1985 2 : hamiltonian(1, 1) = CMPLX(-0.70_dp, 0.0_dp, KIND=dp)
1986 2 : hamiltonian(2, 2) = CMPLX(-0.20_dp, 0.0_dp, KIND=dp)
1987 2 : hamiltonian(3, 3) = CMPLX(0.90_dp, 0.0_dp, KIND=dp)
1988 2 : hamiltonian(1, 2) = CMPLX(0.08_dp, 0.04_dp, KIND=dp)
1989 2 : hamiltonian(2, 1) = CONJG(hamiltonian(1, 2))
1990 2 : hamiltonian(1, 3) = CMPLX(-0.12_dp, 0.07_dp, KIND=dp)
1991 2 : hamiltonian(3, 1) = CONJG(hamiltonian(1, 3))
1992 2 : hamiltonian(2, 3) = CMPLX(0.05_dp, -0.09_dp, KIND=dp)
1993 2 : hamiltonian(3, 2) = CONJG(hamiltonian(2, 3))
1994 :
1995 : generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1996 2 : generator(1, 1) = CMPLX(0.0_dp, 0.16_dp, KIND=dp)
1997 2 : generator(2, 2) = CMPLX(0.0_dp, -0.09_dp, KIND=dp)
1998 2 : generator(1, 2) = CMPLX(0.18_dp, -0.07_dp, KIND=dp)
1999 2 : generator(2, 1) = -CONJG(generator(1, 2))
2000 : direction_a(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2001 2 : direction_a(1, 1) = CMPLX(0.0_dp, -0.05_dp, KIND=dp)
2002 2 : direction_a(2, 2) = CMPLX(0.0_dp, 0.04_dp, KIND=dp)
2003 2 : direction_a(1, 2) = CMPLX(-0.08_dp, 0.06_dp, KIND=dp)
2004 2 : direction_a(2, 1) = -CONJG(direction_a(1, 2))
2005 2 : occupation = [1.0_dp, 0.34_dp]
2006 :
2007 2 : CALL strict_orbitals(c0, x, overlap, orbitals_base)
2008 2 : rotation = dense_antihermitian_exp(generator)
2009 38 : orbitals = MATMUL(orbitals_base, rotation)
2010 70 : hc = 2.0_dp*MATMUL(hamiltonian, orbitals)
2011 6 : DO i = 1, k
2012 18 : hc(:, i) = occupation(i)*hc(:, i)
2013 : END DO
2014 54 : sx = MATMUL(overlap, x)
2015 54 : sc0 = MATMUL(overlap, c0)
2016 2 : CALL put_complex_pair(nk(1), nk(2), x)
2017 2 : CALL put_complex_pair(nk(3), nk(4), sx)
2018 2 : CALL put_complex_pair(nk(5), nk(6), hc)
2019 2 : CALL put_complex_pair(nk(9), nk(10), c0)
2020 2 : CALL put_complex_pair(nk(11), nk(12), sc0)
2021 2 : CALL put_complex_pair(rot_matrix(3), rot_matrix(4), generator)
2022 :
2023 2 : strict_env%has_complex_kpoint_state = .TRUE.
2024 2 : strict_env%settings%ot_algorithm = 'TOD'
2025 2 : strict_env%settings%do_rotation = .TRUE.
2026 2 : strict_env%para_env => strict_para_env
2027 2 : strict_env%blacs_env => blacs_env
2028 2 : strict_env%matrix_x => nk(1)
2029 2 : strict_env%matrix_x_im => nk(2)
2030 2 : strict_env%matrix_sx => nk(3)
2031 2 : strict_env%matrix_sx_im => nk(4)
2032 2 : strict_env%matrix_gx => nk(7)
2033 2 : strict_env%matrix_gx_im => nk(8)
2034 2 : strict_env%matrix_c0 => nk(9)
2035 2 : strict_env%matrix_c0_im => nk(10)
2036 2 : strict_env%matrix_sc0 => nk(11)
2037 2 : strict_env%matrix_sc0_im => nk(12)
2038 2 : strict_env%matrix_buf_nk => nk(13)
2039 2 : strict_env%matrix_buf_nk_im => nk(14)
2040 2 : strict_env%matrix_tmp_nk => nk(15)
2041 2 : strict_env%matrix_p => kk(1)
2042 2 : strict_env%matrix_p_im => kk(2)
2043 2 : strict_env%matrix_r => kk(3)
2044 2 : strict_env%matrix_r_im => kk(4)
2045 2 : strict_env%matrix_sinp => kk(5)
2046 2 : strict_env%matrix_sinp_im => kk(6)
2047 2 : strict_env%matrix_cosp => kk(7)
2048 2 : strict_env%matrix_cosp_im => kk(8)
2049 2 : strict_env%matrix_sinp_b => kk(9)
2050 2 : strict_env%matrix_cosp_b => kk(10)
2051 2 : strict_env%matrix_buf1 => kk(11)
2052 2 : strict_env%matrix_buf1_im => kk(12)
2053 2 : strict_env%matrix_buf2 => kk(13)
2054 2 : strict_env%matrix_buf2_im => kk(14)
2055 2 : strict_env%matrix_buf3 => kk(15)
2056 2 : strict_env%matrix_buf3_im => kk(16)
2057 2 : strict_env%matrix_buf4 => kk(17)
2058 2 : strict_env%matrix_buf4_im => kk(18)
2059 2 : strict_env%matrix_os => kk(19)
2060 2 : strict_env%matrix_os_im => kk(20)
2061 2 : strict_env%matrix_buf1_ortho => kk(21)
2062 2 : strict_env%matrix_buf1_ortho_im => kk(22)
2063 2 : strict_env%matrix_buf2_ortho => kk(23)
2064 2 : strict_env%matrix_buf2_ortho_im => kk(24)
2065 2 : strict_env%matrix_tmp_ortho => kk(25)
2066 2 : strict_env%rot_mat_u => rot_matrix(1)
2067 2 : strict_env%rot_mat_u_im => rot_matrix(2)
2068 2 : strict_env%rot_mat_x => rot_matrix(3)
2069 2 : strict_env%rot_mat_x_im => rot_matrix(4)
2070 2 : strict_env%rot_mat_dedu => rot_matrix(5)
2071 2 : strict_env%rot_mat_dedu_im => rot_matrix(6)
2072 2 : strict_env%rot_mat_gx => rot_matrix(7)
2073 2 : strict_env%rot_mat_gx_im => rot_matrix(8)
2074 2 : strict_env%rot_mat_evec_re => rot_matrix(9)
2075 2 : strict_env%rot_mat_evec_im => rot_matrix(10)
2076 2 : strict_env%rot_mat_evals => rotation_evals
2077 2 : ALLOCATE (strict_env%evals(k), strict_env%dum(k))
2078 :
2079 2 : x_re_p => nk(1)
2080 2 : x_im_p => nk(2)
2081 2 : sx_re_p => nk(3)
2082 2 : sx_im_p => nk(4)
2083 2 : hc_re_p => nk(5)
2084 2 : hc_im_p => nk(6)
2085 2 : CALL qs_ot_get_p_complex(x_re_p, x_im_p, sx_re_p, sx_im_p, strict_env)
2086 2 : CALL qs_ot_get_derivative_complex(hc_re_p, hc_im_p, strict_env)
2087 :
2088 2 : gradient(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2089 2 : CALL dbcsr_get_block_p(nk(7), 1, 1, block, found)
2090 18 : IF (found) gradient = gradient + CMPLX(block, 0.0_dp, KIND=dp)
2091 2 : CALL dbcsr_get_block_p(nk(8), 1, 1, block, found)
2092 18 : IF (found) gradient = gradient + CMPLX(0.0_dp, block, KIND=dp)
2093 2 : CALL get_complex_pair(rot_matrix(7), rot_matrix(8), gradient_a)
2094 :
2095 2 : hstep = 1.0E-6_dp
2096 : fd_slope = (strict_rotated_energy(c0, x + hstep*direction, generator, overlap, &
2097 : hamiltonian, occupation) - &
2098 : strict_rotated_energy(c0, x - hstep*direction, generator, overlap, &
2099 34 : hamiltonian, occupation))/(2.0_dp*hstep)
2100 18 : slope = REAL(SUM(CONJG(gradient)*direction), KIND=dp)
2101 2 : error = ABS(slope - fd_slope)
2102 : fd_slope_rot = (strict_rotated_energy(c0, x, generator + hstep*direction_a, overlap, &
2103 : hamiltonian, occupation) - &
2104 : strict_rotated_energy(c0, x, generator - hstep*direction_a, overlap, &
2105 26 : hamiltonian, occupation))/(2.0_dp*hstep)
2106 : slope_rot = 0.5_dp*(SUM(REAL(gradient_a, KIND=dp)*REAL(direction_a, KIND=dp)) + &
2107 26 : SUM(AIMAG(gradient_a)*AIMAG(direction_a)))
2108 2 : error_rot = ABS(slope_rot - fd_slope_rot)
2109 : ortho_error = MAXVAL(ABS(MATMUL(CONJG(TRANSPOSE(orbitals)), MATMUL(overlap, orbitals)) - &
2110 : RESHAPE([CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
2111 : CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
2112 : CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
2113 102 : CMPLX(1.0_dp, 0.0_dp, KIND=dp)], [k, k])))
2114 2 : IF (error > 5.0E-8_dp .OR. error_rot > 5.0E-8_dp .OR. &
2115 0 : ortho_error > 5.0E-13_dp) nfail = nfail + 1
2116 2 : IF (para_env%is_source()) THEN
2117 1 : WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex finite-STRICT derivative: fd/predicted/error', &
2118 2 : fd_slope, slope, error
2119 1 : WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex finite-STRICT rotation: fd/predicted/error', &
2120 2 : fd_slope_rot, slope_rot, error_rot
2121 1 : WRITE (io_unit, '(A,1X,ES13.6)') 'complex finite-STRICT orthonormality error', ortho_error
2122 : END IF
2123 :
2124 2 : DEALLOCATE (strict_env%evals, strict_env%dum)
2125 22 : DO i = SIZE(rot_matrix), 1, -1
2126 22 : CALL dbcsr_release(rot_matrix(i))
2127 : END DO
2128 52 : DO i = SIZE(kk), 1, -1
2129 52 : CALL dbcsr_release(kk(i))
2130 : END DO
2131 32 : DO i = SIZE(nk), 1, -1
2132 32 : CALL dbcsr_release(nk(i))
2133 : END DO
2134 2 : CALL cp_blacs_env_release(blacs_env)
2135 2 : CALL mp_para_env_release(strict_para_env)
2136 :
2137 4 : END SUBROUTINE test_complex_strict_derivative
2138 : ! **************************************************************************************************
2139 : !> \brief Check gauge invariance, Hermiticity, and positivity of the complex inverse operator.
2140 : !> \param para_env parallel environment
2141 : !> \param nfail accumulated number of failures
2142 : ! **************************************************************************************************
2143 2 : SUBROUTINE test_complex_preconditioner_gauge(para_env, nfail)
2144 : TYPE(mp_para_env_type), POINTER :: para_env
2145 : INTEGER, INTENT(INOUT) :: nfail
2146 :
2147 : COMPLEX(KIND=dp) :: value
2148 : COMPLEX(KIND=dp), DIMENSION(k) :: phases
2149 : COMPLEX(KIND=dp), DIMENSION(n, k) :: c0, c0_phase, c0_rot, gradient, &
2150 : gradient_phase, output, output_phase
2151 : COMPLEX(KIND=dp), DIMENSION(n, n) :: eigenvectors, full_all_vectors, h, inverse, &
2152 : inverse_rot, inverse_s, inverse_single, inverse_t, reference_s, reference_t, s, &
2153 : spectral_metric, t
2154 : INTEGER :: i, j
2155 : REAL(KIND=dp) :: full_all_descent, full_all_gauge_error, full_all_orthogonality_error, &
2156 : full_single_hermitian_error, gauge_error, hermitian_error, s_error, t_error
2157 : REAL(KIND=dp), DIMENSION(k) :: c0_evals
2158 : REAL(KIND=dp), DIMENSION(n) :: eigenvalues, eigenvalues_single
2159 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
2160 : TYPE(cp_cfm_type) :: matrix_c0, matrix_c0_phase, matrix_c0_rot, matrix_gradient, &
2161 : matrix_gradient_phase, matrix_h, matrix_output, matrix_output_phase, matrix_s, matrix_t
2162 : TYPE(cp_fm_struct_type), POINTER :: fmstruct_nk, fmstruct_nn
2163 : TYPE(preconditioner_type) :: full_all, full_all_phase, preconditioner, preconditioner_rot, &
2164 : preconditioner_s, preconditioner_single, preconditioner_t
2165 :
2166 2 : NULLIFY (blacs_env, fmstruct_nk, fmstruct_nn)
2167 2 : CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=para_env)
2168 : CALL cp_fm_struct_create(fmstruct=fmstruct_nn, para_env=para_env, context=blacs_env, &
2169 2 : nrow_global=n, ncol_global=n)
2170 : CALL cp_fm_struct_create(fmstruct=fmstruct_nk, para_env=para_env, context=blacs_env, &
2171 2 : nrow_global=n, ncol_global=k)
2172 2 : CALL cp_cfm_create(matrix_h, fmstruct_nn, name='complex preconditioner H')
2173 2 : CALL cp_cfm_create(matrix_s, fmstruct_nn, name='complex preconditioner S')
2174 2 : CALL cp_cfm_create(matrix_t, fmstruct_nn, name='complex preconditioner T')
2175 2 : CALL cp_cfm_create(matrix_c0, fmstruct_nk, name='complex preconditioner C')
2176 2 : CALL cp_cfm_create(matrix_c0_phase, fmstruct_nk, name='complex preconditioner phased C')
2177 2 : CALL cp_cfm_create(matrix_c0_rot, fmstruct_nk, name='complex preconditioner C U')
2178 2 : CALL cp_cfm_create(matrix_gradient, fmstruct_nk, name='complex preconditioner gradient')
2179 : CALL cp_cfm_create(matrix_gradient_phase, fmstruct_nk, &
2180 2 : name='complex preconditioner phased gradient')
2181 2 : CALL cp_cfm_create(matrix_output, fmstruct_nk, name='complex preconditioner output')
2182 : CALL cp_cfm_create(matrix_output_phase, fmstruct_nk, &
2183 2 : name='complex preconditioner phased output')
2184 2 : CALL cp_cfm_set_all(matrix_h, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
2185 2 : CALL cp_cfm_set_all(matrix_s, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
2186 2 : CALL cp_cfm_set_all(matrix_t, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
2187 2 : CALL cp_cfm_set_all(matrix_c0, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
2188 2 : CALL cp_cfm_set_all(matrix_c0_phase, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
2189 2 : CALL cp_cfm_set_all(matrix_c0_rot, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
2190 :
2191 : h(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2192 2 : h(1, 1) = CMPLX(-0.35_dp, 0.0_dp, KIND=dp)
2193 2 : h(2, 2) = CMPLX(0.15_dp, 0.0_dp, KIND=dp)
2194 2 : h(3, 3) = CMPLX(0.90_dp, 0.0_dp, KIND=dp)
2195 2 : h(1, 2) = CMPLX(0.08_dp, 0.04_dp, KIND=dp)
2196 2 : h(2, 1) = CONJG(h(1, 2))
2197 2 : h(1, 3) = CMPLX(-0.06_dp, 0.03_dp, KIND=dp)
2198 2 : h(3, 1) = CONJG(h(1, 3))
2199 2 : h(2, 3) = CMPLX(0.05_dp, -0.07_dp, KIND=dp)
2200 2 : h(3, 2) = CONJG(h(2, 3))
2201 2 : s(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2202 2 : s(1, 1) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
2203 2 : s(2, 2) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
2204 2 : s(3, 3) = CMPLX(1.4_dp, 0.0_dp, KIND=dp)
2205 2 : s(1, 3) = CMPLX(0.08_dp, 0.03_dp, KIND=dp)
2206 2 : s(3, 1) = CONJG(s(1, 3))
2207 2 : s(2, 3) = CMPLX(-0.04_dp, 0.02_dp, KIND=dp)
2208 2 : s(3, 2) = CONJG(s(2, 3))
2209 2 : t = h
2210 8 : DO i = 1, n
2211 8 : t(i, i) = t(i, i) + CMPLX(1.50_dp, 0.0_dp, KIND=dp)
2212 : END DO
2213 2 : c0(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2214 2 : c0(1, 1) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
2215 2 : c0(2, 2) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
2216 2 : c0_rot(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2217 2 : c0_rot(1, 1) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
2218 2 : c0_rot(1, 2) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
2219 2 : c0_rot(2, 1) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
2220 2 : c0_rot(2, 2) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
2221 : phases = [EXP(CMPLX(0.0_dp, 0.43_dp, KIND=dp)), &
2222 2 : EXP(CMPLX(0.0_dp, -0.67_dp, KIND=dp))]
2223 6 : DO j = 1, k
2224 18 : c0_phase(:, j) = phases(j)*c0(:, j)
2225 : END DO
2226 : gradient(:, 1) = [CMPLX(0.17_dp, 0.02_dp, KIND=dp), &
2227 : CMPLX(-0.08_dp, 0.05_dp, KIND=dp), &
2228 8 : CMPLX(0.11_dp, -0.04_dp, KIND=dp)]
2229 : gradient(:, 2) = [CMPLX(-0.03_dp, 0.06_dp, KIND=dp), &
2230 : CMPLX(0.09_dp, -0.02_dp, KIND=dp), &
2231 8 : CMPLX(-0.14_dp, 0.05_dp, KIND=dp)]
2232 6 : DO j = 1, k
2233 18 : gradient_phase(:, j) = phases(j)*gradient(:, j)
2234 : END DO
2235 6 : c0_evals = [REAL(h(1, 1), KIND=dp), REAL(h(2, 2), KIND=dp)]
2236 :
2237 8 : DO j = 1, n
2238 26 : DO i = 1, n
2239 18 : CALL cp_cfm_set_element(matrix_h, i, j, h(i, j))
2240 18 : CALL cp_cfm_set_element(matrix_s, i, j, s(i, j))
2241 24 : CALL cp_cfm_set_element(matrix_t, i, j, t(i, j))
2242 : END DO
2243 : END DO
2244 6 : DO j = 1, k
2245 18 : DO i = 1, n
2246 12 : CALL cp_cfm_set_element(matrix_c0, i, j, c0(i, j))
2247 12 : CALL cp_cfm_set_element(matrix_c0_phase, i, j, c0_phase(i, j))
2248 12 : CALL cp_cfm_set_element(matrix_c0_rot, i, j, c0_rot(i, j))
2249 12 : CALL cp_cfm_set_element(matrix_gradient, i, j, gradient(i, j))
2250 16 : CALL cp_cfm_set_element(matrix_gradient_phase, i, j, gradient_phase(i, j))
2251 : END DO
2252 : END DO
2253 :
2254 2 : CALL init_preconditioner(full_all, para_env, blacs_env)
2255 2 : CALL init_preconditioner(full_all_phase, para_env, blacs_env)
2256 2 : CALL init_preconditioner(preconditioner, para_env, blacs_env)
2257 2 : CALL init_preconditioner(preconditioner_rot, para_env, blacs_env)
2258 2 : CALL init_preconditioner(preconditioner_s, para_env, blacs_env)
2259 2 : CALL init_preconditioner(preconditioner_single, para_env, blacs_env)
2260 2 : CALL init_preconditioner(preconditioner_t, para_env, blacs_env)
2261 2 : CALL make_complex_full_single_inverse(preconditioner, matrix_c0, matrix_h, matrix_s, 0.20_dp)
2262 2 : CALL make_complex_full_single_inverse(preconditioner_rot, matrix_c0_rot, matrix_h, matrix_s, 0.20_dp)
2263 2 : CALL make_complex_full_s_inverse(preconditioner_s, matrix_s)
2264 2 : CALL make_complex_full_single(preconditioner_single, matrix_h, matrix_s, 0.15_dp, 0.20_dp)
2265 2 : CALL make_complex_full_kinetic(preconditioner_t, matrix_t, matrix_s, 0.20_dp)
2266 2 : CALL make_complex_full_all(full_all, matrix_c0, matrix_h, matrix_s, c0_evals, 0.08_dp)
2267 : CALL make_complex_full_all(full_all_phase, matrix_c0_phase, matrix_h, matrix_s, &
2268 2 : c0_evals, 0.08_dp)
2269 2 : CALL apply_preconditioner(full_all, matrix_gradient, matrix_output)
2270 2 : CALL apply_preconditioner(full_all_phase, matrix_gradient_phase, matrix_output_phase)
2271 8 : DO j = 1, n
2272 26 : DO i = 1, n
2273 18 : CALL cp_cfm_get_element(preconditioner%complex_fm, i, j, value)
2274 18 : inverse(i, j) = value
2275 18 : CALL cp_cfm_get_element(preconditioner_rot%complex_fm, i, j, value)
2276 18 : inverse_rot(i, j) = value
2277 18 : CALL cp_cfm_get_element(preconditioner_s%complex_fm, i, j, value)
2278 18 : inverse_s(i, j) = value
2279 18 : CALL cp_cfm_get_element(preconditioner_single%complex_fm, i, j, value)
2280 18 : inverse_single(i, j) = value
2281 18 : CALL cp_cfm_get_element(preconditioner_t%complex_fm, i, j, value)
2282 24 : inverse_t(i, j) = value
2283 : END DO
2284 : END DO
2285 26 : gauge_error = MAXVAL(ABS(inverse - inverse_rot))
2286 26 : hermitian_error = MAXVAL(ABS(inverse - CONJG(TRANSPOSE(inverse))))
2287 2 : CALL diag_complex(inverse, eigenvectors, eigenvalues)
2288 26 : full_single_hermitian_error = MAXVAL(ABS(inverse_single - CONJG(TRANSPOSE(inverse_single))))
2289 2 : CALL diag_complex(inverse_single, eigenvectors, eigenvalues_single)
2290 2 : CALL dense_hermitian_inverse(s, reference_s)
2291 26 : CALL dense_hermitian_inverse(t + 0.20_dp*s, reference_t)
2292 26 : s_error = MAXVAL(ABS(inverse_s - reference_s))
2293 26 : t_error = MAXVAL(ABS(inverse_t - reference_t))
2294 6 : DO j = 1, k
2295 18 : DO i = 1, n
2296 12 : CALL cp_cfm_get_element(matrix_output, i, j, value)
2297 12 : output(i, j) = value
2298 12 : CALL cp_cfm_get_element(matrix_output_phase, i, j, value)
2299 16 : output_phase(i, j) = value
2300 : END DO
2301 : END DO
2302 8 : DO j = 1, n
2303 26 : DO i = 1, n
2304 18 : CALL cp_cfm_get_element(full_all%complex_fm, i, j, value)
2305 24 : full_all_vectors(i, j) = value
2306 : END DO
2307 : END DO
2308 2 : full_all_gauge_error = 0.0_dp
2309 6 : DO j = 1, k
2310 : full_all_gauge_error = MAX(full_all_gauge_error, &
2311 18 : MAXVAL(ABS(output_phase(:, j) - phases(j)*output(:, j))))
2312 : END DO
2313 : spectral_metric = MATMUL(CONJG(TRANSPOSE(full_all_vectors)), &
2314 158 : MATMUL(s, full_all_vectors))
2315 : full_all_orthogonality_error = MAXVAL(ABS(spectral_metric - &
2316 : RESHAPE([CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
2317 : CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
2318 : CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
2319 : CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
2320 : CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
2321 : CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
2322 : CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
2323 : CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
2324 26 : CMPLX(1.0_dp, 0.0_dp, KIND=dp)], [n, n])))
2325 18 : full_all_descent = REAL(SUM(CONJG(gradient)*output), KIND=dp)
2326 8 : IF (gauge_error > 1.0E-11_dp .OR. hermitian_error > 1.0E-12_dp .OR. &
2327 0 : MINVAL(eigenvalues) <= 0.0_dp) nfail = nfail + 1
2328 2 : IF (s_error > 1.0E-11_dp .OR. t_error > 1.0E-11_dp) nfail = nfail + 1
2329 8 : IF (full_single_hermitian_error > 1.0E-12_dp .OR. &
2330 0 : MINVAL(eigenvalues_single) <= 0.0_dp) nfail = nfail + 1
2331 : IF (full_all_gauge_error > 1.0E-11_dp .OR. &
2332 2 : full_all_orthogonality_error > 1.0E-11_dp .OR. full_all_descent <= 0.0_dp) nfail = nfail + 1
2333 2 : IF (para_env%is_source()) THEN
2334 1 : WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex FULL_SINGLE gauge/hermitian/min-eigenvalue', &
2335 5 : gauge_error, hermitian_error, MINVAL(eigenvalues)
2336 1 : WRITE (io_unit, '(A,2(1X,ES13.6))') 'complex S/KINETIC inverse errors', s_error, t_error
2337 1 : WRITE (io_unit, '(A,2(1X,ES13.6))') 'complex FULL_SINGLE hermitian/min-eigenvalue', &
2338 5 : full_single_hermitian_error, MINVAL(eigenvalues_single)
2339 1 : WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex FULL_ALL phase/orthogonality/descent', &
2340 2 : full_all_gauge_error, full_all_orthogonality_error, full_all_descent
2341 : END IF
2342 :
2343 2 : CALL destroy_preconditioner(full_all_phase)
2344 2 : CALL destroy_preconditioner(full_all)
2345 2 : CALL destroy_preconditioner(preconditioner_t)
2346 2 : CALL destroy_preconditioner(preconditioner_single)
2347 2 : CALL destroy_preconditioner(preconditioner_s)
2348 2 : CALL destroy_preconditioner(preconditioner_rot)
2349 2 : CALL destroy_preconditioner(preconditioner)
2350 2 : CALL cp_cfm_release(matrix_c0_rot)
2351 2 : CALL cp_cfm_release(matrix_c0_phase)
2352 2 : CALL cp_cfm_release(matrix_c0)
2353 2 : CALL cp_cfm_release(matrix_output_phase)
2354 2 : CALL cp_cfm_release(matrix_output)
2355 2 : CALL cp_cfm_release(matrix_gradient_phase)
2356 2 : CALL cp_cfm_release(matrix_gradient)
2357 2 : CALL cp_cfm_release(matrix_t)
2358 2 : CALL cp_cfm_release(matrix_s)
2359 2 : CALL cp_cfm_release(matrix_h)
2360 2 : CALL cp_fm_struct_release(fmstruct_nk)
2361 2 : CALL cp_fm_struct_release(fmstruct_nn)
2362 2 : CALL cp_blacs_env_release(blacs_env)
2363 :
2364 4 : END SUBROUTINE test_complex_preconditioner_gauge
2365 :
2366 : ! **************************************************************************************************
2367 : !> \brief Check the intentionally narrow complex k-point preconditioner support matrix.
2368 : !> \param nfail accumulated number of failures
2369 : ! **************************************************************************************************
2370 2 : SUBROUTINE test_preconditioner_support(nfail)
2371 : INTEGER, INTENT(INOUT) :: nfail
2372 :
2373 2 : IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_none, .FALSE.)) nfail = nfail + 1
2374 2 : IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_full_single_inverse, .FALSE.)) THEN
2375 0 : nfail = nfail + 1
2376 : END IF
2377 2 : IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_full_all, .FALSE.)) nfail = nfail + 1
2378 2 : IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_full_single, .FALSE.)) nfail = nfail + 1
2379 2 : IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_full_kinetic, .FALSE.)) nfail = nfail + 1
2380 2 : IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_s_inverse, .FALSE.)) nfail = nfail + 1
2381 2 : IF (qs_ot_kpoint_preconditioner_supported(ot_precond_full_single_inverse, .TRUE.)) nfail = nfail + 1
2382 :
2383 2 : IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
2384 0 : ot_precond_full_single, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
2385 2 : IF (qs_ot_kpoint_preconditioner_solver_supported( &
2386 0 : ot_precond_full_single, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
2387 :
2388 2 : IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
2389 0 : ot_precond_full_single_inverse, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
2390 2 : IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
2391 0 : ot_precond_full_single_inverse, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
2392 2 : IF (qs_ot_kpoint_preconditioner_solver_supported( &
2393 0 : ot_precond_full_single_inverse, ot_precond_solver_direct, .FALSE.)) nfail = nfail + 1
2394 2 : IF (qs_ot_kpoint_preconditioner_solver_supported( &
2395 0 : ot_precond_full_single_inverse, ot_precond_solver_chebyshev, .FALSE.)) nfail = nfail + 1
2396 2 : IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
2397 0 : ot_precond_full_all, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
2398 2 : IF (qs_ot_kpoint_preconditioner_solver_supported( &
2399 0 : ot_precond_full_all, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
2400 2 : IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
2401 0 : ot_precond_full_kinetic, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
2402 2 : IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
2403 0 : ot_precond_full_kinetic, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
2404 2 : IF (qs_ot_kpoint_preconditioner_solver_supported( &
2405 0 : ot_precond_full_kinetic, ot_precond_solver_direct, .FALSE.)) nfail = nfail + 1
2406 2 : IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
2407 0 : ot_precond_s_inverse, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
2408 2 : IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
2409 0 : ot_precond_s_inverse, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
2410 2 : IF (qs_ot_kpoint_preconditioner_solver_supported( &
2411 0 : ot_precond_s_inverse, ot_precond_solver_chebyshev, .FALSE.)) nfail = nfail + 1
2412 2 : IF (ABS(qs_ot_kpoint_preconditioner_scale(0.25_dp) - 4.0_dp) > EPSILON(1.0_dp)) THEN
2413 0 : nfail = nfail + 1
2414 : END IF
2415 :
2416 2 : END SUBROUTINE test_preconditioner_support
2417 :
2418 : ! **************************************************************************************************
2419 : !> \brief Create and reserve a one-block DBCSR matrix.
2420 : !> \param matrix ...
2421 : !> \param name ...
2422 : !> \param dist ...
2423 : !> \param row_size ...
2424 : !> \param col_size ...
2425 : ! **************************************************************************************************
2426 210 : SUBROUTINE create_one_block(matrix, name, dist, row_size, col_size)
2427 : TYPE(dbcsr_type), INTENT(INOUT) :: matrix
2428 : CHARACTER(LEN=*), INTENT(IN) :: name
2429 : TYPE(dbcsr_distribution_type), INTENT(IN) :: dist
2430 : INTEGER, DIMENSION(:), INTENT(INOUT), POINTER :: row_size, col_size
2431 :
2432 210 : CALL dbcsr_create(matrix, name, dist, dbcsr_type_no_symmetry, row_size, col_size)
2433 210 : CALL dbcsr_reserve_blocks(matrix, rows=[1], cols=[1])
2434 210 : CALL dbcsr_finalize(matrix)
2435 :
2436 210 : END SUBROUTINE create_one_block
2437 :
2438 : ! **************************************************************************************************
2439 : !> \brief Store a dense complex matrix in a real/imaginary DBCSR pair.
2440 : !> \param matrix_re ...
2441 : !> \param matrix_im ...
2442 : !> \param matrix ...
2443 : ! **************************************************************************************************
2444 28 : SUBROUTINE put_complex_pair(matrix_re, matrix_im, matrix)
2445 : TYPE(dbcsr_type), INTENT(INOUT) :: matrix_re, matrix_im
2446 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: matrix
2447 :
2448 248 : CALL dbcsr_put_block(matrix_re, 1, 1, REAL(matrix, KIND=dp))
2449 248 : CALL dbcsr_put_block(matrix_im, 1, 1, AIMAG(matrix))
2450 :
2451 28 : END SUBROUTINE put_complex_pair
2452 :
2453 : ! **************************************************************************************************
2454 : !> \brief Retrieve a dense complex matrix from a one-block DBCSR pair.
2455 : !> \param matrix_re ...
2456 : !> \param matrix_im ...
2457 : !> \param matrix ...
2458 : ! **************************************************************************************************
2459 8 : SUBROUTINE get_complex_pair(matrix_re, matrix_im, matrix)
2460 : TYPE(dbcsr_type), INTENT(INOUT) :: matrix_re, matrix_im
2461 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: matrix
2462 :
2463 : LOGICAL :: found
2464 8 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: block
2465 :
2466 64 : matrix(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2467 8 : CALL dbcsr_get_block_p(matrix_re, 1, 1, block, found)
2468 64 : IF (found) matrix = matrix + CMPLX(block, 0.0_dp, KIND=dp)
2469 8 : CALL dbcsr_get_block_p(matrix_im, 1, 1, block, found)
2470 64 : IF (found) matrix = matrix + CMPLX(0.0_dp, block, KIND=dp)
2471 :
2472 8 : END SUBROUTINE get_complex_pair
2473 :
2474 : ! **************************************************************************************************
2475 : !> \brief Dense fixed-occupation energy after finite REF orthogonalization and rotation.
2476 : !> \param x REF coordinate
2477 : !> \param generator anti-Hermitian rotation generator
2478 : !> \param hamiltonian Hermitian one-particle Hamiltonian
2479 : !> \param occupation orbital occupations
2480 : !> \return energy
2481 : ! **************************************************************************************************
2482 8 : FUNCTION rotated_polar_energy(x, generator, hamiltonian, occupation) RESULT(energy)
2483 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: x, generator, hamiltonian
2484 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: occupation
2485 : REAL(KIND=dp) :: energy
2486 :
2487 : COMPLEX(KIND=dp), &
2488 16 : DIMENSION(SIZE(x, 1), SIZE(x, 2)) :: orbitals
2489 : COMPLEX(KIND=dp), &
2490 16 : DIMENSION(SIZE(x, 2), SIZE(x, 2)) :: inverse_sqrt, rotation
2491 : INTEGER :: i
2492 :
2493 176 : CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(x)), x), inverse_sqrt)
2494 8 : rotation = dense_antihermitian_exp(generator)
2495 496 : orbitals = MATMUL(MATMUL(x, inverse_sqrt), rotation)
2496 8 : energy = 0.0_dp
2497 24 : DO i = 1, SIZE(occupation)
2498 : energy = energy + occupation(i)* &
2499 296 : REAL(DOT_PRODUCT(orbitals(:, i), MATMUL(hamiltonian, orbitals(:, i))), KIND=dp)
2500 : END DO
2501 :
2502 8 : END FUNCTION rotated_polar_energy
2503 :
2504 : ! **************************************************************************************************
2505 : !> \brief Dense reference exponential for an anti-Hermitian matrix.
2506 : !> \param generator anti-Hermitian generator
2507 : !> \return its unitary exponential
2508 : ! **************************************************************************************************
2509 46 : FUNCTION dense_antihermitian_exp(generator) RESULT(rotation)
2510 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: generator
2511 : COMPLEX(KIND=dp), DIMENSION(SIZE(generator, 1), &
2512 : SIZE(generator, 2)) :: rotation
2513 :
2514 : COMPLEX(KIND=dp), DIMENSION(SIZE(generator, 1), &
2515 46 : SIZE(generator, 2)) :: hermitian, vectors
2516 : INTEGER :: i
2517 46 : REAL(KIND=dp), DIMENSION(SIZE(generator, 1)) :: eigenvalues
2518 :
2519 478 : hermitian = CMPLX(0.0_dp, 1.0_dp, KIND=dp)*generator
2520 46 : CALL diag_complex(hermitian, vectors, eigenvalues)
2521 478 : rotation(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2522 164 : DO i = 1, SIZE(eigenvalues)
2523 : rotation = rotation + EXP(CMPLX(0.0_dp, -eigenvalues(i), KIND=dp))* &
2524 472 : MATMUL(RESHAPE(vectors(:, i), [SIZE(generator, 1), 1]), &
2525 3734 : RESHAPE(CONJG(vectors(:, i)), [1, SIZE(generator, 1)]))
2526 : END DO
2527 :
2528 46 : END FUNCTION dense_antihermitian_exp
2529 :
2530 : ! **************************************************************************************************
2531 : !> \brief Hermitian inverse for an independent dense reference.
2532 : !> \param matrix ...
2533 : !> \param inverse ...
2534 : ! **************************************************************************************************
2535 4 : SUBROUTINE dense_hermitian_inverse(matrix, inverse)
2536 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: matrix
2537 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: inverse
2538 :
2539 : COMPLEX(KIND=dp), &
2540 8 : DIMENSION(SIZE(matrix, 1), SIZE(matrix, 2)) :: vectors
2541 : INTEGER :: i
2542 8 : REAL(KIND=dp), DIMENSION(SIZE(matrix, 1)) :: eigenvalues
2543 :
2544 4 : CALL diag_complex(matrix, vectors, eigenvalues)
2545 52 : inverse(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2546 16 : DO i = 1, SIZE(eigenvalues)
2547 48 : inverse = inverse + MATMUL(RESHAPE(vectors(:, i), [SIZE(matrix, 1), 1]), &
2548 : RESHAPE(CONJG(vectors(:, i)), [1, SIZE(matrix, 1)]))/ &
2549 436 : eigenvalues(i)
2550 : END DO
2551 :
2552 4 : END SUBROUTINE dense_hermitian_inverse
2553 :
2554 : ! **************************************************************************************************
2555 : !> \brief Hermitian inverse square root for the independent dense reference.
2556 : !> \param matrix ...
2557 : !> \param inverse ...
2558 : ! **************************************************************************************************
2559 18 : SUBROUTINE dense_inverse_sqrt(matrix, inverse)
2560 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: matrix
2561 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: inverse
2562 :
2563 : COMPLEX(KIND=dp), &
2564 36 : DIMENSION(SIZE(matrix, 1), SIZE(matrix, 2)) :: vectors
2565 : INTEGER :: i
2566 36 : REAL(KIND=dp), DIMENSION(SIZE(matrix, 1)) :: eigenvalues
2567 :
2568 18 : CALL diag_complex(matrix, vectors, eigenvalues)
2569 126 : inverse(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2570 54 : DO i = 1, SIZE(eigenvalues)
2571 144 : inverse = inverse + MATMUL(RESHAPE(vectors(:, i), [SIZE(matrix, 1), 1]), &
2572 : RESHAPE(CONJG(vectors(:, i)), [1, SIZE(matrix, 1)]))/ &
2573 810 : SQRT(eigenvalues(i))
2574 : END DO
2575 :
2576 18 : END SUBROUTINE dense_inverse_sqrt
2577 :
2578 : ! **************************************************************************************************
2579 : !> \brief Energy of the polar-orthonormalized occupied subspace.
2580 : !> \param coordinate ...
2581 : !> \param hamiltonian ...
2582 : !> \return ...
2583 : ! **************************************************************************************************
2584 4 : FUNCTION polar_energy(coordinate, hamiltonian) RESULT(energy)
2585 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: coordinate, hamiltonian
2586 : REAL(KIND=dp) :: energy
2587 :
2588 : COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 1), &
2589 8 : SIZE(coordinate, 2)) :: orbitals
2590 : COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 2), &
2591 8 : SIZE(coordinate, 2)) :: inverse
2592 :
2593 88 : CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(coordinate)), coordinate), inverse)
2594 116 : orbitals = MATMUL(coordinate, inverse)
2595 156 : energy = REAL(SUM(CONJG(orbitals)*MATMUL(hamiltonian, orbitals)), KIND=dp)
2596 :
2597 4 : END FUNCTION polar_energy
2598 :
2599 : ! **************************************************************************************************
2600 : !> \brief Independent dense finite STRICT transformation for a Hermitian overlap.
2601 : !> \param c0 reference orbitals
2602 : !> \param coordinate tangent coordinate
2603 : !> \param overlap Hermitian overlap matrix
2604 : !> \param orbitals transformed orbitals
2605 : ! **************************************************************************************************
2606 10 : SUBROUTINE strict_orbitals(c0, coordinate, overlap, orbitals)
2607 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: c0, coordinate, overlap
2608 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: orbitals
2609 :
2610 : COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 2), &
2611 20 : SIZE(coordinate, 2)) :: cosp, sinp, vectors
2612 : INTEGER :: i
2613 : REAL(KIND=dp) :: root
2614 20 : REAL(KIND=dp), DIMENSION(SIZE(coordinate, 2)) :: eigenvalues
2615 :
2616 80 : CALL diag_complex(MATMUL(CONJG(TRANSPOSE(coordinate)), MATMUL(overlap, coordinate)), &
2617 580 : vectors, eigenvalues)
2618 70 : cosp(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2619 70 : sinp(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2620 30 : DO i = 1, SIZE(eigenvalues)
2621 20 : root = SQRT(MAX(0.0_dp, eigenvalues(i)))
2622 80 : cosp = cosp + COS(root)*MATMUL(RESHAPE(vectors(:, i), [SIZE(vectors, 1), 1]), &
2623 440 : RESHAPE(CONJG(vectors(:, i)), [1, SIZE(vectors, 1)]))
2624 30 : IF (root < 1.0E-12_dp) THEN
2625 16 : sinp = sinp + MATMUL(RESHAPE(vectors(:, i), [SIZE(vectors, 1), 1]), &
2626 88 : RESHAPE(CONJG(vectors(:, i)), [1, SIZE(vectors, 1)]))
2627 : ELSE
2628 : sinp = sinp + SIN(root)/root* &
2629 64 : MATMUL(RESHAPE(vectors(:, i), [SIZE(vectors, 1), 1]), &
2630 352 : RESHAPE(CONJG(vectors(:, i)), [1, SIZE(vectors, 1)]))
2631 : END IF
2632 : END DO
2633 570 : orbitals = MATMUL(c0, cosp) + MATMUL(coordinate, sinp)
2634 :
2635 10 : END SUBROUTINE strict_orbitals
2636 :
2637 : ! **************************************************************************************************
2638 : !> \brief Energy of the independently transformed finite STRICT orbitals.
2639 : !> \param c0 reference orbitals
2640 : !> \param coordinate tangent coordinate
2641 : !> \param overlap Hermitian overlap matrix
2642 : !> \param hamiltonian Hermitian Hamiltonian
2643 : !> \return energy
2644 : ! **************************************************************************************************
2645 : FUNCTION strict_energy(c0, coordinate, overlap, hamiltonian) RESULT(energy)
2646 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: c0, coordinate, overlap, hamiltonian
2647 : REAL(KIND=dp) :: energy
2648 :
2649 : COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 1), &
2650 : SIZE(coordinate, 2)) :: orbitals
2651 :
2652 : CALL strict_orbitals(c0, coordinate, overlap, orbitals)
2653 : energy = REAL(SUM(CONJG(orbitals)*MATMUL(hamiltonian, orbitals)), KIND=dp)
2654 :
2655 : END FUNCTION strict_energy
2656 :
2657 : ! **************************************************************************************************
2658 : !> \brief Energy of finite STRICT orbitals followed by a complex occupied-space rotation.
2659 : !> \param c0 reference orbitals
2660 : !> \param coordinate tangent coordinate
2661 : !> \param generator anti-Hermitian rotation generator
2662 : !> \param overlap Hermitian overlap matrix
2663 : !> \param hamiltonian Hermitian Hamiltonian
2664 : !> \param occupation orbital occupations
2665 : !> \return energy
2666 : ! **************************************************************************************************
2667 8 : FUNCTION strict_rotated_energy(c0, coordinate, generator, overlap, hamiltonian, &
2668 8 : occupation) RESULT(energy)
2669 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: c0, coordinate, generator, overlap, &
2670 : hamiltonian
2671 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: occupation
2672 : REAL(KIND=dp) :: energy
2673 :
2674 : COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 1), &
2675 16 : SIZE(coordinate, 2)) :: orbitals
2676 : INTEGER :: i
2677 :
2678 8 : CALL strict_orbitals(c0, coordinate, overlap, orbitals)
2679 296 : orbitals = MATMUL(orbitals, dense_antihermitian_exp(generator))
2680 8 : energy = 0.0_dp
2681 24 : DO i = 1, SIZE(occupation)
2682 : energy = energy + occupation(i)* &
2683 296 : REAL(DOT_PRODUCT(orbitals(:, i), MATMUL(hamiltonian, orbitals(:, i))), KIND=dp)
2684 : END DO
2685 :
2686 8 : END FUNCTION strict_rotated_energy
2687 :
2688 : END PROGRAM qs_ot_complex_ref_unittest
|