(git:08825b9)
Loading...
Searching...
No Matches
spectral_localizer_bloch.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 Exact spectral flattening of a translation-invariant AO torus in primitive Bloch blocks.
10!> \author Thomas D. Kuehne, tkuehne@cp2k.org
11! **************************************************************************************************
18 USE ieee_arithmetic, ONLY: ieee_is_finite
19#if defined(__HAS_IEEE_EXCEPTIONS)
20 USE ieee_exceptions, ONLY: ieee_all, ieee_get_halting_mode, ieee_set_halting_mode
21#endif
22 USE kinds, ONLY: dp
26#include "./base/base_uses.f90"
27
28 IMPLICIT NONE
29 PRIVATE
31
32CONTAINS
33
34! **************************************************************************************************
35!> \brief Flatten all Bloch bands, then return to the original distributed real-cell/spin AO basis.
36!> \param h Original complex Hamiltonian in spin/cell/atom order, unchanged
37!> \param s Original complex metric in the same order, unchanged
38!> \param grid Full Gamma-centered torus dimensions
39!> \param nspin Number of physical spin blocks (one or two)
40!> \param energy Absolute query energy
41!> \param scale Positive flattened energy
42!> \param tolerance Structure, covariance and eigensystem tolerance
43!> \param metric_tolerance Minimum global overlap eigenvalue ratio
44!> \param gap_tolerance Minimum original electronic distance from the query
45!> \param para_env Communicator; independent eigensystems are distributed across its ranks
46!> \param flattened Newly allocated covariant shifted Hamiltonian only on success
47!> \param gap Original electronic gap, not a flattened localizer gap
48!> \param metric_ratio Global minimum/maximum overlap eigenvalue ratio across all k-points
49!> \param residuals Covariance, metric orthonormality, eigenproblem and final Hermiticity residuals
50!> \param status 0 success, 1 unresolved gap, -1 invalid layout/data, -2 metric,
51!> -4 eigensolver, -6 excessive residual, -7 broken translation covariance
52!> \note No occupied-state projection, symmetry reduction or modification of coordinate operators.
53! **************************************************************************************************
54 SUBROUTINE localizer_flatten_bloch(h, s, grid, nspin, energy, scale, tolerance, metric_tolerance, &
55 gap_tolerance, para_env, flattened, gap, metric_ratio, residuals, status)
56 TYPE(localizer_matrix_type), INTENT(INOUT) :: h, s
57 INTEGER, INTENT(IN) :: grid(3), nspin
58 REAL(dp), INTENT(IN) :: energy, scale, tolerance, &
59 metric_tolerance, gap_tolerance
60 TYPE(mp_para_env_type), POINTER :: para_env
61 TYPE(localizer_matrix_type), INTENT(OUT) :: flattened
62 REAL(dp), INTENT(OUT) :: gap, metric_ratio, residuals(4)
63 INTEGER, INTENT(OUT) :: status
64
65 COMPLEX(dp), ALLOCATABLE :: fk(:, :, :), hc(:, :, :), hk(:, :, :), &
66 sc(:, :, :), sk(:, :, :)
67 INTEGER :: ik, nb, ncell, ns, nscalar, nsmall, &
68 substatus
69 INTEGER, ALLOCATABLE :: cell_index(:), cells(:, :), starts(:)
70 INTEGER, POINTER :: sizes(:), ssize(:)
71 LOGICAL :: valid
72 REAL(dp) :: all_bounds(2), bounds(2), difference, &
73 errors(2), gap_threshold, local_gap, &
74 threshold
75 REAL(dp), ALLOCATABLE :: weights(:), xkp(:, :)
76 TYPE(localizer_matrix_type) :: reconstructed
77
78 status = -1
79 gap = 0.0_dp
80 metric_ratio = 0.0_dp
81 residuals = huge(1.0_dp)
82 IF (any(grid < 1) .OR. (nspin /= 1 .AND. nspin /= 2)) RETURN
83 IF (.NOT. all(ieee_is_finite([energy, scale, tolerance, metric_tolerance, gap_tolerance]))) RETURN
84 IF (scale <= 0.0_dp .OR. tolerance <= 0.0_dp .OR. tolerance >= 1.0_dp) RETURN
85 IF (metric_tolerance <= 0.0_dp .OR. metric_tolerance >= 1.0_dp .OR. gap_tolerance < 0.0_dp) RETURN
86 CALL dbcsr_get_info(h%real_part, nblkrows_total=nb, row_blk_size=sizes)
87 CALL dbcsr_get_info(s%real_part, nblkrows_total=ns, row_blk_size=ssize)
88 IF (nb < 1 .OR. nb /= ns) RETURN
89 IF (any(sizes /= ssize)) RETURN
90 IF (any(sizes <= 0)) RETURN
91 ! Bound the products before interpreting the repeated atom-block layout.
92 ncell = 1
93 DO ik = 1, 3
94 IF (grid(ik) > nb/ncell) RETURN
95 ncell = ncell*grid(ik)
96 END DO
97 IF (nspin > nb/ncell) RETURN
98 IF (mod(nb, nspin*ncell) /= 0) RETURN
99 ns = nb/(nspin*ncell)
100 DO ik = 1, nb
101 IF (sizes(ik) /= sizes(1 + modulo(ik - 1, ns))) RETURN
102 END DO
103 CALL localizer_dbcsr_check(h, para_env, tolerance, valid)
104 IF (.NOT. valid) RETURN
105 CALL localizer_dbcsr_check(s, para_env, tolerance, valid)
106 IF (.NOT. valid) RETURN
107 nscalar = sum(sizes(:ns))
108 nsmall = nscalar*nspin
109 ALLOCATE (starts(nb), cell_index(nb), cells(3, ncell), xkp(3, ncell), weights(ncell))
110 DO ik = 1, nb
111 starts(ik) = 1 + sum(sizes(:modulo(ik - 1, ns))) + ((ik - 1)/(ns*ncell))*nscalar
112 cell_index(ik) = 1 + modulo((ik - 1)/ns, ncell)
113 END DO
114 DO ik = 1, ncell
115 cells(:, ik) = [modulo(ik - 1, grid(1)), modulo((ik - 1)/grid(1), grid(2)), &
116 (ik - 1)/(grid(1)*grid(2))]
117 xkp(:, ik) = real(cells(:, ik), dp)/real(grid, dp)
118 END DO
119 ALLOCATE (hc(nsmall, nsmall, ncell), sc(nsmall, nsmall, ncell), &
120 hk(nsmall, nsmall, ncell), sk(nsmall, nsmall, ncell), fk(nsmall, nsmall, ncell))
121 CALL torus_blocks(h, starts, cell_index, para_env, hc)
122 CALL torus_blocks(s, starts, cell_index, para_env, sc)
123 residuals = 0.0_dp
124 ! Check the complete matrix, including missing blocks, before using one cell as representative.
125 CALL expand_blocks(hc, h, grid, starts, cell_index, para_env, reconstructed)
126 CALL matrix_difference(reconstructed, h, difference)
127 CALL localizer_dbcsr_release(reconstructed)
128 residuals(1) = difference
129 CALL expand_blocks(sc, s, grid, starts, cell_index, para_env, reconstructed)
130 CALL matrix_difference(reconstructed, s, difference)
131 CALL localizer_dbcsr_release(reconstructed)
132 residuals(1) = max(residuals(1), difference)
133 status = -7
134 IF (.NOT. ieee_is_finite(residuals(1)) .OR. residuals(1) > tolerance) RETURN
135 ! B(R) -> B(k) has positive phase and no normalization; the inverse has negative phase / Nk.
136 weights = 1.0_dp
137 CALL k_grid_to_cell_fft(hc, -xkp, grid, cells, hk, weights=weights)
138 CALL k_grid_to_cell_fft(sc, -xkp, grid, cells, sk, weights=weights)
139 fk = 0.0_dp
140 all_bounds = [huge(1.0_dp), 0.0_dp]
141 gap = huge(1.0_dp)
142 gap_threshold = gap_tolerance
143 status = 0
144 DO ik = 1, ncell
145 IF (modulo(ik - 1, para_env%num_pe) /= para_env%mepos) cycle
146 CALL flatten_block(hk(:, :, ik), sk(:, :, ik), energy, scale, tolerance, metric_tolerance, fk(:, :, ik), &
147 local_gap, bounds, threshold, errors, substatus)
148 IF (substatus /= 0) THEN
149 status = min(status, substatus)
150 cycle
151 END IF
152 all_bounds = [min(all_bounds(1), bounds(1)), max(all_bounds(2), bounds(2))]
153 gap = min(gap, local_gap)
154 gap_threshold = max(gap_threshold, threshold)
155 residuals(2:3) = max(residuals(2:3), errors)
156 END DO
157 CALL para_env%min(status)
158 IF (status /= 0) RETURN
159 CALL para_env%min(all_bounds(1))
160 CALL para_env%max(all_bounds(2))
161 CALL para_env%min(gap)
162 CALL para_env%max(gap_threshold)
163 CALL para_env%max(residuals)
164 metric_ratio = all_bounds(1)/all_bounds(2)
165 status = -2
166 IF (metric_ratio <= metric_tolerance) RETURN
167 status = 1
168 IF (gap <= gap_threshold) RETURN
169 status = -6
170 IF (maxval(residuals) > tolerance .OR. .NOT. all(ieee_is_finite(residuals))) RETURN
171 CALL para_env%sum(fk)
172 CALL k_grid_to_cell_fft(fk, xkp, grid, cells, hc)
173 CALL expand_blocks(hc, h, grid, starts, cell_index, para_env, flattened)
174 CALL localizer_dbcsr_check(flattened, para_env, tolerance, valid, residuals(4))
175 IF (.NOT. valid) THEN
176 CALL localizer_dbcsr_release(flattened)
177 RETURN
178 END IF
179 status = 0
180 END SUBROUTINE localizer_flatten_bloch
181
182! **************************************************************************************************
183!> \brief Extract the distributed first bra cell for every physical spin; no full AO gathering.
184!> \param matrix Original matrix
185!> \param starts Primitive spin/AO offsets for each atomic block
186!> \param cell_index Cell of each block
187!> \param para_env Communicator
188!> \param blocks One primitive spin matrix for every cell displacement
189! **************************************************************************************************
190 SUBROUTINE torus_blocks(matrix, starts, cell_index, para_env, blocks)
191 TYPE(localizer_matrix_type), INTENT(INOUT) :: matrix
192 INTEGER, INTENT(IN) :: starts(:), cell_index(:)
193 TYPE(mp_para_env_type), POINTER :: para_env
194 COMPLEX(dp), INTENT(OUT) :: blocks(:, :, :)
195
196 CALL extract(matrix%real_part, cmplx(1.0_dp, 0.0_dp, dp), .true.)
197 CALL extract(matrix%imaginary_part, cmplx(0.0_dp, 1.0_dp, dp), .false.)
198 CALL para_env%sum(blocks)
199
200 CONTAINS
201
202! **************************************************************************************************
203!> \brief Accumulate one real component into the complex block row.
204!> \param component Real DBCSR component
205!> \param factor Real or imaginary unit
206!> \param initialize Reset output before accumulation
207! **************************************************************************************************
208 SUBROUTINE extract(component, factor, initialize)
209 TYPE(dbcsr_type), INTENT(INOUT) :: component
210 COMPLEX(dp), INTENT(IN) :: factor
211 LOGICAL, INTENT(IN) :: initialize
212
213 INTEGER :: c, col, i, j, row
214 LOGICAL :: transposed
215 REAL(dp), POINTER :: block(:, :)
216 TYPE(dbcsr_iterator_type) :: iterator
217
218 IF (initialize) blocks = 0.0_dp
219 CALL dbcsr_iterator_start(iterator, component)
220 DO WHILE (dbcsr_iterator_blocks_left(iterator))
221 CALL dbcsr_iterator_next_block(iterator, row, col, block, transposed=transposed)
222 cpassert(.NOT. transposed)
223 IF (cell_index(row) /= 1) cycle
224 i = starts(row)
225 j = starts(col)
226 c = cell_index(col)
227 blocks(i:i + SIZE(block, 1) - 1, j:j + SIZE(block, 2) - 1, c) = &
228 blocks(i:i + SIZE(block, 1) - 1, j:j + SIZE(block, 2) - 1, c) + factor*block
229 END DO
230 CALL dbcsr_iterator_stop(iterator)
231 END SUBROUTINE extract
232 END SUBROUTINE torus_blocks
233
234! **************************************************************************************************
235!> \brief Expand one block row into the original DBCSR distribution without changing spin ordering.
236!> \param blocks Primitive spin matrices at each displacement
237!> \param template Original full-storage AO layout
238!> \param grid Torus dimensions
239!> \param starts Primitive AO starts
240!> \param cell_index Cell of each atomic block
241!> \param para_env Communicator
242!> \param matrix Newly allocated full torus matrix
243! **************************************************************************************************
244 SUBROUTINE expand_blocks(blocks, template, grid, starts, cell_index, para_env, matrix)
245 COMPLEX(dp), INTENT(IN) :: blocks(:, :, :)
246 TYPE(localizer_matrix_type), INTENT(IN) :: template
247 INTEGER, INTENT(IN) :: grid(3), starts(:), cell_index(:)
248 TYPE(mp_para_env_type), POINTER :: para_env
249 TYPE(localizer_matrix_type), INTENT(OUT) :: matrix
250
251 INTEGER :: row, col, i, j, c, d(3), t, u
252 INTEGER, POINTER :: sizes(:), rows(:), cols(:), pgrid(:, :)
253 TYPE(dbcsr_distribution_type) :: distribution
254
255 CALL dbcsr_get_info(template%real_part, distribution=distribution, row_blk_size=sizes)
256 CALL dbcsr_distribution_get(distribution, row_dist=rows, col_dist=cols, pgrid=pgrid)
257 CALL dbcsr_create(matrix%real_part, template=template%real_part)
258 CALL dbcsr_create(matrix%imaginary_part, template=template%imaginary_part)
259 DO row = 1, SIZE(starts)
260 t = cell_index(row) - 1
261 i = starts(row)
262 DO col = 1, SIZE(starts)
263 IF (pgrid(rows(row), cols(col)) /= para_env%mepos) cycle
264 u = cell_index(col) - 1
265 j = starts(col)
266 d = modulo([modulo(u, grid(1)) - modulo(t, grid(1)), &
267 modulo(u/grid(1), grid(2)) - modulo(t/grid(1), grid(2)), &
268 u/(grid(1)*grid(2)) - t/(grid(1)*grid(2))], grid)
269 c = 1 + d(1) + grid(1)*(d(2) + grid(2)*d(3))
270 CALL dbcsr_put_block(matrix%real_part, row, col, &
271 REAL(blocks(i:i + sizes(row) - 1, j:j + sizes(col) - 1, c), dp))
272 CALL dbcsr_put_block(matrix%imaginary_part, row, col, &
273 aimag(blocks(i:i + sizes(row) - 1, j:j + sizes(col) - 1, c)))
274 END DO
275 END DO
276 CALL dbcsr_finalize(matrix%real_part)
277 CALL dbcsr_finalize(matrix%imaginary_part)
278 END SUBROUTINE expand_blocks
279
280! **************************************************************************************************
281!> \brief Relative complex Frobenius difference; leaves the trial as the difference.
282!> \param trial Reconstructed matrix
283!> \param reference Original matrix
284!> \param error Relative difference
285! **************************************************************************************************
286 SUBROUTINE matrix_difference(trial, reference, error)
287 TYPE(localizer_matrix_type), INTENT(INOUT) :: trial, reference
288 REAL(dp), INTENT(OUT) :: error
289
290 REAL(dp) :: norm
291
292 norm = hypot(dbcsr_frobenius_norm(reference%real_part), dbcsr_frobenius_norm(reference%imaginary_part))
293 CALL dbcsr_add(trial%real_part, reference%real_part, 1.0_dp, -1.0_dp)
294 CALL dbcsr_add(trial%imaginary_part, reference%imaginary_part, 1.0_dp, -1.0_dp)
295 error = hypot(dbcsr_frobenius_norm(trial%real_part), dbcsr_frobenius_norm(trial%imaginary_part))/ &
296 max(norm, tiny(1.0_dp))
297 END SUBROUTINE matrix_difference
298
299! **************************************************************************************************
300!> \brief Full generalized Bloch eigensystem, with metric/gap and numerical residual diagnostics.
301!> \param h Hermitian primitive Bloch Hamiltonian
302!> \param s Hermitian positive metric
303!> \param energy Absolute query energy
304!> \param scale Flattened band energy
305!> \param tolerance Structure tolerance
306!> \param metric_tolerance Minimum local overlap eigenvalue ratio
307!> \param flat Covariant shifted sign operator
308!> \param gap Distance of the original spectrum from energy
309!> \param bounds Smallest/largest original metric eigenvalues
310!> \param threshold Roundoff gap threshold
311!> \param errors Metric orthonormality and relative eigenproblem residual
312!> \param status 0 success, -1 invalid data, -2 metric, -4 eigensolver
313! **************************************************************************************************
314 SUBROUTINE flatten_block(h, s, energy, scale, tolerance, metric_tolerance, flat, gap, bounds, threshold, errors, status)
315 COMPLEX(dp), INTENT(IN) :: h(:, :), s(:, :)
316 REAL(dp), INTENT(IN) :: energy, scale, tolerance, metric_tolerance
317 COMPLEX(dp), INTENT(OUT) :: flat(:, :)
318 REAL(dp), INTENT(OUT) :: gap, bounds(2), threshold, errors(2)
319 INTEGER, INTENT(OUT) :: status
320
321 COMPLEX(dp), ALLOCATABLE :: a(:, :), b(:, :), work(:), sc(:, :), residual(:, :)
322 REAL(dp), ALLOCATABLE :: eigenvalues(:), rwork(:)
323 REAL(dp) :: hnorm, snorm, herror, serror
324 INTEGER :: n, info, i
325#if defined(__HAS_IEEE_EXCEPTIONS)
326 LOGICAL :: halt(size(ieee_all))
327#endif
328
329 status = -1
330 n = SIZE(h, 1)
331 IF (.NOT. all(ieee_is_finite(real(h, dp))) .OR. .NOT. all(ieee_is_finite(aimag(h)))) RETURN
332 IF (.NOT. all(ieee_is_finite(real(s, dp))) .OR. .NOT. all(ieee_is_finite(aimag(s)))) RETURN
333 hnorm = max(maxval(abs(h)), tiny(1.0_dp))
334 snorm = max(maxval(abs(s)), tiny(1.0_dp))
335 herror = maxval(abs(h - conjg(transpose(h))))
336 serror = maxval(abs(s - conjg(transpose(s))))
337 IF (herror > tolerance*hnorm .OR. serror > tolerance*snorm) RETURN
338 ALLOCATE (a(n, n), b(n, n), work(max(1, 2*n)), eigenvalues(n), rwork(max(1, 3*n)))
339 a(:, :) = s
340#if defined(__HAS_IEEE_EXCEPTIONS)
341 CALL ieee_get_halting_mode(ieee_all, halt)
342 CALL ieee_set_halting_mode(ieee_all, .false.)
343#endif
344 CALL zheev('N', 'U', n, a, n, eigenvalues, work, SIZE(work), rwork, info)
345#if defined(__HAS_IEEE_EXCEPTIONS)
346 CALL ieee_set_halting_mode(ieee_all, halt)
347#endif
348 status = -4
349 IF (info /= 0 .OR. .NOT. all(ieee_is_finite(eigenvalues))) RETURN
350 bounds = [minval(eigenvalues), maxval(eigenvalues)]
351 status = -2
352 IF (bounds(2) <= 0.0_dp .OR. bounds(1) <= metric_tolerance*bounds(2)) RETURN
353 a(:, :) = h - energy*s
354 b(:, :) = s
355#if defined(__HAS_IEEE_EXCEPTIONS)
356 CALL ieee_set_halting_mode(ieee_all, .false.)
357#endif
358 CALL zhegv(1, 'V', 'U', n, a, n, b, n, eigenvalues, work, SIZE(work), rwork, info)
359#if defined(__HAS_IEEE_EXCEPTIONS)
360 CALL ieee_set_halting_mode(ieee_all, halt)
361#endif
362 status = -4
363 IF (info /= 0 .OR. .NOT. all(ieee_is_finite(eigenvalues))) RETURN
364 gap = minval(abs(eigenvalues))
365 threshold = real(n, dp)*(herror + (abs(energy) + maxval(abs(eigenvalues)))*serror)/bounds(1) + &
366 100.0_dp*epsilon(1.0_dp)*real(n, dp)* &
367 max(maxval(abs(eigenvalues)), maxval(abs(h - energy*s))/bounds(1))
368 ALLOCATE (sc(n, n), residual(n, n))
369 sc(:, :) = matmul(s, a)
370 residual(:, :) = matmul(conjg(transpose(a)), sc)
371 DO i = 1, n
372 residual(i, i) = residual(i, i) - 1.0_dp
373 END DO
374 errors(1) = sqrt(sum(abs(residual)**2))
375 residual(:, :) = matmul(h - energy*s, a) - sc*spread(eigenvalues, 1, n)
376 errors(2) = sqrt(sum(abs(residual)**2))/max(tiny(1.0_dp), &
377 sqrt(sum(abs(h - energy*s)**2))*sqrt(sum(abs(a)**2)))
378 flat = matmul(sc*spread(sign(scale, eigenvalues), 1, n), conjg(transpose(sc)))
379 status = 0
380 END SUBROUTINE flatten_block
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
logical function, public dbcsr_iterator_blocks_left(iterator)
...
subroutine, public dbcsr_iterator_stop(iterator)
...
subroutine, public dbcsr_get_info(matrix, nblkrows_total, nblkcols_total, nfullrows_total, nfullcols_total, nblkrows_local, nblkcols_local, nfullrows_local, nfullcols_local, my_prow, my_pcol, local_rows, local_cols, proc_row_dist, proc_col_dist, row_blk_size, col_blk_size, row_blk_offset, col_blk_offset, distribution, name, matrix_type, group)
...
subroutine, public dbcsr_iterator_next_block(iterator, row, column, block, block_number_argument_has_been_removed, row_size, col_size, row_offset, col_offset, transposed)
...
subroutine, public dbcsr_finalize(matrix)
...
subroutine, public dbcsr_iterator_start(iterator, matrix, shared, dynamic, dynamic_byrows)
...
subroutine, public dbcsr_put_block(matrix, row, col, block, summation)
...
subroutine, public dbcsr_add(matrix_a, matrix_b, alpha_scalar, beta_scalar)
...
subroutine, public dbcsr_distribution_get(dist, row_dist, col_dist, nrows, ncols, has_threads, group, mynode, numnodes, nprows, npcols, myprow, mypcol, pgrid, subgroups_defined, prow_group, pcol_group)
...
real(dp) function, public dbcsr_frobenius_norm(matrix)
Compute the frobenius norm of a dbcsr matrix.
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Batched lattice Fourier transforms on regular k-point grids.
subroutine, public k_grid_to_cell_fft(values_k, xkp, nkp_grid, index_to_cell, values_rs, used_fft, weights, allow_incomplete)
Transform reciprocal-grid data back to image-cell values.
Interface to the message passing library MPI.
Exact spectral flattening of a translation-invariant AO torus in primitive Bloch blocks.
subroutine, public localizer_flatten_bloch(h, s, grid, nspin, energy, scale, tolerance, metric_tolerance, gap_tolerance, para_env, flattened, gap, metric_ratio, residuals, status)
Flatten all Bloch bands, then return to the original distributed real-cell/spin AO basis.
Distributed block-sparse finite localizers, retaining the covariant AO metric.
subroutine, public localizer_dbcsr_release(matrix)
Release both real components of a complex sparse operator.
subroutine, public localizer_dbcsr_check(matrix, para_env, tolerance, valid, relative_error)
Check both components before a sparse solver discards the upper triangle.
stores all the informations relevant to an mpi environment