(git:cd2a8c4)
Loading...
Searching...
No Matches
bse_matvec.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 Matrix-free application of the BSE matrices A and B to trial vectors from RI slabs that
10!> are sliced along the RI index over all MPI ranks
11!> \par History
12!> 09.2026 created [Maximilian Graml]
13! **************************************************************************************************
15
16 USE bse_util, ONLY: ia_of_occ_virt,&
17 occ_of_ia,&
25 USE cp_fm_types, ONLY: cp_fm_create,&
33 USE kinds, ONLY: dp
36#include "./base/base_uses.f90"
37
38 IMPLICIT NONE
39
40 PRIVATE
41
42 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'bse_matvec'
43
44 ! share of the free memory per rank the iterative solver may use, for the pass width of
45 ! bse_matvec_apply and the default budget of the subspace ceiling: half, since the probe counts
46 ! reclaimable cache and is optimistic
47 REAL(kind=dp), PARAMETER, PUBLIC :: mem_fraction = 0.5_dp
48
51
52! **************************************************************************************************
53!> \brief RI slabs of one spin channel, every rank holding n_ri_loc whole RI slices
54!> \param alpha prefactor of the exchange term (2 singlet, 0 triplet)
55!> \param w_fac prefactor of the screened term (0 switches it off)
56!> \param eps_diff ε_a - ε_i at the compound index ia = (i-1)*virt + a of bse_util's ia_of_occ_virt
57!> \param B_ia B^P_ia at (a, i, P)
58!> \param B_bar_ia \bar{B}^P_ia at (a, i, P), only for do_abba
59!> \param B_bar_ij \bar{B}^P_ij at (j, i, P)
60!> \param B_ab B^P_ab at (b, a, P)
61!> \param row_count rows of a trial vector owned by each rank, in rank order
62!> \param row_displ first row of each rank minus one, so that row_count and row_displ describe
63!> the contiguous ia range per rank that allgatherv and sum_scatter need
64!> \param blacs_env npe x 1 process grid of the slabs and of all trial vectors
65!> \param block_cols trial vectors per pass in bse_matvec_apply, -1 to size it from free memory
66! **************************************************************************************************
68 INTEGER :: homo = 0, virt = 0, n_ov = 0, &
69 n_ri_loc = 0, block_cols = -1
70 REAL(kind=dp) :: alpha = 0.0_dp, w_fac = 0.0_dp
71 LOGICAL :: do_abba = .false.
72 INTEGER, ALLOCATABLE, DIMENSION(:) :: row_count, row_displ
73 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: eps_diff
74 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: b_ia, b_bar_ia, b_bar_ij, b_ab
75 TYPE(cp_blacs_env_type), POINTER :: blacs_env => null()
76 TYPE(mp_para_env_type), POINTER :: para_env => null()
77 END TYPE bse_matvec_env_type
78
79CONTAINS
80
81! **************************************************************************************************
82!> \brief Moves the RI slabs onto an npe x 1 process grid, such that every rank owns whole RI
83!> slices, and stores them as local 3-index arrays: the contraction over each P in
84!> bse_matvec_apply is then a local DGEMM and only the final sum over P crosses the ranks
85!> \param mv_env the environment created here
86!> \param fm_S_ia B^P_ia, N_RI x homo*virt
87!> \param fm_S_bar_ij \bar{B}^P_ij, the slab that enters W_ij,ab, N_RI x homo*homo
88!> \param fm_S_ab B^P_ab, N_RI x virt*virt
89!> \param fm_S_bar_ia \bar{B}^P_ia, the slab that enters W_ib,aj, N_RI x homo*virt
90!> \param eps_reduced quasiparticle energies of the homo+virt active levels
91!> \param homo occupied levels of the active window
92!> \param virt virtual levels of the active window
93!> \param alpha prefactor of the exchange term, 2 singlet, 0 triplet
94!> \param w_fac prefactor of the screened term, 0 switches it off
95!> \param do_abba slice \bar{B}^P_ia as well, for the application of B
96!> \param unit_nr output unit, positive on the writing rank only
97!> \param block_cols trial vectors per pass of bse_matvec_apply; absent or -1 sizes it from the free memory
98! **************************************************************************************************
99 SUBROUTINE bse_matvec_create(mv_env, fm_S_ia, fm_S_bar_ij, fm_S_ab, fm_S_bar_ia, eps_reduced, &
100 homo, virt, alpha, w_fac, do_abba, unit_nr, block_cols)
101
102 TYPE(bse_matvec_env_type), INTENT(OUT) :: mv_env
103 TYPE(cp_fm_type), INTENT(IN) :: fm_s_ia, fm_s_bar_ij, fm_s_ab, &
104 fm_s_bar_ia
105 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: eps_reduced
106 INTEGER, INTENT(IN) :: homo, virt
107 REAL(kind=dp), INTENT(IN) :: alpha, w_fac
108 LOGICAL, INTENT(IN) :: do_abba
109 INTEGER, INTENT(IN) :: unit_nr
110 INTEGER, INTENT(IN), OPTIONAL :: block_cols
111
112 CHARACTER(LEN=*), PARAMETER :: routinen = 'bse_matvec_create'
113
114 CHARACTER(LEN=12) :: n_idle_str, n_ri_str, npe_str
115 INTEGER :: a, handle, i, n_ri, n_ri_slab, &
116 n_row_block
117 REAL(kind=dp) :: mem_slabs_gb, n_slab_entries
118
119 CALL timeset(routinen, handle)
120
121 mv_env%homo = homo
122 mv_env%virt = virt
123 IF (PRESENT(block_cols)) mv_env%block_cols = block_cols
124 mv_env%n_ov = homo*virt
125 mv_env%alpha = alpha
126 mv_env%w_fac = w_fac
127 mv_env%do_abba = do_abba
128 mv_env%para_env => fm_s_ia%matrix_struct%para_env
129
130 CALL cp_blacs_env_create(mv_env%blacs_env, mv_env%para_env, &
131 grid_2d=[mv_env%para_env%num_pe, 1])
132
133 ! one contiguous ia range per rank, so that a trial vector is gathered with allgatherv and
134 ! its image scattered back with sum_scatter, rather than replicated by two allreduces
135 n_row_block = contiguous_row_block(mv_env%n_ov, mv_env%para_env%num_pe)
136 ALLOCATE (mv_env%row_count(mv_env%para_env%num_pe), mv_env%row_displ(mv_env%para_env%num_pe))
137 DO i = 1, mv_env%para_env%num_pe
138 mv_env%row_displ(i) = min(mv_env%n_ov, (i - 1)*n_row_block)
139 mv_env%row_count(i) = min(mv_env%n_ov, i*n_row_block) - mv_env%row_displ(i)
140 END DO
141
142 ! eps_diff_ia = ε_a - ε_i at the compound index ia = (i-1)*virt + a, the row index of every trial vector
143 ALLOCATE (mv_env%eps_diff(mv_env%n_ov))
144 DO i = 1, homo
145 DO a = 1, virt
146 mv_env%eps_diff(ia_of_occ_virt(i, a, virt)) = eps_reduced(homo + a) - eps_reduced(i)
147 END DO
148 END DO
149
150 ! every slab is sliced on the same grid, so every rank owns the same RI indices of each slab
151 CALL slice_slab(mv_env, fm_s_ia, virt, homo, mv_env%B_ia, mv_env%n_ri_loc)
152 IF (w_fac /= 0.0_dp) THEN
153 CALL slice_slab(mv_env, fm_s_bar_ij, homo, homo, mv_env%B_bar_ij, n_ri_slab)
154 cpassert(n_ri_slab == mv_env%n_ri_loc)
155 CALL slice_slab(mv_env, fm_s_ab, virt, virt, mv_env%B_ab, n_ri_slab)
156 cpassert(n_ri_slab == mv_env%n_ri_loc)
157 IF (do_abba) THEN
158 CALL slice_slab(mv_env, fm_s_bar_ia, virt, homo, mv_env%B_bar_ia, n_ri_slab)
159 cpassert(n_ri_slab == mv_env%n_ri_loc)
160 END IF
161 END IF
162
163 CALL cp_fm_get_info(fm_s_ia, nrow_global=n_ri)
164 IF (mv_env%para_env%num_pe > n_ri .AND. unit_nr > 0) THEN
165 WRITE (npe_str, '(I0)') mv_env%para_env%num_pe
166 WRITE (n_ri_str, '(I0)') n_ri
167 WRITE (n_idle_str, '(I0)') mv_env%para_env%num_pe - n_ri
168 CALL cp_warn(__location__, &
169 "BSE iterative solver: more MPI ranks ("//trim(npe_str)// &
170 ") than RI basis functions ("//trim(n_ri_str)//"); "// &
171 trim(n_idle_str)//" ranks hold no RI slice and idle in the kernel application.")
172 END IF
173
174 IF (unit_nr > 0) THEN
175 ! pair entries of the sliced slabs per RI index
176 n_slab_entries = real(homo*virt, dp)
177 IF (w_fac /= 0.0_dp) THEN
178 n_slab_entries = n_slab_entries + real(homo, dp)**2 + real(virt, dp)**2
179 IF (do_abba) n_slab_entries = n_slab_entries + real(homo*virt, dp)
180 END IF
181 mem_slabs_gb = n_slab_entries*real(mv_env%n_ri_loc, dp)*8.0e-9_dp
182 WRITE (unit_nr, '(T2,A4,T7,A,T71,I10)') 'BSE|', &
183 'Max. number of RI functions per MPI rank', mv_env%n_ri_loc
184 WRITE (unit_nr, '(T2,A4,T7,A,T67,F14.3)') 'BSE|', &
185 'Memory of the RI slabs per MPI rank (GB)', mem_slabs_gb
186 END IF
187
188 CALL timestop(handle)
189
190 END SUBROUTINE bse_matvec_create
191
192! **************************************************************************************************
193!> \brief Rows of a trial vector per rank, the smallest block that leaves no rank beyond the last
194!> \param n_ov number of transitions, the global row count
195!> \param npe ranks of the npe x 1 grid
196!> \return rows of every full block; the last rank takes what remains, possibly none
197! **************************************************************************************************
198 PURE FUNCTION contiguous_row_block(n_ov, npe) RESULT(n_row_block)
199
200 INTEGER, INTENT(IN) :: n_ov, npe
201 INTEGER :: n_row_block
202
203 n_row_block = (n_ov + npe - 1)/npe
204
205 END FUNCTION contiguous_row_block
206
207! **************************************************************************************************
208!> \brief Redistributes one N_RI x n_fast*n_slow slab to whole RI rows per rank and copies the owned
209!> rows into a contiguous (n_fast, n_slow, n_ri_loc) array, slab_loc(x, y, p) = slab(P_p, (y-1)*n_fast + x)
210!> for the p-th owned RI index P_p, so that every slice slab_loc(:, :, p) is a n_fast x n_slow DGEMM operand
211!> \param mv_env process grid and communicator of the slices
212!> \param fm_slab the slab on the grid of the caller, N_RI x n_fast*n_slow
213!> \param n_fast fast index of the pair index of the slab
214!> \param n_slow slow index of the pair index of the slab
215!> \param slab_loc the owned slices, allocated here
216!> \param n_ri_loc RI indices owned by this rank, the third extent of slab_loc
217! **************************************************************************************************
218 SUBROUTINE slice_slab(mv_env, fm_slab, n_fast, n_slow, slab_loc, n_ri_loc)
219
220 TYPE(bse_matvec_env_type), INTENT(IN) :: mv_env
221 TYPE(cp_fm_type), INTENT(IN) :: fm_slab
222 INTEGER, INTENT(IN) :: n_fast, n_slow
223 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :), &
224 INTENT(OUT) :: slab_loc
225 INTEGER, INTENT(OUT) :: n_ri_loc
226
227 CHARACTER(LEN=*), PARAMETER :: routinen = 'slice_slab'
228
229 INTEGER :: handle, n_ri, npair, p
230 TYPE(cp_fm_struct_type), POINTER :: fm_struct
231 TYPE(cp_fm_type) :: fm_sliced
232
233 CALL timeset(routinen, handle)
234
235 ! row count from the struct: the slabs may carry fewer rows than the full RI basis
236 CALL cp_fm_get_info(fm_slab, nrow_global=n_ri, ncol_global=npair)
237 cpassert(npair == n_fast*n_slow)
238
239 ! one-row blocks on the npe x 1 grid spread the RI rows cyclically over the ranks; the copy
240 ! below turns each strided local_data row into a contiguous (n_fast, n_slow) slice
241 NULLIFY (fm_struct)
242 CALL cp_fm_struct_create(fm_struct, para_env=mv_env%para_env, context=mv_env%blacs_env, &
243 nrow_global=n_ri, ncol_global=npair, nrow_block=1, &
244 force_block=.true.)
245 CALL cp_fm_create(fm_sliced, fm_struct, name="fm_slab_ri_sliced")
246 CALL cp_fm_struct_release(fm_struct)
247 CALL cp_fm_to_fm_submat_general(fm_slab, fm_sliced, n_ri, npair, 1, 1, 1, 1, &
248 fm_slab%matrix_struct%context)
249
250 CALL cp_fm_get_info(fm_sliced, nrow_local=n_ri_loc)
251 ALLOCATE (slab_loc(n_fast, n_slow, n_ri_loc))
252 DO p = 1, n_ri_loc
253 slab_loc(:, :, p) = reshape(fm_sliced%local_data(p, 1:npair), [n_fast, n_slow])
254 END DO
255 CALL cp_fm_release(fm_sliced)
256
257 CALL timestop(handle)
258
259 END SUBROUTINE slice_slab
260
261! **************************************************************************************************
262!> \brief Frees the sliced slabs, the transition energies and the process grid of mv_env
263!> \param mv_env the environment released
264! **************************************************************************************************
265 SUBROUTINE bse_matvec_release(mv_env)
266
267 TYPE(bse_matvec_env_type), INTENT(INOUT) :: mv_env
268
269 IF (ALLOCATED(mv_env%row_count)) DEALLOCATE (mv_env%row_count)
270 IF (ALLOCATED(mv_env%row_displ)) DEALLOCATE (mv_env%row_displ)
271 IF (ALLOCATED(mv_env%eps_diff)) DEALLOCATE (mv_env%eps_diff)
272 IF (ALLOCATED(mv_env%B_ia)) DEALLOCATE (mv_env%B_ia)
273 IF (ALLOCATED(mv_env%B_bar_ia)) DEALLOCATE (mv_env%B_bar_ia)
274 IF (ALLOCATED(mv_env%B_bar_ij)) DEALLOCATE (mv_env%B_bar_ij)
275 IF (ALLOCATED(mv_env%B_ab)) DEALLOCATE (mv_env%B_ab)
276 IF (ASSOCIATED(mv_env%blacs_env)) CALL cp_blacs_env_release(mv_env%blacs_env)
277 NULLIFY (mv_env%para_env)
278
279 END SUBROUTINE bse_matvec_release
280
281! **************************************************************************************************
282!> \brief Matrix structure of a block of trial vectors: rows ia distributed, all columns local
283!> \param mv_env process grid and communicator of the trial vectors
284!> \param ncol_global number of trial vectors
285!> \param fm_struct created here, released by the caller
286! **************************************************************************************************
287 SUBROUTINE bse_matvec_vector_struct(mv_env, ncol_global, fm_struct)
288
289 TYPE(bse_matvec_env_type), INTENT(IN) :: mv_env
290 INTEGER, INTENT(IN) :: ncol_global
291 TYPE(cp_fm_struct_type), POINTER :: fm_struct
292
293 NULLIFY (fm_struct)
294 ! the row block matches mv_env%row_count, which bse_matvec_apply's collectives rely on
295 CALL cp_fm_struct_create(fm_struct, para_env=mv_env%para_env, context=mv_env%blacs_env, &
296 nrow_global=mv_env%n_ov, ncol_global=ncol_global, &
297 nrow_block=contiguous_row_block(mv_env%n_ov, mv_env%para_env%num_pe), &
298 force_block=.true.)
299
300 END SUBROUTINE bse_matvec_vector_struct
301
302! **************************************************************************************************
303!> \brief Applies A (and B) to ncol trial vectors without forming an N_ov x N_ov object,
304!> (A Z)_ia = (ε_a-ε_i) Z_ia + α sum_P B^P_ia sum_jb B^P_jb Z_jb - w sum_Pjb \bar{B}^P_ij B^P_ab Z_jb
305!> (B Z)_ia = α sum_P B^P_ia sum_jb B^P_jb Z_jb - w sum_Pjb \bar{B}^P_ib B^P_ja Z_jb
306!> α: exchange prefactor (2 singlet, 0 triplet), w: prefactor of the screened term,
307!> \bar{B}: slab bound by the caller of bse_matvec_create (B itself for TDHF and ALPHA
308!> screening, sum_Q [1+Q(0)]^-1_PQ B^Q otherwise); the sums over P run over the local RI
309!> slices and are completed by a sum over the ranks
310!> \param mv_env slabs, prefactors and transition energies
311!> \param fm_Z trial vectors, columns first_col .. first_col+ncol-1 are read
312!> \param first_col first trial vector read, and first column written
313!> \param ncol number of trial vectors
314!> \param fm_AZ receives A Z in the same columns
315!> \param fm_BZ receives B Z in the same columns, or from first_col_BZ on
316!> \param first_col_BZ first column of fm_BZ written, first_col by default
317! **************************************************************************************************
318 SUBROUTINE bse_matvec_apply(mv_env, fm_Z, first_col, ncol, fm_AZ, fm_BZ, first_col_BZ)
319
320 TYPE(bse_matvec_env_type), INTENT(IN), TARGET :: mv_env
321 TYPE(cp_fm_type), INTENT(IN) :: fm_z
322 INTEGER, INTENT(IN) :: first_col, ncol
323 TYPE(cp_fm_type), INTENT(IN) :: fm_az
324 TYPE(cp_fm_type), INTENT(IN), OPTIONAL :: fm_bz
325 INTEGER, INTENT(IN), OPTIONAL :: first_col_bz
326
327 CHARACTER(LEN=*), PARAMETER :: routinen = 'bse_matvec_apply'
328
329 INTEGER :: col, col_shift_b, handle, handle2, ia, &
330 iloc, k, me, n_work_bufs, nb, nb_max, &
331 nrow_local, p
332 INTEGER, DIMENSION(:), POINTER :: row_indices
333 LOGICAL :: do_b
334 REAL(kind=dp) :: mem_avail_gb
335 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: r_loc
336 REAL(kind=dp), ALLOCATABLE, DIMENSION(:), TARGET :: babz_buf, ra_buf, rb_buf, z_buf
337 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: bjaz_ab, t_pk
338 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
339 POINTER :: b_ia_2d, babz_wide, ra, rb, z, z_wide
340 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :, :), &
341 POINTER :: babz_3d, ra_3d, rb_3d, z_3d
342
343 CALL timeset(routinen, handle)
344
345 do_b = PRESENT(fm_bz)
346 IF (do_b) THEN
347 ! B Z needs the \bar{B}^P_ia slab, which is sliced for do_abba only
348 cpassert(mv_env%do_abba)
349 END IF
350 col_shift_b = 0
351 IF (PRESENT(first_col_bz)) col_shift_b = first_col_bz - first_col
352
353 CALL cp_fm_get_info(fm_z, nrow_local=nrow_local, row_indices=row_indices)
354 ! the collectives below read the rows of one rank as one range, as bse_matvec_vector_struct
355 ! lays them out; a vector built elsewhere would scatter into the wrong rows
356 me = mv_env%para_env%mepos + 1
357 cpassert(nrow_local == mv_env%row_count(me))
358 cpassert(nrow_local == 0 .OR. row_indices(1) == mv_env%row_displ(me) + 1)
359 ALLOCATE (r_loc(nrow_local))
360
361 ! columns per pass from the memory of the replicated work arrays Z, B_ab Z, R^A (and R^B)
362 IF (do_b) THEN
363 n_work_bufs = 4
364 ELSE
365 n_work_bufs = 3
366 END IF
367 IF (mv_env%block_cols > 0) THEN
368 ! pinned by input: the replicated work arrays are then a known n_work_bufs*n_ov*nb doubles
369 nb_max = min(ncol, mv_env%block_cols)
370 ELSE
371 CALL mp_mem_avail_per_rank_gb(mv_env%para_env, mem_avail_gb)
372 nb_max = ncol
373 IF (mem_avail_gb > 0.0_dp) THEN
374 nb_max = int(min(real(ncol, dp), &
375 mem_fraction*mem_avail_gb*1.0e9_dp/(8.0_dp*real(n_work_bufs, dp)*real(mv_env%n_ov, dp))))
376 END IF
377 END IF
378 nb_max = max(nb_max, 1)
379
380 ALLOCATE (z_buf(mv_env%n_ov*nb_max), ra_buf(mv_env%n_ov*nb_max), babz_buf(mv_env%n_ov*nb_max))
381 IF (do_b) THEN
382 ALLOCATE (rb_buf(mv_env%n_ov*nb_max), bjaz_ab(mv_env%virt, mv_env%virt))
383 END IF
384 ALLOCATE (t_pk(mv_env%n_ri_loc, nb_max))
385 b_ia_2d(1:mv_env%n_ov, 1:mv_env%n_ri_loc) => mv_env%B_ia
386
387 DO col = first_col, first_col + ncol - 1, nb_max
388 nb = min(nb_max, first_col + ncol - col)
389 ! three views of one contiguous buffer, bounds-remapping pointer assignment (Fortran 2003):
390 ! Z_ia,k for the exchange DGEMM, Z_3d(a, i, k) per column, Z_wide(a, (i k)) for the wide product
391 z(1:mv_env%n_ov, 1:nb) => z_buf(1:mv_env%n_ov*nb)
392 z_3d(1:mv_env%virt, 1:mv_env%homo, 1:nb) => z_buf(1:mv_env%n_ov*nb)
393 z_wide(1:mv_env%virt, 1:mv_env%homo*nb) => z_buf(1:mv_env%n_ov*nb)
394 ra(1:mv_env%n_ov, 1:nb) => ra_buf(1:mv_env%n_ov*nb)
395 ra_3d(1:mv_env%virt, 1:mv_env%homo, 1:nb) => ra_buf(1:mv_env%n_ov*nb)
396 babz_3d(1:mv_env%virt, 1:mv_env%homo, 1:nb) => babz_buf(1:mv_env%n_ov*nb)
397 babz_wide(1:mv_env%virt, 1:mv_env%homo*nb) => babz_buf(1:mv_env%n_ov*nb)
398 IF (do_b) THEN
399 rb(1:mv_env%n_ov, 1:nb) => rb_buf(1:mv_env%n_ov*nb)
400 rb_3d(1:mv_env%virt, 1:mv_env%homo, 1:nb) => rb_buf(1:mv_env%n_ov*nb)
401 END IF
402
403 ! Z_ia,k on every rank: every rank owns a contiguous ia range, so the column arrives sorted
404 CALL timeset(routinen//"_gather", handle2)
405 DO k = 1, nb
406 CALL mv_env%para_env%allgatherv(fm_z%local_data(1:nrow_local, col + k - 1), z(:, k), &
407 mv_env%row_count, mv_env%row_displ)
408 END DO
409 CALL timestop(handle2)
410
411 ! exchange, identical in A Z and B Z: R_ia,k = α sum_P B^P_ia t_Pk, t_Pk = sum_jb B^P_jb Z_jb,k
412 CALL timeset(routinen//"_exchange", handle2)
413 ra(:, :) = 0.0_dp
414 IF (mv_env%alpha /= 0.0_dp .AND. mv_env%n_ri_loc > 0) THEN
415 CALL dgemm('T', 'N', mv_env%n_ri_loc, nb, mv_env%n_ov, 1.0_dp, b_ia_2d, mv_env%n_ov, z, mv_env%n_ov, &
416 0.0_dp, t_pk, mv_env%n_ri_loc)
417 CALL dgemm('N', 'N', mv_env%n_ov, nb, mv_env%n_ri_loc, mv_env%alpha, b_ia_2d, mv_env%n_ov, t_pk, mv_env%n_ri_loc, &
418 0.0_dp, ra, mv_env%n_ov)
419 END IF
420 IF (do_b) rb(:, :) = ra(:, :)
421 CALL timestop(handle2)
422
423 CALL timeset(routinen//"_W", handle2)
424 IF (mv_env%w_fac /= 0.0_dp) THEN
425 DO p = 1, mv_env%n_ri_loc
426 ! A: R_ia,k -= w sum_jb \bar{B}^P_ij B^P_ab Z_jb,k, BabZ_ib,k = sum_b B^P_ab Z_jb,k first
427 CALL dgemm('T', 'N', mv_env%virt, mv_env%homo*nb, mv_env%virt, 1.0_dp, mv_env%B_ab(:, :, p), mv_env%virt, &
428 z_wide, mv_env%virt, 0.0_dp, babz_wide, mv_env%virt)
429 DO k = 1, nb
430 CALL dgemm('N', 'N', mv_env%virt, mv_env%homo, mv_env%homo, -mv_env%w_fac, babz_3d(:, :, k), mv_env%virt, &
431 mv_env%B_bar_ij(:, :, p), mv_env%homo, 1.0_dp, ra_3d(:, :, k), mv_env%virt)
432 END DO
433 ! B: R_ia,k -= w sum_jb \bar{B}^P_ib B^P_ja Z_jb,k, BjaZ_ab = sum_j B^P_ja Z_jb,k first
434 IF (do_b) THEN
435 DO k = 1, nb
436 CALL dgemm('N', 'T', mv_env%virt, mv_env%virt, mv_env%homo, 1.0_dp, mv_env%B_ia(:, :, p), mv_env%virt, &
437 z_3d(:, :, k), mv_env%virt, 0.0_dp, bjaz_ab, mv_env%virt)
438 CALL dgemm('N', 'N', mv_env%virt, mv_env%homo, mv_env%virt, -mv_env%w_fac, bjaz_ab, mv_env%virt, &
439 mv_env%B_bar_ia(:, :, p), mv_env%virt, 1.0_dp, rb_3d(:, :, k), mv_env%virt)
440 END DO
441 END IF
442 END DO
443 END IF
444 CALL timestop(handle2)
445
446 ! the sum over the ranks lands on the owner of each row, never replicated
447 CALL timeset(routinen//"_reduce", handle2)
448 ! (A Z)_ia,k = (ε_a-ε_i) Z_ia,k + R^A_ia,k and (B Z)_ia,k = R^B_ia,k on the local rows
449 DO k = 1, nb
450 CALL mv_env%para_env%sum_scatter(ra(:, k:k), r_loc, mv_env%row_count)
451 DO iloc = 1, nrow_local
452 ia = row_indices(iloc)
453 fm_az%local_data(iloc, col + k - 1) = r_loc(iloc) + mv_env%eps_diff(ia)*z(ia, k)
454 END DO
455 IF (do_b) THEN
456 CALL mv_env%para_env%sum_scatter(rb(:, k:k), r_loc, mv_env%row_count)
457 fm_bz%local_data(1:nrow_local, col + col_shift_b + k - 1) = r_loc(1:nrow_local)
458 END IF
459 END DO
460 CALL timestop(handle2)
461 END DO
462
463 DEALLOCATE (z_buf, ra_buf, babz_buf, t_pk)
464 IF (do_b) THEN
465 DEALLOCATE (rb_buf, bjaz_ab)
466 END IF
467
468 CALL timestop(handle)
469
470 END SUBROUTINE bse_matvec_apply
471
472! **************************************************************************************************
473!> \brief Diagonal used by the Davidson correction, either ε_a-ε_i or the full diagonal
474!> A_ia,ia = ε_a-ε_i + α sum_P (B^P_ia)^2 - w sum_P \bar{B}^P_ii B^P_aa
475!> with α, w and \bar{B} as in bse_matvec_apply
476!> \param mv_env slabs, prefactors and transition energies
477!> \param precond_kind bse_precond_full_diag or bse_precond_qp_diff
478!> \param diag replicated, size N_ov
479! **************************************************************************************************
480 SUBROUTINE bse_matvec_diagonal(mv_env, precond_kind, diag)
481
482 TYPE(bse_matvec_env_type), INTENT(IN) :: mv_env
483 INTEGER, INTENT(IN) :: precond_kind
484 REAL(kind=dp), DIMENSION(:), INTENT(OUT) :: diag
485
486 CHARACTER(LEN=*), PARAMETER :: routinen = 'bse_matvec_diagonal'
487
488 INTEGER :: a, handle, i, ia, p
489
490 CALL timeset(routinen, handle)
491
492 diag(:) = 0.0_dp
493 IF (precond_kind == bse_precond_full_diag) THEN
494 DO p = 1, mv_env%n_ri_loc
495 DO i = 1, mv_env%homo
496 DO a = 1, mv_env%virt
497 ia = ia_of_occ_virt(i, a, mv_env%virt)
498 diag(ia) = diag(ia) + mv_env%alpha*mv_env%B_ia(a, i, p)**2
499 IF (mv_env%w_fac /= 0.0_dp) THEN
500 diag(ia) = diag(ia) - mv_env%w_fac*mv_env%B_bar_ij(i, i, p)*mv_env%B_ab(a, a, p)
501 END IF
502 END DO
503 END DO
504 END DO
505 CALL mv_env%para_env%sum(diag)
506 END IF
507 diag(:) = diag(:) + mv_env%eps_diff(:)
508
509 CALL timestop(handle)
510
511 END SUBROUTINE bse_matvec_diagonal
512
513! **************************************************************************************************
514!> \brief Exact A (and B) on a list of transitions, replicated on every rank,
515!> A_kl = δ_kl (ε_a-ε_i) + α sum_P B^P_ia B^P_jb - w sum_P \bar{B}^P_ij B^P_ab
516!> B_kl = α sum_P B^P_ia B^P_jb - w sum_P \bar{B}^P_ib B^P_ja
517!> with k = (i,a), l = (j,b) and α, w, \bar{B} as in bse_matvec_apply
518!> \param mv_env slabs, prefactors and transition energies
519!> \param ia_list global transition indices ia = (i-1)*virt + a of the block
520!> \param A_sub SIZE(ia_list) x SIZE(ia_list)
521!> \param B_sub same, only formed when present
522! **************************************************************************************************
523 SUBROUTINE bse_matvec_subblock(mv_env, ia_list, A_sub, B_sub)
524
525 TYPE(bse_matvec_env_type), INTENT(IN) :: mv_env
526 INTEGER, DIMENSION(:), INTENT(IN) :: ia_list
527 REAL(kind=dp), DIMENSION(:, :), INTENT(OUT) :: a_sub
528 REAL(kind=dp), DIMENSION(:, :), INTENT(OUT), &
529 OPTIONAL :: b_sub
530
531 CHARACTER(LEN=*), PARAMETER :: routinen = 'bse_matvec_subblock'
532
533 INTEGER :: handle, k, l, n_sub, p
534 INTEGER, ALLOCATABLE, DIMENSION(:) :: a_virt, i_occ
535 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: b_pk
536
537 CALL timeset(routinen, handle)
538
539 n_sub = SIZE(ia_list)
540 ALLOCATE (i_occ(n_sub), a_virt(n_sub), b_pk(max(mv_env%n_ri_loc, 1), n_sub))
541 DO k = 1, n_sub
542 i_occ(k) = occ_of_ia(ia_list(k), mv_env%virt)
543 a_virt(k) = virt_of_ia(ia_list(k), mv_env%virt)
544 END DO
545
546 ! exchange, identical in A and B: α sum_P B_Pk B_Pl with B_Pk = B^P_ia at ia = ia_list(k)
547 a_sub(:, :) = 0.0_dp
548 IF (mv_env%alpha /= 0.0_dp .AND. mv_env%n_ri_loc > 0) THEN
549 ! P outermost: the gathered entries of one slice lie within one contiguous (a, i) block
550 DO p = 1, mv_env%n_ri_loc
551 DO k = 1, n_sub
552 b_pk(p, k) = mv_env%B_ia(a_virt(k), i_occ(k), p)
553 END DO
554 END DO
555 CALL dgemm('T', 'N', n_sub, n_sub, mv_env%n_ri_loc, mv_env%alpha, b_pk, SIZE(b_pk, 1), b_pk, SIZE(b_pk, 1), &
556 0.0_dp, a_sub, n_sub)
557 END IF
558 IF (PRESENT(b_sub)) b_sub(:, :) = a_sub(:, :)
559
560 IF (mv_env%w_fac /= 0.0_dp) THEN
561 DO p = 1, mv_env%n_ri_loc
562 DO l = 1, n_sub
563 DO k = 1, n_sub
564 a_sub(k, l) = a_sub(k, l) - mv_env%w_fac*mv_env%B_bar_ij(i_occ(l), i_occ(k), p)*mv_env%B_ab(a_virt(l), a_virt(k), p)
565 END DO
566 END DO
567 IF (PRESENT(b_sub)) THEN
568 DO l = 1, n_sub
569 DO k = 1, n_sub
570 b_sub(k, l) = b_sub(k, l) - mv_env%w_fac*mv_env%B_ia(a_virt(k), i_occ(l), p)*mv_env%B_bar_ia(a_virt(l), i_occ(k), p)
571 END DO
572 END DO
573 END IF
574 END DO
575 END IF
576
577 CALL mv_env%para_env%sum(a_sub)
578 IF (PRESENT(b_sub)) CALL mv_env%para_env%sum(b_sub)
579 DO k = 1, n_sub
580 a_sub(k, k) = a_sub(k, k) + mv_env%eps_diff(ia_list(k))
581 END DO
582
583 DEALLOCATE (i_occ, a_virt, b_pk)
584
585 CALL timestop(handle)
586
587 END SUBROUTINE bse_matvec_subblock
588
589! **************************************************************************************************
590!> \brief Debug check of the matrix-free application against the explicit matrices A (and B),
591!> dev = max_ia,k |(A Z)_ia,k - sum_jb A_ia,jb Z_jb,k| over up to eight unit vectors Z_ia,k = δ_ia,k
592!> and one dense vector Z_ia = sin(ia), the same for B, and max_ia |d_ia - A_ia,ia| for the diagonal
593!> \param mv_env the environment under test
594!> \param fm_A_explicit A from create_A_and_B, N_ov x N_ov on the grid of the caller
595!> \param unit_nr output unit, positive on the writing rank only
596!> \param fm_B_explicit B, present for an ABBA run
597! **************************************************************************************************
598 SUBROUTINE bse_matvec_selfcheck(mv_env, fm_A_explicit, unit_nr, fm_B_explicit)
599
600 TYPE(bse_matvec_env_type), INTENT(IN) :: mv_env
601 TYPE(cp_fm_type), INTENT(IN) :: fm_a_explicit
602 INTEGER, INTENT(IN) :: unit_nr
603 TYPE(cp_fm_type), INTENT(IN), OPTIONAL :: fm_b_explicit
604
605 CHARACTER(LEN=*), PARAMETER :: routinen = 'bse_matvec_selfcheck'
606
607 INTEGER :: handle, ia, iloc, k, n_ov, n_unit, ncol, &
608 nrow_local
609 INTEGER, DIMENSION(:), POINTER :: row_indices
610 REAL(kind=dp) :: dev_a, dev_b, dev_diag
611 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: diag
612 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: mv_result, ref_matrix, ref_result, z
613 TYPE(cp_fm_struct_type), POINTER :: fm_struct
614 TYPE(cp_fm_type) :: fm_az, fm_bz, fm_z
615
616 CALL timeset(routinen, handle)
617
618 n_ov = mv_env%n_ov
619 ! eight unit vectors read eight columns of the explicit matrix exactly; the dense column covers the whole contraction
620 n_unit = min(n_ov, 8)
621 dev_b = 0.0_dp
622 ncol = n_unit + 1
623
624 ! trial vectors: e_k for k <= n_unit, then one dense column Z_ia = sin(ia)
625 ALLOCATE (z(n_ov, ncol))
626 z(:, :) = 0.0_dp
627 DO k = 1, n_unit
628 z(k, k) = 1.0_dp
629 END DO
630 DO ia = 1, n_ov
631 z(ia, ncol) = sin(real(ia, dp))
632 END DO
633
634 CALL bse_matvec_vector_struct(mv_env, ncol, fm_struct)
635 CALL cp_fm_create(fm_z, fm_struct, name="fm_Z_selfcheck")
636 CALL cp_fm_create(fm_az, fm_struct, name="fm_AZ_selfcheck")
637 CALL cp_fm_set_all(fm_az, 0.0_dp)
638 IF (PRESENT(fm_b_explicit)) THEN
639 CALL cp_fm_create(fm_bz, fm_struct, name="fm_BZ_selfcheck")
640 CALL cp_fm_set_all(fm_bz, 0.0_dp)
641 END IF
642 CALL cp_fm_struct_release(fm_struct)
643
644 CALL cp_fm_get_info(fm_z, nrow_local=nrow_local, row_indices=row_indices)
645 DO k = 1, ncol
646 DO iloc = 1, nrow_local
647 fm_z%local_data(iloc, k) = z(row_indices(iloc), k)
648 END DO
649 END DO
650
651 IF (PRESENT(fm_b_explicit)) THEN
652 CALL bse_matvec_apply(mv_env, fm_z, 1, ncol, fm_az, fm_bz)
653 ELSE
654 CALL bse_matvec_apply(mv_env, fm_z, 1, ncol, fm_az)
655 END IF
656
657 ALLOCATE (ref_matrix(n_ov, n_ov), mv_result(n_ov, ncol), ref_result(n_ov, ncol), diag(n_ov))
658
659 CALL cp_fm_get_submatrix(fm_a_explicit, ref_matrix)
660 CALL cp_fm_get_submatrix(fm_az, mv_result)
661 ref_result(:, :) = matmul(ref_matrix, z)
662 dev_a = maxval(abs(mv_result - ref_result))
664 dev_diag = 0.0_dp
665 DO ia = 1, n_ov
666 dev_diag = max(dev_diag, abs(diag(ia) - ref_matrix(ia, ia)))
667 END DO
668
669 IF (PRESENT(fm_b_explicit)) THEN
670 CALL cp_fm_get_submatrix(fm_b_explicit, ref_matrix)
671 CALL cp_fm_get_submatrix(fm_bz, mv_result)
672 ref_result(:, :) = matmul(ref_matrix, z)
673 dev_b = maxval(abs(mv_result - ref_result))
674 END IF
675
676 IF (unit_nr > 0) THEN
677 WRITE (unit_nr, '(T2,A10,T13,A,T59,ES22.6)') 'BSE|DEBUG|', &
678 'Max deviation matvec vs explicit A', dev_a
679 IF (PRESENT(fm_b_explicit)) THEN
680 WRITE (unit_nr, '(T2,A10,T13,A,T59,ES22.6)') 'BSE|DEBUG|', &
681 'Max deviation matvec vs explicit B', dev_b
682 END IF
683 WRITE (unit_nr, '(T2,A10,T13,A,T59,ES22.6)') 'BSE|DEBUG|', &
684 'Max deviation diagonal vs explicit A', dev_diag
685 END IF
686
687 DEALLOCATE (z, ref_matrix, mv_result, ref_result, diag)
688 CALL cp_fm_release(fm_z)
689 CALL cp_fm_release(fm_az)
690 IF (PRESENT(fm_b_explicit)) CALL cp_fm_release(fm_bz)
691
692 CALL timestop(handle)
693
694 END SUBROUTINE bse_matvec_selfcheck
695
696END MODULE bse_matvec
static void dgemm(const char transa, const char transb, const int m, const int n, const int k, const double alpha, const double *a, const int lda, const double *b, const int ldb, const double beta, double *c, const int ldc)
Convenient wrapper to hide Fortran nature of dgemm_, swapping a and b.
Matrix-free application of the BSE matrices A and B to trial vectors from RI slabs that are sliced al...
Definition bse_matvec.F:14
subroutine, public bse_matvec_release(mv_env)
Frees the sliced slabs, the transition energies and the process grid of mv_env.
Definition bse_matvec.F:266
subroutine, public bse_matvec_subblock(mv_env, ia_list, a_sub, b_sub)
Exact A (and B) on a list of transitions, replicated on every rank, A_kl = δ_kl (ε_a-ε_i) + α sum_P B...
Definition bse_matvec.F:524
subroutine, public bse_matvec_apply(mv_env, fm_z, first_col, ncol, fm_az, fm_bz, first_col_bz)
Applies A (and B) to ncol trial vectors without forming an N_ov x N_ov object, (A Z)_ia = (ε_a-ε_i) Z...
Definition bse_matvec.F:319
subroutine, public bse_matvec_diagonal(mv_env, precond_kind, diag)
Diagonal used by the Davidson correction, either ε_a-ε_i or the full diagonal A_ia,...
Definition bse_matvec.F:481
real(kind=dp), parameter, public mem_fraction
Definition bse_matvec.F:47
subroutine, public bse_matvec_selfcheck(mv_env, fm_a_explicit, unit_nr, fm_b_explicit)
Debug check of the matrix-free application against the explicit matrices A (and B),...
Definition bse_matvec.F:599
subroutine, public bse_matvec_create(mv_env, fm_s_ia, fm_s_bar_ij, fm_s_ab, fm_s_bar_ia, eps_reduced, homo, virt, alpha, w_fac, do_abba, unit_nr, block_cols)
Moves the RI slabs onto an npe x 1 process grid, such that every rank owns whole RI slices,...
Definition bse_matvec.F:101
subroutine, public bse_matvec_vector_struct(mv_env, ncol_global, fm_struct)
Matrix structure of a block of trial vectors: rows ia distributed, all columns local.
Definition bse_matvec.F:288
Auxiliary routines for GW + Bethe-Salpeter for computing electronic excitations.
Definition bse_util.F:13
pure integer function, public occ_of_ia(ia, virt)
Occupied level of the compound transition index ia = (i-1)*virt + a.
Definition bse_util.F:113
pure integer function, public ia_of_occ_virt(i_occ, a_virt, virt)
Compound transition index ia = (i-1)*virt + a of the pair (i, a): a is the fast index.
Definition bse_util.F:98
pure integer function, public virt_of_ia(ia, virt)
Virtual level of the compound transition index ia = (i-1)*virt + a.
Definition bse_util.F:128
methods related to the blacs parallel environment
subroutine, public cp_blacs_env_release(blacs_env)
releases the given blacs_env
subroutine, public cp_blacs_env_create(blacs_env, para_env, blacs_grid_layout, blacs_repeatable, row_major, grid_2d)
allocates and initializes a type that represent a blacs context
represent the structure of a full matrix
subroutine, public cp_fm_struct_create(fmstruct, para_env, context, nrow_global, ncol_global, nrow_block, ncol_block, descriptor, first_p_pos, local_leading_dimension, template_fmstruct, square_blocks, force_block)
allocates and initializes a full matrix structure
subroutine, public cp_fm_struct_release(fmstruct)
releases a full matrix structure
represent a full matrix distributed on many processors
Definition cp_fm_types.F:15
subroutine, public cp_fm_get_info(matrix, name, nrow_global, ncol_global, nrow_block, ncol_block, nrow_local, ncol_local, row_indices, col_indices, local_data, context, nrow_locals, ncol_locals, matrix_struct, para_env)
returns all kind of information about the full matrix
subroutine, public cp_fm_to_fm_submat_general(source, destination, nrows, ncols, s_firstrow, s_firstcol, d_firstrow, d_firstcol, global_context)
General copy of a submatrix of fm matrix to a submatrix of another fm matrix. The two matrices can ha...
subroutine, public cp_fm_set_all(matrix, alpha, beta)
set all elements of a matrix to the same value, and optionally the diagonal to a different one
subroutine, public cp_fm_create(matrix, matrix_struct, name, nrow, ncol, set_zero)
creates a new full matrix with the given structure
subroutine, public cp_fm_get_submatrix(fm, target_m, start_row, start_col, n_rows, n_cols, transpose)
gets a submatrix of a full matrix op(target_m)(1:n_rows,1:n_cols) =fm(start_row:start_row+n_rows,...
collects all constants needed in input so that they can be used without circular dependencies
integer, parameter, public bse_precond_full_diag
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Interface to the message passing library MPI.
subroutine, public mp_mem_avail_per_rank_gb(comm, mem_avail_gb)
Memory that is currently free on the node, per MPI rank of that node, in GB.
RI slabs of one spin channel, every rank holding n_ri_loc whole RI slices.
Definition bse_matvec.F:67
represent a blacs multidimensional parallel environment (for the mpi corrispective see cp_paratypes/m...
keeps the information about the structure of a full matrix
represent a full matrix
stores all the informations relevant to an mpi environment