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 Interface to Wannier90 code
10 : !> \par History
11 : !> 06.2016 created [JGH]
12 : !> \author JGH
13 : ! **************************************************************************************************
14 : MODULE qs_wannier90
15 : USE atomic_kind_types, ONLY: get_atomic_kind
16 : USE cell_types, ONLY: cell_type,&
17 : get_cell
18 : USE cp_blacs_env, ONLY: cp_blacs_env_type
19 : USE cp_cfm_basic_linalg, ONLY: cp_cfm_gemm
20 : USE cp_cfm_types, ONLY: cp_cfm_create,&
21 : cp_cfm_get_submatrix,&
22 : cp_cfm_release,&
23 : cp_cfm_to_fm,&
24 : cp_cfm_type,&
25 : cp_fm_to_cfm
26 : USE cp_control_types, ONLY: dft_control_type
27 : USE cp_dbcsr_api, ONLY: &
28 : dbcsr_create, dbcsr_deallocate_matrix, dbcsr_desymmetrize, dbcsr_p_type, dbcsr_set, &
29 : dbcsr_type, dbcsr_type_antisymmetric, dbcsr_type_no_symmetry, dbcsr_type_symmetric
30 : USE cp_dbcsr_cp2k_link, ONLY: cp_dbcsr_alloc_block_from_nbl
31 : USE cp_dbcsr_operations, ONLY: copy_dbcsr_to_fm,&
32 : dbcsr_deallocate_matrix_set
33 : USE cp_files, ONLY: close_file,&
34 : open_file
35 : USE cp_fm_struct, ONLY: cp_fm_struct_create,&
36 : cp_fm_struct_release,&
37 : cp_fm_struct_type
38 : USE cp_fm_types, ONLY: cp_fm_copy_general,&
39 : cp_fm_create,&
40 : cp_fm_get_element,&
41 : cp_fm_get_info,&
42 : cp_fm_get_submatrix,&
43 : cp_fm_release,&
44 : cp_fm_set_submatrix,&
45 : cp_fm_type
46 : USE cp_log_handling, ONLY: cp_logger_get_default_io_unit,&
47 : cp_logger_type
48 : USE input_section_types, ONLY: section_vals_get,&
49 : section_vals_get_subs_vals,&
50 : section_vals_type,&
51 : section_vals_val_get
52 : USE kinds, ONLY: default_string_length,&
53 : dp
54 : USE kpoint_methods, ONLY: kpoint_env_initialize,&
55 : kpoint_init_cell_index,&
56 : kpoint_initialize,&
57 : kpoint_initialize_mo_set,&
58 : kpoint_initialize_mos,&
59 : rskp_transform
60 : USE kpoint_mo_symmetry_methods, ONLY: kpoint_same_periodic,&
61 : kpoint_transform_scf_mo
62 : USE kpoint_types, ONLY: get_kpoint_info,&
63 : kpoint_create,&
64 : kpoint_env_type,&
65 : kpoint_release,&
66 : kpoint_sym_type,&
67 : kpoint_type
68 : USE machine, ONLY: m_timestamp,&
69 : timestamp_length
70 : USE mathconstants, ONLY: twopi
71 : USE mathlib, ONLY: diag_complex
72 : USE message_passing, ONLY: mp_para_env_type
73 : USE particle_types, ONLY: particle_type
74 : USE physcon, ONLY: angstrom,&
75 : evolt
76 : USE qs_environment_types, ONLY: get_qs_env,&
77 : qs_env_release,&
78 : qs_environment_type
79 : USE qs_gamma2kp, ONLY: create_kp_from_gamma
80 : USE qs_mo_types, ONLY: get_mo_set,&
81 : mo_set_type
82 : USE qs_moments, ONLY: build_berry_kpoint_matrix
83 : USE qs_neighbor_list_types, ONLY: neighbor_list_set_p_type
84 : USE qs_scf_diagonalization, ONLY: do_general_diag_kp
85 : USE qs_scf_types, ONLY: qs_scf_env_type
86 : USE scf_control_types, ONLY: scf_control_type
87 : USE wannier90, ONLY: wannier_setup
88 : #include "./base/base_uses.f90"
89 :
90 : IMPLICIT NONE
91 : PRIVATE
92 :
93 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_wannier90'
94 : INTEGER, PARAMETER, PRIVATE :: w90_kpoints_mp_grid = 0, &
95 : w90_kpoints_scf = 1
96 :
97 : TYPE berry_matrix_type
98 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: sinmat => NULL(), cosmat => NULL()
99 : END TYPE berry_matrix_type
100 :
101 : PUBLIC :: wannier90_interface, prepare_wannier90_scf_mos
102 :
103 : ! **************************************************************************************************
104 :
105 : CONTAINS
106 :
107 : ! **************************************************************************************************
108 : !> \brief ...
109 : !> \param input ...
110 : !> \param logger ...
111 : !> \param qs_env ...
112 : ! **************************************************************************************************
113 23258 : SUBROUTINE wannier90_interface(input, logger, qs_env)
114 : TYPE(section_vals_type), POINTER :: input
115 : TYPE(cp_logger_type), POINTER :: logger
116 : TYPE(qs_environment_type), POINTER :: qs_env
117 :
118 : CHARACTER(len=*), PARAMETER :: routineN = 'wannier90_interface'
119 :
120 : INTEGER :: handle, iw
121 : LOGICAL :: explicit
122 : TYPE(section_vals_type), POINTER :: w_input
123 :
124 : !--------------------------------------------------------------------------------------------!
125 :
126 11629 : CALL timeset(routineN, handle)
127 : w_input => section_vals_get_subs_vals(section_vals=input, &
128 11629 : subsection_name="DFT%PRINT%WANNIER90")
129 11629 : CALL section_vals_get(w_input, explicit=explicit)
130 11629 : IF (explicit) THEN
131 :
132 32 : iw = cp_logger_get_default_io_unit(logger)
133 :
134 32 : IF (iw > 0) THEN
135 : WRITE (iw, '(/,T2,A)') &
136 16 : '!-----------------------------------------------------------------------------!'
137 16 : WRITE (iw, '(T32,A)') "Interface to Wannier90"
138 : WRITE (iw, '(T2,A)') &
139 16 : '!-----------------------------------------------------------------------------!'
140 : END IF
141 :
142 32 : CALL wannier90_files(qs_env, w_input, iw)
143 :
144 32 : IF (iw > 0) THEN
145 : WRITE (iw, '(/,T2,A)') &
146 16 : '!--------------------------------End of Wannier90-----------------------------!'
147 : END IF
148 : END IF
149 11629 : CALL timestop(handle)
150 :
151 11629 : END SUBROUTINE wannier90_interface
152 :
153 : ! **************************************************************************************************
154 : !> \brief ...
155 : !> \param qs_env ...
156 : !> \param input ...
157 : !> \param iw ...
158 : ! **************************************************************************************************
159 32 : SUBROUTINE wannier90_files(qs_env, input, iw)
160 : TYPE(qs_environment_type), POINTER :: qs_env
161 : TYPE(section_vals_type), POINTER :: input
162 : INTEGER, INTENT(IN) :: iw
163 :
164 : INTEGER, PARAMETER :: num_nnmax = 12
165 :
166 : CHARACTER(len=2) :: asym
167 32 : CHARACTER(len=20), ALLOCATABLE, DIMENSION(:) :: atom_symbols
168 : CHARACTER(len=default_string_length) :: filename, input_kp_scheme, reuse_reason, &
169 : seed_name
170 : CHARACTER(LEN=timestamp_length) :: timestamp
171 : INTEGER :: aligned_degenerate_blocks, aligned_degenerate_max_size, i, i_rep, ib, ib1, ib2, &
172 : ibs, ik, ik2, ikk, ikpgr, ispin, iunit, ix, iy, iz, k, kpoints_source, n_rep, nadd, nao, &
173 : nbs, nexcl, nkp, nmo, nntot, nspins, num_atoms, num_bands, num_bands_tot, num_kpts, &
174 : num_wann
175 32 : INTEGER, ALLOCATABLE, DIMENSION(:) :: exclude_bands
176 32 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: nblist, nnlist
177 32 : INTEGER, ALLOCATABLE, DIMENSION(:, :, :) :: nncell
178 : INTEGER, DIMENSION(2) :: kp_range
179 : INTEGER, DIMENSION(3) :: input_nkp_grid, mp_grid
180 32 : INTEGER, DIMENSION(:), POINTER :: invals
181 32 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
182 : LOGICAL :: diis_step, do_kpoints, full_mesh_diagonalized, gamma_only, input_full_grid, &
183 : input_gamma_centered, input_kpoint_symmetry, mp_grid_explicit, mp_grid_valid, my_kpgrp, &
184 : mygrp, reuse_scf_mos, reused_scf_mos, spinors, use_bloch_phases, validate_reuse_ok, &
185 : validate_reuse_scf_mos
186 : REAL(KIND=dp) :: aligned_degenerate_min_svalue, cmmn, gauge_arg, gauge_imag, gauge_real, &
187 : gauge_tmp, ksign, reuse_candidate_deviation, reuse_candidate_metric_deviation, &
188 : reuse_candidate_min_svalue, reuse_candidate_residual, rmmn, &
189 : validation_eigenvalue_deviation, validation_min_svalue, validation_subspace_deviation, &
190 : wkp_ref
191 32 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigval
192 64 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: atoms_cart, b_latt, kpt_latt
193 32 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: reference_eigenvalues
194 32 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :, :) :: reference_mo_imag, reference_mo_real
195 : REAL(KIND=dp), DIMENSION(3) :: bvec, input_kp_shift, phase_center
196 : REAL(KIND=dp), DIMENSION(3, 3) :: h_inv, real_lattice, recip_lattice
197 64 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues, wkp, wkp_source
198 32 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: xkp, xkp_source
199 32 : TYPE(berry_matrix_type), DIMENSION(:), POINTER :: berry_matrix
200 : TYPE(cell_type), POINTER :: cell
201 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
202 : TYPE(cp_cfm_type) :: fmk1_cfm, fmk2_cfm, mmn_cfm, omat_cfm, &
203 : tmp_cfm
204 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_ao, matrix_struct_mmn, &
205 : matrix_struct_work
206 : TYPE(cp_fm_type) :: mat_imag, mat_real, mmn_imag, mmn_real
207 192 : TYPE(cp_fm_type), DIMENSION(2) :: fmk1, fmk2
208 : TYPE(cp_fm_type), POINTER :: fmdummy, fmi, fmr
209 32 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_ks, matrix_s
210 : TYPE(dbcsr_type), POINTER :: cmatrix, cmatrix_full, rmatrix, &
211 : rmatrix_full
212 : TYPE(dft_control_type), POINTER :: dft_control
213 : TYPE(kpoint_env_type), POINTER :: kp
214 : TYPE(kpoint_type), POINTER :: kpoint, qs_kpoint
215 32 : TYPE(mo_set_type), DIMENSION(:), POINTER :: mos
216 : TYPE(mp_para_env_type), POINTER :: para_env
217 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
218 32 : POINTER :: sab_nl
219 32 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
220 : TYPE(qs_environment_type), POINTER :: qs_env_kp
221 : TYPE(qs_scf_env_type), POINTER :: scf_env
222 : TYPE(scf_control_type), POINTER :: scf_control
223 :
224 : !--------------------------------------------------------------------------------------------!
225 :
226 : ! add code for exclude_bands and projectors
227 :
228 : ! generate all arrays needed for the setup call
229 32 : CALL section_vals_val_get(input, "SEED_NAME", c_val=seed_name)
230 32 : CALL section_vals_val_get(input, "MP_GRID", i_vals=invals, explicit=mp_grid_explicit)
231 32 : CALL section_vals_val_get(input, "KPOINTS_SOURCE", i_val=kpoints_source)
232 32 : CALL section_vals_val_get(input, "WANNIER_FUNCTIONS", i_val=num_wann)
233 32 : CALL section_vals_val_get(input, "ADDED_MOS", i_val=nadd)
234 32 : CALL section_vals_val_get(input, "REUSE_SCF_MOS", l_val=reuse_scf_mos)
235 32 : CALL section_vals_val_get(input, "VALIDATE_REUSE_SCF_MOS", l_val=validate_reuse_scf_mos)
236 32 : CALL section_vals_val_get(input, "USE_BLOCH_PHASES", l_val=use_bloch_phases)
237 32 : reuse_scf_mos = reuse_scf_mos .AND. kpoints_source == w90_kpoints_scf
238 32 : validate_reuse_scf_mos = validate_reuse_scf_mos .AND. reuse_scf_mos
239 128 : mp_grid(1:3) = invals(1:3)
240 : ! excluded bands
241 32 : CALL section_vals_val_get(input, "EXCLUDE_BANDS", n_rep_val=n_rep)
242 32 : nexcl = 0
243 32 : DO i_rep = 1, n_rep
244 0 : CALL section_vals_val_get(input, "EXCLUDE_BANDS", i_rep_val=i_rep, i_vals=invals)
245 32 : nexcl = nexcl + SIZE(invals)
246 : END DO
247 32 : IF (nexcl > 0) THEN
248 0 : ALLOCATE (exclude_bands(nexcl))
249 0 : nexcl = 0
250 0 : DO i_rep = 1, n_rep
251 0 : CALL section_vals_val_get(input, "EXCLUDE_BANDS", i_rep_val=i_rep, i_vals=invals)
252 0 : exclude_bands(nexcl + 1:nexcl + SIZE(invals)) = invals(:)
253 0 : nexcl = nexcl + SIZE(invals)
254 : END DO
255 : END IF
256 : !
257 : ! lattice -> Angstrom
258 32 : CALL get_qs_env(qs_env, cell=cell)
259 32 : CALL get_cell(cell, h=real_lattice, h_inv=h_inv)
260 : ! k-points
261 32 : CALL get_qs_env(qs_env, particle_set=particle_set)
262 32 : CALL get_qs_env(qs_env, para_env=para_env)
263 32 : phase_center = 0.0_dp
264 222 : DO i = 1, SIZE(particle_set)
265 3072 : phase_center(1:3) = phase_center(1:3) + MATMUL(h_inv, particle_set(i)%r)
266 : END DO
267 128 : phase_center(1:3) = phase_center(1:3)/REAL(SIZE(particle_set), KIND=dp)
268 128 : phase_center(1:3) = phase_center(1:3) - FLOOR(phase_center(1:3))
269 32 : recip_lattice(1:3, 1:3) = h_inv(1:3, 1:3)
270 416 : real_lattice(1:3, 1:3) = angstrom*real_lattice(1:3, 1:3)
271 800 : recip_lattice(1:3, 1:3) = (twopi/angstrom)*TRANSPOSE(recip_lattice(1:3, 1:3))
272 32 : NULLIFY (kpoint, qs_kpoint, xkp, wkp, xkp_source, wkp_source)
273 32 : CALL get_qs_env(qs_env, do_kpoints=do_kpoints, kpoints=qs_kpoint)
274 32 : input_kpoint_symmetry = .FALSE.
275 32 : input_full_grid = .FALSE.
276 32 : input_kp_scheme = ""
277 32 : IF (do_kpoints .AND. ASSOCIATED(qs_kpoint)) THEN
278 : CALL get_kpoint_info(qs_kpoint, kp_scheme=input_kp_scheme, nkp_grid=input_nkp_grid, &
279 : kp_shift=input_kp_shift, symmetry=input_kpoint_symmetry, &
280 : full_grid=input_full_grid, gamma_centered=input_gamma_centered, &
281 32 : nkp=nkp, xkp=xkp, wkp=wkp)
282 : END IF
283 32 : CALL kpoint_create(kpoint)
284 :
285 0 : SELECT CASE (kpoints_source)
286 : CASE (w90_kpoints_mp_grid)
287 0 : num_kpts = mp_grid(1)*mp_grid(2)*mp_grid(3)
288 0 : ALLOCATE (kpt_latt(3, num_kpts))
289 0 : kpoint%kp_scheme = "MONKHORST-PACK"
290 0 : kpoint%symmetry = .FALSE.
291 0 : kpoint%nkp_grid(1:3) = mp_grid(1:3)
292 0 : kpoint%verbose = .FALSE.
293 0 : kpoint%full_grid = .TRUE.
294 0 : kpoint%eps_geo = 1.0e-6_dp
295 0 : kpoint%use_real_wfn = .FALSE.
296 0 : kpoint%parallel_group_size = para_env%num_pe
297 0 : i = 0
298 0 : DO ix = 0, mp_grid(1) - 1
299 0 : DO iy = 0, mp_grid(2) - 1
300 0 : DO iz = 0, mp_grid(3) - 1
301 0 : i = i + 1
302 0 : kpt_latt(1, i) = REAL(ix, KIND=dp)/REAL(mp_grid(1), KIND=dp)
303 0 : kpt_latt(2, i) = REAL(iy, KIND=dp)/REAL(mp_grid(2), KIND=dp)
304 0 : kpt_latt(3, i) = REAL(iz, KIND=dp)/REAL(mp_grid(3), KIND=dp)
305 : END DO
306 : END DO
307 : END DO
308 0 : kpoint%nkp = num_kpts
309 0 : ALLOCATE (kpoint%xkp(3, num_kpts), kpoint%wkp(num_kpts))
310 0 : kpoint%wkp(:) = 1._dp/REAL(num_kpts, KIND=dp)
311 0 : DO i = 1, num_kpts
312 0 : kpoint%xkp(1:3, i) = (angstrom/twopi)*MATMUL(recip_lattice, kpt_latt(:, i))
313 : END DO
314 :
315 : CASE (w90_kpoints_scf)
316 32 : IF (.NOT. do_kpoints .OR. .NOT. ASSOCIATED(qs_kpoint)) THEN
317 0 : CPABORT("WANNIER90%KPOINTS_SOURCE SCF requires an active DFT%KPOINTS section.")
318 : END IF
319 32 : SELECT CASE (TRIM(input_kp_scheme))
320 : CASE ("GAMMA")
321 0 : mp_grid(:) = 1
322 0 : num_kpts = 1
323 0 : ALLOCATE (kpt_latt(3, num_kpts))
324 0 : kpt_latt(1:3, 1) = 0.0_dp
325 0 : kpoint%kp_scheme = "GAMMA"
326 0 : kpoint%symmetry = .FALSE.
327 0 : kpoint%verbose = .FALSE.
328 0 : kpoint%full_grid = .TRUE.
329 0 : kpoint%eps_geo = 1.0e-6_dp
330 0 : kpoint%use_real_wfn = .FALSE.
331 0 : kpoint%parallel_group_size = para_env%num_pe
332 0 : kpoint%nkp = num_kpts
333 0 : ALLOCATE (kpoint%xkp(3, num_kpts), kpoint%wkp(num_kpts))
334 0 : kpoint%xkp(1:3, 1) = 0.0_dp
335 0 : kpoint%wkp(1) = 1.0_dp
336 :
337 : CASE ("MONKHORST-PACK", "MACDONALD")
338 26 : mp_grid(1:3) = input_nkp_grid(1:3)
339 26 : kpoint%kp_scheme = input_kp_scheme
340 26 : kpoint%symmetry = .FALSE.
341 104 : kpoint%nkp_grid(1:3) = input_nkp_grid(1:3)
342 104 : kpoint%kp_shift(1:3) = input_kp_shift(1:3)
343 26 : kpoint%gamma_centered = input_gamma_centered
344 26 : kpoint%verbose = .FALSE.
345 26 : kpoint%full_grid = .TRUE.
346 26 : kpoint%eps_geo = 1.0e-6_dp
347 26 : kpoint%use_real_wfn = .FALSE.
348 26 : kpoint%parallel_group_size = para_env%num_pe
349 26 : CALL kpoint_initialize(kpoint, particle_set, cell)
350 26 : num_kpts = kpoint%nkp
351 78 : ALLOCATE (kpt_latt(3, num_kpts))
352 1754 : kpt_latt(1:3, 1:num_kpts) = kpoint%xkp(1:3, 1:num_kpts)
353 26 : IF (input_kpoint_symmetry .AND. .NOT. input_full_grid .AND. iw > 0) THEN
354 : WRITE (iw, '(T2,A)') &
355 12 : "WANNIER90| SCF k-points are symmetry-reduced; regenerating the full SCF mesh."
356 12 : IF (reuse_scf_mos) THEN
357 : WRITE (iw, '(T2,A)') &
358 12 : "WANNIER90| CP2K will try to reconstruct the full-mesh MOs from the SCF orbitals."
359 : ELSE
360 : WRITE (iw, '(T2,A)') &
361 0 : "WANNIER90| The full exported mesh is diagonalized for the Wannier90 files."
362 : END IF
363 : END IF
364 :
365 : CASE ("GENERAL")
366 6 : IF (ASSOCIATED(qs_kpoint%xkp_input)) THEN
367 6 : xkp_source => qs_kpoint%xkp_input
368 6 : wkp_source => qs_kpoint%wkp_input
369 : ELSE
370 0 : xkp_source => xkp
371 0 : wkp_source => wkp
372 : END IF
373 6 : IF (.NOT. ASSOCIATED(xkp_source) .OR. .NOT. ASSOCIATED(wkp_source)) THEN
374 0 : CPABORT("Could not access the SCF GENERAL k-point set for the Wannier90 export.")
375 : END IF
376 6 : num_kpts = SIZE(wkp_source)
377 18 : ALLOCATE (kpt_latt(3, num_kpts))
378 198 : kpt_latt(1:3, 1:num_kpts) = xkp_source(1:3, 1:num_kpts)
379 6 : IF (mp_grid_explicit) THEN
380 0 : IF (mp_grid(1)*mp_grid(2)*mp_grid(3) /= num_kpts) THEN
381 0 : CPABORT("WANNIER90%MP_GRID must contain exactly as many points as the SCF GENERAL mesh.")
382 : END IF
383 : ELSE
384 6 : CALL infer_wannier_mp_grid(kpt_latt, mp_grid, mp_grid_valid)
385 6 : IF (.NOT. mp_grid_valid) THEN
386 0 : CPABORT("Could not infer WANNIER90%MP_GRID from the SCF GENERAL mesh.")
387 : END IF
388 : END IF
389 6 : wkp_ref = 1.0_dp/REAL(num_kpts, KIND=dp)
390 54 : DO i = 1, num_kpts
391 54 : IF (ABS(wkp_source(i) - wkp_ref) > 1.0e-10_dp) THEN
392 0 : CPABORT("WANNIER90%KPOINTS_SOURCE SCF requires equally weighted GENERAL k-points.")
393 : END IF
394 : END DO
395 6 : kpoint%kp_scheme = "GENERAL"
396 6 : kpoint%symmetry = .FALSE.
397 24 : kpoint%nkp_grid(1:3) = mp_grid(1:3)
398 6 : kpoint%verbose = .FALSE.
399 6 : kpoint%full_grid = .TRUE.
400 6 : kpoint%eps_geo = 1.0e-6_dp
401 6 : kpoint%use_real_wfn = .FALSE.
402 6 : kpoint%parallel_group_size = para_env%num_pe
403 6 : kpoint%nkp = num_kpts
404 30 : ALLOCATE (kpoint%xkp(3, num_kpts), kpoint%wkp(num_kpts))
405 390 : kpoint%xkp(1:3, 1:num_kpts) = xkp_source(1:3, 1:num_kpts)
406 54 : kpoint%wkp(1:num_kpts) = wkp_ref
407 6 : IF (input_kpoint_symmetry .AND. .NOT. input_full_grid .AND. iw > 0) THEN
408 : WRITE (iw, '(T2,A)') &
409 2 : "WANNIER90| SCF k-points are symmetry-reduced; using the full input GENERAL mesh."
410 2 : IF (reuse_scf_mos) THEN
411 : WRITE (iw, '(T2,A)') &
412 2 : "WANNIER90| CP2K will try to reconstruct the full-mesh MOs from the SCF orbitals."
413 : ELSE
414 : WRITE (iw, '(T2,A)') &
415 0 : "WANNIER90| The full exported mesh is diagonalized for the Wannier90 files."
416 : END IF
417 : END IF
418 :
419 : CASE DEFAULT
420 32 : CPABORT("WANNIER90%KPOINTS_SOURCE SCF does not support this DFT%KPOINTS scheme.")
421 : END SELECT
422 : CASE DEFAULT
423 32 : CPABORT("Unknown WANNIER90%KPOINTS_SOURCE setting.")
424 : END SELECT
425 : ! number of bands in calculation
426 32 : CALL get_qs_env(qs_env, mos=mos)
427 32 : CALL get_mo_set(mo_set=mos(1), nao=nao, nmo=num_bands_tot)
428 32 : num_bands_tot = MIN(nao, num_bands_tot + nadd)
429 32 : num_bands = num_bands_tot
430 32 : IF (use_bloch_phases .AND. num_wann /= num_bands) THEN
431 0 : CPABORT("WANNIER90%USE_BLOCH_PHASES requires WANNIER_FUNCTIONS to match the number of bands.")
432 : END IF
433 32 : num_atoms = SIZE(particle_set)
434 96 : ALLOCATE (atoms_cart(3, num_atoms))
435 96 : ALLOCATE (atom_symbols(num_atoms))
436 222 : DO i = 1, num_atoms
437 760 : atoms_cart(1:3, i) = particle_set(i)%r(1:3)
438 190 : CALL get_atomic_kind(particle_set(i)%atomic_kind, element_symbol=asym)
439 222 : atom_symbols(i) = asym
440 : END DO
441 32 : gamma_only = .FALSE.
442 32 : spinors = .FALSE.
443 : ! output
444 96 : ALLOCATE (nnlist(num_kpts, num_nnmax))
445 128 : ALLOCATE (nncell(3, num_kpts, num_nnmax))
446 32 : nnlist(:, :) = 0
447 32 : nncell(:, :, :) = 0
448 32 : nntot = 0
449 :
450 32 : IF (iw > 0) THEN
451 : ! setup
452 : CALL wannier_setup(mp_grid, num_kpts, real_lattice, recip_lattice, &
453 16 : kpt_latt, nntot, nnlist, nncell, iw)
454 : END IF
455 :
456 32 : CALL get_qs_env(qs_env, para_env=para_env)
457 32 : CALL para_env%sum(nntot)
458 32 : CALL para_env%sum(nnlist)
459 32 : CALL para_env%sum(nncell)
460 :
461 32 : IF (para_env%is_source()) THEN
462 : ! Write the Wannier90 input file "seed_name.win"
463 16 : WRITE (filename, '(A,A)') TRIM(seed_name), ".win"
464 16 : CALL open_file(filename, unit_number=iunit, file_status="UNKNOWN", file_action="WRITE")
465 : !
466 16 : CALL m_timestamp(timestamp)
467 16 : WRITE (iunit, "(A)") "! Wannier90 input file generated by CP2K "
468 16 : WRITE (iunit, "(A,/)") "! Creation date "//timestamp
469 : !
470 16 : WRITE (iunit, "(A,I5)") "num_wann = ", num_wann
471 16 : IF (num_bands /= num_wann .OR. use_bloch_phases) THEN
472 14 : WRITE (iunit, "(A,I5)") "num_bands = ", num_bands
473 : END IF
474 16 : IF (use_bloch_phases) THEN
475 : ! Keep the external Wannier90 projection matrix fully defined for
476 : ! complete-band Bloch-phase subspaces by writing explicit identity projections.
477 6 : WRITE (iunit, "(A)") "! CP2K writes identity projections for Bloch-phase complete subspaces."
478 : END IF
479 16 : WRITE (iunit, "(/,A,/)") "length_unit = bohr "
480 16 : WRITE (iunit, "(/,A,/)") "! System"
481 16 : WRITE (iunit, "(/,A)") "begin unit_cell_cart"
482 16 : WRITE (iunit, "(A)") "bohr"
483 64 : DO i = 1, 3
484 208 : WRITE (iunit, "(3F12.6)") cell%hmat(i, 1:3)
485 : END DO
486 16 : WRITE (iunit, "(A,/)") "end unit_cell_cart"
487 16 : WRITE (iunit, "(/,A)") "begin atoms_cart"
488 16 : WRITE (iunit, "(A)") "bohr"
489 111 : DO i = 1, num_atoms
490 111 : WRITE (iunit, "(A,3F15.10)") atom_symbols(i), atoms_cart(1:3, i)
491 : END DO
492 16 : WRITE (iunit, "(A,/)") "end atoms_cart"
493 16 : WRITE (iunit, "(/,A,/)") "! Kpoints"
494 16 : WRITE (iunit, "(/,A,3I6/)") "mp_grid = ", mp_grid(1:3)
495 16 : WRITE (iunit, "(A)") "begin kpoints"
496 256 : DO i = 1, num_kpts
497 256 : WRITE (iunit, "(3F12.6)") kpt_latt(1:3, i)
498 : END DO
499 16 : WRITE (iunit, "(A)") "end kpoints"
500 16 : CALL close_file(iunit)
501 16 : IF (use_bloch_phases) THEN
502 6 : WRITE (filename, '(A,A)') TRIM(seed_name), ".amn"
503 6 : CALL open_file(filename, unit_number=iunit, file_status="UNKNOWN", file_action="WRITE")
504 6 : WRITE (iunit, "(A)") "! Wannier90 identity projections generated by CP2K"
505 6 : WRITE (iunit, "(3I8)") num_bands, num_kpts, num_wann
506 166 : DO ik = 1, num_kpts
507 742 : DO ib2 = 1, num_wann
508 2960 : DO ib1 = 1, num_bands
509 2800 : IF (ib1 == ib2) THEN
510 576 : WRITE (iunit, "(3I8,2E30.14)") ib1, ib2, ik, 1.0_dp, 0.0_dp
511 : ELSE
512 1648 : WRITE (iunit, "(3I8,2E30.14)") ib1, ib2, ik, 0.0_dp, 0.0_dp
513 : END IF
514 : END DO
515 : END DO
516 : END DO
517 6 : CALL close_file(iunit)
518 : END IF
519 : ELSE
520 16 : iunit = -1
521 : END IF
522 :
523 : ! calculate bands
524 32 : NULLIFY (qs_env_kp)
525 32 : IF (kpoints_source == w90_kpoints_mp_grid .AND. input_kpoint_symmetry .AND. iw > 0) THEN
526 : WRITE (iw, '(T2,A)') &
527 0 : "WANNIER90| Atomic k-point symmetry from the SCF calculation is not reused."
528 : WRITE (iw, '(T2,A)') &
529 0 : "WANNIER90| A full Monkhorst-Pack grid is generated for the Wannier90 interface."
530 : END IF
531 32 : IF (do_kpoints) THEN
532 : ! we already do kpoints
533 32 : qs_env_kp => qs_env
534 : ELSE
535 : ! we start from gamma point only
536 0 : ALLOCATE (qs_env_kp)
537 0 : CALL create_kp_from_gamma(qs_env, qs_env_kp)
538 : END IF
539 32 : IF (iw > 0) THEN
540 16 : WRITE (unit=iw, FMT="(/,T2,A)") "Start K-Point Calculation ..."
541 : END IF
542 32 : CALL get_qs_env(qs_env=qs_env_kp, para_env=para_env, blacs_env=blacs_env)
543 32 : CALL kpoint_env_initialize(kpoint, para_env, blacs_env)
544 32 : CALL kpoint_initialize_mos(kpoint, mos, nadd)
545 32 : CALL kpoint_initialize_mo_set(kpoint)
546 : !
547 32 : CALL get_qs_env(qs_env=qs_env_kp, sab_orb=sab_nl, dft_control=dft_control)
548 32 : CALL kpoint_init_cell_index(kpoint, sab_nl, para_env, dft_control%nimages)
549 : !
550 : CALL get_qs_env(qs_env=qs_env_kp, matrix_ks_kp=matrix_ks, matrix_s_kp=matrix_s, &
551 32 : scf_env=scf_env, scf_control=scf_control)
552 32 : full_mesh_diagonalized = .FALSE.
553 32 : reused_scf_mos = .FALSE.
554 32 : reuse_reason = ""
555 32 : aligned_degenerate_blocks = 0
556 32 : aligned_degenerate_max_size = 0
557 32 : aligned_degenerate_min_svalue = 0.0_dp
558 32 : IF (reuse_scf_mos) THEN
559 32 : CALL get_kpoint_info(kpoint=kpoint, cell_to_index=cell_to_index)
560 : CALL do_general_diag_kp(matrix_ks, matrix_s, qs_kpoint, scf_env, scf_control, .FALSE., &
561 32 : diis_step)
562 32 : IF (validate_reuse_scf_mos) THEN
563 6 : IF (iw > 0) THEN
564 : WRITE (iw, '(T2,A)') &
565 3 : "WANNIER90| Validating SCF MO reuse against a full-mesh diagonalization reference."
566 : END IF
567 6 : CALL do_general_diag_kp(matrix_ks, matrix_s, kpoint, scf_env, scf_control, .FALSE., diis_step)
568 6 : full_mesh_diagonalized = .TRUE.
569 6 : nspins = dft_control%nspins
570 : CALL save_wannier90_mo_snapshot(kpoint, nspins, para_env, reference_mo_real, &
571 6 : reference_mo_imag, reference_eigenvalues)
572 : CALL diagnose_wannier90_scf_reuse_candidates(kpoint, qs_kpoint, matrix_s, matrix_ks, &
573 : cell_to_index, sab_nl, para_env, iw, &
574 : reuse_candidate_deviation, &
575 : reuse_candidate_min_svalue, &
576 : reuse_candidate_metric_deviation, &
577 6 : reuse_candidate_residual)
578 6 : IF (iw > 0 .AND. reuse_candidate_deviation < 1.0e100_dp) THEN
579 : WRITE (iw, '(T2,A,ES10.3,A,ES10.3,A,ES10.3,A,ES10.3)') &
580 3 : "WANNIER90| Best atom/AO candidate subspace deviation ", &
581 3 : reuse_candidate_deviation, ", minimum singular value ", &
582 3 : reuse_candidate_min_svalue, ", max metric deviation ", &
583 6 : reuse_candidate_metric_deviation, ", max residual ", reuse_candidate_residual
584 : END IF
585 : END IF
586 : CALL prepare_wannier90_scf_mos(kpoint, qs_kpoint, matrix_s, matrix_ks, cell_to_index, &
587 : sab_nl, para_env, reused_scf_mos, reuse_reason, &
588 : aligned_degenerate_blocks, aligned_degenerate_max_size, &
589 32 : aligned_degenerate_min_svalue)
590 32 : IF (validate_reuse_scf_mos) THEN
591 6 : IF (reused_scf_mos) THEN
592 : CALL validate_wannier90_reused_mos(kpoint, matrix_s, cell_to_index, sab_nl, &
593 : para_env, reference_mo_real, reference_mo_imag, &
594 : reference_eigenvalues, validate_reuse_ok, &
595 : validation_subspace_deviation, validation_min_svalue, &
596 6 : validation_eigenvalue_deviation)
597 6 : IF (iw > 0) THEN
598 : WRITE (iw, '(T2,A,ES10.3,A,ES10.3,A,ES10.3)') &
599 3 : "WANNIER90| Reused MO validation: subspace deviation ", &
600 3 : validation_subspace_deviation, ", minimum singular value ", &
601 3 : validation_min_svalue, ", eigenvalue deviation ", &
602 6 : validation_eigenvalue_deviation
603 : END IF
604 6 : IF (.NOT. validate_reuse_ok) THEN
605 0 : reused_scf_mos = .FALSE.
606 : WRITE (reuse_reason, "(A,ES10.3,A,ES10.3)") &
607 0 : "validation failed: dS=", &
608 0 : validation_subspace_deviation, ", dE=", validation_eigenvalue_deviation
609 : END IF
610 : END IF
611 6 : IF (.NOT. reused_scf_mos) THEN
612 : CALL restore_wannier90_mo_snapshot(kpoint, reference_mo_real, reference_mo_imag, &
613 0 : reference_eigenvalues)
614 : END IF
615 : END IF
616 32 : IF (iw > 0) THEN
617 16 : IF (reused_scf_mos) THEN
618 : WRITE (iw, '(T2,A)') &
619 13 : "WANNIER90| Reused SCF MO coefficients for the Wannier90 full k-point mesh."
620 13 : IF (use_bloch_phases) THEN
621 : WRITE (iw, '(T2,A)') &
622 6 : "WANNIER90| Wrote identity projections for Bloch-phase complete band subspaces."
623 : WRITE (iw, '(T2,A,3F10.6)') &
624 6 : "WANNIER90| Applied Bloch phase gauge to reused overlaps around fractional center", &
625 12 : phase_center(1:3)
626 : END IF
627 13 : IF (aligned_degenerate_blocks > 0) THEN
628 : WRITE (iw, '(T2,A,I0,A,I0,A,ES10.3)') &
629 8 : "WANNIER90| Ritz-stabilized ", aligned_degenerate_blocks, &
630 8 : " degenerate SCF MO subspace(s) with S(k),H(k); largest block has ", &
631 8 : aligned_degenerate_max_size, " band(s), min metric eigenvalue ", &
632 16 : aligned_degenerate_min_svalue
633 : END IF
634 : ELSE
635 : WRITE (iw, '(T2,A,A)') &
636 3 : "WANNIER90| Could not reuse SCF MOs: ", TRIM(reuse_reason)
637 : WRITE (iw, '(T2,A)') &
638 3 : "WANNIER90| Falling back to full-mesh diagonalization for the Wannier90 files."
639 : END IF
640 : END IF
641 : END IF
642 32 : IF (.NOT. reused_scf_mos .AND. .NOT. full_mesh_diagonalized) THEN
643 6 : CALL do_general_diag_kp(matrix_ks, matrix_s, kpoint, scf_env, scf_control, .FALSE., diis_step)
644 : END IF
645 32 : IF (ALLOCATED(reference_mo_real)) DEALLOCATE (reference_mo_real)
646 32 : IF (ALLOCATED(reference_mo_imag)) DEALLOCATE (reference_mo_imag)
647 32 : IF (ALLOCATED(reference_eigenvalues)) DEALLOCATE (reference_eigenvalues)
648 : !
649 32 : IF (iw > 0) THEN
650 16 : WRITE (iw, '(T69,A)') "... Finished"
651 : END IF
652 : !
653 : ! Calculate and print Overlaps
654 : !
655 32 : IF (para_env%is_source()) THEN
656 16 : WRITE (filename, '(A,A)') TRIM(seed_name), ".mmn"
657 16 : CALL open_file(filename, unit_number=iunit, file_status="UNKNOWN", file_action="WRITE")
658 16 : CALL m_timestamp(timestamp)
659 16 : WRITE (iunit, "(A)") "! Wannier90 file generated by CP2K "//timestamp
660 16 : WRITE (iunit, "(3I8)") num_bands, num_kpts, nntot
661 : ELSE
662 16 : iunit = -1
663 : END IF
664 : ! create a list of unique b vectors and a table of pointers
665 : ! nblist(ik,i) -> +/- b_latt(1:3,x)
666 128 : ALLOCATE (nblist(num_kpts, nntot))
667 96 : ALLOCATE (b_latt(3, num_kpts*nntot))
668 32 : nblist(:, :) = 0
669 32 : nbs = 0
670 512 : DO ik = 1, num_kpts
671 3392 : DO i = 1, nntot
672 11520 : bvec(1:3) = kpt_latt(1:3, nnlist(ik, i)) - kpt_latt(1:3, ik) + nncell(1:3, ik, i)
673 5760 : ibs = 0
674 5760 : DO k = 1, nbs
675 22656 : IF (SUM(ABS(bvec(1:3) - b_latt(1:3, k))) < 1.e-6_dp) THEN
676 : ibs = k
677 : EXIT
678 : END IF
679 17376 : IF (SUM(ABS(bvec(1:3) + b_latt(1:3, k))) < 1.e-6_dp) THEN
680 1440 : ibs = -k
681 1440 : EXIT
682 : END IF
683 : END DO
684 3360 : IF (ibs /= 0) THEN
685 : ! old lattice vector
686 2784 : nblist(ik, i) = ibs
687 : ELSE
688 : ! new lattice vector
689 96 : nbs = nbs + 1
690 384 : b_latt(1:3, nbs) = bvec(1:3)
691 96 : nblist(ik, i) = nbs
692 : END IF
693 : END DO
694 : END DO
695 : ! calculate all the operator matrices (a|bvec|b)
696 192 : ALLOCATE (berry_matrix(nbs))
697 128 : DO i = 1, nbs
698 96 : NULLIFY (berry_matrix(i)%cosmat)
699 96 : NULLIFY (berry_matrix(i)%sinmat)
700 480 : bvec(1:3) = twopi*MATMUL(TRANSPOSE(cell%h_inv(1:3, 1:3)), b_latt(1:3, i))
701 : CALL build_berry_kpoint_matrix(qs_env_kp, berry_matrix(i)%cosmat, &
702 128 : berry_matrix(i)%sinmat, bvec)
703 : END DO
704 : ! work matrices for MOs (all group)
705 32 : kp => kpoint%kp_env(1)%kpoint_env
706 32 : CALL get_mo_set(kp%mos(1, 1), nmo=nmo)
707 32 : NULLIFY (matrix_struct_ao, matrix_struct_work)
708 : CALL cp_fm_struct_create(matrix_struct_work, nrow_global=nao, &
709 : ncol_global=nmo, &
710 : para_env=para_env, &
711 32 : context=blacs_env)
712 96 : DO i = 1, 2
713 64 : CALL cp_fm_create(fmk1(i), matrix_struct_work)
714 96 : CALL cp_fm_create(fmk2(i), matrix_struct_work)
715 : END DO
716 32 : CALL cp_cfm_create(fmk1_cfm, matrix_struct_work)
717 32 : CALL cp_cfm_create(fmk2_cfm, matrix_struct_work)
718 32 : CALL cp_cfm_create(tmp_cfm, matrix_struct_work)
719 : CALL cp_fm_struct_create(matrix_struct_ao, nrow_global=nao, &
720 : ncol_global=nao, &
721 : para_env=para_env, &
722 32 : context=blacs_env)
723 32 : CALL cp_fm_create(mat_real, matrix_struct_ao)
724 32 : CALL cp_fm_create(mat_imag, matrix_struct_ao)
725 32 : CALL cp_cfm_create(omat_cfm, matrix_struct_ao)
726 : ! work matrices for Mmn(k,b) integrals
727 32 : NULLIFY (matrix_struct_mmn)
728 : CALL cp_fm_struct_create(matrix_struct_mmn, nrow_global=nmo, &
729 : ncol_global=nmo, &
730 : para_env=para_env, &
731 32 : context=blacs_env)
732 32 : CALL cp_fm_create(mmn_real, matrix_struct_mmn)
733 32 : CALL cp_fm_create(mmn_imag, matrix_struct_mmn)
734 32 : CALL cp_cfm_create(mmn_cfm, matrix_struct_mmn)
735 : ! allocate some work matrices
736 32 : ALLOCATE (rmatrix, cmatrix, rmatrix_full, cmatrix_full)
737 : CALL dbcsr_create(rmatrix, template=matrix_s(1, 1)%matrix, &
738 32 : matrix_type=dbcsr_type_symmetric)
739 : CALL dbcsr_create(cmatrix, template=matrix_s(1, 1)%matrix, &
740 32 : matrix_type=dbcsr_type_antisymmetric)
741 : CALL dbcsr_create(rmatrix_full, template=matrix_s(1, 1)%matrix, &
742 32 : matrix_type=dbcsr_type_no_symmetry)
743 : CALL dbcsr_create(cmatrix_full, template=matrix_s(1, 1)%matrix, &
744 32 : matrix_type=dbcsr_type_no_symmetry)
745 32 : CALL cp_dbcsr_alloc_block_from_nbl(rmatrix, sab_nl)
746 32 : CALL cp_dbcsr_alloc_block_from_nbl(cmatrix, sab_nl)
747 : !
748 32 : CALL get_kpoint_info(kpoint=kpoint, cell_to_index=cell_to_index)
749 32 : NULLIFY (fmdummy)
750 32 : nspins = dft_control%nspins
751 64 : DO ispin = 1, nspins
752 : ! loop over all k-points
753 544 : DO ik = 1, num_kpts
754 : ! get the MO coefficients for this k-point
755 480 : my_kpgrp = (ik >= kpoint%kp_range(1) .AND. ik <= kpoint%kp_range(2))
756 : IF (my_kpgrp) THEN
757 480 : ikk = ik - kpoint%kp_range(1) + 1
758 480 : kp => kpoint%kp_env(ikk)%kpoint_env
759 480 : CPASSERT(SIZE(kp%mos, 1) == 2)
760 480 : fmr => kp%mos(1, ispin)%mo_coeff
761 480 : fmi => kp%mos(2, ispin)%mo_coeff
762 480 : CALL cp_fm_copy_general(fmr, fmk1(1), para_env)
763 480 : CALL cp_fm_copy_general(fmi, fmk1(2), para_env)
764 : ELSE
765 0 : NULLIFY (fmr, fmi, kp)
766 0 : CALL cp_fm_copy_general(fmdummy, fmk1(1), para_env)
767 0 : CALL cp_fm_copy_general(fmdummy, fmk1(2), para_env)
768 : END IF
769 480 : CALL cp_fm_to_cfm(fmk1(1), fmk1(2), fmk1_cfm)
770 : ! loop over all connected neighbors
771 3392 : DO i = 1, nntot
772 : ! get the MO coefficients for the connected k-point
773 2880 : ik2 = nnlist(ik, i)
774 2880 : mygrp = (ik2 >= kpoint%kp_range(1) .AND. ik2 <= kpoint%kp_range(2))
775 : IF (mygrp) THEN
776 2880 : ikk = ik2 - kpoint%kp_range(1) + 1
777 2880 : kp => kpoint%kp_env(ikk)%kpoint_env
778 2880 : CPASSERT(SIZE(kp%mos, 1) == 2)
779 2880 : fmr => kp%mos(1, ispin)%mo_coeff
780 2880 : fmi => kp%mos(2, ispin)%mo_coeff
781 2880 : CALL cp_fm_copy_general(fmr, fmk2(1), para_env)
782 2880 : CALL cp_fm_copy_general(fmi, fmk2(2), para_env)
783 : ELSE
784 0 : NULLIFY (fmr, fmi, kp)
785 0 : CALL cp_fm_copy_general(fmdummy, fmk2(1), para_env)
786 0 : CALL cp_fm_copy_general(fmdummy, fmk2(2), para_env)
787 : END IF
788 2880 : CALL cp_fm_to_cfm(fmk2(1), fmk2(2), fmk2_cfm)
789 : !
790 : ! transfer realspace overlaps to connected k-point
791 2880 : ibs = nblist(ik, i)
792 2880 : ksign = SIGN(1.0_dp, REAL(ibs, KIND=dp))
793 2880 : ibs = ABS(ibs)
794 2880 : CALL dbcsr_set(rmatrix, 0.0_dp)
795 2880 : CALL dbcsr_set(cmatrix, 0.0_dp)
796 : CALL rskp_transform(rmatrix, cmatrix, rsmat=berry_matrix(ibs)%cosmat, ispin=1, &
797 : xkp=kpoint%xkp(1:3, ik2), cell_to_index=cell_to_index, sab_nl=sab_nl, &
798 2880 : is_complex=.FALSE., rs_sign=ksign)
799 : CALL rskp_transform(cmatrix, rmatrix, rsmat=berry_matrix(ibs)%sinmat, ispin=1, &
800 : xkp=kpoint%xkp(1:3, ik2), cell_to_index=cell_to_index, sab_nl=sab_nl, &
801 2880 : is_complex=.TRUE., rs_sign=ksign)
802 : !
803 : ! calculate M_(mn)^(k,b) = C(k)^H O(k,b) C(k+b)
804 2880 : CALL dbcsr_desymmetrize(rmatrix, rmatrix_full)
805 2880 : CALL dbcsr_desymmetrize(cmatrix, cmatrix_full)
806 2880 : CALL copy_dbcsr_to_fm(rmatrix_full, mat_real)
807 2880 : CALL copy_dbcsr_to_fm(cmatrix_full, mat_imag)
808 2880 : CALL cp_fm_to_cfm(mat_real, mat_imag, omat_cfm)
809 : CALL cp_cfm_gemm("N", "N", nao, nmo, nao, CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
810 2880 : omat_cfm, fmk2_cfm, CMPLX(0.0_dp, 0.0_dp, KIND=dp), tmp_cfm)
811 : CALL cp_cfm_gemm("C", "N", nmo, nmo, nao, CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
812 2880 : fmk1_cfm, tmp_cfm, CMPLX(0.0_dp, 0.0_dp, KIND=dp), mmn_cfm)
813 2880 : CALL cp_cfm_to_fm(mmn_cfm, mmn_real, mmn_imag)
814 : !
815 : ! write to output file
816 2880 : IF (reused_scf_mos .AND. use_bloch_phases) THEN
817 : ! Reused SCF MOs need the same global Bloch gauge in every overlap block.
818 : gauge_arg = twopi*DOT_PRODUCT(kpoint%xkp(1:3, ik2) - kpoint%xkp(1:3, ik), &
819 7680 : phase_center(1:3))
820 1920 : gauge_real = COS(gauge_arg)
821 1920 : gauge_imag = SIN(gauge_arg)
822 : ELSE
823 : gauge_real = 1.0_dp
824 : gauge_imag = 0.0_dp
825 : END IF
826 2880 : IF (para_env%is_source()) THEN
827 1440 : WRITE (iunit, "(2I8,3I5)") ik, ik2, nncell(1:3, ik, i)
828 : END IF
829 13632 : DO ib2 = 1, nmo
830 52608 : DO ib1 = 1, nmo
831 39456 : CALL cp_fm_get_element(mmn_real, ib1, ib2, rmmn)
832 39456 : CALL cp_fm_get_element(mmn_imag, ib1, ib2, cmmn)
833 39456 : gauge_tmp = gauge_real*rmmn - gauge_imag*cmmn
834 39456 : cmmn = gauge_imag*rmmn + gauge_real*cmmn
835 39456 : rmmn = gauge_tmp
836 49728 : IF (para_env%is_source()) THEN
837 19728 : WRITE (iunit, "(2E30.14)") rmmn, cmmn
838 : END IF
839 : END DO
840 : END DO
841 : !
842 : END DO
843 : END DO
844 : END DO
845 128 : DO i = 1, nbs
846 96 : CALL dbcsr_deallocate_matrix_set(berry_matrix(i)%cosmat)
847 128 : CALL dbcsr_deallocate_matrix_set(berry_matrix(i)%sinmat)
848 : END DO
849 32 : DEALLOCATE (berry_matrix)
850 32 : CALL cp_fm_struct_release(matrix_struct_work)
851 96 : DO i = 1, 2
852 64 : CALL cp_fm_release(fmk1(i))
853 96 : CALL cp_fm_release(fmk2(i))
854 : END DO
855 32 : CALL cp_cfm_release(fmk1_cfm)
856 32 : CALL cp_cfm_release(fmk2_cfm)
857 32 : CALL cp_cfm_release(tmp_cfm)
858 32 : CALL cp_fm_struct_release(matrix_struct_ao)
859 32 : CALL cp_fm_release(mat_real)
860 32 : CALL cp_fm_release(mat_imag)
861 32 : CALL cp_cfm_release(omat_cfm)
862 32 : CALL cp_fm_struct_release(matrix_struct_mmn)
863 32 : CALL cp_fm_release(mmn_real)
864 32 : CALL cp_fm_release(mmn_imag)
865 32 : CALL cp_cfm_release(mmn_cfm)
866 32 : CALL dbcsr_deallocate_matrix(rmatrix)
867 32 : CALL dbcsr_deallocate_matrix(cmatrix)
868 32 : CALL dbcsr_deallocate_matrix(rmatrix_full)
869 32 : CALL dbcsr_deallocate_matrix(cmatrix_full)
870 : !
871 32 : IF (para_env%is_source()) THEN
872 16 : CALL close_file(iunit)
873 : END IF
874 : !
875 : ! Calculate and print Projections
876 : !
877 : ! Print eigenvalues
878 32 : nspins = dft_control%nspins
879 32 : kp => kpoint%kp_env(1)%kpoint_env
880 32 : CALL get_mo_set(kp%mos(1, 1), nmo=nmo)
881 96 : ALLOCATE (eigval(nmo))
882 32 : CALL get_kpoint_info(kpoint, nkp=nkp, kp_range=kp_range, xkp=xkp)
883 32 : IF (para_env%is_source()) THEN
884 16 : WRITE (filename, '(A,A)') TRIM(seed_name), ".eig"
885 16 : CALL open_file(filename, unit_number=iunit, file_status="UNKNOWN", file_action="WRITE")
886 : ELSE
887 16 : iunit = -1
888 : END IF
889 : !
890 512 : DO ik = 1, nkp
891 480 : my_kpgrp = (ik >= kp_range(1) .AND. ik <= kp_range(2))
892 992 : DO ispin = 1, nspins
893 480 : IF (my_kpgrp) THEN
894 480 : ikpgr = ik - kp_range(1) + 1
895 480 : kp => kpoint%kp_env(ikpgr)%kpoint_env
896 480 : CALL get_mo_set(kp%mos(1, ispin), eigenvalues=eigenvalues)
897 2192 : eigval(1:nmo) = eigenvalues(1:nmo)
898 : ELSE
899 0 : eigval(1:nmo) = 0.0_dp
900 : END IF
901 480 : CALL kpoint%para_env_inter_kp%sum(eigval)
902 2192 : eigval(1:nmo) = eigval(1:nmo)*evolt
903 : ! output
904 960 : IF (iunit > 0) THEN
905 1096 : DO ib = 1, nmo
906 1096 : WRITE (iunit, "(2I8,F24.14)") ib, ik, eigval(ib)
907 : END DO
908 : END IF
909 : END DO
910 : END DO
911 32 : IF (para_env%is_source()) THEN
912 16 : CALL close_file(iunit)
913 : END IF
914 : !
915 : ! clean up
916 32 : DEALLOCATE (kpt_latt, atoms_cart, atom_symbols, eigval)
917 32 : DEALLOCATE (nnlist, nncell)
918 32 : DEALLOCATE (nblist, b_latt)
919 32 : IF (nexcl > 0) THEN
920 0 : DEALLOCATE (exclude_bands)
921 : END IF
922 32 : IF (do_kpoints) THEN
923 32 : NULLIFY (qs_env_kp)
924 : ELSE
925 0 : CALL qs_env_release(qs_env_kp)
926 0 : DEALLOCATE (qs_env_kp)
927 : NULLIFY (qs_env_kp)
928 : END IF
929 :
930 32 : CALL kpoint_release(kpoint)
931 :
932 352 : END SUBROUTINE wannier90_files
933 :
934 : ! **************************************************************************************************
935 : !> \brief Reconstruct a full Wannier90 k-point MO set from the SCF k-point MOs.
936 : !> \param kpoint full Wannier90 export k-point object
937 : !> \param qs_kpoint SCF k-point object
938 : !> \param matrix_s real-space overlap matrix
939 : !> \param matrix_ks real-space Kohn-Sham matrix
940 : !> \param cell_to_index real-space cell index table
941 : !> \param sab_nl overlap neighbor list
942 : !> \param para_env global parallel environment
943 : !> \param success true if all full-mesh MOs were reconstructed
944 : !> \param reason diagnostic message when reconstruction is not possible
945 : !> \param aligned_degenerate_blocks number of aligned degenerate MO blocks
946 : !> \param aligned_degenerate_max_size largest aligned degenerate MO block
947 : !> \param aligned_degenerate_min_svalue smallest S(k)-metric subspace singular value
948 : ! **************************************************************************************************
949 36 : SUBROUTINE prepare_wannier90_scf_mos(kpoint, qs_kpoint, matrix_s, matrix_ks, cell_to_index, &
950 : sab_nl, para_env, success, reason, aligned_degenerate_blocks, &
951 : aligned_degenerate_max_size, &
952 : aligned_degenerate_min_svalue)
953 : TYPE(kpoint_type), POINTER :: kpoint, qs_kpoint
954 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
955 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
956 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
957 : POINTER :: sab_nl
958 : TYPE(mp_para_env_type), POINTER :: para_env
959 : LOGICAL, INTENT(OUT) :: success
960 : CHARACTER(LEN=*), INTENT(OUT) :: reason
961 : INTEGER, INTENT(OUT) :: aligned_degenerate_blocks, &
962 : aligned_degenerate_max_size
963 : REAL(KIND=dp), INTENT(OUT) :: aligned_degenerate_min_svalue
964 :
965 : CHARACTER(LEN=default_string_length) :: best_reason, candidate_reason
966 : INTEGER :: aligned_blocks, aligned_max_size, candidate_aligned_blocks, &
967 : candidate_aligned_max_size, ik, ikpgr, ikred, ispin, isym_try, min_gap_band, &
968 : min_gap_kpoint, min_gap_spin, nao, nao_src, nmo, nmo_src, nspins, nsymmetry, &
969 : num_candidates
970 36 : INTEGER, ALLOCATABLE, DIMENSION(:) :: source_kpoint, sym_index
971 : INTEGER, DIMENSION(2) :: kp_range, source_kp_range
972 : LOGICAL :: my_kpgrp, my_source_kpgrp, ok, &
973 : source_window
974 : REAL(KIND=dp) :: aligned_min_svalue, band_gap, best_residual, candidate_residual, &
975 : degenerate_band_tol, local_min_band_gap, min_band_gap, source_owner_count, &
976 : source_window_min_svalue
977 36 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigenvalues_buffer, occupation_buffer, &
978 36 : source_eigenvalues_buffer, &
979 36 : source_occupation_buffer
980 36 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues, occupation
981 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
982 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_source, matrix_struct_work
983 : TYPE(cp_fm_type) :: dst_imag, dst_imag_full, dst_real, &
984 : dst_real_full, src_imag, &
985 : src_imag_full, src_real, src_real_full
986 : TYPE(cp_fm_type), POINTER :: dst_fmi, dst_fmr, src_fmi, src_fmr
987 : TYPE(kpoint_env_type), POINTER :: kp, kp_source
988 : TYPE(kpoint_sym_type), POINTER :: kpsym
989 :
990 36 : success = .FALSE.
991 36 : reason = ""
992 36 : aligned_degenerate_blocks = 0
993 36 : aligned_degenerate_max_size = 0
994 36 : aligned_degenerate_min_svalue = HUGE(1.0_dp)
995 36 : NULLIFY (matrix_struct_source, matrix_struct_work, src_fmr, src_fmi, dst_fmr, dst_fmi)
996 :
997 36 : IF (.NOT. ASSOCIATED(kpoint)) THEN
998 0 : reason = "internal Wannier90 k-point object is not available"
999 0 : RETURN
1000 : END IF
1001 36 : IF (.NOT. ASSOCIATED(qs_kpoint)) THEN
1002 0 : reason = "SCF k-point object is not available"
1003 0 : RETURN
1004 : END IF
1005 36 : IF (.NOT. ASSOCIATED(kpoint%kp_env) .OR. .NOT. ASSOCIATED(qs_kpoint%kp_env)) THEN
1006 0 : reason = "k-point MO environments are not initialized"
1007 0 : RETURN
1008 : END IF
1009 36 : IF (.NOT. ASSOCIATED(kpoint%blacs_env)) THEN
1010 0 : reason = "Wannier90 k-point BLACS environment is not initialized"
1011 0 : RETURN
1012 : END IF
1013 :
1014 36 : CALL build_wannier90_scf_mapping(kpoint, qs_kpoint, source_kpoint, sym_index, ok, reason)
1015 36 : IF (.NOT. ok) RETURN
1016 536 : nsymmetry = COUNT(sym_index > 0)
1017 :
1018 36 : kp => kpoint%kp_env(1)%kpoint_env
1019 36 : nspins = SIZE(kp%mos, 2)
1020 36 : IF (SIZE(kp%mos, 1) < 2) THEN
1021 0 : reason = "Wannier90 export k-point MOs are not complex-valued"
1022 0 : DEALLOCATE (source_kpoint, sym_index)
1023 0 : RETURN
1024 : END IF
1025 36 : CALL get_mo_set(kp%mos(1, 1), nao=nao, nmo=nmo)
1026 :
1027 36 : kp_source => qs_kpoint%kp_env(1)%kpoint_env
1028 36 : IF (SIZE(kp_source%mos, 1) < 2) THEN
1029 0 : reason = "SCF MOs are real-valued; complex symmetry phases cannot be reconstructed"
1030 0 : DEALLOCATE (source_kpoint, sym_index)
1031 0 : RETURN
1032 : END IF
1033 36 : CALL get_mo_set(kp_source%mos(1, 1), nao=nao_src, nmo=nmo_src)
1034 36 : CALL para_env%max(nao_src)
1035 36 : CALL para_env%max(nmo_src)
1036 36 : IF (nao_src /= nao) THEN
1037 0 : reason = "SCF and Wannier90 MO bases have different AO dimensions"
1038 0 : DEALLOCATE (source_kpoint, sym_index)
1039 0 : RETURN
1040 : END IF
1041 36 : IF (nmo_src < nmo) THEN
1042 0 : reason = "SCF MO set has fewer bands than the Wannier90 export"
1043 0 : DEALLOCATE (source_kpoint, sym_index)
1044 0 : RETURN
1045 : END IF
1046 36 : source_window = nmo_src > nmo
1047 36 : degenerate_band_tol = 1.0e-8_dp
1048 36 : CALL get_kpoint_info(qs_kpoint, kp_range=source_kp_range)
1049 36 : IF (source_kp_range(1) /= 1 .OR. source_kp_range(2) /= qs_kpoint%nkp) THEN
1050 6 : reason = "SCF k-point symmetry data are distributed over k-point parallel groups"
1051 6 : DEALLOCATE (source_kpoint, sym_index)
1052 6 : RETURN
1053 : END IF
1054 : ! Positive symmetry entries require atom/AO rotations and Bloch phases. Degenerate subspaces
1055 : ! fully contained in the exported band window are aligned below; only guard when the Wannier90
1056 : ! window cuts through a degenerate SCF manifold at the upper band edge.
1057 30 : IF (nsymmetry > 0 .AND. nmo_src > nmo) THEN
1058 0 : local_min_band_gap = HUGE(1.0_dp)
1059 0 : min_gap_band = nmo
1060 0 : min_gap_kpoint = 0
1061 0 : min_gap_spin = 0
1062 0 : DO ikred = source_kp_range(1), source_kp_range(2)
1063 0 : ikpgr = ikred - source_kp_range(1) + 1
1064 0 : kp_source => qs_kpoint%kp_env(ikpgr)%kpoint_env
1065 0 : DO ispin = 1, nspins
1066 0 : CALL get_mo_set(kp_source%mos(1, ispin), eigenvalues=eigenvalues)
1067 0 : band_gap = ABS(eigenvalues(nmo + 1) - eigenvalues(nmo))
1068 0 : IF (band_gap < local_min_band_gap) THEN
1069 0 : local_min_band_gap = band_gap
1070 0 : min_gap_band = nmo
1071 0 : min_gap_kpoint = ikred
1072 0 : min_gap_spin = ispin
1073 : END IF
1074 : END DO
1075 : END DO
1076 0 : min_band_gap = local_min_band_gap
1077 0 : CALL para_env%min(min_band_gap)
1078 0 : IF (ABS(local_min_band_gap - min_band_gap) > degenerate_band_tol*EPSILON(1.0_dp)) THEN
1079 0 : min_gap_kpoint = 0
1080 0 : min_gap_spin = 0
1081 : END IF
1082 0 : CALL para_env%max(min_gap_kpoint)
1083 0 : CALL para_env%max(min_gap_spin)
1084 0 : CALL para_env%max(min_gap_band)
1085 0 : IF (min_band_gap < degenerate_band_tol) THEN
1086 : WRITE (reason, "(A,ES9.2,A,I0,A,I0,A,I0)") &
1087 0 : "degenerate atom/AO W90 reuse guarded: edge gap=", min_band_gap, ", k=", &
1088 0 : min_gap_kpoint, ", s=", min_gap_spin, ", nband=", min_gap_band
1089 0 : DEALLOCATE (source_kpoint, sym_index)
1090 0 : RETURN
1091 : END IF
1092 : END IF
1093 30 : blacs_env => kpoint%blacs_env
1094 : CALL cp_fm_struct_create(matrix_struct_work, nrow_global=nao, ncol_global=nmo, &
1095 30 : para_env=para_env, context=blacs_env)
1096 30 : CALL cp_fm_create(src_real, matrix_struct_work)
1097 30 : CALL cp_fm_create(src_imag, matrix_struct_work)
1098 30 : CALL cp_fm_create(dst_real, matrix_struct_work)
1099 30 : CALL cp_fm_create(dst_imag, matrix_struct_work)
1100 30 : IF (source_window) THEN
1101 : CALL cp_fm_struct_create(matrix_struct_source, nrow_global=nao, ncol_global=nmo_src, &
1102 0 : para_env=para_env, context=blacs_env)
1103 0 : CALL cp_fm_create(src_real_full, matrix_struct_source)
1104 0 : CALL cp_fm_create(src_imag_full, matrix_struct_source)
1105 0 : CALL cp_fm_create(dst_real_full, matrix_struct_source)
1106 0 : CALL cp_fm_create(dst_imag_full, matrix_struct_source)
1107 : END IF
1108 0 : ALLOCATE (eigenvalues_buffer(nmo), occupation_buffer(nmo), &
1109 210 : source_eigenvalues_buffer(nmo_src), source_occupation_buffer(nmo_src))
1110 :
1111 30 : CALL get_kpoint_info(kpoint, kp_range=kp_range)
1112 30 : CALL get_kpoint_info(qs_kpoint, kp_range=source_kp_range)
1113 :
1114 482 : DO ik = 1, kpoint%nkp
1115 452 : ikred = source_kpoint(ik)
1116 452 : my_kpgrp = (ik >= kp_range(1) .AND. ik <= kp_range(2))
1117 452 : my_source_kpgrp = (ikred >= source_kp_range(1) .AND. ikred <= source_kp_range(2))
1118 934 : DO ispin = 1, nspins
1119 2244 : source_eigenvalues_buffer(1:nmo_src) = 0.0_dp
1120 2244 : source_occupation_buffer(1:nmo_src) = 0.0_dp
1121 452 : IF (my_source_kpgrp) THEN
1122 452 : ikpgr = ikred - source_kp_range(1) + 1
1123 452 : kp_source => qs_kpoint%kp_env(ikpgr)%kpoint_env
1124 452 : src_fmr => kp_source%mos(1, ispin)%mo_coeff
1125 452 : src_fmi => kp_source%mos(2, ispin)%mo_coeff
1126 : CALL get_mo_set(kp_source%mos(1, ispin), eigenvalues=eigenvalues, &
1127 452 : occupation_numbers=occupation)
1128 2244 : source_eigenvalues_buffer(1:nmo_src) = eigenvalues(1:nmo_src)
1129 2244 : source_occupation_buffer(1:nmo_src) = occupation(1:nmo_src)
1130 : ELSE
1131 : NULLIFY (src_fmr, src_fmi)
1132 : END IF
1133 : IF (my_source_kpgrp) THEN
1134 452 : source_owner_count = 1.0_dp
1135 : ELSE
1136 0 : source_owner_count = 0.0_dp
1137 : END IF
1138 452 : CALL para_env%sum(source_owner_count)
1139 452 : CALL para_env%sum(source_eigenvalues_buffer)
1140 452 : CALL para_env%sum(source_occupation_buffer)
1141 452 : IF (source_owner_count > 0.0_dp) THEN
1142 : source_eigenvalues_buffer(1:nmo_src) = &
1143 2244 : source_eigenvalues_buffer(1:nmo_src)/source_owner_count
1144 : source_occupation_buffer(1:nmo_src) = &
1145 2244 : source_occupation_buffer(1:nmo_src)/source_owner_count
1146 : END IF
1147 2244 : eigenvalues_buffer(1:nmo) = source_eigenvalues_buffer(1:nmo)
1148 2244 : occupation_buffer(1:nmo) = source_occupation_buffer(1:nmo)
1149 452 : IF (source_window) THEN
1150 0 : CALL cp_fm_copy_general(src_fmr, src_real_full, para_env)
1151 0 : CALL cp_fm_copy_general(src_fmi, src_imag_full, para_env)
1152 0 : CALL copy_wannier90_mo_window(src_real_full, src_real, nmo)
1153 0 : CALL copy_wannier90_mo_window(src_imag_full, src_imag, nmo)
1154 : ELSE
1155 452 : CALL cp_fm_copy_general(src_fmr, src_real, para_env)
1156 452 : CALL cp_fm_copy_general(src_fmi, src_imag, para_env)
1157 : END IF
1158 :
1159 452 : ok = .FALSE.
1160 452 : reason = ""
1161 452 : aligned_blocks = 0
1162 452 : aligned_max_size = 0
1163 452 : aligned_min_svalue = 0.0_dp
1164 452 : IF (sym_index(ik) > 0) THEN
1165 368 : kpsym => qs_kpoint%kp_sym(ikred)%kpoint_sym
1166 368 : IF (ASSOCIATED(kpsym)) THEN
1167 368 : best_reason = ""
1168 368 : best_residual = HUGE(1.0_dp)
1169 368 : num_candidates = 0
1170 : ! Little-group operations can reach the same target k-point; keep the first valid eigenspace.
1171 3064 : DO isym_try = 1, kpsym%nwred
1172 3064 : IF (.NOT. kpoint_same_periodic(kpoint%xkp(1:3, ik), &
1173 : kpsym%xkp(1:3, isym_try))) CYCLE
1174 368 : num_candidates = num_candidates + 1
1175 : CALL kpoint_transform_scf_mo(src_real, src_imag, dst_real, dst_imag, &
1176 : qs_kpoint, ikred, isym_try, para_env, ok, &
1177 368 : candidate_reason)
1178 368 : IF (.NOT. ok) THEN
1179 0 : reason = candidate_reason
1180 : CYCLE
1181 : END IF
1182 : CALL ritz_stabilize_wannier90_subspace(dst_real, dst_imag, matrix_s, matrix_ks, &
1183 : kpoint%xkp(1:3, ik), cell_to_index, &
1184 : sab_nl, ispin, eigenvalues_buffer, &
1185 : degenerate_band_tol, ok, candidate_reason, &
1186 : candidate_aligned_blocks, &
1187 : candidate_aligned_max_size, &
1188 368 : aligned_min_svalue, candidate_residual)
1189 368 : IF (candidate_residual < best_residual) THEN
1190 368 : best_residual = candidate_residual
1191 368 : best_reason = candidate_reason
1192 : END IF
1193 368 : IF (.NOT. ok) THEN
1194 0 : IF (source_window) THEN
1195 : CALL kpoint_transform_scf_mo(src_real_full, src_imag_full, &
1196 : dst_real_full, dst_imag_full, qs_kpoint, &
1197 0 : ikred, isym_try, para_env, ok, candidate_reason)
1198 0 : IF (ok) THEN
1199 : CALL ritz_reconstruct_wannier90_window(dst_real_full, dst_imag_full, &
1200 : dst_real, dst_imag, matrix_s, &
1201 : matrix_ks, kpoint%xkp(1:3, ik), &
1202 : cell_to_index, sab_nl, ispin, &
1203 : eigenvalues_buffer, nmo, ok, &
1204 : candidate_reason, source_window_min_svalue, &
1205 0 : candidate_residual)
1206 0 : IF (candidate_residual < best_residual) THEN
1207 0 : best_residual = candidate_residual
1208 0 : best_reason = candidate_reason
1209 : END IF
1210 : END IF
1211 : ELSE
1212 : CALL ritz_reconstruct_wannier90_window(dst_real, dst_imag, dst_real, &
1213 : dst_imag, matrix_s, matrix_ks, &
1214 : kpoint%xkp(1:3, ik), cell_to_index, &
1215 : sab_nl, ispin, eigenvalues_buffer, nmo, &
1216 : ok, candidate_reason, source_window_min_svalue, &
1217 0 : candidate_residual)
1218 0 : IF (candidate_residual < best_residual) THEN
1219 0 : best_residual = candidate_residual
1220 0 : best_reason = candidate_reason
1221 : END IF
1222 : END IF
1223 : END IF
1224 368 : IF (ok) THEN
1225 368 : aligned_blocks = candidate_aligned_blocks
1226 368 : aligned_max_size = candidate_aligned_max_size
1227 368 : sym_index(ik) = isym_try
1228 368 : EXIT
1229 : END IF
1230 368 : reason = candidate_reason
1231 : END DO
1232 368 : IF (.NOT. ok .AND. num_candidates > 0 .AND. best_residual < HUGE(1.0_dp)) THEN
1233 : WRITE (reason, "(A,I0,A,ES9.2,A,I0,A,A32)") &
1234 0 : "atom/AO W90 guarded: best/", num_candidates, "=", best_residual, &
1235 0 : " k=", ik, " ", TRIM(best_reason)
1236 368 : ELSE IF (.NOT. ok .AND. num_candidates == 0) THEN
1237 0 : reason = "no matching SCF symmetry operation candidate"
1238 : END IF
1239 : ELSE
1240 0 : reason = "SCF k-point symmetry operation is not available"
1241 : END IF
1242 : ELSE
1243 : CALL kpoint_transform_scf_mo(src_real, src_imag, dst_real, dst_imag, qs_kpoint, &
1244 84 : ikred, sym_index(ik), para_env, ok, reason)
1245 : END IF
1246 452 : IF (ok .AND. sym_index(ik) <= 0) THEN
1247 : ! Even a direct k-point copy must be a closed H(k),S(k) subspace. This catches
1248 : ! incomplete degenerate band windows before they can be exported to Wannier90.
1249 : CALL ritz_stabilize_wannier90_subspace(dst_real, dst_imag, matrix_s, matrix_ks, &
1250 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, &
1251 : ispin, eigenvalues_buffer, degenerate_band_tol, &
1252 : ok, reason, aligned_blocks, aligned_max_size, &
1253 84 : aligned_min_svalue, candidate_residual)
1254 84 : IF (.NOT. ok .AND. source_window) THEN
1255 : CALL kpoint_transform_scf_mo(src_real_full, src_imag_full, dst_real_full, &
1256 : dst_imag_full, qs_kpoint, ikred, sym_index(ik), &
1257 0 : para_env, ok, reason)
1258 0 : IF (ok) THEN
1259 : CALL ritz_reconstruct_wannier90_window(dst_real_full, dst_imag_full, dst_real, &
1260 : dst_imag, matrix_s, matrix_ks, &
1261 : kpoint%xkp(1:3, ik), cell_to_index, &
1262 : sab_nl, ispin, eigenvalues_buffer, nmo, ok, &
1263 : reason, source_window_min_svalue, &
1264 0 : candidate_residual)
1265 : END IF
1266 84 : ELSE IF (.NOT. ok) THEN
1267 : CALL ritz_reconstruct_wannier90_window(dst_real, dst_imag, dst_real, dst_imag, &
1268 : matrix_s, matrix_ks, kpoint%xkp(1:3, ik), &
1269 : cell_to_index, sab_nl, ispin, &
1270 : eigenvalues_buffer, nmo, ok, reason, &
1271 0 : source_window_min_svalue, candidate_residual)
1272 : END IF
1273 : END IF
1274 452 : IF (.NOT. ok) THEN
1275 0 : CALL cp_fm_release(src_real)
1276 0 : CALL cp_fm_release(src_imag)
1277 0 : CALL cp_fm_release(dst_real)
1278 0 : CALL cp_fm_release(dst_imag)
1279 0 : CALL cp_fm_struct_release(matrix_struct_work)
1280 0 : IF (source_window) THEN
1281 0 : CALL cp_fm_release(src_real_full)
1282 0 : CALL cp_fm_release(src_imag_full)
1283 0 : CALL cp_fm_release(dst_real_full)
1284 0 : CALL cp_fm_release(dst_imag_full)
1285 0 : CALL cp_fm_struct_release(matrix_struct_source)
1286 : END IF
1287 0 : DEALLOCATE (source_kpoint, sym_index, eigenvalues_buffer, occupation_buffer, &
1288 0 : source_eigenvalues_buffer, source_occupation_buffer)
1289 0 : RETURN
1290 : END IF
1291 452 : IF (sym_index(ik) /= 0) THEN
1292 410 : aligned_degenerate_blocks = aligned_degenerate_blocks + aligned_blocks
1293 410 : aligned_degenerate_max_size = MAX(aligned_degenerate_max_size, aligned_max_size)
1294 410 : IF (aligned_blocks > 0) THEN
1295 338 : aligned_degenerate_min_svalue = MIN(aligned_degenerate_min_svalue, aligned_min_svalue)
1296 : END IF
1297 : END IF
1298 :
1299 452 : IF (my_kpgrp) THEN
1300 452 : ikpgr = ik - kp_range(1) + 1
1301 452 : kp => kpoint%kp_env(ikpgr)%kpoint_env
1302 452 : dst_fmr => kp%mos(1, ispin)%mo_coeff
1303 452 : dst_fmi => kp%mos(2, ispin)%mo_coeff
1304 : CALL get_mo_set(kp%mos(1, ispin), eigenvalues=eigenvalues, &
1305 452 : occupation_numbers=occupation)
1306 2244 : eigenvalues(1:nmo) = eigenvalues_buffer(1:nmo)
1307 2244 : occupation(1:nmo) = occupation_buffer(1:nmo)
1308 : CALL get_mo_set(kp%mos(2, ispin), eigenvalues=eigenvalues, &
1309 452 : occupation_numbers=occupation)
1310 2244 : IF (ASSOCIATED(eigenvalues)) eigenvalues(1:nmo) = eigenvalues_buffer(1:nmo)
1311 2244 : IF (ASSOCIATED(occupation)) occupation(1:nmo) = occupation_buffer(1:nmo)
1312 : ELSE
1313 : NULLIFY (dst_fmr, dst_fmi)
1314 : END IF
1315 452 : CALL cp_fm_copy_general(dst_real, dst_fmr, para_env)
1316 904 : CALL cp_fm_copy_general(dst_imag, dst_fmi, para_env)
1317 : END DO
1318 : END DO
1319 :
1320 30 : CALL cp_fm_release(src_real)
1321 30 : CALL cp_fm_release(src_imag)
1322 30 : CALL cp_fm_release(dst_real)
1323 30 : CALL cp_fm_release(dst_imag)
1324 30 : CALL cp_fm_struct_release(matrix_struct_work)
1325 30 : IF (source_window) THEN
1326 0 : CALL cp_fm_release(src_real_full)
1327 0 : CALL cp_fm_release(src_imag_full)
1328 0 : CALL cp_fm_release(dst_real_full)
1329 0 : CALL cp_fm_release(dst_imag_full)
1330 0 : CALL cp_fm_struct_release(matrix_struct_source)
1331 : END IF
1332 0 : DEALLOCATE (source_kpoint, sym_index, eigenvalues_buffer, occupation_buffer, &
1333 30 : source_eigenvalues_buffer, source_occupation_buffer)
1334 30 : IF (aligned_degenerate_blocks == 0) aligned_degenerate_min_svalue = 0.0_dp
1335 30 : success = .TRUE.
1336 :
1337 174 : END SUBROUTINE prepare_wannier90_scf_mos
1338 :
1339 : ! **************************************************************************************************
1340 : !> \brief Save a full-mesh Wannier90 MO reference on all ranks for diagnostic validation.
1341 : !> \param kpoint full Wannier90 export k-point object
1342 : !> \param nspins number of spin channels
1343 : !> \param para_env global parallel environment
1344 : !> \param mo_real real MO coefficients, indexed as AO, MO, k-point, spin
1345 : !> \param mo_imag imaginary MO coefficients, indexed as AO, MO, k-point, spin
1346 : !> \param eigenvalue_snapshot MO eigenvalues, indexed as MO, k-point, spin
1347 : ! **************************************************************************************************
1348 6 : SUBROUTINE save_wannier90_mo_snapshot(kpoint, nspins, para_env, mo_real, mo_imag, &
1349 : eigenvalue_snapshot)
1350 : TYPE(kpoint_type), POINTER :: kpoint
1351 : INTEGER, INTENT(IN) :: nspins
1352 : TYPE(mp_para_env_type), POINTER :: para_env
1353 : REAL(KIND=dp), ALLOCATABLE, &
1354 : DIMENSION(:, :, :, :), INTENT(OUT) :: mo_real, mo_imag
1355 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :), &
1356 : INTENT(OUT) :: eigenvalue_snapshot
1357 :
1358 : INTEGER :: ik, ikpgr, ispin, nao, nkp, nmo
1359 : INTEGER, DIMENSION(2) :: kp_range
1360 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: owner_weight
1361 6 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues
1362 : TYPE(cp_fm_type), POINTER :: fmi, fmr
1363 : TYPE(kpoint_env_type), POINTER :: kp
1364 :
1365 6 : CALL get_kpoint_info(kpoint, nkp=nkp, kp_range=kp_range)
1366 6 : kp => kpoint%kp_env(1)%kpoint_env
1367 6 : CALL get_mo_set(kp%mos(1, 1), nao=nao, nmo=nmo)
1368 0 : ALLOCATE (mo_real(nao, nmo, nkp, nspins), mo_imag(nao, nmo, nkp, nspins), &
1369 102 : eigenvalue_snapshot(nmo, nkp, nspins), owner_weight(nkp, nspins))
1370 6 : mo_real(:, :, :, :) = 0.0_dp
1371 6 : mo_imag(:, :, :, :) = 0.0_dp
1372 6 : eigenvalue_snapshot(:, :, :) = 0.0_dp
1373 6 : owner_weight(:, :) = 0.0_dp
1374 278 : DO ik = kp_range(1), kp_range(2)
1375 272 : ikpgr = ik - kp_range(1) + 1
1376 272 : kp => kpoint%kp_env(ikpgr)%kpoint_env
1377 550 : DO ispin = 1, nspins
1378 272 : fmr => kp%mos(1, ispin)%mo_coeff
1379 272 : fmi => kp%mos(2, ispin)%mo_coeff
1380 272 : CALL cp_fm_get_submatrix(fmr, mo_real(:, :, ik, ispin))
1381 272 : CALL cp_fm_get_submatrix(fmi, mo_imag(:, :, ik, ispin))
1382 272 : CALL get_mo_set(kp%mos(1, ispin), eigenvalues=eigenvalues)
1383 1312 : eigenvalue_snapshot(1:nmo, ik, ispin) = eigenvalues(1:nmo)
1384 544 : owner_weight(ik, ispin) = 1.0_dp
1385 : END DO
1386 : END DO
1387 6 : CALL para_env%sum(mo_real)
1388 6 : CALL para_env%sum(mo_imag)
1389 6 : CALL para_env%sum(eigenvalue_snapshot)
1390 6 : CALL para_env%sum(owner_weight)
1391 278 : DO ik = 1, nkp
1392 550 : DO ispin = 1, nspins
1393 544 : IF (owner_weight(ik, ispin) > 0.0_dp) THEN
1394 42352 : mo_real(:, :, ik, ispin) = mo_real(:, :, ik, ispin)/owner_weight(ik, ispin)
1395 42352 : mo_imag(:, :, ik, ispin) = mo_imag(:, :, ik, ispin)/owner_weight(ik, ispin)
1396 : eigenvalue_snapshot(:, ik, ispin) = &
1397 1312 : eigenvalue_snapshot(:, ik, ispin)/owner_weight(ik, ispin)
1398 : END IF
1399 : END DO
1400 : END DO
1401 6 : DEALLOCATE (owner_weight)
1402 :
1403 6 : END SUBROUTINE save_wannier90_mo_snapshot
1404 :
1405 : ! **************************************************************************************************
1406 : !> \brief Restore a full-mesh Wannier90 MO reference after a failed diagnostic reuse attempt.
1407 : !> \param kpoint full Wannier90 export k-point object
1408 : !> \param mo_real real MO coefficient snapshot
1409 : !> \param mo_imag imaginary MO coefficient snapshot
1410 : !> \param eigenvalue_snapshot MO eigenvalue snapshot
1411 : ! **************************************************************************************************
1412 0 : SUBROUTINE restore_wannier90_mo_snapshot(kpoint, mo_real, mo_imag, eigenvalue_snapshot)
1413 : TYPE(kpoint_type), POINTER :: kpoint
1414 : REAL(KIND=dp), DIMENSION(:, :, :, :), INTENT(IN) :: mo_real, mo_imag
1415 : REAL(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: eigenvalue_snapshot
1416 :
1417 : INTEGER :: ik, ikpgr, ispin, nmo, nspins
1418 : INTEGER, DIMENSION(2) :: kp_range
1419 0 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues
1420 : TYPE(cp_fm_type), POINTER :: fmi, fmr
1421 : TYPE(kpoint_env_type), POINTER :: kp
1422 :
1423 0 : CALL get_kpoint_info(kpoint, kp_range=kp_range)
1424 0 : nmo = SIZE(eigenvalue_snapshot, 1)
1425 0 : nspins = SIZE(eigenvalue_snapshot, 3)
1426 0 : DO ik = kp_range(1), kp_range(2)
1427 0 : ikpgr = ik - kp_range(1) + 1
1428 0 : kp => kpoint%kp_env(ikpgr)%kpoint_env
1429 0 : DO ispin = 1, nspins
1430 0 : fmr => kp%mos(1, ispin)%mo_coeff
1431 0 : fmi => kp%mos(2, ispin)%mo_coeff
1432 0 : CALL cp_fm_set_submatrix(fmr, mo_real(:, :, ik, ispin))
1433 0 : CALL cp_fm_set_submatrix(fmi, mo_imag(:, :, ik, ispin))
1434 0 : CALL get_mo_set(kp%mos(1, ispin), eigenvalues=eigenvalues)
1435 0 : eigenvalues(1:nmo) = eigenvalue_snapshot(1:nmo, ik, ispin)
1436 0 : CALL get_mo_set(kp%mos(2, ispin), eigenvalues=eigenvalues)
1437 0 : IF (ASSOCIATED(eigenvalues)) eigenvalues(1:nmo) = eigenvalue_snapshot(1:nmo, ik, ispin)
1438 : END DO
1439 : END DO
1440 :
1441 0 : END SUBROUTINE restore_wannier90_mo_snapshot
1442 :
1443 : ! **************************************************************************************************
1444 : !> \brief Validate current Wannier90 MOs against a saved full-mesh diagonalization reference.
1445 : !> \param kpoint full Wannier90 export k-point object
1446 : !> \param matrix_s real-space overlap matrix
1447 : !> \param cell_to_index real-space cell index table
1448 : !> \param sab_nl overlap neighbor list
1449 : !> \param para_env global parallel environment
1450 : !> \param reference_mo_real real MO coefficient reference
1451 : !> \param reference_mo_imag imaginary MO coefficient reference
1452 : !> \param reference_eigenvalues MO eigenvalue reference
1453 : !> \param success true if the reconstructed MOs match the reference subspaces
1454 : !> \param max_subspace_deviation largest deviation of S(k)-metric singular values from one
1455 : !> \param min_svalue smallest S(k)-metric singular value
1456 : !> \param max_eigenvalue_deviation largest eigenvalue deviation
1457 : ! **************************************************************************************************
1458 6 : SUBROUTINE validate_wannier90_reused_mos(kpoint, matrix_s, cell_to_index, sab_nl, para_env, &
1459 6 : reference_mo_real, reference_mo_imag, reference_eigenvalues, &
1460 : success, max_subspace_deviation, min_svalue, &
1461 : max_eigenvalue_deviation)
1462 : TYPE(kpoint_type), POINTER :: kpoint
1463 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s
1464 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
1465 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1466 : POINTER :: sab_nl
1467 : TYPE(mp_para_env_type), POINTER :: para_env
1468 : REAL(KIND=dp), DIMENSION(:, :, :, :), INTENT(IN) :: reference_mo_real, reference_mo_imag
1469 : REAL(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: reference_eigenvalues
1470 : LOGICAL, INTENT(OUT) :: success
1471 : REAL(KIND=dp), INTENT(OUT) :: max_subspace_deviation, min_svalue, &
1472 : max_eigenvalue_deviation
1473 :
1474 : REAL(KIND=dp), PARAMETER :: eigenvalue_tol = 1.0e-8_dp, &
1475 : subspace_tol = 1.0e-4_dp
1476 :
1477 : INTEGER :: ik, ikpgr, ispin, nao, nkp, nmo, nspins
1478 : INTEGER, DIMENSION(2) :: kp_range
1479 : LOGICAL :: my_kpgrp, ok
1480 : REAL(KIND=dp) :: candidate_deviation, candidate_svalue, &
1481 : owner_count
1482 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigenvalue_buffer
1483 6 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues
1484 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_work
1485 : TYPE(cp_fm_type) :: cand_imag, cand_real, ref_imag, ref_real
1486 : TYPE(cp_fm_type), POINTER :: fmi, fmr
1487 : TYPE(kpoint_env_type), POINTER :: kp
1488 :
1489 6 : success = .FALSE.
1490 6 : max_subspace_deviation = 0.0_dp
1491 6 : min_svalue = HUGE(1.0_dp)
1492 6 : max_eigenvalue_deviation = 0.0_dp
1493 6 : NULLIFY (matrix_struct_work, fmr, fmi)
1494 :
1495 6 : CALL get_kpoint_info(kpoint, nkp=nkp, kp_range=kp_range)
1496 6 : kp => kpoint%kp_env(1)%kpoint_env
1497 6 : nspins = SIZE(kp%mos, 2)
1498 6 : CALL get_mo_set(kp%mos(1, 1), nao=nao, nmo=nmo)
1499 6 : CPASSERT(SIZE(reference_mo_real, 1) == nao)
1500 6 : CPASSERT(SIZE(reference_mo_real, 2) == nmo)
1501 6 : CPASSERT(SIZE(reference_mo_real, 3) == nkp)
1502 6 : CPASSERT(SIZE(reference_mo_real, 4) == nspins)
1503 :
1504 6 : CALL cp_fm_get_info(kp%mos(1, 1)%mo_coeff, matrix_struct=matrix_struct_work)
1505 6 : CALL cp_fm_create(ref_real, matrix_struct_work)
1506 6 : CALL cp_fm_create(ref_imag, matrix_struct_work)
1507 6 : CALL cp_fm_create(cand_real, matrix_struct_work)
1508 6 : CALL cp_fm_create(cand_imag, matrix_struct_work)
1509 18 : ALLOCATE (eigenvalue_buffer(nmo))
1510 :
1511 12 : DO ispin = 1, nspins
1512 284 : DO ik = 1, nkp
1513 272 : CALL cp_fm_set_submatrix(ref_real, reference_mo_real(:, :, ik, ispin))
1514 272 : CALL cp_fm_set_submatrix(ref_imag, reference_mo_imag(:, :, ik, ispin))
1515 272 : my_kpgrp = (ik >= kp_range(1) .AND. ik <= kp_range(2))
1516 : IF (my_kpgrp) THEN
1517 272 : ikpgr = ik - kp_range(1) + 1
1518 272 : kp => kpoint%kp_env(ikpgr)%kpoint_env
1519 272 : fmr => kp%mos(1, ispin)%mo_coeff
1520 272 : fmi => kp%mos(2, ispin)%mo_coeff
1521 272 : CALL get_mo_set(kp%mos(1, ispin), eigenvalues=eigenvalues)
1522 1312 : eigenvalue_buffer(1:nmo) = eigenvalues(1:nmo)
1523 : ELSE
1524 0 : NULLIFY (fmr, fmi)
1525 0 : eigenvalue_buffer(1:nmo) = 0.0_dp
1526 : END IF
1527 272 : CALL cp_fm_copy_general(fmr, cand_real, para_env)
1528 272 : CALL cp_fm_copy_general(fmi, cand_imag, para_env)
1529 272 : IF (my_kpgrp) THEN
1530 272 : owner_count = 1.0_dp
1531 : ELSE
1532 0 : owner_count = 0.0_dp
1533 : END IF
1534 272 : CALL para_env%sum(owner_count)
1535 272 : CALL para_env%sum(eigenvalue_buffer)
1536 1312 : IF (owner_count > 0.0_dp) eigenvalue_buffer(1:nmo) = eigenvalue_buffer(1:nmo)/owner_count
1537 : max_eigenvalue_deviation = MAX(max_eigenvalue_deviation, &
1538 : MAXVAL(ABS(eigenvalue_buffer(1:nmo) - &
1539 1312 : reference_eigenvalues(1:nmo, ik, ispin))))
1540 : CALL measure_wannier90_subspace_error(ref_real, ref_imag, cand_real, cand_imag, matrix_s, &
1541 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, para_env, &
1542 272 : ok, candidate_deviation, candidate_svalue)
1543 278 : IF (.NOT. ok) THEN
1544 0 : max_subspace_deviation = HUGE(1.0_dp)
1545 : ELSE
1546 272 : max_subspace_deviation = MAX(max_subspace_deviation, candidate_deviation)
1547 272 : min_svalue = MIN(min_svalue, candidate_svalue)
1548 : END IF
1549 : END DO
1550 : END DO
1551 6 : CALL para_env%max(max_subspace_deviation)
1552 6 : CALL para_env%min(min_svalue)
1553 6 : CALL para_env%max(max_eigenvalue_deviation)
1554 6 : success = max_subspace_deviation < subspace_tol .AND. max_eigenvalue_deviation < eigenvalue_tol
1555 :
1556 6 : DEALLOCATE (eigenvalue_buffer)
1557 6 : CALL cp_fm_release(ref_real)
1558 6 : CALL cp_fm_release(ref_imag)
1559 6 : CALL cp_fm_release(cand_real)
1560 6 : CALL cp_fm_release(cand_imag)
1561 :
1562 12 : END SUBROUTINE validate_wannier90_reused_mos
1563 :
1564 : ! **************************************************************************************************
1565 : !> \brief Compare atom/AO reuse candidates directly to the full-mesh reference MOs.
1566 : !> \param kpoint full Wannier90 export k-point object holding reference MOs
1567 : !> \param qs_kpoint SCF k-point object
1568 : !> \param matrix_s real-space overlap matrix
1569 : !> \param matrix_ks real-space Kohn-Sham matrix
1570 : !> \param cell_to_index real-space cell index table
1571 : !> \param sab_nl overlap neighbor list
1572 : !> \param para_env global parallel environment
1573 : !> \param iw output unit
1574 : !> \param max_subspace_deviation largest best-candidate subspace deviation
1575 : !> \param min_svalue smallest best-candidate singular value
1576 : !> \param max_metric_deviation largest S(k)-metric deviation of a candidate
1577 : !> \param max_residual largest H(k),S(k) eigen-residual of a candidate
1578 : ! **************************************************************************************************
1579 6 : SUBROUTINE diagnose_wannier90_scf_reuse_candidates(kpoint, qs_kpoint, matrix_s, matrix_ks, &
1580 : cell_to_index, sab_nl, para_env, iw, &
1581 : max_subspace_deviation, min_svalue, &
1582 : max_metric_deviation, max_residual)
1583 : TYPE(kpoint_type), POINTER :: kpoint, qs_kpoint
1584 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
1585 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
1586 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1587 : POINTER :: sab_nl
1588 : TYPE(mp_para_env_type), POINTER :: para_env
1589 : INTEGER, INTENT(IN) :: iw
1590 : REAL(KIND=dp), INTENT(OUT) :: max_subspace_deviation, min_svalue, &
1591 : max_metric_deviation, max_residual
1592 :
1593 : REAL(KIND=dp), PARAMETER :: print_tol = 1.0e-4_dp, &
1594 : residual_print_tol = 1.0e-3_dp
1595 :
1596 : CHARACTER(LEN=default_string_length) :: reason
1597 : INTEGER :: ik, ikpgr, ikred, ispin, isym_try, nao, &
1598 : nao_src, nkp, nmo, nmo_src, nspins
1599 6 : INTEGER, ALLOCATABLE, DIMENSION(:) :: source_kpoint, sym_index
1600 : INTEGER, DIMENSION(2) :: kp_range, source_kp_range
1601 : LOGICAL :: my_kpgrp, ok, source_window
1602 : REAL(KIND=dp) :: best_deviation, best_metric_deviation, best_residual, best_svalue, &
1603 : candidate_deviation, candidate_metric_deviation, candidate_metric_min, &
1604 : candidate_residual, candidate_svalue, owner_count, ref_metric_deviation, ref_metric_min, &
1605 : ref_residual
1606 6 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigenvalues_buffer
1607 6 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues
1608 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_source, matrix_struct_work
1609 : TYPE(cp_fm_type) :: dst_imag, dst_real, ref_imag, ref_real, &
1610 : src_imag, src_imag_full, src_real, &
1611 : src_real_full
1612 : TYPE(cp_fm_type), POINTER :: fmi, fmr, src_fmi, src_fmr
1613 : TYPE(kpoint_env_type), POINTER :: kp, kp_source
1614 : TYPE(kpoint_sym_type), POINTER :: kpsym
1615 :
1616 6 : max_subspace_deviation = HUGE(1.0_dp)
1617 6 : min_svalue = 0.0_dp
1618 6 : max_metric_deviation = HUGE(1.0_dp)
1619 6 : max_residual = HUGE(1.0_dp)
1620 6 : NULLIFY (matrix_struct_source, matrix_struct_work, fmi, fmr, src_fmi, src_fmr)
1621 :
1622 6 : CALL build_wannier90_scf_mapping(kpoint, qs_kpoint, source_kpoint, sym_index, ok, reason)
1623 6 : IF (.NOT. ok) RETURN
1624 6 : kp => kpoint%kp_env(1)%kpoint_env
1625 6 : kp_source => qs_kpoint%kp_env(1)%kpoint_env
1626 6 : IF (SIZE(kp%mos, 1) < 2 .OR. SIZE(kp_source%mos, 1) < 2) THEN
1627 0 : DEALLOCATE (source_kpoint, sym_index)
1628 0 : RETURN
1629 : END IF
1630 6 : nspins = SIZE(kp%mos, 2)
1631 6 : CALL get_mo_set(kp%mos(1, 1), nao=nao, nmo=nmo)
1632 6 : CALL get_mo_set(kp_source%mos(1, 1), nao=nao_src, nmo=nmo_src)
1633 6 : CALL para_env%max(nao_src)
1634 6 : CALL para_env%max(nmo_src)
1635 6 : IF (nao_src /= nao .OR. nmo_src < nmo) THEN
1636 0 : DEALLOCATE (source_kpoint, sym_index)
1637 0 : RETURN
1638 : END IF
1639 6 : source_window = nmo_src > nmo
1640 6 : CALL get_kpoint_info(kpoint, nkp=nkp, kp_range=kp_range)
1641 6 : CALL get_kpoint_info(qs_kpoint, kp_range=source_kp_range)
1642 6 : IF (source_kp_range(1) /= 1 .OR. source_kp_range(2) /= qs_kpoint%nkp) THEN
1643 0 : DEALLOCATE (source_kpoint, sym_index)
1644 0 : RETURN
1645 : END IF
1646 :
1647 : CALL cp_fm_struct_create(matrix_struct_work, nrow_global=nao, ncol_global=nmo, &
1648 6 : para_env=para_env, context=kpoint%blacs_env)
1649 6 : CALL cp_fm_create(ref_real, matrix_struct_work)
1650 6 : CALL cp_fm_create(ref_imag, matrix_struct_work)
1651 6 : CALL cp_fm_create(src_real, matrix_struct_work)
1652 6 : CALL cp_fm_create(src_imag, matrix_struct_work)
1653 6 : CALL cp_fm_create(dst_real, matrix_struct_work)
1654 6 : CALL cp_fm_create(dst_imag, matrix_struct_work)
1655 18 : ALLOCATE (eigenvalues_buffer(nmo))
1656 6 : IF (source_window) THEN
1657 : CALL cp_fm_struct_create(matrix_struct_source, nrow_global=nao, ncol_global=nmo_src, &
1658 0 : para_env=para_env, context=kpoint%blacs_env)
1659 0 : CALL cp_fm_create(src_real_full, matrix_struct_source)
1660 0 : CALL cp_fm_create(src_imag_full, matrix_struct_source)
1661 : END IF
1662 :
1663 6 : max_subspace_deviation = 0.0_dp
1664 6 : min_svalue = HUGE(1.0_dp)
1665 6 : max_metric_deviation = 0.0_dp
1666 6 : max_residual = 0.0_dp
1667 12 : DO ispin = 1, nspins
1668 284 : DO ik = 1, nkp
1669 272 : my_kpgrp = (ik >= kp_range(1) .AND. ik <= kp_range(2))
1670 : IF (my_kpgrp) THEN
1671 272 : ikpgr = ik - kp_range(1) + 1
1672 272 : kp => kpoint%kp_env(ikpgr)%kpoint_env
1673 272 : fmr => kp%mos(1, ispin)%mo_coeff
1674 272 : fmi => kp%mos(2, ispin)%mo_coeff
1675 : ELSE
1676 : NULLIFY (fmr, fmi)
1677 : END IF
1678 272 : CALL cp_fm_copy_general(fmr, ref_real, para_env)
1679 272 : CALL cp_fm_copy_general(fmi, ref_imag, para_env)
1680 :
1681 272 : ikred = source_kpoint(ik)
1682 272 : my_kpgrp = (ikred >= source_kp_range(1) .AND. ikred <= source_kp_range(2))
1683 : IF (my_kpgrp) THEN
1684 272 : ikpgr = ikred - source_kp_range(1) + 1
1685 272 : kp_source => qs_kpoint%kp_env(ikpgr)%kpoint_env
1686 272 : src_fmr => kp_source%mos(1, ispin)%mo_coeff
1687 272 : src_fmi => kp_source%mos(2, ispin)%mo_coeff
1688 272 : CALL get_mo_set(kp_source%mos(1, ispin), eigenvalues=eigenvalues)
1689 1312 : eigenvalues_buffer(1:nmo) = eigenvalues(1:nmo)
1690 : ELSE
1691 0 : NULLIFY (src_fmr, src_fmi)
1692 0 : eigenvalues_buffer(1:nmo) = 0.0_dp
1693 : END IF
1694 272 : IF (my_kpgrp) THEN
1695 272 : owner_count = 1.0_dp
1696 : ELSE
1697 0 : owner_count = 0.0_dp
1698 : END IF
1699 272 : CALL para_env%sum(owner_count)
1700 272 : CALL para_env%sum(eigenvalues_buffer)
1701 1312 : IF (owner_count > 0.0_dp) eigenvalues_buffer(1:nmo) = eigenvalues_buffer(1:nmo)/owner_count
1702 : CALL measure_wannier90_eigenspace_quality(ref_real, ref_imag, matrix_s, matrix_ks, &
1703 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, &
1704 : para_env, ispin, eigenvalues_buffer, ok, &
1705 272 : ref_metric_deviation, ref_metric_min, ref_residual)
1706 272 : IF (ok .AND. para_env%is_source() .AND. iw > 0 .AND. &
1707 : (ref_metric_deviation > print_tol .OR. ref_residual > residual_print_tol)) THEN
1708 : WRITE (iw, '(T2,A,I0,A,ES10.3,A,ES10.3,A,ES10.3)') &
1709 0 : "WANNIER90| reference k=", ik, " dM=", ref_metric_deviation, &
1710 0 : " smin=", ref_metric_min, " resid=", ref_residual
1711 : END IF
1712 272 : IF (source_window) THEN
1713 0 : CALL cp_fm_copy_general(src_fmr, src_real_full, para_env)
1714 0 : CALL cp_fm_copy_general(src_fmi, src_imag_full, para_env)
1715 0 : CALL copy_wannier90_mo_window(src_real_full, src_real, nmo)
1716 0 : CALL copy_wannier90_mo_window(src_imag_full, src_imag, nmo)
1717 : ELSE
1718 272 : CALL cp_fm_copy_general(src_fmr, src_real, para_env)
1719 272 : CALL cp_fm_copy_general(src_fmi, src_imag, para_env)
1720 : END IF
1721 :
1722 272 : best_deviation = HUGE(1.0_dp)
1723 272 : best_metric_deviation = 0.0_dp
1724 272 : best_residual = 0.0_dp
1725 272 : best_svalue = 0.0_dp
1726 272 : IF (sym_index(ik) <= 0) THEN
1727 : CALL kpoint_transform_scf_mo(src_real, src_imag, dst_real, dst_imag, qs_kpoint, &
1728 36 : ikred, sym_index(ik), para_env, ok, reason)
1729 36 : IF (ok) THEN
1730 : CALL measure_wannier90_subspace_error(ref_real, ref_imag, dst_real, dst_imag, matrix_s, &
1731 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, &
1732 36 : para_env, ok, candidate_deviation, candidate_svalue)
1733 36 : IF (ok) THEN
1734 : CALL measure_wannier90_eigenspace_quality(dst_real, dst_imag, matrix_s, matrix_ks, &
1735 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, &
1736 : para_env, ispin, eigenvalues_buffer, ok, &
1737 : candidate_metric_deviation, &
1738 36 : candidate_metric_min, candidate_residual)
1739 : END IF
1740 36 : IF (ok) THEN
1741 36 : best_deviation = candidate_deviation
1742 36 : best_metric_deviation = candidate_metric_deviation
1743 36 : best_residual = candidate_residual
1744 36 : best_svalue = candidate_svalue
1745 36 : IF (para_env%is_source() .AND. iw > 0 .AND. &
1746 : (candidate_deviation > print_tol .OR. candidate_metric_deviation > print_tol .OR. &
1747 : candidate_residual > residual_print_tol)) THEN
1748 : WRITE (iw, '(T2,A,I0,A,I0,A,I0,A,ES10.3,A,ES10.3,A,ES10.3,A,ES10.3)') &
1749 0 : "WANNIER90| reuse candidate k=", ik, " src=", ikred, " sym=", &
1750 0 : sym_index(ik), " dRef=", candidate_deviation, " dM=", &
1751 0 : candidate_metric_deviation, " smin=", candidate_metric_min, &
1752 0 : " resid=", candidate_residual
1753 : END IF
1754 : END IF
1755 : END IF
1756 236 : ELSE IF (ASSOCIATED(qs_kpoint%kp_sym)) THEN
1757 236 : kpsym => qs_kpoint%kp_sym(ikred)%kpoint_sym
1758 236 : IF (ASSOCIATED(kpsym)) THEN
1759 22892 : DO isym_try = 1, kpsym%nwred
1760 22656 : IF (.NOT. kpoint_same_periodic(kpoint%xkp(1:3, ik), &
1761 : kpsym%xkp(1:3, isym_try))) CYCLE
1762 : CALL kpoint_transform_scf_mo(src_real, src_imag, dst_real, dst_imag, &
1763 1424 : qs_kpoint, ikred, isym_try, para_env, ok, reason)
1764 1424 : IF (.NOT. ok) CYCLE
1765 : CALL measure_wannier90_subspace_error(ref_real, ref_imag, dst_real, dst_imag, &
1766 : matrix_s, kpoint%xkp(1:3, ik), cell_to_index, &
1767 : sab_nl, para_env, ok, candidate_deviation, &
1768 1424 : candidate_svalue)
1769 1424 : IF (ok) THEN
1770 : CALL measure_wannier90_eigenspace_quality(dst_real, dst_imag, matrix_s, matrix_ks, &
1771 : kpoint%xkp(1:3, ik), cell_to_index, &
1772 : sab_nl, para_env, ispin, &
1773 : eigenvalues_buffer, ok, &
1774 : candidate_metric_deviation, &
1775 1424 : candidate_metric_min, candidate_residual)
1776 : END IF
1777 3084 : IF (ok .AND. candidate_deviation < best_deviation) THEN
1778 252 : best_deviation = candidate_deviation
1779 252 : best_metric_deviation = candidate_metric_deviation
1780 252 : best_residual = candidate_residual
1781 252 : best_svalue = candidate_svalue
1782 : END IF
1783 : END DO
1784 : END IF
1785 : END IF
1786 278 : IF (best_deviation < HUGE(1.0_dp)) THEN
1787 272 : max_subspace_deviation = MAX(max_subspace_deviation, best_deviation)
1788 272 : min_svalue = MIN(min_svalue, best_svalue)
1789 272 : max_metric_deviation = MAX(max_metric_deviation, best_metric_deviation)
1790 272 : max_residual = MAX(max_residual, best_residual)
1791 : END IF
1792 : END DO
1793 : END DO
1794 6 : CALL para_env%max(max_subspace_deviation)
1795 6 : CALL para_env%min(min_svalue)
1796 6 : CALL para_env%max(max_metric_deviation)
1797 6 : CALL para_env%max(max_residual)
1798 :
1799 6 : IF (source_window) THEN
1800 0 : CALL cp_fm_release(src_real_full)
1801 0 : CALL cp_fm_release(src_imag_full)
1802 0 : CALL cp_fm_struct_release(matrix_struct_source)
1803 : END IF
1804 6 : CALL cp_fm_release(ref_real)
1805 6 : CALL cp_fm_release(ref_imag)
1806 6 : CALL cp_fm_release(src_real)
1807 6 : CALL cp_fm_release(src_imag)
1808 6 : CALL cp_fm_release(dst_real)
1809 6 : CALL cp_fm_release(dst_imag)
1810 6 : CALL cp_fm_struct_release(matrix_struct_work)
1811 6 : DEALLOCATE (eigenvalues_buffer)
1812 6 : DEALLOCATE (source_kpoint, sym_index)
1813 :
1814 24 : END SUBROUTINE diagnose_wannier90_scf_reuse_candidates
1815 :
1816 : ! **************************************************************************************************
1817 : !> \brief Measure the S(k)-metric distance between two Wannier90 MO subspaces.
1818 : !> \param ref_real real part of reference MO coefficients
1819 : !> \param ref_imag imaginary part of reference MO coefficients
1820 : !> \param cand_real real part of candidate MO coefficients
1821 : !> \param cand_imag imaginary part of candidate MO coefficients
1822 : !> \param matrix_s real-space overlap matrix
1823 : !> \param xkp target k-point coordinate
1824 : !> \param cell_to_index real-space cell index table
1825 : !> \param sab_nl overlap neighbor list
1826 : !> \param para_env global parallel environment
1827 : !> \param success true if the metric comparison was performed
1828 : !> \param max_subspace_deviation largest deviation of singular values from one
1829 : !> \param min_svalue smallest singular value of C_ref^+ S(k) C_candidate
1830 : ! **************************************************************************************************
1831 1732 : SUBROUTINE measure_wannier90_subspace_error(ref_real, ref_imag, cand_real, cand_imag, matrix_s, &
1832 : xkp, cell_to_index, sab_nl, para_env, success, &
1833 : max_subspace_deviation, min_svalue)
1834 : TYPE(cp_fm_type), INTENT(IN) :: ref_real, ref_imag, cand_real, cand_imag
1835 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s
1836 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
1837 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
1838 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1839 : POINTER :: sab_nl
1840 : TYPE(mp_para_env_type), POINTER :: para_env
1841 : LOGICAL, INTENT(OUT) :: success
1842 : REAL(KIND=dp), INTENT(OUT) :: max_subspace_deviation, min_svalue
1843 :
1844 1732 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: metric_projected, metric_vectors, &
1845 1732 : overlap, ref_coeff, s_cand
1846 : INTEGER :: ib, nao, nmo, nmo_candidate
1847 : REAL(KIND=dp) :: singular_value
1848 1732 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: metric_values
1849 1732 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: ref_i, ref_r, s_cand_i, s_cand_r
1850 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_metric
1851 : TYPE(cp_fm_type) :: s_cand_imag, s_cand_real
1852 :
1853 1732 : success = .FALSE.
1854 1732 : max_subspace_deviation = HUGE(1.0_dp)
1855 1732 : min_svalue = 0.0_dp
1856 1732 : NULLIFY (matrix_struct_metric)
1857 :
1858 : CALL cp_fm_get_info(ref_real, nrow_global=nao, ncol_global=nmo, &
1859 1732 : matrix_struct=matrix_struct_metric)
1860 1732 : CALL cp_fm_get_info(cand_real, ncol_global=nmo_candidate)
1861 1732 : IF (nmo_candidate /= nmo) RETURN
1862 :
1863 1732 : CALL cp_fm_create(s_cand_real, matrix_struct_metric)
1864 1732 : CALL cp_fm_create(s_cand_imag, matrix_struct_metric)
1865 : CALL apply_wannier90_kp_matrix(matrix_s, 1, xkp, cell_to_index, sab_nl, &
1866 1732 : cand_real, cand_imag, s_cand_real, s_cand_imag)
1867 :
1868 17320 : ALLOCATE (ref_r(nao, nmo), ref_i(nao, nmo), s_cand_r(nao, nmo), s_cand_i(nao, nmo))
1869 1732 : CALL cp_fm_get_submatrix(ref_real, ref_r)
1870 1732 : CALL cp_fm_get_submatrix(ref_imag, ref_i)
1871 1732 : CALL cp_fm_get_submatrix(s_cand_real, s_cand_r)
1872 1732 : CALL cp_fm_get_submatrix(s_cand_imag, s_cand_i)
1873 :
1874 : ALLOCATE (ref_coeff(nao, nmo), s_cand(nao, nmo), overlap(nmo, nmo), &
1875 25980 : metric_projected(nmo, nmo), metric_vectors(nmo, nmo), metric_values(nmo))
1876 259868 : ref_coeff(:, :) = CMPLX(ref_r, ref_i, KIND=dp)
1877 259868 : s_cand(:, :) = CMPLX(s_cand_r, s_cand_i, KIND=dp)
1878 1037760 : overlap(:, :) = MATMUL(CONJG(TRANSPOSE(ref_coeff)), s_cand)
1879 133936 : metric_projected(:, :) = MATMUL(CONJG(TRANSPOSE(overlap)), overlap)
1880 65108 : metric_projected(:, :) = 0.5_dp*(metric_projected + CONJG(TRANSPOSE(metric_projected)))
1881 1732 : CALL diag_complex(metric_projected, metric_vectors, metric_values)
1882 :
1883 1732 : min_svalue = HUGE(1.0_dp)
1884 1732 : max_subspace_deviation = 0.0_dp
1885 8168 : DO ib = 1, nmo
1886 6436 : singular_value = SQRT(MAX(metric_values(ib), 0.0_dp))
1887 6436 : min_svalue = MIN(min_svalue, singular_value)
1888 8168 : max_subspace_deviation = MAX(max_subspace_deviation, ABS(singular_value - 1.0_dp))
1889 : END DO
1890 1732 : CALL para_env%max(max_subspace_deviation)
1891 1732 : CALL para_env%min(min_svalue)
1892 1732 : success = .TRUE.
1893 :
1894 1732 : DEALLOCATE (ref_coeff, s_cand, overlap, metric_projected, metric_vectors, metric_values)
1895 1732 : DEALLOCATE (ref_r, ref_i, s_cand_r, s_cand_i)
1896 1732 : CALL cp_fm_release(s_cand_real)
1897 1732 : CALL cp_fm_release(s_cand_imag)
1898 :
1899 5196 : END SUBROUTINE measure_wannier90_subspace_error
1900 :
1901 : ! **************************************************************************************************
1902 : !> \brief Measure whether transformed MOs are an H(k),S(k) invariant eigenspace.
1903 : !> \param cand_real real part of candidate MO coefficients
1904 : !> \param cand_imag imaginary part of candidate MO coefficients
1905 : !> \param matrix_s real-space overlap matrix
1906 : !> \param matrix_ks real-space Kohn-Sham matrix
1907 : !> \param xkp target k-point coordinate
1908 : !> \param cell_to_index real-space cell index table
1909 : !> \param sab_nl overlap neighbor list
1910 : !> \param para_env global parallel environment
1911 : !> \param ispin spin index
1912 : !> \param eigenvalues source MO eigenvalues corresponding to the candidate columns
1913 : !> \param success true if the metric and residual checks were performed
1914 : !> \param metric_deviation largest deviation of eigenvalues of C^+ S(k) C from one
1915 : !> \param min_metric_eigenvalue smallest eigenvalue of C^+ S(k) C
1916 : !> \param residual_norm largest element of H(k) C - S(k) C eps
1917 : ! **************************************************************************************************
1918 1732 : SUBROUTINE measure_wannier90_eigenspace_quality(cand_real, cand_imag, matrix_s, matrix_ks, xkp, &
1919 1732 : cell_to_index, sab_nl, para_env, ispin, eigenvalues, &
1920 : success, metric_deviation, min_metric_eigenvalue, &
1921 : residual_norm)
1922 : TYPE(cp_fm_type), INTENT(IN) :: cand_real, cand_imag
1923 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
1924 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
1925 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
1926 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1927 : POINTER :: sab_nl
1928 : TYPE(mp_para_env_type), POINTER :: para_env
1929 : INTEGER, INTENT(IN) :: ispin
1930 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: eigenvalues
1931 : LOGICAL, INTENT(OUT) :: success
1932 : REAL(KIND=dp), INTENT(OUT) :: metric_deviation, min_metric_eigenvalue, &
1933 : residual_norm
1934 :
1935 1732 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: cand_coeff, h_coeff, metric_vectors, &
1936 1732 : residual_block, s_coeff, s_projected
1937 : INTEGER :: ib, nao, nmo
1938 1732 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: metric_values
1939 1732 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: cand_i, cand_r, h_coeff_i, h_coeff_r, &
1940 1732 : s_coeff_i, s_coeff_r
1941 : TYPE(cp_cfm_type) :: cand_cfm, metric_cfm, s_cfm
1942 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_metric, &
1943 : matrix_struct_projected
1944 : TYPE(cp_fm_type) :: h_cand_imag, h_cand_real, s_cand_imag, &
1945 : s_cand_real, tmp_fm
1946 :
1947 1732 : success = .FALSE.
1948 1732 : metric_deviation = HUGE(1.0_dp)
1949 1732 : min_metric_eigenvalue = 0.0_dp
1950 1732 : residual_norm = HUGE(1.0_dp)
1951 1732 : NULLIFY (matrix_struct_metric, matrix_struct_projected)
1952 :
1953 : CALL cp_fm_get_info(cand_real, nrow_global=nao, ncol_global=nmo, &
1954 1732 : matrix_struct=matrix_struct_metric)
1955 1732 : IF (SIZE(eigenvalues) < nmo) RETURN
1956 :
1957 1732 : CALL cp_fm_create(s_cand_real, matrix_struct_metric)
1958 1732 : CALL cp_fm_create(s_cand_imag, matrix_struct_metric)
1959 1732 : CALL cp_fm_create(h_cand_real, matrix_struct_metric)
1960 1732 : CALL cp_fm_create(h_cand_imag, matrix_struct_metric)
1961 1732 : CALL cp_fm_create(tmp_fm, matrix_struct_metric)
1962 1732 : CALL cp_cfm_create(cand_cfm, matrix_struct_metric)
1963 1732 : CALL cp_cfm_create(s_cfm, matrix_struct_metric)
1964 :
1965 : CALL apply_wannier90_kp_matrix(matrix_s, 1, xkp, cell_to_index, sab_nl, &
1966 1732 : cand_real, cand_imag, s_cand_real, s_cand_imag)
1967 : CALL apply_wannier90_kp_matrix(matrix_ks, ispin, xkp, cell_to_index, sab_nl, &
1968 1732 : cand_real, cand_imag, h_cand_real, h_cand_imag)
1969 :
1970 : ALLOCATE (cand_r(nao, nmo), cand_i(nao, nmo), s_coeff_r(nao, nmo), &
1971 24248 : s_coeff_i(nao, nmo), h_coeff_r(nao, nmo), h_coeff_i(nao, nmo))
1972 1732 : CALL cp_fm_get_submatrix(cand_real, cand_r)
1973 1732 : CALL cp_fm_get_submatrix(cand_imag, cand_i)
1974 1732 : CALL cp_fm_get_submatrix(s_cand_real, s_coeff_r)
1975 1732 : CALL cp_fm_get_submatrix(s_cand_imag, s_coeff_i)
1976 1732 : CALL cp_fm_get_submatrix(h_cand_real, h_coeff_r)
1977 1732 : CALL cp_fm_get_submatrix(h_cand_imag, h_coeff_i)
1978 :
1979 : ALLOCATE (cand_coeff(nao, nmo), h_coeff(nao, nmo), metric_vectors(nmo, nmo), &
1980 : residual_block(nao, nmo), s_coeff(nao, nmo), s_projected(nmo, nmo), &
1981 29444 : metric_values(nmo))
1982 259868 : cand_coeff(:, :) = CMPLX(cand_r, cand_i, KIND=dp)
1983 259868 : s_coeff(:, :) = CMPLX(s_coeff_r, s_coeff_i, KIND=dp)
1984 259868 : h_coeff(:, :) = CMPLX(h_coeff_r, h_coeff_i, KIND=dp)
1985 : CALL cp_fm_struct_create(matrix_struct_projected, nrow_global=nmo, ncol_global=nmo, &
1986 : para_env=matrix_struct_metric%para_env, &
1987 1732 : context=matrix_struct_metric%context)
1988 1732 : CALL cp_cfm_create(metric_cfm, matrix_struct_projected)
1989 1732 : CALL cp_fm_to_cfm(cand_real, cand_imag, cand_cfm)
1990 1732 : CALL cp_fm_to_cfm(s_cand_real, s_cand_imag, s_cfm)
1991 : CALL cp_cfm_gemm("C", "N", nmo, nmo, nao, CMPLX(1.0_dp, 0.0_dp, KIND=dp), cand_cfm, &
1992 1732 : s_cfm, CMPLX(0.0_dp, 0.0_dp, KIND=dp), metric_cfm)
1993 1732 : CALL cp_cfm_get_submatrix(metric_cfm, s_projected)
1994 65108 : s_projected(:, :) = 0.5_dp*(s_projected + CONJG(TRANSPOSE(s_projected)))
1995 1732 : CALL diag_complex(s_projected, metric_vectors, metric_values)
1996 8168 : metric_deviation = MAXVAL(ABS(metric_values - 1.0_dp))
1997 8168 : min_metric_eigenvalue = MINVAL(metric_values)
1998 :
1999 259868 : residual_block(:, :) = h_coeff
2000 8168 : DO ib = 1, nmo
2001 259868 : residual_block(:, ib) = residual_block(:, ib) - eigenvalues(ib)*s_coeff(:, ib)
2002 : END DO
2003 259868 : residual_norm = MAXVAL(ABS(residual_block))
2004 1732 : CALL para_env%max(metric_deviation)
2005 1732 : CALL para_env%min(min_metric_eigenvalue)
2006 1732 : CALL para_env%max(residual_norm)
2007 1732 : success = .TRUE.
2008 :
2009 0 : DEALLOCATE (cand_coeff, h_coeff, metric_vectors, residual_block, s_coeff, s_projected, &
2010 1732 : metric_values)
2011 1732 : DEALLOCATE (cand_r, cand_i, s_coeff_r, s_coeff_i, h_coeff_r, h_coeff_i)
2012 1732 : CALL cp_fm_release(s_cand_real)
2013 1732 : CALL cp_fm_release(s_cand_imag)
2014 1732 : CALL cp_fm_release(h_cand_real)
2015 1732 : CALL cp_fm_release(h_cand_imag)
2016 1732 : CALL cp_fm_release(tmp_fm)
2017 1732 : CALL cp_cfm_release(cand_cfm)
2018 1732 : CALL cp_cfm_release(s_cfm)
2019 1732 : CALL cp_cfm_release(metric_cfm)
2020 1732 : CALL cp_fm_struct_release(matrix_struct_projected)
2021 :
2022 6928 : END SUBROUTINE measure_wannier90_eigenspace_quality
2023 :
2024 : ! **************************************************************************************************
2025 : !> \brief Copy the leading MO columns from a larger SCF MO matrix into the Wannier90 export window.
2026 : !> \param source source MO coefficient matrix
2027 : !> \param destination destination MO coefficient matrix
2028 : !> \param ncol number of columns to copy
2029 : ! **************************************************************************************************
2030 0 : SUBROUTINE copy_wannier90_mo_window(source, destination, ncol)
2031 : TYPE(cp_fm_type), INTENT(IN) :: source, destination
2032 : INTEGER, INTENT(IN) :: ncol
2033 :
2034 : INTEGER :: ncol_source, nrow
2035 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: destination_buffer, source_buffer
2036 :
2037 0 : CALL cp_fm_get_info(source, nrow_global=nrow, ncol_global=ncol_source)
2038 0 : CPASSERT(ncol_source >= ncol)
2039 0 : ALLOCATE (source_buffer(nrow, ncol_source), destination_buffer(nrow, ncol))
2040 0 : CALL cp_fm_get_submatrix(source, source_buffer)
2041 0 : destination_buffer(1:nrow, 1:ncol) = source_buffer(1:nrow, 1:ncol)
2042 0 : CALL cp_fm_set_submatrix(destination, destination_buffer)
2043 0 : DEALLOCATE (source_buffer, destination_buffer)
2044 :
2045 0 : END SUBROUTINE copy_wannier90_mo_window
2046 :
2047 : ! **************************************************************************************************
2048 : !> \brief Apply a complex k-point matrix to a complex MO coefficient matrix.
2049 : !> \param rsmat real-space matrix images
2050 : !> \param ispin spin index for rsmat
2051 : !> \param xkp target k-point coordinate
2052 : !> \param cell_to_index real-space cell index table
2053 : !> \param sab_nl overlap neighbor list
2054 : !> \param coeff_real real part of input MO coefficients
2055 : !> \param coeff_imag imaginary part of input MO coefficients
2056 : !> \param result_real real part of matrix-vector product
2057 : !> \param result_imag imaginary part of matrix-vector product
2058 : ! **************************************************************************************************
2059 36600 : SUBROUTINE apply_wannier90_kp_matrix(rsmat, ispin, xkp, cell_to_index, sab_nl, &
2060 : coeff_real, coeff_imag, result_real, result_imag)
2061 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: rsmat
2062 : INTEGER, INTENT(IN) :: ispin
2063 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
2064 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
2065 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
2066 : POINTER :: sab_nl
2067 : TYPE(cp_fm_type), INTENT(IN) :: coeff_real, coeff_imag, result_real, &
2068 : result_imag
2069 :
2070 : INTEGER :: nao, ncol
2071 : TYPE(cp_cfm_type) :: coeff_cfm, kmat_cfm, result_cfm
2072 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_ao, matrix_struct_coeff
2073 : TYPE(cp_fm_type) :: mat_imag, mat_real
2074 : TYPE(dbcsr_type), POINTER :: kmat_imag, kmat_imag_full, kmat_real, &
2075 : kmat_real_full
2076 :
2077 6100 : NULLIFY (matrix_struct_ao, matrix_struct_coeff, kmat_imag, kmat_imag_full, kmat_real, &
2078 6100 : kmat_real_full)
2079 :
2080 : CALL cp_fm_get_info(coeff_real, nrow_global=nao, ncol_global=ncol, &
2081 6100 : matrix_struct=matrix_struct_coeff)
2082 :
2083 6100 : ALLOCATE (kmat_real, kmat_imag, kmat_real_full, kmat_imag_full)
2084 : CALL dbcsr_create(kmat_real, template=rsmat(ispin, 1)%matrix, &
2085 6100 : matrix_type=dbcsr_type_symmetric)
2086 : CALL dbcsr_create(kmat_imag, template=rsmat(ispin, 1)%matrix, &
2087 6100 : matrix_type=dbcsr_type_antisymmetric)
2088 : CALL dbcsr_create(kmat_real_full, template=rsmat(ispin, 1)%matrix, &
2089 6100 : matrix_type=dbcsr_type_no_symmetry)
2090 : CALL dbcsr_create(kmat_imag_full, template=rsmat(ispin, 1)%matrix, &
2091 6100 : matrix_type=dbcsr_type_no_symmetry)
2092 6100 : CALL cp_dbcsr_alloc_block_from_nbl(kmat_real, sab_nl)
2093 6100 : CALL cp_dbcsr_alloc_block_from_nbl(kmat_imag, sab_nl)
2094 6100 : CALL dbcsr_set(kmat_real, 0.0_dp)
2095 6100 : CALL dbcsr_set(kmat_imag, 0.0_dp)
2096 : CALL rskp_transform(kmat_real, kmat_imag, rsmat=rsmat, ispin=ispin, &
2097 6100 : xkp=xkp, cell_to_index=cell_to_index, sab_nl=sab_nl)
2098 6100 : CALL dbcsr_desymmetrize(kmat_real, kmat_real_full)
2099 6100 : CALL dbcsr_desymmetrize(kmat_imag, kmat_imag_full)
2100 :
2101 : CALL cp_fm_struct_create(matrix_struct_ao, nrow_global=nao, ncol_global=nao, &
2102 : para_env=matrix_struct_coeff%para_env, &
2103 6100 : context=matrix_struct_coeff%context)
2104 6100 : CALL cp_fm_create(mat_real, matrix_struct_ao)
2105 6100 : CALL cp_fm_create(mat_imag, matrix_struct_ao)
2106 6100 : CALL copy_dbcsr_to_fm(kmat_real_full, mat_real)
2107 6100 : CALL copy_dbcsr_to_fm(kmat_imag_full, mat_imag)
2108 :
2109 6100 : CALL cp_cfm_create(kmat_cfm, matrix_struct_ao)
2110 6100 : CALL cp_cfm_create(coeff_cfm, matrix_struct_coeff)
2111 6100 : CALL cp_cfm_create(result_cfm, matrix_struct_coeff)
2112 6100 : CALL cp_fm_to_cfm(mat_real, mat_imag, kmat_cfm)
2113 6100 : CALL cp_fm_to_cfm(coeff_real, coeff_imag, coeff_cfm)
2114 : CALL cp_cfm_gemm("N", "N", nao, ncol, nao, CMPLX(1.0_dp, 0.0_dp, KIND=dp), kmat_cfm, &
2115 6100 : coeff_cfm, CMPLX(0.0_dp, 0.0_dp, KIND=dp), result_cfm)
2116 6100 : CALL cp_cfm_to_fm(result_cfm, result_real, result_imag)
2117 :
2118 6100 : CALL cp_fm_release(mat_real)
2119 6100 : CALL cp_fm_release(mat_imag)
2120 6100 : CALL cp_cfm_release(kmat_cfm)
2121 6100 : CALL cp_cfm_release(coeff_cfm)
2122 6100 : CALL cp_cfm_release(result_cfm)
2123 6100 : CALL cp_fm_struct_release(matrix_struct_ao)
2124 6100 : CALL dbcsr_deallocate_matrix(kmat_real)
2125 6100 : CALL dbcsr_deallocate_matrix(kmat_imag)
2126 6100 : CALL dbcsr_deallocate_matrix(kmat_real_full)
2127 6100 : CALL dbcsr_deallocate_matrix(kmat_imag_full)
2128 :
2129 6100 : END SUBROUTINE apply_wannier90_kp_matrix
2130 :
2131 : ! **************************************************************************************************
2132 : !> \brief Rayleigh-Ritz stabilize a symmetry-reconstructed Wannier90 MO subspace.
2133 : !> \param dst_real real part of transformed MO coefficients
2134 : !> \param dst_imag imaginary part of transformed MO coefficients
2135 : !> \param matrix_s real-space overlap matrix
2136 : !> \param matrix_ks real-space Kohn-Sham matrix
2137 : !> \param xkp target k-point coordinate
2138 : !> \param cell_to_index real-space cell index table
2139 : !> \param sab_nl overlap neighbor list
2140 : !> \param ispin spin index
2141 : !> \param eigenvalues Ritz eigenvalues of the stabilized subspace
2142 : !> \param degenerate_band_tol degeneracy threshold
2143 : !> \param success true if the subspace was stabilized
2144 : !> \param reason diagnostic message
2145 : !> \param aligned_blocks number of stabilized subspaces
2146 : !> \param aligned_max_size largest stabilized subspace
2147 : !> \param aligned_min_svalue smallest S(k)-metric eigenvalue
2148 : !> \param max_residual largest Ritz residual
2149 : ! **************************************************************************************************
2150 452 : SUBROUTINE ritz_stabilize_wannier90_subspace(dst_real, dst_imag, matrix_s, matrix_ks, &
2151 904 : xkp, cell_to_index, sab_nl, ispin, eigenvalues, &
2152 : degenerate_band_tol, success, reason, aligned_blocks, &
2153 : aligned_max_size, aligned_min_svalue, max_residual)
2154 : TYPE(cp_fm_type), INTENT(IN) :: dst_real, dst_imag
2155 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
2156 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
2157 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
2158 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
2159 : POINTER :: sab_nl
2160 : INTEGER, INTENT(IN) :: ispin
2161 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: eigenvalues
2162 : REAL(KIND=dp), INTENT(IN) :: degenerate_band_tol
2163 : LOGICAL, INTENT(OUT) :: success
2164 : CHARACTER(LEN=*), INTENT(OUT) :: reason
2165 : INTEGER, INTENT(OUT) :: aligned_blocks, aligned_max_size
2166 : REAL(KIND=dp), INTENT(OUT) :: aligned_min_svalue, max_residual
2167 :
2168 : REAL(KIND=dp), PARAMETER :: residual_tol = 1.0e-2_dp
2169 :
2170 452 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: block_coeff, h_block, h_coeff, &
2171 452 : h_projected, h_projected_work, metric_vectors, residual_block, ritz_vectors, s_block, &
2172 452 : s_coeff, s_projected, stabilized
2173 : INTEGER :: block_first, block_last, block_size, ib, &
2174 : nao, nmo
2175 : REAL(KIND=dp) :: metric_deviation, norm_value, &
2176 : residual_norm
2177 452 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: metric_values, ritz_values
2178 452 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: dst_i, dst_r, h_coeff_i, h_coeff_r, &
2179 452 : s_coeff_i, s_coeff_r
2180 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_metric
2181 : TYPE(cp_fm_type) :: h_dst_imag, h_dst_real, s_dst_imag, &
2182 : s_dst_real, tmp_fm
2183 :
2184 452 : success = .FALSE.
2185 452 : reason = ""
2186 452 : aligned_blocks = 0
2187 452 : aligned_max_size = 0
2188 452 : aligned_min_svalue = HUGE(1.0_dp)
2189 452 : max_residual = 0.0_dp
2190 :
2191 452 : NULLIFY (matrix_struct_metric)
2192 : CALL cp_fm_get_info(dst_real, nrow_global=nao, ncol_global=nmo, &
2193 452 : matrix_struct=matrix_struct_metric)
2194 452 : IF (SIZE(eigenvalues) < nmo) THEN
2195 0 : reason = "not enough eigenvalues for Wannier90 Ritz subspace stabilization"
2196 : RETURN
2197 : END IF
2198 :
2199 452 : CALL cp_fm_create(s_dst_real, matrix_struct_metric)
2200 452 : CALL cp_fm_create(s_dst_imag, matrix_struct_metric)
2201 452 : CALL cp_fm_create(h_dst_real, matrix_struct_metric)
2202 452 : CALL cp_fm_create(h_dst_imag, matrix_struct_metric)
2203 452 : CALL cp_fm_create(tmp_fm, matrix_struct_metric)
2204 :
2205 : CALL apply_wannier90_kp_matrix(matrix_s, 1, xkp, cell_to_index, sab_nl, &
2206 452 : dst_real, dst_imag, s_dst_real, s_dst_imag)
2207 : CALL apply_wannier90_kp_matrix(matrix_ks, ispin, xkp, cell_to_index, sab_nl, &
2208 452 : dst_real, dst_imag, h_dst_real, h_dst_imag)
2209 :
2210 0 : ALLOCATE (dst_r(nao, nmo), dst_i(nao, nmo), s_coeff_r(nao, nmo), s_coeff_i(nao, nmo), &
2211 6328 : h_coeff_r(nao, nmo), h_coeff_i(nao, nmo))
2212 452 : CALL cp_fm_get_submatrix(dst_real, dst_r)
2213 452 : CALL cp_fm_get_submatrix(dst_imag, dst_i)
2214 452 : CALL cp_fm_get_submatrix(s_dst_real, s_coeff_r)
2215 452 : CALL cp_fm_get_submatrix(s_dst_imag, s_coeff_i)
2216 452 : CALL cp_fm_get_submatrix(h_dst_real, h_coeff_r)
2217 452 : CALL cp_fm_get_submatrix(h_dst_imag, h_coeff_i)
2218 :
2219 2712 : ALLOCATE (s_coeff(nao, nmo), h_coeff(nao, nmo))
2220 86132 : s_coeff(:, :) = CMPLX(s_coeff_r, s_coeff_i, KIND=dp)
2221 86132 : h_coeff(:, :) = CMPLX(h_coeff_r, h_coeff_i, KIND=dp)
2222 :
2223 452 : block_first = 1
2224 1588 : DO WHILE (block_first <= nmo)
2225 : block_last = block_first
2226 1792 : DO WHILE (block_last < nmo)
2227 1340 : IF (ABS(eigenvalues(block_last + 1) - eigenvalues(block_last)) >= degenerate_band_tol) EXIT
2228 1136 : block_last = block_last + 1
2229 : END DO
2230 1136 : block_size = block_last - block_first + 1
2231 1136 : IF (block_size > 1) THEN
2232 : ! The atom/AO operation fixes the subspace, while the little-group gauge inside an
2233 : ! exactly degenerate manifold is arbitrary. Stabilize only that manifold and verify
2234 : ! that it is an invariant H(k),S(k) subspace before exporting it to Wannier90.
2235 0 : ALLOCATE (block_coeff(nao, block_size), h_block(nao, block_size), &
2236 0 : h_projected(block_size, block_size), h_projected_work(block_size, block_size), &
2237 0 : metric_vectors(block_size, block_size), residual_block(nao, block_size), &
2238 0 : ritz_vectors(block_size, block_size), s_block(nao, block_size), &
2239 0 : s_projected(block_size, block_size), stabilized(nao, block_size), &
2240 11232 : metric_values(block_size), ritz_values(block_size))
2241 : block_coeff(:, :) = CMPLX(dst_r(:, block_first:block_last), &
2242 59376 : dst_i(:, block_first:block_last), KIND=dp)
2243 59376 : s_block(:, :) = s_coeff(:, block_first:block_last)
2244 59376 : h_block(:, :) = h_coeff(:, block_first:block_last)
2245 933648 : s_projected(:, :) = MATMUL(CONJG(TRANSPOSE(block_coeff)), s_block)
2246 933648 : h_projected(:, :) = MATMUL(CONJG(TRANSPOSE(block_coeff)), h_block)
2247 8304 : s_projected(:, :) = 0.5_dp*(s_projected + CONJG(TRANSPOSE(s_projected)))
2248 8304 : h_projected(:, :) = 0.5_dp*(h_projected + CONJG(TRANSPOSE(h_projected)))
2249 :
2250 432 : CALL diag_complex(s_projected, metric_vectors, metric_values)
2251 1520 : aligned_min_svalue = MIN(aligned_min_svalue, MINVAL(metric_values))
2252 1520 : metric_deviation = MAXVAL(ABS(metric_values - 1.0_dp))
2253 1520 : IF (MINVAL(metric_values) < 1.0e-10_dp) THEN
2254 : WRITE (reason, "(A,I0,A,ES9.2,A,ES9.2)") &
2255 0 : "singular metric blk=", block_first, " smin=", MINVAL(metric_values), &
2256 0 : " dS=", metric_deviation
2257 0 : max_residual = HUGE(1.0_dp)
2258 0 : DEALLOCATE (block_coeff, h_block, h_projected, h_projected_work, metric_vectors, &
2259 0 : residual_block, ritz_vectors, s_block, s_projected, stabilized, &
2260 0 : metric_values, ritz_values)
2261 0 : DEALLOCATE (s_coeff, h_coeff, dst_r, dst_i, s_coeff_r, s_coeff_i, h_coeff_r, &
2262 0 : h_coeff_i)
2263 0 : CALL cp_fm_release(s_dst_real)
2264 0 : CALL cp_fm_release(s_dst_imag)
2265 0 : CALL cp_fm_release(h_dst_real)
2266 0 : CALL cp_fm_release(h_dst_imag)
2267 0 : CALL cp_fm_release(tmp_fm)
2268 0 : RETURN
2269 : END IF
2270 :
2271 1520 : DO ib = 1, block_size
2272 4368 : metric_vectors(:, ib) = metric_vectors(:, ib)/SQRT(metric_values(ib))
2273 : END DO
2274 50640 : h_projected_work(:, :) = MATMUL(h_projected, metric_vectors)
2275 50640 : h_projected(:, :) = MATMUL(CONJG(TRANSPOSE(metric_vectors)), h_projected_work)
2276 8304 : h_projected(:, :) = 0.5_dp*(h_projected + CONJG(TRANSPOSE(h_projected)))
2277 432 : CALL diag_complex(h_projected, ritz_vectors, ritz_values)
2278 50640 : h_projected_work(:, :) = MATMUL(metric_vectors, ritz_vectors)
2279 4368 : ritz_vectors(:, :) = h_projected_work
2280 623888 : stabilized(:, :) = MATMUL(block_coeff, ritz_vectors)
2281 623888 : residual_block(:, :) = MATMUL(h_block, ritz_vectors)
2282 59376 : h_block(:, :) = residual_block
2283 623888 : residual_block(:, :) = MATMUL(s_block, ritz_vectors)
2284 59376 : s_block(:, :) = residual_block
2285 1520 : DO ib = 1, block_size
2286 58944 : norm_value = SQRT(ABS(REAL(DOT_PRODUCT(stabilized(:, ib), s_block(:, ib)), KIND=dp)))
2287 1520 : IF (norm_value > EPSILON(1.0_dp)) THEN
2288 58944 : stabilized(:, ib) = stabilized(:, ib)/norm_value
2289 58944 : h_block(:, ib) = h_block(:, ib)/norm_value
2290 58944 : s_block(:, ib) = s_block(:, ib)/norm_value
2291 : END IF
2292 : END DO
2293 59376 : residual_block(:, :) = h_block
2294 1520 : DO ib = 1, block_size
2295 : residual_block(:, ib) = residual_block(:, ib) - &
2296 59376 : eigenvalues(block_first + ib - 1)*s_block(:, ib)
2297 : END DO
2298 59376 : residual_norm = MAXVAL(ABS(residual_block))
2299 432 : max_residual = MAX(max_residual, residual_norm)
2300 432 : IF (residual_norm > residual_tol) THEN
2301 : WRITE (reason, "(A,I0,A,ES9.2)") &
2302 0 : "blk=", block_first, " dS=", metric_deviation
2303 0 : DEALLOCATE (block_coeff, h_block, h_projected, h_projected_work, metric_vectors, &
2304 0 : residual_block, ritz_vectors, s_block, s_projected, stabilized, &
2305 0 : metric_values, ritz_values)
2306 0 : DEALLOCATE (s_coeff, h_coeff, dst_r, dst_i, s_coeff_r, s_coeff_i, h_coeff_r, &
2307 0 : h_coeff_i)
2308 0 : CALL cp_fm_release(s_dst_real)
2309 0 : CALL cp_fm_release(s_dst_imag)
2310 0 : CALL cp_fm_release(h_dst_real)
2311 0 : CALL cp_fm_release(h_dst_imag)
2312 0 : CALL cp_fm_release(tmp_fm)
2313 0 : RETURN
2314 : END IF
2315 :
2316 59376 : dst_r(:, block_first:block_last) = REAL(stabilized, KIND=dp)
2317 59376 : dst_i(:, block_first:block_last) = AIMAG(stabilized)
2318 59376 : h_coeff(:, block_first:block_last) = h_block
2319 59376 : s_coeff(:, block_first:block_last) = s_block
2320 432 : aligned_blocks = aligned_blocks + 1
2321 432 : aligned_max_size = MAX(aligned_max_size, block_size)
2322 0 : DEALLOCATE (block_coeff, h_block, h_projected, h_projected_work, metric_vectors, &
2323 0 : residual_block, ritz_vectors, s_block, s_projected, stabilized, metric_values, &
2324 432 : ritz_values)
2325 : END IF
2326 1136 : block_first = block_last + 1
2327 : END DO
2328 :
2329 2244 : DO ib = 1, nmo
2330 85680 : residual_norm = MAXVAL(ABS(h_coeff(:, ib) - eigenvalues(ib)*s_coeff(:, ib)))
2331 2244 : max_residual = MAX(max_residual, residual_norm)
2332 : END DO
2333 452 : IF (max_residual > residual_tol) THEN
2334 : WRITE (reason, "(A,ES10.3)") &
2335 0 : "atom/AO W90 reuse guarded: Ritz residual=", max_residual
2336 0 : DEALLOCATE (s_coeff, h_coeff, dst_r, dst_i, s_coeff_r, s_coeff_i, h_coeff_r, h_coeff_i)
2337 0 : CALL cp_fm_release(s_dst_real)
2338 0 : CALL cp_fm_release(s_dst_imag)
2339 0 : CALL cp_fm_release(h_dst_real)
2340 0 : CALL cp_fm_release(h_dst_imag)
2341 0 : CALL cp_fm_release(tmp_fm)
2342 0 : RETURN
2343 : END IF
2344 :
2345 452 : CALL cp_fm_set_submatrix(dst_real, dst_r)
2346 452 : CALL cp_fm_set_submatrix(dst_imag, dst_i)
2347 :
2348 452 : IF (aligned_blocks == 0) aligned_min_svalue = 0.0_dp
2349 452 : success = .TRUE.
2350 :
2351 452 : DEALLOCATE (s_coeff, h_coeff, dst_r, dst_i, s_coeff_r, s_coeff_i, h_coeff_r, h_coeff_i)
2352 452 : CALL cp_fm_release(s_dst_real)
2353 452 : CALL cp_fm_release(s_dst_imag)
2354 452 : CALL cp_fm_release(h_dst_real)
2355 452 : CALL cp_fm_release(h_dst_imag)
2356 452 : CALL cp_fm_release(tmp_fm)
2357 :
2358 1808 : END SUBROUTINE ritz_stabilize_wannier90_subspace
2359 :
2360 : ! **************************************************************************************************
2361 : !> \brief Reconstruct a Wannier90 export window from a larger symmetry-transformed SCF MO space.
2362 : !> \param src_real real part of the transformed source MO window
2363 : !> \param src_imag imaginary part of the transformed source MO window
2364 : !> \param dst_real real part of the exported reconstructed MO coefficients
2365 : !> \param dst_imag imaginary part of the exported reconstructed MO coefficients
2366 : !> \param matrix_s real-space overlap matrix
2367 : !> \param matrix_ks real-space Kohn-Sham matrix
2368 : !> \param xkp target k-point coordinate
2369 : !> \param cell_to_index real-space cell index table
2370 : !> \param sab_nl overlap neighbor list
2371 : !> \param ispin spin index
2372 : !> \param eigenvalues reconstructed target eigenvalues for the exported window
2373 : !> \param nmo_export number of MOs to export
2374 : !> \param success true if the reconstructed window is an invariant H(k),S(k) subspace
2375 : !> \param reason diagnostic message
2376 : !> \param min_svalue smallest S(k)-metric eigenvalue in the source window
2377 : !> \param max_residual largest target Ritz residual
2378 : ! **************************************************************************************************
2379 0 : SUBROUTINE ritz_reconstruct_wannier90_window(src_real, src_imag, dst_real, dst_imag, matrix_s, &
2380 : matrix_ks, xkp, cell_to_index, sab_nl, ispin, &
2381 0 : eigenvalues, nmo_export, success, reason, min_svalue, &
2382 : max_residual)
2383 : TYPE(cp_fm_type), INTENT(IN) :: src_real, src_imag, dst_real, dst_imag
2384 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
2385 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
2386 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
2387 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
2388 : POINTER :: sab_nl
2389 : INTEGER, INTENT(IN) :: ispin
2390 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: eigenvalues
2391 : INTEGER, INTENT(IN) :: nmo_export
2392 : LOGICAL, INTENT(OUT) :: success
2393 : CHARACTER(LEN=*), INTENT(OUT) :: reason
2394 : REAL(KIND=dp), INTENT(OUT) :: min_svalue, max_residual
2395 :
2396 : REAL(KIND=dp), PARAMETER :: eigenvalue_tol = 1.0e-6_dp, &
2397 : residual_tol = 1.0e-7_dp
2398 :
2399 0 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: coeff_work, h_coeff, h_projected, &
2400 0 : h_projected_work, metric_vectors, residual_block, ritz_vectors, s_coeff, s_projected, &
2401 0 : source_coeff, stabilized
2402 : INTEGER :: ib, nao, nmo_source
2403 : REAL(KIND=dp) :: max_eigenvalue_shift, metric_deviation, &
2404 : norm_value
2405 0 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: metric_values, ritz_values, &
2406 0 : source_eigenvalues
2407 0 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: dst_i, dst_r, h_coeff_i, h_coeff_r, &
2408 0 : s_coeff_i, s_coeff_r, src_i, src_r
2409 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_metric
2410 : TYPE(cp_fm_type) :: h_src_imag, h_src_real, s_src_imag, &
2411 : s_src_real, tmp_fm
2412 :
2413 0 : success = .FALSE.
2414 0 : reason = ""
2415 0 : min_svalue = HUGE(1.0_dp)
2416 0 : max_residual = HUGE(1.0_dp)
2417 :
2418 0 : NULLIFY (matrix_struct_metric)
2419 : CALL cp_fm_get_info(src_real, nrow_global=nao, ncol_global=nmo_source, &
2420 0 : matrix_struct=matrix_struct_metric)
2421 0 : IF (nmo_export > nmo_source) THEN
2422 0 : reason = "Wannier90 export window is larger than the transformed SCF MO space"
2423 0 : RETURN
2424 : END IF
2425 0 : IF (SIZE(eigenvalues) < nmo_export) THEN
2426 0 : reason = "not enough eigenvalue storage for Wannier90 source-window reconstruction"
2427 : RETURN
2428 : END IF
2429 :
2430 0 : CALL cp_fm_create(s_src_real, matrix_struct_metric)
2431 0 : CALL cp_fm_create(s_src_imag, matrix_struct_metric)
2432 0 : CALL cp_fm_create(h_src_real, matrix_struct_metric)
2433 0 : CALL cp_fm_create(h_src_imag, matrix_struct_metric)
2434 0 : CALL cp_fm_create(tmp_fm, matrix_struct_metric)
2435 :
2436 : CALL apply_wannier90_kp_matrix(matrix_s, 1, xkp, cell_to_index, sab_nl, &
2437 0 : src_real, src_imag, s_src_real, s_src_imag)
2438 : CALL apply_wannier90_kp_matrix(matrix_ks, ispin, xkp, cell_to_index, sab_nl, &
2439 0 : src_real, src_imag, h_src_real, h_src_imag)
2440 :
2441 0 : ALLOCATE (src_r(nao, nmo_source), src_i(nao, nmo_source), &
2442 0 : s_coeff_r(nao, nmo_source), s_coeff_i(nao, nmo_source), &
2443 0 : h_coeff_r(nao, nmo_source), h_coeff_i(nao, nmo_source), &
2444 0 : dst_r(nao, nmo_export), dst_i(nao, nmo_export))
2445 0 : CALL cp_fm_get_submatrix(src_real, src_r)
2446 0 : CALL cp_fm_get_submatrix(src_imag, src_i)
2447 0 : CALL cp_fm_get_submatrix(s_src_real, s_coeff_r)
2448 0 : CALL cp_fm_get_submatrix(s_src_imag, s_coeff_i)
2449 0 : CALL cp_fm_get_submatrix(h_src_real, h_coeff_r)
2450 0 : CALL cp_fm_get_submatrix(h_src_imag, h_coeff_i)
2451 :
2452 0 : ALLOCATE (source_coeff(nao, nmo_source), s_coeff(nao, nmo_source), &
2453 0 : h_coeff(nao, nmo_source), h_projected(nmo_source, nmo_source), &
2454 0 : h_projected_work(nmo_source, nmo_source), metric_vectors(nmo_source, nmo_source), &
2455 0 : residual_block(nao, nmo_export), ritz_vectors(nmo_source, nmo_source), &
2456 0 : s_projected(nmo_source, nmo_source), stabilized(nao, nmo_export), &
2457 0 : coeff_work(nao, nmo_source), metric_values(nmo_source), ritz_values(nmo_source), &
2458 0 : source_eigenvalues(nmo_export))
2459 0 : source_eigenvalues(1:nmo_export) = eigenvalues(1:nmo_export)
2460 0 : source_coeff(:, :) = CMPLX(src_r, src_i, KIND=dp)
2461 0 : s_coeff(:, :) = CMPLX(s_coeff_r, s_coeff_i, KIND=dp)
2462 0 : h_coeff(:, :) = CMPLX(h_coeff_r, h_coeff_i, KIND=dp)
2463 0 : s_projected(:, :) = MATMUL(CONJG(TRANSPOSE(source_coeff)), s_coeff)
2464 0 : h_projected(:, :) = MATMUL(CONJG(TRANSPOSE(source_coeff)), h_coeff)
2465 0 : s_projected(:, :) = 0.5_dp*(s_projected + CONJG(TRANSPOSE(s_projected)))
2466 0 : h_projected(:, :) = 0.5_dp*(h_projected + CONJG(TRANSPOSE(h_projected)))
2467 :
2468 : reconstruct_window: BLOCK
2469 0 : CALL diag_complex(s_projected, metric_vectors, metric_values)
2470 0 : min_svalue = MINVAL(metric_values)
2471 0 : metric_deviation = MAXVAL(ABS(metric_values - 1.0_dp))
2472 0 : IF (min_svalue < 1.0e-10_dp) THEN
2473 : WRITE (reason, "(A,ES9.2,A,ES9.2)") &
2474 0 : "singular expanded metric smin=", min_svalue, " dS=", metric_deviation
2475 0 : EXIT reconstruct_window
2476 : END IF
2477 :
2478 0 : DO ib = 1, nmo_source
2479 0 : metric_vectors(:, ib) = metric_vectors(:, ib)/SQRT(metric_values(ib))
2480 : END DO
2481 0 : h_projected_work(:, :) = MATMUL(h_projected, metric_vectors)
2482 0 : h_projected(:, :) = MATMUL(CONJG(TRANSPOSE(metric_vectors)), h_projected_work)
2483 0 : h_projected(:, :) = 0.5_dp*(h_projected + CONJG(TRANSPOSE(h_projected)))
2484 0 : CALL diag_complex(h_projected, ritz_vectors, ritz_values)
2485 0 : h_projected_work(:, :) = MATMUL(metric_vectors, ritz_vectors)
2486 0 : ritz_vectors(:, :) = h_projected_work
2487 0 : stabilized(:, :) = MATMUL(source_coeff, ritz_vectors(:, 1:nmo_export))
2488 0 : coeff_work(:, :) = MATMUL(h_coeff, ritz_vectors)
2489 0 : h_coeff(:, :) = coeff_work
2490 0 : coeff_work(:, :) = MATMUL(s_coeff, ritz_vectors)
2491 0 : s_coeff(:, :) = coeff_work
2492 0 : DO ib = 1, nmo_export
2493 0 : norm_value = SQRT(ABS(REAL(DOT_PRODUCT(stabilized(:, ib), s_coeff(:, ib)), KIND=dp)))
2494 0 : IF (norm_value > EPSILON(1.0_dp)) THEN
2495 0 : stabilized(:, ib) = stabilized(:, ib)/norm_value
2496 0 : h_coeff(:, ib) = h_coeff(:, ib)/norm_value
2497 0 : s_coeff(:, ib) = s_coeff(:, ib)/norm_value
2498 : END IF
2499 : END DO
2500 0 : residual_block(:, :) = h_coeff(:, 1:nmo_export)
2501 0 : DO ib = 1, nmo_export
2502 0 : residual_block(:, ib) = residual_block(:, ib) - ritz_values(ib)*s_coeff(:, ib)
2503 : END DO
2504 0 : max_residual = MAXVAL(ABS(residual_block))
2505 0 : IF (max_residual > residual_tol) THEN
2506 : WRITE (reason, "(A,ES9.2)") &
2507 0 : "expanded dS=", metric_deviation
2508 0 : EXIT reconstruct_window
2509 : END IF
2510 0 : max_eigenvalue_shift = MAXVAL(ABS(ritz_values(1:nmo_export) - source_eigenvalues(1:nmo_export)))
2511 0 : IF (max_eigenvalue_shift > eigenvalue_tol) THEN
2512 : WRITE (reason, "(A,ES9.2)") &
2513 0 : "expanded dS=", metric_deviation
2514 0 : EXIT reconstruct_window
2515 : END IF
2516 :
2517 0 : dst_r(:, :) = REAL(stabilized, KIND=dp)
2518 0 : dst_i(:, :) = AIMAG(stabilized)
2519 0 : CALL cp_fm_set_submatrix(dst_real, dst_r)
2520 0 : CALL cp_fm_set_submatrix(dst_imag, dst_i)
2521 0 : success = .TRUE.
2522 :
2523 : END BLOCK reconstruct_window
2524 :
2525 0 : DEALLOCATE (source_coeff, s_coeff, h_coeff, h_projected, h_projected_work, metric_vectors, &
2526 0 : residual_block, ritz_vectors, s_projected, stabilized, coeff_work, metric_values, &
2527 0 : ritz_values, source_eigenvalues)
2528 0 : DEALLOCATE (src_r, src_i, s_coeff_r, s_coeff_i, h_coeff_r, h_coeff_i, dst_r, dst_i)
2529 0 : CALL cp_fm_release(s_src_real)
2530 0 : CALL cp_fm_release(s_src_imag)
2531 0 : CALL cp_fm_release(h_src_real)
2532 0 : CALL cp_fm_release(h_src_imag)
2533 0 : CALL cp_fm_release(tmp_fm)
2534 :
2535 0 : END SUBROUTINE ritz_reconstruct_wannier90_window
2536 :
2537 : ! **************************************************************************************************
2538 : !> \brief Map the full Wannier90 mesh to SCF representative k-points and symmetry operations.
2539 : !> \param kpoint full Wannier90 export k-point object
2540 : !> \param qs_kpoint SCF k-point object
2541 : !> \param source_kpoint source representative index for each full k-point
2542 : !> \param sym_index symmetry entry in source kp_sym; 0 direct, -1 time reversal only
2543 : !> \param success true if every full k-point was mapped
2544 : !> \param reason diagnostic message
2545 : ! **************************************************************************************************
2546 42 : SUBROUTINE build_wannier90_scf_mapping(kpoint, qs_kpoint, source_kpoint, sym_index, success, &
2547 : reason)
2548 : TYPE(kpoint_type), POINTER :: kpoint, qs_kpoint
2549 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: source_kpoint, sym_index
2550 : LOGICAL, INTENT(OUT) :: success
2551 : CHARACTER(LEN=*), INTENT(OUT) :: reason
2552 :
2553 : INTEGER :: ik, ikred, imatch, isym, nfull
2554 : TYPE(kpoint_sym_type), POINTER :: kpsym
2555 :
2556 42 : success = .FALSE.
2557 42 : reason = ""
2558 42 : nfull = kpoint%nkp
2559 168 : ALLOCATE (source_kpoint(nfull), sym_index(nfull))
2560 42 : source_kpoint(:) = 0
2561 42 : sym_index(:) = 0
2562 :
2563 142 : DO ikred = 1, qs_kpoint%nkp
2564 100 : imatch = find_matching_kpoint(kpoint%xkp, qs_kpoint%xkp(1:3, ikred))
2565 142 : IF (imatch > 0 .AND. source_kpoint(imatch) == 0) THEN
2566 100 : source_kpoint(imatch) = ikred
2567 100 : sym_index(imatch) = 0
2568 : END IF
2569 : END DO
2570 :
2571 : ! Prefer pure time-reversal partners before general atom/AO symmetry operations.
2572 142 : DO ikred = 1, qs_kpoint%nkp
2573 400 : imatch = find_matching_kpoint(kpoint%xkp, -qs_kpoint%xkp(1:3, ikred))
2574 142 : IF (imatch > 0 .AND. source_kpoint(imatch) == 0) THEN
2575 68 : source_kpoint(imatch) = ikred
2576 68 : sym_index(imatch) = -1
2577 : END IF
2578 : END DO
2579 :
2580 42 : IF (ASSOCIATED(qs_kpoint%kp_sym)) THEN
2581 142 : DO ikred = 1, qs_kpoint%nkp
2582 100 : kpsym => qs_kpoint%kp_sym(ikred)%kpoint_sym
2583 100 : IF (.NOT. ASSOCIATED(kpsym)) CYCLE
2584 100 : IF (.NOT. kpsym%apply_symmetry) CYCLE
2585 5702 : DO isym = 1, kpsym%nwred
2586 5600 : imatch = find_matching_kpoint(kpoint%xkp, kpsym%xkp(1:3, isym))
2587 5700 : IF (imatch > 0 .AND. source_kpoint(imatch) == 0) THEN
2588 604 : source_kpoint(imatch) = ikred
2589 604 : sym_index(imatch) = isym
2590 : END IF
2591 : END DO
2592 : END DO
2593 : END IF
2594 :
2595 814 : DO ik = 1, nfull
2596 814 : IF (source_kpoint(ik) == 0) THEN
2597 0 : reason = "not all full-mesh k-points are represented by the SCF symmetry orbits"
2598 0 : RETURN
2599 : END IF
2600 : END DO
2601 42 : success = .TRUE.
2602 :
2603 42 : END SUBROUTINE build_wannier90_scf_mapping
2604 :
2605 : ! **************************************************************************************************
2606 : !> \brief Find a fractional k-point in a periodic mesh.
2607 : !> \param xkp_mesh mesh coordinates
2608 : !> \param xkp_search coordinate to find
2609 : !> \return matching index, or zero when no match is found
2610 : ! **************************************************************************************************
2611 5800 : INTEGER FUNCTION find_matching_kpoint(xkp_mesh, xkp_search) RESULT(ik_match)
2612 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: xkp_mesh
2613 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp_search
2614 :
2615 : INTEGER :: ik
2616 :
2617 5800 : ik_match = 0
2618 113800 : DO ik = 1, SIZE(xkp_mesh, 2)
2619 113800 : IF (kpoint_same_periodic(xkp_mesh(1:3, ik), xkp_search)) THEN
2620 5800 : ik_match = ik
2621 5800 : RETURN
2622 : END IF
2623 : END DO
2624 :
2625 : END FUNCTION find_matching_kpoint
2626 :
2627 : ! **************************************************************************************************
2628 : !> \brief Infer a tensor-product Wannier90 mesh from explicit fractional k-point coordinates.
2629 : !> \param kpt_latt explicit k-point coordinates in reciprocal-lattice units
2630 : !> \param mp_grid inferred mesh dimensions
2631 : !> \param valid true if the coordinate set is compatible with a tensor-product mesh
2632 : ! **************************************************************************************************
2633 6 : SUBROUTINE infer_wannier_mp_grid(kpt_latt, mp_grid, valid)
2634 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: kpt_latt
2635 : INTEGER, DIMENSION(3), INTENT(OUT) :: mp_grid
2636 : LOGICAL, INTENT(OUT) :: valid
2637 :
2638 : INTEGER :: coord_id, i, idim, idx, n_unique, &
2639 : num_kpts, stride, unique_id
2640 : LOGICAL :: known
2641 6 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: seen
2642 : REAL(KIND=dp) :: coord
2643 6 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: unique_coord
2644 :
2645 6 : num_kpts = SIZE(kpt_latt, 2)
2646 6 : mp_grid(:) = 0
2647 18 : ALLOCATE (unique_coord(3, num_kpts))
2648 24 : DO idim = 1, 3
2649 : n_unique = 0
2650 162 : DO i = 1, num_kpts
2651 144 : coord = kpt_latt(idim, i) - FLOOR(kpt_latt(idim, i))
2652 144 : IF (ABS(coord - 1.0_dp) < 1.0e-8_dp) coord = 0.0_dp
2653 144 : known = .FALSE.
2654 216 : DO unique_id = 1, n_unique
2655 216 : IF (ABS(unique_coord(idim, unique_id) - coord) < 1.0e-8_dp) THEN
2656 : known = .TRUE.
2657 : EXIT
2658 : END IF
2659 : END DO
2660 162 : IF (.NOT. known) THEN
2661 36 : n_unique = n_unique + 1
2662 36 : unique_coord(idim, n_unique) = coord
2663 : END IF
2664 : END DO
2665 24 : mp_grid(idim) = n_unique
2666 : END DO
2667 6 : valid = (mp_grid(1)*mp_grid(2)*mp_grid(3) == num_kpts)
2668 6 : IF (valid) THEN
2669 18 : ALLOCATE (seen(num_kpts))
2670 6 : seen(:) = .FALSE.
2671 54 : DO i = 1, num_kpts
2672 : idx = 1
2673 : stride = 1
2674 192 : DO idim = 1, 3
2675 144 : coord = kpt_latt(idim, i) - FLOOR(kpt_latt(idim, i))
2676 144 : IF (ABS(coord - 1.0_dp) < 1.0e-8_dp) coord = 0.0_dp
2677 144 : coord_id = 0
2678 216 : DO unique_id = 1, mp_grid(idim)
2679 216 : IF (ABS(unique_coord(idim, unique_id) - coord) < 1.0e-8_dp) THEN
2680 : coord_id = unique_id
2681 : EXIT
2682 : END IF
2683 : END DO
2684 144 : CPASSERT(coord_id > 0)
2685 144 : idx = idx + (coord_id - 1)*stride
2686 192 : stride = stride*mp_grid(idim)
2687 : END DO
2688 48 : IF (seen(idx)) valid = .FALSE.
2689 54 : seen(idx) = .TRUE.
2690 : END DO
2691 54 : valid = valid .AND. ALL(seen)
2692 6 : DEALLOCATE (seen)
2693 : END IF
2694 6 : DEALLOCATE (unique_coord)
2695 :
2696 6 : END SUBROUTINE infer_wannier_mp_grid
2697 :
2698 0 : END MODULE qs_wannier90
|