LCOV - code coverage report
Current view: top level - src - bse_main.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 94.0 % 183 172
Test Date: 2026-11-04 00:58:17 Functions: 100.0 % 8 8

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

Generated by: LCOV version 2.0-1