(git:691081d)
Loading...
Searching...
No Matches
gw_auto_ri_generate_RI_basis.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 Automatic RI basis set optimization for molecular GW.
10!> \par History
11!> 09.2026 created [Jan Wilhelm]
12! **************************************************************************************************
26 coulomb_operator_type
28 USE cp_dbcsr_api, ONLY: &
31 dbcsr_type_symmetric
33 USE cp_files, ONLY: close_file,&
39 USE cp_fm_diag, ONLY: cp_fm_geeig
43 USE cp_fm_types, ONLY: cp_fm_create,&
57 USE ieee_arithmetic, ONLY: ieee_is_finite
63 USE kinds, ONLY: default_path_length,&
64 dp
65 USE libint_2c_3c, ONLY: libint_potential_type
68 USE machine, ONLY: m_walltime
69 USE mathlib, ONLY: diamat_all
72 ncoset
77 USE t_c_g0, ONLY: get_lmax_init,&
78 init_t_c_g0 => init
79 USE util, ONLY: sort
80#include "base/base_uses.f90"
81
82 IMPLICIT NONE
83 PRIVATE
84
85 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'gw_auto_ri_generate_RI_basis'
86
87 ! Numerical cutoffs for the reference M_PQ eigenspace and final M_pq pseudoinverse.
88 REAL(KIND=dp), PARAMETER :: reference_m_eigenval_threshold = 1.0e-10_dp
89 REAL(KIND=dp), PARAMETER :: optimized_m_eigenval_threshold = 1.0e-6_dp
90 ! Discard null target directions and reject non-positive-semidefinite M_PQ or B_PQ spectra.
91 REAL(KIND=dp), PARAMETER :: relative_target_eigenval_threshold = 1.0e-12_dp
92 REAL(KIND=dp), PARAMETER :: ab_absolute_target_eigenval_threshold = 1.0e-12_dp
93 REAL(KIND=dp), PARAMETER :: negative_eigenval_tolerance = 1.0e-10_dp
94
96
97 TYPE ab_block_list_type
98 INTEGER, ALLOCATABLE :: block(:)
99 INTEGER, ALLOCATABLE :: first_p_AB(:)
100 INTEGER, ALLOCATABLE :: first_p_atom(:)
101 INTEGER, ALLOCATABLE :: size_opt_RI(:)
102 END TYPE ab_block_list_type
103
104 TYPE optimized_atom_u_type
105 INTEGER, ALLOCATABLE :: ref_atom(:)
106 INTEGER, ALLOCATABLE :: first_P(:)
107 REAL(KIND=dp), ALLOCATABLE :: u_pp(:, :)
108 END TYPE optimized_atom_u_type
109
110CONTAINS
111
112!**************************************************************************************************!
113! The full derivation of the AUTO_RI algorithm will be published separately. !
114! !
115! The equations below define the notation used throughout this module. !
116! !
117! The RI scalar product uses the truncated Coulomb operator: !
118! !
119! (f|g) = ∫dr∫dr' f(r) V_rc(r-r') g(r'). (1) !
120! !
121! For the RI basis-set optimization, the orbital-product density formed by !
122! AOs on atoms A and B is !
123! !
124! ρ_mn^AB(r) = Σ_{μ∈A} Σ_{ν∈B} C_μm C_νn Φ_μ(r) Φ_ν(r). (2) !
125! !
126! The atom-local correlation target B_PQ^{A,C} mimics contributions to the density response χ, !
127! and the exchange target B_PQ^{A,X} mimics contributions to the exchange self-energy: !
128! !
129! B_PQ^{A,C} = (1/N_C^A) Σ_ia^oe w_ia (φ_P^A|ρ_ia^AA)(ρ_ia^AA|φ_Q^A). (3) !
130! !
131! B_PQ^{A,X} = (1/N_X^A) Σ_{n∈W_occ} Σ_i^occ (φ_P^A|ρ_ni^AA)(ρ_ni^AA|φ_Q^A). (4) !
132! !
133! W_occ = [ε_HOMO-E_win, ε_LUMO+E_win], where E_win is OCC_EMPTY_FRONTIER_ORBITAL_WINDOW. !
134! !
135! Here w_ia gives larger weight to frontier-orbital products close to the HOMO and LUMO: !
136! !
137! w_ia = 1/(|ε_i-E_F| |ε_a-E_F|), E_F = (ε_HOMO+ε_LUMO)/2. (5) !
138! !
139! Diagonalizing B^A = B^{A,C}+B^{A,X} identifies the most important atom-local RI functions !
140! that we use in the optimized RI basis set. The contraction coefficients U_Pp^A [see Eq. (7)] !
141! used for optimized RI basis functions are obtained from the eigenvalue problem !
142! !
143! B^A U^A = M^A U^A λ^A, Σ_Q B_PQ^A U_Qp^A = λ_p^A Σ_Q M_PQ^A U_Qp^A. (6) !
144! !
145! The eigenvectors with the largest eigenvalues are the contracted RI functions φ_p^A(r) !
146! used for the optimized RI basis set: !
147! !
148! φ_p^A(r) = Σ_P U_Pp^A φ_P^A(r). (7) !
149! !
150! We also use superpositions of Gaussians on neighboring atoms in the RI basis. Before optimizing !
151! the expansion coefficients, the AA from Eq. (7) used in AB contractions are projected out: !
152! !
153! span{φ_P^{AB,⊥}} = span{φ_P^A,φ_Q^B} \ span{φ_p^A,φ_q^B}. (8) !
154! !
155! For obtaining the AB contraction coefficients, we use the symmetric off-diagonal density: !
156! !
157! ρ̅_mn^AB(r) = ρ_mn^AB(r)+ρ_mn^BA(r). (9) !
158! !
159! To identify the most important functions for a neighboring pair A-B, define !
160! correlation and exchange targets in this projected basis: !
161! !
162! B_PQ^{AB,C} = (1/N_C^{AB}) Σ_ia^oe w_ia (φ_P^{AB,⊥}|ρ̅_ia^AB)(ρ̅_ia^AB|φ_Q^{AB,⊥}). (10) !
163! !
164! B_PQ^{AB,X} = (1/N_X^{AB}) Σ_{n∈W_occ} Σ_i^occ (φ_P^{AB,⊥}|ρ̅_ni^AB)(ρ̅_ni^AB|φ_Q^{AB,⊥}). (11) !
165! !
166! Diagonalizing their sum gives the AB-neighbor-pair contraction coefficients: !
167! !
168! B^AB U^AB = U^AB λ^AB, B^AB = B^{AB,C}+B^{AB,X}. (12) !
169! !
170! The eigenvectors with the largest eigenvalues are used as AB contractions in the optimized !
171! RI basis set: !
172! !
173! φ_p^AB(r) = Σ_P U_Pp^AB φ_P^{AB,⊥}(r). (13) !
174! !
175! Eq. (13) means that the RI basis is not an atom-centered basis any more, but can be delocalized !
176! over two neighboring atoms AB. !
177! !
178! AUTO_RI uses the same number of AA and AB contractions in the final optimized RI basis: !
179! !
180! Σ_A N_A = Σ_{A>B} N_AB = N_RI/2. (14) !
181! !
182!**************************************************************************************************!
183
184! **************************************************************************************************
185!> \brief Executes the AUTO_RI algorithm defined by Eqs. (1)-(14).
186!> \param qs_env ...
187!> \param bs_env ...
188! **************************************************************************************************
189 SUBROUTINE generate_auto_ri_basis(qs_env, bs_env)
190 TYPE(qs_environment_type), POINTER :: qs_env
191 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
192
193 CHARACTER(LEN=*), PARAMETER :: routinen = 'generate_auto_ri_basis'
194
195 INTEGER :: handle
196 REAL(kind=dp) :: time_start
197 REAL(kind=dp), ALLOCATABLE :: u_pp_a(:, :, :)
198
199 CALL timeset(routinen, handle)
200
201 time_start = m_walltime()
202
203 ! Compute AA target matrices B^{AA,C}, B^{AA,X} and contraction U_Pp^A in Eqs. (3)-(7).
204 CALL compute_aa_optimized_ri_basis(bs_env, u_pp_a)
205
206 ! Compute AB target matrices B^{AB,C}, B^{AB,X} and contraction U_Pp^AB in Eqs. (8)-(13).
207 CALL compute_ab_optimized_ri_basis(qs_env, bs_env, u_pp_a)
208 DEALLOCATE (u_pp_a)
209
210 ! Compute and invert M_pq = (φ_p|φ_q) of the optimized RI basis in Eqs. (7) and (13).
211 CALL compute_m_pq_inv_and_v_pq(qs_env, bs_env)
212
213 ! Print the optimized RI basis dimension and total optimization time.
214 CALL print_auto_ri_summary(bs_env, time_start)
215
216 CALL timestop(handle)
217
218 END SUBROUTINE generate_auto_ri_basis
219
220! **************************************************************************************************
221!> \brief Computes the atom-local contraction coefficients U^A from Eqs. (3)-(7):
222!>
223!> B_PQ^{A,C} = (1/N_C^A) Σ_ia^oe w_ia (φ_P^A|ρ_ia^AA)(ρ_ia^AA|φ_Q^A), (3)
224!>
225!> B_PQ^{A,X} = (1/N_X^A) Σ_{n∈W_occ} Σ_i^occ (φ_P^A|ρ_ni^AA)(ρ_ni^AA|φ_Q^A), (4)
226!>
227!> w_ia = 1/(|ε_i-E_F| |ε_a-E_F|), E_F = (ε_HOMO+ε_LUMO)/2, (5)
228!>
229!> B^A U^A = M^A U^A λ^A, (6)
230!>
231!> Contracted RI basis functions for optimized RI basis:
232!>
233!> φ_p^A(r) = Σ_P U_Pp^A φ_P^A(r). (7)
234!>
235!> \param bs_env ...
236!> \param U_Pp_A temporary atom-local contraction coefficients used to construct the AB space
237! **************************************************************************************************
238 SUBROUTINE compute_aa_optimized_ri_basis(bs_env, U_Pp_A)
239 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
240 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: u_pp_a(:, :, :)
241
242 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_AA_optimized_RI_basis'
243
244 INTEGER :: handle, iatom, ikind, max_nao_kind, max_nri_kind, nao_kind, natom, nkind, &
245 nri_kind, ri_size_opt_aa_requested, root_rank
246 INTEGER, ALLOCATABLE, DIMENSION(:) :: kind_of_atom, sizes_opt_ri_aa
247 REAL(kind=dp) :: target_norm
248 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: eigenval
249 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: b_pq, eigenval_aa, m_pq, u_pp_a_atom
250 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: m_pq_by_kind
251 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :, :) :: int_3c
252 TYPE(mp_para_env_type), POINTER :: para_env
253
254 CALL timeset(routinen, handle)
255
256 ! Determine reference dimensions and the number of AA RI functions (i.e. number of
257 ! eigenvectors U_Pp^A used for RI-basis contractions).
258 CALL initialize_aa_optimization(bs_env, para_env, kind_of_atom, natom, nkind, &
259 max_nri_kind, ri_size_opt_aa_requested)
260 ALLOCATE (eigenval_aa(max_nri_kind, natom), source=0.0_dp)
261 ALLOCATE (sizes_opt_ri_aa(natom))
262
263 CALL initialize_auto_ri_integrals(bs_env)
264 max_nao_kind = maxval(bs_env%sizes_AO)
265 ALLOCATE (u_pp_a(max_nri_kind, max_nri_kind, natom), &
266 m_pq_by_kind(max_nri_kind, max_nri_kind, nkind), source=0.0_dp)
267 ALLOCATE (int_3c(max_nao_kind, max_nao_kind, max_nri_kind, nkind), source=0.0_dp)
268
269 ! The few atom-kind integral blocks are inexpensive; compute them on every rank.
270 DO ikind = 1, nkind
271 nao_kind = bs_env%basis_set_AO(ikind)%gto_basis_set%nsgf
272 nri_kind = bs_env%basis_set_RI(ikind)%gto_basis_set%nsgf
273 CALL compute_int_3c( &
274 ao_basis=bs_env%basis_set_AO(ikind)%gto_basis_set, &
275 ri_basis=bs_env%basis_set_RI(ikind)%gto_basis_set, &
276 potential=bs_env%ri_metric, m_pq=m_pq, &
277 int_3c=int_3c(1:nao_kind, 1:nao_kind, 1:nri_kind, ikind))
278 CALL add_ri_metric_regularization(bs_env%basis_set_RI(ikind)%gto_basis_set, &
279 bs_env%regularization_RI, m_pq)
280 m_pq_by_kind(1:nri_kind, 1:nri_kind, ikind) = m_pq
281 DEALLOCATE (m_pq)
282 END DO
283
284 bs_env%auto_ri%sizes_opt_RI = 0
285 DO iatom = 1, natom
286 root_rank = modulo(iatom - 1, para_env%num_pe)
287 ikind = kind_of_atom(iatom)
288 nri_kind = bs_env%basis_set_RI(ikind)%gto_basis_set%nsgf
289 nao_kind = bs_env%sizes_AO(iatom)
290 ALLOCATE (m_pq(nri_kind, nri_kind))
291 m_pq(:, :) = m_pq_by_kind(1:nri_kind, 1:nri_kind, ikind)
292 ! Eqs. (3)-(5): form B^{A,C}, B^{A,X}, and their weighted sum B^A.
293 CALL compute_b_pq_aa(bs_env, iatom, root_rank, &
294 int_3c(1:nao_kind, 1:nao_kind, 1:nri_kind, ikind), &
295 m_pq, b_pq, target_norm)
296 DEALLOCATE (m_pq)
297 IF (root_rank /= para_env%mepos) THEN
298 DEALLOCATE (b_pq)
299 cycle
300 END IF
301 ! Eq. (6): solve B^A U^A = M^A U^A λ^A.
302 CALL solve_aa_eigenproblem(m_pq_by_kind(1:nri_kind, 1:nri_kind, ikind), &
303 b_pq, target_norm, u_pp_a_atom, eigenval)
304 eigenval_aa(1:SIZE(u_pp_a_atom, 2), iatom) = eigenval(1:SIZE(u_pp_a_atom, 2))
305 bs_env%auto_ri%sizes_opt_RI(iatom) = SIZE(u_pp_a_atom, 2)
306 u_pp_a(1:SIZE(u_pp_a_atom, 1), 1:SIZE(u_pp_a_atom, 2), iatom) = u_pp_a_atom
307 DEALLOCATE (u_pp_a_atom, eigenval, b_pq)
308 END DO
309 CALL para_env%sum(bs_env%auto_ri%sizes_opt_RI)
310 CALL para_env%sum(u_pp_a)
311 CALL para_env%sum(eigenval_aa)
312 CALL select_ri_sizes(eigenval_aa, ri_size_opt_aa_requested, 1, sizes_opt_ri_aa)
313 bs_env%auto_ri%sizes_opt_RI(:) = sizes_opt_ri_aa
314 DO iatom = 1, natom
315 u_pp_a(:, sizes_opt_ri_aa(iatom) + 1:, iatom) = 0.0_dp
316 END DO
317 DEALLOCATE (int_3c, m_pq_by_kind)
318
319 CALL timestop(handle)
320
321 END SUBROUTINE compute_aa_optimized_ri_basis
322
323! **************************************************************************************************
324!> \brief Initializes dimensions and atom-kind mappings for the AA optimization.
325!>
326!> This routine does not evaluate an equation of the objective. It maps every atom
327!> to its reference RI basis, allocates the persistent per-atom arrays, and determines
328!> the AA part of the requested basis size from
329!>
330!> Σ_A N_A = N_RI/2. (14)
331!>
332!> At least one AA contraction is assigned to every atom.
333!> \param bs_env ...
334!> \param para_env ...
335!> \param kind_of_atom ...
336!> \param natom ...
337!> \param nkind ...
338!> \param max_nRI_kind ...
339!> \param RI_size_opt_AA_requested ...
340! **************************************************************************************************
341 SUBROUTINE initialize_aa_optimization(bs_env, para_env, kind_of_atom, &
342 natom, nkind, max_nRI_kind, &
343 RI_size_opt_AA_requested)
344 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
345 TYPE(mp_para_env_type), POINTER :: para_env
346 INTEGER, ALLOCATABLE, INTENT(OUT) :: kind_of_atom(:)
347 INTEGER, INTENT(OUT) :: natom, nkind, max_nri_kind, &
348 ri_size_opt_aa_requested
349
350 CHARACTER(LEN=*), PARAMETER :: routinen = 'initialize_AA_optimization'
351
352 INTEGER :: handle, ikind, ri_size_requested
353 INTEGER, ALLOCATABLE :: sizes_ref_ri_by_kind(:)
354
355 CALL timeset(routinen, handle)
356
357 para_env => bs_env%para_env
358 natom = bs_env%n_atom
359 nkind = SIZE(bs_env%basis_set_RI)
360 cpassert(ASSOCIATED(bs_env%ri_rs%atomic_kind_set))
361 cpassert(ASSOCIATED(bs_env%ri_rs%particle_set))
362 cpassert(ASSOCIATED(para_env) .AND. natom > 0 .AND. nkind > 0)
363 cpassert(ALLOCATED(bs_env%basis_set_AO))
364 cpassert(ALLOCATED(bs_env%basis_set_RI))
365 cpassert(SIZE(bs_env%basis_set_AO) == nkind)
366 cpassert(SIZE(bs_env%basis_set_RI) == nkind)
367 cpassert(bs_env%do_gw_ri_rs)
368 cpassert(.NOT. bs_env%do_periodic)
369 cpassert(bs_env%small_cell_full_kp_or_large_cell_Gamma /= small_cell_full_kp)
370
371 CALL get_atomic_kind_set(atomic_kind_set=bs_env%ri_rs%atomic_kind_set, &
372 kind_of=kind_of_atom)
373 ALLOCATE (bs_env%auto_ri%sizes_opt_RI(natom))
374 ALLOCATE (sizes_ref_ri_by_kind(nkind))
375 DO ikind = 1, nkind
376 cpassert(ASSOCIATED(bs_env%basis_set_AO(ikind)%gto_basis_set))
377 cpassert(ASSOCIATED(bs_env%basis_set_RI(ikind)%gto_basis_set))
378 sizes_ref_ri_by_kind(ikind) = bs_env%basis_set_RI(ikind)%gto_basis_set%nsgf
379 END DO
380 max_nri_kind = maxval(sizes_ref_ri_by_kind)
381 ri_size_requested = nint( &
382 min(bs_env%auto_ri%ri_ao_ratio, &
383 REAL(sum(sizes_ref_ri_by_kind(kind_of_atom)), dp)/ &
384 REAL(SUM(bs_env%sizes_AO), dp))*REAL(SUM(bs_env%sizes_AO), dp))
385 ri_size_opt_aa_requested = max(natom, nint(0.5_dp*real(ri_size_requested, dp)))
386 DEALLOCATE (sizes_ref_ri_by_kind)
387
388 CALL timestop(handle)
389
390 END SUBROUTINE initialize_aa_optimization
391
392! **************************************************************************************************
393!> \brief Sets the RI-RS block sizes to the optimized RI basis dimensions.
394!> \param bs_env ...
395! **************************************************************************************************
396 SUBROUTINE set_optimized_ri_basis_sizes(bs_env)
397 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
398
399 CHARACTER(LEN=*), PARAMETER :: routinen = 'set_optimized_RI_basis_sizes'
400
401 INTEGER :: handle
402
403 CALL timeset(routinen, handle)
404
405 bs_env%sizes_RI(:) = bs_env%auto_ri%sizes_opt_RI
406 bs_env%auto_ri%ready = .true.
407
408 CALL timestop(handle)
409
410 END SUBROUTINE set_optimized_ri_basis_sizes
411
412! **************************************************************************************************
413!> \brief Prints the optimized RI basis size and total optimization time.
414!> \param bs_env ...
415!> \param time_start ...
416! **************************************************************************************************
417 SUBROUTINE print_auto_ri_summary(bs_env, time_start)
418 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
419 REAL(kind=dp), INTENT(IN) :: time_start
420
421 CHARACTER(LEN=*), PARAMETER :: routinen = 'print_auto_ri_summary'
422
423 INTEGER :: handle, ri_size_optimized, unit_nr
424 REAL(kind=dp) :: elapsed
425
426 CALL timeset(routinen, handle)
427
428 ri_size_optimized = sum(bs_env%auto_ri%sizes_opt_RI)
429 elapsed = m_walltime() - time_start
430 CALL bs_env%para_env%max(elapsed)
431 unit_nr = bs_env%unit_nr
432 IF (unit_nr > 0) THEN
433 WRITE (unit_nr, '(A)') ' '
434 WRITE (unit_nr, '(T2,A,T74,I9)') &
435 'AUTO_RI| Number of automatic RI functions for χ, ε, W:', ri_size_optimized
436 WRITE (unit_nr, '(T2,A,T71,F8.1,A)') &
437 'AUTO_RI| Total RI basis optimization time:', elapsed, ' s'
438 END IF
439
440 CALL timestop(handle)
441
442 END SUBROUTINE print_auto_ri_summary
443
444! **************************************************************************************************
445!> \brief Adds REGULARIZATION_RI to a two-center RI metric matrix (P|Q).
446!> \param basis ...
447!> \param regularization ...
448!> \param M_PQ reference RI metric M_PQ
449!> \param offset ...
450! **************************************************************************************************
451 SUBROUTINE add_ri_metric_regularization(basis, regularization, M_PQ, offset)
453 TYPE(gto_basis_set_type), INTENT(IN) :: basis
454 REAL(kind=dp), INTENT(IN) :: regularization
455 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: m_pq
456 INTEGER, INTENT(IN), OPTIONAL :: offset
457
458 CHARACTER(LEN=*), PARAMETER :: routinen = 'add_RI_metric_regularization'
459
460 INTEGER :: first, handle, i, iset, nset, start
461 INTEGER, DIMENSION(:), POINTER :: npgf, nsgf_set
462 INTEGER, DIMENSION(:, :), POINTER :: first_sgf
463 REAL(kind=dp) :: shift
464 REAL(kind=dp), DIMENSION(:, :), POINTER :: zet
465
466 CALL timeset(routinen, handle)
467
468 start = 0
469 IF (PRESENT(offset)) start = offset
470 CALL get_gto_basis_set(basis, first_sgf=first_sgf, npgf=npgf, nset=nset, &
471 nsgf_set=nsgf_set, zet=zet)
472 cpassert(start >= 0 .AND. start + basis%nsgf <= SIZE(m_pq, 1))
473 cpassert(start + basis%nsgf <= SIZE(m_pq, 2))
474 DO iset = 1, nset
475 shift = regularization*max(1.0_dp, 1.0_dp/minval(zet(1:npgf(iset), iset)))
476 first = start + first_sgf(1, iset)
477 DO i = first, first + nsgf_set(iset) - 1
478 m_pq(i, i) = m_pq(i, i) + shift
479 END DO
480 END DO
481
482 CALL timestop(handle)
483
484 END SUBROUTINE add_ri_metric_regularization
485
486! **************************************************************************************************
487!> \brief Copies atom-local diagonal blocks M^AA from a distributed metric M.
488!> \param M_pq_local locally stored entries of the optimized RI metric M_pq
489!> \param M_pq_AA_local locally stored entries of the atom-block-diagonal metric
490!> \param row_indices global indices of the locally owned rows
491!> \param col_indices global indices of the locally owned columns
492!> \param sizes_RI optimized RI functions in each atomic matrix block
493! **************************************************************************************************
494 SUBROUTINE copy_m_pq_aa_blocks(M_pq_local, M_pq_AA_local, row_indices, &
495 col_indices, sizes_RI)
496 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: m_pq_local
497 REAL(kind=dp), DIMENSION(:, :), INTENT(OUT) :: m_pq_aa_local
498 INTEGER, DIMENSION(:), INTENT(IN) :: row_indices, col_indices, sizes_ri
499
500 CHARACTER(LEN=*), PARAMETER :: routinen = 'copy_M_pq_AA_blocks'
501
502 INTEGER :: handle, i, j, last_index
503 INTEGER, ALLOCATABLE, DIMENSION(:) :: block_ends, col_atom, row_atom
504
505 CALL timeset(routinen, handle)
506
507 cpassert(SIZE(m_pq_local, 1) == SIZE(row_indices))
508 cpassert(SIZE(m_pq_local, 2) == SIZE(col_indices))
509 cpassert(all(shape(m_pq_aa_local) == shape(m_pq_local)))
510 cpassert(SIZE(sizes_ri) > 0 .AND. all(sizes_ri > 0))
511
512 ALLOCATE (block_ends(SIZE(sizes_ri)))
513 block_ends(1) = sizes_ri(1)
514 DO i = 2, SIZE(sizes_ri)
515 block_ends(i) = block_ends(i - 1) + sizes_ri(i)
516 END DO
517 last_index = block_ends(SIZE(block_ends))
518 cpassert(all(row_indices >= 1 .AND. row_indices <= last_index))
519 cpassert(all(col_indices >= 1 .AND. col_indices <= last_index))
520
521 ALLOCATE (row_atom(SIZE(row_indices)), col_atom(SIZE(col_indices)))
522 DO i = 1, SIZE(row_indices)
523 row_atom(i) = m_pq_index_atom(row_indices(i), block_ends)
524 END DO
525 DO j = 1, SIZE(col_indices)
526 col_atom(j) = m_pq_index_atom(col_indices(j), block_ends)
527 END DO
528
529 m_pq_aa_local = 0.0_dp
530 DO j = 1, SIZE(col_indices)
531 DO i = 1, SIZE(row_indices)
532 IF (row_atom(i) == col_atom(j)) THEN
533 m_pq_aa_local(i, j) = m_pq_local(i, j)
534 END IF
535 END DO
536 END DO
537
538 CALL timestop(handle)
539
540 END SUBROUTINE copy_m_pq_aa_blocks
541
542! **************************************************************************************************
543!> \brief Forms f_i=1/sqrt(λ_i) for eigenvalues of the RI metric matrix M_PQ.
544!> \param eigenval generalized metric eigenvalues
545!> \param factors inverse-square-root weights, or zero for discarded modes
546! **************************************************************************************************
547 SUBROUTINE compute_optimized_m_pq_inv_factors(eigenval, factors)
548 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: eigenval
549 REAL(kind=dp), ALLOCATABLE, DIMENSION(:), &
550 INTENT(OUT) :: factors
551
552 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_optimized_M_pq_inv_factors'
553
554 INTEGER :: handle, i, n_dependent
555 REAL(kind=dp) :: largest, threshold
556
557 CALL timeset(routinen, handle)
558
559 cpassert(SIZE(eigenval) > 0)
560
561 largest = maxval(eigenval)
562 cpassert(largest > 0.0_dp)
563 threshold = optimized_m_eigenval_threshold*largest
564 ALLOCATE (factors(SIZE(eigenval)))
565 factors = 0.0_dp
566 n_dependent = 0
567 DO i = 1, SIZE(eigenval)
568 IF (eigenval(i) > 0.0_dp .AND. eigenval(i) >= threshold) THEN
569 factors(i) = 1.0_dp/sqrt(eigenval(i))
570 ELSE
571 n_dependent = n_dependent + 1
572 END IF
573 END DO
574 cpassert(n_dependent < SIZE(eigenval))
575
576 CALL timestop(handle)
577
578 END SUBROUTINE compute_optimized_m_pq_inv_factors
579
580! **************************************************************************************************
581!> \brief Finds the atom owning a global RI metric index.
582!> \param global_index global metric index
583!> \param block_ends inclusive end index of each consecutive atom block
584!> \return atom index
585! **************************************************************************************************
586 PURE FUNCTION m_pq_index_atom(global_index, block_ends) RESULT(atom_index)
587 INTEGER, INTENT(IN) :: global_index
588 INTEGER, DIMENSION(:), INTENT(IN) :: block_ends
589 INTEGER :: atom_index
590
591 INTEGER :: high, low, middle
592
593 low = 1
594 high = SIZE(block_ends)
595 DO WHILE (low < high)
596 middle = (low + high)/2
597 IF (global_index <= block_ends(middle)) THEN
598 high = middle
599 ELSE
600 low = middle + 1
601 END IF
602 END DO
603 atom_index = low
604
605 END FUNCTION m_pq_index_atom
606
607! **************************************************************************************************
608!> \brief Computes X=U diag(1/sqrt(λ)) with X^T M X=I.
609!>
610!> The columns of X span the non-null eigenspace of the symmetric
611!> two-center RI metric matrix M=U diag(λ) U^T.
612!> \param M_PQ ...
613!> \param X_Pi ...
614!> \param rank ...
615! **************************************************************************************************
616 SUBROUTINE compute_reference_m_pq_inv_factor(M_PQ, X_Pi, rank)
617 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: m_pq
618 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
619 INTENT(OUT) :: x_pi
620 INTEGER, INTENT(OUT) :: rank
621
622 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_reference_M_PQ_inv_factor'
623
624 INTEGER :: column, handle, i, n
625 REAL(kind=dp) :: largest
626 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: eigenval
627 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: eigenvec
628
629 CALL timeset(routinen, handle)
630
631 n = SIZE(m_pq, 1)
632 cpassert(n > 0 .AND. SIZE(m_pq, 2) == n)
633 ALLOCATE (eigenvec, source=0.5_dp*(m_pq + transpose(m_pq)))
634 ALLOCATE (eigenval(n))
635 CALL diamat_all(eigenvec, eigenval, dac=.true.)
636 largest = maxval(eigenval)
637 IF (largest <= 0.0_dp) cpabort("AUTO_RI metric has no positive non-null range")
638 IF (minval(eigenval) < -negative_eigenval_tolerance*largest) THEN
639 cpabort("AUTO_RI metric has no positive non-null range")
640 END IF
641 rank = count(eigenval > reference_m_eigenval_threshold*largest)
642 IF (rank == 0) cpabort("AUTO_RI metric has no positive non-null range")
643 ALLOCATE (x_pi(n, rank))
644 column = 0
645 DO i = 1, n
646 IF (eigenval(i) <= reference_m_eigenval_threshold*largest) cycle
647 column = column + 1
648 x_pi(:, column) = eigenvec(:, i)/sqrt(eigenval(i))
649 END DO
650
651 CALL timestop(handle)
652
653 END SUBROUTINE compute_reference_m_pq_inv_factor
654
655! **************************************************************************************************
656!> \brief Solves eigenvalue problem B^A U^A=M^A U^A λ^A in Eq. (6).
657!>
658!> Columns are ordered from the largest to the smallest λ^A.
659!> Columns are retained when λ_i/λ_max >= eigenval_threshold.
660!> \param M_PQ metric of the large reference RI basis
661!> \param B_PQ normalized atom-local target matrix B^A
662!> \param target_normalization ...
663!> \param U_Pp_A selected AA contractions from the large RI basis P to optimized functions p
664!> \param eigenval normalized target eigenvalues in descending order
665! **************************************************************************************************
666 SUBROUTINE solve_aa_eigenproblem(M_PQ, B_PQ, target_normalization, U_Pp_A, eigenval)
667 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: m_pq, b_pq
668 REAL(kind=dp), INTENT(IN) :: target_normalization
669 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
670 INTENT(OUT) :: u_pp_a
671 REAL(kind=dp), ALLOCATABLE, DIMENSION(:), &
672 INTENT(OUT) :: eigenval
673
674 CHARACTER(LEN=*), PARAMETER :: routinen = 'solve_AA_eigenproblem'
675
676 INTEGER :: handle, i, j, m_pq_rank, n, nret
677 REAL(kind=dp) :: largest_score, relative_fitting_defect
678 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: eigenval_b, eigenval_m
679 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: b_ij, m_pp_check, u_ij, u_pp_a_rotated, &
680 x_pi
681
682 CALL timeset(routinen, handle)
683
684 n = SIZE(m_pq, 1)
685 cpassert(n > 0)
686 cpassert(SIZE(m_pq, 2) == n)
687 cpassert(all(shape(b_pq) == [n, n]))
688 cpassert(target_normalization > 0.0_dp)
689 CALL compute_reference_m_pq_inv_factor(m_pq, x_pi, m_pq_rank)
690
691 ALLOCATE (b_ij(m_pq_rank, m_pq_rank))
692 b_ij(:, :) = matmul(transpose(x_pi), &
693 matmul(0.5_dp*(b_pq + transpose(b_pq)), x_pi))/target_normalization
694 ALLOCATE (u_ij, source=b_ij)
695 ALLOCATE (eigenval_b(m_pq_rank))
696 CALL diamat_all(u_ij, eigenval_b, dac=.true.)
697 largest_score = maxval(eigenval_b)
698 IF (largest_score <= 0.0_dp) THEN
699 cpabort("AUTO_RI conditioned atom-local eigensolver failed")
700 END IF
701 IF (minval(eigenval_b) < -negative_eigenval_tolerance*largest_score) THEN
702 cpabort("AUTO_RI conditioned atom-local eigensolver failed")
703 END IF
704 nret = count(eigenval_b/largest_score >= relative_target_eigenval_threshold)
705 ALLOCATE (u_pp_a(n, nret), eigenval(m_pq_rank))
706 DO i = 1, m_pq_rank
707 eigenval(i) = eigenval_b(m_pq_rank - i + 1)
708 END DO
709 DO i = 1, nret
710 j = m_pq_rank - i + 1
711 u_pp_a(:, i) = matmul(x_pi, u_ij(:, j))
712 END DO
713
714 relative_fitting_defect = 1.0_dp
715 DO i = 1, nret
716 j = m_pq_rank - i + 1
717 relative_fitting_defect = relative_fitting_defect - &
718 dot_product(u_ij(:, j), matmul(b_ij, u_ij(:, j)))
719 END DO
720 IF (abs(relative_fitting_defect) < 100.0_dp*epsilon(1.0_dp)) THEN
721 relative_fitting_defect = 0.0_dp
722 END IF
723 ! A projection cannot capture more than the exact product norm.
724 IF (relative_fitting_defect < -1.0e-8_dp .OR. &
725 relative_fitting_defect > 1.0_dp + 1.0e-8_dp) THEN
726 cpabort("AUTO_RI conditioned atom-local eigensolver failed")
727 END IF
728 IF (nret > 0) THEN
729 ALLOCATE (m_pp_check(nret, nret), eigenval_m(nret), u_pp_a_rotated(n, nret))
730 m_pp_check(:, :) = matmul(transpose(u_pp_a), &
731 matmul(0.5_dp*(m_pq + transpose(m_pq)), u_pp_a))
732 CALL diamat_all(m_pp_check, eigenval_m, dac=.true.)
733 IF (minval(eigenval_m) <= reference_m_eigenval_threshold) THEN
734 cpabort("AUTO_RI conditioned atom-local eigensolver failed")
735 END IF
736 u_pp_a_rotated(:, :) = matmul(u_pp_a, m_pp_check)
737 DO i = 1, nret
738 u_pp_a_rotated(:, i) = u_pp_a_rotated(:, i)/sqrt(eigenval_m(i))
739 END DO
740 u_pp_a(:, :) = matmul(u_pp_a_rotated, transpose(m_pp_check))
741 m_pp_check(:, :) = matmul(transpose(u_pp_a), &
742 matmul(0.5_dp*(m_pq + transpose(m_pq)), u_pp_a))
743 DO i = 1, nret
744 m_pp_check(i, i) = m_pp_check(i, i) - 1.0_dp
745 END DO
746 IF (maxval(abs(m_pp_check)) >= 1.0e-7_dp) THEN
747 cpabort("AUTO_RI conditioned atom-local eigensolver failed")
748 END IF
749 END IF
750
751 CALL timestop(handle)
752
753 END SUBROUTINE solve_aa_eigenproblem
754
755! **************************************************************************************************
756!> \brief Solves eigenvalue problem B^AB U^AB=U^AB λ^AB of Eq. (8).
757!>
758!> Columns are ordered from the largest to the smallest λ^AB.
759!> \param M_PQ combined AB reference-basis metric
760!> \param B_PQ normalized two-center target matrix B^AB
761!> \param U_Pp_AA retained AA columns embedded in the combined AB reference space
762!> \param U_Pp_AB selected AB contractions from the large RI basis P to optimized functions p
763!> \param eigenval normalized scores in descending order
764! **************************************************************************************************
765 SUBROUTINE solve_ab_eigenproblem(M_PQ, B_PQ, U_Pp_AA, U_Pp_AB, eigenval)
766 REAL(kind=dp), INTENT(IN) :: m_pq(:, :), b_pq(:, :), u_pp_aa(:, :)
767 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: u_pp_ab(:, :), eigenval(:)
768
769 CHARACTER(LEN=*), PARAMETER :: routinen = 'solve_AB_eigenproblem'
770
771 INTEGER :: handle, i, k, n, nkeep, rank
772 REAL(kind=dp) :: threshold
773 REAL(kind=dp), ALLOCATABLE :: b_ij(:, :), b_ij_residual(:, :), eigenval_residual(:), &
774 m_aa(:, :), p_ij(:, :), q_ia(:, :), u_aa(:, :), u_ia(:, :), x_pi(:, :)
775
776 CALL timeset(routinen, handle)
777
778 n = SIZE(m_pq, 1)
779 CALL compute_reference_m_pq_inv_factor(m_pq, x_pi, rank)
780
781 ! Xᵀ M X=I; Q spans the retained atomic columns in these orthonormal coordinates.
782 ALLOCATE (q_ia(rank, SIZE(u_pp_aa, 2)))
783 q_ia(:, :) = matmul(transpose(x_pi), matmul(m_pq, u_pp_aa))
784 ALLOCATE (p_ij(rank, rank), source=0.0_dp)
785 DO i = 1, rank
786 p_ij(i, i) = 1.0_dp
787 END DO
788 IF (SIZE(u_pp_aa, 2) > 0) THEN
789 ALLOCATE (m_aa(SIZE(u_pp_aa, 2), SIZE(u_pp_aa, 2)))
790 m_aa(:, :) = matmul(transpose(q_ia), q_ia)
791 CALL compute_reference_m_pq_inv_factor(m_aa, u_aa, k)
792 ALLOCATE (u_ia(rank, SIZE(u_aa, 2)))
793 u_ia(:, :) = matmul(q_ia, u_aa)
794 p_ij(:, :) = p_ij - matmul(u_ia, transpose(u_ia))
795 DEALLOCATE (u_ia)
796 END IF
797 ALLOCATE (b_ij(rank, rank))
798 b_ij(:, :) = matmul(transpose(x_pi), matmul(b_pq, x_pi))
799 ALLOCATE (b_ij_residual(rank, rank))
800 b_ij_residual(:, :) = matmul(p_ij, matmul(b_ij, p_ij))
801 ALLOCATE (eigenval_residual(rank))
802 CALL diamat_all(b_ij_residual, eigenval_residual, dac=.true.)
803 threshold = max(ab_absolute_target_eigenval_threshold, &
804 relative_target_eigenval_threshold*maxval(eigenval_residual), &
805 100.0_dp*epsilon(1.0_dp)*maxval(abs(b_ij)))
806 nkeep = count(eigenval_residual > threshold)
807 ALLOCATE (u_pp_ab(n, nkeep), eigenval(nkeep))
808 k = 0
809 DO i = rank, 1, -1
810 IF (eigenval_residual(i) <= threshold) cycle
811 k = k + 1
812 u_pp_ab(:, k) = matmul(x_pi, matmul(p_ij, b_ij_residual(:, i)))
813 eigenval(k) = eigenval_residual(i)
814 END DO
815
816 CALL timestop(handle)
817
818 END SUBROUTINE solve_ab_eigenproblem
819
820! **************************************************************************************************
821!> \brief Selects the requested RI sizes from the largest positive eigenvalues.
822!> \param eigenval_by_block descending eigenvalues; zero pads unavailable modes
823!> \param RI_size_requested requested total number of functions
824!> \param RI_size_minimum minimum retained size in every nonempty block
825!> \param sizes_RI_selected number retained from each block
826! **************************************************************************************************
827 SUBROUTINE select_ri_sizes(eigenval_by_block, RI_size_requested, RI_size_minimum, &
828 sizes_RI_selected)
829 REAL(kind=dp), INTENT(IN) :: eigenval_by_block(:, :)
830 INTEGER, INTENT(IN) :: ri_size_requested, ri_size_minimum
831 INTEGER, INTENT(OUT) :: sizes_ri_selected(:)
832
833 CHARACTER(LEN=*), PARAMETER :: routinen = 'select_RI_sizes'
834
835 INTEGER :: block, handle, i, n, nmode, remaining
836 INTEGER, ALLOCATABLE :: indices(:)
837 REAL(kind=dp), ALLOCATABLE :: values(:)
838
839 CALL timeset(routinen, handle)
840
841 nmode = SIZE(eigenval_by_block, 1)
842 n = SIZE(eigenval_by_block)
843 cpassert(SIZE(sizes_ri_selected) == SIZE(eigenval_by_block, 2))
844 cpassert(ri_size_minimum >= 0)
845 sizes_ri_selected(:) = 0
846 IF (n > 0) THEN
847 ALLOCATE (values(n), indices(n))
848 values(:) = -reshape(eigenval_by_block, [n])
849 DO block = 1, SIZE(sizes_ri_selected)
850 sizes_ri_selected(block) = &
851 min(ri_size_minimum, count(eigenval_by_block(:, block) > 0.0_dp))
852 values((block - 1)*nmode + 1: &
853 (block - 1)*nmode + sizes_ri_selected(block)) = 0.0_dp
854 END DO
855 CALL sort(values, n, indices)
856 remaining = min(max(0, ri_size_requested - sum(sizes_ri_selected)), count(values < 0.0_dp))
857 DO i = 1, remaining
858 block = (indices(i) - 1)/nmode + 1
859 sizes_ri_selected(block) = sizes_ri_selected(block) + 1
860 END DO
861 END IF
862
863 CALL timestop(handle)
864
865 END SUBROUTINE select_ri_sizes
866
867! **************************************************************************************************
868!> \brief Computes orbital factors for the occupied-empty weight in Eq. (5).
869!>
870!> weights(n) = 1/|ε_n-E_F|, so weights(i)*weights(a) = w_ia.
871!> \param eigenval_MO molecular-orbital eigenvalues, occupied first
872!> \param homo Last occupied state in this spin channel.
873!> \param weights factors whose occupied-empty products give w_ia
874! **************************************************************************************************
875 SUBROUTINE compute_orbital_weights(eigenval_MO, homo, weights)
876 REAL(kind=dp), INTENT(IN) :: eigenval_mo(:)
877 INTEGER, INTENT(IN) :: homo
878 REAL(kind=dp), INTENT(OUT) :: weights(:)
879
880 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_orbital_weights'
881
882 INTEGER :: handle, n
883 REAL(kind=dp) :: gap, middle
884
885 CALL timeset(routinen, handle)
886
887 cpassert(SIZE(weights) == SIZE(eigenval_mo))
888 IF (homo < 1 .OR. homo >= SIZE(eigenval_mo)) THEN
889 cpabort("AUTO_RI orbital weights require occupied and empty states")
890 END IF
891 IF (.NOT. all(ieee_is_finite(eigenval_mo))) THEN
892 cpabort("AUTO_RI orbital weights require finite eigenvalues")
893 END IF
894 gap = eigenval_mo(homo + 1) - eigenval_mo(homo)
895 IF (.NOT. ieee_is_finite(gap) .OR. gap <= 0.0_dp) THEN
896 cpabort("AUTO_RI orbital weights require a finite positive gap")
897 END IF
898 middle = 0.5_dp*(eigenval_mo(homo) + eigenval_mo(homo + 1))
899 DO n = 1, SIZE(eigenval_mo)
900 weights(n) = 1.0_dp/abs(eigenval_mo(n) - middle)
901 END DO
902
903 CALL timestop(handle)
904
905 END SUBROUTINE compute_orbital_weights
906
907! **************************************************************************************************
908!> \brief Computes the occupied-empty contribution B_PQ^C in Eqs. (3) or (10).
909!>
910!> B_PQ^C = Σ_ia w_ia (P|ρ_ia)(ρ_ia|Q).
911!>
912!> \param Int_3c atom-local or atom-pair three-center integrals (μν|P)
913!> \param C_mu_n molecular-orbital coefficients restricted to the relevant AO rows
914!> \param weights Per-orbital weights for this spin channel.
915!> \param homo Last occupied state.
916!> \param B_PQ unnormalized transition matrix for this spin channel
917! **************************************************************************************************
918 SUBROUTINE compute_b_pq_transition(Int_3c, C_mu_n, weights, homo, B_PQ)
919 REAL(kind=dp), CONTIGUOUS, INTENT(IN) :: int_3c(:, :, :), c_mu_n(:, :), weights(:)
920 INTEGER, INTENT(IN) :: homo
921 REAL(kind=dp), INTENT(OUT) :: b_pq(:, :)
922
923 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_B_PQ_transition'
924
925 INTEGER :: handle, n, nao, nri, nstates
926 REAL(kind=dp), ALLOCATABLE :: empty(:, :), occupied(:, :), scaled(:, :)
927 TYPE(local_gemm_ctxt_type) :: gemm_context
928
929 CALL timeset(routinen, handle)
930
931 CALL gemm_context%create(local_gemm_pu_host)
932 nao = SIZE(c_mu_n, 1)
933 nstates = SIZE(c_mu_n, 2)
934 nri = SIZE(int_3c, 3)
935 cpassert(SIZE(int_3c, 1) == nao .AND. SIZE(int_3c, 2) == nao)
936 cpassert(SIZE(b_pq, 1) == nri .AND. SIZE(b_pq, 2) == nri)
937 cpassert(SIZE(weights) == nstates .AND. homo > 0 .AND. homo < nstates)
938 ALLOCATE (scaled(nao, nstates), occupied(nao, nao), empty(nao, nao))
939 DO n = 1, nstates
940 scaled(:, n) = c_mu_n(:, n)*sqrt(weights(n))
941 END DO
942 CALL gemm_context%gemm( &
943 'N', 'T', nao, nao, homo, 1.0_dp, scaled, nao, scaled, nao, &
944 0.0_dp, occupied, nao)
945 CALL gemm_context%gemm('N', 'T', nao, nao, nstates - homo, 1.0_dp, &
946 scaled(:, homo + 1:), nao, scaled(:, homo + 1:), nao, &
947 0.0_dp, empty, nao)
948 CALL contract_b_pq(int_3c, occupied, empty, b_pq)
949 CALL gemm_context%destroy()
950
951 CALL timestop(handle)
952
953 END SUBROUTINE compute_b_pq_transition
954
955! **************************************************************************************************
956!> \brief Computes the frontier-exchange contribution B_PQ^X in Eqs. (4) or (11).
957!>
958!> B_PQ^X = Σ_{n∈W_occ} Σ_i^occ (P|ρ_ni)(ρ_ni|Q).
959!>
960!> \param Int_3c atom-local or atom-pair three-center integrals (μν|P)
961!> \param C_mu_n molecular-orbital coefficients restricted to the relevant AO rows
962!> \param eigenval_MO molecular-orbital eigenvalues, occupied first, in Hartree
963!> \param homo Last occupied state in this spin channel.
964!> \param occ_energy_window frontier-window extension in Hartree
965!> \param B_PQ unnormalized exchange matrix
966! **************************************************************************************************
967 SUBROUTINE compute_b_pq_exchange(Int_3c, C_mu_n, eigenval_MO, homo, occ_energy_window, B_PQ)
968 REAL(kind=dp), CONTIGUOUS, INTENT(IN) :: int_3c(:, :, :), c_mu_n(:, :), &
969 eigenval_mo(:)
970 INTEGER, INTENT(IN) :: homo
971 REAL(kind=dp), INTENT(IN) :: occ_energy_window
972 REAL(kind=dp), INTENT(OUT) :: b_pq(:, :)
973
974 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_B_PQ_exchange'
975
976 INTEGER :: handle, n, nao, nstates
977 REAL(kind=dp), ALLOCATABLE :: frontier(:, :), occupied(:, :), &
978 selected(:, :)
979 TYPE(local_gemm_ctxt_type) :: gemm_context
980
981 CALL timeset(routinen, handle)
982
983 b_pq(:, :) = 0.0_dp
984 nstates = SIZE(eigenval_mo)
985 IF (homo < 1 .OR. homo >= nstates) THEN
986 cpabort("AUTO_RI exchange target requires occupied and empty states")
987 END IF
988 IF (.NOT. ieee_is_finite(occ_energy_window) .OR. &
989 .NOT. all(ieee_is_finite(eigenval_mo))) THEN
990 cpabort("AUTO_RI exchange target requires finite input")
991 END IF
992 IF (occ_energy_window < 0.0_dp .OR. eigenval_mo(homo + 1) <= eigenval_mo(homo)) THEN
993 cpabort("AUTO_RI exchange target requires a valid frontier window")
994 END IF
995 nao = SIZE(c_mu_n, 1)
996 cpassert(SIZE(c_mu_n, 2) == nstates)
997 ALLOCATE (occupied(nao, nao), frontier(nao, nao), selected(nao, nstates), source=0.0_dp)
998 DO n = 1, nstates
999 IF (eigenval_mo(n) < eigenval_mo(homo) - occ_energy_window .OR. &
1000 eigenval_mo(n) > eigenval_mo(homo + 1) + occ_energy_window) cycle
1001 selected(:, n) = c_mu_n(:, n)
1002 END DO
1003 CALL gemm_context%create(local_gemm_pu_host)
1004 ! D_μν = Σ_i^occ C_μi C_νi.
1005 ! F_μν = Σ_{n: ε_n ∈ [ε_H−E_win,ε_L+E_win]} C_μn C_νn.
1006 CALL gemm_context%gemm('N', 'T', nao, nao, homo, 1.0_dp, c_mu_n, nao, c_mu_n, nao, &
1007 0.0_dp, occupied, nao)
1008 CALL gemm_context%gemm('N', 'T', nao, nao, nstates, 1.0_dp, selected, nao, selected, nao, &
1009 0.0_dp, frontier, nao)
1010 CALL contract_b_pq(int_3c, occupied, frontier, b_pq)
1011 CALL gemm_context%destroy()
1012
1013 CALL timestop(handle)
1014
1015 END SUBROUTINE compute_b_pq_exchange
1016
1017! **************************************************************************************************
1018!> \brief Contracts AO density matrices and (μν|P) to form B_PQ.
1019!>
1020!> B_PQ = Σ_μν (μν|P) [left (..|Q) right]_μν.
1021!>
1022!> \param Int_3c atom-local or atom-pair three-center integrals (μν|P)
1023!> \param left First density block, for occupied partners.
1024!> \param right Second density block, for empty or hard-window target states.
1025!> \param B_PQ symmetric RI-basis product matrix
1026! **************************************************************************************************
1027 SUBROUTINE contract_b_pq(Int_3c, left, right, B_PQ)
1028 REAL(kind=dp), CONTIGUOUS, INTENT(IN) :: int_3c(:, :, :), left(:, :), right(:, :)
1029 REAL(kind=dp), INTENT(OUT) :: b_pq(:, :)
1030
1031 CHARACTER(LEN=*), PARAMETER :: routinen = 'contract_B_PQ'
1032
1033 INTEGER :: b, handle, mu, naux, nu, p, q
1034 REAL(kind=dp), ALLOCATABLE :: weighted(:, :), work(:, :)
1035 TYPE(local_gemm_ctxt_type) :: gemm_context
1036
1037 CALL timeset(routinen, handle)
1038
1039 b = SIZE(int_3c, 1)
1040 naux = SIZE(int_3c, 3)
1041 cpassert(SIZE(int_3c, 2) == b)
1042 cpassert(SIZE(left, 1) == b .AND. SIZE(left, 2) == b)
1043 cpassert(SIZE(right, 1) == b .AND. SIZE(right, 2) == b)
1044 cpassert(SIZE(b_pq, 1) == naux .AND. SIZE(b_pq, 2) == naux)
1045 ALLOCATE (work(b, b), weighted(b, b))
1046 CALL gemm_context%create(local_gemm_pu_host)
1047 DO q = 1, naux
1048 ! B_PQ = Σ_μν (μν|P) [left (..|Q) right]_μν.
1049 CALL gemm_context%gemm('N', 'N', b, b, b, 1.0_dp, left, b, int_3c(:, :, q), b, &
1050 0.0_dp, work, b)
1051 CALL gemm_context%gemm('N', 'N', b, b, b, 1.0_dp, work, b, right, b, 0.0_dp, weighted, b)
1052 DO p = 1, q
1053 b_pq(p, q) = 0.0_dp
1054 DO nu = 1, b
1055 DO mu = 1, b
1056 b_pq(p, q) = b_pq(p, q) + int_3c(mu, nu, p)*weighted(mu, nu)
1057 END DO
1058 END DO
1059 b_pq(q, p) = b_pq(p, q)
1060 END DO
1061 END DO
1062 CALL gemm_context%destroy()
1063
1064 CALL timestop(handle)
1065
1066 END SUBROUTINE contract_b_pq
1067
1068! **************************************************************************************************
1069!> \brief Adds separately normalized transition and exchange matrices.
1070!> \param X_Pi inverse metric factor, X_Pi X_Qi = M_PQ^+
1071!> \param B_PQ_transition transition matrix, replaced by the combined target
1072!> \param B_PQ_exchange unnormalized hard-window exchange covariance
1073!> \param target_norm number of nonzero normalized terms
1074! **************************************************************************************************
1075 SUBROUTINE combine_transition_and_exchange_targets( &
1076 X_Pi, B_PQ_transition, B_PQ_exchange, target_norm)
1077 REAL(kind=dp), CONTIGUOUS, INTENT(IN) :: x_pi(:, :)
1078 REAL(kind=dp), INTENT(INOUT) :: b_pq_transition(:, :)
1079 REAL(kind=dp), CONTIGUOUS, INTENT(IN) :: b_pq_exchange(:, :)
1080 REAL(kind=dp), INTENT(OUT) :: target_norm
1081
1082 CHARACTER(LEN=*), PARAMETER :: routinen = 'combine_transition_and_exchange_targets'
1083
1084 INTEGER :: handle, k
1085 REAL(kind=dp) :: exchange_norm, transition_norm
1086 REAL(kind=dp), ALLOCATABLE :: work(:, :)
1087
1088 CALL timeset(routinen, handle)
1089
1090 ALLOCATE (work(SIZE(x_pi, 1), SIZE(x_pi, 2)))
1091 work(:, :) = matmul(b_pq_transition, x_pi)
1092 transition_norm = 0.0_dp
1093 DO k = 1, SIZE(x_pi, 2)
1094 transition_norm = transition_norm + dot_product(x_pi(:, k), work(:, k))
1095 END DO
1096 work(:, :) = matmul(b_pq_exchange, x_pi)
1097 exchange_norm = 0.0_dp
1098 DO k = 1, SIZE(x_pi, 2)
1099 exchange_norm = exchange_norm + dot_product(x_pi(:, k), work(:, k))
1100 END DO
1101 target_norm = 0.0_dp
1102 IF (.NOT. ieee_is_finite(transition_norm) .OR. &
1103 .NOT. ieee_is_finite(exchange_norm) .OR. &
1104 transition_norm < 0.0_dp .OR. exchange_norm < 0.0_dp) THEN
1105 cpabort("AUTO_RI target normalization is invalid")
1106 END IF
1107 ! B = B_ia / Tr(M^+ B_ia) + B_ni / Tr(M^+ B_ni), with zero-norm terms omitted.
1108 IF (transition_norm > 0.0_dp) THEN
1109 b_pq_transition(:, :) = b_pq_transition/transition_norm
1110 target_norm = target_norm + 1.0_dp
1111 ELSE
1112 b_pq_transition(:, :) = 0.0_dp
1113 END IF
1114 IF (exchange_norm > 0.0_dp) THEN
1115 b_pq_transition(:, :) = b_pq_transition + b_pq_exchange/exchange_norm
1116 target_norm = target_norm + 1.0_dp
1117 END IF
1118 IF (target_norm <= 0.0_dp .OR. .NOT. all(ieee_is_finite(b_pq_transition))) THEN
1119 cpabort("AUTO_RI target normalization is invalid")
1120 END IF
1121
1122 CALL timestop(handle)
1123
1124 END SUBROUTINE combine_transition_and_exchange_targets
1125
1126! **************************************************************************************************
1127!> \brief Computes the atom-local matrix B_PQ^A in Eqs. (3)-(6).
1128!>
1129!> B_PQ^A = B_PQ^{A,C}/N_C^A + B_PQ^{A,X}/N_X^A.
1130!>
1131!> The molecular-orbital coefficient rows belonging to one atom are
1132!> collected on one MPI rank. No global AO-pair matrix is formed.
1133!> \param bs_env molecular spectrum and distributed MO coefficients
1134!> \param iatom Atom whose AO rows are needed.
1135!> \param root_rank MPI rank performing the atomic contractions.
1136!> \param Int_3c atom-local three-center integrals (μν|P)
1137!> \param M_PQ atom-local metric of the large reference RI basis
1138!> \param B_PQ normalized atom-local matrix B_PQ^A
1139!> \param target_norm number of nonzero normalized contributions
1140! **************************************************************************************************
1141 SUBROUTINE compute_b_pq_aa(bs_env, iatom, root_rank, Int_3c, M_PQ, &
1142 B_PQ, target_norm)
1143 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1144 INTEGER, INTENT(IN) :: iatom, root_rank
1145 REAL(kind=dp), CONTIGUOUS, INTENT(IN) :: int_3c(:, :, :), m_pq(:, :)
1146 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: b_pq(:, :)
1147 REAL(kind=dp), INTENT(OUT) :: target_norm
1148
1149 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_B_PQ_AA'
1150
1151 INTEGER :: column, first, handle, i, j, nao, nri, &
1152 nstates, rank, row, spin
1153 INTEGER, POINTER :: cols(:), rows(:)
1154 REAL(kind=dp), ALLOCATABLE :: b_pq_exchange(:, :), b_pq_spin(:, :), &
1155 c_mu_n(:, :), weights(:), x_pi(:, :)
1156
1157 CALL timeset(routinen, handle)
1158 NULLIFY (rows, cols)
1159 nao = SIZE(int_3c, 1)
1160 nri = SIZE(int_3c, 3)
1161 nstates = bs_env%n_mo_retained
1162 cpassert(ALLOCATED(bs_env%eigenval_scf_Gamma))
1163 first = 1 + sum(bs_env%sizes_AO(:iatom - 1))
1164 ALLOCATE (b_pq(nri, nri), source=0.0_dp)
1165 ALLOCATE (c_mu_n(nao, nstates), weights(nstates), b_pq_spin(nri, nri))
1166 target_norm = 0.0_dp
1167 ALLOCATE (b_pq_exchange(nri, nri), source=0.0_dp)
1168 DO spin = 1, bs_env%n_spin
1169 CALL compute_orbital_weights(bs_env%eigenval_scf_Gamma(1:nstates, spin), &
1170 bs_env%n_occ(spin), weights)
1171 CALL cp_fm_get_info(bs_env%fm_mo_coeff_Gamma(spin), row_indices=rows, col_indices=cols)
1172 c_mu_n(:, :) = 0.0_dp
1173 DO i = 1, SIZE(rows)
1174 row = rows(i) - first + 1
1175 IF (row < 1 .OR. row > nao) cycle
1176 DO j = 1, SIZE(cols)
1177 column = cols(j)
1178 IF (column > nstates) cycle
1179 c_mu_n(row, column) = bs_env%fm_mo_coeff_Gamma(spin)%local_data(i, j)
1180 END DO
1181 END DO
1182 CALL bs_env%para_env%sum(c_mu_n, root_rank)
1183 IF (bs_env%para_env%mepos /= root_rank) cycle
1184 CALL compute_b_pq_transition(int_3c, c_mu_n, weights, bs_env%n_occ(spin), b_pq_spin)
1185 b_pq(:, :) = b_pq + b_pq_spin
1186 CALL compute_b_pq_exchange(int_3c, c_mu_n, &
1187 bs_env%eigenval_scf_Gamma(1:nstates, spin), &
1188 bs_env%n_occ(spin), bs_env%auto_ri%occ_energy_window, b_pq_spin)
1189 b_pq_exchange(:, :) = b_pq_exchange + b_pq_spin
1190 END DO
1191 IF (bs_env%para_env%mepos /= root_rank) THEN
1192 CALL timestop(handle)
1193 RETURN
1194 END IF
1195 CALL compute_reference_m_pq_inv_factor(m_pq, x_pi, rank)
1196 CALL combine_transition_and_exchange_targets(x_pi, b_pq, b_pq_exchange, target_norm)
1197 CALL timestop(handle)
1198
1199 END SUBROUTINE compute_b_pq_aa
1200
1201! **************************************************************************************************
1202!> \brief Normalizes each contraction coefficient vector U_Pp.
1203!>
1204!> U_Pp <- U_Pp/sqrt(Σ_P |U_Pp|^2).
1205!> \param U_Pp contractions from the large RI basis P to optimized functions p
1206! **************************************************************************************************
1207 SUBROUTINE normalize_ri_coefficients(U_Pp)
1208 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: u_pp
1209
1210 CHARACTER(LEN=*), PARAMETER :: routinen = 'normalize_RI_coefficients'
1211
1212 INTEGER :: handle, j
1213 REAL(kind=dp) :: column_norm
1214
1215 CALL timeset(routinen, handle)
1216
1217 DO j = 1, SIZE(u_pp, 2)
1218 column_norm = norm2(u_pp(:, j))
1219 IF (column_norm <= tiny(1.0_dp)) THEN
1220 cpabort("AUTO_RI produced a numerically zero localized column")
1221 END IF
1222 u_pp(:, j) = u_pp(:, j)/column_norm
1223 END DO
1224
1225 CALL timestop(handle)
1226
1227 END SUBROUTINE normalize_ri_coefficients
1228
1229! **************************************************************************************************
1230!> \brief Creates a matrix in the existing molecular BLACS context.
1231!> \param qs_env ...
1232!> \param matrix ...
1233!> \param nr ...
1234!> \param nc ...
1235! **************************************************************************************************
1236 SUBROUTINE create_distributed_matrix(qs_env, matrix, nr, nc)
1237 TYPE(qs_environment_type), POINTER :: qs_env
1238 TYPE(cp_fm_type), INTENT(OUT) :: matrix
1239 INTEGER, INTENT(IN) :: nr, nc
1240
1241 CHARACTER(LEN=*), PARAMETER :: routinen = 'create_distributed_matrix'
1242
1243 INTEGER :: handle
1244 TYPE(cp_blacs_env_type), POINTER :: blacs
1245 TYPE(cp_fm_struct_type), POINTER :: layout
1246 TYPE(mp_para_env_type), POINTER :: para
1247
1248 CALL timeset(routinen, handle)
1249
1250 NULLIFY (blacs, para, layout)
1251 CALL get_qs_env(qs_env, blacs_env=blacs, para_env=para)
1252 CALL cp_fm_struct_create(layout, context=blacs, nrow_global=nr, ncol_global=nc, para_env=para)
1253 CALL cp_fm_create(matrix, layout, set_zero=.true.)
1254 CALL cp_fm_struct_release(layout)
1255
1256 CALL timestop(handle)
1257
1258 END SUBROUTINE create_distributed_matrix
1259
1260! **************************************************************************************************
1261!> \brief Computes and stores the two-center contractions U^AB in Eqs. (8)-(13).
1262!>
1263!> The selected AA space is projected out according to Eq. (8). The symmetric
1264!> pair density in Eq. (9) defines B^{AB,C} and B^{AB,X} in Eqs. (10) and (11).
1265!> Diagonalizing their sum gives U^AB in Eq. (12), which expands the contracted
1266!> pair functions in Eq. (13).
1267!> \param qs_env ...
1268!> \param bs_env ...
1269!> \param U_Pp_A temporary atom-local contractions used to project the AA space
1270! **************************************************************************************************
1271 SUBROUTINE compute_ab_optimized_ri_basis(qs_env, bs_env, U_Pp_A)
1272 TYPE(qs_environment_type), POINTER :: qs_env
1273 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1274 REAL(kind=dp), DIMENSION(:, :, :), INTENT(IN) :: u_pp_a
1275
1276 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_AB_optimized_RI_basis'
1277
1278 INTEGER :: ab_pair_count, center_index, current_atom, expected_ri_size, first_ab_pair, &
1279 handle, handle_pair_batches, iatom, jatom, k, last_ab_pair, nao_kind, nri_a, nri_ab_ref, &
1280 nri_b, ri_size_opt_aa, ri_size_opt_ab, ri_size_optimized, ri_size_ref, ri_size_requested, &
1281 root_rank, row, size_opt_ri_ab, size_u_pp_ab, size_u_pp_ab_storage, spin
1282 INTEGER, ALLOCATABLE :: ab_atom_a(:), ab_atom_b(:), ab_size_opt_ri_to_a(:), block_next(:), &
1283 sizes_opt_ri_aa(:), sizes_opt_ri_ab(:), sizes_ref_ri(:), u_pp_ab_offset(:)
1284 REAL(kind=dp), ALLOCATABLE :: b_pq(:, :), c_mu_n_ab(:, :, :), c_mu_n_atom(:, :), &
1285 eigenval(:), eigenval_ab(:, :), m_pq(:, :), u_pp_aa(:, :), u_pp_ab(:, :), &
1286 u_pp_ab_packed(:)
1287 TYPE(gw_3c_ctx_type) :: ctx
1288 TYPE(gw_3c_ws_type) :: ws
1289 TYPE(mp_para_env_type), POINTER :: para
1290
1291 CALL timeset(routinen, handle)
1292
1293 NULLIFY (para)
1294 CALL get_qs_env(qs_env, para_env=para)
1295 ALLOCATE (sizes_ref_ri(bs_env%n_atom), sizes_opt_ri_aa(bs_env%n_atom))
1296 DO iatom = 1, bs_env%n_atom
1297 sizes_ref_ri(iatom) = get_ref_ri_size(bs_env, iatom)
1298 END DO
1299 sizes_opt_ri_aa(:) = bs_env%auto_ri%sizes_opt_RI
1300 ri_size_ref = sum(sizes_ref_ri)
1301 ri_size_opt_aa = sum(sizes_opt_ri_aa)
1302
1303 ab_pair_count = 0
1304 DO iatom = 1, bs_env%n_atom - 1
1305 DO jatom = iatom + 1, bs_env%n_atom
1306 IF (norm2(bs_env%ri_rs%particle_set(iatom)%r - bs_env%ri_rs%particle_set(jatom)%r) <= &
1307 bs_env%auto_ri%neighbor_radius) THEN
1308 ab_pair_count = ab_pair_count + 1
1309 END IF
1310 END DO
1311 END DO
1312 ALLOCATE (ab_atom_a(ab_pair_count), ab_atom_b(ab_pair_count), &
1313 ab_size_opt_ri_to_a(ab_pair_count), sizes_opt_ri_ab(ab_pair_count), &
1314 u_pp_ab_offset(ab_pair_count))
1315 ab_size_opt_ri_to_a = 0
1316 u_pp_ab_offset = 0
1317 ALLOCATE (eigenval_ab(2*maxval(sizes_ref_ri), ab_pair_count), source=0.0_dp)
1318
1319 CALL gw_3c_ctx_create(ctx, qs_env, bs_env%ri_metric, bs_env%basis_set_AO, &
1320 bs_env%basis_set_AO, bs_env%basis_set_RI)
1321 CALL gw_3c_ws_create(ws, ctx)
1322 k = 0
1323 DO iatom = 1, bs_env%n_atom - 1
1324 DO jatom = iatom + 1, bs_env%n_atom
1325 IF (norm2(bs_env%ri_rs%particle_set(iatom)%r - bs_env%ri_rs%particle_set(jatom)%r) > &
1326 bs_env%auto_ri%neighbor_radius) cycle
1327 k = k + 1
1328 ab_atom_a(k) = iatom
1329 ab_atom_b(k) = jatom
1330 END DO
1331 END DO
1332 size_u_pp_ab_storage = 0
1333 DO k = 1, ab_pair_count
1334 root_rank = modulo(k - 1, para%num_pe)
1335 IF (para%mepos /= root_rank) cycle
1336 nri_ab_ref = sizes_ref_ri(ab_atom_a(k)) + sizes_ref_ri(ab_atom_b(k))
1337 u_pp_ab_offset(k) = size_u_pp_ab_storage + 1
1338 size_u_pp_ab_storage = size_u_pp_ab_storage + nri_ab_ref*nri_ab_ref
1339 END DO
1340 ALLOCATE (u_pp_ab_packed(size_u_pp_ab_storage))
1341 CALL timeset(routinen//'_pair_batches', handle_pair_batches)
1342 DO first_ab_pair = 1, ab_pair_count, para%num_pe
1343 last_ab_pair = min(ab_pair_count, first_ab_pair + para%num_pe - 1)
1344 ! Gather only one pair's MO rows per rank, then solve all batch pairs concurrently.
1345 DO k = first_ab_pair, last_ab_pair
1346 root_rank = k - first_ab_pair
1347 iatom = ab_atom_a(k)
1348 jatom = ab_atom_b(k)
1349 IF (para%mepos == root_rank) THEN
1350 ALLOCATE (c_mu_n_ab( &
1351 bs_env%sizes_AO(iatom) + bs_env%sizes_AO(jatom), &
1352 bs_env%n_mo_retained, bs_env%n_spin))
1353 END IF
1354 DO spin = 1, bs_env%n_spin
1355 row = 0
1356 DO center_index = 1, 2
1357 current_atom = iatom
1358 IF (center_index == 2) current_atom = jatom
1359 nao_kind = bs_env%sizes_AO(current_atom)
1360 ALLOCATE (c_mu_n_atom(nao_kind, bs_env%n_mo_retained))
1361 CALL cp_fm_get_submatrix(bs_env%fm_mo_coeff_Gamma(spin), c_mu_n_atom, &
1362 start_row=1 + sum(bs_env%sizes_AO(:current_atom - 1)))
1363 IF (para%mepos == root_rank) THEN
1364 c_mu_n_ab(row + 1:row + nao_kind, :, spin) = c_mu_n_atom
1365 END IF
1366 DEALLOCATE (c_mu_n_atom)
1367 row = row + nao_kind
1368 END DO
1369 END DO
1370 END DO
1371 k = first_ab_pair + para%mepos
1372 IF (k <= last_ab_pair) THEN
1373 iatom = ab_atom_a(k)
1374 jatom = ab_atom_b(k)
1375 nri_a = sizes_ref_ri(iatom)
1376 nri_b = sizes_ref_ri(jatom)
1377 nri_ab_ref = nri_a + nri_b
1378 ! Eqs. (9)-(11): form the symmetric pair density and B^{AB,C}+B^{AB,X}.
1379 CALL compute_b_pq_ab(bs_env, ctx, ws, iatom, jatom, c_mu_n_ab, m_pq, b_pq)
1380 ALLOCATE (u_pp_aa(nri_ab_ref, sizes_opt_ri_aa(iatom) + sizes_opt_ri_aa(jatom)), &
1381 source=0.0_dp)
1382 u_pp_aa(1:nri_a, 1:sizes_opt_ri_aa(iatom)) = &
1383 u_pp_a(1:nri_a, 1:sizes_opt_ri_aa(iatom), iatom)
1384 u_pp_aa(nri_a + 1:, sizes_opt_ri_aa(iatom) + 1:) = &
1385 u_pp_a(1:nri_b, 1:sizes_opt_ri_aa(jatom), jatom)
1386 ! Eqs. (8) and (12): project out the AA space and diagonalize the remainder.
1387 CALL solve_ab_eigenproblem(m_pq, b_pq, u_pp_aa, u_pp_ab, eigenval)
1388 eigenval_ab(1:SIZE(eigenval), k) = eigenval
1389 DEALLOCATE (eigenval)
1390 u_pp_ab_packed(u_pp_ab_offset(k):u_pp_ab_offset(k) + SIZE(u_pp_ab) - 1) = &
1391 reshape(u_pp_ab, [SIZE(u_pp_ab)])
1392 DEALLOCATE (u_pp_ab)
1393 DEALLOCATE (u_pp_aa, b_pq, m_pq, c_mu_n_ab)
1394 END IF
1395 END DO
1396 CALL timestop(handle_pair_batches)
1397 k = ab_pair_count
1398 CALL gw_3c_ws_release(ws)
1399 CALL gw_3c_ctx_release(ctx)
1400 ri_size_requested = nint(min(bs_env%auto_ri%ri_ao_ratio, &
1401 REAL(ri_size_ref, dp)/REAL(SUM(bs_env%sizes_AO), dp))* &
1402 REAL(SUM(bs_env%sizes_AO), dp))
1403 ri_size_requested = max(ri_size_requested, bs_env%n_atom)
1404
1405 CALL para%sum(eigenval_ab)
1406 CALL select_ri_sizes(eigenval_ab, ri_size_requested - sum(sizes_opt_ri_aa), 0, &
1407 sizes_opt_ri_ab)
1408 ri_size_opt_ab = sum(sizes_opt_ri_ab)
1409 bs_env%auto_ri%sizes_opt_RI(:) = sizes_opt_ri_aa
1410 DO k = 1, ab_pair_count
1411 iatom = ab_atom_a(k)
1412 jatom = ab_atom_b(k)
1413 size_opt_ri_ab = sizes_opt_ri_ab(k)
1414 ab_size_opt_ri_to_a(k) = balance_ab_columns(bs_env%auto_ri%sizes_opt_RI(iatom), &
1415 bs_env%auto_ri%sizes_opt_RI(jatom), &
1416 size_opt_ri_ab)
1417 bs_env%auto_ri%sizes_opt_RI(iatom) = &
1418 bs_env%auto_ri%sizes_opt_RI(iatom) + ab_size_opt_ri_to_a(k)
1419 bs_env%auto_ri%sizes_opt_RI(jatom) = &
1420 bs_env%auto_ri%sizes_opt_RI(jatom) + size_opt_ri_ab - ab_size_opt_ri_to_a(k)
1421 END DO
1422
1423 ri_size_optimized = sum(bs_env%auto_ri%sizes_opt_RI)
1424 expected_ri_size = ri_size_opt_aa + ri_size_opt_ab
1425 cpassert(ri_size_optimized == expected_ri_size)
1426 cpassert(all(bs_env%auto_ri%sizes_opt_RI > 0))
1427 IF (ri_size_optimized > ri_size_ref) THEN
1428 cpabort("AUTO_RI request exceeds independent reference RI directions")
1429 END IF
1430 size_u_pp_ab = 0
1431 DO iatom = 1, bs_env%n_atom
1432 size_u_pp_ab = size_u_pp_ab + sizes_ref_ri(iatom)*sizes_opt_ri_aa(iatom)
1433 END DO
1434 DO k = 1, ab_pair_count
1435 size_u_pp_ab = size_u_pp_ab + &
1436 (sizes_ref_ri(ab_atom_a(k)) + &
1437 sizes_ref_ri(ab_atom_b(k)))*sizes_opt_ri_ab(k)
1438 END DO
1439 ALLOCATE (bs_env%auto_ri%AB_atom_A(bs_env%n_atom + ab_pair_count), source=0)
1440 ALLOCATE (bs_env%auto_ri%AB_atom_B(bs_env%n_atom + ab_pair_count), source=0)
1441 ALLOCATE (bs_env%auto_ri%AB_first_p_A(bs_env%n_atom + ab_pair_count), source=0)
1442 ALLOCATE (bs_env%auto_ri%AB_first_p_B(bs_env%n_atom + ab_pair_count), source=0)
1443 ALLOCATE (bs_env%auto_ri%AB_size_opt_RI_to_A(bs_env%n_atom + ab_pair_count), source=0)
1444 ALLOCATE (bs_env%auto_ri%AB_size_ref_RI(bs_env%n_atom + ab_pair_count), source=0)
1445 ALLOCATE (bs_env%auto_ri%AB_size_opt_RI(bs_env%n_atom + ab_pair_count), source=0)
1446 ALLOCATE (bs_env%auto_ri%U_Pp_AB_offset(bs_env%n_atom + ab_pair_count), source=0)
1447 ALLOCATE (bs_env%auto_ri%U_Pp_AB(size_u_pp_ab), source=0.0_dp)
1448 bs_env%auto_ri%AB_block_count = 0
1449
1450 ALLOCATE (block_next(bs_env%n_atom))
1451 block_next(1) = 1
1452 DO iatom = 2, bs_env%n_atom
1453 block_next(iatom) = block_next(iatom - 1) + bs_env%auto_ri%sizes_opt_RI(iatom - 1)
1454 END DO
1455 DO iatom = 1, bs_env%n_atom
1456 IF (sizes_opt_ri_aa(iatom) > 0) bs_env%auto_ri%AB_block_count = &
1457 bs_env%auto_ri%AB_block_count + 1
1458 CALL store_aa_columns(bs_env, iatom, &
1459 u_pp_a(1:sizes_ref_ri(iatom), 1:sizes_opt_ri_aa(iatom), iatom), &
1460 block_next)
1461 END DO
1462
1463 DO k = 1, ab_pair_count
1464 iatom = ab_atom_a(k)
1465 jatom = ab_atom_b(k)
1466 size_opt_ri_ab = sizes_opt_ri_ab(k)
1467 IF (size_opt_ri_ab == 0) cycle
1468 root_rank = modulo(k - 1, para%num_pe)
1469 nri_ab_ref = sizes_ref_ri(iatom) + sizes_ref_ri(jatom)
1470 ALLOCATE (u_pp_ab(nri_ab_ref, size_opt_ri_ab))
1471 IF (para%mepos == root_rank) THEN
1472 u_pp_ab(:, :) = reshape( &
1473 u_pp_ab_packed(u_pp_ab_offset(k): &
1474 u_pp_ab_offset(k) + &
1475 nri_ab_ref*size_opt_ri_ab - 1), &
1476 [nri_ab_ref, size_opt_ri_ab])
1477 END IF
1478 CALL para%bcast(u_pp_ab, root_rank)
1479 bs_env%auto_ri%AB_block_count = bs_env%auto_ri%AB_block_count + 1
1480 ! Eq. (13): store the selected neighbor-pair contractions in the global transform.
1481 CALL store_ab_columns(bs_env, u_pp_ab, iatom, jatom, ab_size_opt_ri_to_a(k), block_next)
1482 DEALLOCATE (u_pp_ab)
1483 END DO
1484
1485 DO iatom = 1, bs_env%n_atom
1486 expected_ri_size = 1 + sum(bs_env%auto_ri%sizes_opt_RI(:iatom))
1487 cpassert(block_next(iatom) == expected_ri_size)
1488 END DO
1489 DEALLOCATE (block_next, ab_atom_a, ab_atom_b, ab_size_opt_ri_to_a, &
1490 u_pp_ab_offset, u_pp_ab_packed)
1491
1492 CALL timestop(handle)
1493
1494 END SUBROUTINE compute_ab_optimized_ri_basis
1495
1496! **************************************************************************************************
1497!> \brief Divides AB columns between atoms A and B for load balance.
1498!> \param load_a current number of columns owned by A
1499!> \param load_b current number of columns owned by B
1500!> \param RI_size_opt_AB number of new pair RI functions
1501!> \return ...
1502! **************************************************************************************************
1503 PURE INTEGER FUNCTION balance_ab_columns(load_a, load_b, RI_size_opt_AB) RESULT(RI_size_to_a)
1504 INTEGER, INTENT(IN) :: load_a, load_b, ri_size_opt_ab
1505
1506 ri_size_to_a = (load_b + ri_size_opt_ab - load_a + 1)/2
1507 ri_size_to_a = max(0, min(ri_size_opt_ab, ri_size_to_a))
1508
1509 END FUNCTION balance_ab_columns
1510
1511! **************************************************************************************************
1512!> \brief Stores the AA contraction columns for one atom.
1513!> \param bs_env ...
1514!> \param atom ...
1515!> \param U_Pp_A atom-local contraction coefficients
1516!> \param block_next ...
1517! **************************************************************************************************
1518 SUBROUTINE store_aa_columns(bs_env, atom, U_Pp_A, block_next)
1519 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1520 INTEGER, INTENT(IN) :: atom
1521 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: u_pp_a
1522 INTEGER, DIMENSION(:), INTENT(INOUT) :: block_next
1523
1524 CHARACTER(LEN=*), PARAMETER :: routinen = 'store_AA_columns'
1525
1526 INTEGER :: ab_block, first, handle, ri_size_opt_aa
1527 REAL(kind=dp), ALLOCATABLE :: u_pp_a_normalized(:, :)
1528
1529 CALL timeset(routinen, handle)
1530
1531 first = block_next(atom)
1532 ab_block = bs_env%auto_ri%AB_block_count
1533 ri_size_opt_aa = SIZE(u_pp_a, 2)
1534 ALLOCATE (u_pp_a_normalized(SIZE(u_pp_a, 1), ri_size_opt_aa))
1535 IF (ri_size_opt_aa > 0) THEN
1536 u_pp_a_normalized(:, :) = u_pp_a
1537 CALL normalize_ri_coefficients(u_pp_a_normalized)
1538 bs_env%auto_ri%AB_atom_A(ab_block) = atom
1539 bs_env%auto_ri%AB_atom_B(ab_block) = atom
1540 bs_env%auto_ri%AB_first_p_A(ab_block) = &
1541 first - sum(bs_env%auto_ri%sizes_opt_RI(:atom - 1))
1542 bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block) = ri_size_opt_aa
1543 CALL store_u_pp_ab(bs_env%auto_ri, ab_block, u_pp_a_normalized)
1544 END IF
1545 DEALLOCATE (u_pp_a_normalized)
1546 block_next(atom) = first + ri_size_opt_aa
1547
1548 CALL timestop(handle)
1549
1550 END SUBROUTINE store_aa_columns
1551
1552! **************************************************************************************************
1553!> \brief Stores an AB contraction in the blocks assigned to atoms A and B.
1554!> \param bs_env ...
1555!> \param U_Pp_AB neighbor-pair contractions
1556!> \param atom_A ...
1557!> \param atom_B ...
1558!> \param RI_size_to_a number of optimized pair RI functions assigned to atom A
1559!> \param block_next ...
1560! **************************************************************************************************
1561 SUBROUTINE store_ab_columns(bs_env, U_Pp_AB, atom_A, atom_B, RI_size_to_a, block_next)
1562 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1563 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
1564 INTENT(INOUT) :: u_pp_ab
1565 INTEGER, INTENT(IN) :: atom_a, atom_b, ri_size_to_a
1566 INTEGER, DIMENSION(:), INTENT(INOUT) :: block_next
1567
1568 CHARACTER(LEN=*), PARAMETER :: routinen = 'store_AB_columns'
1569
1570 INTEGER :: ab_block, first_a, first_b, handle, &
1571 ri_size_opt_ab
1572
1573 CALL timeset(routinen, handle)
1574
1575 ri_size_opt_ab = SIZE(u_pp_ab, 2)
1576 ab_block = bs_env%auto_ri%AB_block_count
1577 first_a = block_next(atom_a)
1578 first_b = block_next(atom_b)
1579 CALL normalize_ri_coefficients(u_pp_ab)
1580
1581 bs_env%auto_ri%AB_atom_A(ab_block) = atom_a
1582 bs_env%auto_ri%AB_atom_B(ab_block) = atom_b
1583 bs_env%auto_ri%AB_first_p_A(ab_block) = first_a - sum(bs_env%auto_ri%sizes_opt_RI(:atom_a - 1))
1584 bs_env%auto_ri%AB_first_p_B(ab_block) = first_b - sum(bs_env%auto_ri%sizes_opt_RI(:atom_b - 1))
1585 bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block) = ri_size_to_a
1586 CALL store_u_pp_ab(bs_env%auto_ri, ab_block, u_pp_ab)
1587 block_next(atom_a) = first_a + ri_size_to_a
1588 block_next(atom_b) = first_b + ri_size_opt_ab - ri_size_to_a
1589
1590 CALL timestop(handle)
1591
1592 END SUBROUTINE store_ab_columns
1593
1594! **************************************************************************************************
1595!> \brief Packs one AB U_Pp matrix into the persistent AUTO_RI data; B=A denotes an AA block.
1596!> \param auto_ri ...
1597!> \param AB_block ...
1598!> \param U_Pp ...
1599! **************************************************************************************************
1600 SUBROUTINE store_u_pp_ab(auto_ri, AB_block, U_Pp)
1601 TYPE(auto_ri_type), INTENT(INOUT) :: auto_ri
1602 INTEGER, INTENT(IN) :: ab_block
1603 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: u_pp
1604
1605 INTEGER :: first, last
1606
1607 first = 1
1608 IF (ab_block > 1) THEN
1609 first = auto_ri%U_Pp_AB_offset(ab_block - 1) + &
1610 auto_ri%AB_size_ref_RI(ab_block - 1)* &
1611 auto_ri%AB_size_opt_RI(ab_block - 1)
1612 END IF
1613 last = first + SIZE(u_pp) - 1
1614 cpassert(last <= SIZE(auto_ri%U_Pp_AB))
1615 auto_ri%U_Pp_AB_offset(ab_block) = first
1616 auto_ri%AB_size_ref_RI(ab_block) = SIZE(u_pp, 1)
1617 auto_ri%AB_size_opt_RI(ab_block) = SIZE(u_pp, 2)
1618 auto_ri%U_Pp_AB(first:last) = reshape(u_pp, [SIZE(u_pp)])
1619
1620 END SUBROUTINE store_u_pp_ab
1621
1622! **************************************************************************************************
1623!> \brief Computes the two-center matrix B_PQ^AB in Eqs. (9)-(12).
1624!>
1625!> Its numerator uses the symmetric off-diagonal density
1626!> ρ̅_mn^AB=ρ_mn^AB+ρ_mn^BA after projection from the retained AA
1627!> space. Its normalization uses the complete two-atom density.
1628!> \param bs_env ...
1629!> \param ctx ...
1630!> \param ws ...
1631!> \param iatom ...
1632!> \param jatom ...
1633!> \param C_mu_n_AB molecular-orbital coefficients on the AO rows of atoms A and B
1634!> \param M_PQ combined metric of the large reference RI bases on A and B
1635!> \param B_PQ normalized two-center matrix B_PQ^AB
1636! **************************************************************************************************
1637 SUBROUTINE compute_b_pq_ab(bs_env, ctx, ws, iatom, jatom, C_mu_n_AB, M_PQ, B_PQ)
1638 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1639 TYPE(gw_3c_ctx_type), INTENT(IN) :: ctx
1640 TYPE(gw_3c_ws_type), INTENT(INOUT) :: ws
1641 INTEGER, INTENT(IN) :: iatom, jatom
1642 REAL(kind=dp), INTENT(IN) :: c_mu_n_ab(:, :, :)
1643 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: m_pq(:, :), b_pq(:, :)
1644
1645 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_B_PQ_AB'
1646
1647 INTEGER :: atoms(2), handle, homo, i, j, k, kind_a, kind_b, nao_ab, nri_ab_ref, &
1648 offset_ao(2), offset_ref_ri(2), rank, sizes_ao(2), sizes_ref_ri(2), spin
1649 LOGICAL :: screened
1650 REAL(kind=dp) :: norm_exchange, norm_transition
1651 REAL(kind=dp), ALLOCATABLE :: b_pi(:, :), b_pq_exchange_ab(:, :), b_pq_exchange_full(:, :), &
1652 b_pq_spin(:, :), b_pq_transition_ab(:, :), b_pq_transition_full(:, :), &
1653 int_3c_ab(:, :, :), int_3c_all(:, :, :), m_pq_block(:, :), weights(:), x_pi(:, :)
1654
1655 CALL timeset(routinen, handle)
1656
1657 atoms(1) = iatom
1658 atoms(2) = jatom
1659 sizes_ao = bs_env%sizes_AO(atoms)
1660 DO i = 1, 2
1661 sizes_ref_ri(i) = get_ref_ri_size(bs_env, atoms(i))
1662 END DO
1663 offset_ao(1) = 0
1664 offset_ao(2) = sizes_ao(1)
1665 offset_ref_ri(1) = 0
1666 offset_ref_ri(2) = sizes_ref_ri(1)
1667 nao_ab = sum(sizes_ao)
1668 nri_ab_ref = sum(sizes_ref_ri)
1669 ALLOCATE (b_pq(nri_ab_ref, nri_ab_ref), &
1670 m_pq(nri_ab_ref, nri_ab_ref), source=0.0_dp)
1671 ALLOCATE (int_3c_all(nao_ab, nao_ab, nri_ab_ref), source=0.0_dp)
1672 DO i = 1, 2
1673 DO j = 1, 2
1674 CALL compute_m_pq_ab( &
1675 bs_env, atoms(i), atoms(j), m_pq_block, bs_env%ri_metric)
1676 m_pq(offset_ref_ri(i) + 1:offset_ref_ri(i) + sizes_ref_ri(i), &
1677 offset_ref_ri(j) + 1:offset_ref_ri(j) + sizes_ref_ri(j)) = m_pq_block
1678 DEALLOCATE (m_pq_block)
1679 DO k = 1, 2
1681 int_3c_all, ctx, ws, atom_j=atoms(j), atom_k=atoms(k), &
1682 atom_i=atoms(i), j_offset=offset_ao(j), k_offset=offset_ao(k), &
1683 i_offset=offset_ref_ri(i), screened=screened)
1684 END DO
1685 END DO
1686 END DO
1687 kind_a = bs_env%ri_rs%particle_set(iatom)%atomic_kind%kind_number
1688 kind_b = bs_env%ri_rs%particle_set(jatom)%atomic_kind%kind_number
1689 CALL add_ri_metric_regularization(bs_env%basis_set_RI(kind_a)%gto_basis_set, &
1690 bs_env%regularization_RI, m_pq)
1691 CALL add_ri_metric_regularization(bs_env%basis_set_RI(kind_b)%gto_basis_set, &
1692 bs_env%regularization_RI, m_pq, &
1693 offset=sizes_ref_ri(1))
1694 ALLOCATE (int_3c_ab, source=int_3c_all)
1695 int_3c_ab(1:sizes_ao(1), 1:sizes_ao(1), :) = 0.0_dp
1696 int_3c_ab(sizes_ao(1) + 1:, sizes_ao(1) + 1:, :) = 0.0_dp
1697 ALLOCATE (weights(bs_env%n_mo_retained), b_pq_spin(nri_ab_ref, nri_ab_ref))
1698 ALLOCATE (b_pq_transition_ab(nri_ab_ref, nri_ab_ref), &
1699 b_pq_exchange_ab(nri_ab_ref, nri_ab_ref), &
1700 b_pq_transition_full(nri_ab_ref, nri_ab_ref), &
1701 b_pq_exchange_full(nri_ab_ref, nri_ab_ref), &
1702 source=0.0_dp)
1703 DO spin = 1, bs_env%n_spin
1704 homo = bs_env%n_occ(spin)
1705 CALL compute_orbital_weights( &
1706 eigenval_mo=bs_env%eigenval_scf_Gamma(1:bs_env%n_mo_retained, spin), &
1707 homo=homo, weights=weights)
1708 CALL compute_b_pq_transition(int_3c_all, c_mu_n_ab(:, :, spin), weights, homo, b_pq_spin)
1709 b_pq_transition_full(:, :) = b_pq_transition_full + b_pq_spin
1710 CALL compute_b_pq_transition(int_3c_ab, c_mu_n_ab(:, :, spin), weights, homo, b_pq_spin)
1711 b_pq_transition_ab(:, :) = b_pq_transition_ab + b_pq_spin
1712 CALL compute_b_pq_exchange(int_3c_all, c_mu_n_ab(:, :, spin), &
1713 bs_env%eigenval_scf_Gamma(1:bs_env%n_mo_retained, spin), &
1714 homo, bs_env%auto_ri%occ_energy_window, b_pq_spin)
1715 b_pq_exchange_full(:, :) = b_pq_exchange_full + b_pq_spin
1716 CALL compute_b_pq_exchange(int_3c_ab, c_mu_n_ab(:, :, spin), &
1717 bs_env%eigenval_scf_Gamma(1:bs_env%n_mo_retained, spin), &
1718 homo, bs_env%auto_ri%occ_energy_window, b_pq_spin)
1719 b_pq_exchange_ab(:, :) = b_pq_exchange_ab + b_pq_spin
1720 END DO
1721 CALL compute_reference_m_pq_inv_factor(m_pq, x_pi, rank)
1722 DEALLOCATE (b_pq_spin)
1723 ALLOCATE (b_pi(nri_ab_ref, rank))
1724 b_pi(:, :) = matmul(b_pq_transition_full, x_pi)
1725 norm_transition = sum(x_pi*b_pi)
1726 b_pi(:, :) = matmul(b_pq_exchange_full, x_pi)
1727 norm_exchange = sum(x_pi*b_pi)
1728 ! Eqs. (10)-(12): B_PQ^AB = (B_PQ^{AB,C}/N_C^{AB}+B_PQ^{AB,X}/N_X^{AB})/2.
1729 IF (norm_transition > 0.0_dp) THEN
1730 b_pq(:, :) = b_pq + 0.5_dp*b_pq_transition_ab/norm_transition
1731 END IF
1732 IF (norm_exchange > 0.0_dp) THEN
1733 b_pq(:, :) = b_pq + 0.5_dp*b_pq_exchange_ab/norm_exchange
1734 END IF
1735
1736 CALL timestop(handle)
1737
1738 END SUBROUTINE compute_b_pq_ab
1739
1740! **************************************************************************************************
1741!> \brief Builds lists of AB blocks whose columns belong to each atom; B=A denotes an AA block.
1742!> \param bs_env ...
1743!> \param lists ...
1744! **************************************************************************************************
1745 SUBROUTINE build_ab_block_lists(bs_env, lists)
1746 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1747 TYPE(ab_block_list_type), ALLOCATABLE, INTENT(OUT) :: lists(:)
1748
1749 CHARACTER(LEN=*), PARAMETER :: routinen = 'build_AB_block_lists'
1750
1751 INTEGER :: ab_block, atom_a, atom_b, handle, &
1752 position, ri_size_to_a, size_opt_ri_ab
1753 INTEGER, ALLOCATABLE :: n_ab_blocks_by_atom(:)
1754
1755 CALL timeset(routinen, handle)
1756
1757 ALLOCATE (lists(bs_env%n_atom))
1758 ALLOCATE (n_ab_blocks_by_atom(bs_env%n_atom), source=0)
1759 DO ab_block = 1, bs_env%auto_ri%AB_block_count
1760 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
1761 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
1762 size_opt_ri_ab = bs_env%auto_ri%AB_size_opt_RI(ab_block)
1763 ri_size_to_a = bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block)
1764 IF (ri_size_to_a > 0) n_ab_blocks_by_atom(atom_a) = n_ab_blocks_by_atom(atom_a) + 1
1765 IF (atom_b /= atom_a .AND. ri_size_to_a < size_opt_ri_ab) THEN
1766 n_ab_blocks_by_atom(atom_b) = n_ab_blocks_by_atom(atom_b) + 1
1767 END IF
1768 END DO
1769
1770 DO atom_a = 1, bs_env%n_atom
1771 ALLOCATE (lists(atom_a)%block(n_ab_blocks_by_atom(atom_a)))
1772 ALLOCATE (lists(atom_a)%first_p_AB(n_ab_blocks_by_atom(atom_a)))
1773 ALLOCATE (lists(atom_a)%first_p_atom(n_ab_blocks_by_atom(atom_a)))
1774 ALLOCATE (lists(atom_a)%size_opt_RI(n_ab_blocks_by_atom(atom_a)))
1775 END DO
1776 n_ab_blocks_by_atom = 0
1777 DO ab_block = 1, bs_env%auto_ri%AB_block_count
1778 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
1779 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
1780 size_opt_ri_ab = bs_env%auto_ri%AB_size_opt_RI(ab_block)
1781 ri_size_to_a = bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block)
1782 IF (ri_size_to_a > 0) THEN
1783 position = n_ab_blocks_by_atom(atom_a) + 1
1784 n_ab_blocks_by_atom(atom_a) = position
1785 lists(atom_a)%block(position) = ab_block
1786 lists(atom_a)%first_p_AB(position) = 1
1787 lists(atom_a)%first_p_atom(position) = bs_env%auto_ri%AB_first_p_A(ab_block)
1788 lists(atom_a)%size_opt_RI(position) = ri_size_to_a
1789 END IF
1790 IF (atom_b /= atom_a .AND. ri_size_to_a < size_opt_ri_ab) THEN
1791 position = n_ab_blocks_by_atom(atom_b) + 1
1792 n_ab_blocks_by_atom(atom_b) = position
1793 lists(atom_b)%block(position) = ab_block
1794 lists(atom_b)%first_p_AB(position) = ri_size_to_a + 1
1795 lists(atom_b)%first_p_atom(position) = &
1796 bs_env%auto_ri%AB_first_p_B(ab_block)
1797 lists(atom_b)%size_opt_RI(position) = size_opt_ri_ab - ri_size_to_a
1798 END IF
1799 END DO
1800 DO atom_a = 1, bs_env%n_atom
1801 IF (sum(lists(atom_a)%size_opt_RI) /= bs_env%auto_ri%sizes_opt_RI(atom_a)) THEN
1802 cpabort("AUTO_RI AB-block columns do not fill the optimized atom block")
1803 END IF
1804 END DO
1805 DEALLOCATE (n_ab_blocks_by_atom)
1806
1807 CALL timestop(handle)
1808
1809 END SUBROUTINE build_ab_block_lists
1810
1811! **************************************************************************************************
1812!> \brief Returns the location of atom in atoms(:count), or zero if it is absent.
1813!> \param atom ...
1814!> \param atoms ...
1815!> \param count ...
1816!> \return ...
1817! **************************************************************************************************
1818 PURE INTEGER FUNCTION find_atom_position(atom, atoms, count) RESULT(position)
1819 INTEGER, INTENT(IN) :: atom
1820 INTEGER, DIMENSION(:), INTENT(IN) :: atoms
1821 INTEGER, INTENT(IN) :: count
1822
1823 INTEGER :: i
1824
1825 position = 0
1826 DO i = 1, count
1827 IF (atoms(i) == atom) THEN
1828 position = i
1829 RETURN
1830 END IF
1831 END DO
1832
1833 END FUNCTION find_atom_position
1834
1835! **************************************************************************************************
1836!> \brief Collects U_Pp from the large reference basis for one optimized atom block.
1837!> \param bs_env ...
1838!> \param atom ...
1839!> \param list ...
1840!> \param atom_U ...
1841! **************************************************************************************************
1842 SUBROUTINE build_optimized_atom_u(bs_env, atom, list, atom_U)
1843 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1844 INTEGER, INTENT(IN) :: atom
1845 TYPE(ab_block_list_type), INTENT(IN) :: list
1846 TYPE(optimized_atom_u_type), INTENT(OUT) :: atom_u
1847
1848 CHARACTER(LEN=*), PARAMETER :: routinen = 'build_optimized_atom_U'
1849
1850 INTEGER :: ab_block, atom_a, atom_b, first, first_p_ab, first_p_atom, first_row, handle, i, &
1851 last, nri_ref_a, nri_ref_block, nsupport, position, size_opt_ri_ab
1852 INTEGER, ALLOCATABLE :: support_atoms(:)
1853 REAL(kind=dp), ALLOCATABLE :: u_pp_ab(:, :)
1854
1855 CALL timeset(routinen, handle)
1856
1857 ALLOCATE (support_atoms(2*SIZE(list%block)))
1858 nsupport = 0
1859 DO i = 1, SIZE(list%block)
1860 ab_block = list%block(i)
1861 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
1862 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
1863 position = find_atom_position(atom_a, support_atoms, nsupport)
1864 IF (position == 0) THEN
1865 nsupport = nsupport + 1
1866 support_atoms(nsupport) = atom_a
1867 END IF
1868 IF (atom_b /= atom_a) THEN
1869 position = find_atom_position(atom_b, support_atoms, nsupport)
1870 IF (position == 0) THEN
1871 nsupport = nsupport + 1
1872 support_atoms(nsupport) = atom_b
1873 END IF
1874 END IF
1875 END DO
1876 cpassert(nsupport > 0)
1877 ALLOCATE (atom_u%ref_atom(nsupport), atom_u%first_P(nsupport))
1878 atom_u%ref_atom(:) = support_atoms(:nsupport)
1879 first_row = 1
1880 DO i = 1, nsupport
1881 atom_u%first_P(i) = first_row
1882 first_row = first_row + get_ref_ri_size(bs_env, atom_u%ref_atom(i))
1883 END DO
1884 ALLOCATE (atom_u%U_Pp(first_row - 1, bs_env%auto_ri%sizes_opt_RI(atom)), &
1885 source=0.0_dp)
1886
1887 DO i = 1, SIZE(list%block)
1888 ab_block = list%block(i)
1889 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
1890 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
1891 nri_ref_block = bs_env%auto_ri%AB_size_ref_RI(ab_block)
1892 size_opt_ri_ab = bs_env%auto_ri%AB_size_opt_RI(ab_block)
1893 first = bs_env%auto_ri%U_Pp_AB_offset(ab_block)
1894 last = first + nri_ref_block*size_opt_ri_ab - 1
1895 ALLOCATE (u_pp_ab(nri_ref_block, size_opt_ri_ab))
1896 u_pp_ab(:, :) = reshape(bs_env%auto_ri%U_Pp_AB(first:last), &
1897 [nri_ref_block, size_opt_ri_ab])
1898 first_p_ab = list%first_p_AB(i)
1899 first_p_atom = list%first_p_atom(i)
1900 size_opt_ri_ab = list%size_opt_RI(i)
1901 IF (first_p_ab < 1 .OR. &
1902 first_p_ab + size_opt_ri_ab - 1 > SIZE(u_pp_ab, 2)) THEN
1903 cpabort("AUTO_RI AB-block column range is invalid")
1904 END IF
1905 IF (first_p_atom < 1 .OR. &
1906 first_p_atom + size_opt_ri_ab - 1 > bs_env%auto_ri%sizes_opt_RI(atom)) THEN
1907 cpabort("AUTO_RI optimized atom-block column range is invalid")
1908 END IF
1909
1910 nri_ref_a = get_ref_ri_size(bs_env, atom_a)
1911 position = find_atom_position(atom_a, atom_u%ref_atom, nsupport)
1912 cpassert(position > 0)
1913 first_row = atom_u%first_P(position)
1914 atom_u%U_Pp(first_row:first_row + nri_ref_a - 1, &
1915 first_p_atom:first_p_atom + size_opt_ri_ab - 1) = &
1916 u_pp_ab(1:nri_ref_a, first_p_ab:first_p_ab + size_opt_ri_ab - 1)
1917 IF (atom_b /= atom_a) THEN
1918 position = find_atom_position(atom_b, atom_u%ref_atom, nsupport)
1919 cpassert(position > 0)
1920 first_row = atom_u%first_P(position)
1921 atom_u%U_Pp(first_row:first_row + get_ref_ri_size(bs_env, atom_b) - 1, &
1922 first_p_atom:first_p_atom + size_opt_ri_ab - 1) = &
1923 u_pp_ab(nri_ref_a + 1:, first_p_ab:first_p_ab + size_opt_ri_ab - 1)
1924 END IF
1925 DEALLOCATE (u_pp_ab)
1926 END DO
1927 DEALLOCATE (support_atoms)
1928
1929 CALL timestop(handle)
1930
1931 END SUBROUTINE build_optimized_atom_u
1932
1933! **************************************************************************************************
1934!> \brief Adds REGULARIZATION_RI to one atom-local reference metric block.
1935!> \param bs_env ...
1936!> \param atom ...
1937!> \param M_PQ atom-local metric block in the large reference RI basis
1938! **************************************************************************************************
1939 SUBROUTINE regularize_m_pq_block(bs_env, atom, M_PQ)
1940 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1941 INTEGER, INTENT(IN) :: atom
1942 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: m_pq
1943
1944 CHARACTER(LEN=*), PARAMETER :: routinen = 'regularize_M_PQ_block'
1945
1946 INTEGER :: handle, i, iset, kind, nset
1947 INTEGER, DIMENSION(:), POINTER :: npgf, nsgf_set
1948 INTEGER, DIMENSION(:, :), POINTER :: first_sgf
1949 REAL(kind=dp), DIMENSION(:, :), POINTER :: zet
1950
1951 CALL timeset(routinen, handle)
1952
1953 cpassert(SIZE(m_pq, 1) == SIZE(m_pq, 2))
1954 kind = bs_env%ri_rs%particle_set(atom)%atomic_kind%kind_number
1955 CALL get_gto_basis_set(bs_env%basis_set_RI(kind)%gto_basis_set, &
1956 first_sgf=first_sgf, npgf=npgf, nset=nset, &
1957 nsgf_set=nsgf_set, zet=zet)
1958 DO iset = 1, nset
1959 DO i = first_sgf(1, iset), first_sgf(1, iset) + nsgf_set(iset) - 1
1960 m_pq(i, i) = m_pq(i, i) + &
1961 bs_env%regularization_RI* &
1962 max(1.0_dp, 1.0_dp/minval(zet(1:npgf(iset), iset)))
1963 END DO
1964 END DO
1965
1966 CALL timestop(handle)
1967
1968 END SUBROUTINE regularize_m_pq_block
1969
1970! **************************************************************************************************
1971!> \brief Directly contracts one optimized atom-pair metric block.
1972!>
1973!> M_pq^{AB} = Σ_IJ (U_Ip^A)^T M_IJ U_Jq^B. Each reference
1974!> atom-pair block M_IJ is discarded immediately after this contraction.
1975!> \param bs_env ...
1976!> \param atom_U_a contractions U_Ip for optimized block A
1977!> \param atom_U_b contractions U_Jq for optimized block B
1978!> \param coulomb true for Coulomb; false for the GW fitting operator
1979!> \param symmetric true when both transforms describe the same optimized block
1980!> \param matrix_optimized contracted M_pq or V_pq block in the optimized RI basis
1981! **************************************************************************************************
1982 SUBROUTINE compute_optimized_ri_matrix_block( &
1983 bs_env, atom_U_a, atom_U_b, coulomb, symmetric, matrix_optimized)
1984 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1985 TYPE(optimized_atom_u_type), INTENT(IN) :: atom_u_a, atom_u_b
1986 LOGICAL, INTENT(IN) :: coulomb, symmetric
1987 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
1988 INTENT(OUT) :: matrix_optimized
1989
1990 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_optimized_RI_matrix_block'
1991
1992 INTEGER :: atom_a, atom_b, first_a, first_b, &
1993 handle, i, j, last_a, last_b, &
1994 nri_small_a, nri_small_b
1995 REAL(kind=dp), ALLOCATABLE :: matrix_pq_contribution(:, :), matrix_reference(:, :), &
1996 matrix_reference_times_u_b(:, :)
1997
1998 CALL timeset(routinen, handle)
1999
2000 nri_small_a = SIZE(atom_u_a%U_Pp, 2)
2001 nri_small_b = SIZE(atom_u_b%U_Pp, 2)
2002 IF (symmetric) THEN
2003 cpassert(nri_small_a == nri_small_b)
2004 END IF
2005 ALLOCATE (matrix_optimized(nri_small_a, nri_small_b), source=0.0_dp)
2006 DO i = 1, SIZE(atom_u_a%ref_atom)
2007 atom_a = atom_u_a%ref_atom(i)
2008 first_a = atom_u_a%first_P(i)
2009 last_a = first_a + get_ref_ri_size(bs_env, atom_a) - 1
2010 DO j = 1, SIZE(atom_u_b%ref_atom)
2011 IF (symmetric .AND. j > i) cycle
2012 atom_b = atom_u_b%ref_atom(j)
2013 first_b = atom_u_b%first_P(j)
2014 last_b = first_b + get_ref_ri_size(bs_env, atom_b) - 1
2015 IF (coulomb) THEN
2016 CALL compute_m_pq_ab(bs_env, atom_a, atom_b, matrix_reference)
2017 ELSE
2018 CALL compute_m_pq_ab( &
2019 bs_env, atom_a, atom_b, matrix_reference, bs_env%ri_metric)
2020 IF (atom_a == atom_b .AND. bs_env%regularization_RI > 0.0_dp) THEN
2021 CALL regularize_m_pq_block(bs_env, atom_a, matrix_reference)
2022 END IF
2023 END IF
2024 ALLOCATE (matrix_reference_times_u_b(SIZE(matrix_reference, 1), nri_small_b))
2025 matrix_reference_times_u_b(:, :) = &
2026 matmul(matrix_reference, atom_u_b%U_Pp(first_b:last_b, :))
2027 ALLOCATE (matrix_pq_contribution(nri_small_a, nri_small_b))
2028 matrix_pq_contribution(:, :) = &
2029 matmul(transpose(atom_u_a%U_Pp(first_a:last_a, :)), &
2030 matrix_reference_times_u_b)
2031 matrix_optimized(:, :) = matrix_optimized + matrix_pq_contribution
2032 IF (symmetric .AND. i /= j) THEN
2033 matrix_optimized(:, :) = matrix_optimized + transpose(matrix_pq_contribution)
2034 END IF
2035 DEALLOCATE (matrix_pq_contribution, matrix_reference, matrix_reference_times_u_b)
2036 END DO
2037 END DO
2038 IF (.NOT. all(ieee_is_finite(matrix_optimized))) THEN
2039 cpabort("AUTO_RI optimized metric block contains invalid values")
2040 END IF
2041
2042 CALL timestop(handle)
2043
2044 END SUBROUTINE compute_optimized_ri_matrix_block
2045
2046! **************************************************************************************************
2047!> \brief Builds M_opt or V_opt directly from contracted atom-pair blocks.
2048!> \param qs_env ...
2049!> \param bs_env ...
2050!> \param lists ...
2051!> \param matrix_pq contracted M_pq or V_pq in the optimized RI basis
2052!> \param coulomb true for Coulomb; false for the GW fitting operator
2053! **************************************************************************************************
2054 SUBROUTINE compute_contracted_ri_matrix(qs_env, bs_env, lists, matrix_pq, coulomb)
2055 TYPE(qs_environment_type), POINTER :: qs_env
2056 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2057 TYPE(ab_block_list_type), DIMENSION(:), INTENT(IN) :: lists
2058 TYPE(cp_fm_type), INTENT(OUT) :: matrix_pq
2059 LOGICAL, INTENT(IN) :: coulomb
2060
2061 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_contracted_RI_matrix'
2062
2063 INTEGER :: handle, iatom, jatom, npcol, nprow, &
2064 pair_index, ri_size_opt
2065 INTEGER, POINTER :: col_dist(:), row_dist(:), sizes_opt_ri(:)
2066 LOGICAL :: atom_u_a_ready
2067 REAL(kind=dp), ALLOCATABLE :: matrix_pq_block(:, :)
2068 TYPE(dbcsr_distribution_type) :: dist
2069 TYPE(dbcsr_distribution_type), POINTER :: template
2070 TYPE(dbcsr_type) :: blocks
2071 TYPE(optimized_atom_u_type) :: atom_u_a, atom_u_b
2072
2073 CALL timeset(routinen, handle)
2074
2075 ALLOCATE (sizes_opt_ri(bs_env%n_atom), row_dist(bs_env%n_atom), col_dist(bs_env%n_atom))
2076 sizes_opt_ri(:) = bs_env%auto_ri%sizes_opt_RI
2077 CALL get_qs_env(qs_env, dbcsr_dist=template)
2078 CALL dbcsr_distribution_get(template, nprows=nprow, npcols=npcol)
2079 DO iatom = 1, SIZE(sizes_opt_ri)
2080 row_dist(iatom) = mod(iatom - 1, nprow)
2081 col_dist(iatom) = mod(iatom - 1, npcol)
2082 END DO
2083 ri_size_opt = sum(sizes_opt_ri)
2084 CALL create_distributed_matrix(qs_env, matrix_pq, ri_size_opt, ri_size_opt)
2085 CALL dbcsr_distribution_new(dist, template=template, row_dist=row_dist, col_dist=col_dist)
2086 CALL dbcsr_create(blocks, name='AUTO_RI contracted metric', dist=dist, &
2087 matrix_type=dbcsr_type_symmetric, &
2088 row_blk_size=sizes_opt_ri, col_blk_size=sizes_opt_ri)
2089
2090 pair_index = 0
2091 DO iatom = 1, SIZE(sizes_opt_ri)
2092 atom_u_a_ready = .false.
2093 DO jatom = 1, iatom
2094 pair_index = pair_index + 1
2095 IF (mod(pair_index - 1, bs_env%para_env%num_pe) /= bs_env%para_env%mepos) cycle
2096 IF (.NOT. atom_u_a_ready) THEN
2097 CALL build_optimized_atom_u(bs_env, iatom, lists(iatom), atom_u_a)
2098 atom_u_a_ready = .true.
2099 END IF
2100 IF (iatom == jatom) THEN
2101 CALL compute_optimized_ri_matrix_block( &
2102 bs_env, atom_u_a, atom_u_a, coulomb, .true., &
2103 matrix_pq_block)
2104 matrix_pq_block(:, :) = 0.5_dp*(matrix_pq_block + transpose(matrix_pq_block))
2105 ELSE
2106 CALL build_optimized_atom_u(bs_env, jatom, lists(jatom), atom_u_b)
2107 CALL compute_optimized_ri_matrix_block( &
2108 bs_env, atom_u_a, atom_u_b, coulomb, .false., &
2109 matrix_pq_block)
2110 END IF
2111 CALL dbcsr_put_block(blocks, iatom, jatom, matrix_pq_block)
2112 DEALLOCATE (matrix_pq_block)
2113 END DO
2114 END DO
2115 CALL dbcsr_finalize(blocks)
2116 CALL copy_dbcsr_to_fm(blocks, matrix_pq)
2117 CALL dbcsr_release(blocks)
2119 DEALLOCATE (sizes_opt_ri, row_dist, col_dist)
2120
2121 CALL timestop(handle)
2122
2123 END SUBROUTINE compute_contracted_ri_matrix
2124
2125! **************************************************************************************************
2126!> \brief Computes the pseudoinverse of the optimized fitting metric M_opt.
2127!>
2128!> The generalized problem M_opt X=M_AA X λ separates linearly
2129!> dependent directions. The result is M_opt^+=X diag(1/λ) X^T.
2130!> \param qs_env ...
2131!> \param bs_env ...
2132!> \param M_pq optimized RI fitting metric
2133! **************************************************************************************************
2134 SUBROUTINE compute_m_pq_inv(qs_env, bs_env, M_pq)
2135 TYPE(qs_environment_type), POINTER :: qs_env
2136 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2137 TYPE(cp_fm_type), INTENT(INOUT) :: m_pq
2138
2139 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_M_pq_inv'
2140
2141 INTEGER :: handle, ncol_local, nrow_local, &
2142 ri_size_opt
2143 INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
2144 REAL(kind=dp), ALLOCATABLE :: eigenval(:), factors(:)
2145 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
2146 POINTER :: m_pq_aa_local, m_pq_local
2147 TYPE(cp_fm_type) :: m_pq_aa, work, x_pq
2148
2149 CALL timeset(routinen, handle)
2150
2151 ri_size_opt = sum(bs_env%auto_ri%sizes_opt_RI)
2152 CALL create_distributed_matrix(qs_env, bs_env%auto_ri%M_pq_inv, &
2153 ri_size_opt, ri_size_opt)
2154 CALL create_distributed_matrix(qs_env, x_pq, ri_size_opt, ri_size_opt)
2155 CALL create_distributed_matrix(qs_env, m_pq_aa, ri_size_opt, ri_size_opt)
2156 CALL create_distributed_matrix(qs_env, work, ri_size_opt, ri_size_opt)
2157 CALL cp_fm_get_info(m_pq, nrow_local=nrow_local, ncol_local=ncol_local, &
2158 row_indices=row_indices, col_indices=col_indices, &
2159 local_data=m_pq_local)
2160 CALL cp_fm_get_info(m_pq_aa, local_data=m_pq_aa_local)
2161 m_pq_aa_local = 0.0_dp
2162 CALL copy_m_pq_aa_blocks(m_pq_local(:nrow_local, :ncol_local), &
2163 m_pq_aa_local(:nrow_local, :ncol_local), &
2164 row_indices(:nrow_local), col_indices(:ncol_local), &
2165 bs_env%auto_ri%sizes_opt_RI)
2166
2167 ALLOCATE (eigenval(ri_size_opt))
2168 CALL cp_fm_geeig(m_pq, m_pq_aa, x_pq, eigenval, work)
2169 CALL compute_optimized_m_pq_inv_factors(eigenval, factors)
2170 CALL cp_fm_column_scale(x_pq, factors)
2171 CALL cp_fm_syrk("U", "N", ri_size_opt, 1.0_dp, x_pq, 1, 1, 0.0_dp, &
2172 bs_env%auto_ri%M_pq_inv)
2173 CALL cp_fm_uplo_to_full(bs_env%auto_ri%M_pq_inv, work)
2174
2175 DEALLOCATE (eigenval, factors)
2176 CALL cp_fm_release(x_pq)
2177 CALL cp_fm_release(m_pq_aa)
2178 CALL cp_fm_release(work)
2179
2180 CALL timestop(handle)
2181
2182 END SUBROUTINE compute_m_pq_inv
2183
2184! **************************************************************************************************
2185!> \brief Computes M_opt and V_opt without constructing M_ref or V_ref.
2186!> \param qs_env ...
2187!> \param bs_env ...
2188! **************************************************************************************************
2189 SUBROUTINE compute_m_pq_inv_and_v_pq(qs_env, bs_env)
2190 TYPE(qs_environment_type), POINTER :: qs_env
2191 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2192
2193 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_M_pq_inv_and_V_pq'
2194
2195 INTEGER :: handle
2196 TYPE(ab_block_list_type), ALLOCATABLE :: lists(:)
2197 TYPE(cp_fm_type) :: m_pq
2198
2199 CALL timeset(routinen, handle)
2200
2201 CALL build_ab_block_lists(bs_env, lists)
2202 CALL initialize_auto_ri_integrals(bs_env)
2203 CALL compute_contracted_ri_matrix(qs_env, bs_env, lists, m_pq, .false.)
2204 CALL compute_m_pq_inv(qs_env, bs_env, m_pq)
2205 CALL cp_fm_release(m_pq)
2206 CALL compute_contracted_ri_matrix(qs_env, bs_env, lists, bs_env%auto_ri%V_pq, .true.)
2207
2208 ! Activate the optimized per-atom RI block sizes for RI-RS.
2209 CALL set_optimized_ri_basis_sizes(bs_env)
2210
2211 CALL timestop(handle)
2212
2213 END SUBROUTINE compute_m_pq_inv_and_v_pq
2214
2215! **************************************************************************************************
2216!> \brief Computes the atom-local two-center RI metric (P|Q) and the
2217!> three-center integrals (μν|P) used in Eqs. (3) and (4).
2218!> \param ao_basis ...
2219!> \param ri_basis ...
2220!> \param potential fitting operator used for both integral types
2221!> \param M_PQ two-center metric of the large reference RI basis
2222!> \param Int_3c three-center integrals (μν|P)
2223! **************************************************************************************************
2224 SUBROUTINE compute_int_3c(ao_basis, ri_basis, potential, M_PQ, Int_3c)
2225 TYPE(gto_basis_set_type), INTENT(IN) :: ao_basis, ri_basis
2226 TYPE(coulomb_operator_type), INTENT(IN) :: potential
2227 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
2228 INTENT(OUT) :: m_pq
2229 REAL(kind=dp), INTENT(OUT) :: int_3c(:, :, :)
2230
2231 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_int_3c'
2232
2233 INTEGER :: handle, iset, jset, kset, max_ao_l, &
2234 max_l, nao, ncoa, ncob, ncoc, nri, &
2235 nsgfa, nsgfb, nsgfc, sgfa, sgfb, sgfc
2236 INTEGER, DIMENSION(:), POINTER :: lmax_a, lmax_c, lmin_a, lmin_c, npgf_a, &
2237 npgf_c, nsgf_set_a, nsgf_set_c
2238 INTEGER, DIMENSION(:, :), POINTER :: first_sgf_a, first_sgf_c
2239 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: int_2c_cart, int_2c_spherical
2240 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: int_3c_cart, int_3c_spherical_all, &
2241 int_3c_spherical_block
2242 REAL(kind=dp), DIMENSION(3) :: center
2243 REAL(kind=dp), DIMENSION(:), POINTER :: radius_a, radius_b, radius_c
2244 REAL(kind=dp), DIMENSION(:, :), POINTER :: pgf_radius_a, pgf_radius_c, sphi_a, &
2245 sphi_c, zeta_a, zeta_c
2246 TYPE(coulomb_integral_context_type) :: context
2247
2248 CALL timeset(routinen, handle)
2249
2250 CALL get_gto_basis_set(ao_basis, nsgf=nao, lmax=lmax_a, lmin=lmin_a, &
2251 npgf=npgf_a, nsgf_set=nsgf_set_a, first_sgf=first_sgf_a, &
2252 pgf_radius=pgf_radius_a, sphi=sphi_a, zet=zeta_a)
2253 CALL get_gto_basis_set(ri_basis, nsgf=nri, lmax=lmax_c, lmin=lmin_c, &
2254 npgf=npgf_c, nsgf_set=nsgf_set_c, first_sgf=first_sgf_c, &
2255 pgf_radius=pgf_radius_c, sphi=sphi_c, zet=zeta_c)
2256 cpassert(nao > 0 .AND. nri > 0)
2257 max_ao_l = maxval(lmax_a)
2258 max_l = max(max_ao_l, maxval(lmax_c))
2259 CALL init_orbital_pointers(max(max_l, 2*max_ao_l))
2260 CALL init_spherical_harmonics(max_l, -1)
2261 IF (potential%potential_type == do_potential_truncated) THEN
2262 IF (active_integral_library%coulomb2_library /= library_libint .OR. &
2263 active_integral_library%coulomb3_library /= library_libint) THEN
2264 CALL cp_abort(__location__, &
2265 "AUTO_RI TRUNCATED fitting requires LIBINT for "// &
2266 "COULOMB_2C and COULOMB_3C")
2267 END IF
2268 CALL coulomb_integral_init(context, max_am_2c=max(max_l, 2*max_ao_l), max_am_3c=max_l)
2269 ELSE
2270 CALL coulomb_integral_init(context, max_am_2c=max_l, max_am_3c=max_l)
2271 END IF
2272 center = 0.0_dp
2273
2274 ALLOCATE (m_pq(nri, nri), int_3c_spherical_all(nao, nao, nri))
2275 m_pq = 0.0_dp
2276 int_3c_spherical_all = 0.0_dp
2277
2278 DO iset = 1, ri_basis%nset
2279 ncoa = npgf_c(iset)*ncoset(lmax_c(iset))
2280 sgfa = first_sgf_c(1, iset)
2281 nsgfa = nsgf_set_c(iset)
2282 radius_a => pgf_radius_c(1:npgf_c(iset), iset)
2283 DO jset = 1, ri_basis%nset
2284 ncob = npgf_c(jset)*ncoset(lmax_c(jset))
2285 sgfb = first_sgf_c(1, jset)
2286 nsgfb = nsgf_set_c(jset)
2287 radius_b => pgf_radius_c(1:npgf_c(jset), jset)
2288 ALLOCATE (int_2c_cart(ncoa, ncob), int_2c_spherical(nsgfa, nsgfb))
2289 int_2c_cart = 0.0_dp
2290 CALL compute_coulomb_2c(context, lmin_c(iset), lmax_c(iset), &
2291 lmin_c(jset), lmax_c(jset), &
2292 npgf_c(iset), npgf_c(jset), zeta_c(1:npgf_c(iset), iset), &
2293 zeta_c(1:npgf_c(jset), jset), &
2294 radius_a, radius_b, center, center, int_2c_cart, potential)
2295 CALL ab_contract(int_2c_spherical, int_2c_cart, sphi_c(:, sgfa:), sphi_c(:, sgfb:), &
2296 ncoa, ncob, nsgfa, nsgfb)
2297 m_pq(sgfa:sgfa + nsgfa - 1, sgfb:sgfb + nsgfb - 1) = int_2c_spherical
2298 DEALLOCATE (int_2c_spherical, int_2c_cart)
2299 END DO
2300 END DO
2301
2302 DO iset = 1, ao_basis%nset
2303 ncoa = npgf_a(iset)*ncoset(lmax_a(iset))
2304 sgfa = first_sgf_a(1, iset)
2305 nsgfa = nsgf_set_a(iset)
2306 radius_a => pgf_radius_a(1:npgf_a(iset), iset)
2307 DO jset = 1, ao_basis%nset
2308 ncob = npgf_a(jset)*ncoset(lmax_a(jset))
2309 sgfb = first_sgf_a(1, jset)
2310 nsgfb = nsgf_set_a(jset)
2311 radius_b => pgf_radius_a(1:npgf_a(jset), jset)
2312 DO kset = 1, ri_basis%nset
2313 ncoc = npgf_c(kset)*ncoset(lmax_c(kset))
2314 sgfc = first_sgf_c(1, kset)
2315 nsgfc = nsgf_set_c(kset)
2316 radius_c => pgf_radius_c(1:npgf_c(kset), kset)
2317 ALLOCATE (int_3c_cart(ncoa, ncob, ncoc), &
2318 int_3c_spherical_block(nsgfa, nsgfb, nsgfc))
2319 int_3c_cart = 0.0_dp
2320 CALL compute_coulomb_3c(context, lmin_a(iset), lmax_a(iset), &
2321 lmin_a(jset), lmax_a(jset), &
2322 lmin_c(kset), lmax_c(kset), npgf_a(iset), npgf_a(jset), &
2323 npgf_c(kset), zeta_a(1:npgf_a(iset), iset), &
2324 zeta_a(1:npgf_a(jset), jset), zeta_c(1:npgf_c(kset), kset), &
2325 radius_a, radius_b, radius_c, center, center, center, &
2326 int_3c_cart, potential)
2327 CALL abc_contract(int_3c_spherical_block, int_3c_cart, &
2328 sphi_a(:, sgfa:), sphi_a(:, sgfb:), &
2329 sphi_c(:, sgfc:), ncoa, ncob, ncoc, nsgfa, nsgfb, nsgfc)
2330 int_3c_spherical_all(sgfa:sgfa + nsgfa - 1, sgfb:sgfb + nsgfb - 1, &
2331 sgfc:sgfc + nsgfc - 1) = int_3c_spherical_block
2332 DEALLOCATE (int_3c_spherical_block, int_3c_cart)
2333 END DO
2334 END DO
2335 END DO
2336
2337 int_3c(:, :, :) = int_3c_spherical_all
2338 CALL coulomb_integral_cleanup(context)
2339 IF (.NOT. all(ieee_is_finite(m_pq))) THEN
2340 cpabort("AUTO_RI metric contains invalid values")
2341 END IF
2342 IF (.NOT. all(ieee_is_finite(int_3c))) THEN
2343 cpabort("AUTO_RI three-center integrals contain invalid values")
2344 END IF
2345
2346 CALL timestop(handle)
2347
2348 END SUBROUTINE compute_int_3c
2349
2350! **************************************************************************************************
2351!> \brief Initializes the integral kernels used by the AUTO_RI optimization.
2352!> \param bs_env ...
2353! **************************************************************************************************
2354 SUBROUTINE initialize_auto_ri_integrals(bs_env)
2355 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2356
2357 CHARACTER(LEN=*), PARAMETER :: routinen = 'initialize_auto_ri_integrals'
2358
2359 CHARACTER(LEN=default_path_length) :: table_file
2360 INTEGER :: handle, ikind, max_l, table_lmax, unit_id
2361
2362 CALL timeset(routinen, handle)
2363
2364 max_l = 0
2365 DO ikind = 1, SIZE(bs_env%basis_set_RI)
2366 cpassert(ASSOCIATED(bs_env%basis_set_RI(ikind)%gto_basis_set))
2367 max_l = max(max_l, maxval(bs_env%basis_set_RI(ikind)%gto_basis_set%lmax))
2368 END DO
2369 ! The OS two-center kernel accesses l+1 even without force evaluation.
2370 CALL init_orbital_pointers(max_l + 1)
2371 CALL init_spherical_harmonics(max_l, -1)
2372
2373 IF (bs_env%ri_metric%potential_type == do_potential_truncated) THEN
2374 ! The truncated-Coulomb kernel needs auxiliary functions through l_A+l_B+1.
2375 table_lmax = 2*max_l + 1
2376 IF (get_lmax_init() < table_lmax) THEN
2377 unit_id = -1
2378 IF (bs_env%para_env%is_source()) THEN
2379 table_file = discover_file(bs_env%ri_metric%filename)
2380 CALL open_file(unit_number=unit_id, file_name=trim(table_file))
2381 END IF
2382 CALL init_t_c_g0(table_lmax, unit_id, bs_env%para_env%mepos, bs_env%para_env)
2383 IF (bs_env%para_env%is_source()) CALL close_file(unit_id)
2384 END IF
2385 END IF
2386
2387 CALL timestop(handle)
2388
2389 END SUBROUTINE initialize_auto_ri_integrals
2390
2391! **************************************************************************************************
2392!> \brief Computes the atom-pair two-center RI metric matrix (P_A|Q_B).
2393!> \param bs_env ...
2394!> \param atom_A ...
2395!> \param atom_B ...
2396!> \param M_PQ metric block in the large reference RI basis
2397!> \param ri_potential ...
2398! **************************************************************************************************
2399 SUBROUTINE compute_m_pq_ab(bs_env, atom_A, atom_B, M_PQ, ri_potential)
2400 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2401 INTEGER, INTENT(IN) :: atom_a, atom_b
2402 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
2403 INTENT(OUT) :: m_pq
2404 TYPE(libint_potential_type), INTENT(IN), OPTIONAL :: ri_potential
2405
2406 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_M_PQ_AB'
2407
2408 INTEGER :: atom_1, atom_2, handle, kind_1, kind_2
2409 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: m_pq_canonical
2410 TYPE(libint_potential_type) :: potential
2411
2412 CALL timeset(routinen, handle)
2413
2414 cpassert(ASSOCIATED(bs_env%ri_rs%particle_set))
2415 cpassert(atom_a >= 1 .AND. atom_a <= bs_env%n_atom)
2416 cpassert(atom_b >= 1 .AND. atom_b <= bs_env%n_atom)
2417 IF (atom_a >= atom_b) THEN
2418 atom_1 = atom_a
2419 atom_2 = atom_b
2420 ELSE
2421 atom_1 = atom_b
2422 atom_2 = atom_a
2423 END IF
2424 kind_1 = bs_env%ri_rs%particle_set(atom_1)%atomic_kind%kind_number
2425 kind_2 = bs_env%ri_rs%particle_set(atom_2)%atomic_kind%kind_number
2426
2427 IF (PRESENT(ri_potential)) THEN
2428 cpassert(ri_potential%potential_type == do_potential_truncated)
2429 potential = ri_potential
2430 ELSE
2431 potential%potential_type = do_potential_coulomb
2432 END IF
2433 CALL compute_m_pq( &
2434 basis_a=bs_env%basis_set_RI(kind_1)%gto_basis_set, &
2435 basis_b=bs_env%basis_set_RI(kind_2)%gto_basis_set, &
2436 center_a=bs_env%ri_rs%particle_set(atom_1)%r, &
2437 center_b=bs_env%ri_rs%particle_set(atom_2)%r, &
2438 potential=potential, m_pq=m_pq_canonical)
2439
2440 IF (atom_a >= atom_b) THEN
2441 CALL move_alloc(m_pq_canonical, m_pq)
2442 ELSE
2443 ALLOCATE (m_pq(SIZE(m_pq_canonical, 2), SIZE(m_pq_canonical, 1)))
2444 m_pq(:, :) = transpose(m_pq_canonical)
2445 DEALLOCATE (m_pq_canonical)
2446 END IF
2447
2448 CALL timestop(handle)
2449
2450 END SUBROUTINE compute_m_pq_ab
2451
2452! **************************************************************************************************
2453!> \brief Computes one contracted two-center RI metric block (P_A|Q_B).
2454!> \param basis_a ...
2455!> \param basis_b ...
2456!> \param center_a ...
2457!> \param center_b ...
2458!> \param potential ...
2459!> \param M_PQ metric block in the large reference RI basis
2460! **************************************************************************************************
2461 SUBROUTINE compute_m_pq(basis_a, basis_b, center_a, center_b, potential, M_PQ)
2462 TYPE(gto_basis_set_type), POINTER :: basis_a, basis_b
2463 REAL(kind=dp), DIMENSION(3), INTENT(IN) :: center_a, center_b
2464 TYPE(libint_potential_type), INTENT(IN) :: potential
2465 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
2466 INTENT(OUT) :: m_pq
2467
2468 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_M_PQ'
2469
2470 INTEGER :: handle
2471
2472 CALL timeset(routinen, handle)
2473
2474 ALLOCATE (m_pq(basis_a%nsgf, basis_b%nsgf), source=0.0_dp)
2475 SELECT CASE (potential%potential_type)
2477 CALL int_operators_r12_ab_os(operator_truncated, m_pq, rab=center_b - center_a, &
2478 fba=basis_a, fbb=basis_b, &
2479 r_cutoff=potential%cutoff_radius, calculate_forces=.false.)
2481 CALL int_operators_r12_ab_os(operator_coulomb, m_pq, rab=center_b - center_a, &
2482 fba=basis_a, fbb=basis_b, calculate_forces=.false.)
2483 CASE DEFAULT
2484 cpabort("AUTO_RI two-center metric requires COULOMB or TRUNCATED")
2485 END SELECT
2486
2487 CALL timestop(handle)
2488
2489 END SUBROUTINE compute_m_pq
2490
2491! **************************************************************************************************
2492!> \brief Returns the reference RI basis size of one atom.
2493!> \param bs_env ...
2494!> \param iatom ...
2495!> \return ...
2496! **************************************************************************************************
2497 INTEGER FUNCTION get_ref_ri_size(bs_env, iatom) RESULT(nRI_ref)
2498 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2499 INTEGER, INTENT(IN) :: iatom
2500
2501 INTEGER :: ikind
2502
2503 ikind = bs_env%ri_rs%particle_set(iatom)%atomic_kind%kind_number
2504 nri_ref = bs_env%basis_set_RI(ikind)%gto_basis_set%nsgf
2505
2506 END FUNCTION get_ref_ri_size
2507
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
Contraction of integrals over primitive Cartesian Gaussians based on the contraction matrix sphi whic...
subroutine, public abc_contract(abcint, sabc, sphi_a, sphi_b, sphi_c, ncoa, ncob, ncoc, nsgfa, nsgfb, nsgfc)
contract three-center overlap integrals (a,b,c) and transfer to spherical Gaussians
subroutine, public ab_contract(abint, sab, sphi_a, sphi_b, ncoa, ncob, nsgfa, nsgfb)
contract overlap integrals (a,b) and transfer to spherical Gaussians
Definition atom.F:9
Define the atomic kind types and their sub types.
subroutine, public get_atomic_kind_set(atomic_kind_set, atom_of_kind, kind_of, natom_of_kind, maxatom, natom, nshell, fist_potential_present, shell_present, shell_adiabatic, shell_check_distance, damping_present)
Get attributes of an atomic kind set.
subroutine, public get_gto_basis_set(gto_basis_set, name, aliases, norm_type, kind_radius, ncgf, nset, nsgf, cgf_symbol, sgf_symbol, norm_cgf, set_radius, lmax, lmin, lx, ly, lz, m, ncgf_set, npgf, nsgf_set, nshell, cphi, pgf_radius, sphi, scon, zet, first_cgf, first_sgf, l, last_cgf, last_sgf, n, gcc, maxco, maxl, maxpgf, maxsgf_set, maxshell, maxso, nco_sum, npgf_sum, nshell_sum, maxder, short_kind_radius, npgf_seg_sum, ccon)
...
constants for the different operators of the 2c-integrals
integer, parameter, public operator_truncated
integer, parameter, public operator_coulomb
Common interface for two- and three-center Coulomb integrals.
subroutine, public coulomb_integral_cleanup(context)
Release the engines initialized in a Coulomb integral context.
subroutine, public compute_coulomb_3c(context, la_min, la_max, lb_min, lb_max, lc_min, lc_max, npgfa, npgfb, npgfc, zeta, zetb, zetc, rpgfa, rpgfb, rpgfc, ra, rb, rc, habc, potential_parameter)
Evaluate one uncontracted three-center Coulomb integral block.
subroutine, public coulomb_integral_init(context, max_am_2c, max_am_3c)
Initialize the engines needed by the requested Coulomb integral operations.
subroutine, public compute_coulomb_2c(context, la_min, la_max, lb_min, lb_max, npgfa, npgfb, zeta, zetb, rpgfa, rpgfb, ra, rb, hab, potential_parameter)
Evaluate one uncontracted two-center Coulomb integral block.
methods related to the blacs parallel environment
subroutine, public dbcsr_distribution_release(dist)
...
subroutine, public dbcsr_distribution_new(dist, template, group, pgrid, row_dist, col_dist, reuse_arrays)
...
subroutine, public dbcsr_finalize(matrix)
...
subroutine, public dbcsr_release(matrix)
...
subroutine, public dbcsr_put_block(matrix, row, col, block, summation)
...
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)
...
DBCSR operations in CP2K.
subroutine, public copy_dbcsr_to_fm(matrix, fm)
Copy a DBCSR matrix to a BLACS matrix.
Utility routines to open and close files. Tracking of preconnections.
Definition cp_files.F:16
subroutine, public open_file(file_name, file_status, file_form, file_action, file_position, file_pad, unit_number, debug, skip_get_unit_number, file_access)
Opens the requested file using a free unit number.
Definition cp_files.F:311
character(len=default_path_length) function, public discover_file(file_name)
Checks various locations for a file name.
Definition cp_files.F:521
subroutine, public close_file(unit_number, file_status, keep_preconnection)
Close an open file given by its logical unit number. Optionally, keep the file and unit preconnected.
Definition cp_files.F:122
Basic linear algebra operations for full matrices.
subroutine, public cp_fm_column_scale(matrixa, scaling)
scales column i of matrix a with scaling(i)
subroutine, public cp_fm_syrk(uplo, trans, k, alpha, matrix_a, ia, ja, beta, matrix_c)
performs a rank-k update of a symmetric matrix_c matrix_c = beta * matrix_c + alpha * matrix_a * tran...
subroutine, public cp_fm_uplo_to_full(matrix, work, uplo)
given a triangular matrix according to uplo, computes the corresponding full matrix
used for collecting some of the diagonalization schemes available for cp_fm_type. cp_fm_power also mo...
Definition cp_fm_diag.F:17
subroutine, public cp_fm_geeig(amatrix, bmatrix, eigenvectors, eigenvalues, work)
General Eigenvalue Problem AX = BXE. Use cuSOLVERMp directly when requested and large enough; otherwi...
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_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,...
Calculation of contracted, spherical Gaussian integrals using the (OS) integral scheme....
subroutine, public int_operators_r12_ab_os(r12_operator, vab, dvab, rab, fba, fbb, omega, r_cutoff, calculate_forces)
Calcululates the two-center integrals of the type (a|O(r12)|b) using the OS scheme.
Automatic RI basis set optimization for molecular GW.
subroutine, public generate_auto_ri_basis(qs_env, bs_env)
Executes the AUTO_RI algorithm defined by Eqs. (1)-(14).
Input and persistent data for automatic RI basis optimization.
Utility method to build 3-center integrals for small cell GW.
subroutine, public build_3c_integral_block_ctx(int_3c, ctx, ws, atom_j, atom_k, atom_i, cell_j, cell_k, cell_i, j_offset, k_offset, i_offset, screened)
Computes the 3c integral block (mu(atom_j) nu(atom_k) | P(atom_i)) for ONE atom triple,...
subroutine, public gw_3c_ws_create(ws, ctx)
Creates a per-thread 3c workspace: libint object + LIBXSMM contraction buffers.
subroutine, public gw_3c_ws_release(ws)
Releases a per-thread 3c workspace.
subroutine, public gw_3c_ctx_release(ctx)
Releases the shared 3c-integral context.
subroutine, public gw_3c_ctx_create(ctx, qs_env, potential_parameter, basis_j, basis_k, basis_i)
Builds the shared 3c-integral context: screening radii, basis maxima, contracted sphi tables,...
collects all constants needed in input so that they can be used without circular dependencies
integer, parameter, public do_potential_truncated
integer, parameter, public small_cell_full_kp
integer, parameter, public do_potential_coulomb
Library choices for electronic integral APIs.
type(integral_library_type), save, public active_integral_library
integer, parameter, public library_libint
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
integer, parameter, public default_path_length
Definition kinds.F:58
2- and 3-center electron repulsion integral routines based on libint2 Currently available operators: ...
An array-based list which grows on demand. When the internal array is full, a new array of twice the ...
Definition list.F:24
integer, parameter, public local_gemm_pu_host
Machine interface based on Fortran 2003 and POSIX.
Definition machine.F:17
real(kind=dp) function, public m_walltime()
returns time from a real-time clock, protected against rolling early/easily
Definition machine.F:141
Collection of simple mathematical functions and subroutines.
Definition mathlib.F:15
subroutine, public diamat_all(a, eigval, dac)
Diagonalize the symmetric n by n matrix a using the LAPACK library. Only the upper triangle of matrix...
Definition mathlib.F:381
Interface to the message passing library MPI.
Provides Cartesian and spherical orbital pointers and indices.
subroutine, public init_orbital_pointers(maxl)
Initialize or update the orbital pointers.
integer, dimension(:), allocatable, public ncoset
Calculation of the spherical harmonics and the corresponding orbital transformation matrices.
subroutine, public init_spherical_harmonics(maxl, output_unit)
Initialize or update the orbital transformation matrices.
subroutine, public get_qs_env(qs_env, atomic_kind_set, qs_kind_set, cell, super_cell, cell_ref, use_ref_cell, kpoints, dft_control, mos, sab_orb, sab_all, qmmm, qmmm_periodic, mimic, sac_ae, sac_ppl, sac_lri, sap_ppnl, sab_vdw, sab_scp, sap_oce, sab_lrc, sab_se, sab_xtbe, sab_tbe, sab_core, sab_xb, sab_xtb_pp, sab_xtb_nonbond, sab_almo, sab_kp, sab_kp_nosym, sab_cneo, particle_set, energy, force, matrix_h, matrix_h_im, matrix_ks, matrix_ks_im, matrix_vxc, run_rtp, rtp, matrix_h_kp, matrix_h_im_kp, matrix_ks_kp, matrix_ks_im_kp, matrix_vxc_kp, kinetic_kp, matrix_s_kp, matrix_w_kp, matrix_s_ri_aux_kp, matrix_s, matrix_s_ri_aux, matrix_w, matrix_p_mp2, matrix_p_mp2_admm, matrix_vhxc, rho, rho_xc, pw_env, ewald_env, ewald_pw, active_space, mpools, input, para_env, blacs_env, scf_control, rel_control, kinetic, qs_charges, vppl, xcint_weights, rho_core, rho_nlcc, rho_nlcc_g, ks_env, ks_qmmm_env, wf_history, scf_env, local_particles, local_molecules, distribution_2d, dbcsr_dist, molecule_kind_set, molecule_set, subsys, cp_subsys, oce, local_rho_set, rho_atom_set, task_list, task_list_soft, rho0_atom_set, rho0_mpole, rhoz_set, rhoz_cneo_set, ecoul_1c, rho0_s_rs, rho0_s_gs, rhoz_cneo_s_rs, rhoz_cneo_s_gs, do_kpoints, has_unit_metric, requires_mo_derivs, mo_derivs, mo_loc_history, nkind, natom, nelectron_total, nelectron_spin, efield, neighbor_list_id, linres_control, xas_env, virial, cp_ddapc_env, cp_ddapc_ewald, outer_scf_history, outer_scf_ihistory, x_data, et_coupling, dftb_potential, results, se_taper, se_store_int_env, se_nddo_mpole, se_nonbond_env, admm_env, lri_env, lri_density, exstate_env, ec_env, harris_env, dispersion_env, gcp_env, vee, rho_external, external_vxc, mask, mp2_env, bs_env, kg_env, wanniercentres, atprop, ls_scf_env, do_transport, transport_env, v_hartree_rspace, s_mstruct_changed, rho_changed, potential_changed, forces_up_to_date, mscfg_env, almo_scf_env, gradient_history, variable_history, embed_pot, spin_embed_pot, polar_env, mos_last_converged, eeq, rhs, do_rixs, tb_tblite)
Get the QUICKSTEP environment.
This module computes the basic integrals for the truncated coulomb operator.
Definition t_c_g0.F:58
subroutine, public init(nder, iunit, mepos, group)
...
Definition t_c_g0.F:1361
integer function, public get_lmax_init()
Returns the value of nderiv_init so that one can check if opening the potential file is worhtwhile.
Definition t_c_g0.F:1468
All kind of helpful little routines.
Definition util.F:14
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
Shared read-only context for repeated 3-center integral block builds: screening parameters,...
Per-thread workspace for 3-center integral block builds: libint object + contraction buffers....
stores all the informations relevant to an mpi environment