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 basis_set_types, ONLY: get_gto_basis_set,&
17 : gto_basis_set_type
18 : USE bibliography, ONLY: Gresch2017,&
19 : Soluyanov2011,&
20 : cite_reference
21 : USE cell_types, ONLY: cell_type,&
22 : get_cell
23 : USE cp_blacs_env, ONLY: cp_blacs_env_type
24 : USE cp_cfm_types, ONLY: &
25 : cp_cfm_copy_general, cp_cfm_create, cp_cfm_get_element, cp_cfm_get_info, &
26 : cp_cfm_get_submatrix, cp_cfm_release, cp_cfm_set_submatrix, cp_cfm_to_cfm, cp_cfm_type, &
27 : cp_fm_to_cfm
28 : USE cp_control_types, ONLY: dft_control_type
29 : USE cp_dbcsr_api, ONLY: &
30 : dbcsr_add, dbcsr_create, dbcsr_deallocate_matrix, dbcsr_desymmetrize, dbcsr_get_block_p, &
31 : dbcsr_p_type, dbcsr_scale, dbcsr_set, dbcsr_type, dbcsr_type_antisymmetric, &
32 : dbcsr_type_no_symmetry, dbcsr_type_symmetric
33 : USE cp_dbcsr_cp2k_link, ONLY: cp_dbcsr_alloc_block_from_nbl
34 : USE cp_dbcsr_operations, ONLY: copy_dbcsr_to_fm,&
35 : dbcsr_deallocate_matrix_set
36 : USE cp_files, ONLY: close_file,&
37 : open_file
38 : USE cp_fm_struct, ONLY: cp_fm_struct_create,&
39 : cp_fm_struct_release,&
40 : cp_fm_struct_type
41 : USE cp_fm_types, ONLY: cp_fm_create,&
42 : cp_fm_release,&
43 : cp_fm_type
44 : USE cp_log_handling, ONLY: cp_logger_get_default_io_unit,&
45 : cp_logger_type
46 : USE input_section_types, ONLY: section_vals_get,&
47 : section_vals_get_subs_vals,&
48 : section_vals_type,&
49 : section_vals_val_get
50 : USE kinds, ONLY: default_path_length,&
51 : default_string_length,&
52 : dp
53 : USE kpoint_methods, ONLY: kpoint_env_initialize,&
54 : kpoint_init_cell_index,&
55 : kpoint_initialize,&
56 : kpoint_initialize_mo_set,&
57 : kpoint_initialize_mos,&
58 : rskp_transform
59 : USE kpoint_mo_symmetry_methods, ONLY: kpoint_same_periodic,&
60 : kpoint_transform_scf_mo
61 : USE kpoint_types, ONLY: get_kpoint_info,&
62 : kpoint_create,&
63 : kpoint_env_type,&
64 : kpoint_release,&
65 : kpoint_sym_type,&
66 : kpoint_type
67 : USE machine, ONLY: m_timestamp,&
68 : timestamp_length
69 : USE mathconstants, ONLY: twopi
70 : USE mathlib, ONLY: diag_complex
71 : USE message_passing, ONLY: mp_para_env_type
72 : USE parallel_gemm_api, ONLY: parallel_gemm
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_kind_types, ONLY: get_qs_kind,&
81 : qs_kind_type
82 : USE qs_mo_types, ONLY: get_mo_set,&
83 : mo_set_type
84 : USE qs_moments, ONLY: build_berry_kpoint_matrix
85 : USE qs_neighbor_list_types, ONLY: get_iterator_info,&
86 : get_neighbor_list_set_p,&
87 : neighbor_list_iterate,&
88 : neighbor_list_iterator_create,&
89 : neighbor_list_iterator_p_type,&
90 : neighbor_list_iterator_release,&
91 : neighbor_list_set_p_type
92 : USE qs_scf_diagonalization, ONLY: do_general_diag_kp
93 : USE qs_scf_types, ONLY: qs_scf_env_type
94 : USE scf_control_types, ONLY: scf_control_type
95 : USE soc_pseudopotential_methods, ONLY: V_SOC_xyz_from_pseudopotential
96 : USE topology_inversion, ONLY: gaussian_inversion_action
97 : USE topology_state_io, ONLY: topology_state_begin,&
98 : topology_state_point
99 : USE topology_symmetry, ONLY: inversion_representation
100 : USE topology_tqc, ONLY: inversion_ebr_signature,&
101 : inversion_indicators
102 : USE topology_wilson, ONLY: chern_from_wcc,&
103 : surface_resolved,&
104 : wcc_distance,&
105 : wilson_spectrum,&
106 : wilson_step,&
107 : z2_from_wcc
108 : USE wannier90, ONLY: wannier_setup
109 : USE wannier90_nnkpts, ONLY: read_wannier90_nnkpts
110 : USE wannier90_projections, ONLY: wannier90_hybrid_candidates,&
111 : wannier90_select_projections,&
112 : wannier90_window_mask
113 : #include "./base/base_uses.f90"
114 :
115 : IMPLICIT NONE
116 : PRIVATE
117 :
118 : CHARACTER(LEN=12), PARAMETER :: window_keywords(4) = &
119 : [CHARACTER(LEN=12) :: 'DIS_WIN_MIN', 'DIS_WIN_MAX', 'DIS_FROZ_MIN', 'DIS_FROZ_MAX']
120 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_wannier90'
121 : INTEGER, PARAMETER, PRIVATE :: w90_kpoints_mp_grid = 0, &
122 : w90_kpoints_scf = 1, w90_kpoints_nnkp = 2, w90_kpoints_wilson = 3, &
123 : w90_kpoints_trim = 4
124 :
125 : TYPE berry_matrix_type
126 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: sinmat => NULL(), cosmat => NULL()
127 : END TYPE berry_matrix_type
128 :
129 : PUBLIC :: wannier90_interface, prepare_wannier90_scf_mos
130 :
131 : ! **************************************************************************************************
132 :
133 : CONTAINS
134 :
135 : ! **************************************************************************************************
136 : !> \brief ...
137 : !> \param input ...
138 : !> \param logger ...
139 : !> \param qs_env ...
140 : ! **************************************************************************************************
141 12967 : SUBROUTINE wannier90_interface(input, logger, qs_env)
142 : TYPE(section_vals_type), POINTER :: input
143 : TYPE(cp_logger_type), POINTER :: logger
144 : TYPE(qs_environment_type), POINTER :: qs_env
145 :
146 : CHARACTER(len=*), PARAMETER :: routineN = 'wannier90_interface'
147 :
148 : INTEGER :: first_spin, handle, i, ichern, ispin, &
149 : iw, iz2, last_spin, max_refinement, &
150 : old_chern, old_z2, refinement, &
151 : selection, source
152 : LOGICAL :: converged, explicit, require_chern, &
153 : require_z2
154 : REAL(KIND=dp) :: error, movement, tolerance
155 12967 : REAL(KIND=dp), ALLOCATABLE :: centres(:, :), previous(:, :)
156 : TYPE(dft_control_type), POINTER :: dft_control
157 : TYPE(section_vals_type), POINTER :: w_input
158 :
159 : !--------------------------------------------------------------------------------------------!
160 :
161 12967 : CALL timeset(routineN, handle)
162 : w_input => section_vals_get_subs_vals(section_vals=input, &
163 12967 : subsection_name="DFT%PRINT%WANNIER90")
164 12967 : CALL section_vals_get(w_input, explicit=explicit)
165 12967 : IF (explicit) THEN
166 :
167 58 : iw = cp_logger_get_default_io_unit(logger)
168 :
169 58 : IF (iw > 0) THEN
170 : WRITE (iw, '(/,T2,A)') &
171 29 : '!-----------------------------------------------------------------------------!'
172 29 : WRITE (iw, '(T32,A)') "Interface to Wannier90"
173 : WRITE (iw, '(T2,A)') &
174 29 : '!-----------------------------------------------------------------------------!'
175 : END IF
176 :
177 58 : CALL get_qs_env(qs_env, dft_control=dft_control)
178 58 : first_spin = 1
179 58 : last_spin = dft_control%nspins
180 58 : IF (last_spin /= 1 .AND. last_spin /= 2) CPABORT('Wannier90 requires collinear spin channels.')
181 58 : CALL section_vals_val_get(w_input, 'SPIN_CHANNEL', i_val=selection)
182 58 : IF (selection /= 0) THEN
183 4 : IF (selection > last_spin) CPABORT('The requested Wannier90 spin channel is not present.')
184 : first_spin = selection
185 : last_spin = selection
186 : END IF
187 118 : DO ispin = first_spin, last_spin
188 60 : CALL section_vals_val_get(w_input, "KPOINTS_SOURCE", i_val=source)
189 60 : CALL section_vals_val_get(w_input, "WILSON_MAX_REFINEMENT", i_val=max_refinement)
190 60 : CALL section_vals_val_get(w_input, "WILSON_TOL", r_val=tolerance)
191 60 : CALL section_vals_val_get(w_input, "Z2", l_val=require_z2)
192 60 : CALL section_vals_val_get(w_input, "CHERN", l_val=require_chern)
193 60 : IF (tolerance <= 0.0_dp) CPABORT("WILSON_TOL must be positive.")
194 60 : IF (source == w90_kpoints_wilson) THEN
195 6 : IF (max_refinement < 1 .OR. max_refinement > 10) THEN
196 0 : CPABORT("WILSON_MAX_REFINEMENT must be between 1 and 10.")
197 : END IF
198 6 : converged = .FALSE.
199 6 : old_z2 = -1
200 6 : old_chern = HUGE(0)
201 12 : DO refinement = 0, max_refinement
202 12 : CALL wannier90_files(qs_env, w_input, iw, ispin, refinement, centres, iz2, ichern)
203 12 : IF (refinement > 0) THEN
204 6 : error = 0.0_dp
205 24 : DO i = 1, SIZE(previous, 2)
206 24 : error = MAX(error, wcc_distance(previous(:, i), centres(:, 2*i - 1)))
207 : END DO
208 6 : movement = 0.0_dp
209 30 : DO i = 2, SIZE(centres, 2)
210 30 : movement = MAX(movement, wcc_distance(centres(:, i - 1), centres(:, i)))
211 : END DO
212 6 : IF (iw > 0) WRITE (iw, '(T2,A,I0,A,ES12.4,A,ES12.4)') &
213 3 : "TOPOLOGY| Refinement ", refinement, ": WCC change ", error, ", transverse step ", movement
214 6 : converged = error < tolerance .AND. movement < 0.1_dp .AND. iz2 == old_z2
215 6 : IF (require_z2) converged = converged .AND. iz2 >= 0 .AND. surface_resolved(centres)
216 6 : IF (require_chern) converged = converged .AND. ichern /= HUGE(0) .AND. ichern == old_chern
217 4 : IF (converged) EXIT
218 : END IF
219 6 : CALL MOVE_ALLOC(centres, previous)
220 6 : old_z2 = iz2
221 10 : old_chern = ichern
222 : END DO
223 6 : IF (.NOT. converged) THEN
224 0 : CPABORT("Wilson surface unconverged; increase WILSON_MAX_REFINEMENT or mesh.")
225 : END IF
226 6 : IF (iw > 0) THEN
227 3 : WRITE (iw, '(T2,A)') "TOPOLOGY| Wilson surface sampling converged."
228 3 : IF (iz2 >= 0) WRITE (iw, '(T2,A,I0)') "TOPOLOGY| Converged Z2 invariant: ", iz2
229 3 : IF (require_chern) WRITE (iw, '(T2,A,I0)') "TOPOLOGY| Converged first Chern number: ", ichern
230 : END IF
231 : ELSE
232 54 : CALL wannier90_files(qs_env, w_input, iw, ispin, 0, centres, iz2, ichern)
233 : END IF
234 178 : IF (ALLOCATED(previous)) DEALLOCATE (previous)
235 : END DO
236 :
237 58 : IF (iw > 0) THEN
238 : WRITE (iw, '(/,T2,A)') &
239 29 : '!--------------------------------End of Wannier90-----------------------------!'
240 : END IF
241 : END IF
242 12967 : CALL timestop(handle)
243 :
244 12967 : END SUBROUTINE wannier90_interface
245 :
246 : ! **************************************************************************************************
247 : !> \brief Copies a k-point CFM into the all-k-point Wannier work distribution.
248 : !> \param source source CFM; it may be unassociated on ranks outside its k-point group
249 : !> \param destination destination CFM
250 : !> \param para_env communicator spanning the source and destination distributions
251 : ! **************************************************************************************************
252 6468 : SUBROUTINE wannier90_copy_cfm_general(source, destination, para_env)
253 : TYPE(cp_cfm_type), INTENT(IN), POINTER :: source
254 : TYPE(cp_cfm_type), INTENT(IN) :: destination
255 : TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env
256 :
257 : TYPE(cp_cfm_type) :: empty_source
258 :
259 6468 : IF (ASSOCIATED(source)) THEN
260 6468 : CALL cp_cfm_copy_general(source, destination, para_env)
261 : ELSE
262 0 : CALL cp_cfm_copy_general(empty_source, destination, para_env)
263 : END IF
264 :
265 6468 : END SUBROUTINE wannier90_copy_cfm_general
266 :
267 : ! **************************************************************************************************
268 : !> \brief ...
269 : !> \param qs_env ...
270 : !> \param input ...
271 : !> \param iw ...
272 : !> \param ispin selected spin channel
273 : !> \param refinement number of joint mesh doublings
274 : !> \param wcc_out Wilson centres for each closed loop
275 : !> \param z2_value candidate Z2 parity, or -1 when not requested
276 : !> \param chern_value candidate first Chern number, or HUGE(0) when unavailable
277 : ! **************************************************************************************************
278 66 : SUBROUTINE wannier90_files(qs_env, input, iw, ispin, refinement, wcc_out, z2_value, chern_value)
279 : TYPE(qs_environment_type), POINTER :: qs_env
280 : TYPE(section_vals_type), POINTER :: input
281 : INTEGER, INTENT(IN) :: iw, ispin, refinement
282 : REAL(KIND=dp), ALLOCATABLE, INTENT(OUT) :: wcc_out(:, :)
283 : INTEGER, INTENT(OUT) :: z2_value, chern_value
284 :
285 : INTEGER, PARAMETER :: num_nnmax = 12
286 :
287 : CHARACTER(len=2) :: asym
288 66 : CHARACTER(len=20), ALLOCATABLE, DIMENSION(:) :: atom_symbols
289 : CHARACTER(len=default_path_length) :: nnkp_file
290 : CHARACTER(len=default_string_length) :: filename, input_kp_scheme, reuse_reason, &
291 : seed_name
292 : CHARACTER(LEN=timestamp_length) :: timestamp
293 : COMPLEX(KIND=dp) :: zmmn
294 66 : COMPLEX(KIND=dp), ALLOCATABLE :: ao_projections(:, :, :), export_coeff(:, :), &
295 66 : export_scalar(:, :), hybrid_projections(:, :, :), initial_amn(:, :, :), &
296 66 : link_matrix(:, :), parity_metric(:, :), parity_phase(:), reference_mo(:, :, :), &
297 66 : scalar_overlap(:, :), soc_h(:, :), soc_u(:, :), soc_xyz(:, :, :), spinor_coeff(:, :, :), &
298 66 : wilson_product(:, :, :)
299 : INTEGER :: aligned_degenerate_blocks, aligned_degenerate_max_size, axis, base_mesh(2), &
300 : counts(2), first_point, i, i_rep, ib, ib1, ib2, ibs, ik, ik2, ikk, ikpgr, iloop, &
301 : initial_projections, ipoint, iunit, ix, iy, iz, jpar, k, kept_index, kpoints_source, &
302 : loop_direction(3), n_rep, nadd, nao, nberry_images, nbs, nelectron, nexcl, nkp, nloop, &
303 : nmo, nntot, npoint, nscalar, num_atoms, num_bands, num_bands_tot, num_kpts, num_wann, &
304 : spin_channel, state_components, state_unit, status, strong, tqc_dimension, trim_id, &
305 : weak(3), z4
306 66 : INTEGER, ALLOCATABLE :: ebr_coefficients(:, :), hybrid_groups(:, :), loop_index(:), &
307 66 : parity_map(:), parity_odd(:), projection_pivots(:)
308 66 : INTEGER, ALLOCATABLE, DIMENSION(:) :: band_indices, exclude_bands
309 66 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: nblist, nnlist
310 66 : INTEGER, ALLOCATABLE, DIMENSION(:, :, :) :: nncell
311 : INTEGER, DIMENSION(2) :: kp_range
312 : INTEGER, DIMENSION(3) :: input_nkp_grid, mp_grid
313 66 : INTEGER, DIMENSION(:), POINTER :: invals
314 66 : INTEGER, DIMENSION(:, :, :), POINTER :: berry_cell_index, cell_to_index
315 : LOGICAL :: diis_step, do_chern, do_kpoints, do_parity, do_soc, do_tqc, do_wilson, do_z2, &
316 : export_state, full_mesh_diagonalized, input_full_grid, input_gamma_centered, &
317 : input_kpoint_symmetry, lowest_bands, mp_grid_explicit, mp_grid_valid, my_kpgrp, mygrp, &
318 : nonnegative_atomic, ordered_berry, require_global_gap, reuse_scf_mos, reused_scf_mos, &
319 : signed_atomic, time_reversal, use_bloch_phases, validate_reuse_ok, &
320 : validate_reuse_scf_mos, window_set(4)
321 66 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: allowed_band, keep_band
322 : REAL(KIND=dp) :: aligned_degenerate_min_svalue, berry_phase, chern_winding, cmmn, &
323 : conduction_min, direct_gap, energy_window(4), gap_tol, gauge_arg, gauge_imag, gauge_real, &
324 : gauge_tmp, ksign, link_sv, loop_origin(3), pair_gap, parity_checks(4), &
325 : parity_energy_error, parity_energy_tolerance, parity_error, parity_gap, parity_origin(3), &
326 : parity_tolerance, projection_min_svalue, reuse_candidate_deviation, &
327 : reuse_candidate_metric_deviation, reuse_candidate_min_svalue, reuse_candidate_residual, &
328 : rmmn, transverse(3), valence_max, validation_eigenvalue_deviation, validation_min_svalue
329 : REAL(KIND=dp) :: validation_subspace_deviation, wkp_ref
330 66 : REAL(KIND=dp), ALLOCATABLE :: loop_sv(:), parity_energies(:), &
331 66 : projection_eigenvalues(:, :), &
332 66 : scalar_values(:), spinor_values(:, :)
333 66 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigval
334 66 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: atoms_cart, b_latt, kpt_latt, &
335 66 : reference_eigenvalues
336 : REAL(KIND=dp), DIMENSION(3) :: bvec, input_kp_shift, phase_center
337 : REAL(KIND=dp), DIMENSION(3, 3) :: h_inv, real_lattice, recip_lattice
338 66 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues, wkp, wkp_source
339 66 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: xkp, xkp_source
340 66 : REAL(KIND=dp), POINTER :: rvals(:)
341 66 : TYPE(berry_matrix_type), DIMENSION(:), POINTER :: berry_matrix
342 : TYPE(cell_type), POINTER :: cell
343 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
344 : TYPE(cp_cfm_type) :: fmk1_cfm, fmk2_cfm, mmn_cfm, omat_cfm, &
345 : tmp_cfm
346 : TYPE(cp_cfm_type), POINTER :: cmo_coeff_source
347 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_ao, matrix_struct_mmn, &
348 : matrix_struct_work
349 : TYPE(cp_fm_type) :: mat_imag, mat_real
350 66 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_ks, matrix_s, soc_matrices
351 : TYPE(dbcsr_type), POINTER :: cmatrix, cmatrix_full, loop_imag, &
352 : loop_real, rmatrix, rmatrix_full
353 : TYPE(dft_control_type), POINTER :: dft_control
354 : TYPE(kpoint_env_type), POINTER :: kp
355 : TYPE(kpoint_type), POINTER :: berry_kpoint, kpoint, qs_kpoint
356 66 : TYPE(mo_set_type), DIMENSION(:), POINTER :: mos
357 : TYPE(mp_para_env_type), POINTER :: para_env
358 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
359 66 : POINTER :: overlap_nl, sab_nl
360 66 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
361 : TYPE(qs_environment_type), POINTER :: qs_env_kp
362 : TYPE(qs_scf_env_type), POINTER :: scf_env
363 : TYPE(scf_control_type), POINTER :: scf_control
364 :
365 : !--------------------------------------------------------------------------------------------!
366 :
367 : ! generate all arrays needed for the setup call
368 66 : CALL section_vals_val_get(input, "SEED_NAME", c_val=seed_name)
369 66 : CALL get_qs_env(qs_env, dft_control=dft_control)
370 66 : IF (dft_control%nspins == 2) THEN
371 6 : IF (LEN_TRIM(seed_name) + 5 > LEN(seed_name)) THEN
372 0 : CPABORT('Wannier90 seed is too long for the spin suffix.')
373 : END IF
374 6 : IF (ispin == 1) seed_name = TRIM(seed_name)//'_up'
375 6 : IF (ispin == 2) seed_name = TRIM(seed_name)//'_down'
376 6 : IF (iw > 0) WRITE (iw, '(T2,A,I0,2A)') 'WANNIER90| Spin channel ', ispin, ' file prefix ', TRIM(seed_name)
377 : END IF
378 66 : CALL section_vals_val_get(input, "MP_GRID", i_vals=invals, explicit=mp_grid_explicit)
379 66 : CALL section_vals_val_get(input, "KPOINTS_SOURCE", i_val=kpoints_source)
380 66 : ordered_berry = kpoints_source >= w90_kpoints_nnkp
381 66 : CALL section_vals_val_get(input, "NNKP_FILE", c_val=nnkp_file)
382 66 : spin_channel = ispin
383 66 : CALL section_vals_val_get(input, "WILSON_LOOP", l_val=do_wilson)
384 66 : CALL section_vals_val_get(input, "Z2", l_val=do_z2)
385 66 : CALL section_vals_val_get(input, "CHERN", l_val=do_chern)
386 66 : CALL section_vals_val_get(input, "REQUIRE_GLOBAL_GAP", l_val=require_global_gap)
387 66 : CALL section_vals_val_get(input, "STATE_EXPORT", l_val=export_state)
388 66 : CALL section_vals_val_get(input, "PARITY", l_val=do_parity)
389 66 : CALL section_vals_val_get(input, "INVERSION_TQC", l_val=do_tqc)
390 66 : CALL section_vals_val_get(input, "TQC_DIMENSION", i_val=tqc_dimension)
391 66 : CALL section_vals_val_get(input, "PARITY_ORIGIN", r_vals=rvals)
392 264 : parity_origin = rvals
393 66 : CALL section_vals_val_get(input, "PARITY_TOLERANCE", r_val=parity_tolerance)
394 66 : CALL section_vals_val_get(input, "PARITY_ENERGY_TOL", r_val=parity_energy_tolerance)
395 66 : do_parity = do_parity .OR. do_tqc .OR. kpoints_source == w90_kpoints_trim
396 66 : IF (do_parity .AND. .NOT. ordered_berry) THEN
397 0 : CPABORT("PARITY requires explicit NNKP, WILSON or TRIM points.")
398 : END IF
399 66 : IF (tqc_dimension < 2 .OR. tqc_dimension > 3) CPABORT("TQC_DIMENSION must be 2 or 3.")
400 66 : IF (parity_tolerance <= 0.0_dp .OR. parity_energy_tolerance <= 0.0_dp) THEN
401 0 : CPABORT("Invalid parity tolerance.")
402 : END IF
403 66 : IF (export_state .AND. .NOT. ordered_berry) THEN
404 0 : CPABORT("STATE_EXPORT requires explicit NNKP, WILSON or TRIM points.")
405 : END IF
406 66 : CALL section_vals_val_get(input, "TIME_REVERSAL", l_val=time_reversal)
407 66 : CALL section_vals_val_get(input, "SOC", l_val=do_soc)
408 66 : IF (do_tqc .AND. (.NOT. do_soc .OR. .NOT. time_reversal)) THEN
409 0 : CPABORT("INVERSION_TQC requires SOC T and TIME_REVERSAL T.")
410 : END IF
411 66 : CALL section_vals_val_get(input, "WILSON_GAP_TOL", r_val=gap_tol)
412 66 : IF (gap_tol <= 0.0_dp) CPABORT("WILSON_GAP_TOL must be positive.")
413 66 : z2_value = -1
414 66 : chern_value = HUGE(0)
415 66 : do_wilson = do_wilson .OR. kpoints_source == w90_kpoints_wilson
416 66 : IF (do_wilson .AND. kpoints_source == w90_kpoints_trim) THEN
417 0 : CPABORT("TRIM points are not Wilson loops; use KPOINTS_SOURCE WILSON.")
418 : END IF
419 66 : IF (do_wilson) CALL cite_reference(Gresch2017)
420 66 : IF (do_z2) CALL cite_reference(Soluyanov2011)
421 66 : IF ((do_wilson .OR. do_soc) .AND. kpoints_source < w90_kpoints_nnkp) THEN
422 0 : CPABORT("Native Wilson/SOC requires KPOINTS_SOURCE NNKP or WILSON.")
423 : END IF
424 66 : IF (do_z2 .AND. (kpoints_source /= w90_kpoints_wilson .OR. .NOT. do_soc .OR. .NOT. time_reversal)) THEN
425 0 : CPABORT("Z2 requires KPOINTS_SOURCE WILSON, SOC T, and TIME_REVERSAL T.")
426 : END IF
427 66 : IF (do_chern .AND. (kpoints_source /= w90_kpoints_wilson .OR. do_z2)) THEN
428 0 : CPABORT("CHERN requires a full WILSON surface and cannot be combined with Z2.")
429 : END IF
430 66 : IF (require_global_gap .AND. .NOT. do_wilson) THEN
431 0 : CPABORT("REQUIRE_GLOBAL_GAP requires Wilson analysis.")
432 : END IF
433 66 : CALL get_qs_env(qs_env, dft_control=dft_control)
434 66 : IF (spin_channel < 1 .OR. spin_channel > dft_control%nspins) THEN
435 0 : CPABORT("WANNIER90%SPIN_CHANNEL is not available in this calculation.")
436 : END IF
437 66 : IF (do_soc .AND. dft_control%nspins /= 1) THEN
438 0 : CPABORT("WANNIER90 SOC currently requires a restricted SCF.")
439 : END IF
440 66 : CALL section_vals_val_get(input, "WANNIER_FUNCTIONS", n_rep_val=n_rep)
441 66 : IF (n_rep > 1 .AND. n_rep /= dft_control%nspins) THEN
442 0 : CPABORT('Specify WANNIER_FUNCTIONS once, or once per spin channel.')
443 : END IF
444 66 : i_rep = 1
445 66 : IF (n_rep > 1) i_rep = ispin
446 66 : CALL section_vals_val_get(input, "WANNIER_FUNCTIONS", i_rep_val=i_rep, i_val=num_wann)
447 66 : CALL section_vals_val_get(input, "ADDED_MOS", i_val=nadd)
448 66 : CALL section_vals_val_get(input, "REUSE_SCF_MOS", l_val=reuse_scf_mos)
449 66 : CALL section_vals_val_get(input, "VALIDATE_REUSE_SCF_MOS", l_val=validate_reuse_scf_mos)
450 66 : CALL section_vals_val_get(input, "USE_BLOCH_PHASES", l_val=use_bloch_phases)
451 66 : CALL section_vals_val_get(input, "INITIAL_PROJECTIONS", i_val=initial_projections)
452 66 : IF (initial_projections == -1 .AND. use_bloch_phases) initial_projections = 0
453 66 : energy_window = [-HUGE(0.0_dp)/100, HUGE(0.0_dp)/100, -HUGE(0.0_dp)/100, HUGE(0.0_dp)/100]
454 330 : DO i = 1, 4
455 264 : CALL section_vals_val_get(input, TRIM(window_keywords(i)), explicit=window_set(i))
456 330 : IF (window_set(i)) CALL section_vals_val_get(input, TRIM(window_keywords(i)), r_val=energy_window(i))
457 : END DO
458 322 : IF (ordered_berry .AND. (initial_projections > 0 .OR. ANY(window_set))) THEN
459 0 : CPABORT("AO projections and energy windows require KPOINTS_SOURCE MP_GRID or SCF.")
460 : END IF
461 66 : IF (window_set(3) .AND. .NOT. window_set(4)) CPABORT("DIS_FROZ_MIN requires DIS_FROZ_MAX.")
462 66 : IF (energy_window(1) > energy_window(2) .OR. energy_window(3) > energy_window(4)) THEN
463 0 : CPABORT("Invalid Wannier90 energy window.")
464 : END IF
465 66 : reuse_scf_mos = reuse_scf_mos .AND. kpoints_source == w90_kpoints_scf
466 66 : validate_reuse_scf_mos = validate_reuse_scf_mos .AND. reuse_scf_mos
467 264 : mp_grid(1:3) = invals(1:3)
468 : ! excluded bands
469 66 : CALL section_vals_val_get(input, "EXCLUDE_BANDS", n_rep_val=n_rep)
470 66 : nexcl = 0
471 92 : DO i_rep = 1, n_rep
472 26 : CALL section_vals_val_get(input, "EXCLUDE_BANDS", i_rep_val=i_rep, i_vals=invals)
473 92 : nexcl = nexcl + SIZE(invals)
474 : END DO
475 66 : IF (nexcl > 0) THEN
476 72 : ALLOCATE (exclude_bands(nexcl))
477 24 : nexcl = 0
478 50 : DO i_rep = 1, n_rep
479 26 : CALL section_vals_val_get(input, "EXCLUDE_BANDS", i_rep_val=i_rep, i_vals=invals)
480 144 : exclude_bands(nexcl + 1:nexcl + SIZE(invals)) = invals(:)
481 50 : nexcl = nexcl + SIZE(invals)
482 : END DO
483 : END IF
484 : !
485 : ! lattice -> Angstrom
486 66 : CALL get_qs_env(qs_env, cell=cell)
487 66 : CALL get_cell(cell, h=real_lattice, h_inv=h_inv)
488 : ! k-points
489 66 : CALL get_qs_env(qs_env, particle_set=particle_set)
490 66 : CALL get_qs_env(qs_env, para_env=para_env)
491 66 : phase_center = 0.0_dp
492 302 : DO i = 1, SIZE(particle_set)
493 3842 : phase_center(1:3) = phase_center(1:3) + MATMUL(h_inv, particle_set(i)%r)
494 : END DO
495 264 : phase_center(1:3) = phase_center(1:3)/REAL(SIZE(particle_set), KIND=dp)
496 264 : phase_center(1:3) = phase_center(1:3) - FLOOR(phase_center(1:3))
497 66 : recip_lattice(1:3, 1:3) = h_inv(1:3, 1:3)
498 858 : real_lattice(1:3, 1:3) = angstrom*real_lattice(1:3, 1:3)
499 1650 : recip_lattice(1:3, 1:3) = (twopi/angstrom)*TRANSPOSE(recip_lattice(1:3, 1:3))
500 66 : NULLIFY (kpoint, qs_kpoint, xkp, wkp, xkp_source, wkp_source)
501 66 : CALL get_qs_env(qs_env, do_kpoints=do_kpoints, kpoints=qs_kpoint)
502 66 : input_kpoint_symmetry = .FALSE.
503 66 : input_full_grid = .FALSE.
504 66 : input_kp_scheme = ""
505 66 : IF (do_kpoints .AND. ASSOCIATED(qs_kpoint)) THEN
506 : CALL get_kpoint_info(qs_kpoint, kp_scheme=input_kp_scheme, nkp_grid=input_nkp_grid, &
507 : kp_shift=input_kp_shift, symmetry=input_kpoint_symmetry, &
508 : full_grid=input_full_grid, gamma_centered=input_gamma_centered, &
509 66 : nkp=nkp, xkp=xkp, wkp=wkp)
510 : END IF
511 66 : CALL kpoint_create(kpoint)
512 :
513 22 : SELECT CASE (kpoints_source)
514 : CASE (w90_kpoints_nnkp, w90_kpoints_wilson, w90_kpoints_trim)
515 22 : IF (kpoints_source == w90_kpoints_nnkp) THEN
516 4 : CALL read_wannier90_nnkpts(nnkp_file, real_lattice, recip_lattice, kpt_latt, nnlist, nncell)
517 18 : ELSE IF (kpoints_source == w90_kpoints_trim) THEN
518 6 : num_kpts = 2**tqc_dimension
519 42 : ALLOCATE (kpt_latt(3, num_kpts), nnlist(num_kpts, 1), nncell(3, num_kpts, 1))
520 6 : kpt_latt = 0.0_dp
521 6 : nncell = 0
522 38 : DO i = 1, num_kpts
523 112 : DO axis = 1, tqc_dimension
524 112 : IF (BTEST(i - 1, axis - 1)) kpt_latt(axis, i) = 0.5_dp
525 : END DO
526 38 : nnlist(i, 1) = i
527 : END DO
528 : ELSE
529 12 : CALL section_vals_val_get(input, "WILSON_MESH", i_vals=invals)
530 36 : base_mesh = invals
531 12 : IF (base_mesh(1) < 2 .OR. base_mesh(2) < 2) THEN
532 0 : CPABORT("WILSON_MESH entries must be at least two.")
533 : END IF
534 12 : npoint = base_mesh(1)*2**refinement
535 12 : nloop = (base_mesh(2) - 1)*2**refinement + 1
536 12 : CALL section_vals_val_get(input, "WILSON_DIRECTION", i_vals=invals)
537 48 : loop_direction = invals
538 12 : IF (ALL(loop_direction == 0)) CPABORT("WILSON_DIRECTION must be nonzero.")
539 12 : CALL section_vals_val_get(input, "WILSON_ORIGIN", r_vals=rvals)
540 48 : loop_origin = rvals
541 12 : CALL section_vals_val_get(input, "WILSON_TRANSVERSE", r_vals=rvals)
542 48 : transverse = rvals
543 : bvec = [loop_direction(2)*transverse(3) - loop_direction(3)*transverse(2), &
544 : loop_direction(3)*transverse(1) - loop_direction(1)*transverse(3), &
545 48 : loop_direction(1)*transverse(2) - loop_direction(2)*transverse(1)]
546 48 : IF (SUM(bvec**2) < 1.e-20_dp) THEN
547 0 : CPABORT("Wilson loop and transverse vectors must be linearly independent.")
548 : END IF
549 48 : IF (do_chern .AND. MAXVAL(ABS(transverse - NINT(transverse))) > 1.e-10_dp) THEN
550 0 : CPABORT("CHERN requires integer WILSON_TRANSVERSE: a closed full surface, not a half-plane.")
551 : END IF
552 12 : IF (do_z2) THEN
553 28 : IF (MAXVAL(ABS(2*loop_origin - NINT(2*loop_origin))) > 1.e-10_dp .OR. &
554 : MAXVAL(ABS(2*transverse - NINT(2*transverse))) > 1.e-10_dp) THEN
555 0 : CPABORT("Z2 surface boundaries must pass through time-reversal-invariant momenta.")
556 : END IF
557 : ! Initially restrict native Z2 to standard half-planes, avoiding multiple coverings.
558 : IF (SUM(ABS(loop_direction)) /= 1 .OR. &
559 40 : ABS(SUM(ABS(transverse)) - 0.5_dp) > 1.e-10_dp .OR. &
560 : ABS(DOT_PRODUCT(REAL(loop_direction, dp), transverse)) > 1.e-10_dp) THEN
561 0 : CPABORT("Native Z2 requires distinct coordinate axes with windings one and one half.")
562 : END IF
563 : END IF
564 12 : num_kpts = npoint*nloop
565 84 : ALLOCATE (kpt_latt(3, num_kpts), nnlist(num_kpts, 1), nncell(3, num_kpts, 1))
566 12 : nncell = 0
567 60 : DO iloop = 1, nloop
568 48 : first_point = (iloop - 1)*npoint + 1
569 360 : DO ipoint = 1, npoint
570 312 : i = first_point + ipoint - 1
571 : kpt_latt(:, i) = loop_origin + REAL(iloop - 1, dp)/REAL(nloop - 1, dp)*transverse + &
572 1248 : REAL(ipoint - 1, dp)/REAL(npoint, dp)*loop_direction
573 360 : nnlist(i, 1) = i + 1
574 : END DO
575 48 : nnlist(i, 1) = first_point
576 204 : nncell(:, i, 1) = loop_direction
577 : END DO
578 : END IF
579 22 : num_kpts = SIZE(kpt_latt, 2)
580 22 : nntot = SIZE(nnlist, 2)
581 22 : kpoint%kp_scheme = "GENERAL"
582 22 : kpoint%symmetry = .FALSE.
583 22 : kpoint%verbose = .FALSE.
584 22 : kpoint%full_grid = .TRUE.
585 22 : kpoint%eps_geo = 1.0e-6_dp
586 22 : kpoint%use_real_wfn = .FALSE.
587 22 : kpoint%parallel_group_size = para_env%num_pe
588 22 : kpoint%nkp = num_kpts
589 110 : ALLOCATE (kpoint%xkp(3, num_kpts), kpoint%wkp(num_kpts))
590 1462 : kpoint%xkp = kpt_latt
591 : ! Export weights do not enter the converged SCF density.
592 382 : kpoint%wkp = 1.0_dp/REAL(num_kpts, KIND=dp)
593 22 : IF (iw > 0) WRITE (iw, '(T2,A,I0,A,I0)') &
594 11 : "WANNIER90| Explicit points: ", num_kpts, ", neighbours per point: ", nntot
595 : CASE (w90_kpoints_mp_grid)
596 0 : num_kpts = mp_grid(1)*mp_grid(2)*mp_grid(3)
597 0 : ALLOCATE (kpt_latt(3, num_kpts))
598 0 : kpoint%kp_scheme = "MONKHORST-PACK"
599 0 : kpoint%symmetry = .FALSE.
600 0 : kpoint%nkp_grid(1:3) = mp_grid(1:3)
601 0 : kpoint%verbose = .FALSE.
602 0 : kpoint%full_grid = .TRUE.
603 0 : kpoint%eps_geo = 1.0e-6_dp
604 0 : kpoint%use_real_wfn = .FALSE.
605 0 : kpoint%parallel_group_size = para_env%num_pe
606 0 : i = 0
607 0 : DO ix = 0, mp_grid(1) - 1
608 0 : DO iy = 0, mp_grid(2) - 1
609 0 : DO iz = 0, mp_grid(3) - 1
610 0 : i = i + 1
611 0 : kpt_latt(1, i) = REAL(ix, KIND=dp)/REAL(mp_grid(1), KIND=dp)
612 0 : kpt_latt(2, i) = REAL(iy, KIND=dp)/REAL(mp_grid(2), KIND=dp)
613 0 : kpt_latt(3, i) = REAL(iz, KIND=dp)/REAL(mp_grid(3), KIND=dp)
614 : END DO
615 : END DO
616 : END DO
617 0 : kpoint%nkp = num_kpts
618 0 : ALLOCATE (kpoint%xkp(3, num_kpts), kpoint%wkp(num_kpts))
619 0 : kpoint%wkp(:) = 1._dp/REAL(num_kpts, KIND=dp)
620 : ! rskp_transform uses reduced coordinates in exp(2*pi*i*k.R), with integer cell shifts R.
621 0 : kpoint%xkp(:, :) = kpt_latt(:, :)
622 :
623 : CASE (w90_kpoints_scf)
624 44 : IF (.NOT. do_kpoints .OR. .NOT. ASSOCIATED(qs_kpoint)) THEN
625 0 : CPABORT("WANNIER90%KPOINTS_SOURCE SCF requires an active DFT%KPOINTS section.")
626 : END IF
627 44 : SELECT CASE (TRIM(input_kp_scheme))
628 : CASE ("GAMMA")
629 0 : mp_grid(:) = 1
630 0 : num_kpts = 1
631 0 : ALLOCATE (kpt_latt(3, num_kpts))
632 0 : kpt_latt(1:3, 1) = 0.0_dp
633 0 : kpoint%kp_scheme = "GAMMA"
634 0 : kpoint%symmetry = .FALSE.
635 0 : kpoint%verbose = .FALSE.
636 0 : kpoint%full_grid = .TRUE.
637 0 : kpoint%eps_geo = 1.0e-6_dp
638 0 : kpoint%use_real_wfn = .FALSE.
639 0 : kpoint%parallel_group_size = para_env%num_pe
640 0 : kpoint%nkp = num_kpts
641 0 : ALLOCATE (kpoint%xkp(3, num_kpts), kpoint%wkp(num_kpts))
642 0 : kpoint%xkp(1:3, 1) = 0.0_dp
643 0 : kpoint%wkp(1) = 1.0_dp
644 :
645 : CASE ("MONKHORST-PACK", "MACDONALD")
646 38 : mp_grid(1:3) = input_nkp_grid(1:3)
647 38 : kpoint%kp_scheme = input_kp_scheme
648 38 : kpoint%symmetry = .FALSE.
649 152 : kpoint%nkp_grid(1:3) = input_nkp_grid(1:3)
650 152 : kpoint%kp_shift(1:3) = input_kp_shift(1:3)
651 38 : kpoint%gamma_centered = input_gamma_centered
652 38 : kpoint%verbose = .FALSE.
653 38 : kpoint%full_grid = .TRUE.
654 38 : kpoint%eps_geo = 1.0e-6_dp
655 38 : kpoint%use_real_wfn = .FALSE.
656 38 : kpoint%parallel_group_size = para_env%num_pe
657 38 : CALL kpoint_initialize(kpoint, particle_set, cell)
658 38 : num_kpts = kpoint%nkp
659 114 : ALLOCATE (kpt_latt(3, num_kpts))
660 2150 : kpt_latt(1:3, 1:num_kpts) = kpoint%xkp(1:3, 1:num_kpts)
661 38 : IF (input_kpoint_symmetry .AND. .NOT. input_full_grid .AND. iw > 0) THEN
662 : WRITE (iw, '(T2,A)') &
663 18 : "WANNIER90| SCF k-points are symmetry-reduced; regenerating the full SCF mesh."
664 18 : IF (reuse_scf_mos) THEN
665 : WRITE (iw, '(T2,A)') &
666 17 : "WANNIER90| CP2K will try to reconstruct the full-mesh MOs from the SCF orbitals."
667 : ELSE
668 : WRITE (iw, '(T2,A)') &
669 1 : "WANNIER90| The full exported mesh is diagonalized for the Wannier90 files."
670 : END IF
671 : END IF
672 :
673 : CASE ("GENERAL")
674 6 : IF (ASSOCIATED(qs_kpoint%xkp_input)) THEN
675 6 : xkp_source => qs_kpoint%xkp_input
676 6 : wkp_source => qs_kpoint%wkp_input
677 : ELSE
678 0 : xkp_source => xkp
679 0 : wkp_source => wkp
680 : END IF
681 6 : IF (.NOT. ASSOCIATED(xkp_source) .OR. .NOT. ASSOCIATED(wkp_source)) THEN
682 0 : CPABORT("Could not access the SCF GENERAL k-point set for the Wannier90 export.")
683 : END IF
684 6 : num_kpts = SIZE(wkp_source)
685 18 : ALLOCATE (kpt_latt(3, num_kpts))
686 198 : kpt_latt(1:3, 1:num_kpts) = xkp_source(1:3, 1:num_kpts)
687 6 : IF (mp_grid_explicit) THEN
688 0 : IF (mp_grid(1)*mp_grid(2)*mp_grid(3) /= num_kpts) THEN
689 0 : CPABORT("WANNIER90%MP_GRID must contain exactly as many points as the SCF GENERAL mesh.")
690 : END IF
691 : ELSE
692 6 : CALL infer_wannier_mp_grid(kpt_latt, mp_grid, mp_grid_valid)
693 6 : IF (.NOT. mp_grid_valid) THEN
694 0 : CPABORT("Could not infer WANNIER90%MP_GRID from the SCF GENERAL mesh.")
695 : END IF
696 : END IF
697 6 : wkp_ref = 1.0_dp/REAL(num_kpts, KIND=dp)
698 54 : DO i = 1, num_kpts
699 54 : IF (ABS(wkp_source(i) - wkp_ref) > 1.0e-10_dp) THEN
700 0 : CPABORT("WANNIER90%KPOINTS_SOURCE SCF requires equally weighted GENERAL k-points.")
701 : END IF
702 : END DO
703 6 : kpoint%kp_scheme = "GENERAL"
704 6 : kpoint%symmetry = .FALSE.
705 24 : kpoint%nkp_grid(1:3) = mp_grid(1:3)
706 6 : kpoint%verbose = .FALSE.
707 6 : kpoint%full_grid = .TRUE.
708 6 : kpoint%eps_geo = 1.0e-6_dp
709 6 : kpoint%use_real_wfn = .FALSE.
710 6 : kpoint%parallel_group_size = para_env%num_pe
711 6 : kpoint%nkp = num_kpts
712 30 : ALLOCATE (kpoint%xkp(3, num_kpts), kpoint%wkp(num_kpts))
713 390 : kpoint%xkp(1:3, 1:num_kpts) = xkp_source(1:3, 1:num_kpts)
714 54 : kpoint%wkp(1:num_kpts) = wkp_ref
715 6 : IF (input_kpoint_symmetry .AND. .NOT. input_full_grid .AND. iw > 0) THEN
716 : WRITE (iw, '(T2,A)') &
717 2 : "WANNIER90| SCF k-points are symmetry-reduced; using the full input GENERAL mesh."
718 2 : IF (reuse_scf_mos) THEN
719 : WRITE (iw, '(T2,A)') &
720 2 : "WANNIER90| CP2K will try to reconstruct the full-mesh MOs from the SCF orbitals."
721 : ELSE
722 : WRITE (iw, '(T2,A)') &
723 0 : "WANNIER90| The full exported mesh is diagonalized for the Wannier90 files."
724 : END IF
725 : END IF
726 :
727 : CASE DEFAULT
728 44 : CPABORT("WANNIER90%KPOINTS_SOURCE SCF does not support this DFT%KPOINTS scheme.")
729 : END SELECT
730 : CASE DEFAULT
731 66 : CPABORT("Unknown WANNIER90%KPOINTS_SOURCE setting.")
732 : END SELECT
733 : ! number of bands in calculation
734 66 : CALL get_qs_env(qs_env, mos=mos)
735 66 : CALL get_mo_set(mo_set=mos(ispin), nao=nao, nmo=num_bands_tot, nelectron=nelectron)
736 66 : num_bands_tot = MIN(nao, num_bands_tot + nadd)
737 66 : nscalar = num_bands_tot
738 66 : IF (do_soc) num_bands_tot = 2*nscalar
739 198 : ALLOCATE (keep_band(num_bands_tot))
740 418 : keep_band = .TRUE.
741 184 : DO i = 1, nexcl
742 118 : ib = exclude_bands(i)
743 118 : IF (ib < 1 .OR. ib > num_bands_tot) CPABORT("WANNIER90%EXCLUDE_BANDS: index out of range.")
744 118 : IF (ordered_berry .AND. .NOT. keep_band(ib)) THEN
745 0 : CPABORT("WANNIER90%EXCLUDE_BANDS: duplicate band index.")
746 : END IF
747 184 : keep_band(ib) = .FALSE.
748 : END DO
749 418 : num_bands = COUNT(keep_band)
750 66 : IF (num_bands < 1 .OR. num_wann > num_bands) THEN
751 0 : CPABORT("WANNIER90 EXCLUDE_BANDS leaves too few bands for WANNIER_FUNCTIONS.")
752 : END IF
753 198 : ALLOCATE (band_indices(num_bands))
754 770 : band_indices(:) = PACK([(i, i=1, num_bands_tot)], keep_band)
755 530 : lowest_bands = num_bands < num_bands_tot .AND. ALL(band_indices == [(i, i=1, num_bands)])
756 66 : IF (require_global_gap .AND. .NOT. lowest_bands) THEN
757 0 : CPABORT("REQUIRE_GLOBAL_GAP needs a lowest-band prefix and an excluded band above it.")
758 : END IF
759 66 : IF (do_z2) THEN
760 4 : IF (num_bands /= nelectron) THEN
761 0 : CPABORT("Z2 requires one occupied spinor per electron; adjust EXCLUDE_BANDS.")
762 : END IF
763 4 : IF (num_bands == num_bands_tot .OR. MOD(num_bands, 2) /= 0) THEN
764 0 : CPABORT("Z2 needs an even occupied spinor subspace and at least one excluded conduction band.")
765 : END IF
766 68 : IF (ANY(band_indices /= [(i, i=1, num_bands)])) THEN
767 0 : CPABORT("Z2 requires the lowest occupied spinor bands.")
768 : END IF
769 : END IF
770 66 : IF (do_wilson) THEN
771 16 : IF (nntot /= 1) CPABORT("WILSON_LOOP requires exactly one directed neighbour per point.")
772 48 : ALLOCATE (loop_index(num_kpts))
773 68 : i = 1
774 68 : nloop = 0
775 68 : DO WHILE (i <= num_kpts)
776 52 : first_point = i
777 52 : nloop = nloop + 1
778 : DO
779 328 : loop_index(i) = nloop
780 328 : IF (nnlist(i, 1) == first_point) EXIT
781 276 : IF (nnlist(i, 1) /= i + 1 .OR. i == num_kpts) THEN
782 0 : CPABORT("WILSON_LOOP requires contiguous ordered closed loops.")
783 : END IF
784 52 : i = i + 1
785 : END DO
786 52 : i = i + 1
787 : END DO
788 112 : ALLOCATE (wilson_product(num_bands, num_bands, nloop), loop_sv(nloop))
789 16 : wilson_product = CMPLX(0.0_dp, 0.0_dp, dp)
790 60 : DO i = 1, num_bands
791 224 : wilson_product(i, i, :) = CMPLX(1.0_dp, 0.0_dp, dp)
792 : END DO
793 68 : loop_sv = 1.0_dp
794 64 : ALLOCATE (wcc_out(num_bands, nloop))
795 : ELSE
796 50 : ALLOCATE (wcc_out(0, 0))
797 : END IF
798 264 : ALLOCATE (link_matrix(num_bands, num_bands))
799 66 : IF (initial_projections == 0 .AND. num_wann /= num_bands .AND. .NOT. ordered_berry) THEN
800 0 : CPABORT("IDENTITY projections require WANNIER_FUNCTIONS to match the number of bands.")
801 : END IF
802 66 : IF (initial_projections >= 0 .AND. .NOT. ordered_berry .AND. num_wann < 1) THEN
803 0 : CPABORT("Initial projections require a positive WANNIER_FUNCTIONS count.")
804 : END IF
805 322 : IF (ANY(window_set) .AND. num_wann == num_bands) THEN
806 0 : CPABORT("Energy windows require more bands than Wannier functions.")
807 : END IF
808 322 : IF (ANY(window_set) .AND. num_wann < 1) THEN
809 0 : CPABORT("Energy windows require a positive WANNIER_FUNCTIONS count.")
810 : END IF
811 198 : ALLOCATE (allowed_band(num_bands))
812 328 : IF (ANY(window_set)) ALLOCATE (projection_eigenvalues(num_bands, num_kpts))
813 66 : num_atoms = SIZE(particle_set)
814 198 : ALLOCATE (atoms_cart(3, num_atoms))
815 198 : ALLOCATE (atom_symbols(num_atoms))
816 302 : DO i = 1, num_atoms
817 944 : atoms_cart(1:3, i) = particle_set(i)%r(1:3)
818 236 : CALL get_atomic_kind(particle_set(i)%atomic_kind, element_symbol=asym)
819 302 : atom_symbols(i) = asym
820 : END DO
821 66 : IF (kpoints_source < w90_kpoints_nnkp) THEN
822 264 : ALLOCATE (nnlist(num_kpts, num_nnmax), nncell(3, num_kpts, num_nnmax))
823 44 : nnlist(:, :) = 0
824 44 : nncell(:, :, :) = 0
825 44 : nntot = 0
826 44 : IF (iw > 0) THEN
827 : CALL wannier_setup(mp_grid, num_kpts, real_lattice, recip_lattice, &
828 22 : kpt_latt, nntot, nnlist, nncell, iw)
829 : END IF
830 44 : CALL para_env%sum(nntot)
831 44 : CALL para_env%sum(nnlist)
832 44 : CALL para_env%sum(nncell)
833 : END IF
834 :
835 66 : IF (para_env%is_source() .AND. kpoints_source < w90_kpoints_nnkp) THEN
836 : ! Write the ordinary file-export input "seed_name.win".
837 22 : WRITE (filename, '(A,A)') TRIM(seed_name), ".win"
838 22 : CALL open_file(filename, unit_number=iunit, file_status="REPLACE", file_action="WRITE")
839 : !
840 22 : CALL m_timestamp(timestamp)
841 22 : WRITE (iunit, "(A)") "! Wannier90 input file generated by CP2K "
842 22 : WRITE (iunit, "(A,/)") "! Creation date "//timestamp
843 : !
844 22 : WRITE (iunit, "(A,I5)") "num_wann = ", num_wann
845 22 : IF (num_bands /= num_wann .OR. use_bloch_phases) THEN
846 20 : WRITE (iunit, "(A,I5)") "num_bands = ", num_bands
847 : END IF
848 110 : DO i = 1, 4
849 110 : IF (window_set(i)) WRITE (iunit, '(2A,ES24.16)') TRIM(window_keywords(i)), ' = ', energy_window(i)*evolt
850 : END DO
851 22 : WRITE (iunit, "(/,A,/)") "length_unit = bohr"
852 22 : WRITE (iunit, "(/,A,/)") "! System"
853 22 : WRITE (iunit, "(/,A)") "begin unit_cell_cart"
854 22 : WRITE (iunit, "(A)") "bohr"
855 88 : DO i = 1, 3
856 286 : WRITE (iunit, "(3ES24.16)") cell%hmat(1:3, i)
857 : END DO
858 22 : WRITE (iunit, "(A,/)") "end unit_cell_cart"
859 22 : WRITE (iunit, "(/,A)") "begin atoms_cart"
860 22 : WRITE (iunit, "(A)") "bohr"
861 128 : DO i = 1, num_atoms
862 128 : WRITE (iunit, "(A,3F15.10)") atom_symbols(i), atoms_cart(1:3, i)
863 : END DO
864 22 : WRITE (iunit, "(A,/)") "end atoms_cart"
865 22 : WRITE (iunit, "(/,A,/)") "! Kpoints"
866 22 : WRITE (iunit, "(/,A,3I6/)") "mp_grid = ", mp_grid(1:3)
867 22 : WRITE (iunit, "(A)") "begin kpoints"
868 310 : DO i = 1, num_kpts
869 310 : WRITE (iunit, "(3ES24.16)") kpt_latt(1:3, i)
870 : END DO
871 22 : WRITE (iunit, "(A)") "end kpoints"
872 22 : CALL close_file(iunit)
873 : ELSE
874 44 : iunit = -1
875 : END IF
876 :
877 : ! calculate bands
878 66 : NULLIFY (qs_env_kp)
879 66 : IF (kpoints_source == w90_kpoints_mp_grid .AND. input_kpoint_symmetry .AND. iw > 0) THEN
880 : WRITE (iw, '(T2,A)') &
881 0 : "WANNIER90| Atomic k-point symmetry from the SCF calculation is not reused."
882 : WRITE (iw, '(T2,A)') &
883 0 : "WANNIER90| A full Monkhorst-Pack grid is generated for the Wannier90 interface."
884 : END IF
885 66 : IF (do_kpoints) THEN
886 : ! we already do kpoints
887 66 : qs_env_kp => qs_env
888 : ELSE
889 : ! we start from gamma point only
890 0 : CALL create_kp_from_gamma(qs_env, qs_env_kp)
891 : END IF
892 66 : IF (iw > 0) THEN
893 33 : WRITE (unit=iw, FMT="(/,T2,A)") "Start K-Point Calculation ..."
894 : END IF
895 : ! The Gamma-to-k-point conversion normalizes the spin dimensions for diagonalization.
896 : ! Keep the original band selection, but use the converted MO sizes for its workspace.
897 66 : CALL get_qs_env(qs_env=qs_env_kp, para_env=para_env, blacs_env=blacs_env, mos=mos)
898 66 : CALL kpoint_env_initialize(kpoint, para_env, blacs_env)
899 66 : CALL kpoint_initialize_mos(kpoint, mos, nadd)
900 66 : CALL kpoint_initialize_mo_set(kpoint)
901 : !
902 66 : CALL get_qs_env(qs_env=qs_env_kp, sab_orb=sab_nl, dft_control=dft_control)
903 66 : CALL kpoint_init_cell_index(kpoint, sab_nl, para_env, dft_control%nimages)
904 : !
905 : CALL get_qs_env(qs_env=qs_env_kp, matrix_ks_kp=matrix_ks, matrix_s_kp=matrix_s, &
906 66 : scf_env=scf_env, scf_control=scf_control)
907 66 : full_mesh_diagonalized = .FALSE.
908 66 : reused_scf_mos = .FALSE.
909 66 : reuse_reason = ""
910 66 : aligned_degenerate_blocks = 0
911 66 : aligned_degenerate_max_size = 0
912 66 : aligned_degenerate_min_svalue = 0.0_dp
913 66 : IF (reuse_scf_mos) THEN
914 42 : CALL get_kpoint_info(kpoint=kpoint, cell_to_index=cell_to_index)
915 : CALL do_general_diag_kp(matrix_ks, matrix_s, qs_kpoint, scf_env, scf_control, .FALSE., &
916 42 : diis_step)
917 42 : IF (validate_reuse_scf_mos) THEN
918 14 : IF (iw > 0) THEN
919 : WRITE (iw, '(T2,A)') &
920 7 : "WANNIER90| Validating SCF MO reuse against a full-mesh diagonalization reference."
921 : END IF
922 14 : CALL do_general_diag_kp(matrix_ks, matrix_s, kpoint, scf_env, scf_control, .FALSE., diis_step)
923 14 : full_mesh_diagonalized = .TRUE.
924 14 : CALL save_wannier90_mo_snapshot(kpoint, ispin, para_env, reference_mo, reference_eigenvalues)
925 : CALL diagnose_wannier90_scf_reuse_candidates(kpoint, qs_kpoint, matrix_s, matrix_ks, &
926 : cell_to_index, sab_nl, para_env, iw, ispin, &
927 : reuse_candidate_deviation, &
928 : reuse_candidate_min_svalue, &
929 : reuse_candidate_metric_deviation, &
930 14 : reuse_candidate_residual)
931 14 : IF (iw > 0 .AND. reuse_candidate_deviation < 1.0e100_dp) THEN
932 : WRITE (iw, '(T2,A,ES10.3,A,ES10.3,A,ES10.3,A,ES10.3)') &
933 7 : "WANNIER90| Best atom/AO candidate subspace deviation ", &
934 7 : reuse_candidate_deviation, ", minimum singular value ", &
935 7 : reuse_candidate_min_svalue, ", max metric deviation ", &
936 14 : reuse_candidate_metric_deviation, ", max residual ", reuse_candidate_residual
937 : END IF
938 : END IF
939 : CALL prepare_wannier90_scf_mos(kpoint, qs_kpoint, matrix_s, matrix_ks, cell_to_index, &
940 : sab_nl, para_env, reused_scf_mos, reuse_reason, &
941 : aligned_degenerate_blocks, aligned_degenerate_max_size, &
942 42 : aligned_degenerate_min_svalue, spin_channel=ispin)
943 42 : IF (validate_reuse_scf_mos) THEN
944 14 : IF (reused_scf_mos) THEN
945 : CALL validate_wannier90_reused_mos(kpoint, matrix_s, cell_to_index, sab_nl, &
946 : para_env, ispin, reference_mo, &
947 : reference_eigenvalues, validate_reuse_ok, &
948 : validation_subspace_deviation, validation_min_svalue, &
949 14 : validation_eigenvalue_deviation)
950 14 : IF (iw > 0) THEN
951 : WRITE (iw, '(T2,A,ES10.3,A,ES10.3,A,ES10.3)') &
952 7 : "WANNIER90| Reused MO validation: subspace deviation ", &
953 7 : validation_subspace_deviation, ", minimum singular value ", &
954 7 : validation_min_svalue, ", eigenvalue deviation ", &
955 14 : validation_eigenvalue_deviation
956 : END IF
957 14 : IF (.NOT. validate_reuse_ok) THEN
958 2 : reused_scf_mos = .FALSE.
959 : WRITE (reuse_reason, "(A,ES10.3,A,ES10.3)") &
960 2 : "validation failed: dS=", &
961 4 : validation_subspace_deviation, ", dE=", validation_eigenvalue_deviation
962 : END IF
963 : END IF
964 14 : IF (.NOT. reused_scf_mos) THEN
965 : CALL restore_wannier90_mo_snapshot(kpoint, ispin, reference_mo, &
966 2 : reference_eigenvalues)
967 : END IF
968 : END IF
969 42 : IF (iw > 0) THEN
970 21 : IF (reused_scf_mos) THEN
971 : WRITE (iw, '(T2,A)') &
972 17 : "WANNIER90| Reused SCF MO coefficients for the Wannier90 full k-point mesh."
973 17 : IF (use_bloch_phases) THEN
974 10 : IF (initial_projections == 0) WRITE (iw, '(T2,A)') &
975 6 : "WANNIER90| Wrote identity projections for Bloch-phase complete band subspaces."
976 : WRITE (iw, '(T2,A,3F10.6)') &
977 10 : "WANNIER90| Applied Bloch phase gauge to reused overlaps around fractional center", &
978 20 : phase_center(1:3)
979 : END IF
980 17 : IF (aligned_degenerate_blocks > 0) THEN
981 : WRITE (iw, '(T2,A,I0,A,I0,A,ES10.3)') &
982 10 : "WANNIER90| Ritz-stabilized ", aligned_degenerate_blocks, &
983 10 : " degenerate SCF MO subspace(s) with S(k),H(k); largest block has ", &
984 10 : aligned_degenerate_max_size, " band(s), min metric eigenvalue ", &
985 20 : aligned_degenerate_min_svalue
986 : END IF
987 : ELSE
988 : WRITE (iw, '(T2,A,A)') &
989 4 : "WANNIER90| Could not reuse SCF MOs: ", TRIM(reuse_reason)
990 : WRITE (iw, '(T2,A)') &
991 4 : "WANNIER90| Falling back to full-mesh diagonalization for the Wannier90 files."
992 : END IF
993 : END IF
994 : END IF
995 66 : IF (.NOT. reused_scf_mos .AND. .NOT. full_mesh_diagonalized) THEN
996 30 : CALL do_general_diag_kp(matrix_ks, matrix_s, kpoint, scf_env, scf_control, .FALSE., diis_step)
997 : END IF
998 66 : IF (ALLOCATED(reference_mo)) DEALLOCATE (reference_mo)
999 66 : IF (ALLOCATED(reference_eigenvalues)) DEALLOCATE (reference_eigenvalues)
1000 : !
1001 66 : IF (iw > 0) THEN
1002 33 : WRITE (iw, '(T69,A)') "... Finished"
1003 : END IF
1004 : !
1005 : ! Calculate and print Overlaps
1006 : !
1007 : ! create a list of unique b vectors and a table of pointers
1008 : ! nblist(ik,i) -> +/- b_latt(1:3,x)
1009 264 : ALLOCATE (nblist(num_kpts, nntot))
1010 198 : ALLOCATE (b_latt(3, num_kpts*nntot))
1011 66 : nblist(:, :) = 0
1012 66 : nbs = 0
1013 1002 : DO ik = 1, num_kpts
1014 4914 : DO i = 1, nntot
1015 15648 : bvec(1:3) = kpt_latt(1:3, nnlist(ik, i)) - kpt_latt(1:3, ik) + nncell(1:3, ik, i)
1016 7656 : ibs = 0
1017 7656 : DO k = 1, nbs
1018 29984 : IF (SUM(ABS(bvec(1:3) - b_latt(1:3, k))) < 1.e-6_dp) THEN
1019 : ibs = k
1020 : EXIT
1021 : END IF
1022 22240 : IF (SUM(ABS(bvec(1:3) + b_latt(1:3, k))) < 1.e-6_dp) THEN
1023 1776 : ibs = -k
1024 1776 : EXIT
1025 : END IF
1026 : END DO
1027 4848 : IF (ibs /= 0) THEN
1028 : ! old lattice vector
1029 3752 : nblist(ik, i) = ibs
1030 : ELSE
1031 : ! new lattice vector
1032 160 : nbs = nbs + 1
1033 640 : b_latt(1:3, nbs) = bvec(1:3)
1034 160 : nblist(ik, i) = nbs
1035 : END IF
1036 : END DO
1037 : END DO
1038 : ! calculate all the operator matrices (a|bvec|b)
1039 66 : overlap_nl => sab_nl
1040 66 : NULLIFY (berry_kpoint)
1041 66 : IF (ordered_berry) THEN
1042 22 : CALL get_qs_env(qs_env_kp, sab_all=overlap_nl)
1043 22 : CALL kpoint_create(berry_kpoint)
1044 22 : CALL kpoint_init_cell_index(berry_kpoint, overlap_nl, para_env, nberry_images)
1045 22 : CALL get_kpoint_info(berry_kpoint, cell_to_index=berry_cell_index)
1046 : END IF
1047 66 : IF (.NOT. ASSOCIATED(overlap_nl)) CPABORT("Explicit overlaps require k-point neighbour lists.")
1048 358 : ALLOCATE (berry_matrix(nbs))
1049 226 : DO i = 1, nbs
1050 160 : NULLIFY (berry_matrix(i)%cosmat)
1051 160 : NULLIFY (berry_matrix(i)%sinmat)
1052 800 : bvec(1:3) = -twopi*MATMUL(TRANSPOSE(cell%h_inv(1:3, 1:3)), b_latt(1:3, i))
1053 : CALL build_berry_kpoint_matrix(qs_env_kp, berry_matrix(i)%cosmat, &
1054 226 : berry_matrix(i)%sinmat, bvec, ordered=ordered_berry, ordered_kpoints=berry_kpoint)
1055 : END DO
1056 : ! work matrices for MOs (all group)
1057 66 : kp => kpoint%kp_env(1)%kpoint_env
1058 66 : CALL get_mo_set(kp%mos(ispin), nmo=nmo)
1059 66 : IF (ordered_berry .AND. nmo /= nscalar) THEN
1060 0 : CPABORT("WANNIER90: unexpected orbital count in export.")
1061 : END IF
1062 66 : NULLIFY (matrix_struct_ao, matrix_struct_work)
1063 : CALL cp_fm_struct_create(matrix_struct_work, nrow_global=nao, &
1064 : ncol_global=nmo, &
1065 : para_env=para_env, &
1066 66 : context=blacs_env)
1067 66 : CALL cp_cfm_create(fmk1_cfm, matrix_struct_work)
1068 66 : CALL cp_cfm_create(fmk2_cfm, matrix_struct_work)
1069 66 : CALL cp_cfm_create(tmp_cfm, matrix_struct_work)
1070 : CALL cp_fm_struct_create(matrix_struct_ao, nrow_global=nao, &
1071 : ncol_global=nao, &
1072 : para_env=para_env, &
1073 66 : context=blacs_env)
1074 66 : CALL cp_fm_create(mat_real, matrix_struct_ao)
1075 66 : CALL cp_fm_create(mat_imag, matrix_struct_ao)
1076 66 : CALL cp_cfm_create(omat_cfm, matrix_struct_ao)
1077 : ! work matrices for Mmn(k,b) integrals
1078 66 : NULLIFY (matrix_struct_mmn)
1079 : CALL cp_fm_struct_create(matrix_struct_mmn, nrow_global=nmo, &
1080 : ncol_global=nmo, &
1081 : para_env=para_env, &
1082 66 : context=blacs_env)
1083 66 : CALL cp_cfm_create(mmn_cfm, matrix_struct_mmn)
1084 : ! allocate some work matrices
1085 66 : ALLOCATE (rmatrix_full, cmatrix_full)
1086 : CALL dbcsr_create(rmatrix_full, template=matrix_s(1, 1)%matrix, &
1087 66 : matrix_type=dbcsr_type_no_symmetry)
1088 : CALL dbcsr_create(cmatrix_full, template=matrix_s(1, 1)%matrix, &
1089 66 : matrix_type=dbcsr_type_no_symmetry)
1090 66 : IF (ordered_berry) THEN
1091 22 : ALLOCATE (loop_real, loop_imag)
1092 22 : CALL dbcsr_create(loop_real, template=matrix_s(1, 1)%matrix, matrix_type=dbcsr_type_no_symmetry)
1093 22 : CALL dbcsr_create(loop_imag, template=matrix_s(1, 1)%matrix, matrix_type=dbcsr_type_no_symmetry)
1094 22 : CALL cp_dbcsr_alloc_block_from_nbl(loop_real, overlap_nl)
1095 22 : CALL cp_dbcsr_alloc_block_from_nbl(loop_imag, overlap_nl)
1096 22 : CALL cp_dbcsr_alloc_block_from_nbl(rmatrix_full, overlap_nl)
1097 22 : CALL cp_dbcsr_alloc_block_from_nbl(cmatrix_full, overlap_nl)
1098 : ELSE
1099 44 : CALL cp_dbcsr_alloc_block_from_nbl(rmatrix_full, sab_nl, desymmetrize=.TRUE.)
1100 44 : CALL cp_dbcsr_alloc_block_from_nbl(cmatrix_full, sab_nl, desymmetrize=.TRUE.)
1101 : END IF
1102 : !
1103 66 : CALL get_kpoint_info(kpoint=kpoint, cell_to_index=cell_to_index)
1104 66 : NULLIFY (cmo_coeff_source)
1105 66 : IF (do_soc .OR. do_parity) THEN
1106 : ! Parity needs the overlap work matrices even without SOC, and after SOC is done.
1107 12 : ALLOCATE (rmatrix, cmatrix)
1108 12 : CALL dbcsr_create(rmatrix, template=matrix_s(1, 1)%matrix, matrix_type=dbcsr_type_symmetric)
1109 12 : CALL dbcsr_create(cmatrix, template=matrix_s(1, 1)%matrix, matrix_type=dbcsr_type_antisymmetric)
1110 12 : CALL cp_dbcsr_alloc_block_from_nbl(rmatrix, sab_nl)
1111 12 : CALL cp_dbcsr_alloc_block_from_nbl(cmatrix, sab_nl)
1112 : END IF
1113 66 : IF (do_soc) THEN
1114 : ! Second variation in the scalar KS eigenbasis. Retain selected spinors at each k.
1115 30 : CPASSERT(ALL(kpoint%kp_range == [1, num_kpts]))
1116 10 : NULLIFY (soc_matrices)
1117 10 : CALL V_SOC_xyz_from_pseudopotential(qs_env_kp, soc_matrices)
1118 100 : ALLOCATE (soc_xyz(nmo, nmo, 3), soc_h(2*nmo, 2*nmo), soc_u(2*nmo, 2*nmo))
1119 60 : ALLOCATE (scalar_values(2*nmo), spinor_values(2*nmo, num_kpts))
1120 80 : ALLOCATE (spinor_coeff(2*nmo, num_bands, num_kpts), scalar_overlap(nmo, nmo))
1121 146 : DO ik = 1, num_kpts
1122 136 : kp => kpoint%kp_env(ik)%kpoint_env
1123 136 : CALL cp_cfm_copy_general(kp%mos(1)%cmo_coeff, fmk1_cfm, para_env)
1124 136 : CALL get_mo_set(kp%mos(1), eigenvalues=eigenvalues)
1125 984 : scalar_values(1:nmo) = eigenvalues(1:nmo)
1126 984 : scalar_values(nmo + 1:) = eigenvalues(1:nmo)
1127 544 : DO axis = 1, 3
1128 408 : CALL dbcsr_set(rmatrix, 0.0_dp)
1129 408 : CALL dbcsr_set(cmatrix, 0.0_dp)
1130 : CALL rskp_transform(cmatrix, rmatrix, rsmat=soc_matrices, ispin=axis, &
1131 408 : xkp=kpoint%xkp(:, ik), cell_to_index=cell_to_index, sab_nl=sab_nl)
1132 408 : CALL dbcsr_scale(rmatrix, -1.0_dp)
1133 408 : CALL dbcsr_desymmetrize(rmatrix, rmatrix_full)
1134 408 : CALL dbcsr_desymmetrize(cmatrix, cmatrix_full)
1135 408 : CALL copy_dbcsr_to_fm(rmatrix_full, mat_real)
1136 408 : CALL copy_dbcsr_to_fm(cmatrix_full, mat_imag)
1137 408 : CALL cp_fm_to_cfm(mat_real, mat_imag, omat_cfm)
1138 : CALL parallel_gemm("N", "N", nao, nmo, nao, CMPLX(1.0_dp, 0.0_dp, dp), &
1139 408 : omat_cfm, fmk1_cfm, CMPLX(0.0_dp, 0.0_dp, dp), tmp_cfm)
1140 : CALL parallel_gemm("C", "N", nmo, nmo, nao, CMPLX(1.0_dp, 0.0_dp, dp), &
1141 408 : fmk1_cfm, tmp_cfm, CMPLX(0.0_dp, 0.0_dp, dp), mmn_cfm)
1142 544 : CALL cp_cfm_get_submatrix(mmn_cfm, soc_xyz(:, :, axis))
1143 : END DO
1144 9592 : soc_h(1:nmo, 1:nmo) = soc_xyz(:, :, 3)
1145 9592 : soc_h(nmo + 1:, nmo + 1:) = -soc_xyz(:, :, 3)
1146 : ! Match H_KS_spinor_kp and CP2K's real-spherical angular-momentum convention.
1147 9592 : soc_h(1:nmo, nmo + 1:) = soc_xyz(:, :, 1) + CMPLX(0.0_dp, 1.0_dp, dp)*soc_xyz(:, :, 2)
1148 9592 : soc_h(nmo + 1:, 1:nmo) = soc_xyz(:, :, 1) - CMPLX(0.0_dp, 1.0_dp, dp)*soc_xyz(:, :, 2)
1149 1832 : DO ib = 1, 2*nmo
1150 1832 : soc_h(ib, ib) = soc_h(ib, ib) + scalar_values(ib)
1151 : END DO
1152 36264 : IF (MAXVAL(ABS(soc_h - CONJG(TRANSPOSE(soc_h)))) > 1.e-8_dp) THEN
1153 0 : IF (iw > 0) WRITE (iw, '(T2,A,I0,A,ES18.10)') &
1154 0 : "TOPOLOGY| SOC Hermiticity error at point ", ik, ": ", &
1155 0 : MAXVAL(ABS(soc_h - CONJG(TRANSPOSE(soc_h))))
1156 0 : CPABORT("WANNIER90 SOC: non-Hermitian second-variational Hamiltonian.")
1157 : END IF
1158 136 : CALL diag_complex(soc_h, soc_u, spinor_values(:, ik))
1159 14802 : spinor_coeff(:, :, ik) = soc_u(:, band_indices)
1160 : END DO
1161 10 : CALL dbcsr_deallocate_matrix_set(soc_matrices)
1162 10 : DEALLOCATE (soc_xyz, soc_h, soc_u, scalar_values)
1163 10 : IF (iw > 0) WRITE (iw, '(T2,A,I0)') "TOPOLOGY| Scalar bands in SOC second variation: ", nmo
1164 : END IF
1165 : ! Eigenvalues determine the projection windows and the sampled spectral gaps.
1166 66 : kp => kpoint%kp_env(1)%kpoint_env
1167 66 : CALL get_mo_set(kp%mos(ispin), nmo=nmo)
1168 66 : CPASSERT(nmo >= nscalar)
1169 254 : ALLOCATE (eigval(MERGE(2*nmo, nmo, do_soc)))
1170 66 : direct_gap = HUGE(1.0_dp)
1171 66 : valence_max = -HUGE(1.0_dp)
1172 66 : conduction_min = HUGE(1.0_dp)
1173 66 : CALL get_kpoint_info(kpoint, nkp=nkp, kp_range=kp_range, xkp=xkp)
1174 66 : IF (para_env%is_source()) THEN
1175 33 : WRITE (filename, '(A,A)') TRIM(seed_name), ".eig"
1176 33 : CALL open_file(filename, unit_number=iunit, file_status="REPLACE", file_action="WRITE")
1177 : ELSE
1178 33 : iunit = -1
1179 : END IF
1180 : !
1181 1002 : DO ik = 1, nkp
1182 936 : my_kpgrp = (ik >= kp_range(1) .AND. ik <= kp_range(2))
1183 936 : IF (do_soc) THEN
1184 1832 : eigval(:) = spinor_values(:, ik)
1185 800 : ELSE IF (my_kpgrp) THEN
1186 800 : ikpgr = ik - kp_range(1) + 1
1187 800 : kp => kpoint%kp_env(ikpgr)%kpoint_env
1188 800 : CALL get_mo_set(kp%mos(ispin), eigenvalues=eigenvalues)
1189 3296 : eigval(1:nmo) = eigenvalues(1:nmo)
1190 : ELSE
1191 0 : eigval(1:nmo) = 0.0_dp
1192 : END IF
1193 936 : IF (.NOT. do_soc) CALL kpoint%para_env_inter_kp%sum(eigval)
1194 936 : IF (.NOT. ordered_berry .AND. nexcl > 0) THEN
1195 80 : DO ib = 1, num_bands_tot - 1
1196 64 : IF (keep_band(ib) .EQV. keep_band(ib + 1)) CYCLE
1197 : ! Degenerate states outside the outer window cannot enter the Wannier subspace.
1198 32 : kept_index = MERGE(ib, ib + 1, keep_band(ib))
1199 32 : IF (eigval(kept_index) < energy_window(1) .OR. eigval(kept_index) > energy_window(2)) CYCLE
1200 16 : IF (ABS(eigval(ib + 1) - eigval(ib)) < 1.0e-8_dp) THEN
1201 0 : CPABORT("WANNIER90 EXCLUDE_BANDS must retain or remove complete degenerate subspaces.")
1202 : END IF
1203 : END DO
1204 : END IF
1205 936 : IF (do_wilson) THEN
1206 328 : IF (lowest_bands) THEN
1207 328 : valence_max = MAX(valence_max, eigval(num_bands))
1208 328 : conduction_min = MIN(conduction_min, eigval(num_bands + 1))
1209 : END IF
1210 1488 : DO ib = 1, num_bands_tot - 1
1211 10032 : IF (ANY(band_indices == ib) .NEQV. ANY(band_indices == ib + 1)) THEN
1212 328 : pair_gap = eigval(ib + 1) - eigval(ib)
1213 328 : direct_gap = MIN(direct_gap, pair_gap)
1214 : END IF
1215 : END DO
1216 : END IF
1217 5128 : eigval(:) = eigval*evolt
1218 4616 : IF (ANY(window_set)) THEN
1219 80 : projection_eigenvalues(:, ik) = eigval(band_indices)
1220 : CALL wannier90_window_mask(projection_eigenvalues(:, ik), num_wann, energy_window*evolt, &
1221 80 : window_set, allowed_band)
1222 : END IF
1223 : ! output
1224 1002 : IF (iunit > 0) THEN
1225 2140 : DO ib = 1, num_bands
1226 2140 : WRITE (iunit, "(2I8,F24.14)") ib, ik, eigval(band_indices(ib))
1227 : END DO
1228 : END IF
1229 : END DO
1230 66 : IF (iunit > 0) THEN
1231 33 : CALL close_file(iunit)
1232 : END IF
1233 66 : IF (.NOT. ordered_berry .AND. initial_projections >= 0) THEN
1234 120 : ALLOCATE (initial_amn(num_bands, num_wann, num_kpts))
1235 24 : IF (initial_projections == 0) THEN
1236 14 : initial_amn(:, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
1237 62 : DO i = 1, num_wann
1238 1342 : initial_amn(i, i, :) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
1239 : END DO
1240 : ELSE
1241 : CALL build_wannier90_ao_projections(kpoint, matrix_s, sab_nl, para_env, ispin, band_indices, &
1242 12 : reused_scf_mos .AND. use_bloch_phases, phase_center, ao_projections)
1243 42 : IF (para_env%is_source() .AND. ANY(window_set)) THEN
1244 9 : DO ik = 1, num_kpts
1245 : CALL wannier90_window_mask(projection_eigenvalues(:, ik), num_wann, energy_window*evolt, &
1246 40 : window_set, allowed_band)
1247 41 : DO ib = 1, num_bands
1248 160 : IF (.NOT. allowed_band(ib)) ao_projections(ib, :, ik) = CMPLX(0.0_dp, 0.0_dp, dp)
1249 : END DO
1250 : END DO
1251 : END IF
1252 10 : IF (initial_projections == 2) THEN
1253 4 : CALL build_wannier90_hybrid_groups(qs_env, hybrid_groups)
1254 4 : CALL wannier90_hybrid_candidates(ao_projections, hybrid_groups, hybrid_projections)
1255 4 : DEALLOCATE (ao_projections, hybrid_groups)
1256 4 : CALL MOVE_ALLOC(hybrid_projections, ao_projections)
1257 4 : IF (iw > 0) WRITE (iw, '(T2,A)') "WANNIER90| Selecting from the full radial s/p hybrid candidate bank."
1258 : END IF
1259 30 : ALLOCATE (projection_pivots(num_wann))
1260 : CALL wannier90_select_projections(ao_projections, para_env, initial_amn, projection_pivots, &
1261 10 : projection_min_svalue)
1262 10 : IF (iw > 0) THEN
1263 5 : WRITE (iw, '(T2,A,ES12.4)') "WANNIER90| Minimum projection singular value ", projection_min_svalue
1264 5 : WRITE (iw, '(T2,A,*(I6))') "WANNIER90| Selected projection candidates ", projection_pivots
1265 : END IF
1266 10 : IF (projection_min_svalue < 1.0e-10_dp) THEN
1267 0 : CPABORT("No full-rank Wannier90 AO trial set was found on the entire k-point mesh.")
1268 : END IF
1269 10 : DEALLOCATE (projection_pivots)
1270 20 : DEALLOCATE (ao_projections)
1271 : END IF
1272 24 : CALL write_wannier90_amn(initial_amn, para_env, seed_name)
1273 24 : DEALLOCATE (initial_amn)
1274 : END IF
1275 66 : IF (para_env%is_source()) THEN
1276 33 : WRITE (filename, '(A,A)') TRIM(seed_name), ".mmn"
1277 33 : CALL open_file(filename, unit_number=iunit, file_status="REPLACE", file_action="WRITE")
1278 33 : CALL m_timestamp(timestamp)
1279 33 : WRITE (iunit, "(A)") "! Wannier90 file generated by CP2K "//timestamp
1280 33 : WRITE (iunit, "(3I8)") num_bands, num_kpts, nntot
1281 : ELSE
1282 33 : iunit = -1
1283 : END IF
1284 : ! loop over all k-points
1285 1002 : DO ik = 1, num_kpts
1286 : ! get the MO coefficients for this k-point
1287 936 : my_kpgrp = (ik >= kpoint%kp_range(1) .AND. ik <= kpoint%kp_range(2))
1288 : IF (my_kpgrp) THEN
1289 936 : ikk = ik - kpoint%kp_range(1) + 1
1290 936 : kp => kpoint%kp_env(ikk)%kpoint_env
1291 936 : cmo_coeff_source => kp%mos(ispin)%cmo_coeff
1292 : ELSE
1293 0 : NULLIFY (cmo_coeff_source, kp)
1294 : END IF
1295 936 : CALL wannier90_copy_cfm_general(cmo_coeff_source, fmk1_cfm, para_env)
1296 : ! loop over all connected neighbors
1297 4914 : DO i = 1, nntot
1298 : ! get the MO coefficients for the connected k-point
1299 3912 : ik2 = nnlist(ik, i)
1300 3912 : mygrp = (ik2 >= kpoint%kp_range(1) .AND. ik2 <= kpoint%kp_range(2))
1301 : IF (mygrp) THEN
1302 3912 : ikk = ik2 - kpoint%kp_range(1) + 1
1303 3912 : kp => kpoint%kp_env(ikk)%kpoint_env
1304 3912 : cmo_coeff_source => kp%mos(ispin)%cmo_coeff
1305 : ELSE
1306 0 : NULLIFY (cmo_coeff_source, kp)
1307 : END IF
1308 3912 : CALL wannier90_copy_cfm_general(cmo_coeff_source, fmk2_cfm, para_env)
1309 : !
1310 : ! transfer realspace overlaps to connected k-point
1311 3912 : ibs = nblist(ik, i)
1312 3912 : ksign = SIGN(1.0_dp, REAL(ibs, KIND=dp))
1313 3912 : ibs = ABS(ibs)
1314 3912 : IF (ordered_berry) THEN
1315 : ! The cross-k AO operator is not Hermitian. Retain ordered atom pairs
1316 : ! and use exp(i*k'.R) [cos(b.r) - i*sin(b.r)] without symmetry shortcuts.
1317 360 : CALL dbcsr_set(rmatrix_full, 0.0_dp)
1318 360 : CALL dbcsr_set(cmatrix_full, 0.0_dp)
1319 360 : CALL dbcsr_set(loop_real, 0.0_dp)
1320 360 : CALL dbcsr_set(loop_imag, 0.0_dp)
1321 : CALL rskp_transform(rmatrix_full, cmatrix_full, berry_matrix(ibs)%cosmat, 1, &
1322 360 : kpoint%xkp(:, ik2), berry_cell_index, overlap_nl)
1323 : CALL rskp_transform(loop_real, loop_imag, berry_matrix(ibs)%sinmat, 1, &
1324 360 : kpoint%xkp(:, ik2), berry_cell_index, overlap_nl, rs_sign=ksign)
1325 360 : CALL dbcsr_add(rmatrix_full, loop_imag, 1.0_dp, -1.0_dp)
1326 360 : CALL dbcsr_add(cmatrix_full, loop_real, 1.0_dp, 1.0_dp)
1327 : ELSE
1328 : CALL transform_wannier90_overlap(berry_matrix(ibs)%cosmat, berry_matrix(ibs)%sinmat, &
1329 : ksign, kpoint%xkp(:, ik), kpoint%xkp(:, ik2), &
1330 3552 : cell_to_index, sab_nl, rmatrix_full, cmatrix_full)
1331 : END IF
1332 : !
1333 : ! calculate M_(mn)^(k,b) = C(k)^H O(k,b) C(k+b)
1334 3912 : CALL copy_dbcsr_to_fm(rmatrix_full, mat_real)
1335 3912 : CALL copy_dbcsr_to_fm(cmatrix_full, mat_imag)
1336 3912 : CALL cp_fm_to_cfm(mat_real, mat_imag, omat_cfm)
1337 : CALL parallel_gemm("N", "N", nao, nmo, nao, CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
1338 3912 : omat_cfm, fmk2_cfm, CMPLX(0.0_dp, 0.0_dp, KIND=dp), tmp_cfm)
1339 : CALL parallel_gemm("C", "N", nmo, nmo, nao, CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
1340 3912 : fmk1_cfm, tmp_cfm, CMPLX(0.0_dp, 0.0_dp, KIND=dp), mmn_cfm)
1341 3912 : IF (do_soc) THEN
1342 136 : CALL cp_cfm_get_submatrix(mmn_cfm, scalar_overlap)
1343 136 : link_matrix(:, :) = MATMUL(CONJG(TRANSPOSE(spinor_coeff(1:nmo, :, ik))), &
1344 544 : MATMUL(scalar_overlap, spinor_coeff(1:nmo, :, ik2))) + &
1345 544 : MATMUL(CONJG(TRANSPOSE(spinor_coeff(nmo + 1:, :, ik))), &
1346 922480 : MATMUL(scalar_overlap, spinor_coeff(nmo + 1:, :, ik2)))
1347 : END IF
1348 : !
1349 : ! write to output file
1350 3912 : IF (reused_scf_mos .AND. use_bloch_phases) THEN
1351 : ! Reused SCF MOs need the same global Bloch gauge in every overlap block.
1352 : gauge_arg = twopi*DOT_PRODUCT(kpoint%xkp(1:3, ik2) - kpoint%xkp(1:3, ik), &
1353 9344 : phase_center(1:3))
1354 2336 : gauge_real = COS(gauge_arg)
1355 2336 : gauge_imag = SIN(gauge_arg)
1356 : ELSE
1357 : gauge_real = 1.0_dp
1358 : gauge_imag = 0.0_dp
1359 : END IF
1360 3912 : IF (iunit > 0) THEN
1361 1956 : WRITE (iunit, "(2I8,3I5)") ik, ik2, nncell(1:3, ik, i)
1362 : END IF
1363 17800 : DO ib2 = 1, num_bands
1364 77704 : DO ib1 = 1, num_bands
1365 59904 : IF (do_soc) THEN
1366 8704 : rmmn = REAL(link_matrix(ib1, ib2), dp)
1367 8704 : cmmn = AIMAG(link_matrix(ib1, ib2))
1368 : ELSE
1369 51200 : CALL cp_cfm_get_element(mmn_cfm, band_indices(ib1), band_indices(ib2), zmmn)
1370 51200 : rmmn = REAL(zmmn, KIND=dp)
1371 51200 : cmmn = AIMAG(zmmn)
1372 : END IF
1373 59904 : gauge_tmp = gauge_real*rmmn - gauge_imag*cmmn
1374 59904 : cmmn = gauge_imag*rmmn + gauge_real*cmmn
1375 59904 : rmmn = gauge_tmp
1376 59904 : link_matrix(ib1, ib2) = CMPLX(rmmn, cmmn, dp)
1377 73792 : IF (iunit > 0) THEN
1378 29952 : WRITE (iunit, "(2E30.14)") rmmn, cmmn
1379 : END IF
1380 : END DO
1381 : END DO
1382 4848 : IF (do_wilson) THEN
1383 328 : iloop = loop_index(ik)
1384 328 : CALL wilson_step(wilson_product(:, :, iloop), link_matrix, link_sv, status, 1.e-10_dp)
1385 328 : IF (status /= 0) THEN
1386 0 : CPABORT("Wilson link singular or SVD failed; refine sampling and check subspace.")
1387 : END IF
1388 328 : loop_sv(iloop) = MIN(loop_sv(iloop), link_sv)
1389 : END IF
1390 : !
1391 : END DO
1392 : END DO
1393 : ! Optional full-state snapshots, before the distributed work matrices are released.
1394 66 : IF (export_state .OR. do_parity) THEN
1395 12 : state_components = 1
1396 12 : IF (do_soc) state_components = 2
1397 84 : ALLOCATE (export_scalar(nao, nscalar), export_coeff(nao*state_components, num_bands))
1398 12 : state_unit = -1
1399 12 : IF (export_state) CALL topology_state_begin(qs_env_kp, TRIM(seed_name)//".topology", nao, num_bands, num_kpts, &
1400 4 : state_components, num_bands_tot, spin_channel, band_indices, state_unit)
1401 12 : IF (do_parity) THEN
1402 24 : CPASSERT(ALL(kpoint%kp_range == [1, num_kpts]))
1403 0 : ALLOCATE (parity_metric(nao, nao), parity_phase(nao), parity_map(nao), &
1404 0 : parity_odd(2**tqc_dimension), ebr_coefficients(2, 2**tqc_dimension), &
1405 112 : parity_energies(num_bands_tot))
1406 56 : parity_odd = -1
1407 8 : IF (do_tqc) THEN
1408 102 : IF (num_bands /= nelectron .OR. MOD(num_bands, 2) /= 0 .OR. &
1409 : ANY(band_indices /= [(i, i=1, num_bands)])) THEN
1410 0 : CPABORT("INVERSION_TQC requires the full occupied spinor band prefix.")
1411 : END IF
1412 : END IF
1413 : END IF
1414 156 : DO ik = 1, num_kpts
1415 144 : kp => kpoint%kp_env(ik)%kpoint_env
1416 144 : CALL cp_cfm_copy_general(kp%mos(spin_channel)%cmo_coeff, fmk1_cfm, para_env)
1417 144 : CALL cp_cfm_get_submatrix(fmk1_cfm, export_scalar)
1418 144 : IF (do_soc) THEN
1419 230304 : export_coeff(:nao, :) = MATMUL(export_scalar, spinor_coeff(:nscalar, :, ik))
1420 174560 : export_coeff(nao + 1:, :) = MATMUL(export_scalar, spinor_coeff(nscalar + 1:, :, ik))
1421 32 : CALL topology_state_point(state_unit, ik, kpt_latt(:, ik), spinor_values(:, ik), export_coeff)
1422 688 : IF (do_parity) parity_energies(:) = spinor_values(:, ik)
1423 : ELSE
1424 784 : export_coeff(:, :) = export_scalar(:, band_indices)
1425 112 : CALL get_mo_set(kp%mos(spin_channel), eigenvalues=eigenvalues)
1426 112 : CALL topology_state_point(state_unit, ik, kpt_latt(:, ik), eigenvalues(:nscalar), export_coeff)
1427 128 : IF (do_parity) parity_energies(:) = eigenvalues(:nscalar)
1428 : END IF
1429 144 : IF (.NOT. do_parity) CYCLE
1430 40 : parity_gap = HUGE(1.0_dp)
1431 712 : DO jpar = 1, num_bands_tot
1432 4776 : IF (ANY(band_indices == jpar)) CYCLE
1433 3920 : parity_gap = MIN(parity_gap, MINVAL(ABS(parity_energies(band_indices) - parity_energies(jpar))))
1434 : END DO
1435 40 : IF (num_bands == num_bands_tot .OR. parity_gap <= gap_tol) THEN
1436 0 : CPABORT("PARITY requires an isolated selected subspace and computed excluded bands.")
1437 : END IF
1438 160 : IF (MAXVAL(ABS(2*kpt_latt(:, ik) - NINT(2*kpt_latt(:, ik)))) > 1.e-8_dp) CYCLE
1439 : CALL gaussian_inversion_action(qs_env_kp, kpt_latt(:, ik), parity_origin, parity_map, &
1440 36 : parity_phase, parity_tolerance, status)
1441 36 : IF (status /= 0) THEN
1442 0 : CPABORT("PARITY: inversion does not preserve geometry and atomic kinds.")
1443 : END IF
1444 36 : CALL dbcsr_set(rmatrix, 0.0_dp)
1445 36 : CALL dbcsr_set(cmatrix, 0.0_dp)
1446 : CALL rskp_transform(rmatrix, cmatrix, rsmat=matrix_s, ispin=1, xkp=kpoint%xkp(:, ik), &
1447 36 : cell_to_index=cell_to_index, sab_nl=sab_nl)
1448 36 : CALL dbcsr_desymmetrize(rmatrix, rmatrix_full)
1449 36 : CALL dbcsr_desymmetrize(cmatrix, cmatrix_full)
1450 36 : CALL copy_dbcsr_to_fm(rmatrix_full, mat_real)
1451 36 : CALL copy_dbcsr_to_fm(cmatrix_full, mat_imag)
1452 36 : CALL cp_fm_to_cfm(mat_real, mat_imag, omat_cfm)
1453 36 : CALL cp_cfm_get_submatrix(omat_cfm, parity_metric)
1454 : CALL inversion_representation(parity_metric, export_coeff, parity_map, parity_phase, &
1455 : parity_energies(band_indices), do_soc .AND. time_reversal, parity_tolerance, &
1456 300 : parity_energy_tolerance, counts, parity_error, parity_energy_error, status, parity_checks)
1457 36 : IF (iw > 0) THEN
1458 18 : WRITE (iw, '(T2,A,3F10.5,A,2I8,A,3ES14.5)') 'TQC| TRIM ', kpt_latt(:, ik), &
1459 36 : ' even/odd states ', counts, ' residuals/gap ', parity_error, parity_energy_error, parity_gap
1460 18 : WRITE (iw, '(T2,A,4ES14.5)') 'TQC| Metric/normalization/inversion/TR residuals: ', parity_checks
1461 : END IF
1462 36 : IF (status /= 0) THEN
1463 0 : CPABORT("PARITY: metric, subspace or symmetry representation check failed.")
1464 : END IF
1465 36 : IF (tqc_dimension == 2 .AND. MODULO(NINT(2*kpt_latt(3, ik)), 2) /= 0) CYCLE
1466 36 : trim_id = 1
1467 128 : DO axis = 1, tqc_dimension
1468 128 : trim_id = trim_id + 2**(axis - 1)*MODULO(NINT(2*kpt_latt(axis, ik)), 2)
1469 : END DO
1470 36 : IF (parity_odd(trim_id) >= 0 .AND. parity_odd(trim_id) /= counts(2)) THEN
1471 0 : CPABORT("PARITY: repeated equivalent TRIM have inconsistent multiplicities.")
1472 : END IF
1473 156 : parity_odd(trim_id) = counts(2)
1474 : END DO
1475 12 : IF (state_unit > 0) CALL close_file(state_unit)
1476 12 : IF (do_parity) THEN
1477 8 : IF (ALL(parity_odd < 0)) THEN
1478 0 : CPABORT("PARITY requires a TRIM in the requested dimension.")
1479 : END IF
1480 : END IF
1481 12 : IF (do_tqc) THEN
1482 38 : IF (ANY(parity_odd < 0)) THEN
1483 0 : CPABORT("INVERSION_TQC requires all four (2D) or eight (3D) TRIM.")
1484 : END IF
1485 38 : IF (ANY(MOD(parity_odd, 2) /= 0)) THEN
1486 0 : CPABORT("INVERSION_TQC requires whole odd-parity Kramers pairs.")
1487 : END IF
1488 38 : parity_odd(:) = parity_odd/2
1489 6 : CALL inversion_indicators(parity_odd, num_bands/2, tqc_dimension, strong, weak, z4, status)
1490 6 : IF (status /= 0) CPABORT("Invalid inversion indicator data.")
1491 : CALL inversion_ebr_signature(parity_odd, num_bands/2, signed_atomic, nonnegative_atomic, &
1492 6 : ebr_coefficients, status)
1493 6 : IF (status /= 0) CPABORT("Invalid inversion EBR data.")
1494 6 : IF (iw > 0) THEN
1495 3 : WRITE (iw, '(T2,A,I0)') 'TQC| Inversion-subgroup dimension: ', tqc_dimension
1496 3 : WRITE (iw, '(T2,A,I0)') 'TQC| Fu-Kane parity index: ', strong
1497 3 : IF (tqc_dimension == 3) THEN
1498 1 : WRITE (iw, '(T2,A,3I3)') 'TQC| Weak parity indices: ', weak
1499 1 : WRITE (iw, '(T2,A,I0)') 'TQC| Inversion Z4 (sum odd pairs mod 4): ', z4
1500 : END IF
1501 3 : WRITE (iw, '(T2,A,L1)') 'TQC| Signed atomic signature: ', signed_atomic
1502 3 : WRITE (iw, '(T2,A,L1)') 'TQC| Nonnegative atomic signature: ', nonnegative_atomic
1503 3 : IF (nonnegative_atomic) THEN
1504 14 : DO i = 1, SIZE(parity_odd)
1505 12 : WRITE (iw, '(T2,A,I0,A,2I8)') 'TQC| Center bit index ', i - 1, &
1506 26 : ' even/odd EBR pairs ', ebr_coefficients(:, i)
1507 : END DO
1508 2 : WRITE (iw, '(T2,A)') 'TQC| Atomic-compatible symmetry signature; not a proof of trivial topology.'
1509 1 : ELSE IF (signed_atomic) THEN
1510 : WRITE (iw, '(T2,A)') 'TQC| Nonnegative EBR obstruction; '// &
1511 0 : 'exclude hidden stable topology before calling it fragile.'
1512 : ELSE
1513 1 : WRITE (iw, '(T2,A)') 'TQC| Stable inversion-symmetry indicator obstruction.'
1514 : END IF
1515 3 : WRITE (iw, '(T2,A)') 'TQC| TRIM checks do not establish a bulk gap; verify band isolation over the BZ.'
1516 3 : WRITE (iw, '(T2,A)') 'TQC| Converge the scalar-state basis used for second-variational SOC.'
1517 : END IF
1518 : END IF
1519 : END IF
1520 226 : DO i = 1, nbs
1521 160 : CALL dbcsr_deallocate_matrix_set(berry_matrix(i)%cosmat)
1522 226 : CALL dbcsr_deallocate_matrix_set(berry_matrix(i)%sinmat)
1523 : END DO
1524 66 : DEALLOCATE (berry_matrix)
1525 66 : CALL cp_fm_struct_release(matrix_struct_work)
1526 66 : CALL cp_cfm_release(fmk1_cfm)
1527 66 : CALL cp_cfm_release(fmk2_cfm)
1528 66 : CALL cp_cfm_release(tmp_cfm)
1529 66 : CALL cp_fm_struct_release(matrix_struct_ao)
1530 66 : CALL cp_fm_release(mat_real)
1531 66 : CALL cp_fm_release(mat_imag)
1532 66 : CALL cp_cfm_release(omat_cfm)
1533 66 : CALL cp_fm_struct_release(matrix_struct_mmn)
1534 66 : CALL cp_cfm_release(mmn_cfm)
1535 66 : CALL dbcsr_deallocate_matrix(rmatrix_full)
1536 66 : CALL dbcsr_deallocate_matrix(cmatrix_full)
1537 66 : IF (do_soc .OR. do_parity) THEN
1538 12 : CALL dbcsr_deallocate_matrix(rmatrix)
1539 12 : CALL dbcsr_deallocate_matrix(cmatrix)
1540 : END IF
1541 66 : IF (ordered_berry) THEN
1542 22 : CALL dbcsr_deallocate_matrix(loop_real)
1543 22 : CALL dbcsr_deallocate_matrix(loop_imag)
1544 : END IF
1545 : !
1546 66 : IF (iunit > 0) THEN
1547 33 : CALL close_file(iunit)
1548 : END IF
1549 : !
1550 66 : IF (do_wilson) THEN
1551 16 : IF (iw > 0 .AND. num_bands < num_bands_tot) WRITE (iw, '(T2,A,ES18.10)') &
1552 8 : "TOPOLOGY| Minimum sampled subspace gap [eV]: ", direct_gap*evolt
1553 16 : IF (direct_gap < gap_tol) CPABORT("Wilson subspace is not isolated on the sampled points.")
1554 16 : IF (lowest_bands) THEN
1555 16 : IF (iw > 0) THEN
1556 : WRITE (iw, '(T2,A,ES18.10)') &
1557 8 : "TOPOLOGY| Maximum selected-band energy [eV]: ", valence_max*evolt
1558 : WRITE (iw, '(T2,A,ES18.10)') &
1559 8 : "TOPOLOGY| Minimum excluded-band energy [eV]: ", conduction_min*evolt
1560 : WRITE (iw, '(T2,A,ES18.10)') &
1561 8 : "TOPOLOGY| Sampled indirect gap [eV]: ", (conduction_min - valence_max)*evolt
1562 8 : IF (conduction_min - valence_max <= gap_tol) WRITE (iw, '(T2,A)') &
1563 0 : "TOPOLOGY| Isolated band subspace, but no resolved common spectral gap on these samples."
1564 : END IF
1565 16 : IF (require_global_gap .AND. conduction_min - valence_max <= gap_tol) THEN
1566 0 : CPABORT("No positive sampled indirect gap above the selected bands.")
1567 : END IF
1568 : END IF
1569 68 : DO iloop = 1, nloop
1570 52 : CALL wilson_spectrum(wilson_product(:, :, iloop), wcc_out(:, iloop), berry_phase, status)
1571 52 : IF (status /= 0) CPABORT("Wilson eigenvalue calculation failed.")
1572 52 : IF (iw > 0) WRITE (iw, '(T2,A,I0,A,F18.12,A,ES12.4)') &
1573 42 : "TOPOLOGY| Loop ", iloop, " Berry phase [rad]: ", berry_phase, " min singular value: ", loop_sv(iloop)
1574 : END DO
1575 16 : IF (para_env%is_source()) THEN
1576 8 : CALL open_file(TRIM(seed_name)//".wilson", unit_number=iunit, file_status="UNKNOWN", file_action="WRITE")
1577 8 : WRITE (iunit, '(A)') "# loop, minimum link singular value, sorted WCC (arg(lambda)/(2*pi))"
1578 34 : DO iloop = 1, nloop
1579 34 : WRITE (iunit, '(I8,*(1X,ES24.16))') iloop, loop_sv(iloop), wcc_out(:, iloop)
1580 : END DO
1581 8 : CALL close_file(iunit)
1582 : END IF
1583 16 : IF (do_z2) THEN
1584 4 : CALL z2_from_wcc(wcc_out, z2_value, status, 1.e-5_dp)
1585 4 : IF (status /= 0 .AND. iw > 0) WRITE (iw, '(T2,A,I0)') &
1586 0 : "TOPOLOGY| Kramers/crossing check requests refinement, status: ", status
1587 4 : IF (iw > 0) WRITE (iw, '(T2,A,I0)') "TOPOLOGY| Z2 candidate (before sampling convergence): ", z2_value
1588 : END IF
1589 16 : IF (do_chern) THEN
1590 4 : CALL chern_from_wcc(wcc_out, chern_value, chern_winding, status, 1.e-5_dp)
1591 4 : IF (status /= 0) THEN
1592 0 : chern_value = HUGE(0)
1593 0 : IF (iw > 0) WRITE (iw, '(T2,A,I0)') &
1594 0 : "TOPOLOGY| Chern closure/winding check requests refinement, status: ", status
1595 4 : ELSE IF (iw > 0) THEN
1596 : WRITE (iw, '(T2,A,I0,A,F18.12)') &
1597 2 : "TOPOLOGY| First Chern candidate: ", chern_value, ", winding: ", chern_winding
1598 : END IF
1599 : END IF
1600 : END IF
1601 : ! clean up
1602 66 : IF (ordered_berry) CALL kpoint_release(berry_kpoint)
1603 66 : DEALLOCATE (kpt_latt, atoms_cart, atom_symbols, eigval)
1604 66 : DEALLOCATE (nnlist, nncell)
1605 66 : DEALLOCATE (nblist, b_latt)
1606 66 : IF (para_env%is_source()) THEN
1607 : ! Also replace an earlier exclusion map when the seed is reused without exclusions.
1608 : CALL open_file(TRIM(seed_name)//'_band_indices.dat', unit_number=iunit, &
1609 33 : file_status='REPLACE', file_action='WRITE')
1610 33 : WRITE (iunit, '(A)') '# Exported band, original MO index (before EXCLUDE_BANDS)'
1611 150 : DO ib = 1, num_bands
1612 150 : WRITE (iunit, '(2I10)') ib, band_indices(ib)
1613 : END DO
1614 33 : CALL close_file(iunit)
1615 : END IF
1616 66 : IF (nexcl > 0) DEALLOCATE (exclude_bands)
1617 66 : DEALLOCATE (band_indices, keep_band, allowed_band)
1618 66 : IF (ALLOCATED(projection_eigenvalues)) DEALLOCATE (projection_eigenvalues)
1619 66 : IF (do_kpoints) THEN
1620 66 : NULLIFY (qs_env_kp)
1621 : ELSE
1622 0 : CALL qs_env_release(qs_env_kp)
1623 0 : DEALLOCATE (qs_env_kp)
1624 : NULLIFY (qs_env_kp)
1625 : END IF
1626 :
1627 66 : CALL kpoint_release(kpoint)
1628 :
1629 1056 : END SUBROUTINE wannier90_files
1630 :
1631 : ! **************************************************************************************************
1632 : !> \brief Reconstruct a full Wannier90 k-point MO set from the SCF k-point MOs.
1633 : !> \param kpoint full Wannier90 export k-point object
1634 : !> \param qs_kpoint SCF k-point object
1635 : !> \param matrix_s real-space overlap matrix
1636 : !> \param matrix_ks real-space Kohn-Sham matrix
1637 : !> \param cell_to_index real-space cell index table
1638 : !> \param sab_nl overlap neighbor list
1639 : !> \param para_env global parallel environment
1640 : !> \param success true if all full-mesh MOs were reconstructed
1641 : !> \param reason diagnostic message when reconstruction is not possible
1642 : !> \param aligned_degenerate_blocks number of aligned degenerate MO blocks
1643 : !> \param aligned_degenerate_max_size largest aligned degenerate MO block
1644 : !> \param aligned_degenerate_min_svalue smallest S(k)-metric subspace singular value
1645 : !> \param spin_channel Optional single spin channel; otherwise reconstruct all channels.
1646 : ! **************************************************************************************************
1647 46 : SUBROUTINE prepare_wannier90_scf_mos(kpoint, qs_kpoint, matrix_s, matrix_ks, cell_to_index, &
1648 : sab_nl, para_env, success, reason, aligned_degenerate_blocks, &
1649 : aligned_degenerate_max_size, aligned_degenerate_min_svalue, spin_channel)
1650 : TYPE(kpoint_type), POINTER :: kpoint, qs_kpoint
1651 : TYPE(dbcsr_p_type), POINTER :: matrix_s(:, :), matrix_ks(:, :)
1652 : INTEGER, POINTER :: cell_to_index(:, :, :)
1653 : TYPE(neighbor_list_set_p_type), POINTER :: sab_nl(:)
1654 : TYPE(mp_para_env_type), POINTER :: para_env
1655 : LOGICAL, INTENT(OUT) :: success
1656 : CHARACTER(LEN=*), INTENT(OUT) :: reason
1657 : INTEGER, INTENT(OUT) :: aligned_degenerate_blocks, &
1658 : aligned_degenerate_max_size
1659 : REAL(KIND=dp), INTENT(OUT) :: aligned_degenerate_min_svalue
1660 : INTEGER, INTENT(IN), OPTIONAL :: spin_channel
1661 :
1662 : INTEGER :: blocks, first_spin, ispin, last_spin, &
1663 : max_size
1664 : REAL(KIND=dp) :: min_svalue
1665 :
1666 46 : success = .FALSE.
1667 46 : reason = 'Wannier90 export k-point MOs are not initialized'
1668 46 : aligned_degenerate_blocks = 0
1669 46 : aligned_degenerate_max_size = 0
1670 46 : aligned_degenerate_min_svalue = 0.0_dp
1671 52 : IF (.NOT. ASSOCIATED(kpoint)) RETURN
1672 46 : IF (.NOT. ASSOCIATED(kpoint%kp_env)) RETURN
1673 46 : IF (SIZE(kpoint%kp_env) == 0) RETURN
1674 46 : first_spin = 1
1675 46 : last_spin = SIZE(kpoint%kp_env(1)%kpoint_env%mos)
1676 46 : IF (PRESENT(spin_channel)) THEN
1677 42 : CPASSERT(spin_channel >= 1 .AND. spin_channel <= last_spin)
1678 : first_spin = spin_channel
1679 : last_spin = spin_channel
1680 : END IF
1681 86 : DO ispin = first_spin, last_spin
1682 : CALL prepare_wannier90_spin_mos(kpoint, qs_kpoint, matrix_s, matrix_ks, cell_to_index, &
1683 46 : sab_nl, para_env, ispin, success, reason, blocks, max_size, min_svalue)
1684 46 : IF (.NOT. success) RETURN
1685 126 : IF (blocks > 0) THEN
1686 24 : IF (aligned_degenerate_blocks == 0) aligned_degenerate_min_svalue = min_svalue
1687 24 : aligned_degenerate_min_svalue = MIN(aligned_degenerate_min_svalue, min_svalue)
1688 24 : aligned_degenerate_blocks = aligned_degenerate_blocks + blocks
1689 24 : aligned_degenerate_max_size = MAX(aligned_degenerate_max_size, max_size)
1690 : END IF
1691 : END DO
1692 46 : END SUBROUTINE prepare_wannier90_scf_mos
1693 :
1694 : ! **************************************************************************************************
1695 : !> \brief Reconstruct one spin channel with its own MO dimensions and Hamiltonian.
1696 : !> \param kpoint Full Wannier90 export k-point object.
1697 : !> \param qs_kpoint Source SCF k-point object.
1698 : !> \param matrix_s Real-space overlap matrices.
1699 : !> \param matrix_ks Spin-dependent real-space Hamiltonians.
1700 : !> \param cell_to_index Real-space cell index table.
1701 : !> \param sab_nl Overlap neighbour list.
1702 : !> \param para_env Global parallel environment.
1703 : !> \param ispin Selected spin channel.
1704 : !> \param success Whether all MOs in this channel were reconstructed.
1705 : !> \param reason Explanation if reconstruction is unavailable.
1706 : !> \param aligned_degenerate_blocks Number of aligned blocks.
1707 : !> \param aligned_degenerate_max_size Largest aligned block.
1708 : !> \param aligned_degenerate_min_svalue Smallest metric singular value.
1709 : ! **************************************************************************************************
1710 46 : SUBROUTINE prepare_wannier90_spin_mos(kpoint, qs_kpoint, matrix_s, matrix_ks, cell_to_index, &
1711 : sab_nl, para_env, ispin, success, reason, aligned_degenerate_blocks, &
1712 : aligned_degenerate_max_size, &
1713 : aligned_degenerate_min_svalue)
1714 : TYPE(kpoint_type), POINTER :: kpoint, qs_kpoint
1715 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
1716 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
1717 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1718 : POINTER :: sab_nl
1719 : TYPE(mp_para_env_type), POINTER :: para_env
1720 : INTEGER, INTENT(IN) :: ispin
1721 : LOGICAL, INTENT(OUT) :: success
1722 : CHARACTER(LEN=*), INTENT(OUT) :: reason
1723 : INTEGER, INTENT(OUT) :: aligned_degenerate_blocks, &
1724 : aligned_degenerate_max_size
1725 : REAL(KIND=dp), INTENT(OUT) :: aligned_degenerate_min_svalue
1726 :
1727 : CHARACTER(LEN=default_string_length) :: best_reason, candidate_reason
1728 : INTEGER :: aligned_blocks, aligned_max_size, candidate_aligned_blocks, &
1729 : candidate_aligned_max_size, ik, ikpgr, ikred, isym_try, min_gap_band, min_gap_kpoint, &
1730 : min_gap_spin, nao, nao_src, nmo, nmo_src, nsymmetry, num_candidates
1731 46 : INTEGER, ALLOCATABLE, DIMENSION(:) :: source_kpoint, sym_index
1732 : INTEGER, DIMENSION(2) :: kp_range, source_kp_range
1733 : LOGICAL :: my_kpgrp, my_source_kpgrp, ok, &
1734 : source_window
1735 : REAL(KIND=dp) :: aligned_min_svalue, band_gap, best_residual, candidate_residual, &
1736 : degenerate_band_tol, local_min_band_gap, min_band_gap, source_owner_count, &
1737 : source_window_min_svalue
1738 46 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigenvalues_buffer, occupation_buffer, &
1739 46 : source_eigenvalues_buffer, &
1740 46 : source_occupation_buffer
1741 46 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues, occupation
1742 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
1743 : TYPE(cp_cfm_type) :: dst_cfm_full, src_cfm, src_cfm_full
1744 : TYPE(cp_cfm_type), POINTER :: destination_cfm, source_cfm
1745 : TYPE(cp_cfm_type), TARGET :: dst_cfm, empty_destination
1746 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_source, matrix_struct_work
1747 : TYPE(kpoint_env_type), POINTER :: kp, kp_source
1748 : TYPE(kpoint_sym_type), POINTER :: kpsym
1749 :
1750 46 : success = .FALSE.
1751 46 : reason = ""
1752 46 : aligned_degenerate_blocks = 0
1753 46 : aligned_degenerate_max_size = 0
1754 46 : aligned_degenerate_min_svalue = HUGE(1.0_dp)
1755 46 : NULLIFY (matrix_struct_source, matrix_struct_work)
1756 :
1757 46 : IF (.NOT. ASSOCIATED(kpoint)) THEN
1758 0 : reason = "internal Wannier90 k-point object is not available"
1759 0 : RETURN
1760 : END IF
1761 46 : IF (.NOT. ASSOCIATED(qs_kpoint)) THEN
1762 0 : reason = "SCF k-point object is not available"
1763 0 : RETURN
1764 : END IF
1765 46 : IF (.NOT. ASSOCIATED(kpoint%kp_env) .OR. .NOT. ASSOCIATED(qs_kpoint%kp_env)) THEN
1766 0 : reason = "k-point MO environments are not initialized"
1767 0 : RETURN
1768 : END IF
1769 46 : IF (.NOT. ASSOCIATED(kpoint%blacs_env)) THEN
1770 0 : reason = "Wannier90 k-point BLACS environment is not initialized"
1771 0 : RETURN
1772 : END IF
1773 :
1774 46 : CALL build_wannier90_scf_mapping(kpoint, qs_kpoint, source_kpoint, sym_index, ok, reason)
1775 46 : IF (.NOT. ok) RETURN
1776 626 : nsymmetry = COUNT(sym_index > 0)
1777 :
1778 46 : kp => kpoint%kp_env(1)%kpoint_env
1779 46 : IF (.NOT. ASSOCIATED(kp%mos(ispin)%cmo_coeff)) THEN
1780 0 : reason = "Wannier90 export k-point MOs are not complex-valued"
1781 0 : DEALLOCATE (source_kpoint, sym_index)
1782 0 : RETURN
1783 : END IF
1784 46 : CALL get_mo_set(kp%mos(ispin), nao=nao, nmo=nmo)
1785 :
1786 46 : kp_source => qs_kpoint%kp_env(1)%kpoint_env
1787 46 : IF (.NOT. ASSOCIATED(kp_source%mos(ispin)%cmo_coeff)) THEN
1788 0 : reason = "SCF MOs are real-valued; complex symmetry phases cannot be reconstructed"
1789 0 : DEALLOCATE (source_kpoint, sym_index)
1790 0 : RETURN
1791 : END IF
1792 46 : CALL get_mo_set(kp_source%mos(ispin), nao=nao_src, nmo=nmo_src)
1793 46 : CALL para_env%max(nao_src)
1794 46 : CALL para_env%max(nmo_src)
1795 46 : IF (nao_src /= nao) THEN
1796 0 : reason = "SCF and Wannier90 MO bases have different AO dimensions"
1797 0 : DEALLOCATE (source_kpoint, sym_index)
1798 0 : RETURN
1799 : END IF
1800 46 : IF (nmo_src < nmo) THEN
1801 0 : reason = "SCF MO set has fewer bands than the Wannier90 export"
1802 0 : DEALLOCATE (source_kpoint, sym_index)
1803 0 : RETURN
1804 : END IF
1805 46 : source_window = nmo_src > nmo
1806 46 : degenerate_band_tol = 1.0e-8_dp
1807 46 : CALL get_kpoint_info(qs_kpoint, kp_range=source_kp_range)
1808 46 : IF (source_kp_range(1) /= 1 .OR. source_kp_range(2) /= qs_kpoint%nkp) THEN
1809 6 : reason = "SCF k-point symmetry data are distributed over k-point parallel groups"
1810 6 : DEALLOCATE (source_kpoint, sym_index)
1811 6 : RETURN
1812 : END IF
1813 : ! Positive symmetry entries require atom/AO rotations and Bloch phases. Degenerate subspaces
1814 : ! fully contained in the exported band window are aligned below; only guard when the Wannier90
1815 : ! window cuts through a degenerate SCF manifold at the upper band edge.
1816 40 : IF (nsymmetry > 0 .AND. nmo_src > nmo) THEN
1817 0 : local_min_band_gap = HUGE(1.0_dp)
1818 0 : min_gap_band = nmo
1819 0 : min_gap_kpoint = 0
1820 0 : min_gap_spin = 0
1821 0 : DO ikred = source_kp_range(1), source_kp_range(2)
1822 0 : ikpgr = ikred - source_kp_range(1) + 1
1823 0 : kp_source => qs_kpoint%kp_env(ikpgr)%kpoint_env
1824 0 : CALL get_mo_set(kp_source%mos(ispin), eigenvalues=eigenvalues)
1825 0 : band_gap = ABS(eigenvalues(nmo + 1) - eigenvalues(nmo))
1826 0 : IF (band_gap < local_min_band_gap) THEN
1827 0 : local_min_band_gap = band_gap
1828 0 : min_gap_band = nmo
1829 0 : min_gap_kpoint = ikred
1830 0 : min_gap_spin = ispin
1831 : END IF
1832 : END DO
1833 0 : min_band_gap = local_min_band_gap
1834 0 : CALL para_env%min(min_band_gap)
1835 0 : IF (ABS(local_min_band_gap - min_band_gap) > degenerate_band_tol*EPSILON(1.0_dp)) THEN
1836 0 : min_gap_kpoint = 0
1837 0 : min_gap_spin = 0
1838 : END IF
1839 0 : CALL para_env%max(min_gap_kpoint)
1840 0 : CALL para_env%max(min_gap_spin)
1841 0 : CALL para_env%max(min_gap_band)
1842 0 : IF (min_band_gap < degenerate_band_tol) THEN
1843 : WRITE (reason, "(A,ES9.2,A,I0,A,I0,A,I0)") &
1844 0 : "degenerate atom/AO W90 reuse guarded: edge gap=", min_band_gap, ", k=", &
1845 0 : min_gap_kpoint, ", s=", min_gap_spin, ", nband=", min_gap_band
1846 0 : DEALLOCATE (source_kpoint, sym_index)
1847 0 : RETURN
1848 : END IF
1849 : END IF
1850 40 : blacs_env => kpoint%blacs_env
1851 : CALL cp_fm_struct_create(matrix_struct_work, nrow_global=nao, ncol_global=nmo, &
1852 40 : para_env=para_env, context=blacs_env)
1853 40 : CALL cp_cfm_create(src_cfm, matrix_struct_work)
1854 40 : CALL cp_cfm_create(dst_cfm, matrix_struct_work)
1855 40 : IF (source_window) THEN
1856 : CALL cp_fm_struct_create(matrix_struct_source, nrow_global=nao, ncol_global=nmo_src, &
1857 0 : para_env=para_env, context=blacs_env)
1858 0 : CALL cp_cfm_create(src_cfm_full, matrix_struct_source)
1859 0 : CALL cp_cfm_create(dst_cfm_full, matrix_struct_source)
1860 : END IF
1861 0 : ALLOCATE (eigenvalues_buffer(nmo), occupation_buffer(nmo), &
1862 280 : source_eigenvalues_buffer(nmo_src), source_occupation_buffer(nmo_src))
1863 :
1864 40 : CALL get_kpoint_info(kpoint, kp_range=kp_range)
1865 40 : CALL get_kpoint_info(qs_kpoint, kp_range=source_kp_range)
1866 :
1867 572 : DO ik = 1, kpoint%nkp
1868 532 : ikred = source_kpoint(ik)
1869 532 : my_kpgrp = (ik >= kp_range(1) .AND. ik <= kp_range(2))
1870 532 : my_source_kpgrp = (ikred >= source_kp_range(1) .AND. ikred <= source_kp_range(2))
1871 2628 : source_eigenvalues_buffer(1:nmo_src) = 0.0_dp
1872 2628 : source_occupation_buffer(1:nmo_src) = 0.0_dp
1873 532 : NULLIFY (source_cfm)
1874 532 : IF (my_source_kpgrp) THEN
1875 532 : ikpgr = ikred - source_kp_range(1) + 1
1876 532 : kp_source => qs_kpoint%kp_env(ikpgr)%kpoint_env
1877 532 : source_cfm => kp_source%mos(ispin)%cmo_coeff
1878 : CALL get_mo_set(kp_source%mos(ispin), eigenvalues=eigenvalues, &
1879 532 : occupation_numbers=occupation)
1880 2628 : source_eigenvalues_buffer(1:nmo_src) = eigenvalues(1:nmo_src)
1881 2628 : source_occupation_buffer(1:nmo_src) = occupation(1:nmo_src)
1882 : END IF
1883 532 : IF (source_window) THEN
1884 0 : CALL wannier90_copy_cfm_general(source_cfm, src_cfm_full, para_env)
1885 : ELSE
1886 532 : CALL wannier90_copy_cfm_general(source_cfm, src_cfm, para_env)
1887 : END IF
1888 532 : IF (my_source_kpgrp) THEN
1889 532 : source_owner_count = 1.0_dp
1890 : ELSE
1891 0 : source_owner_count = 0.0_dp
1892 : END IF
1893 532 : CALL para_env%sum(source_owner_count)
1894 532 : CALL para_env%sum(source_eigenvalues_buffer)
1895 532 : CALL para_env%sum(source_occupation_buffer)
1896 532 : IF (source_owner_count > 0.0_dp) THEN
1897 : source_eigenvalues_buffer(1:nmo_src) = &
1898 2628 : source_eigenvalues_buffer(1:nmo_src)/source_owner_count
1899 : source_occupation_buffer(1:nmo_src) = &
1900 2628 : source_occupation_buffer(1:nmo_src)/source_owner_count
1901 : END IF
1902 2628 : eigenvalues_buffer(1:nmo) = source_eigenvalues_buffer(1:nmo)
1903 2628 : occupation_buffer(1:nmo) = source_occupation_buffer(1:nmo)
1904 532 : IF (source_window) THEN
1905 0 : CALL cp_cfm_to_cfm(src_cfm_full, src_cfm, nmo)
1906 : END IF
1907 :
1908 532 : ok = .FALSE.
1909 532 : reason = ""
1910 532 : aligned_blocks = 0
1911 532 : aligned_max_size = 0
1912 532 : aligned_min_svalue = 0.0_dp
1913 532 : IF (sym_index(ik) > 0) THEN
1914 412 : kpsym => qs_kpoint%kp_sym(ikred)%kpoint_sym
1915 412 : IF (ASSOCIATED(kpsym)) THEN
1916 412 : best_reason = ""
1917 412 : best_residual = HUGE(1.0_dp)
1918 412 : num_candidates = 0
1919 : ! Little-group operations can reach the same target k-point; keep the first valid eigenspace.
1920 3198 : DO isym_try = 1, kpsym%nwred
1921 3198 : IF (.NOT. kpoint_same_periodic(kpoint%xkp(1:3, ik), &
1922 : kpsym%xkp(1:3, isym_try))) CYCLE
1923 412 : num_candidates = num_candidates + 1
1924 : CALL kpoint_transform_scf_mo(src_cfm, dst_cfm, &
1925 : qs_kpoint, ikred, isym_try, ok, &
1926 412 : candidate_reason)
1927 412 : IF (.NOT. ok) THEN
1928 0 : reason = candidate_reason
1929 : CYCLE
1930 : END IF
1931 : CALL ritz_stabilize_wannier90_subspace(dst_cfm, matrix_s, matrix_ks, &
1932 : kpoint%xkp(1:3, ik), cell_to_index, &
1933 : sab_nl, ispin, eigenvalues_buffer, &
1934 : degenerate_band_tol, ok, candidate_reason, &
1935 : candidate_aligned_blocks, &
1936 : candidate_aligned_max_size, &
1937 412 : aligned_min_svalue, candidate_residual)
1938 412 : IF (candidate_residual < best_residual) THEN
1939 412 : best_residual = candidate_residual
1940 412 : best_reason = candidate_reason
1941 : END IF
1942 412 : IF (.NOT. ok) THEN
1943 0 : IF (source_window) THEN
1944 : CALL kpoint_transform_scf_mo(src_cfm_full, dst_cfm_full, qs_kpoint, &
1945 0 : ikred, isym_try, ok, candidate_reason)
1946 0 : IF (ok) THEN
1947 : CALL ritz_reconstruct_wannier90_window(dst_cfm_full, dst_cfm, matrix_s, &
1948 : matrix_ks, kpoint%xkp(1:3, ik), &
1949 : cell_to_index, sab_nl, ispin, &
1950 : eigenvalues_buffer, nmo, ok, &
1951 : candidate_reason, source_window_min_svalue, &
1952 0 : candidate_residual)
1953 0 : IF (candidate_residual < best_residual) THEN
1954 0 : best_residual = candidate_residual
1955 0 : best_reason = candidate_reason
1956 : END IF
1957 : END IF
1958 : ELSE
1959 : CALL ritz_reconstruct_wannier90_window(dst_cfm, dst_cfm, matrix_s, matrix_ks, &
1960 : kpoint%xkp(1:3, ik), cell_to_index, &
1961 : sab_nl, ispin, eigenvalues_buffer, nmo, &
1962 : ok, candidate_reason, source_window_min_svalue, &
1963 0 : candidate_residual)
1964 0 : IF (candidate_residual < best_residual) THEN
1965 0 : best_residual = candidate_residual
1966 0 : best_reason = candidate_reason
1967 : END IF
1968 : END IF
1969 : END IF
1970 412 : IF (ok) THEN
1971 412 : aligned_blocks = candidate_aligned_blocks
1972 412 : aligned_max_size = candidate_aligned_max_size
1973 412 : sym_index(ik) = isym_try
1974 412 : EXIT
1975 : END IF
1976 412 : reason = candidate_reason
1977 : END DO
1978 412 : IF (.NOT. ok .AND. num_candidates > 0 .AND. best_residual < HUGE(1.0_dp)) THEN
1979 : WRITE (reason, "(A,I0,A,ES9.2,A,I0,A,A32)") &
1980 0 : "atom/AO W90 guarded: best/", num_candidates, "=", best_residual, &
1981 0 : " k=", ik, " ", TRIM(best_reason)
1982 412 : ELSE IF (.NOT. ok .AND. num_candidates == 0) THEN
1983 0 : reason = "no matching SCF symmetry operation candidate"
1984 : END IF
1985 : ELSE
1986 0 : reason = "SCF k-point symmetry operation is not available"
1987 : END IF
1988 : ELSE
1989 : CALL kpoint_transform_scf_mo(src_cfm, dst_cfm, qs_kpoint, &
1990 120 : ikred, sym_index(ik), ok, reason)
1991 : END IF
1992 532 : IF (ok .AND. sym_index(ik) <= 0) THEN
1993 : ! Even a direct k-point copy must be a closed H(k),S(k) subspace. This catches
1994 : ! incomplete degenerate band windows before they can be exported to Wannier90.
1995 : CALL ritz_stabilize_wannier90_subspace(dst_cfm, matrix_s, matrix_ks, &
1996 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, &
1997 : ispin, eigenvalues_buffer, degenerate_band_tol, &
1998 : ok, reason, aligned_blocks, aligned_max_size, &
1999 120 : aligned_min_svalue, candidate_residual)
2000 120 : IF (.NOT. ok .AND. source_window) THEN
2001 : CALL kpoint_transform_scf_mo(src_cfm_full, dst_cfm_full, qs_kpoint, &
2002 0 : ikred, sym_index(ik), ok, reason)
2003 0 : IF (ok) THEN
2004 : CALL ritz_reconstruct_wannier90_window(dst_cfm_full, dst_cfm, matrix_s, matrix_ks, &
2005 : kpoint%xkp(1:3, ik), cell_to_index, &
2006 : sab_nl, ispin, eigenvalues_buffer, nmo, ok, &
2007 : reason, source_window_min_svalue, &
2008 0 : candidate_residual)
2009 : END IF
2010 120 : ELSE IF (.NOT. ok) THEN
2011 : CALL ritz_reconstruct_wannier90_window(dst_cfm, dst_cfm, matrix_s, matrix_ks, &
2012 : kpoint%xkp(1:3, ik), &
2013 : cell_to_index, sab_nl, ispin, &
2014 : eigenvalues_buffer, nmo, ok, reason, &
2015 0 : source_window_min_svalue, candidate_residual)
2016 : END IF
2017 : END IF
2018 532 : IF (.NOT. ok) THEN
2019 0 : CALL cp_cfm_release(src_cfm)
2020 0 : CALL cp_cfm_release(dst_cfm)
2021 0 : CALL cp_fm_struct_release(matrix_struct_work)
2022 0 : IF (source_window) THEN
2023 0 : CALL cp_cfm_release(src_cfm_full)
2024 0 : CALL cp_cfm_release(dst_cfm_full)
2025 0 : CALL cp_fm_struct_release(matrix_struct_source)
2026 : END IF
2027 0 : DEALLOCATE (source_kpoint, sym_index, eigenvalues_buffer, occupation_buffer, &
2028 0 : source_eigenvalues_buffer, source_occupation_buffer)
2029 0 : RETURN
2030 : END IF
2031 532 : IF (sym_index(ik) /= 0) THEN
2032 456 : aligned_degenerate_blocks = aligned_degenerate_blocks + aligned_blocks
2033 456 : aligned_degenerate_max_size = MAX(aligned_degenerate_max_size, aligned_max_size)
2034 456 : IF (aligned_blocks > 0) THEN
2035 360 : aligned_degenerate_min_svalue = MIN(aligned_degenerate_min_svalue, aligned_min_svalue)
2036 : END IF
2037 : END IF
2038 :
2039 532 : IF (my_kpgrp) THEN
2040 532 : ikpgr = ik - kp_range(1) + 1
2041 532 : kp => kpoint%kp_env(ikpgr)%kpoint_env
2042 : CALL get_mo_set(kp%mos(ispin), eigenvalues=eigenvalues, &
2043 532 : occupation_numbers=occupation)
2044 2628 : eigenvalues(1:nmo) = eigenvalues_buffer(1:nmo)
2045 2628 : occupation(1:nmo) = occupation_buffer(1:nmo)
2046 532 : destination_cfm => kp%mos(ispin)%cmo_coeff
2047 : ELSE
2048 : destination_cfm => empty_destination
2049 : END IF
2050 572 : CALL cp_cfm_copy_general(dst_cfm, destination_cfm, para_env)
2051 : END DO
2052 :
2053 40 : CALL cp_cfm_release(src_cfm)
2054 40 : CALL cp_cfm_release(dst_cfm)
2055 40 : CALL cp_fm_struct_release(matrix_struct_work)
2056 40 : IF (source_window) THEN
2057 0 : CALL cp_cfm_release(src_cfm_full)
2058 0 : CALL cp_cfm_release(dst_cfm_full)
2059 0 : CALL cp_fm_struct_release(matrix_struct_source)
2060 : END IF
2061 0 : DEALLOCATE (source_kpoint, sym_index, eigenvalues_buffer, occupation_buffer, &
2062 40 : source_eigenvalues_buffer, source_occupation_buffer)
2063 40 : IF (aligned_degenerate_blocks == 0) aligned_degenerate_min_svalue = 0.0_dp
2064 40 : success = .TRUE.
2065 :
2066 224 : END SUBROUTINE prepare_wannier90_spin_mos
2067 :
2068 : ! **************************************************************************************************
2069 : !> \brief Save a full-mesh Wannier90 MO reference on all ranks for diagnostic validation.
2070 : !> \param kpoint full Wannier90 export k-point object
2071 : !> \param ispin Selected spin channel.
2072 : !> \param para_env global parallel environment
2073 : !> \param mo MO coefficients for the selected spin, indexed as AO, MO, k-point
2074 : !> \param eigenvalue_snapshot MO eigenvalues for the selected spin, indexed as MO, k-point
2075 : ! **************************************************************************************************
2076 14 : SUBROUTINE save_wannier90_mo_snapshot(kpoint, ispin, para_env, mo, &
2077 : eigenvalue_snapshot)
2078 : TYPE(kpoint_type), POINTER :: kpoint
2079 : INTEGER, INTENT(IN) :: ispin
2080 : TYPE(mp_para_env_type), POINTER :: para_env
2081 : COMPLEX(KIND=dp), ALLOCATABLE, &
2082 : DIMENSION(:, :, :), INTENT(OUT) :: mo
2083 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), &
2084 : INTENT(OUT) :: eigenvalue_snapshot
2085 :
2086 : INTEGER :: ik, ikpgr, nao, nkp, nmo
2087 : INTEGER, DIMENSION(2) :: kp_range
2088 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: owner_weight
2089 14 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues
2090 : TYPE(kpoint_env_type), POINTER :: kp
2091 :
2092 14 : CALL get_kpoint_info(kpoint, nkp=nkp, kp_range=kp_range)
2093 14 : kp => kpoint%kp_env(1)%kpoint_env
2094 14 : CALL get_mo_set(kp%mos(ispin), nao=nao, nmo=nmo)
2095 0 : ALLOCATE (mo(nao, nmo, nkp), &
2096 140 : eigenvalue_snapshot(nmo, nkp), owner_weight(nkp))
2097 14 : mo(:, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
2098 14 : eigenvalue_snapshot(:, :) = 0.0_dp
2099 14 : owner_weight(:) = 0.0_dp
2100 350 : DO ik = kp_range(1), kp_range(2)
2101 336 : ikpgr = ik - kp_range(1) + 1
2102 336 : kp => kpoint%kp_env(ikpgr)%kpoint_env
2103 336 : CALL cp_cfm_get_submatrix(kp%mos(ispin)%cmo_coeff, mo(:, :, ik))
2104 336 : CALL get_mo_set(kp%mos(ispin), eigenvalues=eigenvalues)
2105 1600 : eigenvalue_snapshot(1:nmo, ik) = eigenvalues(1:nmo)
2106 350 : owner_weight(ik) = 1.0_dp
2107 : END DO
2108 14 : CALL para_env%sum(mo)
2109 14 : CALL para_env%sum(eigenvalue_snapshot)
2110 14 : CALL para_env%sum(owner_weight)
2111 350 : DO ik = 1, nkp
2112 350 : IF (owner_weight(ik) > 0.0_dp) THEN
2113 50000 : mo(:, :, ik) = mo(:, :, ik)/owner_weight(ik)
2114 : eigenvalue_snapshot(:, ik) = &
2115 1600 : eigenvalue_snapshot(:, ik)/owner_weight(ik)
2116 : END IF
2117 : END DO
2118 14 : DEALLOCATE (owner_weight)
2119 :
2120 14 : END SUBROUTINE save_wannier90_mo_snapshot
2121 :
2122 : ! **************************************************************************************************
2123 : !> \brief Restore a full-mesh Wannier90 MO reference after a failed diagnostic reuse attempt.
2124 : !> \param kpoint full Wannier90 export k-point object
2125 : !> \param ispin Selected spin channel.
2126 : !> \param mo complex MO coefficient snapshot
2127 : !> \param eigenvalue_snapshot MO eigenvalue snapshot
2128 : ! **************************************************************************************************
2129 2 : SUBROUTINE restore_wannier90_mo_snapshot(kpoint, ispin, mo, eigenvalue_snapshot)
2130 : TYPE(kpoint_type), POINTER :: kpoint
2131 : INTEGER, INTENT(IN) :: ispin
2132 : COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: mo
2133 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: eigenvalue_snapshot
2134 :
2135 : INTEGER :: ik, ikpgr, nmo
2136 : INTEGER, DIMENSION(2) :: kp_range
2137 2 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues
2138 : TYPE(kpoint_env_type), POINTER :: kp
2139 :
2140 2 : CALL get_kpoint_info(kpoint, kp_range=kp_range)
2141 2 : nmo = SIZE(eigenvalue_snapshot, 1)
2142 18 : DO ik = kp_range(1), kp_range(2)
2143 16 : ikpgr = ik - kp_range(1) + 1
2144 16 : kp => kpoint%kp_env(ikpgr)%kpoint_env
2145 16 : CALL cp_cfm_set_submatrix(kp%mos(ispin)%cmo_coeff, mo(:, :, ik))
2146 16 : CALL get_mo_set(kp%mos(ispin), eigenvalues=eigenvalues)
2147 146 : eigenvalues(1:nmo) = eigenvalue_snapshot(1:nmo, ik)
2148 : END DO
2149 :
2150 2 : END SUBROUTINE restore_wannier90_mo_snapshot
2151 :
2152 : ! **************************************************************************************************
2153 : !> \brief Validate current Wannier90 MOs against a saved full-mesh diagonalization reference.
2154 : !> \param kpoint full Wannier90 export k-point object
2155 : !> \param matrix_s real-space overlap matrix
2156 : !> \param cell_to_index real-space cell index table
2157 : !> \param sab_nl overlap neighbor list
2158 : !> \param para_env global parallel environment
2159 : !> \param ispin Selected spin channel.
2160 : !> \param reference_mo complex MO coefficient reference
2161 : !> \param reference_eigenvalues MO eigenvalue reference
2162 : !> \param success true if the reconstructed MOs match the reference subspaces
2163 : !> \param max_subspace_deviation largest deviation of S(k)-metric singular values from one
2164 : !> \param min_svalue smallest S(k)-metric singular value
2165 : !> \param max_eigenvalue_deviation largest eigenvalue deviation
2166 : ! **************************************************************************************************
2167 14 : SUBROUTINE validate_wannier90_reused_mos(kpoint, matrix_s, cell_to_index, sab_nl, para_env, ispin, &
2168 14 : reference_mo, reference_eigenvalues, &
2169 : success, max_subspace_deviation, min_svalue, &
2170 : max_eigenvalue_deviation)
2171 : TYPE(kpoint_type), POINTER :: kpoint
2172 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s
2173 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
2174 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
2175 : POINTER :: sab_nl
2176 : TYPE(mp_para_env_type), POINTER :: para_env
2177 : INTEGER, INTENT(IN) :: ispin
2178 : COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: reference_mo
2179 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: reference_eigenvalues
2180 : LOGICAL, INTENT(OUT) :: success
2181 : REAL(KIND=dp), INTENT(OUT) :: max_subspace_deviation, min_svalue, &
2182 : max_eigenvalue_deviation
2183 :
2184 : REAL(KIND=dp), PARAMETER :: eigenvalue_tol = 1.0e-8_dp, &
2185 : subspace_tol = 1.0e-4_dp
2186 :
2187 : INTEGER :: ik, ikpgr, nao, nkp, nmo
2188 : INTEGER, DIMENSION(2) :: kp_range
2189 : LOGICAL :: my_kpgrp, ok
2190 : REAL(KIND=dp) :: candidate_deviation, candidate_svalue, &
2191 : owner_count
2192 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigenvalue_buffer
2193 14 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues
2194 : TYPE(cp_cfm_type) :: cand_cfm, ref_cfm
2195 : TYPE(cp_cfm_type), POINTER :: candidate_source
2196 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_work
2197 : TYPE(kpoint_env_type), POINTER :: kp
2198 :
2199 14 : success = .FALSE.
2200 14 : max_subspace_deviation = 0.0_dp
2201 14 : min_svalue = HUGE(1.0_dp)
2202 14 : max_eigenvalue_deviation = 0.0_dp
2203 14 : NULLIFY (matrix_struct_work)
2204 :
2205 14 : CALL get_kpoint_info(kpoint, nkp=nkp, kp_range=kp_range)
2206 14 : kp => kpoint%kp_env(1)%kpoint_env
2207 14 : CALL get_mo_set(kp%mos(ispin), nao=nao, nmo=nmo)
2208 14 : CPASSERT(SIZE(reference_mo, 1) == nao)
2209 14 : CPASSERT(SIZE(reference_mo, 2) == nmo)
2210 14 : CPASSERT(SIZE(reference_mo, 3) == nkp)
2211 :
2212 14 : matrix_struct_work => kp%mos(ispin)%cmo_coeff%matrix_struct
2213 14 : CALL cp_cfm_create(ref_cfm, matrix_struct_work)
2214 14 : CALL cp_cfm_create(cand_cfm, matrix_struct_work)
2215 42 : ALLOCATE (eigenvalue_buffer(nmo))
2216 :
2217 350 : DO ik = 1, nkp
2218 336 : CALL cp_cfm_set_submatrix(ref_cfm, reference_mo(:, :, ik))
2219 336 : my_kpgrp = (ik >= kp_range(1) .AND. ik <= kp_range(2))
2220 0 : NULLIFY (candidate_source)
2221 : IF (my_kpgrp) THEN
2222 336 : ikpgr = ik - kp_range(1) + 1
2223 336 : kp => kpoint%kp_env(ikpgr)%kpoint_env
2224 336 : candidate_source => kp%mos(ispin)%cmo_coeff
2225 336 : CALL get_mo_set(kp%mos(ispin), eigenvalues=eigenvalues)
2226 1600 : eigenvalue_buffer(1:nmo) = eigenvalues(1:nmo)
2227 : ELSE
2228 0 : eigenvalue_buffer(1:nmo) = 0.0_dp
2229 : END IF
2230 336 : CALL wannier90_copy_cfm_general(candidate_source, cand_cfm, para_env)
2231 336 : IF (my_kpgrp) THEN
2232 336 : owner_count = 1.0_dp
2233 : ELSE
2234 0 : owner_count = 0.0_dp
2235 : END IF
2236 336 : CALL para_env%sum(owner_count)
2237 336 : CALL para_env%sum(eigenvalue_buffer)
2238 1600 : IF (owner_count > 0.0_dp) eigenvalue_buffer(1:nmo) = eigenvalue_buffer(1:nmo)/owner_count
2239 : max_eigenvalue_deviation = MAX(max_eigenvalue_deviation, &
2240 : MAXVAL(ABS(eigenvalue_buffer(1:nmo) - &
2241 1600 : reference_eigenvalues(1:nmo, ik))))
2242 : CALL measure_wannier90_subspace_error(ref_cfm, cand_cfm, matrix_s, &
2243 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, para_env, &
2244 336 : ok, candidate_deviation, candidate_svalue)
2245 350 : IF (.NOT. ok) THEN
2246 0 : max_subspace_deviation = HUGE(1.0_dp)
2247 : ELSE
2248 336 : max_subspace_deviation = MAX(max_subspace_deviation, candidate_deviation)
2249 336 : min_svalue = MIN(min_svalue, candidate_svalue)
2250 : END IF
2251 : END DO
2252 14 : CALL para_env%max(max_subspace_deviation)
2253 14 : CALL para_env%min(min_svalue)
2254 14 : CALL para_env%max(max_eigenvalue_deviation)
2255 14 : success = max_subspace_deviation < subspace_tol .AND. max_eigenvalue_deviation < eigenvalue_tol
2256 :
2257 14 : DEALLOCATE (eigenvalue_buffer)
2258 14 : CALL cp_cfm_release(ref_cfm)
2259 14 : CALL cp_cfm_release(cand_cfm)
2260 :
2261 28 : END SUBROUTINE validate_wannier90_reused_mos
2262 :
2263 : ! **************************************************************************************************
2264 : !> \brief Compare atom/AO reuse candidates directly to the full-mesh reference MOs.
2265 : !> \param kpoint full Wannier90 export k-point object holding reference MOs
2266 : !> \param qs_kpoint SCF k-point object
2267 : !> \param matrix_s real-space overlap matrix
2268 : !> \param matrix_ks real-space Kohn-Sham matrix
2269 : !> \param cell_to_index real-space cell index table
2270 : !> \param sab_nl overlap neighbor list
2271 : !> \param para_env global parallel environment
2272 : !> \param iw output unit
2273 : !> \param ispin Selected spin channel.
2274 : !> \param max_subspace_deviation largest best-candidate subspace deviation
2275 : !> \param min_svalue smallest best-candidate singular value
2276 : !> \param max_metric_deviation largest S(k)-metric deviation of a candidate
2277 : !> \param max_residual largest H(k),S(k) eigen-residual of a candidate
2278 : ! **************************************************************************************************
2279 14 : SUBROUTINE diagnose_wannier90_scf_reuse_candidates(kpoint, qs_kpoint, matrix_s, matrix_ks, &
2280 : cell_to_index, sab_nl, para_env, iw, ispin, &
2281 : max_subspace_deviation, min_svalue, &
2282 : max_metric_deviation, max_residual)
2283 : TYPE(kpoint_type), POINTER :: kpoint, qs_kpoint
2284 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
2285 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
2286 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
2287 : POINTER :: sab_nl
2288 : TYPE(mp_para_env_type), POINTER :: para_env
2289 : INTEGER, INTENT(IN) :: iw, ispin
2290 : REAL(KIND=dp), INTENT(OUT) :: max_subspace_deviation, min_svalue, &
2291 : max_metric_deviation, max_residual
2292 :
2293 : REAL(KIND=dp), PARAMETER :: print_tol = 1.0e-4_dp, &
2294 : residual_print_tol = 1.0e-3_dp
2295 :
2296 : CHARACTER(LEN=default_string_length) :: reason
2297 : INTEGER :: ik, ikpgr, ikred, isym_try, nao, &
2298 : nao_src, nkp, nmo, nmo_src
2299 14 : INTEGER, ALLOCATABLE, DIMENSION(:) :: source_kpoint, sym_index
2300 : INTEGER, DIMENSION(2) :: kp_range, source_kp_range
2301 : LOGICAL :: my_kpgrp, ok, source_window
2302 : REAL(KIND=dp) :: best_deviation, best_metric_deviation, best_residual, best_svalue, &
2303 : candidate_deviation, candidate_metric_deviation, candidate_metric_min, &
2304 : candidate_residual, candidate_svalue, owner_count, ref_metric_deviation, ref_metric_min, &
2305 : ref_residual
2306 14 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigenvalues_buffer
2307 14 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues
2308 : TYPE(cp_cfm_type) :: dst_cfm, ref_cfm, src_cfm, src_cfm_full
2309 : TYPE(cp_cfm_type), POINTER :: source_cfm
2310 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_source, matrix_struct_work
2311 : TYPE(kpoint_env_type), POINTER :: kp, kp_source
2312 : TYPE(kpoint_sym_type), POINTER :: kpsym
2313 :
2314 14 : max_subspace_deviation = HUGE(1.0_dp)
2315 14 : min_svalue = 0.0_dp
2316 14 : max_metric_deviation = HUGE(1.0_dp)
2317 14 : max_residual = HUGE(1.0_dp)
2318 14 : NULLIFY (matrix_struct_source, matrix_struct_work)
2319 :
2320 14 : CALL build_wannier90_scf_mapping(kpoint, qs_kpoint, source_kpoint, sym_index, ok, reason)
2321 14 : IF (.NOT. ok) RETURN
2322 14 : kp => kpoint%kp_env(1)%kpoint_env
2323 14 : kp_source => qs_kpoint%kp_env(1)%kpoint_env
2324 14 : IF (.NOT. ASSOCIATED(kp%mos(ispin)%cmo_coeff) .OR. &
2325 : .NOT. ASSOCIATED(kp_source%mos(ispin)%cmo_coeff)) THEN
2326 0 : DEALLOCATE (source_kpoint, sym_index)
2327 0 : RETURN
2328 : END IF
2329 14 : CALL get_mo_set(kp%mos(ispin), nao=nao, nmo=nmo)
2330 14 : CALL get_mo_set(kp_source%mos(ispin), nao=nao_src, nmo=nmo_src)
2331 14 : CALL para_env%max(nao_src)
2332 14 : CALL para_env%max(nmo_src)
2333 14 : IF (nao_src /= nao .OR. nmo_src < nmo) THEN
2334 0 : DEALLOCATE (source_kpoint, sym_index)
2335 0 : RETURN
2336 : END IF
2337 14 : source_window = nmo_src > nmo
2338 14 : CALL get_kpoint_info(kpoint, nkp=nkp, kp_range=kp_range)
2339 14 : CALL get_kpoint_info(qs_kpoint, kp_range=source_kp_range)
2340 14 : IF (source_kp_range(1) /= 1 .OR. source_kp_range(2) /= qs_kpoint%nkp) THEN
2341 0 : DEALLOCATE (source_kpoint, sym_index)
2342 0 : RETURN
2343 : END IF
2344 :
2345 : CALL cp_fm_struct_create(matrix_struct_work, nrow_global=nao, ncol_global=nmo, &
2346 14 : para_env=para_env, context=kpoint%blacs_env)
2347 14 : CALL cp_cfm_create(ref_cfm, matrix_struct_work)
2348 14 : CALL cp_cfm_create(src_cfm, matrix_struct_work)
2349 14 : CALL cp_cfm_create(dst_cfm, matrix_struct_work)
2350 42 : ALLOCATE (eigenvalues_buffer(nmo))
2351 14 : IF (source_window) THEN
2352 : CALL cp_fm_struct_create(matrix_struct_source, nrow_global=nao, ncol_global=nmo_src, &
2353 0 : para_env=para_env, context=kpoint%blacs_env)
2354 0 : CALL cp_cfm_create(src_cfm_full, matrix_struct_source)
2355 : END IF
2356 :
2357 14 : max_subspace_deviation = 0.0_dp
2358 14 : min_svalue = HUGE(1.0_dp)
2359 14 : max_metric_deviation = 0.0_dp
2360 14 : max_residual = 0.0_dp
2361 350 : DO ik = 1, nkp
2362 336 : my_kpgrp = (ik >= kp_range(1) .AND. ik <= kp_range(2))
2363 0 : NULLIFY (source_cfm)
2364 : IF (my_kpgrp) THEN
2365 336 : ikpgr = ik - kp_range(1) + 1
2366 336 : kp => kpoint%kp_env(ikpgr)%kpoint_env
2367 336 : source_cfm => kp%mos(ispin)%cmo_coeff
2368 : END IF
2369 336 : CALL wannier90_copy_cfm_general(source_cfm, ref_cfm, para_env)
2370 :
2371 336 : ikred = source_kpoint(ik)
2372 336 : my_kpgrp = (ikred >= source_kp_range(1) .AND. ikred <= source_kp_range(2))
2373 0 : NULLIFY (source_cfm)
2374 : IF (my_kpgrp) THEN
2375 336 : ikpgr = ikred - source_kp_range(1) + 1
2376 336 : kp_source => qs_kpoint%kp_env(ikpgr)%kpoint_env
2377 336 : source_cfm => kp_source%mos(ispin)%cmo_coeff
2378 336 : CALL get_mo_set(kp_source%mos(ispin), eigenvalues=eigenvalues)
2379 1600 : eigenvalues_buffer(1:nmo) = eigenvalues(1:nmo)
2380 : ELSE
2381 0 : eigenvalues_buffer(1:nmo) = 0.0_dp
2382 : END IF
2383 336 : IF (source_window) THEN
2384 0 : CALL wannier90_copy_cfm_general(source_cfm, src_cfm_full, para_env)
2385 : ELSE
2386 336 : CALL wannier90_copy_cfm_general(source_cfm, src_cfm, para_env)
2387 : END IF
2388 336 : IF (my_kpgrp) THEN
2389 336 : owner_count = 1.0_dp
2390 : ELSE
2391 0 : owner_count = 0.0_dp
2392 : END IF
2393 336 : CALL para_env%sum(owner_count)
2394 336 : CALL para_env%sum(eigenvalues_buffer)
2395 1600 : IF (owner_count > 0.0_dp) eigenvalues_buffer(1:nmo) = eigenvalues_buffer(1:nmo)/owner_count
2396 : CALL measure_wannier90_eigenspace_quality(ref_cfm, matrix_s, matrix_ks, &
2397 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, &
2398 : para_env, ispin, eigenvalues_buffer, ok, &
2399 336 : ref_metric_deviation, ref_metric_min, ref_residual)
2400 336 : IF (ok .AND. para_env%is_source() .AND. iw > 0 .AND. &
2401 : (ref_metric_deviation > print_tol .OR. ref_residual > residual_print_tol)) THEN
2402 : WRITE (iw, '(T2,A,I0,A,ES10.3,A,ES10.3,A,ES10.3)') &
2403 0 : "WANNIER90| reference k=", ik, " dM=", ref_metric_deviation, &
2404 0 : " smin=", ref_metric_min, " resid=", ref_residual
2405 : END IF
2406 336 : IF (source_window) THEN
2407 0 : CALL cp_cfm_to_cfm(src_cfm_full, src_cfm, nmo)
2408 : END IF
2409 :
2410 336 : best_deviation = HUGE(1.0_dp)
2411 336 : best_metric_deviation = 0.0_dp
2412 336 : best_residual = 0.0_dp
2413 336 : best_svalue = 0.0_dp
2414 336 : IF (sym_index(ik) <= 0) THEN
2415 : CALL kpoint_transform_scf_mo(src_cfm, dst_cfm, qs_kpoint, &
2416 68 : ikred, sym_index(ik), ok, reason)
2417 68 : IF (ok) THEN
2418 : CALL measure_wannier90_subspace_error(ref_cfm, dst_cfm, matrix_s, &
2419 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, &
2420 68 : para_env, ok, candidate_deviation, candidate_svalue)
2421 68 : IF (ok) THEN
2422 : CALL measure_wannier90_eigenspace_quality(dst_cfm, matrix_s, matrix_ks, &
2423 : kpoint%xkp(1:3, ik), cell_to_index, sab_nl, &
2424 : para_env, ispin, eigenvalues_buffer, ok, &
2425 : candidate_metric_deviation, &
2426 68 : candidate_metric_min, candidate_residual)
2427 : END IF
2428 68 : IF (ok) THEN
2429 68 : best_deviation = candidate_deviation
2430 68 : best_metric_deviation = candidate_metric_deviation
2431 68 : best_residual = candidate_residual
2432 68 : best_svalue = candidate_svalue
2433 68 : IF (para_env%is_source() .AND. iw > 0 .AND. &
2434 : (candidate_deviation > print_tol .OR. candidate_metric_deviation > print_tol .OR. &
2435 : candidate_residual > residual_print_tol)) THEN
2436 : WRITE (iw, '(T2,A,I0,A,I0,A,I0,A,ES10.3,A,ES10.3,A,ES10.3,A,ES10.3)') &
2437 0 : "WANNIER90| reuse candidate k=", ik, " src=", ikred, " sym=", &
2438 0 : sym_index(ik), " dRef=", candidate_deviation, " dM=", &
2439 0 : candidate_metric_deviation, " smin=", candidate_metric_min, &
2440 0 : " resid=", candidate_residual
2441 : END IF
2442 : END IF
2443 : END IF
2444 268 : ELSE IF (ASSOCIATED(qs_kpoint%kp_sym)) THEN
2445 268 : kpsym => qs_kpoint%kp_sym(ikred)%kpoint_sym
2446 268 : IF (ASSOCIATED(kpsym)) THEN
2447 25036 : DO isym_try = 1, kpsym%nwred
2448 24768 : IF (.NOT. kpoint_same_periodic(kpoint%xkp(1:3, ik), &
2449 : kpsym%xkp(1:3, isym_try))) CYCLE
2450 : CALL kpoint_transform_scf_mo(src_cfm, dst_cfm, &
2451 2128 : qs_kpoint, ikred, isym_try, ok, reason)
2452 2128 : IF (.NOT. ok) CYCLE
2453 : CALL measure_wannier90_subspace_error(ref_cfm, dst_cfm, &
2454 : matrix_s, kpoint%xkp(1:3, ik), cell_to_index, &
2455 : sab_nl, para_env, ok, candidate_deviation, &
2456 2128 : candidate_svalue)
2457 2128 : IF (ok) THEN
2458 : CALL measure_wannier90_eigenspace_quality(dst_cfm, matrix_s, matrix_ks, &
2459 : kpoint%xkp(1:3, ik), cell_to_index, &
2460 : sab_nl, para_env, ispin, &
2461 : eigenvalues_buffer, ok, &
2462 : candidate_metric_deviation, &
2463 2128 : candidate_metric_min, candidate_residual)
2464 : END IF
2465 4524 : IF (ok .AND. candidate_deviation < best_deviation) THEN
2466 314 : best_deviation = candidate_deviation
2467 314 : best_metric_deviation = candidate_metric_deviation
2468 314 : best_residual = candidate_residual
2469 314 : best_svalue = candidate_svalue
2470 : END IF
2471 : END DO
2472 : END IF
2473 : END IF
2474 350 : IF (best_deviation < HUGE(1.0_dp)) THEN
2475 336 : max_subspace_deviation = MAX(max_subspace_deviation, best_deviation)
2476 336 : min_svalue = MIN(min_svalue, best_svalue)
2477 336 : max_metric_deviation = MAX(max_metric_deviation, best_metric_deviation)
2478 336 : max_residual = MAX(max_residual, best_residual)
2479 : END IF
2480 : END DO
2481 14 : CALL para_env%max(max_subspace_deviation)
2482 14 : CALL para_env%min(min_svalue)
2483 14 : CALL para_env%max(max_metric_deviation)
2484 14 : CALL para_env%max(max_residual)
2485 :
2486 14 : IF (source_window) THEN
2487 0 : CALL cp_cfm_release(src_cfm_full)
2488 0 : CALL cp_fm_struct_release(matrix_struct_source)
2489 : END IF
2490 14 : CALL cp_cfm_release(ref_cfm)
2491 14 : CALL cp_cfm_release(src_cfm)
2492 14 : CALL cp_cfm_release(dst_cfm)
2493 14 : CALL cp_fm_struct_release(matrix_struct_work)
2494 14 : DEALLOCATE (eigenvalues_buffer)
2495 14 : DEALLOCATE (source_kpoint, sym_index)
2496 :
2497 56 : END SUBROUTINE diagnose_wannier90_scf_reuse_candidates
2498 :
2499 : ! **************************************************************************************************
2500 : !> \brief Measure the S(k)-metric distance between two Wannier90 MO subspaces.
2501 : !> \param ref_cfm reference MO coefficients
2502 : !> \param cand_cfm candidate MO coefficients
2503 : !> \param matrix_s real-space overlap matrix
2504 : !> \param xkp target k-point coordinate
2505 : !> \param cell_to_index real-space cell index table
2506 : !> \param sab_nl overlap neighbor list
2507 : !> \param para_env global parallel environment
2508 : !> \param success true if the metric comparison was performed
2509 : !> \param max_subspace_deviation largest deviation of singular values from one
2510 : !> \param min_svalue smallest singular value of C_ref^+ S(k) C_candidate
2511 : ! **************************************************************************************************
2512 2532 : SUBROUTINE measure_wannier90_subspace_error(ref_cfm, cand_cfm, matrix_s, &
2513 : xkp, cell_to_index, sab_nl, para_env, success, &
2514 : max_subspace_deviation, min_svalue)
2515 : TYPE(cp_cfm_type), INTENT(IN) :: ref_cfm, cand_cfm
2516 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s
2517 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
2518 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
2519 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
2520 : POINTER :: sab_nl
2521 : TYPE(mp_para_env_type), POINTER :: para_env
2522 : LOGICAL, INTENT(OUT) :: success
2523 : REAL(KIND=dp), INTENT(OUT) :: max_subspace_deviation, min_svalue
2524 :
2525 2532 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: metric_projected, metric_vectors, &
2526 2532 : overlap, ref_coeff, s_cand_coeff
2527 : INTEGER :: ib, nao, nmo, nmo_candidate
2528 : REAL(KIND=dp) :: singular_value
2529 2532 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: metric_values
2530 : TYPE(cp_cfm_type) :: s_cand
2531 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_metric
2532 :
2533 2532 : success = .FALSE.
2534 2532 : max_subspace_deviation = HUGE(1.0_dp)
2535 2532 : min_svalue = 0.0_dp
2536 2532 : NULLIFY (matrix_struct_metric)
2537 :
2538 : CALL cp_cfm_get_info(ref_cfm, nrow_global=nao, ncol_global=nmo, &
2539 2532 : matrix_struct=matrix_struct_metric)
2540 2532 : CALL cp_cfm_get_info(cand_cfm, ncol_global=nmo_candidate)
2541 2532 : IF (nmo_candidate /= nmo) RETURN
2542 :
2543 2532 : CALL cp_cfm_create(s_cand, matrix_struct_metric)
2544 : CALL apply_wannier90_kp_matrix(matrix_s, 1, xkp, cell_to_index, sab_nl, &
2545 2532 : cand_cfm, s_cand)
2546 :
2547 15192 : ALLOCATE (ref_coeff(nao, nmo), s_cand_coeff(nao, nmo))
2548 2532 : CALL cp_cfm_get_submatrix(ref_cfm, ref_coeff)
2549 2532 : CALL cp_cfm_get_submatrix(s_cand, s_cand_coeff)
2550 :
2551 : ALLOCATE (overlap(nmo, nmo), &
2552 25320 : metric_projected(nmo, nmo), metric_vectors(nmo, nmo), metric_values(nmo))
2553 5311724 : overlap(:, :) = MATMUL(CONJG(TRANSPOSE(ref_coeff)), s_cand_coeff)
2554 196096 : metric_projected(:, :) = MATMUL(CONJG(TRANSPOSE(overlap)), overlap)
2555 87124 : metric_projected(:, :) = 0.5_dp*(metric_projected + CONJG(TRANSPOSE(metric_projected)))
2556 2532 : CALL diag_complex(metric_projected, metric_vectors, metric_values)
2557 :
2558 2532 : min_svalue = HUGE(1.0_dp)
2559 2532 : max_subspace_deviation = 0.0_dp
2560 11096 : DO ib = 1, nmo
2561 8564 : singular_value = SQRT(MAX(metric_values(ib), 0.0_dp))
2562 8564 : min_svalue = MIN(min_svalue, singular_value)
2563 11096 : max_subspace_deviation = MAX(max_subspace_deviation, ABS(singular_value - 1.0_dp))
2564 : END DO
2565 2532 : CALL para_env%max(max_subspace_deviation)
2566 2532 : CALL para_env%min(min_svalue)
2567 2532 : success = .TRUE.
2568 :
2569 2532 : DEALLOCATE (ref_coeff, s_cand_coeff, overlap, metric_projected, metric_vectors, metric_values)
2570 2532 : CALL cp_cfm_release(s_cand)
2571 :
2572 7596 : END SUBROUTINE measure_wannier90_subspace_error
2573 :
2574 : ! **************************************************************************************************
2575 : !> \brief Measure whether transformed MOs are an H(k),S(k) invariant eigenspace.
2576 : !> \param cand_cfm candidate MO coefficients
2577 : !> \param matrix_s real-space overlap matrix
2578 : !> \param matrix_ks real-space Kohn-Sham matrix
2579 : !> \param xkp target k-point coordinate
2580 : !> \param cell_to_index real-space cell index table
2581 : !> \param sab_nl overlap neighbor list
2582 : !> \param para_env global parallel environment
2583 : !> \param ispin spin index
2584 : !> \param eigenvalues source MO eigenvalues corresponding to the candidate columns
2585 : !> \param success true if the metric and residual checks were performed
2586 : !> \param metric_deviation largest deviation of eigenvalues of C^+ S(k) C from one
2587 : !> \param min_metric_eigenvalue smallest eigenvalue of C^+ S(k) C
2588 : !> \param residual_norm largest element of H(k) C - S(k) C eps
2589 : ! **************************************************************************************************
2590 2532 : SUBROUTINE measure_wannier90_eigenspace_quality(cand_cfm, matrix_s, matrix_ks, xkp, &
2591 2532 : cell_to_index, sab_nl, para_env, ispin, eigenvalues, &
2592 : success, metric_deviation, min_metric_eigenvalue, &
2593 : residual_norm)
2594 : TYPE(cp_cfm_type), INTENT(IN) :: cand_cfm
2595 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
2596 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
2597 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
2598 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
2599 : POINTER :: sab_nl
2600 : TYPE(mp_para_env_type), POINTER :: para_env
2601 : INTEGER, INTENT(IN) :: ispin
2602 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: eigenvalues
2603 : LOGICAL, INTENT(OUT) :: success
2604 : REAL(KIND=dp), INTENT(OUT) :: metric_deviation, min_metric_eigenvalue, &
2605 : residual_norm
2606 :
2607 2532 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: h_coeff, metric_vectors, residual_block, &
2608 2532 : s_coeff, s_projected
2609 : INTEGER :: ib, nao, nmo
2610 2532 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: metric_values
2611 : TYPE(cp_cfm_type) :: h_cand, metric_cfm, s_cand
2612 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_metric, &
2613 : matrix_struct_projected
2614 :
2615 2532 : success = .FALSE.
2616 2532 : metric_deviation = HUGE(1.0_dp)
2617 2532 : min_metric_eigenvalue = 0.0_dp
2618 2532 : residual_norm = HUGE(1.0_dp)
2619 2532 : NULLIFY (matrix_struct_metric, matrix_struct_projected)
2620 :
2621 : CALL cp_cfm_get_info(cand_cfm, nrow_global=nao, ncol_global=nmo, &
2622 2532 : matrix_struct=matrix_struct_metric)
2623 2532 : IF (SIZE(eigenvalues) < nmo) RETURN
2624 :
2625 2532 : CALL cp_cfm_create(s_cand, matrix_struct_metric)
2626 2532 : CALL cp_cfm_create(h_cand, matrix_struct_metric)
2627 :
2628 : CALL apply_wannier90_kp_matrix(matrix_s, 1, xkp, cell_to_index, sab_nl, &
2629 2532 : cand_cfm, s_cand)
2630 : CALL apply_wannier90_kp_matrix(matrix_ks, ispin, xkp, cell_to_index, sab_nl, &
2631 2532 : cand_cfm, h_cand)
2632 :
2633 : ALLOCATE (h_coeff(nao, nmo), metric_vectors(nmo, nmo), &
2634 : residual_block(nao, nmo), s_coeff(nao, nmo), s_projected(nmo, nmo), &
2635 37980 : metric_values(nmo))
2636 2532 : CALL cp_cfm_get_submatrix(s_cand, s_coeff)
2637 2532 : CALL cp_cfm_get_submatrix(h_cand, h_coeff)
2638 : CALL cp_fm_struct_create(matrix_struct_projected, nrow_global=nmo, ncol_global=nmo, &
2639 : para_env=matrix_struct_metric%para_env, &
2640 2532 : context=matrix_struct_metric%context)
2641 2532 : CALL cp_cfm_create(metric_cfm, matrix_struct_projected)
2642 : CALL parallel_gemm("C", "N", nmo, nmo, nao, CMPLX(1.0_dp, 0.0_dp, KIND=dp), cand_cfm, &
2643 2532 : s_cand, CMPLX(0.0_dp, 0.0_dp, KIND=dp), metric_cfm)
2644 2532 : CALL cp_cfm_get_submatrix(metric_cfm, s_projected)
2645 87124 : s_projected(:, :) = 0.5_dp*(s_projected + CONJG(TRANSPOSE(s_projected)))
2646 2532 : CALL diag_complex(s_projected, metric_vectors, metric_values)
2647 11096 : metric_deviation = MAXVAL(ABS(metric_values - 1.0_dp))
2648 11096 : min_metric_eigenvalue = MINVAL(metric_values)
2649 :
2650 309164 : residual_block(:, :) = h_coeff
2651 11096 : DO ib = 1, nmo
2652 309164 : residual_block(:, ib) = residual_block(:, ib) - eigenvalues(ib)*s_coeff(:, ib)
2653 : END DO
2654 309164 : residual_norm = MAXVAL(ABS(residual_block))
2655 2532 : CALL para_env%max(metric_deviation)
2656 2532 : CALL para_env%min(min_metric_eigenvalue)
2657 2532 : CALL para_env%max(residual_norm)
2658 2532 : success = .TRUE.
2659 :
2660 2532 : DEALLOCATE (h_coeff, metric_vectors, residual_block, s_coeff, s_projected, metric_values)
2661 2532 : CALL cp_cfm_release(s_cand)
2662 2532 : CALL cp_cfm_release(h_cand)
2663 2532 : CALL cp_cfm_release(metric_cfm)
2664 2532 : CALL cp_fm_struct_release(matrix_struct_projected)
2665 :
2666 10128 : END SUBROUTINE measure_wannier90_eigenspace_quality
2667 :
2668 : ! **************************************************************************************************
2669 : !> \brief Write fixed trial projections in the exported MO gauge for external Wannier90.
2670 : !> \param amn Band/trial/k-point projection matrices.
2671 : !> \param para_env Global parallel environment.
2672 : !> \param seed_name Output filename prefix.
2673 : ! **************************************************************************************************
2674 24 : SUBROUTINE write_wannier90_amn(amn, para_env, seed_name)
2675 : COMPLEX(KIND=dp), INTENT(IN) :: amn(:, :, :)
2676 : TYPE(mp_para_env_type), POINTER :: para_env
2677 : CHARACTER(LEN=*), INTENT(IN) :: seed_name
2678 :
2679 : INTEGER :: ib, ik, iunit, iwann
2680 :
2681 24 : IF (.NOT. para_env%is_source()) RETURN
2682 12 : CALL open_file(TRIM(seed_name)//'.amn', unit_number=iunit, file_status='REPLACE', file_action='WRITE')
2683 12 : WRITE (iunit, '(A)') '! Initial Wannier90 projections generated by CP2K'
2684 12 : WRITE (iunit, '(3I10)') SIZE(amn, 1), SIZE(amn, 3), SIZE(amn, 2)
2685 220 : DO ik = 1, SIZE(amn, 3)
2686 924 : DO iwann = 1, SIZE(amn, 2)
2687 3792 : DO ib = 1, SIZE(amn, 1)
2688 2880 : WRITE (iunit, '(3I10,2ES24.16)') ib, iwann, ik, REAL(amn(ib, iwann, ik), dp), &
2689 6464 : AIMAG(amn(ib, iwann, ik))
2690 : END DO
2691 : END DO
2692 : END DO
2693 12 : CALL close_file(iunit)
2694 : END SUBROUTINE write_wannier90_amn
2695 :
2696 : ! **************************************************************************************************
2697 : !> \brief Enumerate all on-atom radial s/p shell pairs in global spherical-AO ordering.
2698 : !> \param qs_env Electronic environment providing the basis and atom ordering.
2699 : !> \param groups Global indices of an s function and the three functions of a p shell.
2700 : ! **************************************************************************************************
2701 4 : SUBROUTINE build_wannier90_hybrid_groups(qs_env, groups)
2702 : TYPE(qs_environment_type), POINTER :: qs_env
2703 : INTEGER, ALLOCATABLE, INTENT(OUT) :: groups(:, :)
2704 :
2705 : INTEGER :: iatom, ig, ikind, ip, is, iset, ishell, &
2706 : n, ng, np, ns, nset, nsgf, offset, pass
2707 4 : INTEGER, ALLOCATABLE :: p_shells(:), s_shells(:)
2708 4 : INTEGER, POINTER :: first_sgf(:, :), l(:, :), nshell(:)
2709 : TYPE(gto_basis_set_type), POINTER :: basis
2710 4 : TYPE(particle_type), POINTER :: particles(:)
2711 4 : TYPE(qs_kind_type), POINTER :: qs_kinds(:)
2712 :
2713 4 : CALL get_qs_env(qs_env, particle_set=particles, qs_kind_set=qs_kinds)
2714 4 : ng = 0
2715 12 : DO pass = 1, 2
2716 8 : offset = 0
2717 8 : ig = 0
2718 24 : DO iatom = 1, SIZE(particles)
2719 16 : CALL get_atomic_kind(particles(iatom)%atomic_kind, kind_number=ikind)
2720 16 : CALL get_qs_kind(qs_kinds(ikind), basis_set=basis, nsgf=nsgf)
2721 16 : IF (ASSOCIATED(basis)) THEN
2722 16 : CALL get_gto_basis_set(basis, nset=nset, nshell=nshell, l=l, first_sgf=first_sgf)
2723 160 : ALLOCATE (s_shells(SUM(nshell)), p_shells(SUM(nshell)))
2724 16 : ns = 0
2725 16 : np = 0
2726 48 : DO iset = 1, nset
2727 96 : DO ishell = 1, nshell(iset)
2728 48 : n = offset + first_sgf(ishell, iset)
2729 32 : SELECT CASE (l(ishell, iset))
2730 : CASE (0)
2731 32 : ns = ns + 1
2732 32 : s_shells(ns) = n
2733 : CASE (1)
2734 16 : np = np + 1
2735 48 : p_shells(np) = n
2736 : END SELECT
2737 : END DO
2738 : END DO
2739 16 : IF (pass == 1) THEN
2740 8 : ng = ng + ns*np
2741 : ELSE
2742 24 : DO is = 1, ns
2743 40 : DO ip = 1, np
2744 16 : ig = ig + 1
2745 16 : n = p_shells(ip)
2746 96 : groups(:, ig) = [s_shells(is), n, n + 1, n + 2]
2747 : END DO
2748 : END DO
2749 : END IF
2750 32 : DEALLOCATE (s_shells, p_shells)
2751 : END IF
2752 40 : offset = offset + nsgf
2753 : END DO
2754 12 : IF (pass == 1) THEN
2755 12 : ALLOCATE (groups(4, ng))
2756 : END IF
2757 : END DO
2758 8 : END SUBROUTINE build_wannier90_hybrid_groups
2759 :
2760 : ! **************************************************************************************************
2761 : !> \brief Form C(k)^H S(k) projections on all AO trials using the exported MO coefficients.
2762 : !> \param kpoint Full export mesh and its distributed MO coefficients.
2763 : !> \param matrix_s Real-space overlap matrices.
2764 : !> \param sab_nl Overlap neighbour lists.
2765 : !> \param para_env Global parallel environment.
2766 : !> \param ispin Selected spin channel.
2767 : !> \param bands Original MO indices retained in the export.
2768 : !> \param apply_phase Whether the overlap export uses the additional global Bloch gauge.
2769 : !> \param phase_center Fractional origin for that gauge.
2770 : !> \param projections Band/AO/k-point projections, stored only on the source rank.
2771 : ! **************************************************************************************************
2772 10 : SUBROUTINE build_wannier90_ao_projections(kpoint, matrix_s, sab_nl, para_env, ispin, bands, &
2773 : apply_phase, phase_center, projections)
2774 : TYPE(kpoint_type), POINTER :: kpoint
2775 : TYPE(dbcsr_p_type), POINTER :: matrix_s(:, :)
2776 : TYPE(neighbor_list_set_p_type), POINTER :: sab_nl(:)
2777 : TYPE(mp_para_env_type), POINTER :: para_env
2778 : INTEGER, INTENT(IN) :: ispin, bands(:)
2779 : LOGICAL, INTENT(IN) :: apply_phase
2780 : REAL(KIND=dp), INTENT(IN) :: phase_center(3)
2781 : COMPLEX(KIND=dp), ALLOCATABLE, INTENT(OUT) :: projections(:, :, :)
2782 :
2783 : COMPLEX(KIND=dp), ALLOCATABLE :: buffer(:, :)
2784 : INTEGER :: ik, iklocal, nao, nk, nmo
2785 10 : INTEGER, POINTER :: cell_to_index(:, :, :)
2786 : REAL(KIND=dp) :: phase
2787 : TYPE(cp_cfm_type) :: mo_cfm, sc_cfm
2788 : TYPE(cp_cfm_type), POINTER :: source_cfm
2789 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct
2790 :
2791 10 : NULLIFY (matrix_struct, cell_to_index, source_cfm)
2792 10 : CALL get_mo_set(kpoint%kp_env(1)%kpoint_env%mos(ispin), nao=nao, nmo=nmo)
2793 10 : nk = SIZE(kpoint%xkp, 2)
2794 10 : CALL get_kpoint_info(kpoint, cell_to_index=cell_to_index)
2795 : CALL cp_fm_struct_create(matrix_struct, nrow_global=nao, ncol_global=nmo, &
2796 10 : para_env=para_env, context=kpoint%blacs_env)
2797 10 : CALL cp_cfm_create(mo_cfm, matrix_struct)
2798 10 : CALL cp_cfm_create(sc_cfm, matrix_struct)
2799 40 : ALLOCATE (buffer(nmo, nao))
2800 10 : IF (para_env%is_source()) THEN
2801 25 : ALLOCATE (projections(SIZE(bands), nao, nk))
2802 : ELSE
2803 5 : ALLOCATE (projections(0, 0, 0))
2804 : END IF
2805 90 : DO ik = 1, nk
2806 80 : IF (ik >= kpoint%kp_range(1) .AND. ik <= kpoint%kp_range(2)) THEN
2807 80 : iklocal = ik - kpoint%kp_range(1) + 1
2808 80 : source_cfm => kpoint%kp_env(iklocal)%kpoint_env%mos(ispin)%cmo_coeff
2809 : ELSE
2810 0 : NULLIFY (source_cfm)
2811 : END IF
2812 80 : CALL wannier90_copy_cfm_general(source_cfm, mo_cfm, para_env)
2813 : CALL apply_wannier90_kp_matrix(matrix_s, 1, kpoint%xkp(:, ik), cell_to_index, sab_nl, &
2814 80 : mo_cfm, sc_cfm)
2815 80 : CALL cp_cfm_get_submatrix(sc_cfm, buffer, transpose=.TRUE.)
2816 90 : IF (para_env%is_source()) THEN
2817 1008 : projections(:, :, ik) = CONJG(buffer(bands, :))
2818 40 : IF (apply_phase) THEN
2819 : ! M'(k,b) = exp(i[phi(k+b)-phi(k)]) M(k,b) requires A'(k) = exp(-i phi(k)) A(k).
2820 128 : phase = -twopi*DOT_PRODUCT(kpoint%xkp(:, ik), phase_center)
2821 760 : projections(:, :, ik) = projections(:, :, ik)*CMPLX(COS(phase), SIN(phase), KIND=dp)
2822 : END IF
2823 : END IF
2824 : END DO
2825 10 : DEALLOCATE (buffer)
2826 10 : CALL cp_cfm_release(mo_cfm)
2827 10 : CALL cp_cfm_release(sc_cfm)
2828 10 : CALL cp_fm_struct_release(matrix_struct)
2829 20 : END SUBROUTINE build_wannier90_ao_projections
2830 :
2831 : ! **************************************************************************************************
2832 : !> \brief Assemble the non-Hermitian inter-k-point AO overlap from its real-space images.
2833 : !> \param cosmat Real part of <mu,0|exp(-i b.r)|nu,R> for the stored direction of b.
2834 : !> \param sinmat Imaginary part of the same integrals.
2835 : !> \param bsign Sign of b relative to the stored integrals; cosine is even and sine is odd.
2836 : !> \param kleft Reduced left k-point.
2837 : !> \param kright Reduced right k-point, equal to kleft+b modulo a reciprocal lattice vector.
2838 : !> \param cell_to_index Real-space image index table.
2839 : !> \param sab_nl Symmetric orbital neighbour list.
2840 : !> \param real_matrix Real part of the general AO overlap matrix.
2841 : !> \param imag_matrix Imaginary part of the general AO overlap matrix.
2842 : ! **************************************************************************************************
2843 3552 : SUBROUTINE transform_wannier90_overlap(cosmat, sinmat, bsign, kleft, kright, cell_to_index, &
2844 : sab_nl, real_matrix, imag_matrix)
2845 : TYPE(dbcsr_p_type), POINTER :: cosmat(:, :), sinmat(:, :)
2846 : REAL(KIND=dp), INTENT(IN) :: bsign, kleft(3), kright(3)
2847 : INTEGER, POINTER :: cell_to_index(:, :, :)
2848 : TYPE(neighbor_list_set_p_type), POINTER :: sab_nl(:)
2849 : TYPE(dbcsr_type), INTENT(INOUT) :: real_matrix, imag_matrix
2850 :
2851 : INTEGER :: direction, iatom, ic, icol, &
2852 : image_cell(3), irow, jatom
2853 : LOGICAL :: found, symmetric, transposed
2854 : REAL(KIND=dp) :: ci, cr, phase
2855 3552 : REAL(KIND=dp), POINTER :: cb(:, :), ib(:, :), rb(:, :), sb(:, :)
2856 3552 : TYPE(neighbor_list_iterator_p_type), POINTER :: iterator(:)
2857 :
2858 3552 : CALL get_neighbor_list_set_p(sab_nl, symmetric=symmetric)
2859 3552 : CPASSERT(symmetric)
2860 3552 : CALL dbcsr_set(real_matrix, 0.0_dp)
2861 3552 : CALL dbcsr_set(imag_matrix, 0.0_dp)
2862 3552 : CALL neighbor_list_iterator_create(iterator, sab_nl)
2863 3729408 : DO WHILE (neighbor_list_iterate(iterator) == 0)
2864 3725856 : CALL get_iterator_info(iterator, iatom=iatom, jatom=jatom, cell=image_cell)
2865 3725856 : ic = cell_to_index(image_cell(1), image_cell(2), image_cell(3))
2866 3725856 : CPASSERT(ic > 0)
2867 3725856 : irow = MIN(iatom, jatom)
2868 3725856 : icol = MAX(iatom, jatom)
2869 3725856 : CALL dbcsr_get_block_p(cosmat(1, ic)%matrix, irow, icol, cb, found=found)
2870 3725856 : CPASSERT(found)
2871 3725856 : CALL dbcsr_get_block_p(sinmat(1, ic)%matrix, irow, icol, sb, found=found)
2872 3725856 : CPASSERT(found)
2873 11181120 : DO direction = 1, 2
2874 7451712 : IF (direction == 1) THEN
2875 3725856 : irow = iatom
2876 3725856 : icol = jatom
2877 3725856 : transposed = iatom > jatom
2878 14903424 : phase = twopi*DOT_PRODUCT(kright, REAL(image_cell, KIND=dp))
2879 : ELSE
2880 : ! B_nu,mu(-R,b) = exp(i b.R) B_mu,nu(R,b)^T, not its Hermitian transpose.
2881 : ! Self-pair images already occur in both directions in the neighbour list.
2882 3725856 : IF (iatom == jatom) CYCLE
2883 2786304 : irow = jatom
2884 2786304 : icol = iatom
2885 2786304 : transposed = iatom < jatom
2886 11145216 : phase = -twopi*DOT_PRODUCT(kleft, REAL(image_cell, KIND=dp))
2887 : END IF
2888 6512160 : CALL dbcsr_get_block_p(real_matrix, irow, icol, rb, found=found)
2889 6512160 : CPASSERT(found)
2890 6512160 : CALL dbcsr_get_block_p(imag_matrix, irow, icol, ib, found=found)
2891 6512160 : CPASSERT(found)
2892 6512160 : cr = COS(phase)
2893 6512160 : ci = SIN(phase)
2894 23262336 : IF (transposed) THEN
2895 205685760 : rb(:, :) = rb(:, :) + cr*TRANSPOSE(cb) - bsign*ci*TRANSPOSE(sb)
2896 205685760 : ib(:, :) = ib(:, :) + ci*TRANSPOSE(cb) + bsign*cr*TRANSPOSE(sb)
2897 : ELSE
2898 290364576 : rb(:, :) = rb(:, :) + cr*cb(:, :) - bsign*ci*sb(:, :)
2899 290364576 : ib(:, :) = ib(:, :) + ci*cb(:, :) + bsign*cr*sb(:, :)
2900 : END IF
2901 : END DO
2902 : END DO
2903 3552 : CALL neighbor_list_iterator_release(iterator)
2904 3552 : END SUBROUTINE transform_wannier90_overlap
2905 :
2906 : ! **************************************************************************************************
2907 : !> \brief Apply a complex k-point matrix to a complex MO coefficient matrix.
2908 : !> \param rsmat real-space matrix images
2909 : !> \param ispin spin index for rsmat
2910 : !> \param xkp target k-point coordinate
2911 : !> \param cell_to_index real-space cell index table
2912 : !> \param sab_nl overlap neighbor list
2913 : !> \param coeff input MO coefficients
2914 : !> \param RESULT output of applying the k-point matrix to the MO coefficients
2915 : ! **************************************************************************************************
2916 34960 : SUBROUTINE apply_wannier90_kp_matrix(rsmat, ispin, xkp, cell_to_index, sab_nl, &
2917 : coeff, RESULT)
2918 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: rsmat
2919 : INTEGER, INTENT(IN) :: ispin
2920 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
2921 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
2922 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
2923 : POINTER :: sab_nl
2924 : TYPE(cp_cfm_type), INTENT(IN) :: coeff
2925 : TYPE(cp_cfm_type), INTENT(INOUT) :: result
2926 :
2927 : INTEGER :: nao, ncol
2928 : TYPE(cp_cfm_type) :: kmat_cfm
2929 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_ao, matrix_struct_coeff
2930 : TYPE(cp_fm_type) :: mat_imag, mat_real
2931 : TYPE(dbcsr_type), POINTER :: kmat_imag, kmat_imag_full, kmat_real, &
2932 : kmat_real_full
2933 :
2934 8740 : NULLIFY (matrix_struct_ao, matrix_struct_coeff, kmat_imag, kmat_imag_full, kmat_real, &
2935 8740 : kmat_real_full)
2936 :
2937 : CALL cp_cfm_get_info(coeff, nrow_global=nao, ncol_global=ncol, &
2938 8740 : matrix_struct=matrix_struct_coeff)
2939 :
2940 8740 : ALLOCATE (kmat_real, kmat_imag, kmat_real_full, kmat_imag_full)
2941 : CALL dbcsr_create(kmat_real, template=rsmat(ispin, 1)%matrix, &
2942 8740 : matrix_type=dbcsr_type_symmetric)
2943 : CALL dbcsr_create(kmat_imag, template=rsmat(ispin, 1)%matrix, &
2944 8740 : matrix_type=dbcsr_type_antisymmetric)
2945 : CALL dbcsr_create(kmat_real_full, template=rsmat(ispin, 1)%matrix, &
2946 8740 : matrix_type=dbcsr_type_no_symmetry)
2947 : CALL dbcsr_create(kmat_imag_full, template=rsmat(ispin, 1)%matrix, &
2948 8740 : matrix_type=dbcsr_type_no_symmetry)
2949 8740 : CALL cp_dbcsr_alloc_block_from_nbl(kmat_real, sab_nl)
2950 8740 : CALL cp_dbcsr_alloc_block_from_nbl(kmat_imag, sab_nl)
2951 8740 : CALL dbcsr_set(kmat_real, 0.0_dp)
2952 8740 : CALL dbcsr_set(kmat_imag, 0.0_dp)
2953 : CALL rskp_transform(kmat_real, kmat_imag, rsmat=rsmat, ispin=ispin, &
2954 8740 : xkp=xkp, cell_to_index=cell_to_index, sab_nl=sab_nl)
2955 8740 : CALL dbcsr_desymmetrize(kmat_real, kmat_real_full)
2956 8740 : CALL dbcsr_desymmetrize(kmat_imag, kmat_imag_full)
2957 :
2958 : CALL cp_fm_struct_create(matrix_struct_ao, nrow_global=nao, ncol_global=nao, &
2959 : para_env=matrix_struct_coeff%para_env, &
2960 8740 : context=matrix_struct_coeff%context)
2961 8740 : CALL cp_fm_create(mat_real, matrix_struct_ao)
2962 8740 : CALL cp_fm_create(mat_imag, matrix_struct_ao)
2963 8740 : CALL copy_dbcsr_to_fm(kmat_real_full, mat_real)
2964 8740 : CALL copy_dbcsr_to_fm(kmat_imag_full, mat_imag)
2965 :
2966 8740 : CALL cp_cfm_create(kmat_cfm, matrix_struct_ao)
2967 8740 : CALL cp_fm_to_cfm(mat_real, mat_imag, kmat_cfm)
2968 : CALL parallel_gemm("N", "N", nao, ncol, nao, CMPLX(1.0_dp, 0.0_dp, KIND=dp), kmat_cfm, &
2969 8740 : coeff, CMPLX(0.0_dp, 0.0_dp, KIND=dp), RESULT)
2970 :
2971 8740 : CALL cp_fm_release(mat_real)
2972 8740 : CALL cp_fm_release(mat_imag)
2973 8740 : CALL cp_cfm_release(kmat_cfm)
2974 8740 : CALL cp_fm_struct_release(matrix_struct_ao)
2975 8740 : CALL dbcsr_deallocate_matrix(kmat_real)
2976 8740 : CALL dbcsr_deallocate_matrix(kmat_imag)
2977 8740 : CALL dbcsr_deallocate_matrix(kmat_real_full)
2978 8740 : CALL dbcsr_deallocate_matrix(kmat_imag_full)
2979 :
2980 8740 : END SUBROUTINE apply_wannier90_kp_matrix
2981 :
2982 : ! **************************************************************************************************
2983 : !> \brief Rayleigh-Ritz stabilize a symmetry-reconstructed Wannier90 MO subspace.
2984 : !> \param dst_cfm transformed MO coefficients
2985 : !> \param matrix_s real-space overlap matrix
2986 : !> \param matrix_ks real-space Kohn-Sham matrix
2987 : !> \param xkp target k-point coordinate
2988 : !> \param cell_to_index real-space cell index table
2989 : !> \param sab_nl overlap neighbor list
2990 : !> \param ispin spin index
2991 : !> \param eigenvalues Ritz eigenvalues of the stabilized subspace
2992 : !> \param degenerate_band_tol degeneracy threshold
2993 : !> \param success true if the subspace was stabilized
2994 : !> \param reason diagnostic message
2995 : !> \param aligned_blocks number of stabilized subspaces
2996 : !> \param aligned_max_size largest stabilized subspace
2997 : !> \param aligned_min_svalue smallest S(k)-metric eigenvalue
2998 : !> \param max_residual largest Ritz residual
2999 : ! **************************************************************************************************
3000 532 : SUBROUTINE ritz_stabilize_wannier90_subspace(dst_cfm, matrix_s, matrix_ks, &
3001 1064 : xkp, cell_to_index, sab_nl, ispin, eigenvalues, &
3002 : degenerate_band_tol, success, reason, aligned_blocks, &
3003 : aligned_max_size, aligned_min_svalue, max_residual)
3004 : TYPE(cp_cfm_type), INTENT(IN) :: dst_cfm
3005 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
3006 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
3007 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
3008 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
3009 : POINTER :: sab_nl
3010 : INTEGER, INTENT(IN) :: ispin
3011 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: eigenvalues
3012 : REAL(KIND=dp), INTENT(IN) :: degenerate_band_tol
3013 : LOGICAL, INTENT(OUT) :: success
3014 : CHARACTER(LEN=*), INTENT(OUT) :: reason
3015 : INTEGER, INTENT(OUT) :: aligned_blocks, aligned_max_size
3016 : REAL(KIND=dp), INTENT(OUT) :: aligned_min_svalue, max_residual
3017 :
3018 : REAL(KIND=dp), PARAMETER :: residual_tol = 1.0e-2_dp
3019 :
3020 532 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: block_coeff, dst_coeff, h_block, h_coeff, &
3021 532 : h_projected, h_projected_work, metric_vectors, residual_block, ritz_vectors, s_block, &
3022 532 : s_coeff, s_projected, stabilized
3023 : INTEGER :: block_first, block_last, block_size, ib, &
3024 : nao, nmo
3025 : REAL(KIND=dp) :: metric_deviation, norm_value, &
3026 : residual_norm
3027 532 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: metric_values, ritz_values
3028 : TYPE(cp_cfm_type) :: h_dst, s_dst
3029 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_metric
3030 :
3031 532 : success = .FALSE.
3032 532 : reason = ""
3033 532 : aligned_blocks = 0
3034 532 : aligned_max_size = 0
3035 532 : aligned_min_svalue = HUGE(1.0_dp)
3036 532 : max_residual = 0.0_dp
3037 :
3038 532 : NULLIFY (matrix_struct_metric)
3039 : CALL cp_cfm_get_info(dst_cfm, nrow_global=nao, ncol_global=nmo, &
3040 532 : matrix_struct=matrix_struct_metric)
3041 532 : IF (SIZE(eigenvalues) < nmo) THEN
3042 0 : reason = "not enough eigenvalues for Wannier90 Ritz subspace stabilization"
3043 : RETURN
3044 : END IF
3045 :
3046 532 : CALL cp_cfm_create(s_dst, matrix_struct_metric)
3047 532 : CALL cp_cfm_create(h_dst, matrix_struct_metric)
3048 :
3049 : stabilize_subspace: BLOCK
3050 : CALL apply_wannier90_kp_matrix(matrix_s, 1, xkp, cell_to_index, sab_nl, &
3051 532 : dst_cfm, s_dst)
3052 : CALL apply_wannier90_kp_matrix(matrix_ks, ispin, xkp, cell_to_index, sab_nl, &
3053 532 : dst_cfm, h_dst)
3054 :
3055 4256 : ALLOCATE (dst_coeff(nao, nmo), s_coeff(nao, nmo), h_coeff(nao, nmo))
3056 532 : CALL cp_cfm_get_submatrix(dst_cfm, dst_coeff)
3057 532 : CALL cp_cfm_get_submatrix(s_dst, s_coeff)
3058 532 : CALL cp_cfm_get_submatrix(h_dst, h_coeff)
3059 :
3060 532 : block_first = 1
3061 1916 : DO WHILE (block_first <= nmo)
3062 : block_last = block_first
3063 2096 : DO WHILE (block_last < nmo)
3064 1564 : IF (ABS(eigenvalues(block_last + 1) - eigenvalues(block_last)) >= degenerate_band_tol) EXIT
3065 1384 : block_last = block_last + 1
3066 : END DO
3067 1384 : block_size = block_last - block_first + 1
3068 1384 : IF (block_size > 1) THEN
3069 : ! The atom/AO operation fixes the subspace, while the little-group gauge inside an
3070 : ! exactly degenerate manifold is arbitrary. Stabilize only that manifold and verify
3071 : ! that it is an invariant H(k),S(k) subspace before exporting it to Wannier90.
3072 0 : ALLOCATE (block_coeff(nao, block_size), h_block(nao, block_size), &
3073 0 : h_projected(block_size, block_size), h_projected_work(block_size, block_size), &
3074 0 : metric_vectors(block_size, block_size), residual_block(nao, block_size), &
3075 0 : ritz_vectors(block_size, block_size), s_block(nao, block_size), &
3076 0 : s_projected(block_size, block_size), stabilized(nao, block_size), &
3077 12688 : metric_values(block_size), ritz_values(block_size))
3078 63192 : block_coeff(:, :) = dst_coeff(:, block_first:block_last)
3079 63192 : s_block(:, :) = s_coeff(:, block_first:block_last)
3080 63192 : h_block(:, :) = h_coeff(:, block_first:block_last)
3081 977816 : s_projected(:, :) = MATMUL(CONJG(TRANSPOSE(block_coeff)), s_block)
3082 977816 : h_projected(:, :) = MATMUL(CONJG(TRANSPOSE(block_coeff)), h_block)
3083 9032 : s_projected(:, :) = 0.5_dp*(s_projected + CONJG(TRANSPOSE(s_projected)))
3084 9032 : h_projected(:, :) = 0.5_dp*(h_projected + CONJG(TRANSPOSE(h_projected)))
3085 :
3086 488 : CALL diag_complex(s_projected, metric_vectors, metric_values)
3087 1688 : aligned_min_svalue = MIN(aligned_min_svalue, MINVAL(metric_values))
3088 1688 : metric_deviation = MAXVAL(ABS(metric_values - 1.0_dp))
3089 1688 : IF (MINVAL(metric_values) < 1.0e-10_dp) THEN
3090 : WRITE (reason, "(A,I0,A,ES9.2,A,ES9.2)") &
3091 0 : "singular metric blk=", block_first, " smin=", MINVAL(metric_values), &
3092 0 : " dS=", metric_deviation
3093 0 : max_residual = HUGE(1.0_dp)
3094 0 : EXIT stabilize_subspace
3095 : END IF
3096 :
3097 1688 : DO ib = 1, block_size
3098 4760 : metric_vectors(:, ib) = metric_vectors(:, ib)/SQRT(metric_values(ib))
3099 : END DO
3100 53720 : h_projected_work(:, :) = MATMUL(h_projected, metric_vectors)
3101 53720 : h_projected(:, :) = MATMUL(CONJG(TRANSPOSE(metric_vectors)), h_projected_work)
3102 9032 : h_projected(:, :) = 0.5_dp*(h_projected + CONJG(TRANSPOSE(h_projected)))
3103 488 : CALL diag_complex(h_projected, ritz_vectors, ritz_values)
3104 53720 : h_projected_work(:, :) = MATMUL(metric_vectors, ritz_vectors)
3105 4760 : ritz_vectors(:, :) = h_projected_work
3106 653464 : stabilized(:, :) = MATMUL(block_coeff, ritz_vectors)
3107 653464 : residual_block(:, :) = MATMUL(h_block, ritz_vectors)
3108 63192 : h_block(:, :) = residual_block
3109 653464 : residual_block(:, :) = MATMUL(s_block, ritz_vectors)
3110 63192 : s_block(:, :) = residual_block
3111 1688 : DO ib = 1, block_size
3112 62704 : norm_value = SQRT(ABS(REAL(DOT_PRODUCT(stabilized(:, ib), s_block(:, ib)), KIND=dp)))
3113 1688 : IF (norm_value > EPSILON(1.0_dp)) THEN
3114 62704 : stabilized(:, ib) = stabilized(:, ib)/norm_value
3115 62704 : h_block(:, ib) = h_block(:, ib)/norm_value
3116 62704 : s_block(:, ib) = s_block(:, ib)/norm_value
3117 : END IF
3118 : END DO
3119 63192 : residual_block(:, :) = h_block
3120 1688 : DO ib = 1, block_size
3121 : residual_block(:, ib) = residual_block(:, ib) - &
3122 63192 : eigenvalues(block_first + ib - 1)*s_block(:, ib)
3123 : END DO
3124 63192 : residual_norm = MAXVAL(ABS(residual_block))
3125 488 : max_residual = MAX(max_residual, residual_norm)
3126 488 : IF (residual_norm > residual_tol) THEN
3127 : WRITE (reason, "(A,I0,A,ES9.2)") &
3128 0 : "blk=", block_first, " dS=", metric_deviation
3129 0 : EXIT stabilize_subspace
3130 : END IF
3131 :
3132 63192 : dst_coeff(:, block_first:block_last) = stabilized
3133 63192 : h_coeff(:, block_first:block_last) = h_block
3134 63192 : s_coeff(:, block_first:block_last) = s_block
3135 488 : aligned_blocks = aligned_blocks + 1
3136 488 : aligned_max_size = MAX(aligned_max_size, block_size)
3137 0 : DEALLOCATE (block_coeff, h_block, h_projected, h_projected_work, metric_vectors, &
3138 0 : residual_block, ritz_vectors, s_block, s_projected, stabilized, metric_values, &
3139 488 : ritz_values)
3140 : END IF
3141 1384 : block_first = block_last + 1
3142 : END DO
3143 :
3144 2628 : DO ib = 1, nmo
3145 93744 : residual_norm = MAXVAL(ABS(h_coeff(:, ib) - eigenvalues(ib)*s_coeff(:, ib)))
3146 2628 : max_residual = MAX(max_residual, residual_norm)
3147 : END DO
3148 532 : IF (max_residual > residual_tol) THEN
3149 : WRITE (reason, "(A,ES10.3)") &
3150 0 : "atom/AO W90 reuse guarded: Ritz residual=", max_residual
3151 0 : EXIT stabilize_subspace
3152 : END IF
3153 :
3154 532 : CALL cp_cfm_set_submatrix(dst_cfm, dst_coeff)
3155 :
3156 532 : IF (aligned_blocks == 0) aligned_min_svalue = 0.0_dp
3157 532 : success = .TRUE.
3158 : END BLOCK stabilize_subspace
3159 :
3160 532 : CALL cp_cfm_release(s_dst)
3161 532 : CALL cp_cfm_release(h_dst)
3162 :
3163 1064 : END SUBROUTINE ritz_stabilize_wannier90_subspace
3164 :
3165 : ! **************************************************************************************************
3166 : !> \brief Reconstruct a Wannier90 export window from a larger symmetry-transformed SCF MO space.
3167 : !> \param src_cfm transformed source MO window
3168 : !> \param dst_cfm exported reconstructed MO coefficients
3169 : !> \param matrix_s real-space overlap matrix
3170 : !> \param matrix_ks real-space Kohn-Sham matrix
3171 : !> \param xkp target k-point coordinate
3172 : !> \param cell_to_index real-space cell index table
3173 : !> \param sab_nl overlap neighbor list
3174 : !> \param ispin spin index
3175 : !> \param eigenvalues reconstructed target eigenvalues for the exported window
3176 : !> \param nmo_export number of MOs to export
3177 : !> \param success true if the reconstructed window is an invariant H(k),S(k) subspace
3178 : !> \param reason diagnostic message
3179 : !> \param min_svalue smallest S(k)-metric eigenvalue in the source window
3180 : !> \param max_residual largest target Ritz residual
3181 : ! **************************************************************************************************
3182 0 : SUBROUTINE ritz_reconstruct_wannier90_window(src_cfm, dst_cfm, matrix_s, &
3183 : matrix_ks, xkp, cell_to_index, sab_nl, ispin, &
3184 0 : eigenvalues, nmo_export, success, reason, min_svalue, &
3185 : max_residual)
3186 : TYPE(cp_cfm_type), INTENT(IN) :: src_cfm, dst_cfm
3187 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_s, matrix_ks
3188 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
3189 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
3190 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
3191 : POINTER :: sab_nl
3192 : INTEGER, INTENT(IN) :: ispin
3193 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: eigenvalues
3194 : INTEGER, INTENT(IN) :: nmo_export
3195 : LOGICAL, INTENT(OUT) :: success
3196 : CHARACTER(LEN=*), INTENT(OUT) :: reason
3197 : REAL(KIND=dp), INTENT(OUT) :: min_svalue, max_residual
3198 :
3199 : REAL(KIND=dp), PARAMETER :: eigenvalue_tol = 1.0e-6_dp, &
3200 : residual_tol = 1.0e-7_dp
3201 :
3202 0 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: coeff_work, h_coeff, h_projected, &
3203 0 : h_projected_work, metric_vectors, residual_block, ritz_vectors, s_coeff, s_projected, &
3204 0 : source_coeff, stabilized
3205 : INTEGER :: ib, nao, nmo_source
3206 : REAL(KIND=dp) :: max_eigenvalue_shift, metric_deviation, &
3207 : norm_value
3208 0 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: metric_values, ritz_values, &
3209 0 : source_eigenvalues
3210 : TYPE(cp_cfm_type) :: h_src, s_src
3211 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct_metric
3212 :
3213 0 : success = .FALSE.
3214 0 : reason = ""
3215 0 : min_svalue = HUGE(1.0_dp)
3216 0 : max_residual = HUGE(1.0_dp)
3217 :
3218 0 : NULLIFY (matrix_struct_metric)
3219 : CALL cp_cfm_get_info(src_cfm, nrow_global=nao, ncol_global=nmo_source, &
3220 0 : matrix_struct=matrix_struct_metric)
3221 0 : IF (nmo_export > nmo_source) THEN
3222 0 : reason = "Wannier90 export window is larger than the transformed SCF MO space"
3223 0 : RETURN
3224 : END IF
3225 0 : IF (SIZE(eigenvalues) < nmo_export) THEN
3226 0 : reason = "not enough eigenvalue storage for Wannier90 source-window reconstruction"
3227 : RETURN
3228 : END IF
3229 :
3230 0 : CALL cp_cfm_create(s_src, matrix_struct_metric)
3231 0 : CALL cp_cfm_create(h_src, matrix_struct_metric)
3232 :
3233 : CALL apply_wannier90_kp_matrix(matrix_s, 1, xkp, cell_to_index, sab_nl, &
3234 0 : src_cfm, s_src)
3235 : CALL apply_wannier90_kp_matrix(matrix_ks, ispin, xkp, cell_to_index, sab_nl, &
3236 0 : src_cfm, h_src)
3237 :
3238 0 : ALLOCATE (source_coeff(nao, nmo_source), s_coeff(nao, nmo_source), &
3239 0 : h_coeff(nao, nmo_source), h_projected(nmo_source, nmo_source), &
3240 0 : h_projected_work(nmo_source, nmo_source), metric_vectors(nmo_source, nmo_source), &
3241 0 : residual_block(nao, nmo_export), ritz_vectors(nmo_source, nmo_source), &
3242 0 : s_projected(nmo_source, nmo_source), stabilized(nao, nmo_export), &
3243 0 : coeff_work(nao, nmo_source), metric_values(nmo_source), ritz_values(nmo_source), &
3244 0 : source_eigenvalues(nmo_export))
3245 0 : source_eigenvalues(1:nmo_export) = eigenvalues(1:nmo_export)
3246 0 : CALL cp_cfm_get_submatrix(src_cfm, source_coeff)
3247 0 : CALL cp_cfm_get_submatrix(s_src, s_coeff)
3248 0 : CALL cp_cfm_get_submatrix(h_src, h_coeff)
3249 0 : s_projected(:, :) = MATMUL(CONJG(TRANSPOSE(source_coeff)), s_coeff)
3250 0 : h_projected(:, :) = MATMUL(CONJG(TRANSPOSE(source_coeff)), h_coeff)
3251 0 : s_projected(:, :) = 0.5_dp*(s_projected + CONJG(TRANSPOSE(s_projected)))
3252 0 : h_projected(:, :) = 0.5_dp*(h_projected + CONJG(TRANSPOSE(h_projected)))
3253 :
3254 : reconstruct_window: BLOCK
3255 0 : CALL diag_complex(s_projected, metric_vectors, metric_values)
3256 0 : min_svalue = MINVAL(metric_values)
3257 0 : metric_deviation = MAXVAL(ABS(metric_values - 1.0_dp))
3258 0 : IF (min_svalue < 1.0e-10_dp) THEN
3259 : WRITE (reason, "(A,ES9.2,A,ES9.2)") &
3260 0 : "singular expanded metric smin=", min_svalue, " dS=", metric_deviation
3261 0 : EXIT reconstruct_window
3262 : END IF
3263 :
3264 0 : DO ib = 1, nmo_source
3265 0 : metric_vectors(:, ib) = metric_vectors(:, ib)/SQRT(metric_values(ib))
3266 : END DO
3267 0 : h_projected_work(:, :) = MATMUL(h_projected, metric_vectors)
3268 0 : h_projected(:, :) = MATMUL(CONJG(TRANSPOSE(metric_vectors)), h_projected_work)
3269 0 : h_projected(:, :) = 0.5_dp*(h_projected + CONJG(TRANSPOSE(h_projected)))
3270 0 : CALL diag_complex(h_projected, ritz_vectors, ritz_values)
3271 0 : h_projected_work(:, :) = MATMUL(metric_vectors, ritz_vectors)
3272 0 : ritz_vectors(:, :) = h_projected_work
3273 0 : stabilized(:, :) = MATMUL(source_coeff, ritz_vectors(:, 1:nmo_export))
3274 0 : coeff_work(:, :) = MATMUL(h_coeff, ritz_vectors)
3275 0 : h_coeff(:, :) = coeff_work
3276 0 : coeff_work(:, :) = MATMUL(s_coeff, ritz_vectors)
3277 0 : s_coeff(:, :) = coeff_work
3278 0 : DO ib = 1, nmo_export
3279 0 : norm_value = SQRT(ABS(REAL(DOT_PRODUCT(stabilized(:, ib), s_coeff(:, ib)), KIND=dp)))
3280 0 : IF (norm_value > EPSILON(1.0_dp)) THEN
3281 0 : stabilized(:, ib) = stabilized(:, ib)/norm_value
3282 0 : h_coeff(:, ib) = h_coeff(:, ib)/norm_value
3283 0 : s_coeff(:, ib) = s_coeff(:, ib)/norm_value
3284 : END IF
3285 : END DO
3286 0 : residual_block(:, :) = h_coeff(:, 1:nmo_export)
3287 0 : DO ib = 1, nmo_export
3288 0 : residual_block(:, ib) = residual_block(:, ib) - ritz_values(ib)*s_coeff(:, ib)
3289 : END DO
3290 0 : max_residual = MAXVAL(ABS(residual_block))
3291 0 : IF (max_residual > residual_tol) THEN
3292 : WRITE (reason, "(A,ES9.2)") &
3293 0 : "expanded dS=", metric_deviation
3294 0 : EXIT reconstruct_window
3295 : END IF
3296 0 : max_eigenvalue_shift = MAXVAL(ABS(ritz_values(1:nmo_export) - source_eigenvalues(1:nmo_export)))
3297 0 : IF (max_eigenvalue_shift > eigenvalue_tol) THEN
3298 : WRITE (reason, "(A,ES9.2)") &
3299 0 : "expanded dS=", metric_deviation
3300 0 : EXIT reconstruct_window
3301 : END IF
3302 :
3303 0 : CALL cp_cfm_set_submatrix(dst_cfm, stabilized)
3304 0 : success = .TRUE.
3305 :
3306 : END BLOCK reconstruct_window
3307 :
3308 0 : DEALLOCATE (source_coeff, s_coeff, h_coeff, h_projected, h_projected_work, metric_vectors, &
3309 0 : residual_block, ritz_vectors, s_projected, stabilized, coeff_work, metric_values, &
3310 0 : ritz_values, source_eigenvalues)
3311 0 : CALL cp_cfm_release(s_src)
3312 0 : CALL cp_cfm_release(h_src)
3313 :
3314 0 : END SUBROUTINE ritz_reconstruct_wannier90_window
3315 :
3316 : ! **************************************************************************************************
3317 : !> \brief Map the full Wannier90 mesh to SCF representative k-points and symmetry operations.
3318 : !> \param kpoint full Wannier90 export k-point object
3319 : !> \param qs_kpoint SCF k-point object
3320 : !> \param source_kpoint source representative index for each full k-point
3321 : !> \param sym_index symmetry entry in source kp_sym; 0 direct, -1 time reversal only
3322 : !> \param success true if every full k-point was mapped
3323 : !> \param reason diagnostic message
3324 : ! **************************************************************************************************
3325 60 : SUBROUTINE build_wannier90_scf_mapping(kpoint, qs_kpoint, source_kpoint, sym_index, success, &
3326 : reason)
3327 : TYPE(kpoint_type), POINTER :: kpoint, qs_kpoint
3328 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: source_kpoint, sym_index
3329 : LOGICAL, INTENT(OUT) :: success
3330 : CHARACTER(LEN=*), INTENT(OUT) :: reason
3331 :
3332 : INTEGER :: ik, ikred, imatch, isym, nfull
3333 : TYPE(kpoint_sym_type), POINTER :: kpsym
3334 :
3335 60 : success = .FALSE.
3336 60 : reason = ""
3337 60 : nfull = kpoint%nkp
3338 240 : ALLOCATE (source_kpoint(nfull), sym_index(nfull))
3339 60 : source_kpoint(:) = 0
3340 60 : sym_index(:) = 0
3341 :
3342 226 : DO ikred = 1, qs_kpoint%nkp
3343 166 : imatch = find_matching_kpoint(kpoint%xkp, qs_kpoint%xkp(1:3, ikred))
3344 226 : IF (imatch > 0 .AND. source_kpoint(imatch) == 0) THEN
3345 166 : source_kpoint(imatch) = ikred
3346 166 : sym_index(imatch) = 0
3347 : END IF
3348 : END DO
3349 :
3350 : ! Prefer pure time-reversal partners before general atom/AO symmetry operations.
3351 226 : DO ikred = 1, qs_kpoint%nkp
3352 664 : imatch = find_matching_kpoint(kpoint%xkp, -qs_kpoint%xkp(1:3, ikred))
3353 226 : IF (imatch > 0 .AND. source_kpoint(imatch) == 0) THEN
3354 70 : source_kpoint(imatch) = ikred
3355 70 : sym_index(imatch) = -1
3356 : END IF
3357 : END DO
3358 :
3359 60 : IF (ASSOCIATED(qs_kpoint%kp_sym)) THEN
3360 226 : DO ikred = 1, qs_kpoint%nkp
3361 166 : kpsym => qs_kpoint%kp_sym(ikred)%kpoint_sym
3362 166 : IF (.NOT. ASSOCIATED(kpsym)) CYCLE
3363 166 : IF (.NOT. kpsym%apply_symmetry) CYCLE
3364 8058 : DO isym = 1, kpsym%nwred
3365 7904 : imatch = find_matching_kpoint(kpoint%xkp, kpsym%xkp(1:3, isym))
3366 8070 : IF (imatch > 0 .AND. source_kpoint(imatch) == 0) THEN
3367 680 : source_kpoint(imatch) = ikred
3368 680 : sym_index(imatch) = isym
3369 : END IF
3370 : END DO
3371 : END DO
3372 : END IF
3373 :
3374 976 : DO ik = 1, nfull
3375 976 : IF (source_kpoint(ik) == 0) THEN
3376 0 : reason = "not all full-mesh k-points are represented by the SCF symmetry orbits"
3377 0 : RETURN
3378 : END IF
3379 : END DO
3380 60 : success = .TRUE.
3381 :
3382 60 : END SUBROUTINE build_wannier90_scf_mapping
3383 :
3384 : ! **************************************************************************************************
3385 : !> \brief Find a fractional k-point in a periodic mesh.
3386 : !> \param xkp_mesh mesh coordinates
3387 : !> \param xkp_search coordinate to find
3388 : !> \return matching index, or zero when no match is found
3389 : ! **************************************************************************************************
3390 8236 : INTEGER FUNCTION find_matching_kpoint(xkp_mesh, xkp_search) RESULT(ik_match)
3391 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: xkp_mesh
3392 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp_search
3393 :
3394 : INTEGER :: ik
3395 :
3396 8236 : ik_match = 0
3397 125162 : DO ik = 1, SIZE(xkp_mesh, 2)
3398 125162 : IF (kpoint_same_periodic(xkp_mesh(1:3, ik), xkp_search)) THEN
3399 8236 : ik_match = ik
3400 8236 : RETURN
3401 : END IF
3402 : END DO
3403 :
3404 : END FUNCTION find_matching_kpoint
3405 :
3406 : ! **************************************************************************************************
3407 : !> \brief Infer a tensor-product Wannier90 mesh from explicit fractional k-point coordinates.
3408 : !> \param kpt_latt explicit k-point coordinates in reciprocal-lattice units
3409 : !> \param mp_grid inferred mesh dimensions
3410 : !> \param valid true if the coordinate set is compatible with a tensor-product mesh
3411 : ! **************************************************************************************************
3412 6 : SUBROUTINE infer_wannier_mp_grid(kpt_latt, mp_grid, valid)
3413 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: kpt_latt
3414 : INTEGER, DIMENSION(3), INTENT(OUT) :: mp_grid
3415 : LOGICAL, INTENT(OUT) :: valid
3416 :
3417 : INTEGER :: coord_id, i, idim, idx, n_unique, &
3418 : num_kpts, stride, unique_id
3419 : LOGICAL :: known
3420 6 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: seen
3421 : REAL(KIND=dp) :: coord
3422 6 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: unique_coord
3423 :
3424 6 : num_kpts = SIZE(kpt_latt, 2)
3425 6 : mp_grid(:) = 0
3426 18 : ALLOCATE (unique_coord(3, num_kpts))
3427 24 : DO idim = 1, 3
3428 : n_unique = 0
3429 162 : DO i = 1, num_kpts
3430 144 : coord = kpt_latt(idim, i) - FLOOR(kpt_latt(idim, i))
3431 144 : IF (ABS(coord - 1.0_dp) < 1.0e-8_dp) coord = 0.0_dp
3432 144 : known = .FALSE.
3433 216 : DO unique_id = 1, n_unique
3434 216 : IF (ABS(unique_coord(idim, unique_id) - coord) < 1.0e-8_dp) THEN
3435 : known = .TRUE.
3436 : EXIT
3437 : END IF
3438 : END DO
3439 162 : IF (.NOT. known) THEN
3440 36 : n_unique = n_unique + 1
3441 36 : unique_coord(idim, n_unique) = coord
3442 : END IF
3443 : END DO
3444 24 : mp_grid(idim) = n_unique
3445 : END DO
3446 6 : valid = (mp_grid(1)*mp_grid(2)*mp_grid(3) == num_kpts)
3447 6 : IF (valid) THEN
3448 18 : ALLOCATE (seen(num_kpts))
3449 6 : seen(:) = .FALSE.
3450 54 : DO i = 1, num_kpts
3451 : idx = 1
3452 : stride = 1
3453 192 : DO idim = 1, 3
3454 144 : coord = kpt_latt(idim, i) - FLOOR(kpt_latt(idim, i))
3455 144 : IF (ABS(coord - 1.0_dp) < 1.0e-8_dp) coord = 0.0_dp
3456 144 : coord_id = 0
3457 216 : DO unique_id = 1, mp_grid(idim)
3458 216 : IF (ABS(unique_coord(idim, unique_id) - coord) < 1.0e-8_dp) THEN
3459 : coord_id = unique_id
3460 : EXIT
3461 : END IF
3462 : END DO
3463 144 : CPASSERT(coord_id > 0)
3464 144 : idx = idx + (coord_id - 1)*stride
3465 192 : stride = stride*mp_grid(idim)
3466 : END DO
3467 48 : IF (seen(idx)) valid = .FALSE.
3468 54 : seen(idx) = .TRUE.
3469 : END DO
3470 54 : valid = valid .AND. ALL(seen)
3471 6 : DEALLOCATE (seen)
3472 : END IF
3473 6 : DEALLOCATE (unique_coord)
3474 :
3475 6 : END SUBROUTINE infer_wannier_mp_grid
3476 :
3477 272 : END MODULE qs_wannier90
|