(git:6344619)
Loading...
Searching...
No Matches
wannier90_projections.F
Go to the documentation of this file.
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 Gauge-covariant selection of fixed trial orbitals for Wannier90.
10!> \author Thomas D. Kuehne, tkuehne@cp2k.org
11! **************************************************************************************************
13#if defined (__HAS_IEEE_EXCEPTIONS)
14 USE ieee_exceptions, ONLY: ieee_all, ieee_get_halting_mode, ieee_set_halting_mode
15#endif
16 USE kinds, ONLY: dp
18#include "./base/base_uses.f90"
19
20 IMPLICIT NONE
21 PRIVATE
22
23 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'wannier90_projections'
26
27CONTAINS
28
29! **************************************************************************************************
30!> \brief Restrict fixed trial selection to the bands admitted by disentanglement windows.
31!> \param eigenvalues Exported eigenvalues in the same units as the windows.
32!> \param num_wann Number of target Wannier functions.
33!> \param windows Outer minimum/maximum and frozen minimum/maximum energies.
34!> \param window_set Whether each bound is explicitly set.
35!> \param allowed Bands used to select the physical trial orbitals.
36! **************************************************************************************************
37 SUBROUTINE wannier90_window_mask(eigenvalues, num_wann, windows, window_set, allowed)
38 REAL(kind=dp), INTENT(IN) :: eigenvalues(:)
39 INTEGER, INTENT(IN) :: num_wann
40 REAL(kind=dp), INTENT(IN) :: windows(4)
41 LOGICAL, INTENT(IN) :: window_set(4)
42 LOGICAL, INTENT(OUT) :: allowed(:)
43
44 LOGICAL :: frozen(size(eigenvalues))
45
46 cpassert(SIZE(allowed) == SIZE(eigenvalues))
47 allowed = .true.
48 IF (window_set(1)) allowed = allowed .AND. eigenvalues >= windows(1)
49 IF (window_set(2)) allowed = allowed .AND. eigenvalues <= windows(2)
50 IF (count(allowed) < num_wann) THEN
51 cpabort('Wannier90 outer window contains too few bands.')
52 END IF
53 IF (window_set(4)) THEN
54 frozen = eigenvalues <= windows(4)
55 IF (window_set(3)) frozen = frozen .AND. eigenvalues >= windows(3)
56 IF (count(frozen) > num_wann) THEN
57 cpabort('Wannier90 frozen window contains too many bands.')
58 END IF
59 IF (any(frozen .AND. .NOT. allowed)) THEN
60 cpabort('Wannier90 frozen states lie outside the outer window.')
61 END IF
62 IF (count(frozen) == num_wann) allowed = frozen
63 END IF
64 END SUBROUTINE wannier90_window_mask
65
66! **************************************************************************************************
67!> \brief Form tetrahedral s/p trials from the full AO bank before selecting projections.
68!> \param ao Band/AO/k-point projections on the source rank, empty elsewhere.
69!> \param groups AO indices of one s and three p functions for each radial shell pair.
70!> \param candidates Hybrid trials and AOs not belonging to any hybrid group.
71! **************************************************************************************************
72 SUBROUTINE wannier90_hybrid_candidates(ao, groups, candidates)
73 COMPLEX(KIND=dp), INTENT(IN) :: ao(:, :, :)
74 INTEGER, INTENT(IN) :: groups(:, :)
75 COMPLEX(KIND=dp), ALLOCATABLE, INTENT(OUT) :: candidates(:, :, :)
76
77 REAL(kind=dp), PARAMETER :: hybrid(4, 4) = 0.5_dp*reshape([1.0_dp, 1.0_dp, 1.0_dp, 1.0_dp, &
78 1.0_dp, 1.0_dp, -1.0_dp, -1.0_dp, 1.0_dp, -1.0_dp, 1.0_dp, -1.0_dp, 1.0_dp, -1.0_dp, &
79 -1.0_dp, 1.0_dp], [4, 4])
80
81 INTEGER :: iao, ic, ig, ik, nao, nb, nk
82 LOGICAL, ALLOCATABLE :: grouped(:)
83
84 nb = SIZE(ao, 1)
85 nao = SIZE(ao, 2)
86 nk = SIZE(ao, 3)
87 IF (nb == 0) THEN
88 ALLOCATE (candidates(0, 0, 0))
89 RETURN
90 END IF
91 cpassert(SIZE(groups, 1) == 4)
92 cpassert(all(groups > 0) .AND. all(groups <= nao))
93 ALLOCATE (grouped(nao))
94 grouped = .false.
95 DO ig = 1, SIZE(groups, 2)
96 DO ic = 1, 4
97 cpassert(count(groups(:, ig) == groups(ic, ig)) == 1)
98 END DO
99 grouped(groups(:, ig)) = .true.
100 END DO
101 ALLOCATE (candidates(nb, count(.NOT. grouped) + 4*SIZE(groups, 2), nk))
102 ic = 0
103 DO iao = 1, nao
104 IF (grouped(iao)) cycle
105 ic = ic + 1
106 candidates(:, ic, :) = ao(:, iao, :)
107 END DO
108 ! Include every radial s/p pair. Selecting only already-pivoted AO columns
109 ! would exclude physical trials that the original QR happened to discard.
110 DO ig = 1, SIZE(groups, 2)
111 DO ik = 1, nk
112 candidates(:, ic + 1:ic + 4, ik) = matmul(ao(:, groups(:, ig), ik), hybrid)
113 END DO
114 ic = ic + 4
115 END DO
116 DEALLOCATE (grouped)
117 END SUBROUTINE wannier90_hybrid_candidates
118
119! **************************************************************************************************
120!> \brief Select the same trial orbitals at every k-point using a mesh-wide pivoted QR.
121!> \param candidates Band/candidate/k-point projections, stored only on the source rank.
122!> \param para_env Global parallel environment.
123!> \param amn Selected projection matrices, allocated with the target dimensions on all ranks.
124!> \param pivots Selected candidate indices, allocated on all ranks.
125!> \param min_svalue Smallest singular value of the selected projections over the mesh.
126!> \param anchor_kpoint Optional fixed QR anchor; zero selects the stacked mesh. Without an
127!> explicit anchor, try individual k-points if the stacked selection loses rank.
128! **************************************************************************************************
129 SUBROUTINE wannier90_select_projections(candidates, para_env, amn, pivots, min_svalue, anchor_kpoint)
130 COMPLEX(KIND=dp), INTENT(IN) :: candidates(:, :, :)
131 TYPE(mp_para_env_type), POINTER :: para_env
132 COMPLEX(KIND=dp), INTENT(OUT) :: amn(:, :, :)
133 INTEGER, INTENT(OUT) :: pivots(:)
134 REAL(kind=dp), INTENT(OUT) :: min_svalue
135 INTEGER, INTENT(IN), OPTIONAL :: anchor_kpoint
136
137 COMPLEX(KIND=dp), ALLOCATABLE :: qr(:, :)
138 INTEGER :: anchor, best_defect, defect, &
139 first_anchor, ic, ik, iw, last_anchor, &
140 nb, nc, nk, nr, nw, previous_defect, &
141 saved_pivot
142 INTEGER, ALLOCATABLE :: best_pivots(:)
143
144 nb = SIZE(amn, 1)
145 nw = SIZE(amn, 2)
146 nk = SIZE(amn, 3)
147 cpassert(nb >= nw .AND. nw > 0 .AND. SIZE(pivots) == nw)
148 first_anchor = 0
149 last_anchor = nk
150 IF (PRESENT(anchor_kpoint)) THEN
151 cpassert(anchor_kpoint >= 0 .AND. anchor_kpoint <= nk)
152 first_anchor = anchor_kpoint
153 last_anchor = anchor_kpoint
154 END IF
155 min_svalue = 0.0_dp
156 IF (para_env%is_source()) THEN
157 nc = SIZE(candidates, 2)
158 cpassert(SIZE(candidates, 1) == nb .AND. SIZE(candidates, 3) == nk)
159 cpassert(nc >= nw)
160 ALLOCATE (best_pivots(nw))
161 best_defect = huge(0)
162 DO anchor = first_anchor, last_anchor
163 nr = nb*nk
164 IF (anchor > 0) nr = nb
165 ALLOCATE (qr(nr, nc))
166 IF (anchor == 0) THEN
167 DO ik = 1, nk
168 qr((ik - 1)*nb + 1:ik*nb, :) = candidates(:, :, ik)
169 END DO
170 ELSE
171 qr(:, :) = candidates(:, :, anchor)
172 END IF
173 ! A unitary change of the MO gauge acts on rows, not on the trial orbitals.
174 ! The selected columns therefore represent one physical trial set on the full mesh.
175 CALL select_qr_pivots(qr, pivots)
176 DO ik = 1, nk
177 amn(:, :, ik) = candidates(:, pivots, ik)
178 END DO
179 DEALLOCATE (qr)
180
181 min_svalue = wannier90_projection_rank(amn, defect)
182 IF (defect < best_defect) THEN
183 best_defect = defect
184 best_pivots(:) = pivots(:)
185 END IF
186 IF (min_svalue >= 1.0e-10_dp) EXIT
187 END DO
188 IF (best_defect > 0 .AND. .NOT. PRESENT(anchor_kpoint)) THEN
189 ! QR optimizes one anchor, not rank everywhere. Each accepted exchange
190 ! strictly reduces the number of missing trial directions across the mesh.
191 pivots = best_pivots
192 DO
193 previous_defect = best_defect
194 repair: DO iw = 1, nw
195 saved_pivot = pivots(iw)
196 DO ic = 1, nc
197 IF (any(pivots == ic)) cycle
198 pivots(iw) = ic
199 DO ik = 1, nk
200 amn(:, :, ik) = candidates(:, pivots, ik)
201 END DO
202 min_svalue = wannier90_projection_rank(amn, defect)
203 IF (defect < best_defect) THEN
204 best_defect = defect
205 EXIT repair
206 END IF
207 pivots(iw) = saved_pivot
208 END DO
209 END DO repair
210 IF (best_defect == 0 .OR. best_defect == previous_defect) EXIT
211 END DO
212 DO ik = 1, nk
213 amn(:, :, ik) = candidates(:, pivots, ik)
214 END DO
215 min_svalue = wannier90_projection_rank(amn)
216 END IF
217 DEALLOCATE (best_pivots)
218 END IF
219 CALL para_env%bcast(amn)
220 CALL para_env%bcast(pivots)
221 CALL para_env%bcast(min_svalue)
222
223 END SUBROUTINE wannier90_select_projections
224
225! **************************************************************************************************
226!> \brief Pivoted Householder QR with a reproducible choice among numerically tied columns.
227!> \param qr Stacked or single-k-point candidate matrix, overwritten by its factorization.
228!> \param pivots Original indices of the selected columns.
229! **************************************************************************************************
230 SUBROUTINE select_qr_pivots(qr, pivots)
231 COMPLEX(KIND=dp), INTENT(INOUT) :: qr(:, :)
232 INTEGER, INTENT(OUT) :: pivots(:)
233
234 REAL(kind=dp), PARAMETER :: tie_tolerance = 1.0e-12_dp
235
236 COMPLEX(KIND=dp) :: tau(1)
237 COMPLEX(KIND=dp), ALLOCATABLE :: column(:), work(:)
238 INTEGER :: ic, info, ip, iw, nc, nr, original
239 INTEGER, ALLOCATABLE :: order(:)
240 REAL(kind=dp) :: largest
241 REAL(kind=dp), ALLOCATABLE :: norms(:)
242
243 nr = SIZE(qr, 1)
244 nc = SIZE(qr, 2)
245 cpassert(SIZE(pivots) <= min(nr, nc))
246 ALLOCATE (column(nr), work(max(1, nc)), order(nc), norms(nc))
247 order(:) = [(ic, ic=1, nc)]
248 DO iw = 1, SIZE(pivots)
249 DO ic = iw, nc
250 norms(ic) = sum(abs(qr(iw:nr, ic))**2)
251 END DO
252 largest = maxval(norms(iw:nc))
253 original = huge(0)
254 ip = iw
255 DO ic = iw, nc
256 IF (norms(ic) < largest*(1.0_dp - tie_tolerance)) cycle
257 IF (order(ic) < original) THEN
258 ip = ic
259 original = order(ic)
260 END IF
261 END DO
262 column(:) = qr(:, iw)
263 qr(:, iw) = qr(:, ip)
264 qr(:, ip) = column
265 order(ip) = order(iw)
266 order(iw) = original
267 pivots(iw) = original
268 ! LAPACK constructs and applies the reflector; only pivot tie-breaking differs.
269 CALL zgeqrf(nr - iw + 1, 1, qr(iw, iw), nr, tau, work, SIZE(work), info)
270 cpassert(info == 0)
271 IF (iw < nc) THEN
272 CALL zunmqr('L', 'C', nr - iw + 1, nc - iw, 1, qr(iw, iw), nr, tau, &
273 qr(iw, iw + 1), nr, work, SIZE(work), info)
274 cpassert(info == 0)
275 END IF
276 END DO
277 DEALLOCATE (column, work, order, norms)
278 END SUBROUTINE select_qr_pivots
279
280! **************************************************************************************************
281!> \brief Validate the trial rank independently at every k-point.
282!> \param amn Band/trial/k-point projections, possibly rectangular.
283!> \param rank_defect Total number of missing trial directions summed over the mesh.
284!> \return Smallest singular value over all k-points.
285! **************************************************************************************************
286 FUNCTION wannier90_projection_rank(amn, rank_defect) RESULT(min_svalue)
287 COMPLEX(KIND=dp), INTENT(IN) :: amn(:, :, :)
288 INTEGER, INTENT(OUT), OPTIONAL :: rank_defect
289 REAL(kind=dp) :: min_svalue
290
291 COMPLEX(KIND=dp) :: query(1), unused_u(1, 1), unused_v(1, 1)
292 COMPLEX(KIND=dp), ALLOCATABLE :: matrix(:, :), work(:)
293 INTEGER :: ik, info, lwork, nb, nw
294 REAL(kind=dp), ALLOCATABLE :: rwork(:), svalues(:)
295#if defined (__HAS_IEEE_EXCEPTIONS)
296 LOGICAL :: halt(size(ieee_all))
297#endif
298
299 nb = SIZE(amn, 1)
300 nw = SIZE(amn, 2)
301 cpassert(nb >= nw .AND. nw > 0 .AND. SIZE(amn, 3) > 0)
302 ALLOCATE (matrix(nb, nw), svalues(nw), rwork(5*nw))
303 matrix(:, :) = amn(:, :, 1)
304 CALL zgesvd('N', 'N', nb, nw, matrix, nb, svalues, unused_u, 1, unused_v, 1, query, -1, rwork, info)
305 cpassert(info == 0)
306 lwork = max(1, int(real(query(1), kind=dp)))
307 ALLOCATE (work(lwork))
308 min_svalue = huge(0.0_dp)
309 IF (PRESENT(rank_defect)) rank_defect = 0
310 DO ik = 1, SIZE(amn, 3)
311 matrix(:, :) = amn(:, :, ik)
312 ! The values-only SVD uses DLASQ, whose IEEECK probe deliberately raises exceptions.
313#if defined (__HAS_IEEE_EXCEPTIONS)
314 CALL ieee_get_halting_mode(ieee_all, halt)
315 CALL ieee_set_halting_mode(ieee_all, .false.)
316#endif
317 CALL zgesvd('N', 'N', nb, nw, matrix, nb, svalues, unused_u, 1, unused_v, 1, work, lwork, rwork, info)
318#if defined (__HAS_IEEE_EXCEPTIONS)
319 CALL ieee_set_halting_mode(ieee_all, halt)
320#endif
321 cpassert(info == 0)
322 min_svalue = min(min_svalue, minval(svalues))
323 IF (PRESENT(rank_defect)) rank_defect = rank_defect + count(svalues < 1.0e-10_dp)
324 END DO
325 DEALLOCATE (matrix, work, rwork, svalues)
326 END FUNCTION wannier90_projection_rank
327
328END MODULE wannier90_projections
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Interface to the message passing library MPI.
Gauge-covariant selection of fixed trial orbitals for Wannier90.
subroutine, public wannier90_window_mask(eigenvalues, num_wann, windows, window_set, allowed)
Restrict fixed trial selection to the bands admitted by disentanglement windows.
real(kind=dp) function, public wannier90_projection_rank(amn, rank_defect)
Validate the trial rank independently at every k-point.
subroutine, public wannier90_select_projections(candidates, para_env, amn, pivots, min_svalue, anchor_kpoint)
Select the same trial orbitals at every k-point using a mesh-wide pivoted QR.
subroutine, public wannier90_hybrid_candidates(ao, groups, candidates)
Form tetrahedral s/p trials from the full AO bank before selecting projections.
stores all the informations relevant to an mpi environment