LCOV - code coverage report
Current view: top level - src - gw_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:92574dc) Lines: 95.5 % 1449 1384
Test Date: 2026-09-24 01:27:39 Functions: 98.2 % 57 56

            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
      10              : !> \par History
      11              : !>      01.2026 Maximilian Graml: add more bounds to exploit sparsity in 3c integrals, fixes
      12              : !> \author Jan Wilhelm
      13              : !> \date 07.2023
      14              : ! **************************************************************************************************
      15              : MODULE gw_utils
      16              :    USE atomic_kind_types,               ONLY: atomic_kind_type,&
      17              :                                               get_atomic_kind_set
      18              :    USE basis_set_types,                 ONLY: get_gto_basis_set,&
      19              :                                               gto_basis_set_type
      20              :    USE bibliography,                    ONLY: Graml2024,&
      21              :                                               cite_reference
      22              :    USE cell_types,                      ONLY: cell_type,&
      23              :                                               pbc,&
      24              :                                               scaled_to_real
      25              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_create,&
      26              :                                               cp_blacs_env_release,&
      27              :                                               cp_blacs_env_type
      28              :    USE cp_cfm_types,                    ONLY: cp_cfm_create,&
      29              :                                               cp_cfm_release,&
      30              :                                               cp_cfm_to_cfm,&
      31              :                                               cp_cfm_to_fm,&
      32              :                                               cp_cfm_type
      33              :    USE cp_control_types,                ONLY: dft_control_type
      34              :    USE cp_dbcsr_api,                    ONLY: &
      35              :         dbcsr_create, dbcsr_distribution_release, dbcsr_distribution_type, dbcsr_p_type, &
      36              :         dbcsr_release, dbcsr_set, dbcsr_type, dbcsr_type_no_symmetry, dbcsr_type_symmetric
      37              :    USE cp_dbcsr_operations,             ONLY: copy_dbcsr_to_fm,&
      38              :                                               copy_fm_to_dbcsr,&
      39              :                                               cp_dbcsr_dist2d_to_dist,&
      40              :                                               dbcsr_allocate_matrix_set,&
      41              :                                               dbcsr_deallocate_matrix_set
      42              :    USE cp_files,                        ONLY: close_file,&
      43              :                                               open_file
      44              :    USE cp_fm_basic_linalg,              ONLY: cp_fm_scale_and_add
      45              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      46              :                                               cp_fm_struct_release,&
      47              :                                               cp_fm_struct_type
      48              :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      49              :                                               cp_fm_get_diag,&
      50              :                                               cp_fm_release,&
      51              :                                               cp_fm_set_all,&
      52              :                                               cp_fm_to_fm,&
      53              :                                               cp_fm_type
      54              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      55              :                                               cp_logger_type
      56              :    USE cp_output_handling,              ONLY: cp_print_key_generate_filename
      57              :    USE dbt_api,                         ONLY: &
      58              :         dbt_clear, dbt_create, dbt_destroy, dbt_filter, dbt_iterator_blocks_left, &
      59              :         dbt_iterator_next_block, dbt_iterator_start, dbt_iterator_stop, dbt_iterator_type, &
      60              :         dbt_mp_environ_pgrid, dbt_pgrid_create, dbt_pgrid_destroy, dbt_pgrid_type, dbt_type
      61              :    USE distribution_2d_types,           ONLY: distribution_2d_type
      62              :    USE gw_auto_ri_generate_RI_basis,    ONLY: generate_auto_ri_basis
      63              :    USE gw_auto_ri_types,                ONLY: auto_ri_type
      64              :    USE gw_utils_communication,          ONLY: fm_to_local_array
      65              :    USE gw_utils_compute_integrals,      ONLY: build_3c_integral_block
      66              :    USE gw_utils_fm,                     ONLY: cfm_contract_ABA,&
      67              :                                               fm_invert
      68              :    USE input_constants,                 ONLY: &
      69              :         G0W0, do_potential_coulomb, do_potential_truncated, evGW0, ri_rpa_g0w0_crossing_newton, &
      70              :         ri_rs_large_cell_Gamma, ri_rs_non_periodic, rtp_bse_kernel_ri_ao, rtp_bse_kernel_ri_rs, &
      71              :         rtp_method_bse, rtp_method_bse_linearized, tensor_large_cell_Gamma, &
      72              :         tensor_small_cell_full_kp, xc_none
      73              :    USE input_section_types,             ONLY: section_vals_get,&
      74              :                                               section_vals_get_subs_vals,&
      75              :                                               section_vals_type,&
      76              :                                               section_vals_val_get,&
      77              :                                               section_vals_val_set
      78              :    USE kinds,                           ONLY: default_path_length,&
      79              :                                               default_string_length,&
      80              :                                               dp,&
      81              :                                               int_8
      82              :    USE kpoint_k_r_trafo_simple,         ONLY: rs_to_kp
      83              :    USE kpoint_types,                    ONLY: get_kpoint_info,&
      84              :                                               kpoint_create,&
      85              :                                               kpoint_type
      86              :    USE libint_2c_3c,                    ONLY: libint_potential_type
      87              :    USE libint_wrapper,                  ONLY: cp_libint_static_cleanup,&
      88              :                                               cp_libint_static_init
      89              :    USE machine,                         ONLY: m_walltime
      90              :    USE mathconstants,                   ONLY: gaussi,&
      91              :                                               z_one,&
      92              :                                               z_zero
      93              :    USE mathlib,                         ONLY: diag_complex,&
      94              :                                               gcd
      95              :    USE message_passing,                 ONLY: mp_cart_type,&
      96              :                                               mp_mem_avail_per_rank_GB,&
      97              :                                               mp_mem_used_per_rank_GB,&
      98              :                                               mp_para_env_type
      99              :    USE mp2_gpw,                         ONLY: create_mat_munu
     100              :    USE mp2_ri_2c,                       ONLY: RI_2c_integral_mat,&
     101              :                                               trunc_coulomb_for_exchange
     102              :    USE particle_methods,                ONLY: get_particle_set
     103              :    USE particle_types,                  ONLY: particle_type
     104              :    USE physcon,                         ONLY: angstrom,&
     105              :                                               evolt
     106              :    USE post_scf_bandstructure_types,    ONLY: post_scf_bandstructure_type,&
     107              :                                               ri_rs_env,&
     108              :                                               ri_rs_grid_opt_type
     109              :    USE post_scf_bandstructure_utils,    ONLY: allocate_GW_eigenvalues,&
     110              :                                               gw_flavour_label,&
     111              :                                               rsmat_to_kp
     112              :    USE qs_energy_types,                 ONLY: qs_energy_type
     113              :    USE qs_environment_types,            ONLY: get_qs_env,&
     114              :                                               qs_env_part_release,&
     115              :                                               qs_environment_type
     116              :    USE qs_integral_utils,               ONLY: basis_set_list_setup
     117              :    USE qs_interactions,                 ONLY: init_interaction_radii_orb_basis
     118              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
     119              :                                               qs_kind_type
     120              :    USE qs_ks_methods,                   ONLY: qs_ks_build_kohn_sham_matrix
     121              :    USE qs_neighbor_list_types,          ONLY: neighbor_list_set_p_type,&
     122              :                                               release_neighbor_list_sets
     123              :    USE qs_tensors,                      ONLY: build_2c_integrals,&
     124              :                                               build_2c_neighbor_lists,&
     125              :                                               build_3c_integrals,&
     126              :                                               build_3c_neighbor_lists,&
     127              :                                               get_tensor_occupancy,&
     128              :                                               neighbor_list_3c_destroy
     129              :    USE qs_tensors_types,                ONLY: create_2c_tensor,&
     130              :                                               create_3c_tensor,&
     131              :                                               distribution_3d_create,&
     132              :                                               distribution_3d_type,&
     133              :                                               neighbor_list_3c_type
     134              :    USE rpa_gw,                          ONLY: continuation_pade
     135              :    USE time_frequency_grids,            ONLY: build_minimax_time_frequency_grid
     136              : #include "base/base_uses.f90"
     137              : 
     138              :    IMPLICIT NONE
     139              : 
     140              :    PRIVATE
     141              : 
     142              :    PUBLIC :: create_and_init_bs_env_for_gw, de_init_bs_env, &
     143              :              compute_xkp, time_to_freq, analyt_conti_and_print, &
     144              :              add_R, is_cell_in_index_to_cell, get_V_tr_R, power, &
     145              :              rtbse_resolve_rirs_flag
     146              : 
     147              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'gw_utils'
     148              : 
     149              : CONTAINS
     150              : 
     151              : ! **************************************************************************************************
     152              : !> \brief Initializes the GW environment from the input and electronic-structure data.
     153              : !> \param qs_env ...
     154              : !> \param bs_env Band-structure environment containing GW parameters.
     155              : !> \param bs_sec BAND_STRUCTURE input section
     156              : ! **************************************************************************************************
     157          124 :    SUBROUTINE create_and_init_bs_env_for_gw(qs_env, bs_env, bs_sec)
     158              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     159              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     160              :       TYPE(section_vals_type), POINTER                   :: bs_sec
     161              : 
     162              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'create_and_init_bs_env_for_gw'
     163              : 
     164              :       INTEGER                                            :: handle
     165              : 
     166          124 :       CALL timeset(routineN, handle)
     167              : 
     168          124 :       CALL cite_reference(Graml2024)
     169              : 
     170          124 :       CALL get_parameters_from_qs_env(qs_env, bs_env)
     171              : 
     172          124 :       CALL read_gw_input_parameters(bs_env, bs_sec)
     173              : 
     174          124 :       CALL print_header_and_input_parameters(bs_env)
     175              : 
     176          124 :       CALL setup_AO_and_RI_basis_set(qs_env, bs_env)
     177              : 
     178          124 :       CALL set_heuristic_parameters(bs_env, qs_env)
     179              : 
     180          124 :       CALL cp_libint_static_init()
     181          124 :       IF (bs_env%auto_ri%enabled) CALL generate_auto_ri_basis(qs_env, bs_env)
     182              : 
     183          124 :       CALL get_RI_basis(qs_env, bs_env)
     184              : 
     185          124 :       CALL compute_V_xc(qs_env, bs_env)
     186              : 
     187          124 :       CALL init_interaction_radii(bs_env)
     188              : 
     189              :       ! The RI-RS drivers build their (µν|P) blocks on the fly through gw_3c_ctx
     190              :       ! RT-BSE with the AO-RI kernel need nl_3c it after GW, so
     191              :       ! keep everything when an RT-BSE run follows.
     192              :       IF (.NOT. bs_env%do_gw_ri_rs .OR. &
     193          124 :           bs_env%rtp_method == rtp_method_bse .OR. &
     194              :           bs_env%rtp_method == rtp_method_bse_linearized) THEN
     195           98 :          CALL create_tensors(qs_env, bs_env)
     196              :       END IF
     197              : 
     198          124 :       CALL allocate_GW_eigenvalues(bs_env)
     199              : 
     200          232 :       SELECT CASE (bs_env%gw_implementation)
     201              :       CASE (tensor_large_cell_Gamma, ri_rs_large_cell_Gamma, ri_rs_non_periodic)
     202              : 
     203          108 :          IF (.NOT. bs_env%do_gw_ri_rs) THEN
     204           68 :             CALL check_sparsity_3c(qs_env, bs_env)
     205              : 
     206           68 :             CALL set_sparsity_parallelization_parameters(bs_env)
     207              :          END IF
     208              : 
     209          108 :          CALL check_for_restart_files(qs_env, bs_env)
     210              : 
     211              :       CASE (tensor_small_cell_full_kp)
     212              : 
     213           16 :          CALL compute_3c_integrals(qs_env, bs_env)
     214              : 
     215           16 :          CALL setup_cells_Delta_R(bs_env)
     216              : 
     217           16 :          CALL setup_parallelization_Delta_R(bs_env)
     218              : 
     219           16 :          CALL allocate_matrices_small_cell_full_kp_tensor(qs_env, bs_env)
     220              : 
     221           16 :          CALL trafo_V_xc_R_to_kp(qs_env, bs_env)
     222              : 
     223          140 :          CALL heuristic_RI_regularization(qs_env, bs_env)
     224              : 
     225              :       END SELECT
     226              : 
     227          124 :       CALL setup_time_and_frequency_minimax_grid(bs_env)
     228              : 
     229              :       ! Free memory in qs_env. SCF real-space grids, task  lists, neighbor
     230              :       ! lists and work arrays on large systems hold GBs per rank for the whole run.
     231              :       ! Not required by GW. LDOS and RT-BSE still needs these information.
     232              :       !
     233              :       ! Recommendation in case of memory issues: first perform GW calculation without calculating
     234              :       !                                          LDOS (to save memory). Then, use GW restart files
     235              :       !                                          in a subsequent calculation to calculate the LDOS
     236              :       IF (.NOT. bs_env%do_ldos .AND. &
     237          124 :           bs_env%rtp_method /= rtp_method_bse .AND. &
     238              :           bs_env%rtp_method /= rtp_method_bse_linearized) THEN
     239           50 :          CALL qs_env_part_release(qs_env)
     240              :       END IF
     241              : 
     242          124 :       CALL timestop(handle)
     243              : 
     244          124 :    END SUBROUTINE create_and_init_bs_env_for_gw
     245              : 
     246              : ! **************************************************************************************************
     247              : !> \brief Store the QS environment parameters needed by GW.
     248              : !> \param qs_env ...
     249              : !> \param bs_env ...
     250              : ! **************************************************************************************************
     251          124 :    SUBROUTINE get_parameters_from_qs_env(qs_env, bs_env)
     252              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     253              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     254              : 
     255              :       TYPE(section_vals_type), POINTER                   :: input, rtbse_sec
     256              : 
     257          124 :       NULLIFY (input, rtbse_sec)
     258              :       CALL get_qs_env(qs_env, atomic_kind_set=bs_env%ri_rs%atomic_kind_set, &
     259          124 :                       cell=bs_env%ri_rs%cell, particle_set=bs_env%ri_rs%particle_set, input=input)
     260          124 :       rtbse_sec => section_vals_get_subs_vals(input, "DFT%REAL_TIME_PROPAGATION%RTBSE")
     261          124 :       CALL section_vals_val_get(rtbse_sec, "CUTOFF_RADIUS_W0", r_val=bs_env%ri_rs%cutoff_radius_w0)
     262          124 :    END SUBROUTINE get_parameters_from_qs_env
     263              : 
     264              : ! **************************************************************************************************
     265              : !> \brief Reads the GW input parameters.
     266              : !> \param bs_env ...
     267              : !> \param bs_sec BAND_STRUCTURE input section
     268              : ! **************************************************************************************************
     269          248 :    SUBROUTINE read_gw_input_parameters(bs_env, bs_sec)
     270              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     271              :       TYPE(section_vals_type), POINTER                   :: bs_sec
     272              : 
     273              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'read_gw_input_parameters'
     274              : 
     275              :       INTEGER                                            :: handle
     276              :       LOGICAL                                            :: do_evgw0
     277              :       TYPE(section_vals_type), POINTER                   :: auto_ri_sec, evgw0_sec, grid_opt_sec, &
     278              :                                                             gw_sec, ri_rs_sec
     279              : 
     280          124 :       CALL timeset(routineN, handle)
     281              : 
     282          124 :       NULLIFY (auto_ri_sec, evgw0_sec, grid_opt_sec, gw_sec, ri_rs_sec)
     283          124 :       gw_sec => section_vals_get_subs_vals(bs_sec, "GW")
     284          124 :       auto_ri_sec => section_vals_get_subs_vals(gw_sec, "AUTO_RI")
     285          124 :       evgw0_sec => section_vals_get_subs_vals(gw_sec, "EVGW0")
     286          124 :       ri_rs_sec => section_vals_get_subs_vals(gw_sec, "RI_RS")
     287          124 :       grid_opt_sec => section_vals_get_subs_vals(ri_rs_sec, "GRID_OPTIMIZATION")
     288              : 
     289          124 :       CALL section_vals_val_get(gw_sec, "NUM_TIME_FREQ_POINTS", i_val=bs_env%num_time_freq_points)
     290          124 :       CALL section_vals_val_get(gw_sec, "EPS_FILTER", r_val=bs_env%eps_filter)
     291          124 :       CALL section_vals_val_get(gw_sec, "REGULARIZATION_RI", r_val=bs_env%input_regularization_RI)
     292          124 :       CALL section_vals_val_get(gw_sec, "REGULARIZATION_MINIMAX", r_val=bs_env%input_regularization_minimax)
     293          124 :       CALL section_vals_val_get(gw_sec, "CUTOFF_RADIUS_RI", r_val=bs_env%ri_metric%cutoff_radius)
     294          124 :       CALL section_vals_val_get(gw_sec, "MEMORY_PER_PROC", r_val=bs_env%input_memory_per_proc_GB)
     295          124 :       CALL section_vals_val_get(gw_sec, "APPROX_KP_EXTRAPOL", l_val=bs_env%approx_kp_extrapol)
     296          124 :       CALL section_vals_val_get(gw_sec, "SIZE_LATTICE_SUM", i_val=bs_env%size_lattice_sum_V)
     297          124 :       CALL section_vals_val_get(gw_sec, "KPOINTS_W", i_vals=bs_env%nkp_grid_chi_eps_W_input)
     298          124 :       CALL section_vals_val_get(gw_sec, "HEDIN_SHIFT", l_val=bs_env%do_hedin_shift)
     299          124 :       CALL section_vals_val_get(gw_sec, "FREQ_MAX_FIT", r_val=bs_env%freq_max_fit)
     300          124 :       CALL read_rirs_input(bs_env%ri_rs, ri_rs_sec)
     301          124 :       CALL read_rirs_grid_opt(bs_env%ri_rs, grid_opt_sec)
     302          124 :       CALL read_evgw0_input(bs_env%ri_rs, evgw0_sec, do_evgw0)
     303          124 :       CALL read_auto_ri_input(bs_env%auto_ri, auto_ri_sec)
     304          124 :       CALL check_gw_input(bs_env, do_evgw0)
     305              : 
     306          124 :       CALL timestop(handle)
     307              : 
     308          124 :    END SUBROUTINE read_gw_input_parameters
     309              : 
     310              : ! **************************************************************************************************
     311              : !> \brief Reads the RI-RS input.
     312              : !> \param ri_rs RI-RS calculation parameters
     313              : !> \param ri_rs_sec RI_RS input section
     314              : ! **************************************************************************************************
     315          124 :    SUBROUTINE read_rirs_input(ri_rs, ri_rs_sec)
     316              :       TYPE(ri_rs_env), INTENT(INOUT)                     :: ri_rs
     317              :       TYPE(section_vals_type), POINTER                   :: ri_rs_sec
     318              : 
     319              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'read_rirs_input'
     320              : 
     321              :       INTEGER                                            :: handle
     322              : 
     323          124 :       CALL timeset(routineN, handle)
     324              : 
     325          124 :       CALL section_vals_val_get(ri_rs_sec, "TIKHONOV", r_val=ri_rs%tikhonov)
     326          124 :       CALL section_vals_val_get(ri_rs_sec, "GRID_SELECT", i_val=ri_rs%grid_select)
     327          124 :       CALL section_vals_val_get(ri_rs_sec, "GRID_FILE_SUFFIX", c_val=ri_rs%grid_file_suffix)
     328          124 :       CALL section_vals_val_get(ri_rs_sec, "CUTOFF_RADIUS_RL_RI", r_val=ri_rs%cutoff_radius_ri_rs)
     329          124 :       CALL section_vals_val_get(ri_rs_sec, "CUTOFF_RADIUS_RL_AO", r_val=ri_rs%cutoff_radius_ri_ao)
     330          124 :       CALL section_vals_val_get(ri_rs_sec, "N_PROCS_PER_ATOM_Z_LP", i_val=ri_rs%n_procs_per_atom_z_lp)
     331          124 :       CALL section_vals_val_get(ri_rs_sec, "N_PANELS", i_val=ri_rs%n_panels)
     332          124 :       CALL section_vals_val_get(ri_rs_sec, "KEEP_SPARSITY_RL", l_val=ri_rs%keep_sparsity_rirs)
     333          124 :       CALL section_vals_val_get(ri_rs_sec, "CUTOFF_RADIUS_RL_W", r_val=ri_rs%cutoff_radius_v_w)
     334          124 :       CALL section_vals_val_get(ri_rs_sec, "CUTOFF_RADIUS_G_W", r_val=ri_rs%cutoff_radius_g_w)
     335              : 
     336          124 :       CALL timestop(handle)
     337              : 
     338          124 :    END SUBROUTINE read_rirs_input
     339              : 
     340              : ! **************************************************************************************************
     341              : !> \brief Read the RI-RS grid optimization input.
     342              : !> \param ri_rs RI-RS parameters receiving the optimized-grid settings
     343              : !> \param grid_opt_sec GRID_OPTIMIZATION input section
     344              : ! **************************************************************************************************
     345          124 :    SUBROUTINE read_rirs_grid_opt(ri_rs, grid_opt_sec)
     346              :       TYPE(ri_rs_env), INTENT(INOUT), TARGET             :: ri_rs
     347              :       TYPE(section_vals_type), POINTER                   :: grid_opt_sec
     348              : 
     349              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'read_rirs_grid_opt'
     350              : 
     351              :       INTEGER                                            :: handle
     352              :       TYPE(ri_rs_grid_opt_type), POINTER                 :: grid_opt
     353              : 
     354          124 :       CALL timeset(routineN, handle)
     355              : 
     356          124 :       CALL section_vals_get(grid_opt_sec, explicit=ri_rs%grid_opt%enabled)
     357          124 :       IF (.NOT. ri_rs%grid_opt%enabled) THEN
     358          114 :          CALL timestop(handle)
     359          114 :          RETURN
     360              :       END IF
     361              : 
     362              :       NULLIFY (grid_opt)
     363           10 :       grid_opt => ri_rs%grid_opt
     364              : 
     365           10 :       CALL section_vals_val_get(grid_opt_sec, "CUTOFF_ATOMIC_CLUSTER", r_val=grid_opt%cutoff_atomic_cluster)
     366           10 :       CALL section_vals_val_get(grid_opt_sec, "MAX_ITER", i_val=grid_opt%max_iter)
     367           10 :       CALL section_vals_val_get(grid_opt_sec, "RS_AO_RATIO", r_val=grid_opt%rs_ao_ratio)
     368           10 :       IF (grid_opt%rs_ao_ratio <= 0.0_dp) CPABORT("RS_AO_RATIO must be positive")
     369           10 :       CALL timestop(handle)
     370              : 
     371              :    END SUBROUTINE read_rirs_grid_opt
     372              : 
     373              : ! **************************************************************************************************
     374              : !> \brief Reads the eigenvalue-self-consistent GW input.
     375              : !> \param ri_rs RI-RS parameters receiving the evGW0 convergence settings
     376              : !> \param evgw0_sec EVGW0 input section
     377              : !> \param do_evgw0 whether the EVGW0 section is activated
     378              : ! **************************************************************************************************
     379          248 :    SUBROUTINE read_evgw0_input(ri_rs, evgw0_sec, do_evgw0)
     380              :       TYPE(ri_rs_env), INTENT(INOUT)                     :: ri_rs
     381              :       TYPE(section_vals_type), POINTER                   :: evgw0_sec
     382              :       LOGICAL, INTENT(OUT)                               :: do_evgw0
     383              : 
     384              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'read_evgw0_input'
     385              : 
     386              :       INTEGER                                            :: handle
     387              : 
     388          124 :       CALL timeset(routineN, handle)
     389              : 
     390          124 :       CALL section_vals_val_get(evgw0_sec, "_SECTION_PARAMETERS_", l_val=do_evgw0)
     391          124 :       CALL section_vals_val_get(evgw0_sec, "MAX_ITER", i_val=ri_rs%evgw0_iter)
     392          124 :       CALL section_vals_val_get(evgw0_sec, "EPS_ITER", r_val=ri_rs%evgw0_eps_iter)
     393              : 
     394          124 :       CALL timestop(handle)
     395              : 
     396          124 :    END SUBROUTINE read_evgw0_input
     397              : 
     398              : ! **************************************************************************************************
     399              : !> \brief Reads and validates the automatic RI input.
     400              : !> \param auto_ri automatic RI basis optimization parameters
     401              : !> \param auto_ri_sec AUTO_RI input section
     402              : ! **************************************************************************************************
     403          124 :    SUBROUTINE read_auto_ri_input(auto_ri, auto_ri_sec)
     404              :       TYPE(auto_ri_type), INTENT(INOUT)                  :: auto_ri
     405              :       TYPE(section_vals_type), POINTER                   :: auto_ri_sec
     406              : 
     407              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'read_auto_ri_input'
     408              : 
     409              :       INTEGER                                            :: handle
     410              : 
     411          124 :       CALL timeset(routineN, handle)
     412              : 
     413          124 :       CALL section_vals_get(auto_ri_sec, explicit=auto_ri%enabled)
     414          124 :       IF (.NOT. auto_ri%enabled) THEN
     415          120 :          CALL timestop(handle)
     416          120 :          RETURN
     417              :       END IF
     418              : 
     419            4 :       CALL section_vals_val_get(auto_ri_sec, "RI_AO_RATIO", r_val=auto_ri%ri_ao_ratio)
     420            4 :       CALL section_vals_val_get(auto_ri_sec, "OCC_EMPTY_FRONTIER_ORBITAL_WINDOW", r_val=auto_ri%occ_energy_window)
     421            4 :       CALL section_vals_val_get(auto_ri_sec, "NEIGHBOR_RADIUS", r_val=auto_ri%neighbor_radius)
     422            4 :       IF (.NOT. (auto_ri%ri_ao_ratio > 0.0_dp)) CPABORT("AUTO_RI%RI_AO_RATIO must be positive")
     423            4 :       IF (.NOT. (auto_ri%occ_energy_window >= 0.0_dp)) THEN
     424            0 :          CPABORT("AUTO_RI%OCC_EMPTY_FRONTIER_ORBITAL_WINDOW must be nonnegative")
     425              :       END IF
     426            4 :       IF (.NOT. (auto_ri%neighbor_radius > 0.0_dp)) CPABORT("AUTO_RI%NEIGHBOR_RADIUS must be positive")
     427              : 
     428            4 :       CALL timestop(handle)
     429              : 
     430              :    END SUBROUTINE read_auto_ri_input
     431              : 
     432              : ! **************************************************************************************************
     433              : !> \brief Checks compatibility among the GW input sections and assigns the GW flavour.
     434              : !> \param bs_env ...
     435              : !> \param do_evgw0 whether eigenvalue self-consistency is requested
     436              : ! **************************************************************************************************
     437          124 :    SUBROUTINE check_gw_input(bs_env, do_evgw0)
     438              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     439              :       LOGICAL, INTENT(IN)                                :: do_evgw0
     440              : 
     441              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'check_gw_input'
     442              : 
     443              :       INTEGER                                            :: handle
     444              : 
     445          124 :       CALL timeset(routineN, handle)
     446              : 
     447              :       ! GRID_OPTIMIZATION is available only with RI-RS.
     448          124 :       CPASSERT(.NOT. bs_env%ri_rs%grid_opt%enabled .OR. bs_env%do_gw_ri_rs)
     449          124 :       IF (bs_env%ri_rs%grid_opt%enabled) THEN
     450           10 :          IF (bs_env%ri_rs%grid_opt%max_iter < 1) THEN
     451            0 :             CPABORT("GRID_OPTIMIZATION%MAX_ITER must be positive")
     452              :          END IF
     453           10 :          IF (bs_env%ri_rs%grid_opt%cutoff_atomic_cluster <= 0.0_dp) THEN
     454            0 :             CPABORT("GRID_OPTIMIZATION%CUTOFF_ATOMIC_CLUSTER must be positive")
     455              :          END IF
     456           10 :          IF (bs_env%ri_rs%tikhonov < 0.0_dp) THEN
     457            0 :             CPABORT("RI_RS%TIKHONOV must not be negative")
     458              :          END IF
     459           10 :          IF (bs_env%do_periodic) THEN
     460            0 :             CPABORT("GRID_OPTIMIZATION currently supports nonperiodic local environments only")
     461              :          END IF
     462              :       END IF
     463              : 
     464              :       ! evGW0 implemented so far only with RI-RS.
     465          124 :       CPASSERT(.NOT. do_evgw0 .OR. bs_env%do_gw_ri_rs)
     466          124 :       IF (bs_env%auto_ri%enabled) THEN
     467              :          ! AUTO_RI is available only with RI-RS.
     468            4 :          CPASSERT(bs_env%do_gw_ri_rs)
     469              :          ! AUTO_RI is implemented only for nonperiodic systems.
     470            4 :          CPASSERT(.NOT. bs_env%do_periodic)
     471              :          ! AUTO_RI is incompatible with real-space truncation of G and W.
     472            4 :          CPASSERT(bs_env%ri_rs%cutoff_radius_g_w <= 0.0_dp)
     473              :       END IF
     474              : 
     475          124 :       bs_env%gw_flavour = G0W0
     476          124 :       IF (do_evgw0) THEN
     477            4 :          bs_env%gw_flavour = evGW0
     478              :          CALL cp_warn(__LOCATION__, &
     479              :                       "evGW0 in the RI-RS GW code is experimental. The quasiparticle energies "// &
     480              :                       "of the previous cycle are fed back into the Green's function, so the "// &
     481              :                       "error of the analytic continuation propagates and accumulates over the "// &
     482              :                       "cycles instead of affecting one state only, as it does in G0W0. In "// &
     483              :                       "tests on small molecules the resulting deviation stayed below 100 meV, "// &
     484              :                       "but this has not been established in general. Use at your own risk and "// &
     485            4 :                       "check the convergence of the reported states.")
     486              :       END IF
     487              : 
     488          124 :       CALL resolve_memory_per_proc(bs_env)
     489              : 
     490          124 :       CALL timestop(handle)
     491              : 
     492          124 :    END SUBROUTINE check_gw_input
     493              : 
     494              : ! **************************************************************************************************
     495              : !> \brief Determine memory per processor available for low-sclaing tensor GW routines.
     496              : !>
     497              : !>        MEMORY_PER_PROC = -1 (the default) : Automatically detect available memory from the system.
     498              : !>        MEMORY_PER_PROC > 0.0 : Override with user-defined value (in GB).
     499              : !>
     500              : !> \param bs_env ...
     501              : ! **************************************************************************************************
     502          124 :    SUBROUTINE resolve_memory_per_proc(bs_env)
     503              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     504              : 
     505              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'resolve_memory_per_proc'
     506              :       REAL(KIND=dp), PARAMETER :: detected_memory_fraction = 0.8_dp, &
     507              :          fallback_memory_per_proc_GB = 2.0_dp
     508              : 
     509              :       INTEGER                                            :: handle
     510              :       REAL(KIND=dp)                                      :: mem_free_per_proc_GB, mem_occ_per_proc_GB
     511              : 
     512          124 :       CALL timeset(routineN, handle)
     513              : 
     514          124 :       bs_env%auto_memory_per_proc = bs_env%input_memory_per_proc_GB <= 0.0_dp
     515              : 
     516          124 :       IF (bs_env%auto_memory_per_proc) THEN
     517              : 
     518          124 :          CALL mp_mem_avail_per_rank_GB(bs_env%para_env, mem_free_per_proc_GB)
     519          124 :          CALL mp_mem_used_per_rank_GB(bs_env%para_env, mem_occ_per_proc_GB)
     520              : 
     521              :          ! detected_memory_fraction is a safety net on the detected free memory
     522              :          bs_env%input_memory_per_proc_GB = detected_memory_fraction*mem_free_per_proc_GB &
     523          124 :                                            + mem_occ_per_proc_GB
     524              : 
     525              :          ! mp_mem_avail_per_rank_GB returns zero if the free memory cannot be detected
     526          124 :          IF (mem_free_per_proc_GB <= 0.0_dp) THEN
     527            0 :             bs_env%auto_memory_per_proc = .FALSE.
     528            0 :             bs_env%input_memory_per_proc_GB = fallback_memory_per_proc_GB
     529              :             CALL cp_warn(__LOCATION__, &
     530              :                          "Could not detect the available memory per MPI process. Falling back "// &
     531              :                          "to MEMORY_PER_PROC = 2 GB. Set MEMORY_PER_PROC in the GW section "// &
     532            0 :                          "explicitly for good performance.")
     533              :          END IF
     534              : 
     535            0 :       ELSE IF (bs_env%do_gw_ri_rs) THEN
     536              : 
     537              :          CALL cp_warn(__LOCATION__, &
     538              :                       "MEMORY_PER_PROC is set, but it is not used by the RI-RS GW code, which "// &
     539              :                       "detects the available memory per MPI process automatically. The keyword "// &
     540            0 :                       "is ignored.")
     541              : 
     542              :       END IF
     543              : 
     544          124 :       CALL timestop(handle)
     545              : 
     546          124 :    END SUBROUTINE resolve_memory_per_proc
     547              : 
     548              : ! **************************************************************************************************
     549              : !> \brief Prints the common GW input parameters and the RI-RS block when active.
     550              : !> \param bs_env ...
     551              : ! **************************************************************************************************
     552          124 :    SUBROUTINE print_header_and_input_parameters(bs_env)
     553              : 
     554              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     555              : 
     556              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'print_header_and_input_parameters'
     557              : 
     558              :       INTEGER                                            :: handle, u
     559              : 
     560          124 :       CALL timeset(routineN, handle)
     561              : 
     562          124 :       u = bs_env%unit_nr
     563              : 
     564          124 :       IF (u > 0) THEN
     565           62 :          WRITE (u, '(T2,A)') ' '
     566           62 :          WRITE (u, '(T2,A)') REPEAT('-', 79)
     567           62 :          WRITE (u, '(T2,A,A78)') '-', '-'
     568           62 :          WRITE (u, '(T2,A,A46,A32)') '-', 'GW CALCULATION', '-'
     569           62 :          WRITE (u, '(T2,A,A78)') '-', '-'
     570           62 :          WRITE (u, '(T2,A)') REPEAT('-', 79)
     571           62 :          WRITE (u, '(T2,A)') ' '
     572           62 :          WRITE (u, '(T2,A,I45)') 'Input: Number of time/freq. points', bs_env%num_time_freq_points
     573           62 :          WRITE (u, "(T2,A,F44.1,A)") 'Input: ω_max for fitting Σ(iω) (eV)', bs_env%freq_max_fit*evolt
     574           62 :          WRITE (u, '(T2,A,ES27.1)') 'Input: Filter threshold for sparse tensor operations', &
     575          124 :             bs_env%eps_filter
     576           62 :          WRITE (u, "(T2,A,L55)") 'Input: Apply Hedin shift', bs_env%do_hedin_shift
     577           62 :          IF (bs_env%auto_memory_per_proc) THEN
     578           62 :             WRITE (u, '(T2,A,F34.1,A)') 'Detected: Available memory per MPI process', &
     579          124 :                bs_env%input_memory_per_proc_GB, ' GB'
     580              :          ELSE
     581            0 :             WRITE (u, '(T2,A,F37.1,A)') 'Input: Available memory per MPI process', &
     582            0 :                bs_env%input_memory_per_proc_GB, ' GB'
     583              :          END IF
     584           62 :          IF (bs_env%do_gw_ri_rs) CALL print_gw_rirs_input(bs_env)
     585              :       END IF
     586              : 
     587          124 :       CALL timestop(handle)
     588              : 
     589          124 :    END SUBROUTINE print_header_and_input_parameters
     590              : 
     591              : ! **************************************************************************************************
     592              : !> \brief Prints the input parameters specific to an RI-RS GW calculation.
     593              : !> \param bs_env ...
     594              : ! **************************************************************************************************
     595           20 :    SUBROUTINE print_gw_rirs_input(bs_env)
     596              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     597              : 
     598              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'print_gw_rirs_input'
     599              : 
     600              :       INTEGER                                            :: handle, unit_nr
     601              : 
     602           20 :       CALL timeset(routineN, handle)
     603              : 
     604           20 :       unit_nr = bs_env%unit_nr
     605              : 
     606           20 :       WRITE (unit_nr, '(A)') ' '
     607           20 :       WRITE (unit_nr, '(T2,A,ES43.2)') 'Input: RI-RS Tikhonov regularization', &
     608           40 :          bs_env%ri_rs%tikhonov
     609              :       CALL print_cutoff_radius_input(unit_nr, 'RI-RS integration sphere cutoff', &
     610           20 :                                      bs_env%ri_rs%cutoff_radius_ri_rs)
     611              :       CALL print_cutoff_radius_input(unit_nr, 'AO grid hard cutoff radius', &
     612           20 :                                      bs_env%ri_rs%cutoff_radius_ri_ao)
     613           20 :       WRITE (unit_nr, '(T2,A,I40)') 'Input: MPI ranks per atom in Z_lP solve', &
     614           40 :          bs_env%ri_rs%n_procs_per_atom_z_lp
     615           20 :       WRITE (unit_nr, '(T2,A,L43)') 'Input: Keep sparsity in χ/G/W panels', &
     616           40 :          bs_env%ri_rs%keep_sparsity_rirs
     617              :       CALL print_cutoff_radius_input(unit_nr, 'G/W panel truncation radius', &
     618           20 :                                      bs_env%ri_rs%cutoff_radius_v_w)
     619              :       CALL print_cutoff_radius_input(unit_nr, 'G/W operator truncation radius', &
     620           20 :                                      bs_env%ri_rs%cutoff_radius_g_w)
     621           20 :       WRITE (unit_nr, '(T2,A,A62)') 'Input: GW flavour', TRIM(gw_flavour_label(bs_env))
     622              : 
     623           20 :       IF (bs_env%ri_rs%grid_opt%enabled) THEN
     624            5 :          WRITE (unit_nr, '(A)') ' '
     625              :          WRITE (unit_nr, '(T2,A,T80,L1)') &
     626            5 :             'Input: RI-RS grid optimization activated', .TRUE.
     627              :          WRITE (unit_nr, '(T2,A,T69,F12.1)') &
     628            5 :             'Input: RS points per AO function', bs_env%ri_rs%grid_opt%rs_ao_ratio
     629              :          WRITE (unit_nr, '(T2,A,T73,F6.1,A)') &
     630            5 :             'Input: Cutoff radius for atomic clusters', &
     631           10 :             bs_env%ri_rs%grid_opt%cutoff_atomic_cluster*angstrom, ' Å'
     632              :          WRITE (unit_nr, '(T2,A,T75,I6)') &
     633            5 :             'Input: Maximum grid optimization iterations', &
     634           10 :             bs_env%ri_rs%grid_opt%max_iter
     635              :       END IF
     636              : 
     637           20 :       IF (bs_env%auto_ri%enabled) THEN
     638            2 :          WRITE (unit_nr, '(A)') ' '
     639              :          WRITE (unit_nr, '(T2,A,T80,L1)') &
     640            2 :             'Input: AUTO_RI basis optimization activated:', .TRUE.
     641              :          WRITE (unit_nr, '(T2,A,T69,F12.1)') &
     642            2 :             'Input: AUTO_RI number of RI functions per AO function:', bs_env%auto_ri%ri_ao_ratio
     643              :          WRITE (unit_nr, '(T2,A,T74,F4.1,A)') &
     644            2 :             'Input: AUTO_RI frontier orbital window:', bs_env%auto_ri%occ_energy_window*evolt, ' eV'
     645              :          WRITE (unit_nr, '(T2,A,T75,F4.1,A)') &
     646            2 :             'Input: AUTO_RI neighbor radius:', bs_env%auto_ri%neighbor_radius*angstrom, ' Å'
     647              :       END IF
     648              : 
     649           20 :       IF (bs_env%gw_flavour == evGW0) THEN
     650            2 :          WRITE (unit_nr, '(T2,A,I42)') 'Input: Maximum number of evGW0 cycles', &
     651            4 :             bs_env%ri_rs%evgw0_iter
     652            2 :          WRITE (unit_nr, '(T2,A,F42.5,A)') 'Input: evGW0 convergence threshold', &
     653            4 :             bs_env%ri_rs%evgw0_eps_iter*evolt, ' eV'
     654              :       END IF
     655           20 :       WRITE (unit_nr, '(A)') ' '
     656              : 
     657           20 :       CALL timestop(handle)
     658              : 
     659           20 :    END SUBROUTINE print_gw_rirs_input
     660              : 
     661              : ! **************************************************************************************************
     662              : !> \brief Prints a positive RI-RS cutoff radius.
     663              : !> \param unit_nr output unit
     664              : !> \param label description of the cutoff radius
     665              : !> \param cutoff_radius cutoff radius in atomic units
     666              : ! **************************************************************************************************
     667           80 :    SUBROUTINE print_cutoff_radius_input(unit_nr, label, cutoff_radius)
     668              :       INTEGER, INTENT(IN)                                :: unit_nr
     669              :       CHARACTER(LEN=*), INTENT(IN)                       :: label
     670              :       REAL(KIND=dp), INTENT(IN)                          :: cutoff_radius
     671              : 
     672              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'print_cutoff_radius_input'
     673              : 
     674              :       INTEGER                                            :: handle
     675              : 
     676           80 :       CALL timeset(routineN, handle)
     677              : 
     678           80 :       IF (cutoff_radius > 0.0_dp) THEN
     679              :          WRITE (unit_nr, '(T2,2A,T73,F6.2,A)') &
     680            3 :             'Input: ', TRIM(label), cutoff_radius*angstrom, ' Å'
     681              :       END IF
     682              : 
     683           80 :       CALL timestop(handle)
     684              : 
     685           80 :    END SUBROUTINE print_cutoff_radius_input
     686              : 
     687              : ! **************************************************************************************************
     688              : !> \brief Creates the AO and reference RI basis sets for every atomic kind.
     689              : !> \param qs_env ...
     690              : !> \param bs_env ...
     691              : ! **************************************************************************************************
     692          124 :    SUBROUTINE setup_AO_and_RI_basis_set(qs_env, bs_env)
     693              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     694              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     695              : 
     696              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'setup_AO_and_RI_basis_set'
     697              : 
     698              :       INTEGER                                            :: handle, natom, nkind
     699          124 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     700          124 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     701              : 
     702          124 :       CALL timeset(routineN, handle)
     703              : 
     704              :       CALL get_qs_env(qs_env, &
     705              :                       qs_kind_set=qs_kind_set, &
     706              :                       particle_set=particle_set, &
     707          124 :                       natom=natom, nkind=nkind)
     708              : 
     709              :       ! set up basis
     710          496 :       ALLOCATE (bs_env%sizes_RI(natom), bs_env%sizes_AO(natom))
     711          840 :       ALLOCATE (bs_env%basis_set_RI(nkind), bs_env%basis_set_AO(nkind))
     712              : 
     713          124 :       CALL basis_set_list_setup(bs_env%basis_set_RI, "RI_AUX", qs_kind_set)
     714          124 :       CALL basis_set_list_setup(bs_env%basis_set_AO, "ORB", qs_kind_set)
     715              : 
     716              :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=bs_env%sizes_RI, &
     717          124 :                             basis=bs_env%basis_set_RI)
     718              :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=bs_env%sizes_AO, &
     719          124 :                             basis=bs_env%basis_set_AO)
     720              : 
     721          124 :       CALL timestop(handle)
     722              : 
     723          124 :    END SUBROUTINE setup_AO_and_RI_basis_set
     724              : 
     725              : ! **************************************************************************************************
     726              : !> \brief Sets internal GW heuristics that are not exposed as user input.
     727              : !> \param bs_env ...
     728              : !> \param qs_env ...
     729              : ! **************************************************************************************************
     730          124 :    SUBROUTINE set_heuristic_parameters(bs_env, qs_env)
     731              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     732              :       TYPE(qs_environment_type), OPTIONAL, POINTER       :: qs_env
     733              : 
     734              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'set_heuristic_parameters'
     735              : 
     736              :       INTEGER                                            :: handle, u
     737              :       LOGICAL                                            :: do_BvK_cell
     738              : 
     739          124 :       CALL timeset(routineN, handle)
     740              : 
     741              :       ! for generating numerically stable minimax Fourier integration weights
     742          124 :       bs_env%num_points_per_magnitude = 200
     743              : 
     744          124 :       IF (bs_env%input_regularization_minimax > -1.0E-12_dp) THEN
     745            0 :          bs_env%regularization_minimax = bs_env%input_regularization_minimax
     746              :       ELSE
     747              :          ! for periodic systems and for 20 minimax points, we use a regularized minimax mesh
     748              :          ! (from experience: regularized minimax meshes converge faster for periodic systems
     749              :          !  and for 20 pts)
     750          124 :          IF (bs_env%do_periodic .OR. bs_env%num_time_freq_points >= 20) THEN
     751           98 :             bs_env%regularization_minimax = 1.0E-6_dp
     752              :          ELSE
     753           26 :             bs_env%regularization_minimax = 0.0_dp
     754              :          END IF
     755              :       END IF
     756              : 
     757          124 :       bs_env%stabilize_exp = 70.0_dp
     758          124 :       bs_env%eps_atom_grid_2d_mat = 1.0E-50_dp
     759              : 
     760              :       ! use a 16-parameter Padé fit
     761          124 :       bs_env%nparam_pade = 16
     762              : 
     763              :       ! resolution of the identity with the truncated Coulomb metric
     764          124 :       bs_env%ri_metric%potential_type = do_potential_truncated
     765          124 :       bs_env%ri_metric%omega = 0.0_dp
     766              :       ! cutoff radius is specified in the input
     767          124 :       bs_env%ri_metric%filename = "t_c_g.dat"
     768              : 
     769          124 :       bs_env%eps_eigval_mat_RI = 0.0_dp
     770              : 
     771          124 :       IF (bs_env%input_regularization_RI > -1.0E-12_dp) THEN
     772            0 :          bs_env%regularization_RI = bs_env%input_regularization_RI
     773              :       ELSE
     774              :          ! default case:
     775              : 
     776              :          ! 1. for periodic systems, we use the regularized resolution of the identity per default
     777          124 :          bs_env%regularization_RI = 1.0E-2_dp
     778              : 
     779              :          ! 2. for molecules, no regularization is necessary
     780          124 :          IF (.NOT. bs_env%do_periodic) bs_env%regularization_RI = 0.0_dp
     781              : 
     782              :       END IF
     783              : 
     784              :       ! Coulomb operator for the exchange self-energy. Periodic systems use the truncated
     785              :       ! operator described by Guidon, VandeVondele, and Hutter, JCTC 5, 3010 (2009).
     786          124 :       IF (bs_env%do_periodic) THEN
     787           26 :          do_BvK_cell = bs_env%gw_implementation == tensor_small_cell_full_kp
     788              :          CALL trunc_coulomb_for_exchange(qs_env, bs_env%trunc_coulomb, &
     789              :                                          rel_cutoff_trunc_coulomb_ri_x=0.5_dp, &
     790              :                                          cell_grid=bs_env%cell_grid_scf_desymm, &
     791           26 :                                          do_BvK_cell=do_BvK_cell)
     792              :       ELSE
     793           98 :          bs_env%trunc_coulomb%potential_type = do_potential_coulomb
     794           98 :          bs_env%trunc_coulomb%cutoff_radius = -1.0_dp
     795           98 :          bs_env%trunc_coulomb%omega = 0.0_dp
     796           98 :          bs_env%trunc_coulomb%filename = ""
     797              :       END IF
     798              : 
     799              :       ! for small-cell GW, we need more cells than normally used by the filter bs_env%eps_filter
     800              :       ! (in particular for computing the self-energy because of higher number of cells needed)
     801          124 :       bs_env%heuristic_filter_factor = 1.0E-4_dp
     802              : 
     803          124 :       u = bs_env%unit_nr
     804          124 :       IF (u > 0) THEN
     805           62 :          IF (bs_env%do_periodic) THEN
     806           13 :             WRITE (u, FMT="(T2,2A,F21.1,A)") "Cutoff radius for the truncated Coulomb ", &
     807           26 :                "operator in Σ^x:", bs_env%trunc_coulomb%cutoff_radius*angstrom, " Å"
     808              :          END IF
     809           62 :          WRITE (u, FMT="(T2,2A,F15.1,A)") "Cutoff radius for the truncated Coulomb ", &
     810          124 :             "operator in RI metric:", bs_env%ri_metric%cutoff_radius*angstrom, " Å"
     811           62 :          WRITE (u, FMT="(T2,A,ES48.1)") "Regularization parameter of RI ", bs_env%regularization_RI
     812           62 :          WRITE (u, FMT="(T2,A,ES38.1)") "Regularization parameter of minimax grids", &
     813          124 :             bs_env%regularization_minimax
     814           62 :          IF (bs_env%do_periodic) THEN
     815           13 :             WRITE (u, FMT="(T2,A,I53)") "Lattice sum size for V(k):", bs_env%size_lattice_sum_V
     816              :          END IF
     817              :       END IF
     818              : 
     819          124 :       CALL timestop(handle)
     820              : 
     821          124 :    END SUBROUTINE set_heuristic_parameters
     822              : 
     823              : ! **************************************************************************************************
     824              : !> \brief Sets up the RI basis, its matrix distributions, and M^-1(k=0).
     825              : !> \param qs_env ...
     826              : !> \param bs_env ...
     827              : ! **************************************************************************************************
     828          124 :    SUBROUTINE get_RI_basis(qs_env, bs_env)
     829              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     830              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     831              : 
     832              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'get_RI_basis'
     833              : 
     834              :       INTEGER                                            :: handle
     835              : 
     836          124 :       CALL timeset(routineN, handle)
     837              : 
     838          124 :       CALL set_AO_RI_basis_function_indices(qs_env, bs_env)
     839          124 :       CALL setup_kpoints_chi_eps_W(bs_env, bs_env%kpoints_chi_eps_W)
     840          124 :       IF (bs_env%gw_implementation == tensor_small_cell_full_kp) THEN
     841           16 :          CALL setup_cells_3c(qs_env, bs_env)
     842              :       END IF
     843          124 :       CALL set_parallelization_parameters(qs_env, bs_env)
     844          124 :       CALL allocate_matrices(qs_env, bs_env)
     845          124 :       IF (bs_env%gw_implementation /= tensor_small_cell_full_kp) THEN
     846          108 :          CALL compute_Minv_Gamma(qs_env, bs_env)
     847              :       END IF
     848              : 
     849          124 :       CALL timestop(handle)
     850              : 
     851          124 :    END SUBROUTINE get_RI_basis
     852              : 
     853              : ! **************************************************************************************************
     854              : !> \brief Determines the AO and RI basis-function indices for every atom.
     855              : !> \param qs_env ...
     856              : !> \param bs_env ...
     857              : ! **************************************************************************************************
     858          124 :    SUBROUTINE set_AO_RI_basis_function_indices(qs_env, bs_env)
     859              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     860              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     861              : 
     862              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'set_AO_RI_basis_function_indices'
     863              : 
     864              :       INTEGER                                            :: handle, i_RI, iatom, ikind, iset, &
     865              :                                                             max_AO_bf_per_atom, n_ao_test, n_atom, &
     866              :                                                             n_kind, n_RI, nset, nsgf, u
     867          124 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: kind_of
     868          124 :       INTEGER, DIMENSION(:), POINTER                     :: l_max, l_min, nsgf_set
     869          124 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     870              :       TYPE(gto_basis_set_type), POINTER                  :: basis
     871          124 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     872              : 
     873          124 :       CALL timeset(routineN, handle)
     874              : 
     875              :       ! determine RI basis set size
     876          124 :       CALL get_qs_env(qs_env, atomic_kind_set=atomic_kind_set, qs_kind_set=qs_kind_set)
     877              : 
     878          124 :       n_kind = SIZE(qs_kind_set)
     879          124 :       n_atom = bs_env%n_atom
     880              : 
     881          124 :       CALL get_atomic_kind_set(atomic_kind_set, kind_of=kind_of)
     882              : 
     883          296 :       DO ikind = 1, n_kind
     884          172 :          CALL get_qs_kind(qs_kind_set(ikind), basis_set=basis, basis_type="RI_AUX")
     885          296 :          CPASSERT(ASSOCIATED(basis))
     886              :       END DO
     887              : 
     888          372 :       ALLOCATE (bs_env%i_RI_start_from_atom(n_atom))
     889          248 :       ALLOCATE (bs_env%i_RI_end_from_atom(n_atom))
     890          248 :       ALLOCATE (bs_env%i_ao_start_from_atom(n_atom))
     891          248 :       ALLOCATE (bs_env%i_ao_end_from_atom(n_atom))
     892              : 
     893          124 :       n_RI = 0
     894          412 :       DO iatom = 1, n_atom
     895          288 :          bs_env%i_RI_start_from_atom(iatom) = n_RI + 1
     896          288 :          n_RI = n_RI + bs_env%sizes_RI(iatom)
     897          412 :          bs_env%i_RI_end_from_atom(iatom) = n_RI
     898              :       END DO
     899          124 :       bs_env%n_RI = n_RI
     900              : 
     901          124 :       max_AO_bf_per_atom = 0
     902          124 :       n_ao_test = 0
     903          412 :       DO iatom = 1, n_atom
     904          288 :          bs_env%i_ao_start_from_atom(iatom) = n_ao_test + 1
     905          288 :          ikind = kind_of(iatom)
     906          288 :          CALL get_qs_kind(qs_kind=qs_kind_set(ikind), nsgf=nsgf, basis_type="ORB")
     907          288 :          n_ao_test = n_ao_test + nsgf
     908          288 :          bs_env%i_ao_end_from_atom(iatom) = n_ao_test
     909          412 :          max_AO_bf_per_atom = MAX(max_AO_bf_per_atom, nsgf)
     910              :       END DO
     911          124 :       CPASSERT(n_ao_test == bs_env%n_ao)
     912          124 :       bs_env%max_AO_bf_per_atom = max_AO_bf_per_atom
     913              : 
     914          124 :       u = bs_env%unit_nr
     915         4028 :       ALLOCATE (bs_env%l_RI(n_RI), source=-1)
     916          124 :       IF (bs_env%auto_ri%enabled) THEN
     917            4 :          CPASSERT(bs_env%auto_ri%ready)
     918              :       ELSE
     919              :          i_RI = 0
     920          396 :          DO iatom = 1, n_atom
     921          276 :             ikind = kind_of(iatom)
     922          276 :             nset = bs_env%basis_set_RI(ikind)%gto_basis_set%nset
     923          276 :             l_max => bs_env%basis_set_RI(ikind)%gto_basis_set%lmax
     924          276 :             l_min => bs_env%basis_set_RI(ikind)%gto_basis_set%lmin
     925          276 :             nsgf_set => bs_env%basis_set_RI(ikind)%gto_basis_set%nsgf_set
     926         1914 :             DO iset = 1, nset
     927         1518 :                CPASSERT(l_max(iset) == l_min(iset))
     928         5116 :                bs_env%l_RI(i_RI + 1:i_RI + nsgf_set(iset)) = l_max(iset)
     929         1794 :                i_RI = i_RI + nsgf_set(iset)
     930              :             END DO
     931              :          END DO
     932          120 :          CPASSERT(i_RI == n_RI)
     933          120 :          IF (u > 0) THEN
     934           60 :             WRITE (u, FMT="(T2,A)") " "
     935           60 :             WRITE (u, FMT="(T2,2A,T75,I8)") "Number of auxiliary Gaussian basis functions ", &
     936          120 :                "for χ, ε, W", n_RI
     937              :          END IF
     938              :       END IF
     939              : 
     940          124 :       CALL timestop(handle)
     941              : 
     942          248 :    END SUBROUTINE set_AO_RI_basis_function_indices
     943              : 
     944              : ! **************************************************************************************************
     945              : !> \brief Sets the reciprocal mesh used for χ, ε, and W.
     946              : !> \param bs_env ...
     947              : !> \param kpoints_chi_eps_W reciprocal mesh used for χ, ε, and W
     948              : ! **************************************************************************************************
     949          124 :    SUBROUTINE setup_kpoints_chi_eps_W(bs_env, kpoints_chi_eps_W)
     950              : 
     951              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     952              :       TYPE(kpoint_type), POINTER                         :: kpoints_chi_eps_W
     953              : 
     954              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'setup_kpoints_chi_eps_W'
     955              : 
     956              :       INTEGER                                            :: handle, i_dim, n_dim, nkp, nkp_extra, &
     957              :                                                             nkp_orig, u
     958              :       INTEGER, DIMENSION(3)                              :: nkp_grid, nkp_grid_extra, periodic
     959              :       REAL(KIND=dp)                                      :: exp_s_p, n_dim_inv
     960              : 
     961          124 :       CALL timeset(routineN, handle)
     962              : 
     963              :       ! routine adapted from mp2_integrals.F
     964          124 :       NULLIFY (kpoints_chi_eps_W)
     965          124 :       CALL kpoint_create(kpoints_chi_eps_W)
     966              : 
     967          124 :       kpoints_chi_eps_W%kp_scheme = "GENERAL"
     968              : 
     969          496 :       periodic(1:3) = bs_env%periodic(1:3)
     970              : 
     971          124 :       CPASSERT(SIZE(bs_env%nkp_grid_chi_eps_W_input) == 3)
     972              : 
     973              :       IF (bs_env%nkp_grid_chi_eps_W_input(1) > 0 .AND. &
     974          124 :           bs_env%nkp_grid_chi_eps_W_input(2) > 0 .AND. &
     975              :           bs_env%nkp_grid_chi_eps_W_input(3) > 0) THEN
     976              : 
     977              :          ! 1. k-point mesh for χ, ε, W from input
     978              : 
     979            0 :          DO i_dim = 1, 3
     980            0 :             SELECT CASE (periodic(i_dim))
     981              :             CASE (0)
     982            0 :                nkp_grid(i_dim) = 1
     983            0 :                nkp_grid_extra(i_dim) = 1
     984              :             CASE (1)
     985            0 :                nkp_grid(i_dim) = bs_env%nkp_grid_chi_eps_W_input(i_dim)
     986            0 :                nkp_grid_extra(i_dim) = nkp_grid(i_dim)*2
     987              :             CASE DEFAULT
     988            0 :                CPABORT("Error in periodicity.")
     989              :             END SELECT
     990              :          END DO
     991              : 
     992              :       ELSE IF (bs_env%nkp_grid_chi_eps_W_input(1) == -1 .AND. &
     993          124 :                bs_env%nkp_grid_chi_eps_W_input(2) == -1 .AND. &
     994              :                bs_env%nkp_grid_chi_eps_W_input(3) == -1) THEN
     995              : 
     996              :          ! 2. automatic k-point mesh for χ, ε, W
     997              : 
     998          496 :          DO i_dim = 1, 3
     999              : 
    1000          372 :             CPASSERT(periodic(i_dim) == 0 .OR. periodic(i_dim) == 1)
    1001              : 
    1002          124 :             SELECT CASE (periodic(i_dim))
    1003              :             CASE (0)
    1004          320 :                nkp_grid(i_dim) = 1
    1005          320 :                nkp_grid_extra(i_dim) = 1
    1006              :             CASE (1)
    1007           72 :                SELECT CASE (bs_env%gw_implementation)
    1008              :                CASE (tensor_large_cell_Gamma, ri_rs_large_cell_Gamma, ri_rs_non_periodic)
    1009           20 :                   nkp_grid(i_dim) = 4
    1010           20 :                   nkp_grid_extra(i_dim) = 6
    1011              :                CASE (tensor_small_cell_full_kp)
    1012           32 :                   nkp_grid(i_dim) = bs_env%kpoints_scf_desymm%nkp_grid(i_dim)*4
    1013           52 :                   nkp_grid_extra(i_dim) = bs_env%kpoints_scf_desymm%nkp_grid(i_dim)*8
    1014              :                END SELECT
    1015              :             CASE DEFAULT
    1016          372 :                CPABORT("Error in periodicity.")
    1017              :             END SELECT
    1018              : 
    1019              :          END DO
    1020              : 
    1021              :       ELSE
    1022              : 
    1023            0 :          CPABORT("An error occured when setting up the k-mesh for W.")
    1024              : 
    1025              :       END IF
    1026              : 
    1027          124 :       nkp_orig = MAX(nkp_grid(1)*nkp_grid(2)*nkp_grid(3)/2, 1)
    1028              : 
    1029          124 :       nkp_extra = nkp_grid_extra(1)*nkp_grid_extra(2)*nkp_grid_extra(3)/2
    1030              : 
    1031          124 :       nkp = nkp_orig + nkp_extra
    1032              : 
    1033          496 :       kpoints_chi_eps_W%nkp_grid(1:3) = nkp_grid(1:3)
    1034          124 :       kpoints_chi_eps_W%nkp = nkp
    1035              : 
    1036          496 :       bs_env%nkp_grid_chi_eps_W_orig(1:3) = nkp_grid(1:3)
    1037          496 :       bs_env%nkp_grid_chi_eps_W_extra(1:3) = nkp_grid_extra(1:3)
    1038          124 :       bs_env%nkp_chi_eps_W_orig = nkp_orig
    1039          124 :       bs_env%nkp_chi_eps_W_orig_plus_extra = nkp
    1040              : 
    1041          620 :       ALLOCATE (kpoints_chi_eps_W%xkp(3, nkp), kpoints_chi_eps_W%wkp(nkp))
    1042          372 :       ALLOCATE (bs_env%wkp_no_extra(nkp), bs_env%wkp_s_p(nkp))
    1043              : 
    1044          124 :       CALL compute_xkp(kpoints_chi_eps_W%xkp, 1, nkp_orig, nkp_grid)
    1045          124 :       CALL compute_xkp(kpoints_chi_eps_W%xkp, nkp_orig + 1, nkp, nkp_grid_extra)
    1046              : 
    1047          496 :       n_dim = SUM(periodic)
    1048          124 :       IF (n_dim == 0) THEN
    1049              :          ! molecules
    1050           98 :          kpoints_chi_eps_W%wkp(1) = 1.0_dp
    1051           98 :          bs_env%wkp_s_p(1) = 1.0_dp
    1052           98 :          bs_env%wkp_no_extra(1) = 1.0_dp
    1053              :       ELSE
    1054              : 
    1055           26 :          n_dim_inv = 1.0_dp/REAL(n_dim, KIND=dp)
    1056              : 
    1057              :          ! k-point weights are chosen to automatically extrapolate the k-point mesh
    1058           26 :          CALL compute_wkp(kpoints_chi_eps_W%wkp(1:nkp_orig), nkp_orig, nkp_extra, n_dim_inv)
    1059              :          CALL compute_wkp(kpoints_chi_eps_W%wkp(nkp_orig + 1:nkp), &
    1060           26 :                           nkp_extra, nkp_orig, n_dim_inv)
    1061              : 
    1062         2154 :          bs_env%wkp_no_extra(1:nkp_orig) = 0.0_dp
    1063         8398 :          bs_env%wkp_no_extra(nkp_orig + 1:nkp) = 1.0_dp/REAL(nkp_extra, KIND=dp)
    1064              : 
    1065           26 :          IF (n_dim == 3) THEN
    1066              :             ! W_PQ(k) for an s-function P and a p-function Q diverges as 1/k at k=0
    1067              :             ! (instead of 1/k^2 for P and Q both being s-functions).
    1068            0 :             exp_s_p = 2.0_dp*n_dim_inv
    1069            0 :             CALL compute_wkp(bs_env%wkp_s_p(1:nkp_orig), nkp_orig, nkp_extra, exp_s_p)
    1070            0 :             CALL compute_wkp(bs_env%wkp_s_p(nkp_orig + 1:nkp), nkp_extra, nkp_orig, exp_s_p)
    1071              :          ELSE
    1072        10526 :             bs_env%wkp_s_p(1:nkp) = bs_env%wkp_no_extra(1:nkp)
    1073              :          END IF
    1074              : 
    1075              :       END IF
    1076              : 
    1077          124 :       IF (bs_env%approx_kp_extrapol) THEN
    1078            2 :          bs_env%wkp_orig = 1.0_dp/REAL(nkp_orig, KIND=dp)
    1079              :       END IF
    1080              : 
    1081              :       ! heuristic parameter: how many k-points for χ, ε, and W are used simultaneously
    1082              :       ! (less simultaneous k-points: less memory, but more computational effort because of
    1083              :       !  recomputation of V(k))
    1084          124 :       bs_env%nkp_chi_eps_W_batch = 4
    1085              : 
    1086              :       bs_env%num_chi_eps_W_batches = (bs_env%nkp_chi_eps_W_orig_plus_extra - 1)/ &
    1087          124 :                                      bs_env%nkp_chi_eps_W_batch + 1
    1088              : 
    1089          124 :       u = bs_env%unit_nr
    1090              : 
    1091          124 :       IF (u > 0 .AND. bs_env%do_periodic) THEN
    1092           13 :          WRITE (u, FMT="(T2,A)") " "
    1093           13 :          WRITE (u, FMT="(T2,1A,T71,3I4)") "K-point mesh 1 for χ, ε, W", nkp_grid(1:3)
    1094           13 :          WRITE (u, FMT="(T2,2A,T71,3I4)") "K-point mesh 2 for χ, ε, W ", &
    1095           26 :             "(for k-point extrapolation of W)", nkp_grid_extra(1:3)
    1096           13 :          WRITE (u, FMT="(T2,A,T80,L)") "Approximate the k-point extrapolation", &
    1097           26 :             bs_env%approx_kp_extrapol
    1098              :       END IF
    1099              : 
    1100          124 :       CALL timestop(handle)
    1101              : 
    1102          124 :    END SUBROUTINE setup_kpoints_chi_eps_W
    1103              : 
    1104              : ! **************************************************************************************************
    1105              : !> \brief ...
    1106              : !> \param xkp ...
    1107              : !> \param ikp_start ...
    1108              : !> \param ikp_end ...
    1109              : !> \param grid ...
    1110              : ! **************************************************************************************************
    1111          248 :    SUBROUTINE compute_xkp(xkp, ikp_start, ikp_end, grid)
    1112              : 
    1113              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: xkp
    1114              :       INTEGER                                            :: ikp_start, ikp_end
    1115              :       INTEGER, DIMENSION(3)                              :: grid
    1116              : 
    1117              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'compute_xkp'
    1118              : 
    1119              :       INTEGER                                            :: handle, i, ix, iy, iz
    1120              : 
    1121          248 :       CALL timeset(routineN, handle)
    1122              : 
    1123          248 :       i = ikp_start
    1124          620 :       DO ix = 1, grid(1)
    1125         4316 :          DO iy = 1, grid(2)
    1126        25264 :             DO iz = 1, grid(3)
    1127              : 
    1128        21196 :                IF (i > ikp_end) CYCLE
    1129              : 
    1130        10598 :                xkp(1, i) = REAL(2*ix - grid(1) - 1, KIND=dp)/(2._dp*REAL(grid(1), KIND=dp))
    1131        10598 :                xkp(2, i) = REAL(2*iy - grid(2) - 1, KIND=dp)/(2._dp*REAL(grid(2), KIND=dp))
    1132        10598 :                xkp(3, i) = REAL(2*iz - grid(3) - 1, KIND=dp)/(2._dp*REAL(grid(3), KIND=dp))
    1133        24892 :                i = i + 1
    1134              : 
    1135              :             END DO
    1136              :          END DO
    1137              :       END DO
    1138              : 
    1139          248 :       CALL timestop(handle)
    1140              : 
    1141          248 :    END SUBROUTINE compute_xkp
    1142              : 
    1143              : ! **************************************************************************************************
    1144              : !> \brief ...
    1145              : !> \param wkp ...
    1146              : !> \param nkp_1 ...
    1147              : !> \param nkp_2 ...
    1148              : !> \param exponent ...
    1149              : ! **************************************************************************************************
    1150           52 :    SUBROUTINE compute_wkp(wkp, nkp_1, nkp_2, exponent)
    1151              :       REAL(KIND=dp), DIMENSION(:)                        :: wkp
    1152              :       INTEGER                                            :: nkp_1, nkp_2
    1153              :       REAL(KIND=dp)                                      :: exponent
    1154              : 
    1155              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'compute_wkp'
    1156              : 
    1157              :       INTEGER                                            :: handle
    1158              :       REAL(KIND=dp)                                      :: nkp_ratio
    1159              : 
    1160           52 :       CALL timeset(routineN, handle)
    1161              : 
    1162           52 :       nkp_ratio = REAL(nkp_2, KIND=dp)/REAL(nkp_1, KIND=dp)
    1163              : 
    1164        10552 :       wkp(:) = 1.0_dp/REAL(nkp_1, KIND=dp)/(1.0_dp - nkp_ratio**exponent)
    1165              : 
    1166           52 :       CALL timestop(handle)
    1167              : 
    1168           52 :    END SUBROUTINE compute_wkp
    1169              : 
    1170              : ! **************************************************************************************************
    1171              : !> \brief ...
    1172              : !> \param qs_env ...
    1173              : !> \param bs_env ...
    1174              : ! **************************************************************************************************
    1175           16 :    SUBROUTINE setup_cells_3c(qs_env, bs_env)
    1176              : 
    1177              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1178              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1179              : 
    1180              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'setup_cells_3c'
    1181              : 
    1182              :       INTEGER :: atom_i, atom_j, atom_k, block_count, handle, i, i_cell_x, i_cell_x_max, &
    1183              :          i_cell_x_min, i_size, ikind, img, j, j_cell, j_cell_max, j_cell_y, j_cell_y_max, &
    1184              :          j_cell_y_min, j_size, k_cell, k_cell_max, k_cell_z, k_cell_z_max, k_cell_z_min, k_size, &
    1185              :          nimage_pairs_3c, nimages_3c, nimages_3c_max, nkind, u
    1186           16 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: kind_of, n_other_3c_images_max
    1187           16 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: index_to_cell_3c_max, nblocks_3c_max
    1188              :       INTEGER, DIMENSION(3)                              :: cell_index, n_max
    1189              :       REAL(KIND=dp) :: avail_mem_per_proc_GB, cell_dist, cell_radius_3c, dij, dik, djk, eps, &
    1190              :          exp_min_ao, exp_min_RI, frobenius_norm, mem_3c_GB, mem_occ_per_proc_GB, radius_ao, &
    1191              :          radius_ao_product, radius_RI
    1192           16 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: exp_ao_kind, exp_RI_kind, &
    1193           16 :                                                             radius_ao_kind, &
    1194           16 :                                                             radius_ao_product_kind, radius_RI_kind
    1195           16 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :)     :: int_3c
    1196              :       REAL(KIND=dp), DIMENSION(3)                        :: rij, rik, rjk, vec_cell_j, vec_cell_k
    1197           16 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: exp_ao, exp_RI
    1198           16 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
    1199              :       TYPE(cell_type), POINTER                           :: cell
    1200           16 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
    1201              : 
    1202           16 :       CALL timeset(routineN, handle)
    1203              : 
    1204           16 :       CALL get_qs_env(qs_env, nkind=nkind, atomic_kind_set=atomic_kind_set, particle_set=particle_set, cell=cell)
    1205              : 
    1206              :       ALLOCATE (exp_ao_kind(nkind), exp_RI_kind(nkind), radius_ao_kind(nkind), &
    1207          112 :                 radius_ao_product_kind(nkind), radius_RI_kind(nkind))
    1208              : 
    1209           48 :       exp_min_RI = 10.0_dp
    1210           48 :       exp_min_ao = 10.0_dp
    1211           48 :       exp_RI_kind = 10.0_dp
    1212           48 :       exp_AO_kind = 10.0_dp
    1213              : 
    1214           16 :       eps = bs_env%eps_filter*bs_env%heuristic_filter_factor
    1215              : 
    1216           48 :       DO ikind = 1, nkind
    1217              : 
    1218           32 :          CALL get_gto_basis_set(bs_env%basis_set_RI(ikind)%gto_basis_set, zet=exp_RI)
    1219           32 :          CALL get_gto_basis_set(bs_env%basis_set_ao(ikind)%gto_basis_set, zet=exp_ao)
    1220              : 
    1221              :          ! we need to remove all exponents lower than a lower bound, e.g. 1E-3, because
    1222              :          ! for contracted basis sets, there might be exponents = 0 in zet
    1223           64 :          DO i = 1, SIZE(exp_RI, 1)
    1224          112 :             DO j = 1, SIZE(exp_RI, 2)
    1225           48 :                IF (exp_RI(i, j) < exp_min_RI .AND. exp_RI(i, j) > 1E-3_dp) exp_min_RI = exp_RI(i, j)
    1226           80 :                IF (exp_RI(i, j) < exp_RI_kind(ikind) .AND. exp_RI(i, j) > 1E-3_dp) THEN
    1227           32 :                   exp_RI_kind(ikind) = exp_RI(i, j)
    1228              :                END IF
    1229              :             END DO
    1230              :          END DO
    1231          160 :          DO i = 1, SIZE(exp_ao, 1)
    1232          384 :             DO j = 1, SIZE(exp_ao, 2)
    1233          224 :                IF (exp_ao(i, j) < exp_min_ao .AND. exp_ao(i, j) > 1E-3_dp) exp_min_ao = exp_ao(i, j)
    1234          352 :                IF (exp_ao(i, j) < exp_ao_kind(ikind) .AND. exp_ao(i, j) > 1E-3_dp) THEN
    1235           96 :                   exp_ao_kind(ikind) = exp_ao(i, j)
    1236              :                END IF
    1237              :             END DO
    1238              :          END DO
    1239           32 :          radius_ao_kind(ikind) = SQRT(-LOG(eps)/exp_ao_kind(ikind))
    1240           32 :          radius_ao_product_kind(ikind) = SQRT(-LOG(eps)/(2.0_dp*exp_ao_kind(ikind)))
    1241           48 :          radius_RI_kind(ikind) = SQRT(-LOG(eps)/exp_RI_kind(ikind))
    1242              :       END DO
    1243              : 
    1244           16 :       radius_ao = SQRT(-LOG(eps)/exp_min_ao)
    1245           16 :       radius_ao_product = SQRT(-LOG(eps)/(2.0_dp*exp_min_ao))
    1246           16 :       radius_RI = SQRT(-LOG(eps)/exp_min_RI)
    1247              : 
    1248           16 :       CALL get_atomic_kind_set(atomic_kind_set=atomic_kind_set, kind_of=kind_of)
    1249              : 
    1250              :       ! For a 3c integral (μR υS | P0) we have that cell R and cell S need to be within radius_3c
    1251           16 :       cell_radius_3c = radius_ao_product + radius_RI + bs_env%ri_metric%cutoff_radius
    1252              : 
    1253           64 :       n_max(1:3) = bs_env%periodic(1:3)*30
    1254              : 
    1255           16 :       nimages_3c_max = 0
    1256              : 
    1257           16 :       i_cell_x_min = 0
    1258           16 :       i_cell_x_max = 0
    1259           16 :       j_cell_y_min = 0
    1260           16 :       j_cell_y_max = 0
    1261           16 :       k_cell_z_min = 0
    1262           16 :       k_cell_z_max = 0
    1263              : 
    1264          152 :       DO i_cell_x = -n_max(1), n_max(1)
    1265         8448 :          DO j_cell_y = -n_max(2), n_max(2)
    1266        67968 :             DO k_cell_z = -n_max(3), n_max(3)
    1267              : 
    1268       238144 :                cell_index(1:3) = [i_cell_x, j_cell_y, k_cell_z]
    1269              : 
    1270        59536 :                CALL get_cell_dist(cell_index, bs_env%hmat, cell_dist)
    1271              : 
    1272        67832 :                IF (cell_dist < cell_radius_3c) THEN
    1273          392 :                   nimages_3c_max = nimages_3c_max + 1
    1274          392 :                   i_cell_x_min = MIN(i_cell_x_min, i_cell_x)
    1275          392 :                   i_cell_x_max = MAX(i_cell_x_max, i_cell_x)
    1276          392 :                   j_cell_y_min = MIN(j_cell_y_min, j_cell_y)
    1277          392 :                   j_cell_y_max = MAX(j_cell_y_max, j_cell_y)
    1278          392 :                   k_cell_z_min = MIN(k_cell_z_min, k_cell_z)
    1279          392 :                   k_cell_z_max = MAX(k_cell_z_max, k_cell_z)
    1280              :                END IF
    1281              : 
    1282              :             END DO
    1283              :          END DO
    1284              :       END DO
    1285              : 
    1286              :       ! get index_to_cell_3c_max for the maximum possible cell range;
    1287              :       ! compute 3c integrals later in this routine and check really which cell is needed
    1288           48 :       ALLOCATE (index_to_cell_3c_max(3, nimages_3c_max))
    1289              : 
    1290           16 :       img = 0
    1291          152 :       DO i_cell_x = -n_max(1), n_max(1)
    1292         8448 :          DO j_cell_y = -n_max(2), n_max(2)
    1293        67968 :             DO k_cell_z = -n_max(3), n_max(3)
    1294              : 
    1295       238144 :                cell_index(1:3) = [i_cell_x, j_cell_y, k_cell_z]
    1296              : 
    1297        59536 :                CALL get_cell_dist(cell_index, bs_env%hmat, cell_dist)
    1298              : 
    1299        67832 :                IF (cell_dist < cell_radius_3c) THEN
    1300          392 :                   img = img + 1
    1301         1568 :                   index_to_cell_3c_max(1:3, img) = cell_index(1:3)
    1302              :                END IF
    1303              : 
    1304              :             END DO
    1305              :          END DO
    1306              :       END DO
    1307              : 
    1308              :       ! get pairs of R and S which have non-zero 3c integral (μR υS | P0)
    1309           64 :       ALLOCATE (nblocks_3c_max(nimages_3c_max, nimages_3c_max))
    1310           16 :       nblocks_3c_max(:, :) = 0
    1311              : 
    1312           16 :       block_count = 0
    1313          408 :       DO j_cell = 1, nimages_3c_max
    1314        10040 :          DO k_cell = 1, nimages_3c_max
    1315              : 
    1316        38038 :             DO atom_j = 1, bs_env%n_atom
    1317       119924 :             DO atom_k = 1, bs_env%n_atom
    1318       353598 :             DO atom_i = 1, bs_env%n_atom
    1319              : 
    1320       243306 :                block_count = block_count + 1
    1321       243306 :                IF (MODULO(block_count, bs_env%para_env%num_pe) /= bs_env%para_env%mepos) CYCLE
    1322              : 
    1323       486612 :                CALL scaled_to_real(vec_cell_j, REAL(index_to_cell_3c_max(1:3, j_cell), kind=dp), cell)
    1324       486612 :                CALL scaled_to_real(vec_cell_k, REAL(index_to_cell_3c_max(1:3, k_cell), kind=dp), cell)
    1325              : 
    1326       486612 :                rij = pbc(particle_set(atom_j)%r(:), cell) - pbc(particle_set(atom_i)%r(:), cell) + vec_cell_j(:)
    1327              :                rjk = pbc(particle_set(atom_k)%r(:), cell) - pbc(particle_set(atom_j)%r(:), cell) &
    1328       486612 :                      + vec_cell_k(:) - vec_cell_j(:)
    1329       486612 :                rik(:) = rij(:) + rjk(:)
    1330       486612 :                dij = NORM2(rij)
    1331       486612 :                dik = NORM2(rik)
    1332       486612 :                djk = NORM2(rjk)
    1333       121653 :                IF (djk > radius_ao_kind(kind_of(atom_j)) + radius_ao_kind(kind_of(atom_k))) CYCLE
    1334        38127 :                IF (dij > radius_ao_kind(kind_of(atom_j)) + radius_RI_kind(kind_of(atom_i)) &
    1335              :                    + bs_env%ri_metric%cutoff_radius) CYCLE
    1336        19497 :                IF (dik > radius_RI_kind(kind_of(atom_i)) + radius_ao_kind(kind_of(atom_k)) &
    1337              :                    + bs_env%ri_metric%cutoff_radius) CYCLE
    1338              : 
    1339        12822 :                j_size = bs_env%i_ao_end_from_atom(atom_j) - bs_env%i_ao_start_from_atom(atom_j) + 1
    1340        12822 :                k_size = bs_env%i_ao_end_from_atom(atom_k) - bs_env%i_ao_start_from_atom(atom_k) + 1
    1341        12822 :                i_size = bs_env%i_RI_end_from_atom(atom_i) - bs_env%i_RI_start_from_atom(atom_i) + 1
    1342              : 
    1343        64110 :                ALLOCATE (int_3c(j_size, k_size, i_size))
    1344              : 
    1345              :                ! compute 3-c int. ( μ(atom j) R , ν (atom k) S | P (atom i) 0 )
    1346              :                ! ("|": truncated Coulomb operator), inside build_3c_integrals: (j k | i)
    1347              :                CALL build_3c_integral_block(int_3c, qs_env, bs_env%ri_metric, &
    1348              :                                             basis_j=bs_env%basis_set_AO, &
    1349              :                                             basis_k=bs_env%basis_set_AO, &
    1350              :                                             basis_i=bs_env%basis_set_RI, &
    1351              :                                             cell_j=index_to_cell_3c_max(1:3, j_cell), &
    1352              :                                             cell_k=index_to_cell_3c_max(1:3, k_cell), &
    1353        12822 :                                             atom_k=atom_k, atom_j=atom_j, atom_i=atom_i)
    1354              : 
    1355       678851 :                frobenius_norm = NORM2(int_3c)
    1356              : 
    1357        12822 :                DEALLOCATE (int_3c)
    1358              : 
    1359              :                ! we use a higher threshold here to safe memory when storing the 3c integrals
    1360              :                ! in every tensor group
    1361        95100 :                IF (frobenius_norm > eps) THEN
    1362         2720 :                   nblocks_3c_max(j_cell, k_cell) = nblocks_3c_max(j_cell, k_cell) + 1
    1363              :                END IF
    1364              : 
    1365              :             END DO
    1366              :             END DO
    1367              :             END DO
    1368              : 
    1369              :          END DO
    1370              :       END DO
    1371              : 
    1372           16 :       CALL bs_env%para_env%sum(nblocks_3c_max)
    1373              : 
    1374           48 :       ALLOCATE (n_other_3c_images_max(nimages_3c_max))
    1375           16 :       n_other_3c_images_max(:) = 0
    1376              : 
    1377           16 :       nimages_3c = 0
    1378           16 :       nimage_pairs_3c = 0
    1379              : 
    1380          408 :       DO j_cell = 1, nimages_3c_max
    1381        10024 :          DO k_cell = 1, nimages_3c_max
    1382        10024 :             IF (nblocks_3c_max(j_cell, k_cell) > 0) THEN
    1383          960 :                n_other_3c_images_max(j_cell) = n_other_3c_images_max(j_cell) + 1
    1384          960 :                nimage_pairs_3c = nimage_pairs_3c + 1
    1385              :             END IF
    1386              :          END DO
    1387              : 
    1388          408 :          IF (n_other_3c_images_max(j_cell) > 0) nimages_3c = nimages_3c + 1
    1389              : 
    1390              :       END DO
    1391              : 
    1392           16 :       bs_env%nimages_3c = nimages_3c
    1393           48 :       ALLOCATE (bs_env%index_to_cell_3c(3, nimages_3c))
    1394              :       ALLOCATE (bs_env%cell_to_index_3c(i_cell_x_min:i_cell_x_max, &
    1395              :                                         j_cell_y_min:j_cell_y_max, &
    1396           80 :                                         k_cell_z_min:k_cell_z_max))
    1397          848 :       bs_env%cell_to_index_3c(:, :, :) = -1
    1398              : 
    1399           64 :       ALLOCATE (bs_env%nblocks_3c(nimages_3c, nimages_3c))
    1400           16 :       bs_env%nblocks_3c(nimages_3c, nimages_3c) = 0
    1401              : 
    1402           16 :       j_cell = 0
    1403          408 :       DO j_cell_max = 1, nimages_3c_max
    1404          392 :          IF (n_other_3c_images_max(j_cell_max) == 0) CYCLE
    1405          178 :          j_cell = j_cell + 1
    1406          712 :          cell_index(1:3) = index_to_cell_3c_max(1:3, j_cell_max)
    1407          712 :          bs_env%index_to_cell_3c(1:3, j_cell) = cell_index(1:3)
    1408          178 :          bs_env%cell_to_index_3c(cell_index(1), cell_index(2), cell_index(3)) = j_cell
    1409              : 
    1410          178 :          k_cell = 0
    1411         4604 :          DO k_cell_max = 1, nimages_3c_max
    1412         4410 :             IF (n_other_3c_images_max(k_cell_max) == 0) CYCLE
    1413         2066 :             k_cell = k_cell + 1
    1414              : 
    1415         4802 :             bs_env%nblocks_3c(j_cell, k_cell) = nblocks_3c_max(j_cell_max, k_cell_max)
    1416              :          END DO
    1417              : 
    1418              :       END DO
    1419              : 
    1420              :       ! we use: 8*10^-9 GB / double precision number
    1421              :       mem_3c_GB = REAL(bs_env%n_RI, KIND=dp)*REAL(bs_env%n_ao, KIND=dp)**2 &
    1422           16 :                   *REAL(nimage_pairs_3c, KIND=dp)*8E-9_dp
    1423              : 
    1424           16 :       CALL mp_mem_used_per_rank_GB(bs_env%para_env, mem_occ_per_proc_GB)
    1425              : 
    1426              :       ! number of processors per group that entirely stores the 3c integrals and does tensor ops
    1427           16 :       avail_mem_per_proc_GB = bs_env%input_memory_per_proc_GB - mem_occ_per_proc_GB
    1428              : 
    1429              :       ! careful: downconvering real to integer, 1.9 -> 1; thus add 1.0 for upconversion, 1.9 -> 2
    1430           16 :       bs_env%group_size_tensor = MAX(INT(mem_3c_GB/avail_mem_per_proc_GB + 1.0_dp), 1)
    1431              : 
    1432           16 :       u = bs_env%unit_nr
    1433              : 
    1434           16 :       IF (u > 0) THEN
    1435            8 :          WRITE (u, FMT="(T2,A,F52.1,A)") "Radius of atomic orbitals", radius_ao*angstrom, " Å"
    1436            8 :          WRITE (u, FMT="(T2,A,F55.1,A)") "Radius of RI functions", radius_RI*angstrom, " Å"
    1437            8 :          WRITE (u, FMT="(T2,A,I47)") "Number of cells for 3c integrals", nimages_3c
    1438            8 :          WRITE (u, FMT="(T2,A,I42)") "Number of cell pairs for 3c integrals", nimage_pairs_3c
    1439            8 :          WRITE (u, '(T2,A)') ''
    1440            8 :          IF (bs_env%auto_memory_per_proc) THEN
    1441            8 :             WRITE (u, '(T2,A,F34.1,A)') 'Detected: Available memory per MPI process', &
    1442           16 :                bs_env%input_memory_per_proc_GB, ' GB'
    1443              :          ELSE
    1444            0 :             WRITE (u, '(T2,A,F37.1,A)') 'Input: Available memory per MPI process', &
    1445            0 :                bs_env%input_memory_per_proc_GB, ' GB'
    1446              :          END IF
    1447            8 :          WRITE (u, '(T2,A,F35.1,A)') 'Used memory per MPI process before GW run', &
    1448           16 :             mem_occ_per_proc_GB, ' GB'
    1449            8 :          WRITE (u, '(T2,A,F44.1,A)') 'Memory of three-center integrals', mem_3c_GB, ' GB'
    1450              :       END IF
    1451              : 
    1452           16 :       CALL timestop(handle)
    1453              : 
    1454           32 :    END SUBROUTINE setup_cells_3c
    1455              : 
    1456              : ! **************************************************************************************************
    1457              : !> \brief ...
    1458              : !> \param cell_index ...
    1459              : !> \param hmat ...
    1460              : !> \param cell_dist ...
    1461              : ! **************************************************************************************************
    1462       119072 :    SUBROUTINE get_cell_dist(cell_index, hmat, cell_dist)
    1463              : 
    1464              :       INTEGER, DIMENSION(3)                              :: cell_index
    1465              :       REAL(KIND=dp)                                      :: hmat(3, 3), cell_dist
    1466              : 
    1467              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'get_cell_dist'
    1468              : 
    1469              :       INTEGER                                            :: handle, i_dim
    1470              :       INTEGER, DIMENSION(3)                              :: cell_index_adj
    1471              :       REAL(KIND=dp)                                      :: cell_dist_3(3)
    1472              : 
    1473       119072 :       CALL timeset(routineN, handle)
    1474              : 
    1475              :       ! the distance of cells needs to be taken to adjacent neighbors, not
    1476              :       ! between the center of the cells. We thus need to rescale the cell index
    1477       476288 :       DO i_dim = 1, 3
    1478       357216 :          IF (cell_index(i_dim) > 0) cell_index_adj(i_dim) = cell_index(i_dim) - 1
    1479       357216 :          IF (cell_index(i_dim) < 0) cell_index_adj(i_dim) = cell_index(i_dim) + 1
    1480       476288 :          IF (cell_index(i_dim) == 0) cell_index_adj(i_dim) = cell_index(i_dim)
    1481              :       END DO
    1482              : 
    1483      1905152 :       cell_dist_3(1:3) = MATMUL(hmat, REAL(cell_index_adj, KIND=dp))
    1484              : 
    1485       476288 :       cell_dist = NORM2(cell_dist_3)
    1486              : 
    1487       119072 :       CALL timestop(handle)
    1488              : 
    1489       119072 :    END SUBROUTINE get_cell_dist
    1490              : 
    1491              : ! **************************************************************************************************
    1492              : !> \brief ...
    1493              : !> \param qs_env ...
    1494              : !> \param bs_env ...
    1495              : ! **************************************************************************************************
    1496          124 :    SUBROUTINE set_parallelization_parameters(qs_env, bs_env)
    1497              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1498              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1499              : 
    1500              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'set_parallelization_parameters'
    1501              : 
    1502              :       INTEGER                                            :: color_sub, dummy_1, dummy_2, handle, &
    1503              :                                                             num_pe, num_t_groups, u
    1504              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1505              : 
    1506          124 :       CALL timeset(routineN, handle)
    1507              : 
    1508          124 :       CALL get_qs_env(qs_env, para_env=para_env)
    1509              : 
    1510          124 :       num_pe = para_env%num_pe
    1511              :       ! if not already set, use all processors for the group (for large-cell GW, performance
    1512              :       ! seems to be best for a single group with all MPI processes per group)
    1513          124 :       IF (bs_env%group_size_tensor < 0 .OR. bs_env%group_size_tensor > num_pe) THEN
    1514          108 :          bs_env%group_size_tensor = num_pe
    1515              :       END IF
    1516              : 
    1517              :       ! group_size_tensor must divide num_pe without rest; otherwise everything will be complicated
    1518          124 :       IF (MODULO(num_pe, bs_env%group_size_tensor) /= 0) THEN
    1519            0 :          CALL find_good_group_size(num_pe, bs_env%group_size_tensor)
    1520              :       END IF
    1521              : 
    1522              :       ! para_env_tensor for tensor subgroups
    1523          124 :       color_sub = para_env%mepos/bs_env%group_size_tensor
    1524          124 :       bs_env%tensor_group_color = color_sub
    1525              : 
    1526          124 :       ALLOCATE (bs_env%para_env_tensor)
    1527          124 :       CALL bs_env%para_env_tensor%from_split(para_env, color_sub)
    1528              : 
    1529          124 :       num_t_groups = para_env%num_pe/bs_env%group_size_tensor
    1530          124 :       bs_env%num_tensor_groups = num_t_groups
    1531              : 
    1532              :       CALL get_i_j_atoms(bs_env%atoms_i, bs_env%atoms_j, bs_env%n_atom_i, bs_env%n_atom_j, &
    1533          124 :                          color_sub, bs_env)
    1534              : 
    1535          372 :       ALLOCATE (bs_env%atoms_i_t_group(2, num_t_groups))
    1536          248 :       ALLOCATE (bs_env%atoms_j_t_group(2, num_t_groups))
    1537          264 :       DO color_sub = 0, num_t_groups - 1
    1538              :          CALL get_i_j_atoms(bs_env%atoms_i_t_group(1:2, color_sub + 1), &
    1539              :                             bs_env%atoms_j_t_group(1:2, color_sub + 1), &
    1540          264 :                             dummy_1, dummy_2, color_sub, bs_env)
    1541              :       END DO
    1542              : 
    1543          124 :       u = bs_env%unit_nr
    1544          124 :       IF (u > 0 .AND. .NOT. bs_env%do_gw_ri_rs) THEN
    1545           42 :          WRITE (u, '(T2,A,I47)') 'Group size for tensor operations', bs_env%group_size_tensor
    1546           42 :          IF (bs_env%group_size_tensor > 1 .AND. bs_env%n_atom < 5) THEN
    1547           34 :             WRITE (u, '(T2,A)') 'The requested group size is > 1 which can lead to bad performance.'
    1548           34 :             WRITE (u, '(T2,A)') 'Using more memory per MPI process might improve performance.'
    1549           34 :             WRITE (u, '(T2,A)') '(Also increase MEMORY_PER_PROC when using more memory per process.)'
    1550              :          END IF
    1551              :       END IF
    1552              : 
    1553          124 :       CALL timestop(handle)
    1554              : 
    1555          124 :    END SUBROUTINE set_parallelization_parameters
    1556              : 
    1557              : ! **************************************************************************************************
    1558              : !> \brief ...
    1559              : !> \param num_pe ...
    1560              : !> \param group_size ...
    1561              : ! **************************************************************************************************
    1562            0 :    SUBROUTINE find_good_group_size(num_pe, group_size)
    1563              : 
    1564              :       INTEGER                                            :: num_pe, group_size
    1565              : 
    1566              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'find_good_group_size'
    1567              : 
    1568              :       INTEGER                                            :: group_size_minus, group_size_orig, &
    1569              :                                                             group_size_plus, handle, i_diff
    1570              : 
    1571            0 :       CALL timeset(routineN, handle)
    1572              : 
    1573            0 :       group_size_orig = group_size
    1574              : 
    1575            0 :       DO i_diff = 1, num_pe
    1576              : 
    1577            0 :          group_size_minus = group_size - i_diff
    1578              : 
    1579            0 :          IF (MODULO(num_pe, group_size_minus) == 0 .AND. group_size_minus > 0) THEN
    1580            0 :             group_size = group_size_minus
    1581            0 :             EXIT
    1582              :          END IF
    1583              : 
    1584            0 :          group_size_plus = group_size + i_diff
    1585              : 
    1586            0 :          IF (MODULO(num_pe, group_size_plus) == 0 .AND. group_size_plus <= num_pe) THEN
    1587            0 :             group_size = group_size_plus
    1588            0 :             EXIT
    1589              :          END IF
    1590              : 
    1591              :       END DO
    1592              : 
    1593            0 :       IF (group_size_orig == group_size) CPABORT("Group size error")
    1594              : 
    1595            0 :       CALL timestop(handle)
    1596              : 
    1597            0 :    END SUBROUTINE find_good_group_size
    1598              : 
    1599              : ! **************************************************************************************************
    1600              : !> \brief ...
    1601              : !> \param atoms_i ...
    1602              : !> \param atoms_j ...
    1603              : !> \param n_atom_i ...
    1604              : !> \param n_atom_j ...
    1605              : !> \param color_sub ...
    1606              : !> \param bs_env ...
    1607              : ! **************************************************************************************************
    1608          264 :    SUBROUTINE get_i_j_atoms(atoms_i, atoms_j, n_atom_i, n_atom_j, color_sub, bs_env)
    1609              : 
    1610              :       INTEGER, DIMENSION(2)                              :: atoms_i, atoms_j
    1611              :       INTEGER                                            :: n_atom_i, n_atom_j, color_sub
    1612              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1613              : 
    1614              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'get_i_j_atoms'
    1615              : 
    1616              :       INTEGER                                            :: handle, i_atoms_per_group, i_group, &
    1617              :                                                             ipcol, ipcol_loop, iprow, iprow_loop, &
    1618              :                                                             j_atoms_per_group, npcol, nprow
    1619              : 
    1620          264 :       CALL timeset(routineN, handle)
    1621              : 
    1622              :       ! create a square mesh of tensor groups for iatom and jatom; code from blacs_env_create
    1623          264 :       CALL square_mesh(nprow, npcol, bs_env%num_tensor_groups)
    1624              : 
    1625          264 :       i_group = 0
    1626          528 :       DO ipcol_loop = 0, npcol - 1
    1627          840 :          DO iprow_loop = 0, nprow - 1
    1628          312 :             IF (i_group == color_sub) THEN
    1629          264 :                iprow = iprow_loop
    1630          264 :                ipcol = ipcol_loop
    1631              :             END IF
    1632          576 :             i_group = i_group + 1
    1633              :          END DO
    1634              :       END DO
    1635              : 
    1636          264 :       IF (MODULO(bs_env%n_atom, nprow) == 0) THEN
    1637          222 :          i_atoms_per_group = bs_env%n_atom/nprow
    1638              :       ELSE
    1639           42 :          i_atoms_per_group = bs_env%n_atom/nprow + 1
    1640              :       END IF
    1641              : 
    1642          264 :       IF (MODULO(bs_env%n_atom, npcol) == 0) THEN
    1643          264 :          j_atoms_per_group = bs_env%n_atom/npcol
    1644              :       ELSE
    1645            0 :          j_atoms_per_group = bs_env%n_atom/npcol + 1
    1646              :       END IF
    1647              : 
    1648          264 :       atoms_i(1) = iprow*i_atoms_per_group + 1
    1649          264 :       atoms_i(2) = MIN((iprow + 1)*i_atoms_per_group, bs_env%n_atom)
    1650          264 :       n_atom_i = atoms_i(2) - atoms_i(1) + 1
    1651              : 
    1652          264 :       atoms_j(1) = ipcol*j_atoms_per_group + 1
    1653          264 :       atoms_j(2) = MIN((ipcol + 1)*j_atoms_per_group, bs_env%n_atom)
    1654          264 :       n_atom_j = atoms_j(2) - atoms_j(1) + 1
    1655              : 
    1656          264 :       CALL timestop(handle)
    1657              : 
    1658          264 :    END SUBROUTINE get_i_j_atoms
    1659              : 
    1660              : ! **************************************************************************************************
    1661              : !> \brief ...
    1662              : !> \param nprow ...
    1663              : !> \param npcol ...
    1664              : !> \param nproc ...
    1665              : ! **************************************************************************************************
    1666          264 :    SUBROUTINE square_mesh(nprow, npcol, nproc)
    1667              :       INTEGER                                            :: nprow, npcol, nproc
    1668              : 
    1669              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'square_mesh'
    1670              : 
    1671              :       INTEGER                                            :: gcd_max, handle, ipe, jpe
    1672              : 
    1673          264 :       CALL timeset(routineN, handle)
    1674              : 
    1675          264 :       gcd_max = -1
    1676          576 :       DO ipe = 1, CEILING(SQRT(REAL(nproc, dp)))
    1677          312 :          jpe = nproc/ipe
    1678          312 :          IF (ipe*jpe /= nproc) CYCLE
    1679          576 :          IF (gcd(ipe, jpe) >= gcd_max) THEN
    1680          312 :             nprow = ipe
    1681          312 :             npcol = jpe
    1682          312 :             gcd_max = gcd(ipe, jpe)
    1683              :          END IF
    1684              :       END DO
    1685              : 
    1686          264 :       CALL timestop(handle)
    1687              : 
    1688          264 :    END SUBROUTINE square_mesh
    1689              : 
    1690              : ! **************************************************************************************************
    1691              : !> \brief ...
    1692              : !> \param qs_env ...
    1693              : !> \param bs_env ...
    1694              : ! **************************************************************************************************
    1695          124 :    SUBROUTINE allocate_matrices(qs_env, bs_env)
    1696              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1697              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1698              : 
    1699              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'allocate_matrices'
    1700              : 
    1701              :       INTEGER                                            :: handle, i_t
    1702              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env, blacs_env_tensor
    1703              :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct, fm_struct_RI_global
    1704              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1705              : 
    1706          124 :       CALL timeset(routineN, handle)
    1707              : 
    1708          124 :       CALL get_qs_env(qs_env, para_env=para_env, blacs_env=blacs_env)
    1709              : 
    1710          124 :       fm_struct => bs_env%fm_ks_Gamma(1)%matrix_struct
    1711              : 
    1712          124 :       CALL cp_fm_create(bs_env%fm_Gocc, fm_struct)
    1713          124 :       CALL cp_fm_create(bs_env%fm_Gvir, fm_struct)
    1714              : 
    1715          124 :       NULLIFY (fm_struct_RI_global)
    1716              :       CALL cp_fm_struct_create(fm_struct_RI_global, context=blacs_env, nrow_global=bs_env%n_RI, &
    1717          124 :                                ncol_global=bs_env%n_RI, para_env=para_env)
    1718          124 :       CALL cp_fm_create(bs_env%fm_RI_RI, fm_struct_RI_global)
    1719          124 :       CALL cp_fm_create(bs_env%fm_chi_Gamma_freq, fm_struct_RI_global)
    1720          124 :       CALL cp_fm_create(bs_env%fm_W_MIC_freq, fm_struct_RI_global)
    1721          124 :       IF (bs_env%approx_kp_extrapol) THEN
    1722            2 :          CALL cp_fm_create(bs_env%fm_W_MIC_freq_1_extra, fm_struct_RI_global)
    1723            2 :          CALL cp_fm_create(bs_env%fm_W_MIC_freq_1_no_extra, fm_struct_RI_global)
    1724            2 :          CALL cp_fm_set_all(bs_env%fm_W_MIC_freq_1_extra, 0.0_dp)
    1725            2 :          CALL cp_fm_set_all(bs_env%fm_W_MIC_freq_1_no_extra, 0.0_dp)
    1726              :       END IF
    1727          124 :       CALL cp_fm_struct_release(fm_struct_RI_global)
    1728              : 
    1729          124 :       IF (.NOT. bs_env%do_gw_ri_rs) THEN
    1730              :          ! create blacs_env for subgroups of tensor operations
    1731           84 :          NULLIFY (blacs_env_tensor)
    1732           84 :          CALL cp_blacs_env_create(blacs_env=blacs_env_tensor, para_env=bs_env%para_env_tensor)
    1733              : 
    1734              :          ! allocate dbcsr matrices in the tensor subgroup; actually, one only needs a small
    1735              :          ! subset of blocks in the tensor subgroup, however, all atomic blocks are allocated.
    1736              :          ! One might think of creating a dbcsr matrix with only the blocks that are needed
    1737              :          ! in the tensor subgroup
    1738              :          CALL create_mat_munu(bs_env%mat_ao_ao_tensor, qs_env, bs_env%eps_atom_grid_2d_mat, &
    1739           84 :                               blacs_env_tensor, do_ri_aux_basis=.FALSE.)
    1740              : 
    1741              :          CALL create_mat_munu(bs_env%mat_RI_RI_tensor, qs_env, bs_env%eps_atom_grid_2d_mat, &
    1742           84 :                               blacs_env_tensor, do_ri_aux_basis=.TRUE.)
    1743              : 
    1744           84 :          CALL cp_blacs_env_release(blacs_env_tensor)
    1745              :       END IF
    1746              : 
    1747              :       CALL create_mat_munu(bs_env%mat_RI_RI, qs_env, bs_env%eps_atom_grid_2d_mat, &
    1748              :                            blacs_env, do_ri_aux_basis=.TRUE., &
    1749          124 :                            custom_row_blk_sizes=bs_env%sizes_RI)
    1750              : 
    1751          124 :       NULLIFY (bs_env%mat_chi_Gamma_tau)
    1752          124 :       CALL dbcsr_allocate_matrix_set(bs_env%mat_chi_Gamma_tau, bs_env%num_time_freq_points)
    1753              : 
    1754         2022 :       DO i_t = 1, bs_env%num_time_freq_points
    1755         1898 :          ALLOCATE (bs_env%mat_chi_Gamma_tau(i_t)%matrix)
    1756         2022 :          CALL dbcsr_create(bs_env%mat_chi_Gamma_tau(i_t)%matrix, template=bs_env%mat_RI_RI%matrix)
    1757              :       END DO
    1758              : 
    1759          124 :       CALL timestop(handle)
    1760              : 
    1761          124 :    END SUBROUTINE allocate_matrices
    1762              : 
    1763              : ! **************************************************************************************************
    1764              : !> \brief Computes and stores the inverse RI metric at the Γ point.
    1765              : !> \param qs_env ...
    1766              : !> \param bs_env ...
    1767              : ! **************************************************************************************************
    1768          108 :    SUBROUTINE compute_Minv_Gamma(qs_env, bs_env)
    1769              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1770              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1771              : 
    1772              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_Minv_Gamma'
    1773              : 
    1774              :       INTEGER                                            :: handle
    1775              :       REAL(KIND=dp)                                      :: eigenvalue_threshold, &
    1776              :                                                             metric_regularization
    1777          108 :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :)     :: fm_M
    1778              : 
    1779          108 :       CALL timeset(routineN, handle)
    1780              : 
    1781          108 :       CALL cp_fm_create(bs_env%fm_Minv_Gamma, bs_env%fm_RI_RI%matrix_struct)
    1782          108 :       IF (bs_env%auto_ri%enabled) THEN
    1783            4 :          CPASSERT(bs_env%auto_ri%ready)
    1784            4 :          CALL cp_fm_to_fm(bs_env%auto_ri%M_pq_inv, bs_env%fm_Minv_Gamma)
    1785              :       ELSE
    1786          104 :          metric_regularization = 0.0_dp
    1787          104 :          eigenvalue_threshold = 0.0_dp
    1788          104 :          IF (bs_env%do_gw_ri_rs) THEN
    1789           36 :             metric_regularization = bs_env%regularization_RI
    1790           36 :             eigenvalue_threshold = bs_env%eps_eigval_mat_RI
    1791              :          END IF
    1792              :          CALL RI_2c_integral_mat(qs_env, fm_M, bs_env%fm_RI_RI, bs_env%n_RI, &
    1793              :                                  bs_env%ri_metric, do_kpoints=.FALSE., &
    1794          104 :                                  regularization_RI=metric_regularization)
    1795          104 :          CALL fm_invert(fm_M(1, 1), eigenvalue_threshold, bs_env%unit_nr)
    1796          104 :          CALL cp_fm_to_fm(fm_M(1, 1), bs_env%fm_Minv_Gamma)
    1797          104 :          CALL cp_fm_release(fm_M)
    1798              :       END IF
    1799              : 
    1800          108 :       CALL timestop(handle)
    1801              : 
    1802          108 :    END SUBROUTINE compute_Minv_Gamma
    1803              : 
    1804              : ! **************************************************************************************************
    1805              : !> \brief ...
    1806              : !> \param qs_env ...
    1807              : !> \param bs_env ...
    1808              : ! **************************************************************************************************
    1809          248 :    SUBROUTINE compute_V_xc(qs_env, bs_env)
    1810              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1811              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1812              : 
    1813              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'compute_V_xc'
    1814              : 
    1815              :       INTEGER                                            :: handle, img, ispin, myfun, nimages
    1816              :       LOGICAL                                            :: hf_present
    1817              :       REAL(KIND=dp)                                      :: energy_ex, energy_exc, energy_total, &
    1818              :                                                             myfraction
    1819          124 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: mat_ks_without_v_xc
    1820          124 :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: matrix_ks_kp
    1821              :       TYPE(dft_control_type), POINTER                    :: dft_control
    1822              :       TYPE(qs_energy_type), POINTER                      :: energy
    1823              :       TYPE(section_vals_type), POINTER                   :: hf_section, input, xc_section
    1824              : 
    1825          124 :       CALL timeset(routineN, handle)
    1826              : 
    1827          124 :       CALL get_qs_env(qs_env, input=input, energy=energy, dft_control=dft_control)
    1828              : 
    1829              :       ! previously, dft_control%nimages set to # neighbor cells, revert for Γ-only KS matrix
    1830          124 :       nimages = dft_control%nimages
    1831          124 :       dft_control%nimages = bs_env%nimages_scf
    1832              : 
    1833              :       ! we need to reset XC functional, therefore, get XC input
    1834          124 :       xc_section => section_vals_get_subs_vals(input, "DFT%XC")
    1835          124 :       CALL section_vals_val_get(xc_section, "XC_FUNCTIONAL%_SECTION_PARAMETERS_", i_val=myfun)
    1836          124 :       CALL section_vals_val_set(xc_section, "XC_FUNCTIONAL%_SECTION_PARAMETERS_", i_val=xc_none)
    1837          124 :       hf_section => section_vals_get_subs_vals(input, "DFT%XC%HF", can_return_null=.TRUE.)
    1838          124 :       hf_present = .FALSE.
    1839          124 :       IF (ASSOCIATED(hf_section)) THEN
    1840          124 :          CALL section_vals_get(hf_section, explicit=hf_present)
    1841              :       END IF
    1842          124 :       IF (hf_present) THEN
    1843              :          ! Special case for handling hfx
    1844           64 :          CALL section_vals_val_get(xc_section, "HF%FRACTION", r_val=myfraction)
    1845           64 :          CALL section_vals_val_set(xc_section, "HF%FRACTION", r_val=0.0_dp)
    1846              :       END IF
    1847              : 
    1848              :       ! save the energy before the energy gets updated
    1849          124 :       energy_total = energy%total
    1850          124 :       energy_exc = energy%exc
    1851          124 :       energy_ex = energy%ex
    1852              : 
    1853          232 :       SELECT CASE (bs_env%gw_implementation)
    1854              :       CASE (tensor_large_cell_Gamma, ri_rs_large_cell_Gamma, ri_rs_non_periodic)
    1855              : 
    1856          108 :          NULLIFY (mat_ks_without_v_xc)
    1857          108 :          CALL dbcsr_allocate_matrix_set(mat_ks_without_v_xc, bs_env%n_spin)
    1858              : 
    1859          232 :          DO ispin = 1, bs_env%n_spin
    1860          124 :             ALLOCATE (mat_ks_without_v_xc(ispin)%matrix)
    1861          232 :             IF (hf_present) THEN
    1862              :                CALL dbcsr_create(mat_ks_without_v_xc(ispin)%matrix, template=bs_env%mat_ao_ao%matrix, &
    1863           76 :                                  matrix_type=dbcsr_type_symmetric)
    1864              :             ELSE
    1865           48 :                CALL dbcsr_create(mat_ks_without_v_xc(ispin)%matrix, template=bs_env%mat_ao_ao%matrix)
    1866              :             END IF
    1867              :          END DO
    1868              : 
    1869              :          ! calculate KS-matrix without XC
    1870              :          CALL qs_ks_build_kohn_sham_matrix(qs_env, calculate_forces=.FALSE., just_energy=.FALSE., &
    1871          108 :                                            ext_ks_matrix=mat_ks_without_v_xc)
    1872              : 
    1873          232 :          DO ispin = 1, bs_env%n_spin
    1874              :             ! transfer dbcsr matrix to fm
    1875          124 :             CALL cp_fm_create(bs_env%fm_V_xc_Gamma(ispin), bs_env%fm_s_Gamma%matrix_struct)
    1876          124 :             CALL copy_dbcsr_to_fm(mat_ks_without_v_xc(ispin)%matrix, bs_env%fm_V_xc_Gamma(ispin))
    1877              : 
    1878              :             ! v_xc = h_ks - h_ks(v_xc = 0)
    1879              :             CALL cp_fm_scale_and_add(alpha=-1.0_dp, matrix_a=bs_env%fm_V_xc_Gamma(ispin), &
    1880          232 :                                      beta=1.0_dp, matrix_b=bs_env%fm_ks_Gamma(ispin))
    1881              :          END DO
    1882              : 
    1883          108 :          CALL dbcsr_deallocate_matrix_set(mat_ks_without_v_xc)
    1884              : 
    1885              :       CASE (tensor_small_cell_full_kp)
    1886              : 
    1887              :          ! calculate KS-matrix without XC
    1888           16 :          CALL qs_ks_build_kohn_sham_matrix(qs_env, calculate_forces=.FALSE., just_energy=.FALSE.)
    1889           16 :          CALL get_qs_env(qs_env=qs_env, matrix_ks_kp=matrix_ks_kp)
    1890              : 
    1891          608 :          ALLOCATE (bs_env%fm_V_xc_R(dft_control%nimages, bs_env%n_spin))
    1892          156 :          DO ispin = 1, bs_env%n_spin
    1893          560 :             DO img = 1, dft_control%nimages
    1894              :                ! safe fm_V_xc_R in fm_matrix because saving in dbcsr matrix caused trouble...
    1895          528 :                CALL copy_dbcsr_to_fm(matrix_ks_kp(ispin, img)%matrix, bs_env%fm_work_mo(1))
    1896              :                CALL cp_fm_create(bs_env%fm_V_xc_R(img, ispin), bs_env%fm_work_mo(1)%matrix_struct, &
    1897          528 :                                  set_zero=.TRUE.)
    1898              :                ! store h_ks(v_xc = 0) in fm_V_xc_R
    1899              :                CALL cp_fm_scale_and_add(alpha=1.0_dp, matrix_a=bs_env%fm_V_xc_R(img, ispin), &
    1900          544 :                                         beta=1.0_dp, matrix_b=bs_env%fm_work_mo(1))
    1901              :             END DO
    1902              :          END DO
    1903              : 
    1904              :       END SELECT
    1905              : 
    1906              :       ! set back the energy
    1907          124 :       energy%total = energy_total
    1908          124 :       energy%exc = energy_exc
    1909          124 :       energy%ex = energy_ex
    1910              : 
    1911              :       ! set back nimages
    1912          124 :       dft_control%nimages = nimages
    1913              : 
    1914              :       ! set the DFT functional and HF fraction back
    1915              :       CALL section_vals_val_set(xc_section, "XC_FUNCTIONAL%_SECTION_PARAMETERS_", &
    1916          124 :                                 i_val=myfun)
    1917          124 :       IF (hf_present) THEN
    1918              :          CALL section_vals_val_set(xc_section, "HF%FRACTION", &
    1919           64 :                                    r_val=myfraction)
    1920              :       END IF
    1921              : 
    1922          124 :       IF (bs_env%gw_implementation == tensor_small_cell_full_kp) THEN
    1923              :          ! calculate KS-matrix again with XC
    1924           16 :          CALL qs_ks_build_kohn_sham_matrix(qs_env, calculate_forces=.FALSE., just_energy=.FALSE.)
    1925           32 :          DO ispin = 1, bs_env%n_spin
    1926          560 :             DO img = 1, dft_control%nimages
    1927              :                ! store h_ks in fm_work_mo
    1928          528 :                CALL copy_dbcsr_to_fm(matrix_ks_kp(ispin, img)%matrix, bs_env%fm_work_mo(1))
    1929              :                ! v_xc = h_ks - h_ks(v_xc = 0)
    1930              :                CALL cp_fm_scale_and_add(alpha=-1.0_dp, matrix_a=bs_env%fm_V_xc_R(img, ispin), &
    1931          544 :                                         beta=1.0_dp, matrix_b=bs_env%fm_work_mo(1))
    1932              :             END DO
    1933              :          END DO
    1934              :       END IF
    1935              : 
    1936          124 :       CALL timestop(handle)
    1937              : 
    1938          124 :    END SUBROUTINE compute_V_xc
    1939              : 
    1940              : ! **************************************************************************************************
    1941              : !> \brief Sets the interaction radii used by the GW calculation.
    1942              : !> \param bs_env ...
    1943              : ! **************************************************************************************************
    1944          124 :    SUBROUTINE init_interaction_radii(bs_env)
    1945              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1946              : 
    1947              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'init_interaction_radii'
    1948              : 
    1949              :       INTEGER                                            :: handle, ibasis
    1950              :       TYPE(gto_basis_set_type), POINTER                  :: orb_basis, ri_basis
    1951              : 
    1952          124 :       CALL timeset(routineN, handle)
    1953              : 
    1954          296 :       DO ibasis = 1, SIZE(bs_env%basis_set_AO)
    1955              : 
    1956          172 :          orb_basis => bs_env%basis_set_AO(ibasis)%gto_basis_set
    1957          172 :          CALL init_interaction_radii_orb_basis(orb_basis, bs_env%eps_filter)
    1958              : 
    1959          172 :          ri_basis => bs_env%basis_set_RI(ibasis)%gto_basis_set
    1960          296 :          CALL init_interaction_radii_orb_basis(ri_basis, bs_env%eps_filter)
    1961              : 
    1962              :       END DO
    1963              : 
    1964          124 :       CALL timestop(handle)
    1965              : 
    1966          124 :    END SUBROUTINE init_interaction_radii
    1967              : 
    1968              : ! **************************************************************************************************
    1969              : !> \brief ...
    1970              : !> \param qs_env ...
    1971              : !> \param bs_env ...
    1972              : ! **************************************************************************************************
    1973           98 :    SUBROUTINE create_tensors(qs_env, bs_env)
    1974              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1975              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1976              : 
    1977              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'create_tensors'
    1978              : 
    1979              :       INTEGER                                            :: handle
    1980              : 
    1981           98 :       CALL timeset(routineN, handle)
    1982              : 
    1983              :       ! split blocks does not improve load balancing/efficienfy for tensor contraction, so we go
    1984              :       ! with the standard atomic blocks
    1985              :       CALL create_3c_t(bs_env%t_RI_AO__AO, bs_env%para_env_tensor, "(RI AO | AO)", [1, 2], [3], &
    1986              :                        bs_env%sizes_RI, bs_env%sizes_AO, &
    1987           98 :                        create_nl_3c=.TRUE., nl_3c=bs_env%nl_3c, qs_env=qs_env)
    1988              :       CALL create_3c_t(bs_env%t_RI__AO_AO, bs_env%para_env_tensor, "(RI | AO AO)", [1], [2, 3], &
    1989           98 :                        bs_env%sizes_RI, bs_env%sizes_AO)
    1990              : 
    1991           98 :       CALL create_2c_t(bs_env)
    1992              : 
    1993           98 :       CALL timestop(handle)
    1994              : 
    1995           98 :    END SUBROUTINE create_tensors
    1996              : 
    1997              : ! **************************************************************************************************
    1998              : !> \brief ...
    1999              : !> \param tensor ...
    2000              : !> \param para_env ...
    2001              : !> \param tensor_name ...
    2002              : !> \param map1 ...
    2003              : !> \param map2 ...
    2004              : !> \param sizes_RI ...
    2005              : !> \param sizes_AO ...
    2006              : !> \param create_nl_3c ...
    2007              : !> \param nl_3c ...
    2008              : !> \param qs_env ...
    2009              : ! **************************************************************************************************
    2010          196 :    SUBROUTINE create_3c_t(tensor, para_env, tensor_name, map1, map2, sizes_RI, sizes_AO, &
    2011              :                           create_nl_3c, nl_3c, qs_env)
    2012              :       TYPE(dbt_type)                                     :: tensor
    2013              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    2014              :       CHARACTER(LEN=12)                                  :: tensor_name
    2015              :       INTEGER, DIMENSION(:)                              :: map1, map2
    2016              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: sizes_RI, sizes_AO
    2017              :       LOGICAL, OPTIONAL                                  :: create_nl_3c
    2018              :       TYPE(neighbor_list_3c_type), OPTIONAL              :: nl_3c
    2019              :       TYPE(qs_environment_type), OPTIONAL, POINTER       :: qs_env
    2020              : 
    2021              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'create_3c_t'
    2022              : 
    2023              :       INTEGER                                            :: handle, nkind
    2024          196 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: dist_AO_1, dist_AO_2, dist_RI
    2025              :       INTEGER, DIMENSION(3)                              :: pcoord, pdims, pdims_3d
    2026              :       LOGICAL                                            :: my_create_nl_3c
    2027          588 :       TYPE(dbt_pgrid_type)                               :: pgrid_3d
    2028              :       TYPE(distribution_3d_type)                         :: dist_3d
    2029          196 :       TYPE(mp_cart_type)                                 :: mp_comm_t3c_2
    2030          196 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
    2031              : 
    2032          196 :       CALL timeset(routineN, handle)
    2033              : 
    2034          196 :       pdims_3d = 0
    2035          196 :       CALL dbt_pgrid_create(para_env, pdims_3d, pgrid_3d)
    2036              :       CALL create_3c_tensor(tensor, dist_RI, dist_AO_1, dist_AO_2, &
    2037              :                             pgrid_3d, sizes_RI, sizes_AO, sizes_AO, &
    2038          196 :                             map1=map1, map2=map2, name=tensor_name)
    2039              : 
    2040          196 :       IF (PRESENT(create_nl_3c)) THEN
    2041           98 :          my_create_nl_3c = create_nl_3c
    2042              :       ELSE
    2043              :          my_create_nl_3c = .FALSE.
    2044              :       END IF
    2045              : 
    2046           98 :       IF (my_create_nl_3c) THEN
    2047           98 :          CALL get_qs_env(qs_env, nkind=nkind, particle_set=particle_set)
    2048           98 :          CALL dbt_mp_environ_pgrid(pgrid_3d, pdims, pcoord)
    2049           98 :          CALL mp_comm_t3c_2%create(pgrid_3d%mp_comm_2d, 3, pdims)
    2050              :          CALL distribution_3d_create(dist_3d, dist_RI, dist_AO_1, dist_AO_2, &
    2051           98 :                                      nkind, particle_set, mp_comm_t3c_2, own_comm=.TRUE.)
    2052              : 
    2053              :          CALL build_3c_neighbor_lists(nl_3c, &
    2054              :                                       qs_env%bs_env%basis_set_RI, &
    2055              :                                       qs_env%bs_env%basis_set_AO, &
    2056              :                                       qs_env%bs_env%basis_set_AO, &
    2057              :                                       dist_3d, qs_env%bs_env%ri_metric, &
    2058           98 :                                       "GW_3c_nl", qs_env, own_dist=.TRUE.)
    2059              :       END IF
    2060              : 
    2061          196 :       DEALLOCATE (dist_RI, dist_AO_1, dist_AO_2)
    2062          196 :       CALL dbt_pgrid_destroy(pgrid_3d)
    2063              : 
    2064          196 :       CALL timestop(handle)
    2065              : 
    2066          392 :    END SUBROUTINE create_3c_t
    2067              : 
    2068              : ! **************************************************************************************************
    2069              : !> \brief Creates the AO-AO and RI-RI two-center tensors.
    2070              : !> \param bs_env ...
    2071              : ! **************************************************************************************************
    2072           98 :    SUBROUTINE create_2c_t(bs_env)
    2073              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2074              : 
    2075              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'create_2c_t'
    2076              : 
    2077              :       INTEGER                                            :: handle
    2078           98 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: dist_1, dist_2
    2079              :       INTEGER, DIMENSION(2)                              :: pdims_2d
    2080          294 :       TYPE(dbt_pgrid_type)                               :: pgrid_2d
    2081              : 
    2082           98 :       CALL timeset(routineN, handle)
    2083              : 
    2084              :       ! inspired from rpa_im_time.F / hfx_types.F
    2085              : 
    2086           98 :       pdims_2d = 0
    2087           98 :       CALL dbt_pgrid_create(bs_env%para_env_tensor, pdims_2d, pgrid_2d)
    2088              : 
    2089              :       CALL create_2c_tensor(bs_env%t_G, dist_1, dist_2, pgrid_2d, &
    2090              :                             bs_env%sizes_AO, bs_env%sizes_AO, &
    2091           98 :                             name="(AO | AO)")
    2092           98 :       DEALLOCATE (dist_1, dist_2)
    2093              :       CALL create_2c_tensor(bs_env%t_chi, dist_1, dist_2, pgrid_2d, &
    2094              :                             bs_env%sizes_RI, bs_env%sizes_RI, &
    2095           98 :                             name="(RI | RI)")
    2096           98 :       DEALLOCATE (dist_1, dist_2)
    2097              :       CALL create_2c_tensor(bs_env%t_W, dist_1, dist_2, pgrid_2d, &
    2098              :                             bs_env%sizes_RI, bs_env%sizes_RI, &
    2099           98 :                             name="(RI | RI)")
    2100           98 :       DEALLOCATE (dist_1, dist_2)
    2101           98 :       CALL dbt_pgrid_destroy(pgrid_2d)
    2102              : 
    2103           98 :       CALL timestop(handle)
    2104              : 
    2105           98 :    END SUBROUTINE create_2c_t
    2106              : 
    2107              : ! **************************************************************************************************
    2108              : !> \brief ...
    2109              : !> \param qs_env ...
    2110              : !> \param bs_env ...
    2111              : ! **************************************************************************************************
    2112           68 :    SUBROUTINE check_sparsity_3c(qs_env, bs_env)
    2113              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    2114              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2115              : 
    2116              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'check_sparsity_3c'
    2117              : 
    2118              :       INTEGER                                            :: handle, n_atom_step, RI_atom
    2119              :       INTEGER(int_8)                                     :: non_zero_elements_sum, nze
    2120              :       REAL(dp)                                           :: max_dist_AO_atoms, occ, occupation_sum
    2121              :       REAL(KIND=dp)                                      :: t1, t2
    2122           68 :       TYPE(dbt_type), ALLOCATABLE, DIMENSION(:, :)       :: t_3c_global_array
    2123              : 
    2124           68 :       CALL timeset(routineN, handle)
    2125              : 
    2126              :       ! check the sparsity of 3c integral tensor (µν|P); calculate maximum distance between
    2127              :       ! AO atoms µ, ν where at least a single integral (µν|P) is larger than the filter threshold
    2128              : 
    2129          612 :       ALLOCATE (t_3c_global_array(1, 1))
    2130           68 :       CALL dbt_create(bs_env%t_RI_AO__AO, t_3c_global_array(1, 1))
    2131              : 
    2132              :       ! Allocate arrays to store min/max indices for overlap with other AO/RI functions on each atom
    2133              :       ! (Filled during loop via get_i_j_atom_ranges)
    2134          272 :       ALLOCATE (bs_env%min_RI_idx_from_AO_AO_atom(bs_env%n_atom, bs_env%n_atom))
    2135          272 :       ALLOCATE (bs_env%max_RI_idx_from_AO_AO_atom(bs_env%n_atom, bs_env%n_atom))
    2136          272 :       ALLOCATE (bs_env%min_AO_idx_from_RI_AO_atom(bs_env%n_atom, bs_env%n_atom))
    2137          272 :       ALLOCATE (bs_env%max_AO_idx_from_RI_AO_atom(bs_env%n_atom, bs_env%n_atom))
    2138          500 :       bs_env%min_RI_idx_from_AO_AO_atom(:, :) = bs_env%n_RI
    2139          500 :       bs_env%max_RI_idx_from_AO_AO_atom(:, :) = 1
    2140          500 :       bs_env%min_AO_idx_from_RI_AO_atom(:, :) = bs_env%n_AO
    2141          500 :       bs_env%max_AO_idx_from_RI_AO_atom(:, :) = 1
    2142              : 
    2143           68 :       CALL bs_env%para_env%sync()
    2144           68 :       t1 = m_walltime()
    2145              : 
    2146           68 :       occupation_sum = 0.0_dp
    2147           68 :       non_zero_elements_sum = 0
    2148           68 :       max_dist_AO_atoms = 0.0_dp
    2149           68 :       n_atom_step = INT(SQRT(REAL(bs_env%n_atom, KIND=dp)))
    2150              :       ! do not compute full 3c integrals at once because it may cause out of memory
    2151          208 :       DO RI_atom = 1, bs_env%n_atom, n_atom_step
    2152              : 
    2153              :          CALL build_3c_integrals(t_3c_global_array, &
    2154              :                                  bs_env%eps_filter, &
    2155              :                                  qs_env, &
    2156              :                                  bs_env%nl_3c, &
    2157              :                                  int_eps=bs_env%eps_filter, &
    2158              :                                  basis_i=bs_env%basis_set_RI, &
    2159              :                                  basis_j=bs_env%basis_set_AO, &
    2160              :                                  basis_k=bs_env%basis_set_AO, &
    2161              :                                  bounds_i=[RI_atom, MIN(RI_atom + n_atom_step - 1, bs_env%n_atom)], &
    2162              :                                  potential_parameter=bs_env%ri_metric, &
    2163          420 :                                  desymmetrize=.FALSE.)
    2164              : 
    2165          140 :          CALL dbt_filter(t_3c_global_array(1, 1), bs_env%eps_filter)
    2166              : 
    2167          140 :          CALL bs_env%para_env%sync()
    2168              : 
    2169          140 :          CALL get_tensor_occupancy(t_3c_global_array(1, 1), nze, occ)
    2170          140 :          non_zero_elements_sum = non_zero_elements_sum + nze
    2171          140 :          occupation_sum = occupation_sum + occ
    2172              : 
    2173          140 :          CALL get_max_dist_AO_atoms(t_3c_global_array(1, 1), max_dist_AO_atoms, qs_env)
    2174              : 
    2175              :          ! Extract indices per block
    2176          140 :          CALL get_i_j_atom_ranges(t_3c_global_array(1, 1), bs_env)
    2177              : 
    2178          348 :          CALL dbt_clear(t_3c_global_array(1, 1))
    2179              : 
    2180              :       END DO
    2181              : 
    2182           68 :       t2 = m_walltime()
    2183              : 
    2184              :       ! Sync/max for max_dist_AO_atoms is done inside each get_max_dist_AO_atoms
    2185           68 :       bs_env%max_dist_AO_atoms = max_dist_AO_atoms
    2186              :       ! occupation_sum is a global quantity, also needs no sync here
    2187           68 :       bs_env%occupation_3c_int = occupation_sum
    2188              : 
    2189           68 :       CALL bs_env%para_env%min(bs_env%min_RI_idx_from_AO_AO_atom)
    2190           68 :       CALL bs_env%para_env%max(bs_env%max_RI_idx_from_AO_AO_atom)
    2191           68 :       CALL bs_env%para_env%min(bs_env%min_AO_idx_from_RI_AO_atom)
    2192           68 :       CALL bs_env%para_env%max(bs_env%max_AO_idx_from_RI_AO_atom)
    2193              : 
    2194           68 :       CALL dbt_destroy(t_3c_global_array(1, 1))
    2195          136 :       DEALLOCATE (t_3c_global_array)
    2196              : 
    2197           68 :       IF (bs_env%unit_nr > 0) THEN
    2198           34 :          WRITE (bs_env%unit_nr, '(T2,A)') ''
    2199              :          WRITE (bs_env%unit_nr, '(T2,A,F27.1,A)') &
    2200           34 :             'Computed 3-center integrals (µν|P), execution time', t2 - t1, ' s'
    2201           34 :          WRITE (bs_env%unit_nr, '(T2,A,F48.3,A)') 'Percentage of non-zero (µν|P)', &
    2202           68 :             bs_env%occupation_3c_int*100, ' %'
    2203           34 :          WRITE (bs_env%unit_nr, '(T2,A,F33.1,A)') 'Max. distance between µ,ν in non-zero (µν|P)', &
    2204           68 :             bs_env%max_dist_AO_atoms*angstrom, ' A'
    2205           34 :          WRITE (bs_env%unit_nr, '(T2,2A,I20,A)') 'Required memory if storing all 3-center ', &
    2206           68 :             'integrals (µν|P)', INT(REAL(non_zero_elements_sum, KIND=dp)*8.0E-9_dp), ' GB'
    2207              :       END IF
    2208              : 
    2209           68 :       CALL timestop(handle)
    2210              : 
    2211          136 :    END SUBROUTINE check_sparsity_3c
    2212              : 
    2213              : ! **************************************************************************************************
    2214              : !> \brief ...
    2215              : !> \param t_3c_int ...
    2216              : !> \param max_dist_AO_atoms ...
    2217              : !> \param qs_env ...
    2218              : ! **************************************************************************************************
    2219          140 :    SUBROUTINE get_max_dist_AO_atoms(t_3c_int, max_dist_AO_atoms, qs_env)
    2220              :       TYPE(dbt_type)                                     :: t_3c_int
    2221              :       REAL(KIND=dp), INTENT(INOUT)                       :: max_dist_AO_atoms
    2222              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    2223              : 
    2224              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'get_max_dist_AO_atoms'
    2225              : 
    2226              :       INTEGER                                            :: atom_1, atom_2, handle, num_cells
    2227              :       INTEGER, DIMENSION(3)                              :: atom_ind
    2228          140 :       INTEGER, DIMENSION(:, :), POINTER                  :: index_to_cell
    2229              :       REAL(KIND=dp)                                      :: abs_rab
    2230              :       REAL(KIND=dp), DIMENSION(3)                        :: rab
    2231              :       TYPE(cell_type), POINTER                           :: cell
    2232              :       TYPE(dbt_iterator_type)                            :: iter
    2233              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    2234          140 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
    2235              : 
    2236          140 :       CALL timeset(routineN, handle)
    2237              : 
    2238          140 :       NULLIFY (cell, particle_set, para_env)
    2239          140 :       CALL get_qs_env(qs_env, cell=cell, particle_set=particle_set, para_env=para_env)
    2240              : 
    2241              :       ! max_dist_AO_atoms is compared to earlier steps in the loop with step n_atom_step
    2242              :       ! do not initialize/overwrite here
    2243              : 
    2244              : ! IMPORTANT: Use thread-local copy for max_dist_AO_atoms via REDUCTION to avoid race conditions
    2245              : !$OMP PARALLEL DEFAULT(NONE) &
    2246              : !$OMP SHARED(t_3c_int, num_cells, index_to_cell, particle_set, cell) &
    2247              : !$OMP PRIVATE(iter, atom_ind, rab, abs_rab, atom_1, atom_2) &
    2248          140 : !$OMP REDUCTION(MAX:max_dist_AO_atoms)
    2249              : 
    2250              :       CALL dbt_iterator_start(iter, t_3c_int)
    2251              :       DO WHILE (dbt_iterator_blocks_left(iter))
    2252              :          CALL dbt_iterator_next_block(iter, atom_ind)
    2253              : 
    2254              :          atom_1 = atom_ind(2)
    2255              :          atom_2 = atom_ind(3)
    2256              :          rab = pbc(particle_set(atom_1)%r(1:3), particle_set(atom_2)%r(1:3), cell)
    2257              :          abs_rab = SQRT(rab(1)**2 + rab(2)**2 + rab(3)**2)
    2258              : 
    2259              :          ! Reduction takes care of using a thread-local copy
    2260              :          max_dist_AO_atoms = MAX(max_dist_AO_atoms, abs_rab)
    2261              : 
    2262              :       END DO
    2263              :       CALL dbt_iterator_stop(iter)
    2264              : !$OMP END PARALLEL
    2265              : 
    2266          140 :       CALL para_env%max(max_dist_AO_atoms)
    2267              : 
    2268          140 :       CALL timestop(handle)
    2269              : 
    2270          140 :    END SUBROUTINE get_max_dist_AO_atoms
    2271              : 
    2272              : ! **************************************************************************************************
    2273              : !> \brief ...
    2274              : !> \param t_3c ...
    2275              : !> \param bs_env ...
    2276              : ! **************************************************************************************************
    2277          140 :    SUBROUTINE get_i_j_atom_ranges(t_3c, bs_env)
    2278              :       TYPE(dbt_type)                                     :: t_3c
    2279              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2280              : 
    2281              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'get_i_j_atom_ranges'
    2282              : 
    2283              :       INTEGER                                            :: handle, idx_AO_end, idx_AO_start, &
    2284              :                                                             idx_RI_end, idx_RI_start
    2285              :       INTEGER, DIMENSION(3)                              :: atom_ind
    2286              :       TYPE(dbt_iterator_type)                            :: iter
    2287              : 
    2288          140 :       CALL timeset(routineN, handle)
    2289              : 
    2290              :       ! Loop over blocks in 3c, for given min_atom: RI_min/max index from min_atom
    2291              : !$OMP PARALLEL DEFAULT(NONE) &
    2292              : !$OMP SHARED(t_3c, bs_env) &
    2293              : !$OMP PRIVATE(iter, atom_ind, &
    2294          140 : !$OMP         idx_RI_start, idx_RI_end, idx_AO_start, idx_AO_end)
    2295              : 
    2296              :       CALL dbt_iterator_start(iter, t_3c)
    2297              :       DO WHILE (dbt_iterator_blocks_left(iter))
    2298              :          CALL dbt_iterator_next_block(iter, atom_ind)
    2299              : 
    2300              :          ! Pre-fetch indices to avoid referencing 'bs_env' twice inside the ATOMIC blocks
    2301              :          idx_RI_start = bs_env%i_RI_start_from_atom(atom_ind(1))
    2302              :          idx_RI_end = bs_env%i_RI_end_from_atom(atom_ind(1))
    2303              : 
    2304              :          idx_AO_start = bs_env%i_ao_start_from_atom(atom_ind(2))
    2305              :          idx_AO_end = bs_env%i_ao_end_from_atom(atom_ind(2))
    2306              : 
    2307              :          ! Update values safely inside ATOMIC blocks, otherwise race conditions occur
    2308              : !$OMP ATOMIC UPDATE
    2309              :          bs_env%min_RI_idx_from_AO_AO_atom(atom_ind(2), atom_ind(3)) = &
    2310              :             MIN(bs_env%min_RI_idx_from_AO_AO_atom(atom_ind(2), atom_ind(3)), idx_RI_start)
    2311              : !$OMP ATOMIC UPDATE
    2312              :          bs_env%max_RI_idx_from_AO_AO_atom(atom_ind(2), atom_ind(3)) = &
    2313              :             MAX(bs_env%max_RI_idx_from_AO_AO_atom(atom_ind(2), atom_ind(3)), idx_RI_end)
    2314              : 
    2315              : !$OMP ATOMIC UPDATE
    2316              :          bs_env%min_AO_idx_from_RI_AO_atom(atom_ind(1), atom_ind(3)) = &
    2317              :             MIN(bs_env%min_AO_idx_from_RI_AO_atom(atom_ind(1), atom_ind(3)), idx_AO_start)
    2318              : !$OMP ATOMIC UPDATE
    2319              :          bs_env%max_AO_idx_from_RI_AO_atom(atom_ind(1), atom_ind(3)) = &
    2320              :             MAX(bs_env%max_AO_idx_from_RI_AO_atom(atom_ind(1), atom_ind(3)), idx_AO_end)
    2321              : 
    2322              :       END DO
    2323              :       CALL dbt_iterator_stop(iter)
    2324              : !$OMP END PARALLEL
    2325              : 
    2326          140 :       CALL timestop(handle)
    2327              : 
    2328          140 :    END SUBROUTINE get_i_j_atom_ranges
    2329              : 
    2330              : ! **************************************************************************************************
    2331              : !> \brief ...
    2332              : !> \param bs_env ...
    2333              : ! **************************************************************************************************
    2334           68 :    SUBROUTINE set_sparsity_parallelization_parameters(bs_env)
    2335              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2336              : 
    2337              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'set_sparsity_parallelization_parameters'
    2338              : 
    2339              :       INTEGER :: handle, i_ivl, IL_ivl, j_ivl, n_atom_per_IL_ivl, n_atom_per_ivl, n_intervals_i, &
    2340              :          n_intervals_inner_loop_atoms, n_intervals_j, u
    2341              :       INTEGER(KIND=int_8)                                :: input_memory_per_proc
    2342              : 
    2343           68 :       CALL timeset(routineN, handle)
    2344              : 
    2345              :       ! heuristic parameter to prevent out of memory
    2346           68 :       bs_env%safety_factor_memory = 0.10_dp
    2347              : 
    2348           68 :       input_memory_per_proc = INT(bs_env%input_memory_per_proc_GB*1.0E9_dp, KIND=int_8)
    2349              : 
    2350              :       ! choose atomic range for λ ("i_atom"), ν ("j_atom") in
    2351              :       ! M_λνP(iτ) = sum_µ (µν|P) G^occ_µλ(i|τ|,k=0)
    2352              :       ! N_νλQ(iτ) = sum_σ (σλ|Q) G^vir_σν(i|τ|,k=0)
    2353              :       ! such that M and N fit into the memory
    2354              :       n_atom_per_ivl = INT(SQRT(bs_env%safety_factor_memory*input_memory_per_proc &
    2355              :                                 *bs_env%group_size_tensor/24/bs_env%n_RI &
    2356           68 :                                 /SQRT(bs_env%occupation_3c_int)))/bs_env%max_AO_bf_per_atom
    2357              : 
    2358           68 :       n_intervals_i = (bs_env%n_atom_i - 1)/n_atom_per_ivl + 1
    2359           68 :       n_intervals_j = (bs_env%n_atom_j - 1)/n_atom_per_ivl + 1
    2360              : 
    2361           68 :       bs_env%n_intervals_i = n_intervals_i
    2362           68 :       bs_env%n_intervals_j = n_intervals_j
    2363              : 
    2364          204 :       ALLOCATE (bs_env%i_atom_intervals(2, n_intervals_i))
    2365          204 :       ALLOCATE (bs_env%j_atom_intervals(2, n_intervals_j))
    2366              : 
    2367          136 :       DO i_ivl = 1, n_intervals_i
    2368           68 :          bs_env%i_atom_intervals(1, i_ivl) = (i_ivl - 1)*n_atom_per_ivl + bs_env%atoms_i(1)
    2369              :          bs_env%i_atom_intervals(2, i_ivl) = MIN(i_ivl*n_atom_per_ivl + bs_env%atoms_i(1) - 1, &
    2370          136 :                                                  bs_env%atoms_i(2))
    2371              :       END DO
    2372              : 
    2373          136 :       DO j_ivl = 1, n_intervals_j
    2374           68 :          bs_env%j_atom_intervals(1, j_ivl) = (j_ivl - 1)*n_atom_per_ivl + bs_env%atoms_j(1)
    2375              :          bs_env%j_atom_intervals(2, j_ivl) = MIN(j_ivl*n_atom_per_ivl + bs_env%atoms_j(1) - 1, &
    2376          136 :                                                  bs_env%atoms_j(2))
    2377              :       END DO
    2378              : 
    2379          272 :       ALLOCATE (bs_env%skip_Sigma_occ(n_intervals_i, n_intervals_j))
    2380          204 :       ALLOCATE (bs_env%skip_Sigma_vir(n_intervals_i, n_intervals_j))
    2381          204 :       bs_env%skip_Sigma_occ(:, :) = .FALSE.
    2382          204 :       bs_env%skip_Sigma_vir(:, :) = .FALSE.
    2383           68 :       bs_env%n_skip_chi = 0
    2384              : 
    2385          204 :       ALLOCATE (bs_env%skip_chi(n_intervals_i, n_intervals_j))
    2386          204 :       bs_env%skip_chi(:, :) = .FALSE.
    2387           68 :       bs_env%n_skip_sigma = 0
    2388              : 
    2389              :       ! choose atomic range for µ and σ ("inner loop (IL) atom") in
    2390              :       ! M_λνP(iτ) = sum_µ (µν|P) G^occ_µλ(i|τ|,k=0)
    2391              :       ! N_νλQ(iτ) = sum_σ (σλ|Q) G^vir_σν(i|τ|,k=0)
    2392              :       n_atom_per_IL_ivl = MIN(INT(bs_env%safety_factor_memory*input_memory_per_proc &
    2393              :                                   *bs_env%group_size_tensor/n_atom_per_ivl &
    2394              :                                   /bs_env%max_AO_bf_per_atom &
    2395              :                                   /bs_env%n_RI/8/SQRT(bs_env%occupation_3c_int) &
    2396           68 :                                   /bs_env%max_AO_bf_per_atom), bs_env%n_atom)
    2397              : 
    2398           68 :       n_intervals_inner_loop_atoms = (bs_env%n_atom - 1)/n_atom_per_IL_ivl + 1
    2399              : 
    2400           68 :       bs_env%n_intervals_inner_loop_atoms = n_intervals_inner_loop_atoms
    2401              : 
    2402          204 :       ALLOCATE (bs_env%inner_loop_atom_intervals(2, n_intervals_inner_loop_atoms))
    2403          136 :       DO IL_ivl = 1, n_intervals_inner_loop_atoms
    2404           68 :          bs_env%inner_loop_atom_intervals(1, IL_ivl) = (IL_ivl - 1)*n_atom_per_IL_ivl + 1
    2405          136 :          bs_env%inner_loop_atom_intervals(2, IL_ivl) = MIN(IL_ivl*n_atom_per_IL_ivl, bs_env%n_atom)
    2406              :       END DO
    2407              : 
    2408           68 :       u = bs_env%unit_nr
    2409           68 :       IF (u > 0) THEN
    2410           34 :          WRITE (u, '(T2,A)') ''
    2411           34 :          WRITE (u, '(T2,A,I33)') 'Number of i and j atoms in M_λνP(τ), N_νλQ(τ):', n_atom_per_ivl
    2412           34 :          WRITE (u, '(T2,A,I18)') 'Number of inner loop atoms for µ in M_λνP = sum_µ (µν|P) G_µλ', &
    2413           68 :             n_atom_per_IL_ivl
    2414              :       END IF
    2415              : 
    2416           68 :       CALL timestop(handle)
    2417              : 
    2418           68 :    END SUBROUTINE set_sparsity_parallelization_parameters
    2419              : 
    2420              : ! **************************************************************************************************
    2421              : !> \brief ...
    2422              : !> \param qs_env ...
    2423              : !> \param bs_env ...
    2424              : ! **************************************************************************************************
    2425          108 :    SUBROUTINE check_for_restart_files(qs_env, bs_env)
    2426              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    2427              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2428              : 
    2429              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'check_for_restart_files'
    2430              : 
    2431              :       CHARACTER(LEN=9)                                   :: frmt
    2432              :       CHARACTER(len=default_path_length)                 :: f_chi, f_s_n, f_s_p, f_s_x, f_w_t, &
    2433              :                                                             prefix, project_name, Z_lP_name
    2434              :       INTEGER                                            :: handle, i_spin, i_t_or_w, ind, n_spin, &
    2435              :                                                             num_time_freq_points
    2436              :       LOGICAL                                            :: chi_exists, Sigma_neg_time_exists, &
    2437              :                                                             Sigma_pos_time_exists, &
    2438              :                                                             Sigma_x_spin_exists, W_time_exists, &
    2439              :                                                             Z_lP_exists
    2440              :       TYPE(cp_logger_type), POINTER                      :: logger
    2441              :       TYPE(section_vals_type), POINTER                   :: input, print_key
    2442              : 
    2443          108 :       CALL timeset(routineN, handle)
    2444              : 
    2445          108 :       num_time_freq_points = bs_env%num_time_freq_points
    2446          108 :       n_spin = bs_env%n_spin
    2447              : 
    2448          324 :       ALLOCATE (bs_env%read_chi(num_time_freq_points))
    2449          216 :       ALLOCATE (bs_env%calc_chi(num_time_freq_points))
    2450          432 :       ALLOCATE (bs_env%Sigma_c_exists(num_time_freq_points, n_spin))
    2451              : 
    2452          108 :       CALL get_qs_env(qs_env, input=input)
    2453              : 
    2454          108 :       logger => cp_get_default_logger()
    2455          108 :       print_key => section_vals_get_subs_vals(input, 'PROPERTIES%BANDSTRUCTURE%GW%PRINT%RESTART')
    2456              :       project_name = cp_print_key_generate_filename(logger, print_key, extension="", &
    2457          108 :                                                     my_local=.FALSE.)
    2458          108 :       WRITE (prefix, '(2A)') TRIM(project_name), "-RESTART_"
    2459          108 :       bs_env%prefix = prefix
    2460              : 
    2461          108 :       bs_env%all_W_exist = .TRUE.
    2462              : 
    2463         1902 :       DO i_t_or_w = 1, num_time_freq_points
    2464              : 
    2465         1794 :          IF (i_t_or_w < 10) THEN
    2466          960 :             WRITE (frmt, '(A)') '(3A,I1,A)'
    2467          960 :             WRITE (f_chi, frmt) TRIM(prefix), bs_env%chi_name, "_0", i_t_or_w, ".matrix"
    2468          960 :             WRITE (f_W_t, frmt) TRIM(prefix), bs_env%W_time_name, "_0", i_t_or_w, ".matrix"
    2469          834 :          ELSE IF (i_t_or_w < 100) THEN
    2470          834 :             WRITE (frmt, '(A)') '(3A,I2,A)'
    2471          834 :             WRITE (f_chi, frmt) TRIM(prefix), bs_env%chi_name, "_", i_t_or_w, ".matrix"
    2472          834 :             WRITE (f_W_t, frmt) TRIM(prefix), bs_env%W_time_name, "_", i_t_or_w, ".matrix"
    2473              :          ELSE
    2474            0 :             CPABORT('Please implement more than 99 time/frequency points.')
    2475              :          END IF
    2476              : 
    2477         1794 :          INQUIRE (file=TRIM(f_chi), exist=chi_exists)
    2478         1794 :          INQUIRE (file=TRIM(f_W_t), exist=W_time_exists)
    2479              : 
    2480         1794 :          bs_env%read_chi(i_t_or_w) = chi_exists
    2481         1794 :          bs_env%calc_chi(i_t_or_w) = .NOT. chi_exists
    2482              : 
    2483         1794 :          bs_env%all_W_exist = bs_env%all_W_exist .AND. W_time_exists
    2484              : 
    2485              :          ! the self-energy is spin-dependent
    2486         3936 :          DO i_spin = 1, n_spin
    2487              : 
    2488         2034 :             ind = i_t_or_w + (i_spin - 1)*num_time_freq_points
    2489              : 
    2490         2034 :             IF (ind < 10) THEN
    2491          960 :                WRITE (frmt, '(A)') '(3A,I1,A)'
    2492          960 :                WRITE (f_S_p, frmt) TRIM(prefix), bs_env%Sigma_p_name, "_0", ind, ".matrix"
    2493          960 :                WRITE (f_S_n, frmt) TRIM(prefix), bs_env%Sigma_n_name, "_0", ind, ".matrix"
    2494         1074 :             ELSE IF (ind < 100) THEN
    2495         1074 :                WRITE (frmt, '(A)') '(3A,I2,A)'
    2496         1074 :                WRITE (f_S_p, frmt) TRIM(prefix), bs_env%Sigma_p_name, "_", ind, ".matrix"
    2497         1074 :                WRITE (f_S_n, frmt) TRIM(prefix), bs_env%Sigma_n_name, "_", ind, ".matrix"
    2498              :             ELSE
    2499            0 :                CPABORT('Please implement more than 99 combined spin+freq indices.')
    2500              :             END IF
    2501              : 
    2502         2034 :             INQUIRE (file=TRIM(f_S_p), exist=Sigma_pos_time_exists)
    2503         2034 :             INQUIRE (file=TRIM(f_S_n), exist=Sigma_neg_time_exists)
    2504              : 
    2505              :             bs_env%Sigma_c_exists(i_t_or_w, i_spin) = Sigma_pos_time_exists .AND. &
    2506         5742 :                                                       Sigma_neg_time_exists
    2507              : 
    2508              :          END DO
    2509              : 
    2510              :       END DO
    2511              : 
    2512              :       ! Marek : In the RTBSE run, check also for zero frequency W
    2513          108 :       IF (bs_env%rtp_method == rtp_method_bse .OR. &
    2514              :           bs_env%rtp_method == rtp_method_bse_linearized) THEN
    2515           72 :          WRITE (f_W_t, '(3A,I1,A)') TRIM(prefix), "W_freq_rtp", "_0", 0, ".matrix"
    2516           72 :          INQUIRE (file=TRIM(f_W_t), exist=W_time_exists)
    2517          140 :          bs_env%all_W_exist = bs_env%all_W_exist .AND. W_time_exists
    2518              :       END IF
    2519              : 
    2520              :       ! Check for Restart Z_lP file
    2521          108 :       IF (bs_env%do_gw_ri_rs) THEN
    2522           40 :          WRITE (Z_lP_name, '(3A)') TRIM(prefix), "Z_lP", ".matrix"
    2523           40 :          INQUIRE (file=TRIM(Z_lP_name), exist=Z_lP_exists)
    2524           40 :          bs_env%ri_rs%Z_lP_exists = Z_lP_exists
    2525              :       END IF
    2526              : 
    2527          108 :       IF (bs_env%all_W_exist) THEN
    2528          106 :          bs_env%read_chi(:) = .FALSE.
    2529          106 :          bs_env%calc_chi(:) = .FALSE.
    2530              :       END IF
    2531              : 
    2532          108 :       bs_env%Sigma_x_exists = .TRUE.
    2533          232 :       DO i_spin = 1, n_spin
    2534          124 :          WRITE (f_S_x, '(3A,I1,A)') TRIM(prefix), bs_env%Sigma_x_name, "_0", i_spin, ".matrix"
    2535          124 :          INQUIRE (file=TRIM(f_S_x), exist=Sigma_x_spin_exists)
    2536          348 :          bs_env%Sigma_x_exists = bs_env%Sigma_x_exists .AND. Sigma_x_spin_exists
    2537              :       END DO
    2538              : 
    2539              :       ! If any restart files are read, check if the SCF converged in 1 step.
    2540              :       ! This is important because a re-iterated SCF can lead to spurious GW results
    2541              :       IF (ANY(bs_env%read_chi(:)) &
    2542              :           .OR. ANY(bs_env%Sigma_c_exists) &
    2543              :           .OR. bs_env%all_W_exist &
    2544         3932 :           .OR. bs_env%Sigma_x_exists &
    2545              :           ) THEN
    2546              : 
    2547            6 :          IF (qs_env%scf_env%iter_count /= 1) THEN
    2548              :             CALL cp_warn(__LOCATION__, "SCF needed more than 1 step, "// &
    2549            6 :                          "which might lead to spurious GW results when using GW restart files. ")
    2550              :          END IF
    2551              :       END IF
    2552              : 
    2553          108 :       CALL timestop(handle)
    2554              : 
    2555          108 :    END SUBROUTINE check_for_restart_files
    2556              : 
    2557              : ! **************************************************************************************************
    2558              : !> \brief ...
    2559              : !> \param qs_env ...
    2560              : !> \param bs_env ...
    2561              : ! **************************************************************************************************
    2562           16 :    SUBROUTINE compute_3c_integrals(qs_env, bs_env)
    2563              : 
    2564              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    2565              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2566              : 
    2567              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_3c_integrals'
    2568              : 
    2569              :       INTEGER                                            :: handle, j_cell, k_cell, nimages_3c
    2570              : 
    2571           16 :       CALL timeset(routineN, handle)
    2572              : 
    2573           16 :       nimages_3c = bs_env%nimages_3c
    2574         2436 :       ALLOCATE (bs_env%t_3c_int(nimages_3c, nimages_3c))
    2575          194 :       DO j_cell = 1, nimages_3c
    2576         2260 :          DO k_cell = 1, nimages_3c
    2577         2244 :             CALL dbt_create(bs_env%t_RI_AO__AO, bs_env%t_3c_int(j_cell, k_cell))
    2578              :          END DO
    2579              :       END DO
    2580              : 
    2581              :       CALL build_3c_integrals(bs_env%t_3c_int, &
    2582              :                               bs_env%eps_filter, &
    2583              :                               qs_env, &
    2584              :                               bs_env%nl_3c, &
    2585              :                               int_eps=bs_env%eps_filter*0.05_dp, &
    2586              :                               basis_i=bs_env%basis_set_RI, &
    2587              :                               basis_j=bs_env%basis_set_AO, &
    2588              :                               basis_k=bs_env%basis_set_AO, &
    2589              :                               potential_parameter=bs_env%ri_metric, &
    2590              :                               desymmetrize=.FALSE., do_kpoints=.TRUE., cell_sym=.TRUE., &
    2591           16 :                               cell_to_index_ext=bs_env%cell_to_index_3c)
    2592              : 
    2593           16 :       CALL bs_env%para_env%sync()
    2594              : 
    2595           16 :       CALL timestop(handle)
    2596              : 
    2597           16 :    END SUBROUTINE compute_3c_integrals
    2598              : 
    2599              : ! **************************************************************************************************
    2600              : !> \brief ...
    2601              : !> \param bs_env ...
    2602              : ! **************************************************************************************************
    2603           16 :    SUBROUTINE setup_cells_Delta_R(bs_env)
    2604              : 
    2605              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2606              : 
    2607              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'setup_cells_Delta_R'
    2608              : 
    2609              :       INTEGER                                            :: handle
    2610              : 
    2611           16 :       CALL timeset(routineN, handle)
    2612              : 
    2613              :       ! cell sums batch wise for fixed ΔR = S_1 - R_1; for example:
    2614              :       ! Σ_λσ^R = sum_PR1νS1 M^G_λ0,νS1,PR1 M^W_σR,νS1,PR1
    2615              : 
    2616              :       CALL sum_two_R_grids(bs_env%index_to_cell_3c, &
    2617              :                            bs_env%index_to_cell_3c, &
    2618              :                            bs_env%nimages_3c, bs_env%nimages_3c, &
    2619              :                            bs_env%index_to_cell_Delta_R, &
    2620              :                            bs_env%cell_to_index_Delta_R, &
    2621           16 :                            bs_env%nimages_Delta_R)
    2622              : 
    2623           16 :       IF (bs_env%unit_nr > 0) THEN
    2624            8 :          WRITE (bs_env%unit_nr, FMT="(T2,A,I61)") "Number of cells ΔR", bs_env%nimages_Delta_R
    2625              :       END IF
    2626              : 
    2627           16 :       CALL timestop(handle)
    2628              : 
    2629           16 :    END SUBROUTINE setup_cells_Delta_R
    2630              : 
    2631              : ! **************************************************************************************************
    2632              : !> \brief ...
    2633              : !> \param index_to_cell_1 ...
    2634              : !> \param index_to_cell_2 ...
    2635              : !> \param nimages_1 ...
    2636              : !> \param nimages_2 ...
    2637              : !> \param index_to_cell ...
    2638              : !> \param cell_to_index ...
    2639              : !> \param nimages ...
    2640              : ! **************************************************************************************************
    2641           16 :    SUBROUTINE sum_two_R_grids(index_to_cell_1, index_to_cell_2, nimages_1, nimages_2, &
    2642              :                               index_to_cell, cell_to_index, nimages)
    2643              : 
    2644              :       INTEGER, DIMENSION(:, :)                           :: index_to_cell_1, index_to_cell_2
    2645              :       INTEGER                                            :: nimages_1, nimages_2
    2646              :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: index_to_cell
    2647              :       INTEGER, DIMENSION(:, :, :), POINTER               :: cell_to_index
    2648              :       INTEGER                                            :: nimages
    2649              : 
    2650              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'sum_two_R_grids'
    2651              : 
    2652              :       INTEGER                                            :: handle, i_dim, img_1, img_2, nimages_max
    2653           16 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: index_to_cell_tmp
    2654              :       INTEGER, DIMENSION(3)                              :: cell_1, cell_2, R, R_max, R_min
    2655              : 
    2656           16 :       CALL timeset(routineN, handle)
    2657              : 
    2658           64 :       DO i_dim = 1, 3
    2659         1116 :          R_min(i_dim) = MINVAL(index_to_cell_1(i_dim, :)) + MINVAL(index_to_cell_2(i_dim, :))
    2660         1180 :          R_max(i_dim) = MAXVAL(index_to_cell_1(i_dim, :)) + MAXVAL(index_to_cell_2(i_dim, :))
    2661              :       END DO
    2662              : 
    2663           16 :       nimages_max = (R_max(1) - R_min(1) + 1)*(R_max(2) - R_min(2) + 1)*(R_max(3) - R_min(3) + 1)
    2664              : 
    2665           48 :       ALLOCATE (index_to_cell_tmp(3, nimages_max))
    2666         2176 :       index_to_cell_tmp(:, :) = -1
    2667              : 
    2668           80 :       ALLOCATE (cell_to_index(R_min(1):R_max(1), R_min(2):R_max(2), R_min(3):R_max(3)))
    2669         1156 :       cell_to_index(:, :, :) = -1
    2670              : 
    2671           16 :       nimages = 0
    2672              : 
    2673          194 :       DO img_1 = 1, nimages_1
    2674              : 
    2675         2260 :          DO img_2 = 1, nimages_2
    2676              : 
    2677         8264 :             cell_1(1:3) = index_to_cell_1(1:3, img_1)
    2678         8264 :             cell_2(1:3) = index_to_cell_2(1:3, img_2)
    2679              : 
    2680         8264 :             R(1:3) = cell_1(1:3) + cell_2(1:3)
    2681              : 
    2682              :             ! check whether we have found a new cell
    2683         2244 :             IF (cell_to_index(R(1), R(2), R(3)) == -1) THEN
    2684              : 
    2685          516 :                nimages = nimages + 1
    2686          516 :                cell_to_index(R(1), R(2), R(3)) = nimages
    2687         2064 :                index_to_cell_tmp(1:3, nimages) = R(1:3)
    2688              : 
    2689              :             END IF
    2690              : 
    2691              :          END DO
    2692              : 
    2693              :       END DO
    2694              : 
    2695           48 :       ALLOCATE (index_to_cell(3, nimages))
    2696         2080 :       index_to_cell(:, :) = index_to_cell_tmp(1:3, 1:nimages)
    2697              : 
    2698           16 :       CALL timestop(handle)
    2699              : 
    2700           32 :    END SUBROUTINE sum_two_R_grids
    2701              : 
    2702              : ! **************************************************************************************************
    2703              : !> \brief ...
    2704              : !> \param bs_env ...
    2705              : ! **************************************************************************************************
    2706           16 :    SUBROUTINE setup_parallelization_Delta_R(bs_env)
    2707              : 
    2708              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2709              : 
    2710              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'setup_parallelization_Delta_R'
    2711              : 
    2712              :       INTEGER                                            :: handle, i_cell_Delta_R, i_task_local, &
    2713              :                                                             n_tasks_local
    2714           16 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: i_cell_Delta_R_group, &
    2715           16 :                                                             n_tensor_ops_Delta_R
    2716              : 
    2717           16 :       CALL timeset(routineN, handle)
    2718              : 
    2719           16 :       CALL compute_n_tensor_ops_Delta_R(bs_env, n_tensor_ops_Delta_R)
    2720              : 
    2721           16 :       CALL compute_Delta_R_dist(bs_env, n_tensor_ops_Delta_R, i_cell_Delta_R_group, n_tasks_local)
    2722              : 
    2723           16 :       bs_env%n_tasks_Delta_R_local = n_tasks_local
    2724              : 
    2725           48 :       ALLOCATE (bs_env%task_Delta_R(n_tasks_local))
    2726              : 
    2727           16 :       i_task_local = 0
    2728          532 :       DO i_cell_Delta_R = 1, bs_env%nimages_Delta_R
    2729              : 
    2730          516 :          IF (i_cell_Delta_R_group(i_cell_Delta_R) /= bs_env%tensor_group_color) CYCLE
    2731              : 
    2732          223 :          i_task_local = i_task_local + 1
    2733              : 
    2734          532 :          bs_env%task_Delta_R(i_task_local) = i_cell_Delta_R
    2735              : 
    2736              :       END DO
    2737              : 
    2738           32 :       ALLOCATE (bs_env%skip_DR_chi(n_tasks_local))
    2739          239 :       bs_env%skip_DR_chi(:) = .FALSE.
    2740           32 :       ALLOCATE (bs_env%skip_DR_Sigma(n_tasks_local))
    2741          239 :       bs_env%skip_DR_Sigma(:) = .FALSE.
    2742              : 
    2743           16 :       CALL allocate_skip_3xR(bs_env%skip_DR_R12_S_Goccx3c_chi, bs_env)
    2744           16 :       CALL allocate_skip_3xR(bs_env%skip_DR_R12_S_Gvirx3c_chi, bs_env)
    2745           16 :       CALL allocate_skip_3xR(bs_env%skip_DR_R_R2_MxM_chi, bs_env)
    2746              : 
    2747           16 :       CALL allocate_skip_3xR(bs_env%skip_DR_R1_S2_Gx3c_Sigma, bs_env)
    2748           16 :       CALL allocate_skip_3xR(bs_env%skip_DR_R1_R_MxM_Sigma, bs_env)
    2749              : 
    2750           16 :       CALL timestop(handle)
    2751              : 
    2752           32 :    END SUBROUTINE setup_parallelization_Delta_R
    2753              : 
    2754              : ! **************************************************************************************************
    2755              : !> \brief ...
    2756              : !> \param bs_env ...
    2757              : !> \param n_tensor_ops_Delta_R ...
    2758              : ! **************************************************************************************************
    2759           16 :    SUBROUTINE compute_n_tensor_ops_Delta_R(bs_env, n_tensor_ops_Delta_R)
    2760              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2761              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: n_tensor_ops_Delta_R
    2762              : 
    2763              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_n_tensor_ops_Delta_R'
    2764              : 
    2765              :       INTEGER :: handle, i_cell_Delta_R, i_cell_R, i_cell_R1, i_cell_R1_minus_R, i_cell_R2, &
    2766              :          i_cell_R2_m_R1, i_cell_S1, i_cell_S1_m_R1_p_R2, i_cell_S1_minus_R, i_cell_S2, &
    2767              :          nimages_Delta_R
    2768              :       INTEGER, DIMENSION(3) :: cell_DR, cell_m_R1, cell_R, cell_R1, cell_R1_minus_R, cell_R2, &
    2769              :          cell_R2_m_R1, cell_S1, cell_S1_m_R2_p_R1, cell_S1_minus_R, cell_S1_p_S2_m_R1, cell_S2
    2770              :       LOGICAL                                            :: cell_found
    2771              : 
    2772           16 :       CALL timeset(routineN, handle)
    2773              : 
    2774           16 :       nimages_Delta_R = bs_env%nimages_Delta_R
    2775              : 
    2776           48 :       ALLOCATE (n_tensor_ops_Delta_R(nimages_Delta_R))
    2777           16 :       n_tensor_ops_Delta_R(:) = 0
    2778              : 
    2779              :       ! compute number of tensor operations for specific Delta_R
    2780          532 :       DO i_cell_Delta_R = 1, nimages_Delta_R
    2781              : 
    2782          516 :          IF (MODULO(i_cell_Delta_R, bs_env%num_tensor_groups) /= bs_env%tensor_group_color) CYCLE
    2783              : 
    2784         3279 :          DO i_cell_R1 = 1, bs_env%nimages_3c
    2785              : 
    2786        12020 :             cell_R1(1:3) = bs_env%index_to_cell_3c(1:3, i_cell_R1)
    2787        12020 :             cell_DR(1:3) = bs_env%index_to_cell_Delta_R(1:3, i_cell_Delta_R)
    2788              : 
    2789              :             ! S_1 = R_1 + ΔR (from ΔR = S_1 - R_1)
    2790              :             CALL add_R(cell_R1, cell_DR, bs_env%index_to_cell_3c, cell_S1, &
    2791         3005 :                        cell_found, bs_env%cell_to_index_3c, i_cell_S1)
    2792         3005 :             IF (.NOT. cell_found) CYCLE
    2793              : 
    2794         9700 :             DO i_cell_R2 = 1, bs_env%nimages_scf_desymm
    2795              : 
    2796        34920 :                cell_R2(1:3) = bs_env%kpoints_scf_desymm%index_to_cell(1:3, i_cell_R2)
    2797              : 
    2798              :                ! R_2 - R_1
    2799              :                CALL add_R(cell_R2, -cell_R1, bs_env%index_to_cell_3c, cell_R2_m_R1, &
    2800        34920 :                           cell_found, bs_env%cell_to_index_3c, i_cell_R2_m_R1)
    2801         8730 :                IF (.NOT. cell_found) CYCLE
    2802              : 
    2803              :                ! S_1 - R_1 + R_2
    2804              :                CALL add_R(cell_S1, cell_R2_m_R1, bs_env%index_to_cell_3c, cell_S1_m_R2_p_R1, &
    2805         5250 :                           cell_found, bs_env%cell_to_index_3c, i_cell_S1_m_R1_p_R2)
    2806         5250 :                IF (.NOT. cell_found) CYCLE
    2807              : 
    2808        13208 :                n_tensor_ops_Delta_R(i_cell_Delta_R) = n_tensor_ops_Delta_R(i_cell_Delta_R) + 1
    2809              : 
    2810              :             END DO ! i_cell_R2
    2811              : 
    2812         9700 :             DO i_cell_S2 = 1, bs_env%nimages_scf_desymm
    2813              : 
    2814        34920 :                cell_S2(1:3) = bs_env%kpoints_scf_desymm%index_to_cell(1:3, i_cell_S2)
    2815        34920 :                cell_m_R1(1:3) = -cell_R1(1:3)
    2816        34920 :                cell_S1_p_S2_m_R1(1:3) = cell_S1(1:3) + cell_S2(1:3) - cell_R1(1:3)
    2817              : 
    2818         8730 :                CALL is_cell_in_index_to_cell(cell_m_R1, bs_env%index_to_cell_3c, cell_found)
    2819         8730 :                IF (.NOT. cell_found) CYCLE
    2820              : 
    2821         7029 :                CALL is_cell_in_index_to_cell(cell_S1_p_S2_m_R1, bs_env%index_to_cell_3c, cell_found)
    2822          970 :                IF (.NOT. cell_found) CYCLE
    2823              : 
    2824              :             END DO ! i_cell_S2
    2825              : 
    2826        13221 :             DO i_cell_R = 1, bs_env%nimages_scf_desymm
    2827              : 
    2828        34920 :                cell_R = bs_env%kpoints_scf_desymm%index_to_cell(1:3, i_cell_R)
    2829              : 
    2830              :                ! R_1 - R
    2831              :                CALL add_R(cell_R1, -cell_R, bs_env%index_to_cell_3c, cell_R1_minus_R, &
    2832        34920 :                           cell_found, bs_env%cell_to_index_3c, i_cell_R1_minus_R)
    2833         8730 :                IF (.NOT. cell_found) CYCLE
    2834              : 
    2835              :                ! S_1 - R
    2836              :                CALL add_R(cell_S1, -cell_R, bs_env%index_to_cell_3c, cell_S1_minus_R, &
    2837        22764 :                           cell_found, bs_env%cell_to_index_3c, i_cell_S1_minus_R)
    2838         3005 :                IF (.NOT. cell_found) CYCLE
    2839              : 
    2840              :             END DO ! i_cell_R
    2841              : 
    2842              :          END DO ! i_cell_R1
    2843              : 
    2844              :       END DO ! i_cell_Delta_R
    2845              : 
    2846           16 :       CALL bs_env%para_env%sum(n_tensor_ops_Delta_R)
    2847              : 
    2848           16 :       CALL timestop(handle)
    2849              : 
    2850           16 :    END SUBROUTINE compute_n_tensor_ops_Delta_R
    2851              : 
    2852              : ! **************************************************************************************************
    2853              : !> \brief ...
    2854              : !> \param cell_1 ...
    2855              : !> \param cell_2 ...
    2856              : !> \param index_to_cell ...
    2857              : !> \param cell_1_plus_2 ...
    2858              : !> \param cell_found ...
    2859              : !> \param cell_to_index ...
    2860              : !> \param i_cell_1_plus_2 ...
    2861              : ! **************************************************************************************************
    2862       279284 :    SUBROUTINE add_R(cell_1, cell_2, index_to_cell, cell_1_plus_2, cell_found, &
    2863              :                     cell_to_index, i_cell_1_plus_2)
    2864              : 
    2865              :       INTEGER, DIMENSION(3)                              :: cell_1, cell_2
    2866              :       INTEGER, DIMENSION(:, :)                           :: index_to_cell
    2867              :       INTEGER, DIMENSION(3)                              :: cell_1_plus_2
    2868              :       LOGICAL                                            :: cell_found
    2869              :       INTEGER, DIMENSION(:, :, :), INTENT(IN), &
    2870              :          OPTIONAL, POINTER                               :: cell_to_index
    2871              :       INTEGER, INTENT(OUT), OPTIONAL                     :: i_cell_1_plus_2
    2872              : 
    2873              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'add_R'
    2874              : 
    2875              :       INTEGER                                            :: handle
    2876              : 
    2877       279284 :       CALL timeset(routineN, handle)
    2878              : 
    2879      1117136 :       cell_1_plus_2(1:3) = cell_1(1:3) + cell_2(1:3)
    2880              : 
    2881       279284 :       CALL is_cell_in_index_to_cell(cell_1_plus_2, index_to_cell, cell_found)
    2882              : 
    2883       279284 :       IF (PRESENT(i_cell_1_plus_2)) THEN
    2884       279284 :          IF (cell_found) THEN
    2885       160334 :             CPASSERT(PRESENT(cell_to_index))
    2886       160334 :             i_cell_1_plus_2 = cell_to_index(cell_1_plus_2(1), cell_1_plus_2(2), cell_1_plus_2(3))
    2887              :          ELSE
    2888       118950 :             i_cell_1_plus_2 = -1000
    2889              :          END IF
    2890              :       END IF
    2891              : 
    2892       279284 :       CALL timestop(handle)
    2893              : 
    2894       279284 :    END SUBROUTINE add_R
    2895              : 
    2896              : ! **************************************************************************************************
    2897              : !> \brief ...
    2898              : !> \param cell ...
    2899              : !> \param index_to_cell ...
    2900              : !> \param cell_found ...
    2901              : ! **************************************************************************************************
    2902       437679 :    SUBROUTINE is_cell_in_index_to_cell(cell, index_to_cell, cell_found)
    2903              :       INTEGER, DIMENSION(3)                              :: cell
    2904              :       INTEGER, DIMENSION(:, :)                           :: index_to_cell
    2905              :       LOGICAL                                            :: cell_found
    2906              : 
    2907              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'is_cell_in_index_to_cell'
    2908              : 
    2909              :       INTEGER                                            :: handle, i_cell, nimg
    2910              :       INTEGER, DIMENSION(3)                              :: cell_i
    2911              : 
    2912       437679 :       CALL timeset(routineN, handle)
    2913              : 
    2914       437679 :       nimg = SIZE(index_to_cell, 2)
    2915              : 
    2916       437679 :       cell_found = .FALSE.
    2917              : 
    2918      5604294 :       DO i_cell = 1, nimg
    2919              : 
    2920     20666460 :          cell_i(1:3) = index_to_cell(1:3, i_cell)
    2921              : 
    2922      5604294 :          IF (cell_i(1) == cell(1) .AND. cell_i(2) == cell(2) .AND. cell_i(3) == cell(3)) THEN
    2923       263631 :             cell_found = .TRUE.
    2924              :          END IF
    2925              : 
    2926              :       END DO
    2927              : 
    2928       437679 :       CALL timestop(handle)
    2929              : 
    2930       437679 :    END SUBROUTINE is_cell_in_index_to_cell
    2931              : 
    2932              : ! **************************************************************************************************
    2933              : !> \brief ...
    2934              : !> \param bs_env ...
    2935              : !> \param n_tensor_ops_Delta_R ...
    2936              : !> \param i_cell_Delta_R_group ...
    2937              : !> \param n_tasks_local ...
    2938              : ! **************************************************************************************************
    2939           16 :    SUBROUTINE compute_Delta_R_dist(bs_env, n_tensor_ops_Delta_R, i_cell_Delta_R_group, n_tasks_local)
    2940              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    2941              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: n_tensor_ops_Delta_R, &
    2942              :                                                             i_cell_Delta_R_group
    2943              :       INTEGER                                            :: n_tasks_local
    2944              : 
    2945              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_Delta_R_dist'
    2946              : 
    2947              :       INTEGER                                            :: handle, i_Delta_R_max_op, i_group_min, &
    2948              :                                                             nimages_Delta_R, u
    2949           16 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: n_tensor_ops_Delta_R_in_group
    2950              : 
    2951           16 :       CALL timeset(routineN, handle)
    2952              : 
    2953           16 :       nimages_Delta_R = bs_env%nimages_Delta_R
    2954              : 
    2955           16 :       u = bs_env%unit_nr
    2956              : 
    2957           16 :       IF (u > 0 .AND. nimages_Delta_R < bs_env%num_tensor_groups) THEN
    2958            0 :          WRITE (u, FMT="(T2,A,I5,A,I5,A)") "There are only ", nimages_Delta_R, &
    2959            0 :             " tasks to work on but there are ", bs_env%num_tensor_groups, " groups."
    2960            0 :          WRITE (u, FMT="(T2,A)") "Please reduce the number of MPI processes."
    2961            0 :          WRITE (u, '(T2,A)') ''
    2962              :       END IF
    2963              : 
    2964           48 :       ALLOCATE (n_tensor_ops_Delta_R_in_group(bs_env%num_tensor_groups))
    2965           16 :       n_tensor_ops_Delta_R_in_group(:) = 0
    2966           48 :       ALLOCATE (i_cell_Delta_R_group(nimages_Delta_R))
    2967          532 :       i_cell_Delta_R_group(:) = -1
    2968              : 
    2969           16 :       n_tasks_local = 0
    2970              : 
    2971         1914 :       DO WHILE (ANY(n_tensor_ops_Delta_R(:) /= 0))
    2972              : 
    2973              :          ! get largest element of n_tensor_ops_Delta_R
    2974        15484 :          i_Delta_R_max_op = MAXLOC(n_tensor_ops_Delta_R, 1)
    2975              : 
    2976              :          ! distribute i_Delta_R_max_op to tensor group which has currently the smallest load
    2977         1784 :          i_group_min = MINLOC(n_tensor_ops_Delta_R_in_group, 1)
    2978              : 
    2979              :          ! the tensor groups are 0-index based; but i_group_min is 1-index based
    2980          446 :          i_cell_Delta_R_group(i_Delta_R_max_op) = i_group_min - 1
    2981              :          n_tensor_ops_Delta_R_in_group(i_group_min) = n_tensor_ops_Delta_R_in_group(i_group_min) + &
    2982          446 :                                                       n_tensor_ops_Delta_R(i_Delta_R_max_op)
    2983              : 
    2984              :          ! remove i_Delta_R_max_op from n_tensor_ops_Delta_R
    2985          446 :          n_tensor_ops_Delta_R(i_Delta_R_max_op) = 0
    2986              : 
    2987          462 :          IF (bs_env%tensor_group_color == i_group_min - 1) n_tasks_local = n_tasks_local + 1
    2988              : 
    2989              :       END DO
    2990              : 
    2991           16 :       CALL timestop(handle)
    2992              : 
    2993           32 :    END SUBROUTINE compute_Delta_R_dist
    2994              : 
    2995              : ! **************************************************************************************************
    2996              : !> \brief ...
    2997              : !> \param skip ...
    2998              : !> \param bs_env ...
    2999              : ! **************************************************************************************************
    3000           80 :    SUBROUTINE allocate_skip_3xR(skip, bs_env)
    3001              :       LOGICAL, ALLOCATABLE, DIMENSION(:, :, :)           :: skip
    3002              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3003              : 
    3004              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'allocate_skip_3xR'
    3005              : 
    3006              :       INTEGER                                            :: handle
    3007              : 
    3008           80 :       CALL timeset(routineN, handle)
    3009              : 
    3010          400 :       ALLOCATE (skip(bs_env%n_tasks_Delta_R_local, bs_env%nimages_3c, bs_env%nimages_scf_desymm))
    3011           80 :       skip(:, :, :) = .FALSE.
    3012              : 
    3013           80 :       CALL timestop(handle)
    3014              : 
    3015           80 :    END SUBROUTINE allocate_skip_3xR
    3016              : 
    3017              : ! **************************************************************************************************
    3018              : !> \brief ...
    3019              : !> \param qs_env ...
    3020              : !> \param bs_env ...
    3021              : ! **************************************************************************************************
    3022           16 :    SUBROUTINE allocate_matrices_small_cell_full_kp_tensor(qs_env, bs_env)
    3023              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    3024              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3025              : 
    3026              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'allocate_matrices_small_cell_full_kp_tensor'
    3027              : 
    3028              :       INTEGER                                            :: handle, i_spin, i_t, img, n_spin, &
    3029              :                                                             nimages_scf, num_time_freq_points
    3030              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
    3031              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    3032              : 
    3033           16 :       CALL timeset(routineN, handle)
    3034              : 
    3035           16 :       nimages_scf = bs_env%nimages_scf_desymm
    3036           16 :       num_time_freq_points = bs_env%num_time_freq_points
    3037           16 :       n_spin = bs_env%n_spin
    3038              : 
    3039           16 :       CALL get_qs_env(qs_env, para_env=para_env, blacs_env=blacs_env)
    3040              : 
    3041          192 :       ALLOCATE (bs_env%fm_G_S(nimages_scf))
    3042          176 :       ALLOCATE (bs_env%fm_Sigma_x_R(nimages_scf))
    3043         1104 :       ALLOCATE (bs_env%fm_chi_R_t(nimages_scf, num_time_freq_points))
    3044         1088 :       ALLOCATE (bs_env%fm_MWM_R_t(nimages_scf, num_time_freq_points))
    3045         1136 :       ALLOCATE (bs_env%fm_Sigma_c_R_neg_tau(nimages_scf, num_time_freq_points, n_spin))
    3046         1120 :       ALLOCATE (bs_env%fm_Sigma_c_R_pos_tau(nimages_scf, num_time_freq_points, n_spin))
    3047          160 :       DO img = 1, nimages_scf
    3048          144 :          CALL cp_fm_create(bs_env%fm_G_S(img), bs_env%fm_work_mo(1)%matrix_struct)
    3049          144 :          CALL cp_fm_create(bs_env%fm_Sigma_x_R(img), bs_env%fm_work_mo(1)%matrix_struct)
    3050         1096 :          DO i_t = 1, num_time_freq_points
    3051          936 :             CALL cp_fm_create(bs_env%fm_chi_R_t(img, i_t), bs_env%fm_RI_RI%matrix_struct)
    3052          936 :             CALL cp_fm_create(bs_env%fm_MWM_R_t(img, i_t), bs_env%fm_RI_RI%matrix_struct)
    3053          936 :             CALL cp_fm_set_all(bs_env%fm_MWM_R_t(img, i_t), 0.0_dp)
    3054         2016 :             DO i_spin = 1, n_spin
    3055              :                CALL cp_fm_create(bs_env%fm_Sigma_c_R_neg_tau(img, i_t, i_spin), &
    3056          936 :                                  bs_env%fm_work_mo(1)%matrix_struct)
    3057              :                CALL cp_fm_create(bs_env%fm_Sigma_c_R_pos_tau(img, i_t, i_spin), &
    3058          936 :                                  bs_env%fm_work_mo(1)%matrix_struct)
    3059          936 :                CALL cp_fm_set_all(bs_env%fm_Sigma_c_R_neg_tau(img, i_t, i_spin), 0.0_dp)
    3060         1872 :                CALL cp_fm_set_all(bs_env%fm_Sigma_c_R_pos_tau(img, i_t, i_spin), 0.0_dp)
    3061              :             END DO
    3062              :          END DO
    3063              :       END DO
    3064              : 
    3065           16 :       CALL timestop(handle)
    3066              : 
    3067           16 :    END SUBROUTINE allocate_matrices_small_cell_full_kp_tensor
    3068              : 
    3069              : ! **************************************************************************************************
    3070              : !> \brief ...
    3071              : !> \param qs_env ...
    3072              : !> \param bs_env ...
    3073              : ! **************************************************************************************************
    3074           16 :    SUBROUTINE trafo_V_xc_R_to_kp(qs_env, bs_env)
    3075              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    3076              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3077              : 
    3078              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'trafo_V_xc_R_to_kp'
    3079              : 
    3080              :       INTEGER                                            :: handle, ikp, img, ispin, n_ao
    3081           16 :       INTEGER, DIMENSION(:, :, :), POINTER               :: cell_to_index_scf
    3082              :       TYPE(cp_cfm_type)                                  :: cfm_mo_coeff, cfm_V_xc
    3083              :       TYPE(cp_fm_type)                                   :: fm_V_xc_re
    3084           16 :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: matrix_ks
    3085              :       TYPE(kpoint_type), POINTER                         :: kpoints_scf
    3086              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
    3087           16 :          POINTER                                         :: sab_nl
    3088              : 
    3089           16 :       CALL timeset(routineN, handle)
    3090              : 
    3091           16 :       n_ao = bs_env%n_ao
    3092              : 
    3093           16 :       CALL get_qs_env(qs_env, matrix_ks_kp=matrix_ks, kpoints=kpoints_scf)
    3094              : 
    3095           16 :       NULLIFY (sab_nl)
    3096           16 :       CALL get_kpoint_info(kpoints_scf, sab_nl=sab_nl, cell_to_index=cell_to_index_scf)
    3097              : 
    3098           16 :       CALL cp_cfm_create(cfm_V_xc, bs_env%cfm_work_mo%matrix_struct)
    3099           16 :       CALL cp_cfm_create(cfm_mo_coeff, bs_env%cfm_work_mo%matrix_struct)
    3100           16 :       CALL cp_fm_create(fm_V_xc_re, bs_env%cfm_work_mo%matrix_struct)
    3101              : 
    3102          544 :       DO img = 1, bs_env%nimages_scf
    3103         1072 :          DO ispin = 1, bs_env%n_spin
    3104              :             ! JW kind of hack because the format of matrix_ks remains dubious...
    3105          528 :             CALL dbcsr_set(matrix_ks(ispin, img)%matrix, 0.0_dp)
    3106         1056 :             CALL copy_fm_to_dbcsr(bs_env%fm_V_xc_R(img, ispin), matrix_ks(ispin, img)%matrix)
    3107              :          END DO
    3108              :       END DO
    3109              : 
    3110           80 :       ALLOCATE (bs_env%v_xc_n(n_ao, bs_env%nkp_bs_and_DOS, bs_env%n_spin))
    3111              : 
    3112           32 :       DO ispin = 1, bs_env%n_spin
    3113          350 :          DO ikp = 1, bs_env%nkp_bs_and_DOS
    3114              : 
    3115              :             ! v^xc^R -> v^xc(k)  (matrix_ks stores v^xc^R, see SUBROUTINE compute_V_xc)
    3116              :             CALL rsmat_to_kp(matrix_ks, ispin, bs_env%kpoints_DOS%xkp(1:3, ikp), &
    3117          318 :                              cell_to_index_scf, sab_nl, bs_env, cfm_V_xc)
    3118              : 
    3119              :             ! get C_µn(k)
    3120          318 :             CALL cp_cfm_to_cfm(bs_env%cfm_mo_coeff_kp(ikp, ispin), cfm_mo_coeff)
    3121              : 
    3122              :             ! v^xc_nm(k_i) = sum_µν C^*_µn(k_i) v^xc_µν(k_i) C_νn(k_i)
    3123          318 :             CALL cfm_contract_ABA(cfm_mo_coeff, cfm_V_xc)
    3124              : 
    3125              :             ! get v^xc_nn(k_i) which is a real quantity as v^xc is Hermitian
    3126          318 :             CALL cp_cfm_to_fm(cfm_V_xc, fm_V_xc_re)
    3127          334 :             CALL cp_fm_get_diag(fm_V_xc_re, bs_env%v_xc_n(:, ikp, ispin))
    3128              : 
    3129              :          END DO
    3130              : 
    3131              :       END DO
    3132              : 
    3133              :       ! just rebuild the overwritten KS matrix again
    3134           16 :       CALL qs_ks_build_kohn_sham_matrix(qs_env, calculate_forces=.FALSE., just_energy=.FALSE.)
    3135              : 
    3136           16 :       CALL cp_cfm_release(cfm_V_xc)
    3137           16 :       CALL cp_cfm_release(cfm_mo_coeff)
    3138           16 :       CALL cp_fm_release(fm_V_xc_re)
    3139              : 
    3140           16 :       CALL timestop(handle)
    3141              : 
    3142           32 :    END SUBROUTINE trafo_V_xc_R_to_kp
    3143              : 
    3144              : ! **************************************************************************************************
    3145              : !> \brief ...
    3146              : !> \param qs_env ...
    3147              : !> \param bs_env ...
    3148              : ! **************************************************************************************************
    3149           16 :    SUBROUTINE heuristic_RI_regularization(qs_env, bs_env)
    3150              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    3151              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3152              : 
    3153              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'heuristic_RI_regularization'
    3154              : 
    3155           16 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :)  :: M
    3156              :       INTEGER                                            :: handle, ikp, ikp_local, n_RI, nkp, &
    3157              :                                                             nkp_local, u
    3158              :       REAL(KIND=dp)                                      :: cond_nr, cond_nr_max, max_ev, &
    3159              :                                                             max_ev_ikp, min_ev, min_ev_ikp
    3160           16 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :)     :: M_R
    3161              : 
    3162           16 :       CALL timeset(routineN, handle)
    3163              : 
    3164              :       ! compute M^R_PQ = <phi_P,0|V^tr(rc)|phi_Q,R> for RI metric
    3165           16 :       CALL get_V_tr_R(M_R, bs_env%ri_metric, 0.0_dp, bs_env, qs_env)
    3166              : 
    3167           16 :       nkp = bs_env%nkp_chi_eps_W_orig_plus_extra
    3168           16 :       n_RI = bs_env%n_RI
    3169              : 
    3170           16 :       nkp_local = 0
    3171        10256 :       DO ikp = 1, nkp
    3172              :          ! trivial parallelization over k-points
    3173        10240 :          IF (MODULO(ikp, bs_env%para_env%num_pe) /= bs_env%para_env%mepos) CYCLE
    3174        10256 :          nkp_local = nkp_local + 1
    3175              :       END DO
    3176              : 
    3177           80 :       ALLOCATE (M(n_RI, n_RI, nkp_local))
    3178              : 
    3179           16 :       ikp_local = 0
    3180           16 :       cond_nr_max = 0.0_dp
    3181           16 :       min_ev = 1000.0_dp
    3182           16 :       max_ev = -1000.0_dp
    3183              : 
    3184        10256 :       DO ikp = 1, nkp
    3185              : 
    3186              :          ! trivial parallelization
    3187        10240 :          IF (MODULO(ikp, bs_env%para_env%num_pe) /= bs_env%para_env%mepos) CYCLE
    3188              : 
    3189         5120 :          ikp_local = ikp_local + 1
    3190              : 
    3191              :          ! M(k) = sum_R e^ikR M^R
    3192              :          CALL rs_to_kp(M_R, M(:, :, ikp_local), &
    3193              :                        bs_env%kpoints_scf_desymm%index_to_cell, &
    3194         5120 :                        bs_env%kpoints_chi_eps_W%xkp(1:3, ikp))
    3195              : 
    3196              :          ! compute condition number of M_PQ(k)
    3197         5120 :          CALL power(M(:, :, ikp_local), 1.0_dp, 0.0_dp, cond_nr, min_ev_ikp, max_ev_ikp)
    3198              : 
    3199         5120 :          IF (cond_nr > cond_nr_max) cond_nr_max = cond_nr
    3200         5120 :          IF (max_ev_ikp > max_ev) max_ev = max_ev_ikp
    3201         5136 :          IF (min_ev_ikp < min_ev) min_ev = min_ev_ikp
    3202              : 
    3203              :       END DO ! ikp
    3204              : 
    3205           16 :       CALL bs_env%para_env%max(cond_nr_max)
    3206           16 :       CALL bs_env%para_env%min(min_ev)
    3207           16 :       CALL bs_env%para_env%max(max_ev)
    3208              : 
    3209           16 :       u = bs_env%unit_nr
    3210           16 :       IF (u > 0) THEN
    3211            8 :          WRITE (u, FMT="(T2,A,ES34.1)") "Min. abs. eigenvalue of RI metric matrix M(k)", min_ev
    3212            8 :          WRITE (u, FMT="(T2,A,ES34.1)") "Max. abs. eigenvalue of RI metric matrix M(k)", max_ev
    3213            8 :          WRITE (u, FMT="(T2,A,ES50.1)") "Max. condition number of M(k)", cond_nr_max
    3214              :       END IF
    3215              : 
    3216           16 :       CALL timestop(handle)
    3217              : 
    3218           32 :    END SUBROUTINE heuristic_RI_regularization
    3219              : 
    3220              : ! **************************************************************************************************
    3221              : !> \brief ...
    3222              : !> \param V_tr_R ...
    3223              : !> \param pot_type ...
    3224              : !> \param regularization_RI ...
    3225              : !> \param bs_env ...
    3226              : !> \param qs_env ...
    3227              : ! **************************************************************************************************
    3228          168 :    SUBROUTINE get_V_tr_R(V_tr_R, pot_type, regularization_RI, bs_env, qs_env)
    3229              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :)     :: V_tr_R
    3230              :       TYPE(libint_potential_type)                        :: pot_type
    3231              :       REAL(KIND=dp)                                      :: regularization_RI
    3232              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3233              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    3234              : 
    3235              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'get_V_tr_R'
    3236              : 
    3237              :       INTEGER                                            :: handle, img, nimages_scf_desymm
    3238              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: sizes_RI
    3239          168 :       INTEGER, DIMENSION(:), POINTER                     :: col_bsize, row_bsize
    3240              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
    3241          168 :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:)        :: fm_V_tr_R
    3242              :       TYPE(dbcsr_distribution_type)                      :: dbcsr_dist
    3243          168 :       TYPE(dbcsr_type), ALLOCATABLE, DIMENSION(:)        :: mat_V_tr_R
    3244              :       TYPE(distribution_2d_type), POINTER                :: dist_2d
    3245              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
    3246          168 :          POINTER                                         :: sab_RI
    3247          168 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
    3248          168 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
    3249              : 
    3250          168 :       CALL timeset(routineN, handle)
    3251              : 
    3252          168 :       NULLIFY (sab_RI, dist_2d)
    3253              : 
    3254              :       CALL get_qs_env(qs_env=qs_env, &
    3255              :                       blacs_env=blacs_env, &
    3256              :                       distribution_2d=dist_2d, &
    3257              :                       qs_kind_set=qs_kind_set, &
    3258          168 :                       particle_set=particle_set)
    3259              : 
    3260          504 :       ALLOCATE (sizes_RI(bs_env%n_atom))
    3261          168 :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=sizes_RI, basis=bs_env%basis_set_RI)
    3262              :       CALL build_2c_neighbor_lists(sab_RI, bs_env%basis_set_RI, bs_env%basis_set_RI, &
    3263              :                                    pot_type, "2c_nl_RI", qs_env, sym_ij=.FALSE., &
    3264          168 :                                    dist_2d=dist_2d)
    3265          168 :       CALL cp_dbcsr_dist2d_to_dist(dist_2d, dbcsr_dist)
    3266          504 :       ALLOCATE (row_bsize(SIZE(sizes_RI)))
    3267          336 :       ALLOCATE (col_bsize(SIZE(sizes_RI)))
    3268          644 :       row_bsize(:) = sizes_RI
    3269          644 :       col_bsize(:) = sizes_RI
    3270              : 
    3271          168 :       nimages_scf_desymm = bs_env%nimages_scf_desymm
    3272         2016 :       ALLOCATE (mat_V_tr_R(nimages_scf_desymm))
    3273              :       CALL dbcsr_create(mat_V_tr_R(1), "(RI|RI)", dbcsr_dist, dbcsr_type_no_symmetry, &
    3274          168 :                         row_bsize, col_bsize)
    3275          168 :       DEALLOCATE (row_bsize, col_bsize)
    3276              : 
    3277         1512 :       DO img = 2, nimages_scf_desymm
    3278         1512 :          CALL dbcsr_create(mat_V_tr_R(img), template=mat_V_tr_R(1))
    3279              :       END DO
    3280              : 
    3281              :       CALL build_2c_integrals(mat_V_tr_R, 0.0_dp, qs_env, sab_RI, bs_env%basis_set_RI, &
    3282              :                               bs_env%basis_set_RI, pot_type, do_kpoints=.TRUE., &
    3283              :                               ext_kpoints=bs_env%kpoints_scf_desymm, &
    3284          168 :                               regularization_RI=regularization_RI)
    3285              : 
    3286         2016 :       ALLOCATE (fm_V_tr_R(nimages_scf_desymm))
    3287         1680 :       DO img = 1, nimages_scf_desymm
    3288         1512 :          CALL cp_fm_create(fm_V_tr_R(img), bs_env%fm_RI_RI%matrix_struct)
    3289         1512 :          CALL copy_dbcsr_to_fm(mat_V_tr_R(img), fm_V_tr_R(img))
    3290         1680 :          CALL dbcsr_release(mat_V_tr_R(img))
    3291              :       END DO
    3292              : 
    3293          168 :       IF (.NOT. ALLOCATED(V_tr_R)) THEN
    3294          840 :          ALLOCATE (V_tr_R(bs_env%n_RI, bs_env%n_RI, nimages_scf_desymm))
    3295              :       END IF
    3296              : 
    3297          168 :       CALL fm_to_local_array(fm_V_tr_R, V_tr_R)
    3298              : 
    3299          168 :       CALL cp_fm_release(fm_V_tr_R)
    3300          168 :       CALL dbcsr_distribution_release(dbcsr_dist)
    3301          168 :       CALL release_neighbor_list_sets(sab_RI)
    3302              : 
    3303          168 :       CALL timestop(handle)
    3304              : 
    3305          504 :    END SUBROUTINE get_V_tr_R
    3306              : 
    3307              : ! **************************************************************************************************
    3308              : !> \brief ...
    3309              : !> \param matrix ...
    3310              : !> \param exponent ...
    3311              : !> \param eps ...
    3312              : !> \param cond_nr ...
    3313              : !> \param min_ev ...
    3314              : !> \param max_ev ...
    3315              : ! **************************************************************************************************
    3316        82880 :    SUBROUTINE power(matrix, exponent, eps, cond_nr, min_ev, max_ev)
    3317              :       COMPLEX(KIND=dp), DIMENSION(:, :)                  :: matrix
    3318              :       REAL(KIND=dp)                                      :: exponent, eps
    3319              :       REAL(KIND=dp), OPTIONAL                            :: cond_nr, min_ev, max_ev
    3320              : 
    3321              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'power'
    3322              : 
    3323        82880 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: eigenvectors
    3324              :       INTEGER                                            :: handle, i, n
    3325              :       REAL(KIND=dp)                                      :: pos_eval
    3326        82880 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues
    3327              : 
    3328        82880 :       CALL timeset(routineN, handle)
    3329              : 
    3330              :       ! make matrix perfectly Hermitian
    3331      6687296 :       matrix(:, :) = 0.5_dp*(matrix(:, :) + CONJG(TRANSPOSE(matrix(:, :))))
    3332              : 
    3333        82880 :       n = SIZE(matrix, 1)
    3334       497280 :       ALLOCATE (eigenvalues(n), eigenvectors(n, n))
    3335        82880 :       CALL diag_complex(matrix, eigenvectors, eigenvalues)
    3336              : 
    3337       143040 :       IF (PRESENT(cond_nr)) cond_nr = MAXVAL(ABS(eigenvalues))/MINVAL(ABS(eigenvalues))
    3338       112960 :       IF (PRESENT(min_ev)) min_ev = MINVAL(ABS(eigenvalues))
    3339       112960 :       IF (PRESENT(max_ev)) max_ev = MAXVAL(ABS(eigenvalues))
    3340              : 
    3341       565264 :       DO i = 1, n
    3342       482384 :          IF (eps < eigenvalues(i)) THEN
    3343       482384 :             pos_eval = (eigenvalues(i))**(0.5_dp*exponent)
    3344              :          ELSE
    3345              :             pos_eval = 0.0_dp
    3346              :          END IF
    3347      3385088 :          eigenvectors(:, i) = eigenvectors(:, i)*pos_eval
    3348              :       END DO
    3349              : 
    3350        82880 :       CALL ZGEMM("N", "C", n, n, n, z_one, eigenvectors, n, eigenvectors, n, z_zero, matrix, n)
    3351              : 
    3352        82880 :       DEALLOCATE (eigenvalues, eigenvectors)
    3353              : 
    3354        82880 :       CALL timestop(handle)
    3355              : 
    3356        82880 :    END SUBROUTINE power
    3357              : 
    3358              : ! **************************************************************************************************
    3359              : !> \brief ...
    3360              : !> \param bs_env ...
    3361              : ! **************************************************************************************************
    3362          124 :    SUBROUTINE setup_time_and_frequency_minimax_grid(bs_env)
    3363              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3364              : 
    3365              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'setup_time_and_frequency_minimax_grid'
    3366              : 
    3367              :       INTEGER                                            :: handle, homo, ispin, n_mo, n_top, &
    3368              :                                                             num_time_freq_points, u
    3369              :       REAL(KIND=dp)                                      :: E_max, E_max_ispin, E_min, E_min_ispin, &
    3370              :                                                             E_range, max_error_min
    3371              : 
    3372          124 :       CALL timeset(routineN, handle)
    3373              : 
    3374          124 :       n_mo = bs_env%n_ao
    3375          124 :       num_time_freq_points = bs_env%num_time_freq_points
    3376              : 
    3377              :       ! minimum and maximum difference between eigenvalues of unoccupied and an occupied MOs
    3378          124 :       E_min = 1000.0_dp
    3379          124 :       E_max = -1000.0_dp
    3380          264 :       DO ispin = 1, bs_env%n_spin
    3381          140 :          homo = bs_env%n_occ(ispin)
    3382              : 
    3383              :          ! Highest index that is a real state. The canonical orthogonalization removes the
    3384              :          ! linearly dependent basis modes and parks them at the top of the spectrum with a
    3385              :          ! placeholder eigenvalue; n_mo_retained is how many real states it kept.
    3386          140 :          n_top = MAX(MIN(bs_env%n_mo_retained, n_mo), homo + 1)
    3387              : 
    3388          264 :          SELECT CASE (bs_env%gw_implementation)
    3389              :          CASE (tensor_large_cell_Gamma, ri_rs_large_cell_Gamma, ri_rs_non_periodic)
    3390              :             E_min_ispin = bs_env%eigenval_scf_Gamma(homo + 1, ispin) - &
    3391          124 :                           bs_env%eigenval_scf_Gamma(homo, ispin)
    3392              :             E_max_ispin = bs_env%eigenval_scf_Gamma(n_top, ispin) - &
    3393          124 :                           bs_env%eigenval_scf_Gamma(1, ispin)
    3394              :          CASE (tensor_small_cell_full_kp)
    3395              :             E_min_ispin = MINVAL(bs_env%eigenval_scf(homo + 1, :, ispin)) - &
    3396          652 :                           MAXVAL(bs_env%eigenval_scf(homo, :, ispin))
    3397              :             E_max_ispin = MAXVAL(bs_env%eigenval_scf(n_top, :, ispin)) - &
    3398          792 :                           MINVAL(bs_env%eigenval_scf(1, :, ispin))
    3399              :          END SELECT
    3400          140 :          E_min = MIN(E_min, E_min_ispin)
    3401          264 :          E_max = MAX(E_max, E_max_ispin)
    3402              :       END DO
    3403              : 
    3404              :       ! Open-shell uses ONE minimax grid for the combined [min gap, max span] over both spins (the
    3405              :       ! superset covers each channel, so it is accurate; per-spin grids would only be more efficient).
    3406          124 :       IF (bs_env%n_spin > 1) THEN
    3407              :          CALL cp_hint(__LOCATION__, &
    3408              :                       "Open-shell GW uses one minimax grid spanning [min gap, max span] across both "// &
    3409              :                       "spin channels; raise NUM_TIME_FREQ_POINTS if QP convergence is marginal for "// &
    3410           16 :                       "strongly spin-asymmetric systems.")
    3411              :       END IF
    3412              : 
    3413          124 :       E_range = E_max/E_min
    3414              : 
    3415              :       CALL build_minimax_time_frequency_grid(num_time_freq_points, E_min, E_max, bs_env%regularization_minimax, &
    3416              :                                              bs_env%num_points_per_magnitude, bs_env%time_frequency_grid, &
    3417              :                                              build_frequency=.TRUE., build_time=.TRUE., build_transforms=.TRUE., &
    3418              :                                              build_sine=.TRUE., time_scaling=2.0_dp, time_weight_scaling=1.0_dp, &
    3419              :                                              max_fit_error=max_error_min, print_warning=.FALSE., unit_nr=0, &
    3420          124 :                                              prefer_external_backend=.FALSE.)
    3421              : 
    3422              :       ! determine number of fit points in the interval [0,ω_max] for virt, or [-ω_max,0] for occ
    3423         2022 :       bs_env%num_freq_points_fit = COUNT(bs_env%time_frequency_grid%frequency < bs_env%freq_max_fit)
    3424              : 
    3425              :       ! iω values for the analytic continuation Σ^c_n(iω,k) -> Σ^c_n(ϵ,k)
    3426          372 :       ALLOCATE (bs_env%imag_freq_points_fit(bs_env%num_freq_points_fit))
    3427              :       bs_env%imag_freq_points_fit(:) = PACK(bs_env%time_frequency_grid%frequency, &
    3428         2364 :                                             bs_env%time_frequency_grid%frequency < bs_env%freq_max_fit)
    3429              : 
    3430              :       ! reset the number of Padé parameters if smaller than the number of
    3431              :       ! imaginary-frequency points for the fit
    3432          124 :       IF (bs_env%num_freq_points_fit < bs_env%nparam_pade) THEN
    3433          124 :          bs_env%nparam_pade = bs_env%num_freq_points_fit
    3434              :       END IF
    3435              : 
    3436          124 :       u = bs_env%unit_nr
    3437          124 :       IF (u > 0) THEN
    3438           62 :          WRITE (u, '(T2,A)') ''
    3439           62 :          WRITE (u, '(T2,A,F55.2)') 'SCF direct band gap (eV)', E_min*evolt
    3440           62 :          WRITE (u, '(T2,A,F53.2)') 'Max. SCF eigval diff. (eV)', E_max*evolt
    3441           62 :          WRITE (u, '(T2,A,F55.2)') 'E-Range for minimax grid', E_range
    3442           62 :          WRITE (u, '(T2,A,I27)') 'Number of Padé parameters for analytic continuation:', &
    3443          124 :             bs_env%nparam_pade
    3444           62 :          WRITE (u, '(T2,A)') ''
    3445              :       END IF
    3446              : 
    3447              :       ! in minimax grids, Fourier transforms t -> w and w -> t are split using
    3448              :       ! e^(iwt) = cos(wt) + i sin(wt); we thus calculate weights for trafos with a cos and
    3449              :       ! sine prefactor; details in Azizi, Wilhelm, Golze, Giantomassi, Panades-Barrueta,
    3450              :       ! Rinke, Draxl, Gonze et al., 2 publications
    3451              : 
    3452          124 :       CALL timestop(handle)
    3453              : 
    3454          124 :    END SUBROUTINE setup_time_and_frequency_minimax_grid
    3455              : 
    3456              : ! **************************************************************************************************
    3457              : !> \brief Releases the memory-heavy GW intermediates that cannot be freed in bs_env_release,
    3458              : !>        retaining the 3c neighbor list only when an AO-RI RT-BSE self-energy still needs it
    3459              : !> \param qs_env ...
    3460              : !> \param bs_env ...
    3461              : ! **************************************************************************************************
    3462          124 :    SUBROUTINE de_init_bs_env(qs_env, bs_env)
    3463              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    3464              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3465              : 
    3466              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'de_init_bs_env'
    3467              : 
    3468              :       INTEGER                                            :: handle
    3469              :       LOGICAL                                            :: retain_nl_3c, rirs_kernel
    3470              : 
    3471          124 :       CALL timeset(routineN, handle)
    3472              :       ! deallocate quantities here which:
    3473              :       ! 1. cannot be deallocated in bs_env_release due to circular dependencies
    3474              :       ! 2. consume a lot of memory and should not be kept until the quantity is
    3475              :       !    deallocated in bs_env_release
    3476              : 
    3477              :       ! nl_3c feeds only the AO-RI SEX self-energy (compute_3c_integrals); the RI-RS SEX
    3478              :       ! path never reads it, and AO-RI Hartree builds its own blocks. Retain iff AO-RI SEX.
    3479          124 :       retain_nl_3c = .FALSE.
    3480          124 :       IF (ASSOCIATED(bs_env%nl_3c%ij_list) .AND. (bs_env%rtp_method == rtp_method_bse)) THEN
    3481           72 :          CALL rtbse_resolve_rirs_flag(qs_env, bs_env, rirs_kernel=rirs_kernel)
    3482           72 :          retain_nl_3c = .NOT. rirs_kernel
    3483              :       END IF
    3484              : 
    3485           72 :       IF (retain_nl_3c) THEN
    3486           50 :          IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, *) "Retaining nl_3c for AO-RI RT-BSE self-energy"
    3487              :       ELSE
    3488           74 :          CALL neighbor_list_3c_destroy(bs_env%nl_3c)
    3489              :       END IF
    3490              : 
    3491          124 :       CALL cp_libint_static_cleanup()
    3492              : 
    3493          124 :       CALL timestop(handle)
    3494              : 
    3495          124 :    END SUBROUTINE de_init_bs_env
    3496              : 
    3497              : ! **************************************************************************************************
    3498              : !> \brief Resolve the linRTBSE RI-RS kernel switch from the KERNEL_RI input and the GW default.
    3499              : !> \param qs_env ...
    3500              : !> \param bs_env ...
    3501              : !> \param rirs_kernel (optional) .TRUE. if the Hartree + SEX kernels use the RI-RS grid backend
    3502              : !> \author Maximilian Graml
    3503              : !> \note Single source of truth shared by create_rtbse_env (sets the flag) and de_init_bs_env
    3504              : !>       (decides whether to retain nl_3c). KERNEL_RI=DEFAULT follows bs_env%do_gw_ri_rs;
    3505              : !>       RS/AO force; forced .FALSE. for non-linearized (full) RT-BSE (warn on explicit RS).
    3506              : ! **************************************************************************************************
    3507          288 :    SUBROUTINE rtbse_resolve_rirs_flag(qs_env, bs_env, rirs_kernel)
    3508              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    3509              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3510              :       LOGICAL, INTENT(OUT), OPTIONAL                     :: rirs_kernel
    3511              : 
    3512              :       INTEGER                                            :: kernel_ri
    3513              :       LOGICAL                                            :: my_rirs_kernel
    3514              :       TYPE(dft_control_type), POINTER                    :: dft_control
    3515              :       TYPE(section_vals_type), POINTER                   :: input
    3516              : 
    3517          144 :       NULLIFY (dft_control, input)
    3518          144 :       CALL get_qs_env(qs_env, dft_control=dft_control, input=input)
    3519              : 
    3520              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%KERNEL_RI", &
    3521          144 :                                 i_val=kernel_ri)
    3522              :       SELECT CASE (kernel_ri)
    3523              :       CASE (rtp_bse_kernel_ri_rs)
    3524            0 :          my_rirs_kernel = .TRUE.
    3525              :       CASE (rtp_bse_kernel_ri_ao)
    3526            0 :          my_rirs_kernel = .FALSE.
    3527              :       CASE DEFAULT  ! rtp_bse_kernel_ri_default
    3528          144 :          my_rirs_kernel = bs_env%do_gw_ri_rs
    3529              :       END SELECT
    3530              : 
    3531              :       ! RI-RS kernels are implemented for linearized RT-BSE only; full RT-BSE always uses AO-RI.
    3532          144 :       IF (dft_control%rtp_control%rtp_method /= rtp_method_bse_linearized) THEN
    3533           28 :          IF (kernel_ri == rtp_bse_kernel_ri_rs) THEN
    3534            0 :             CPWARN("RI-RS kernels are implemented for linearized RT-BSE only; forcing AO")
    3535              :          END IF
    3536              :          my_rirs_kernel = .FALSE.
    3537              :       END IF
    3538              : 
    3539          144 :       IF (PRESENT(rirs_kernel)) rirs_kernel = my_rirs_kernel
    3540              : 
    3541          144 :    END SUBROUTINE rtbse_resolve_rirs_flag
    3542              : 
    3543              : ! **************************************************************************************************
    3544              : !> \brief ...
    3545              : !> \param bs_env ...
    3546              : !> \param Sigma_c_n_time ...
    3547              : !> \param Sigma_c_n_freq ...
    3548              : !> \param ispin ...
    3549              : ! **************************************************************************************************
    3550          474 :    SUBROUTINE time_to_freq(bs_env, Sigma_c_n_time, Sigma_c_n_freq, ispin)
    3551              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3552              :       REAL(KIND=dp), DIMENSION(:, :, :)                  :: Sigma_c_n_time, Sigma_c_n_freq
    3553              :       INTEGER                                            :: ispin
    3554              : 
    3555              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'time_to_freq'
    3556              : 
    3557              :       INTEGER                                            :: handle, i_t, j_w, n_occ
    3558              :       REAL(KIND=dp)                                      :: freq_j, time_i, w_cos_ij, w_sin_ij
    3559          474 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: Sigma_c_n_cos_time, Sigma_c_n_sin_time
    3560              : 
    3561          474 :       CALL timeset(routineN, handle)
    3562              : 
    3563         1896 :       ALLOCATE (Sigma_c_n_cos_time(bs_env%n_ao, bs_env%num_time_freq_points))
    3564         1422 :       ALLOCATE (Sigma_c_n_sin_time(bs_env%n_ao, bs_env%num_time_freq_points))
    3565              : 
    3566        55666 :       Sigma_c_n_cos_time(:, :) = 0.5_dp*(Sigma_c_n_time(:, :, 1) + Sigma_c_n_time(:, :, 2))
    3567        55666 :       Sigma_c_n_sin_time(:, :) = 0.5_dp*(Sigma_c_n_time(:, :, 1) - Sigma_c_n_time(:, :, 2))
    3568              : 
    3569       111806 :       Sigma_c_n_freq(:, :, :) = 0.0_dp
    3570              : 
    3571         5096 :       DO i_t = 1, bs_env%num_time_freq_points
    3572              : 
    3573        61898 :          DO j_w = 1, bs_env%num_time_freq_points
    3574              : 
    3575        56802 :             freq_j = bs_env%time_frequency_grid%frequency(j_w)
    3576        56802 :             time_i = bs_env%time_frequency_grid%imaginary_time(i_t)
    3577              :             ! integration weights for cosine and sine transform
    3578        56802 :             w_cos_ij = bs_env%time_frequency_grid%cosine_time_to_frequency_weights(j_w, i_t)*COS(freq_j*time_i)
    3579        56802 :             w_sin_ij = bs_env%time_frequency_grid%sine_time_to_frequency_weights(j_w, i_t)*SIN(freq_j*time_i)
    3580              : 
    3581              :             ! 1. Re(Σ^c_nn(k_i,iω)) from cosine transform
    3582              :             Sigma_c_n_freq(:, j_w, 1) = Sigma_c_n_freq(:, j_w, 1) + &
    3583       647872 :                                         w_cos_ij*Sigma_c_n_cos_time(:, i_t)
    3584              : 
    3585              :             ! 2. Im(Σ^c_nn(k_i,iω)) from sine transform
    3586              :             Sigma_c_n_freq(:, j_w, 2) = Sigma_c_n_freq(:, j_w, 2) + &
    3587       652494 :                                         w_sin_ij*Sigma_c_n_sin_time(:, i_t)
    3588              : 
    3589              :          END DO
    3590              : 
    3591              :       END DO
    3592              : 
    3593              :       ! for occupied levels, we need the correlation self-energy for negative omega.
    3594              :       ! Therefore, weight_sin should be computed with -omega, which results in an
    3595              :       ! additional minus for the imaginary part:
    3596          474 :       n_occ = bs_env%n_occ(ispin)
    3597        19654 :       Sigma_c_n_freq(1:n_occ, :, 2) = -Sigma_c_n_freq(1:n_occ, :, 2)
    3598              : 
    3599          474 :       CALL timestop(handle)
    3600              : 
    3601          948 :    END SUBROUTINE time_to_freq
    3602              : 
    3603              : ! **************************************************************************************************
    3604              : !> \brief ...
    3605              : !> \param bs_env ...
    3606              : !> \param Sigma_c_ikp_n_freq ...
    3607              : !> \param Sigma_x_ikp_n ...
    3608              : !> \param V_xc_ikp_n ...
    3609              : !> \param eigenval_scf ...
    3610              : !> \param ikp ...
    3611              : !> \param ispin ...
    3612              : ! **************************************************************************************************
    3613          474 :    SUBROUTINE analyt_conti_and_print(bs_env, Sigma_c_ikp_n_freq, Sigma_x_ikp_n, V_xc_ikp_n, &
    3614          474 :                                      eigenval_scf, ikp, ispin)
    3615              : 
    3616              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3617              :       REAL(KIND=dp), DIMENSION(:, :, :)                  :: Sigma_c_ikp_n_freq
    3618              :       REAL(KIND=dp), DIMENSION(:)                        :: Sigma_x_ikp_n, V_xc_ikp_n, eigenval_scf
    3619              :       INTEGER                                            :: ikp, ispin
    3620              : 
    3621              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'analyt_conti_and_print'
    3622              : 
    3623              :       CHARACTER(len=3)                                   :: occ_vir
    3624              :       CHARACTER(len=default_path_length)                 :: fname
    3625              :       CHARACTER(len=default_string_length)               :: gw_label
    3626              :       INTEGER                                            :: handle, i_mo, ikp_for_print, iunit, &
    3627              :                                                             n_mo, nkp
    3628              :       LOGICAL                                            :: is_bandstruc_kpoint, print_DOS_kpoints, &
    3629              :                                                             print_ikp
    3630          474 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: dummy, eigenval_G, Sigma_c_ikp_n_qp
    3631              : 
    3632          474 :       CALL timeset(routineN, handle)
    3633              : 
    3634          474 :       n_mo = bs_env%n_ao
    3635         2370 :       ALLOCATE (dummy(n_mo), Sigma_c_ikp_n_qp(n_mo), eigenval_G(n_mo))
    3636          474 :       Sigma_c_ikp_n_qp(:) = 0.0_dp
    3637              : 
    3638              :       ! Eigenvalues of the Green's function that produced Σ^c: the DFT ones for G0W0, the
    3639              :       ! previous cycle's quasiparticle energies for evGW0. They set the Newton start value,
    3640              :       ! the Z/m linearization point and the Hedin shift; the QP equation itself stays
    3641              :       ! referenced to the DFT eigenvalues through the Eigenval_scf argument below.
    3642          474 :       IF (bs_env%gw_flavour == evGW0 .AND. ALLOCATED(bs_env%eigenval_evGW0)) THEN
    3643          256 :          eigenval_G(:) = bs_env%eigenval_evGW0(:, ikp, ispin)
    3644              :       ELSE
    3645         5776 :          eigenval_G(:) = eigenval_scf(:)
    3646              :       END IF
    3647              : 
    3648         6032 :       DO i_mo = 1, n_mo
    3649              : 
    3650              :          ! parallelization
    3651         5558 :          IF (MODULO(i_mo, bs_env%para_env%num_pe) /= bs_env%para_env%mepos) CYCLE
    3652              : 
    3653              :          CALL continuation_pade(Sigma_c_ikp_n_qp, &
    3654              :                                 bs_env%imag_freq_points_fit, dummy, dummy, &
    3655              :                                 Sigma_c_ikp_n_freq(:, 1:bs_env%num_freq_points_fit, 1)*z_one + &
    3656              :                                 Sigma_c_ikp_n_freq(:, 1:bs_env%num_freq_points_fit, 2)*gaussi, &
    3657              :                                 Sigma_x_ikp_n(:) - V_xc_ikp_n(:), &
    3658              :                                 eigenval_G(:), eigenval_scf(:), &
    3659              :                                 bs_env%do_hedin_shift, &
    3660              :                                 i_mo, bs_env%n_occ(ispin), bs_env%n_vir(ispin), &
    3661              :                                 bs_env%nparam_pade, bs_env%num_freq_points_fit, &
    3662              :                                 ri_rpa_g0w0_crossing_newton, bs_env%n_occ(ispin), &
    3663       175525 :                                 0.0_dp, .TRUE., .FALSE., 1, e_fermi_ext=bs_env%e_fermi(ispin))
    3664              :       END DO
    3665              : 
    3666          474 :       CALL bs_env%para_env%sum(Sigma_c_ikp_n_qp)
    3667              : 
    3668          474 :       CALL correct_obvious_fitting_fails(Sigma_c_ikp_n_qp, ispin, bs_env)
    3669              : 
    3670              :       bs_env%eigenval_GW(:, ikp, ispin) = eigenval_scf(:) + &
    3671              :                                           Sigma_c_ikp_n_qp(:) + &
    3672              :                                           Sigma_x_ikp_n(:) - &
    3673         6032 :                                           V_xc_ikp_n(:)
    3674              : 
    3675          474 :       IF (ALLOCATED(bs_env%eigenval_G0W0) .AND. bs_env%ri_rs%evgw0_i_iter <= 1) THEN
    3676         5836 :          bs_env%eigenval_G0W0(:, ikp, ispin) = bs_env%eigenval_GW(:, ikp, ispin)
    3677              :       END IF
    3678              : 
    3679         6032 :       bs_env%eigenval_HF(:, ikp, ispin) = eigenval_scf(:) + Sigma_x_ikp_n(:) - V_xc_ikp_n(:)
    3680              : 
    3681              :       ! only print eigenvalues of DOS k-points in case no bandstructure path has been given
    3682          474 :       print_DOS_kpoints = (bs_env%nkp_only_bs <= 0)
    3683              :       ! in kpoints_DOS, the last nkp_only_bs are bandstructure k-points
    3684          474 :       is_bandstruc_kpoint = (ikp > bs_env%nkp_only_DOS)
    3685          474 :       print_ikp = print_DOS_kpoints .OR. is_bandstruc_kpoint
    3686              : 
    3687          474 :       IF (bs_env%para_env%is_source() .AND. print_ikp) THEN
    3688              : 
    3689          221 :          IF (print_DOS_kpoints) THEN
    3690          190 :             nkp = bs_env%nkp_only_DOS
    3691          190 :             ikp_for_print = ikp
    3692              :          ELSE
    3693           31 :             nkp = bs_env%nkp_only_bs
    3694           31 :             ikp_for_print = ikp - bs_env%nkp_only_DOS
    3695              :          END IF
    3696              : 
    3697          221 :          fname = "bandstructure_SCF_and_G0W0"
    3698              : 
    3699              :          ! in an evGW0 run the spectrum of every cycle is appended, so only the very first
    3700              :          ! cycle replaces the file
    3701          221 :          IF (ikp_for_print == 1 .AND. ispin == 1 .AND. bs_env%ri_rs%evgw0_i_iter <= 1) THEN
    3702              :             CALL open_file(TRIM(fname), unit_number=iunit, file_status="REPLACE", &
    3703           62 :                            file_action="WRITE")
    3704              :          ELSE
    3705              :             CALL open_file(TRIM(fname), unit_number=iunit, file_status="OLD", &
    3706          159 :                            file_action="WRITE", file_position="APPEND")
    3707              :          END IF
    3708              : 
    3709          221 :          IF (bs_env%gw_flavour == evGW0 .AND. ikp_for_print == 1 .AND. ispin == 1) THEN
    3710            9 :             WRITE (iunit, "(A)") " "
    3711            9 :             WRITE (iunit, "(A,I0)") "evGW0 cycle: ", bs_env%ri_rs%evgw0_i_iter
    3712              :          END IF
    3713              : 
    3714          221 :          WRITE (iunit, "(A)") " "
    3715          221 :          WRITE (iunit, "(A10,I7,A25,3F10.4,T90,A7,I2)") "kpoint: ", ikp_for_print, "coordinate: ", &
    3716         1105 :             bs_env%kpoints_DOS%xkp(:, ikp), "spin: ", ispin
    3717          221 :          WRITE (iunit, "(A)") " "
    3718          221 :          gw_label = "ϵ_nk^"//TRIM(gw_flavour_label(bs_env))//" (eV)"
    3719          221 :          WRITE (iunit, "(A5,A12,3A17,A16,A18)") "n", "k", "ϵ_nk^DFT (eV)", "Σ^c_nk (eV)", &
    3720          442 :             "Σ^x_nk (eV)", "v_nk^xc (eV)", TRIM(gw_label)
    3721          221 :          WRITE (iunit, "(A)") " "
    3722              : 
    3723         2856 :          DO i_mo = 1, n_mo
    3724         2635 :             IF (i_mo <= bs_env%n_occ(ispin)) occ_vir = 'occ'
    3725         2635 :             IF (i_mo > bs_env%n_occ(ispin)) occ_vir = 'vir'
    3726         2635 :             WRITE (iunit, "(I5,3A,I5,4F16.3,F17.3)") i_mo, ' (', occ_vir, ') ', ikp_for_print, &
    3727         2635 :                eigenval_scf(i_mo)*evolt, &
    3728         2635 :                Sigma_c_ikp_n_qp(i_mo)*evolt, &
    3729         2635 :                Sigma_x_ikp_n(i_mo)*evolt, &
    3730         2635 :                V_xc_ikp_n(i_mo)*evolt, &
    3731         5491 :                bs_env%eigenval_GW(i_mo, ikp, ispin)*evolt
    3732              :          END DO
    3733              : 
    3734          221 :          WRITE (iunit, "(A)") " "
    3735              : 
    3736          221 :          CALL close_file(iunit)
    3737              : 
    3738              :       END IF
    3739              : 
    3740          474 :       CALL timestop(handle)
    3741              : 
    3742          948 :    END SUBROUTINE analyt_conti_and_print
    3743              : 
    3744              : ! **************************************************************************************************
    3745              : !> \brief ...
    3746              : !> \param Sigma_c_ikp_n_qp ...
    3747              : !> \param ispin ...
    3748              : !> \param bs_env ...
    3749              : ! **************************************************************************************************
    3750          474 :    SUBROUTINE correct_obvious_fitting_fails(Sigma_c_ikp_n_qp, ispin, bs_env)
    3751              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: Sigma_c_ikp_n_qp
    3752              :       INTEGER                                            :: ispin
    3753              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    3754              : 
    3755              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'correct_obvious_fitting_fails'
    3756              : 
    3757              :       INTEGER                                            :: handle, homo, i_mo, j_mo, &
    3758              :                                                             n_levels_scissor, n_mo
    3759              :       LOGICAL                                            :: is_occ, is_vir
    3760              :       REAL(KIND=dp)                                      :: sum_Sigma_c
    3761              : 
    3762          474 :       CALL timeset(routineN, handle)
    3763              : 
    3764          474 :       n_mo = bs_env%n_ao
    3765          474 :       homo = bs_env%n_occ(ispin)
    3766              : 
    3767         6032 :       DO i_mo = 1, n_mo
    3768              : 
    3769              :          ! if |𝚺^c| > 13 eV, we use a scissors shift
    3770         6032 :          IF (ABS(Sigma_c_ikp_n_qp(i_mo)) > 13.0_dp/evolt) THEN
    3771              : 
    3772            0 :             is_occ = (i_mo <= homo)
    3773            0 :             is_vir = (i_mo > homo)
    3774              : 
    3775            0 :             n_levels_scissor = 0
    3776            0 :             sum_Sigma_c = 0.0_dp
    3777              : 
    3778              :             ! compute scissor
    3779            0 :             DO j_mo = 1, n_mo
    3780              : 
    3781              :                ! only compute scissor from other GW levels close in energy
    3782            0 :                IF (is_occ .AND. j_mo > homo) CYCLE
    3783            0 :                IF (is_vir .AND. j_mo <= homo) CYCLE
    3784            0 :                IF (ABS(i_mo - j_mo) > 10) CYCLE
    3785            0 :                IF (i_mo == j_mo) CYCLE
    3786              : 
    3787            0 :                n_levels_scissor = n_levels_scissor + 1
    3788            0 :                sum_Sigma_c = sum_Sigma_c + Sigma_c_ikp_n_qp(j_mo)
    3789              : 
    3790              :             END DO
    3791              : 
    3792              :             ! overwrite the self-energy with scissor shift
    3793            0 :             Sigma_c_ikp_n_qp(i_mo) = sum_Sigma_c/REAL(n_levels_scissor, KIND=dp)
    3794              : 
    3795              :          END IF
    3796              : 
    3797              :       END DO ! i_mo
    3798              : 
    3799          474 :       CALL timestop(handle)
    3800              : 
    3801          474 :    END SUBROUTINE correct_obvious_fitting_fails
    3802              : 
    3803              : END MODULE gw_utils
        

Generated by: LCOV version 2.0-1