LCOV - code coverage report
Current view: top level - src - bse_main.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 93.8 % 192 180
Test Date: 2026-10-15 00:55:44 Functions: 100.0 % 9 9

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief Main routines for GW + Bethe-Salpeter for computing electronic excitations: a GW path
      10              : !>        prepares the BSE environment, bse_solve runs the solver on it
      11              : !> \par History
      12              : !>      04.2024 created [Maximilian Graml]
      13              : !>      09.2026 split into prepare_bse_env and bse_solve [Maximilian Graml]
      14              : ! **************************************************************************************************
      15              : 
      16              : MODULE bse_main
      17              : 
      18              :    USE bse_full_diag,                   ONLY: create_A_and_B,&
      19              :                                               create_hermitian_form_of_ABBA,&
      20              :                                               diagonalize_A,&
      21              :                                               diagonalize_C
      22              :    USE bse_input,                       ONLY: read_bse_section
      23              :    USE bse_iterative,                   ONLY: solve_bse_iteratively
      24              :    USE bse_matvec,                      ONLY: bse_memory_left_GB,&
      25              :                                               bse_resolve_memory_per_proc
      26              :    USE bse_print,                       ONLY: print_BSE_start_flag
      27              :    USE bse_types,                       ONLY: bse_env_type
      28              :    USE bse_util,                        ONLY: adapt_BSE_input_params,&
      29              :                                               bse_window_from_dft,&
      30              :                                               bse_window_from_mos,&
      31              :                                               estimate_BSE_resources,&
      32              :                                               fulldiag_memory_estimate_GB,&
      33              :                                               get_bse_spin_block_layout,&
      34              :                                               screen_slabs,&
      35              :                                               truncate_BSE_matrices
      36              :    USE cp_control_types,                ONLY: tddfpt2_control_type
      37              :    USE cp_fm_basic_linalg,              ONLY: cp_fm_add_on_diag,&
      38              :                                               cp_fm_scale
      39              :    USE cp_fm_cholesky,                  ONLY: cp_fm_cholesky_decompose,&
      40              :                                               cp_fm_cholesky_invert
      41              :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      42              :                                               cp_fm_release,&
      43              :                                               cp_fm_to_fm,&
      44              :                                               cp_fm_type
      45              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      46              :                                               cp_logger_type
      47              :    USE cp_output_handling,              ONLY: debug_print_level
      48              :    USE input_constants,                 ONLY: bse_abba,&
      49              :                                               bse_both,&
      50              :                                               bse_fulldiag,&
      51              :                                               bse_iterdiag,&
      52              :                                               bse_screening_w0,&
      53              :                                               bse_tda
      54              :    USE input_section_types,             ONLY: section_vals_get_subs_vals,&
      55              :                                               section_vals_type
      56              :    USE kinds,                           ONLY: dp
      57              :    USE message_passing,                 ONLY: mp_para_env_type
      58              :    USE mo_window,                       ONLY: mo_window_type
      59              :    USE pw_poisson_read_input,           ONLY: pw_poisson_read_parameters
      60              :    USE pw_poisson_types,                ONLY: pw_poisson_parameter_type
      61              :    USE qs_environment_types,            ONLY: get_qs_env,&
      62              :                                               qs_environment_type
      63              :    USE qs_mo_types,                     ONLY: get_mo_set,&
      64              :                                               mo_set_type
      65              :    USE qs_moments,                      ONLY: get_multipoles_ao
      66              : #include "./base/base_uses.f90"
      67              : 
      68              :    IMPLICIT NONE
      69              : 
      70              :    PRIVATE
      71              : 
      72              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'bse_main'
      73              : 
      74              :    PUBLIC :: prepare_bse_env_from_ri_rpa_gw, prepare_bse_env_from_bandstructure_gw, bse_solve, &
      75              :              bse_fulldiag_memory_check, bse_fulldiag_memory_check_from_mos
      76              : 
      77              : CONTAINS
      78              : 
      79              : ! **************************************************************************************************
      80              : !> \brief Entry for the BSE of RI_RPA%GW, which hands over the slabs of all its GW levels and the
      81              : !>        static polarizability Q(0): re-reads the settings, cuts the window on the DFT axis out of
      82              : !>        the slabs and both energy sets, and inverts 1+Q(0). Starts no solver.
      83              : !> \param bse_env settings re-read from its section; state filled here
      84              : !> \param fm_S_ia_full B^P_ia of the GW levels per spin, the RPA copy without the energy factor
      85              : !> \param fm_S_ij_full B^P_ij per spin
      86              : !> \param fm_S_ab_full B^P_ab per spin
      87              : !> \param fm_Q the static polarizability Q(0), untouched; the caller releases it afterwards
      88              : !> \param Eigenval quasiparticle energies (level, 1, spin)
      89              : !> \param Eigenval_scf DFT energies (level, 1, spin)
      90              : !> \param homo occupied MOs per spin
      91              : !> \param virtual virtual MOs per spin
      92              : !> \param dimen_RI RI dimension of the slabs
      93              : !> \param dimen_RI_red reduced RI dimension of Q(0)
      94              : !> \param gw_corr_lev_occ GW-corrected occupied levels; the window may not exceed them
      95              : !> \param bse_lev_virt GW-corrected virtual levels per spin
      96              : !> \param qs_env source of the pointers the solver reads and of the AO multipoles
      97              : !> \param unit_nr output unit, positive on the writing rank only
      98              : ! **************************************************************************************************
      99          112 :    SUBROUTINE prepare_bse_env_from_ri_rpa_gw(bse_env, fm_S_ia_full, fm_S_ij_full, fm_S_ab_full, fm_Q, &
     100          168 :                                              Eigenval, Eigenval_scf, homo, virtual, dimen_RI, dimen_RI_red, &
     101           56 :                                              gw_corr_lev_occ, bse_lev_virt, qs_env, unit_nr)
     102              : 
     103              :       TYPE(bse_env_type), INTENT(INOUT)                  :: bse_env
     104              :       TYPE(cp_fm_type), DIMENSION(:), INTENT(IN)         :: fm_S_ia_full, fm_S_ij_full, fm_S_ab_full
     105              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm_Q
     106              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(IN)      :: Eigenval, Eigenval_scf
     107              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: homo, virtual
     108              :       INTEGER, INTENT(IN)                                :: dimen_RI, dimen_RI_red, gw_corr_lev_occ
     109              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: bse_lev_virt
     110              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     111              :       INTEGER, INTENT(IN)                                :: unit_nr
     112              : 
     113              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'prepare_bse_env_from_ri_rpa_gw'
     114              : 
     115              :       INTEGER                                            :: handle, ispin, n_window, nspins
     116              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: n_mo
     117           56 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenval_dft_spin, eigenval_qp_spin
     118              :       TYPE(mo_window_type)                               :: window
     119              :       TYPE(section_vals_type), POINTER                   :: bse_section
     120              : 
     121           56 :       CALL timeset(routineN, handle)
     122              : 
     123              :       ! the settings afresh from the input, since adapt_BSE_input_params normalises them in place and a
     124              :       ! later energy evaluation on the same environment runs the BSE again; the local pointer keeps the
     125              :       ! section apart from bse_env, whose section pointer the reader sets
     126           56 :       bse_section => bse_env%bse_section
     127           56 :       CALL read_bse_section(bse_section, bse_env)
     128           56 :       CALL bse_resolve_memory_per_proc(bse_env%memory_per_proc_gb, fm_S_ia_full(1)%matrix_struct%para_env, unit_nr)
     129              : 
     130           56 :       nspins = SIZE(homo)
     131              :       ! the GW-corrected level counts are the ceiling of the window
     132           56 :       CALL start_bse_env(bse_env, nspins, dimen_RI, dimen_RI_red, gw_corr_lev_occ, bse_lev_virt, unit_nr)
     133              : 
     134              :       ! one absolute window on the DFT axis, then the window-sized slabs and both energy sets per spin
     135          168 :       ALLOCATE (n_mo(nspins))
     136          124 :       n_mo(:) = homo(:) + virtual(:)
     137              :       CALL bse_window_from_dft(Eigenval_scf(:, 1, :), n_mo, homo, bse_env%bse_cutoff_occ, &
     138           56 :                                bse_env%bse_cutoff_empty, window, warn=.TRUE.)
     139           56 :       DEALLOCATE (n_mo)
     140              : 
     141          224 :       ALLOCATE (bse_env%homo_red(nspins), bse_env%virt_red(nspins), bse_env%homo_full(nspins))
     142          484 :       ALLOCATE (bse_env%fm_S_ia(nspins), bse_env%fm_S_ij(nspins), bse_env%fm_S_ab(nspins))
     143          124 :       bse_env%homo_full(:) = homo(:)
     144          124 :       DO ispin = 1, nspins
     145              :          ! the cutoff summary is printed for a closed-shell reference only
     146              :          CALL truncate_BSE_matrices(fm_S_ia_full(ispin), fm_S_ij_full(ispin), fm_S_ab_full(ispin), &
     147              :                                     Eigenval_scf(:, 1, ispin), Eigenval(:, 1, ispin), &
     148              :                                     eigenval_dft_spin, eigenval_qp_spin, homo(ispin), virtual(ispin), &
     149           68 :                                     dimen_RI, unit_nr, ispin, bse_env, window, nspins == 1)
     150              :          ! the window is one absolute range, so every spin has the same number of levels
     151           68 :          IF (ispin == 1) THEN
     152           56 :             n_window = SIZE(eigenval_qp_spin)
     153          336 :             ALLOCATE (bse_env%eigenval_dft(n_window, nspins), bse_env%eigenval_qp(n_window, nspins))
     154              :          END IF
     155         1870 :          bse_env%eigenval_dft(:, ispin) = eigenval_dft_spin(:)
     156         1870 :          bse_env%eigenval_qp(:, ispin) = eigenval_qp_spin(:)
     157          124 :          DEALLOCATE (eigenval_dft_spin, eigenval_qp_spin)
     158              :       END DO
     159              : 
     160           56 :       CALL invert_static_screening(fm_Q, dimen_RI_red, bse_env%fm_eps_inv)
     161              : 
     162           56 :       IF (bse_env%wc_scaling /= 1.0_dp) THEN
     163            4 :          IF (bse_env%screening_method /= bse_screening_w0 .OR. bse_env%screening_factor > 0.0_dp) THEN
     164            0 :             CPABORT("SCREENING_IN_W other than W_0 cannot be combined with a scaled W^c (static COHSEX).")
     165              :          END IF
     166            4 :          CALL scale_static_screening(bse_env%fm_eps_inv, dimen_RI_red, bse_env%wc_scaling)
     167            4 :          IF (unit_nr > 0) THEN
     168            2 :             WRITE (unit_nr, '(T2,A10,T13,A,T71,F10.4)') 'BSE|', &
     169            4 :                'Direct term W = v + alpha W^c(0), alpha =', bse_env%wc_scaling
     170              :          END IF
     171              :       END IF
     172              : 
     173           56 :       CALL finish_bse_env(bse_env, qs_env, fm_S_ia_full(1)%matrix_struct%para_env)
     174              : 
     175           56 :       CALL timestop(handle)
     176              : 
     177          112 :    END SUBROUTINE prepare_bse_env_from_ri_rpa_gw
     178              : 
     179              : ! **************************************************************************************************
     180              : !> \brief Entry for the BSE of PROPERTIES%BANDSTRUCTURE%GW (PROPERTIES%BSE), which hands over
     181              : !>        window-sized slabs and a ready inverse dielectric matrix: nothing is cut or inverted, the
     182              : !>        slabs are taken over as they are. Shares start_bse_env and finish_bse_env with
     183              : !>        prepare_bse_env_from_ri_rpa_gw. Starts no solver.
     184              : !> \param bse_env settings read from its section by the caller; state filled here
     185              : !> \param fm_S_ia B^P_ia of the window per spin; moved into bse_env, deallocated on return
     186              : !> \param fm_S_ij B^P_ij of the window per spin; moved into bse_env, deallocated on return
     187              : !> \param fm_S_ab B^P_ab of the window per spin; moved into bse_env, deallocated on return
     188              : !> \param fm_eps_inv the inverse dielectric matrix ε^-1_PQ the core screens the slabs with,
     189              : !>        \bar{B}^P_ij = sum_Q ε^-1_PQ B^Q_ij; RI x RI, copied; stays the caller's
     190              : !> \param eigenval_qp quasiparticle energies of the window (level, spin)
     191              : !> \param eigenval_dft DFT energies of the window (level, spin)
     192              : !> \param homo_full occupied MOs per spin of the full reference
     193              : !> \param homo_red occupied levels in the window per spin
     194              : !> \param virt_red virtual levels in the window per spin
     195              : !> \param dimen_RI RI dimension of the slabs and of fm_eps_inv
     196              : !> \param qs_env source of the pointers the solver reads and of the AO multipoles
     197              : !> \param unit_nr output unit, positive on the writing rank only
     198              : ! **************************************************************************************************
     199            6 :    SUBROUTINE prepare_bse_env_from_bandstructure_gw(bse_env, fm_S_ia, fm_S_ij, fm_S_ab, fm_eps_inv, &
     200            6 :                                                     eigenval_qp, eigenval_dft, homo_full, homo_red, virt_red, &
     201              :                                                     dimen_RI, qs_env, unit_nr)
     202              : 
     203              :       TYPE(bse_env_type), INTENT(INOUT)                  :: bse_env
     204              :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:), &
     205              :          INTENT(INOUT)                                   :: fm_S_ia, fm_S_ij, fm_S_ab
     206              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm_eps_inv
     207              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: eigenval_qp, eigenval_dft
     208              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: homo_full, homo_red, virt_red
     209              :       INTEGER, INTENT(IN)                                :: dimen_RI
     210              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     211              :       INTEGER, INTENT(IN)                                :: unit_nr
     212              : 
     213              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'prepare_bse_env_from_bandstructure_gw'
     214              : 
     215              :       INTEGER                                            :: handle, ispin, n_window, nspins
     216              : 
     217            6 :       CALL timeset(routineN, handle)
     218              : 
     219              :       ! the caller (bse_from_bs_env) read the settings afresh from the input; the entry of RI_RPA%GW reads them itself
     220            6 :       IF (.NOT. ASSOCIATED(bse_env%bse_section)) CPABORT("BSE settings not read: read_bse_section comes first")
     221              : 
     222            6 :       nspins = SIZE(homo_full)
     223            6 :       IF (SIZE(fm_S_ia) /= nspins .OR. SIZE(eigenval_qp, 2) /= nspins) THEN
     224            0 :          CPABORT("BSE needs one set of RI integrals and one set of energies per spin")
     225              :       END IF
     226              :       ! every window level carries a GW energy here, and the inverse dielectric matrix has the full RI dimension
     227           14 :       CALL start_bse_env(bse_env, nspins, dimen_RI, dimen_RI, MAXVAL(homo_red), virt_red, unit_nr)
     228              : 
     229           30 :       ALLOCATE (bse_env%homo_red(nspins), bse_env%virt_red(nspins), bse_env%homo_full(nspins))
     230           14 :       bse_env%homo_full(:) = homo_full(:)
     231           14 :       bse_env%homo_red(:) = homo_red(:)
     232           14 :       bse_env%virt_red(:) = virt_red(:)
     233            6 :       n_window = SIZE(eigenval_qp, 1)
     234           14 :       DO ispin = 1, nspins
     235              :          ! one absolute window for every spin: its occupied and virtual levels fill it
     236           14 :          IF (homo_red(ispin) + virt_red(ispin) /= n_window) THEN
     237            0 :             CPABORT("Occupied and virtual levels do not fill the BSE window")
     238              :          END IF
     239              :       END DO
     240           36 :       ALLOCATE (bse_env%eigenval_dft(n_window, nspins), bse_env%eigenval_qp(n_window, nspins))
     241          142 :       bse_env%eigenval_dft(:, :) = eigenval_dft(:, :)
     242          142 :       bse_env%eigenval_qp(:, :) = eigenval_qp(:, :)
     243              : 
     244              :       ! the slabs are window-sized already: take them over
     245            6 :       CALL MOVE_ALLOC(fm_S_ia, bse_env%fm_S_ia)
     246            6 :       CALL MOVE_ALLOC(fm_S_ij, bse_env%fm_S_ij)
     247            6 :       CALL MOVE_ALLOC(fm_S_ab, bse_env%fm_S_ab)
     248            6 :       CALL cp_fm_create(bse_env%fm_eps_inv, fm_eps_inv%matrix_struct)
     249            6 :       CALL cp_fm_to_fm(fm_eps_inv, bse_env%fm_eps_inv)
     250              : 
     251            6 :       CALL finish_bse_env(bse_env, qs_env, bse_env%fm_S_ia(1)%matrix_struct%para_env)
     252              : 
     253            6 :       CALL timestop(handle)
     254              : 
     255            6 :    END SUBROUTINE prepare_bse_env_from_bandstructure_gw
     256              : 
     257              : ! **************************************************************************************************
     258              : !> \brief The part both entries share before their window: the sizes, the run flags from the
     259              : !>        settings, the start banner and the debug flag
     260              : !> \param bse_env settings read from its section; sizes and flags set here
     261              : !> \param nspins spin channels
     262              : !> \param dimen_RI RI dimension of the slabs
     263              : !> \param dimen_RI_red RI dimension of the inverse dielectric matrix
     264              : !> \param gw_corr_lev_occ GW-corrected occupied levels
     265              : !> \param gw_corr_lev_virt GW-corrected virtual levels per spin
     266              : !> \param unit_nr output unit, positive on the writing rank only
     267              : ! **************************************************************************************************
     268           62 :    SUBROUTINE start_bse_env(bse_env, nspins, dimen_RI, dimen_RI_red, gw_corr_lev_occ, gw_corr_lev_virt, unit_nr)
     269              : 
     270              :       TYPE(bse_env_type), INTENT(INOUT)                  :: bse_env
     271              :       INTEGER, INTENT(IN)                                :: nspins, dimen_RI, dimen_RI_red, &
     272              :                                                             gw_corr_lev_occ
     273              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: gw_corr_lev_virt
     274              :       INTEGER, INTENT(IN)                                :: unit_nr
     275              : 
     276              :       TYPE(cp_logger_type), POINTER                      :: logger
     277              : 
     278           62 :       bse_env%nspins = nspins
     279           62 :       bse_env%dimen_RI = dimen_RI
     280           62 :       bse_env%dimen_RI_red = dimen_RI_red
     281           62 :       bse_env%gw_corr_lev_occ = gw_corr_lev_occ
     282          186 :       ALLOCATE (bse_env%gw_corr_lev_virt(nspins))
     283          138 :       bse_env%gw_corr_lev_virt(:) = gw_corr_lev_virt(:)
     284              : 
     285              :       ! run flags from the settings
     286           62 :       bse_env%do_iterdiag = bse_env%bse_diag_method == bse_iterdiag
     287           62 :       bse_env%do_fulldiag = bse_env%bse_diag_method == bse_fulldiag
     288           62 :       bse_env%do_tda = bse_env%flag_tda == bse_tda .OR. bse_env%flag_tda == bse_both
     289           62 :       bse_env%do_abba = bse_env%flag_tda == bse_abba .OR. bse_env%flag_tda == bse_both
     290              : 
     291           62 :       CALL print_BSE_start_flag(bse_env%do_tda, bse_env%do_abba, unit_nr)
     292              : 
     293              :       ! the debug flag follows the debug print level
     294           62 :       logger => cp_get_default_logger()
     295           62 :       IF (logger%iter_info%print_level == debug_print_level) THEN
     296            0 :          bse_env%bse_debug_print = .TRUE.
     297              :       END IF
     298              : 
     299           62 :    END SUBROUTINE start_bse_env
     300              : 
     301              : ! **************************************************************************************************
     302              : !> \brief The part both entries share once the window and the slabs are in place: the
     303              : !>        QS pointers, the open-shell checks, the spin-block layout and the AO multipoles
     304              : !> \param bse_env state filled here
     305              : !> \param qs_env source of the pointers the solver reads and of the AO multipoles
     306              : !> \param para_env_slabs the communicator the slabs live on
     307              : ! **************************************************************************************************
     308           62 :    SUBROUTINE finish_bse_env(bse_env, qs_env, para_env_slabs)
     309              : 
     310              :       TYPE(bse_env_type), INTENT(INOUT)                  :: bse_env
     311              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     312              :       TYPE(mp_para_env_type), POINTER                    :: para_env_slabs
     313              : 
     314              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'finish_bse_env'
     315              : 
     316              :       INTEGER                                            :: handle, nspins
     317           62 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: n_ov, offsets
     318              :       TYPE(pw_poisson_parameter_type)                    :: poisson_params
     319              :       TYPE(section_vals_type), POINTER                   :: input, poisson_section
     320              : 
     321           62 :       CALL timeset(routineN, handle)
     322              : 
     323           62 :       nspins = bse_env%nspins
     324              : 
     325              :       ! what the solver reads from QS and the GW (pointers, none owned)
     326           62 :       bse_env%para_env => para_env_slabs
     327              :       CALL get_qs_env(qs_env, subsys=bse_env%subsys, pw_env=bse_env%pw_env, &
     328              :                       dft_control=bse_env%dft_control, mos=bse_env%mos, &
     329           62 :                       exstate_env=bse_env%exstate_env)
     330              :       ! the Poisson periodicity from the input: PROPERTIES%BSE runs after the GW released the grids
     331           62 :       NULLIFY (input, poisson_section)
     332           62 :       CALL get_qs_env(qs_env, input=input)
     333           62 :       poisson_section => section_vals_get_subs_vals(input, "DFT%POISSON")
     334           62 :       CALL pw_poisson_read_parameters(poisson_section, poisson_params)
     335          248 :       bse_env%n_periodic_poisson = COUNT(poisson_params%periodic == 1)
     336           62 :       CALL bse_env%para_env%sync()
     337              : 
     338           62 :       IF (nspins > 1) THEN
     339           14 :          IF (bse_env%do_iterdiag) THEN
     340            0 :             CPABORT("Iterative BSE is not yet available for open-shell references")
     341              :          END IF
     342              :          CALL cp_warn(__LOCATION__, &
     343              :                       "Open-shell (UKS/LSD) BSE is a recent addition and has not been "// &
     344              :                       "extensively validated. Verify results carefully before using them "// &
     345           14 :                       "for production calculations.")
     346              :       END IF
     347              : 
     348          248 :       ALLOCATE (n_ov(nspins), offsets(nspins))
     349           62 :       CALL get_bse_spin_block_layout(bse_env%homo_red, bse_env%virt_red, n_ov, offsets, bse_env%n_ov_joint)
     350           62 :       DEALLOCATE (n_ov, offsets)
     351              : 
     352              :       ! the AO multipoles for the transition dipoles and, when asked for, the exciton descriptors;
     353              :       ! the open-shell path computes no descriptors
     354           62 :       CALL get_multipoles_ao(qs_env, 1, bse_env%matrix_dipole_ao, bse_env%multipole_ref_point)
     355           62 :       IF (bse_env%num_print_exc_descr /= 0 .AND. nspins == 1) THEN
     356            4 :          CALL get_multipoles_ao(qs_env, 2, bse_env%matrix_quadpole_ao, bse_env%multipole_ref_point)
     357              :       END IF
     358              : 
     359           62 :       CALL timestop(handle)
     360              : 
     361          248 :    END SUBROUTINE finish_bse_env
     362              : 
     363              : ! **************************************************************************************************
     364              : !> \brief Scale the correlation part of the static screened Coulomb interaction in the RI kernel
     365              : !>
     366              : !>        The direct terms of the BSE use (pq|W|rs) = Σ_PQ B^P_pq K_PQ B^Q_rs with the RI kernel
     367              : !>        K = ε^(-1)(0). With W = v + α W^c(0), W^c(0) = (ε^(-1)(0) - 1) v, the kernel becomes
     368              : !>          K_PQ = δ_PQ + α (ε^(-1)_PQ(0) - δ_PQ) = α ε^(-1)_PQ(0) + (1 - α) δ_PQ
     369              : !>        such that
     370              : !>          A_ia,jb = (ϵ_a - ϵ_i) δ_ij δ_ab + 2 (ia|jb) - (ij|ab) - α (ij|W^c|ab)
     371              : !>          B_ia,jb = 2 (ia|jb) - (ib|ja) - α (ib|W^c|ja)
     372              : !>        α = 1: standard BSE@W0, α = 0: unscreened (TDHF-like) kernel.
     373              : !> \param fm_eps_inv ...
     374              : !> \param dimen_RI_red ...
     375              : !> \param alpha ...
     376              : ! **************************************************************************************************
     377            4 :    SUBROUTINE scale_static_screening(fm_eps_inv, dimen_RI_red, alpha)
     378              : 
     379              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: fm_eps_inv
     380              :       INTEGER, INTENT(IN)                                :: dimen_RI_red
     381              :       REAL(KIND=dp), INTENT(IN)                          :: alpha
     382              : 
     383              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'scale_static_screening'
     384              : 
     385              :       INTEGER                                            :: handle
     386              : 
     387            4 :       CALL timeset(routineN, handle)
     388              : 
     389              :       ! K_PQ = α ε^(-1)_PQ + (1 - α) δ_PQ on the leading dimen_RI_red block
     390            4 :       CALL cp_fm_scale(alpha, fm_eps_inv)
     391            4 :       CALL cp_fm_add_on_diag(fm_eps_inv, 1.0_dp - alpha, dimen_RI_red)
     392              : 
     393            4 :       CALL timestop(handle)
     394              : 
     395            4 :    END SUBROUTINE scale_static_screening
     396              : 
     397              : ! **************************************************************************************************
     398              : !> \brief The inverse dielectric matrix in the RI basis, [1+Q(0)]^-1_PQ on the leading dimen_RI_red
     399              : !>        block, P, Q ≤ dimen_RI_red, by Cholesky decomposition and inversion of a copy of Q(0)
     400              : !> \param fm_Q the static polarizability Q_PQ(0), untouched
     401              : !> \param dimen_RI_red reduced RI dimension of Q(0)
     402              : !> \param fm_eps_inv [1+Q(0)]^-1_PQ, created here on fm_Q's structure
     403              : ! **************************************************************************************************
     404          224 :    SUBROUTINE invert_static_screening(fm_Q, dimen_RI_red, fm_eps_inv)
     405              : 
     406              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm_Q
     407              :       INTEGER, INTENT(IN)                                :: dimen_RI_red
     408              :       TYPE(cp_fm_type), INTENT(OUT)                      :: fm_eps_inv
     409              : 
     410              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'invert_static_screening'
     411              : 
     412              :       INTEGER                                            :: handle, info_chol
     413              :       TYPE(cp_fm_type)                                   :: fm_work
     414              : 
     415           56 :       CALL timeset(routineN, handle)
     416              : 
     417              :       ! a copy: the caller keeps Q(0), and the open-shell path inverts once for both spins
     418           56 :       CALL cp_fm_create(fm_eps_inv, fm_Q%matrix_struct)
     419           56 :       CALL cp_fm_to_fm(fm_Q, fm_eps_inv)
     420           56 :       CALL cp_fm_create(fm_work, fm_Q%matrix_struct)
     421              : 
     422              :       ! δ_PQ + Q_PQ(0) on the leading dimen_RI_red block
     423           56 :       CALL cp_fm_add_on_diag(fm_eps_inv, 1.0_dp, n_active=dimen_RI_red)
     424              : 
     425              :       ! [1+Q(0)]^-1_PQ by Cholesky, symmetrised from the stored triangle
     426           56 :       CALL cp_fm_cholesky_decompose(matrix=fm_eps_inv, n=dimen_RI_red, info_out=info_chol)
     427           56 :       IF (info_chol /= 0) THEN
     428            0 :          CALL cp_abort(__LOCATION__, 'Cholesky decomposition failed for static polarization in BSE')
     429              :       END IF
     430           56 :       CALL cp_fm_cholesky_invert(fm_eps_inv, n=dimen_RI_red, work=fm_work)
     431              : 
     432           56 :       CALL cp_fm_release(fm_work)
     433              : 
     434           56 :       CALL timestop(handle)
     435              : 
     436           56 :    END SUBROUTINE invert_static_screening
     437              : 
     438              : ! **************************************************************************************************
     439              : !> \brief Solves the BSE on a prepared environment: input normalisation, the screened slabs from
     440              : !>        [1+Q(0)]^-1, then the FULLDIAG or ITERDIAG solver on the energies USE_KS_ENERGIES selects
     441              : !> \param bse_env prepared by a GW path; the screened slabs are formed here
     442              : !> \param mo_coeff MO coefficients of the reference, per spin, for the properties
     443              : !> \param unit_nr output unit, positive on the writing rank only
     444              : ! **************************************************************************************************
     445           62 :    SUBROUTINE bse_solve(bse_env, mo_coeff, unit_nr)
     446              : 
     447              :       TYPE(bse_env_type), INTENT(INOUT)                  :: bse_env
     448              :       TYPE(cp_fm_type), DIMENSION(:), INTENT(IN)         :: mo_coeff
     449              :       INTEGER, INTENT(IN)                                :: unit_nr
     450              : 
     451              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'bse_solve'
     452              : 
     453              :       INTEGER                                            :: handle, ispin, n_window, nspins
     454              :       REAL(KIND=dp)                                      :: diag_runtime_est
     455              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenval_joint
     456              :       TYPE(cp_fm_type)                                   :: fm_A_BSE, fm_B_BSE, fm_C_BSE, &
     457              :                                                             fm_inv_sqrt_A_minus_B, &
     458              :                                                             fm_sqrt_A_minus_B
     459              :       TYPE(tddfpt2_control_type), POINTER                :: tddfpt_control
     460              : 
     461           62 :       CALL timeset(routineN, handle)
     462              : 
     463           62 :       nspins = bse_env%nspins
     464              : 
     465              :       ! normalisation touches no slab; n_ov_joint x 1 is the transition count on either spin path
     466           62 :       CALL adapt_BSE_input_params(bse_env%n_ov_joint, 1, unit_nr, bse_env)
     467              : 
     468              :       ! the energies the solver sees, flat joint layout [spin 1 window | spin 2 window]
     469           62 :       n_window = SIZE(bse_env%eigenval_qp, 1)
     470          186 :       ALLOCATE (eigenval_joint(n_window*nspins))
     471          138 :       DO ispin = 1, nspins
     472          138 :          IF (bse_env%use_ks_energies) THEN
     473          116 :             eigenval_joint((ispin - 1)*n_window + 1:ispin*n_window) = bse_env%eigenval_dft(:, ispin)
     474              :          ELSE
     475         1890 :             eigenval_joint((ispin - 1)*n_window + 1:ispin*n_window) = bse_env%eigenval_qp(:, ispin)
     476              :          END IF
     477              :       END DO
     478              : 
     479              :       ! \bar{B}^P_ij and \bar{B}^P_ia per spin from the one [1+Q(0)]^-1
     480          400 :       ALLOCATE (bse_env%fm_S_bar_ia(nspins), bse_env%fm_S_bar_ij(nspins))
     481          138 :       DO ispin = 1, nspins
     482              :          CALL screen_slabs(bse_env%fm_eps_inv, bse_env%fm_S_ij(ispin), bse_env%fm_S_ia(ispin), &
     483              :                            bse_env%dimen_RI_red, bse_env%homo_red(ispin), bse_env%virt_red(ispin), &
     484          138 :                            bse_env%fm_S_bar_ij(ispin), bse_env%fm_S_bar_ia(ispin))
     485              :       END DO
     486           62 :       CALL cp_fm_release(bse_env%fm_eps_inv)
     487              : 
     488           62 :       IF (bse_env%do_iterdiag) THEN
     489              :          CALL solve_bse_iteratively(eigenval_joint, &
     490              :                                     bse_env%homo_red, bse_env%virt_red, bse_env%homo_full, &
     491              :                                     bse_env%dimen_RI, bse_env%do_tda, bse_env%do_abba, &
     492            8 :                                     bse_env, mo_coeff, unit_nr)
     493              :       END IF
     494              : 
     495           62 :       IF (bse_env%do_fulldiag) THEN
     496              :          CALL estimate_BSE_resources(bse_env%n_ov_joint, unit_nr, bse_env%do_abba, &
     497           54 :                                      bse_env%para_env, diag_runtime_est)
     498              :          CALL create_A_and_B(bse_env%fm_S_ia, bse_env%fm_S_ij, bse_env%fm_S_ab, &
     499              :                              bse_env%fm_S_bar_ia, bse_env%fm_S_bar_ij, fm_A_BSE, fm_B_BSE, &
     500              :                              bse_env%do_abba, eigenval_joint, unit_nr, bse_env%homo_red, &
     501           54 :                              bse_env%virt_red, bse_env%dimen_RI, bse_env)
     502           54 :          IF (bse_env%do_abba) THEN
     503              :             CALL create_hermitian_form_of_ABBA(fm_A_BSE, fm_B_BSE, fm_C_BSE, &
     504              :                                                fm_sqrt_A_minus_B, fm_inv_sqrt_A_minus_B, &
     505           26 :                                                unit_nr, bse_env, diag_runtime_est)
     506              :          END IF
     507           54 :          CALL cp_fm_release(fm_B_BSE)
     508           54 :          tddfpt_control => bse_env%dft_control%tddfpt2_control
     509              :          ! homo_full: per-spin absolute MO labels for the amplitude table, N_e = SUM(homo_full) for TRK
     510           54 :          IF (bse_env%do_tda .AND. (.NOT. tddfpt_control%do_bse)) THEN
     511              :             CALL diagonalize_A(fm_A_BSE, bse_env%homo_red, bse_env%virt_red, bse_env%homo_full, &
     512           26 :                                unit_nr, diag_runtime_est, bse_env, mo_coeff)
     513              :          END IF
     514           54 :          CALL cp_fm_release(fm_A_BSE)
     515           54 :          IF (bse_env%do_abba) THEN
     516              :             CALL diagonalize_C(fm_C_BSE, bse_env%homo_red, bse_env%virt_red, bse_env%homo_full, &
     517              :                                fm_sqrt_A_minus_B, fm_inv_sqrt_A_minus_B, &
     518           26 :                                unit_nr, diag_runtime_est, bse_env, mo_coeff)
     519              :          END IF
     520           54 :          CALL cp_fm_release(fm_C_BSE)
     521              :       END IF
     522              : 
     523           62 :       DEALLOCATE (eigenval_joint)
     524              : 
     525           62 :       IF (unit_nr > 0) THEN
     526           31 :          WRITE (unit_nr, '(T2,A4,T7,A53)') 'BSE|', 'The BSE was successfully calculated. Have a nice day!'
     527              :       END IF
     528              : 
     529           62 :       CALL timestop(handle)
     530              : 
     531           62 :    END SUBROUTINE bse_solve
     532              : 
     533              : ! **************************************************************************************************
     534              : !> \brief Early memory check for the full diagonalization, before any GW work: the window on the
     535              : !>        DFT axis gives n_ov = sum_spin n_occ n_virt, the estimate is estimate_BSE_resources', and
     536              : !>        the run stops when it exceeds what MEMORY_PER_PROC leaves after the memory the rank holds,
     537              : !>        since a full diagonalization has nothing to shrink
     538              : !> \param bse_env settings; the TDA flag and MEMORY_PER_PROC, resolved here when still -1
     539              : !> \param window the active MO window on the DFT axis, one absolute range for every spin
     540              : !> \param homo occupied MOs per spin
     541              : !> \param para_env the ranks that will hold the matrices
     542              : !> \param unit_nr output unit, positive on the writing rank only
     543              : ! **************************************************************************************************
     544           54 :    SUBROUTINE bse_fulldiag_memory_check(bse_env, window, homo, para_env, unit_nr)
     545              :       TYPE(bse_env_type), INTENT(INOUT)                  :: bse_env
     546              :       TYPE(mo_window_type), INTENT(IN)                   :: window
     547              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: homo
     548              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     549              :       INTEGER, INTENT(IN)                                :: unit_nr
     550              : 
     551              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'bse_fulldiag_memory_check'
     552              : 
     553              :       CHARACTER(LEN=16)                                  :: left_str, mem_str
     554              :       CHARACTER(LEN=80)                                  :: remedy
     555              :       INTEGER                                            :: handle, ispin, n_ov_joint, nspins
     556              :       REAL(KIND=dp)                                      :: mem_est_GB, mem_left_GB
     557              : 
     558           54 :       CALL timeset(routineN, handle)
     559              : 
     560           54 :       nspins = SIZE(homo)
     561           54 :       n_ov_joint = 0
     562          122 :       DO ispin = 1, nspins
     563          122 :          n_ov_joint = n_ov_joint + (homo(ispin) - window%first_mo + 1)*(window%last_mo - homo(ispin))
     564              :       END DO
     565              :       mem_est_GB = fulldiag_memory_estimate_GB(n_ov_joint, bse_env%flag_tda /= bse_tda)/ &
     566           54 :                    REAL(para_env%num_pe, KIND=dp)
     567           54 :       CALL bse_resolve_memory_per_proc(bse_env%memory_per_proc_gb, para_env, unit_nr)
     568           54 :       mem_left_GB = bse_memory_left_GB(bse_env%memory_per_proc_gb, para_env)
     569              : 
     570           54 :       IF (unit_nr > 0) THEN
     571           27 :          WRITE (unit_nr, '(T2,A4)') 'BSE|'
     572           27 :          WRITE (unit_nr, '(T2,A4,T7,A)') 'BSE|', 'Early memory check for the full diagonalization'
     573           27 :          WRITE (unit_nr, '(T2,A4,T7,A,T71,I10)') 'BSE|', 'Transition pairs in the active window (DFT axis)', &
     574           54 :             n_ov_joint
     575           27 :          WRITE (unit_nr, '(T2,A4,T7,A,T67,F14.3)') 'BSE|', 'Peak memory estimate per MPI rank (GB)', mem_est_GB
     576           27 :          WRITE (unit_nr, '(T2,A4,T7,A,T67,F14.3)') 'BSE|', 'Memory per MPI rank for the BSE, MEMORY_PER_PROC (GB)', &
     577           54 :             bse_env%memory_per_proc_gb
     578           27 :          WRITE (unit_nr, '(T2,A4,T7,A,T67,F14.3)') 'BSE|', 'Left after the memory the rank holds (GB)', mem_left_GB
     579           27 :          WRITE (unit_nr, '(T2,A4)') 'BSE|'
     580              :       END IF
     581              : 
     582           54 :       IF (mem_est_GB > mem_left_GB) THEN
     583            0 :          WRITE (mem_str, '(F16.3)') mem_est_GB
     584            0 :          WRITE (left_str, '(F16.3)') mem_left_GB
     585              :          ! ITERDIAG is closed-shell only, so an open-shell run is not pointed to it
     586            0 :          IF (nspins == 1) THEN
     587            0 :             remedy = "more MPI ranks, a tighter window, ITERDIAG, a higher MEMORY_PER_PROC"
     588              :          ELSE
     589            0 :             remedy = "more MPI ranks, a tighter window, a higher MEMORY_PER_PROC"
     590              :          END IF
     591              :          CALL cp_abort(__LOCATION__, &
     592              :                        "BSE full diagonalization: the estimate of "//TRIM(ADJUSTL(mem_str))// &
     593              :                        " GB per MPI rank exceeds the "//TRIM(ADJUSTL(left_str))// &
     594            0 :                        " GB left of MEMORY_PER_PROC. Try one of: "//TRIM(remedy)//".")
     595              :       END IF
     596              : 
     597           54 :       CALL timestop(handle)
     598              : 
     599           54 :    END SUBROUTINE bse_fulldiag_memory_check
     600              : 
     601              : ! **************************************************************************************************
     602              : !> \brief bse_fulldiag_memory_check for a caller that holds MO sets: the window on the DFT axis
     603              : !>        from their eigenvalues, the occupied MOs per spin from their homo
     604              : !> \param bse_env settings; the window cutoffs, the TDA flag and MEMORY_PER_PROC, resolved when still -1
     605              : !> \param mos the MO sets the GW will correct, for their DFT eigenvalues and occupations
     606              : !> \param para_env the ranks that will hold the matrices
     607              : !> \param unit_nr output unit, positive on the writing rank only
     608              : ! **************************************************************************************************
     609           50 :    SUBROUTINE bse_fulldiag_memory_check_from_mos(bse_env, mos, para_env, unit_nr)
     610              : 
     611              :       TYPE(bse_env_type), INTENT(INOUT)                  :: bse_env
     612              :       TYPE(mo_set_type), DIMENSION(:), INTENT(IN)        :: mos
     613              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     614              :       INTEGER, INTENT(IN)                                :: unit_nr
     615              : 
     616              :       INTEGER                                            :: ispin
     617              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: homo
     618              :       TYPE(mo_window_type)                               :: window
     619              : 
     620              :       ! the preparer warns about a combined window; the estimate stays silent
     621           50 :       CALL bse_window_from_mos(mos, bse_env%bse_cutoff_occ, bse_env%bse_cutoff_empty, window, warn=.FALSE.)
     622          150 :       ALLOCATE (homo(SIZE(mos)))
     623          112 :       DO ispin = 1, SIZE(mos)
     624          112 :          CALL get_mo_set(mos(ispin), homo=homo(ispin))
     625              :       END DO
     626           50 :       CALL bse_fulldiag_memory_check(bse_env, window, homo, para_env, unit_nr)
     627           50 :       DEALLOCATE (homo)
     628              : 
     629           50 :    END SUBROUTINE bse_fulldiag_memory_check_from_mos
     630              : 
     631              : END MODULE bse_main
        

Generated by: LCOV version 2.0-1