LCOV - code coverage report
Current view: top level - src - gw_large_cell_gamma_ri_rs.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:21ef868) Lines: 9.5 % 598 57
Test Date: 2026-08-14 07:04:57 Functions: 21.1 % 19 4

            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 GW using RI-RS Approximation for molecules
      10              : !> \par History
      11              : !>      04.2026 created [Ritaj Tyagi]
      12              : ! **************************************************************************************************
      13              : MODULE gw_large_cell_Gamma_ri_rs
      14              :    USE atomic_kind_types,               ONLY: atomic_kind_type
      15              :    USE basis_set_types,                 ONLY: gto_basis_set_type
      16              :    USE cell_types,                      ONLY: cell_type,&
      17              :                                               get_cell,&
      18              :                                               pbc
      19              :    USE constants_operator,              ONLY: operator_coulomb
      20              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_create,&
      21              :                                               cp_blacs_env_release,&
      22              :                                               cp_blacs_env_type
      23              :    USE cp_dbcsr_api,                    ONLY: &
      24              :         dbcsr_add, dbcsr_binary_read, dbcsr_binary_write, dbcsr_copy, dbcsr_create, &
      25              :         dbcsr_deallocate_matrix, dbcsr_distribution_get, dbcsr_distribution_new, &
      26              :         dbcsr_distribution_release, dbcsr_distribution_type, dbcsr_finalize, dbcsr_get_block_p, &
      27              :         dbcsr_get_info, dbcsr_iterator_blocks_left, dbcsr_iterator_next_block, &
      28              :         dbcsr_iterator_start, dbcsr_iterator_stop, dbcsr_iterator_type, dbcsr_multiply, &
      29              :         dbcsr_p_type, dbcsr_put_block, dbcsr_release, dbcsr_scale, dbcsr_set, dbcsr_type, &
      30              :         dbcsr_type_no_symmetry
      31              :    USE cp_dbcsr_contrib,                ONLY: dbcsr_reserve_all_blocks
      32              :    USE cp_dbcsr_operations,             ONLY: copy_dbcsr_to_fm,&
      33              :                                               copy_fm_to_dbcsr,&
      34              :                                               dbcsr_deallocate_matrix_set,&
      35              :                                               max_elements_per_block
      36              :    USE cp_fm_basic_linalg,              ONLY: cp_fm_scale_and_add,&
      37              :                                               cp_fm_uplo_to_full
      38              :    USE cp_fm_cholesky,                  ONLY: cp_fm_cholesky_decompose,&
      39              :                                               cp_fm_cholesky_invert
      40              :    USE cp_fm_diag,                      ONLY: cp_fm_power
      41              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_type
      42              :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      43              :                                               cp_fm_get_info,&
      44              :                                               cp_fm_release,&
      45              :                                               cp_fm_set_all,&
      46              :                                               cp_fm_to_fm,&
      47              :                                               cp_fm_type
      48              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      49              :                                               cp_logger_type
      50              :    USE cp_output_handling,              ONLY: cp_p_file,&
      51              :                                               cp_print_key_should_output
      52              :    USE gw_integrals,                    ONLY: build_3c_integral_block_ctx,&
      53              :                                               gw_3c_ctx_create,&
      54              :                                               gw_3c_ctx_release,&
      55              :                                               gw_3c_ctx_type,&
      56              :                                               gw_3c_ws_create,&
      57              :                                               gw_3c_ws_release,&
      58              :                                               gw_3c_ws_type
      59              :    USE gw_large_cell_gamma,             ONLY: &
      60              :         Fourier_transform_w_to_t, G_occ_vir, compute_QP_energies, compute_fm_chi_Gamma_freq, &
      61              :         create_fm_W_MIC_time, delete_unnecessary_files, fill_fm_Sigma_c_Gamma_time, fm_write, &
      62              :         get_W_MIC, multiply_fm_W_MIC_time_with_Minv_Gamma
      63              :    USE gw_non_periodic_ri_rs,           ONLY: get_basis_offsets,&
      64              :                                               precompute_ri_rs_radii,&
      65              :                                               ri_rs_grid_assembler,&
      66              :                                               solve_D_lp_distributed
      67              :    USE gw_utils,                        ONLY: de_init_bs_env
      68              :    USE input_constants,                 ONLY: rtp_method_bse
      69              :    USE input_section_types,             ONLY: section_vals_type
      70              :    USE kinds,                           ONLY: dp
      71              :    USE kpoint_coulomb_2c,               ONLY: build_2c_coulomb_matrix_kp
      72              :    USE machine,                         ONLY: m_walltime
      73              :    USE message_passing,                 ONLY: mp_para_env_type
      74              :    USE mp2_ri_2c,                       ONLY: RI_2c_integral_mat
      75              :    USE orbital_pointers,                ONLY: indco,&
      76              :                                               ncoset
      77              :    USE parallel_gemm_api,               ONLY: parallel_gemm
      78              :    USE particle_types,                  ONLY: particle_type
      79              :    USE post_scf_bandstructure_types,    ONLY: post_scf_bandstructure_type
      80              :    USE qs_environment_types,            ONLY: get_qs_env,&
      81              :                                               qs_environment_type
      82              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      83              :                                               qs_kind_type
      84              : #include "./base/base_uses.f90"
      85              : 
      86              :    IMPLICIT NONE
      87              : 
      88              :    PRIVATE
      89              : 
      90              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'gw_large_cell_Gamma_ri_rs'
      91              : 
      92              :    PUBLIC :: gw_calc_large_cell_Gamma_ri_rs, &
      93              :              contract_A_B_A, &
      94              :              hadamard_product_inplace, &
      95              :              release_dbcsr_topology_and_matrices, &
      96              :              setup_square_topology
      97              : 
      98              : CONTAINS
      99              : 
     100              : ! **************************************************************************************************
     101              : !> \brief GW calculation using RI-RS formalism for molecules
     102              : !> \param qs_env ...
     103              : !> \param bs_env ...
     104              : ! **************************************************************************************************
     105              : 
     106            0 :    SUBROUTINE gw_calc_large_cell_Gamma_ri_rs(qs_env, bs_env)
     107              : 
     108              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     109              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     110              : 
     111              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'gw_calc_large_cell_Gamma_ri_rs'
     112              : 
     113              :       INTEGER                                            :: handle
     114            0 :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:)        :: fm_Sigma_x_Gamma, fm_W_time
     115            0 :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :, :)  :: fm_Sigma_c_Gamma_time
     116              : 
     117            0 :       CALL timeset(routineN, handle)
     118              : 
     119              :        !!========================================================================
     120              :        !! 0. Precompute AO and RI Radii
     121              :        !!    Per-atom cutoff radii from the most diffuse Gaussian primitives in
     122              :        !!    the AO and RI auxiliary basis sets. Stored in bs_env%ri_rs%
     123              :        !!    radius_ao_per_atom and radius_ri_per_atom, used for sphere-cutoff
     124              :        !!    and phi_local screening.
     125              :        !!========================================================================
     126            0 :       CALL precompute_ri_rs_radii(qs_env, bs_env)
     127              : 
     128              :        !!========================================================================
     129              :        !! 1. Grid Generation for RI-RS
     130              :        !!    (Modified Lebedev grids from Ivan Duchemin and Xavier Blase)
     131              :        !!    Generate flattened 1D array of grid points for RI-RS.
     132              :        !!    Equation: r_g(k) = R_A + r_g(A)
     133              :        !!========================================================================
     134            0 :       CALL ri_rs_grid_assembler(qs_env, bs_env, bs_env%ri_rs%grid_points)
     135              : 
     136              :        !!========================================================================
     137              :        !! 2. Atomic Basis Evaluation
     138              :        !!    Compute values of spherical atomic basis functions at grid points.
     139              :        !!    Expression: Φ_μl = Φ_μ(r_l) (mat_phi_mu_l)
     140              :        !!========================================================================
     141              :       CALL atomic_basis_at_grid_point(qs_env, bs_env, bs_env%ri_rs%grid_points, &
     142            0 :                                       bs_env%ri_rs%mat_phi_mu_l)
     143              : 
     144              :        !!========================================================================
     145              :        !! 3. Compute RI-RS Coefficients (Z_lp)
     146              :        !!    Solve the regularized system for each atom P, where the grid domain
     147              :        !!    is restricted to r_l within a cutoff distance of atom P:
     148              :        !!    a. D_ll' = [ Σ_μ Φ_μ(r_l) Φ_μ(r_l') ]^2 (Equation 13)
     149              :        !!    b. D_lP  = Σ_{μν} Φ_μ(r_l) Φ_ν(r_l) (μν|P) (Equation 15)
     150              :        !!    c. Conditioning:
     151              :        !!       Dvec_l   = 1 / sqrt(D_ll)  (Diagonal scaling vector)
     152              :        !!       D'_ll' = Dvec_l * D_ll' * Dvec_l' + λδ_ll'
     153              :        !!       D'_lP  = Dvec_l * D_lP
     154              :        !!    d. Solve: Σ_l' D'_ll' * Z'_l'P = D'_lP (Equation 14)
     155              :        !!    e. Rescale: Z_lP = Z'_lP * Dvec_l   (Z_lP stored in mat_Z_lP)
     156              :        !!========================================================================
     157              :       CALL compute_coeff_Z_lP(qs_env, bs_env, bs_env%ri_rs%grid_points, &
     158            0 :                               bs_env%ri_rs%mat_phi_mu_l, bs_env%ri_rs%mat_Z_lP)
     159            0 :       bs_env%ri_rs%grid_built = .TRUE.
     160              : 
     161              :        !!========================================================================
     162              :        !! 4. Compute Independent-Particle Polarizability (χ)
     163              :        !!    G^occ_µλ(i|τ|)  = sum_n^occ C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
     164              :        !!    G^vir_µλ(i|τ|)  = sum_n^vir C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
     165              :        !!    G^occ_ll'(i|τ|) = sum_µν Φ_µ(r_l) G^occ_µν Φ_ν(r_l')
     166              :        !!    G^vir_ll'(i|τ|) = sum_µν Φ_µ(r_l) G^vir_µν Φ_ν(r_l')
     167              :        !!    χ_ll'(iτ)       = G^occ_ll'(i|τ|) * G^vir_ll'(i|τ|)
     168              :        !!    χ_PQ(iτ)        = sum_ll' Z_lP χ_ll'(iτ) Z_l'Q
     169              :        !!========================================================================
     170              :       CALL get_mat_chi_Gamma_tau(bs_env, bs_env%mat_chi_Gamma_tau, &
     171            0 :                                  bs_env%ri_rs%mat_phi_mu_l, bs_env%ri_rs%mat_Z_lP)
     172              : 
     173              :        !!========================================================================
     174              :        !! 5. Compute Screened Interaction (W^MIC)
     175              :        !!    χ_PQ(iτ) -> χ_PQ(iω) -> ε_PQ(iω) -> W_PQ(iω) -> W^MIC_PQ(iτ)
     176              :        !!========================================================================
     177            0 :       CALL get_W_MIC(bs_env, qs_env, bs_env%mat_chi_Gamma_tau, fm_W_time)
     178              : 
     179              :        !!========================================================================
     180              :        !! 6. Compute Exact Exchange Self-Energy (Σ^x)
     181              :        !!    D_µν          = sum_n^occ C_µn C_νn
     182              :        !!    D_ll'         = sum_µν Φ_µ(r_l) D_µν Φ_ν(r_l')
     183              :        !!    V^trunc_ll'   = sum_PQ Z_lP V^trunc_PQ Z_l'Q
     184              :        !!    Σ^x_ll'       = D_ll' * V^trunc_ll'
     185              :        !!    Σ^x_λσ(k=0)   = -sum_ll' Φ_λ(r_l) Σ^x_ll' Φ_σ(r_l')
     186              :        !!========================================================================
     187              :       CALL compute_Sigma_x(bs_env, qs_env, bs_env%ri_rs%mat_phi_mu_l, &
     188            0 :                            bs_env%ri_rs%mat_Z_lP, fm_Sigma_x_Gamma)
     189              : 
     190              :        !!========================================================================
     191              :        !! 7. Compute Correlation Self-Energy (Σ^c)
     192              :        !!    W^MIC_ll'(iτ) =  sum_PQ Z_lP W^MIC_PQ(iτ) Z_l'Q
     193              :        !!    Σ^c_ll'(iτ)   = -G^occ_ll'(i|τ|) * W^MIC_ll'(iτ), for τ < 0
     194              :        !!    Σ^c_ll'(iτ)   =  G^vir_ll'(i|τ|) * W^MIC_ll'(iτ), for τ > 0
     195              :        !!    Σ^c_λσ(iτ)    =  sum_ll' Φ_λ(r_l) Σ^c_ll'(iτ) Φ_σ(r_l')
     196              :        !!========================================================================
     197              :       CALL compute_Sigma_c(bs_env, fm_W_time, bs_env%ri_rs%mat_phi_mu_l, &
     198            0 :                            bs_env%ri_rs%mat_Z_lP, fm_Sigma_c_Gamma_time)
     199              : 
     200              :        !!========================================================================
     201              :        !! 8. Compute Quasiparticle Energies
     202              :        !!    Σ^c_λσ(iτ) -> Σ^c_nn(ϵ)
     203              :        !!    ϵ_nk^GW = ϵ_nk^DFT + Σ^c_nn(ϵ) + Σ^x_nn - v^xc_nn
     204              :        !!========================================================================
     205            0 :       CALL compute_QP_energies(bs_env, qs_env, fm_Sigma_x_Gamma, fm_Sigma_c_Gamma_time)
     206              : 
     207            0 :       CALL de_init_bs_env(qs_env, bs_env)
     208              : 
     209            0 :       CALL timestop(handle)
     210              : 
     211            0 :    END SUBROUTINE gw_calc_large_cell_Gamma_ri_rs
     212              : 
     213              : ! **************************************************************************************************
     214              : !> \brief Evaluates atomic basis functions on a real-space grid and builds a sparse DBCSR matrix.
     215              : !> \param qs_env ...
     216              : !> \param bs_env ...
     217              : !> \param ri_rs_grid_points ...
     218              : !> \param mat_phi_mu_l ...
     219              : ! **************************************************************************************************
     220              : 
     221            0 :    SUBROUTINE atomic_basis_at_grid_point(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l)
     222              : 
     223              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     224              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     225              :       REAL(KIND=dp), ALLOCATABLE, INTENT(INOUT)          :: ri_rs_grid_points(:, :)
     226              :       TYPE(dbcsr_type), INTENT(OUT)                      :: mat_phi_mu_l
     227              : 
     228              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'atomic_basis_at_grid_point'
     229              : 
     230              :       INTEGER :: c_size, chunk_size, dimen_ORB, handle, i, i_blk, iatom, natom, npcol, nprow, &
     231              :          num_grid_chunks, r_end, r_start, total_grid_npts
     232              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: first_sgf
     233            0 :       INTEGER, DIMENSION(:), POINTER                     :: c_blk_sizes, col_dist, r_blk_sizes, &
     234            0 :                                                             row_dist
     235            0 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: atom_col_buffer
     236            0 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     237              :       TYPE(cell_type), POINTER                           :: cell
     238              :       TYPE(dbcsr_distribution_type)                      :: dist
     239              :       TYPE(dbcsr_distribution_type), POINTER             :: dbcsr_dist_ks
     240              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     241            0 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     242            0 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     243              : 
     244            0 :       CALL timeset(routineN, handle)
     245              : 
     246              :       ! Setup Grid Blocking
     247            0 :       chunk_size = max_elements_per_block
     248              : 
     249              :       ! Extract environment variables
     250              :       CALL get_qs_env(qs_env, cell=cell, atomic_kind_set=atomic_kind_set, &
     251              :                       qs_kind_set=qs_kind_set, particle_set=particle_set, &
     252            0 :                       para_env=para_env)
     253              : 
     254            0 :       natom = SIZE(particle_set)
     255            0 :       total_grid_npts = SIZE(ri_rs_grid_points, 2)
     256              : 
     257              :       ! Map the starting indices of spherical gaussian functions (SGF) for each atom
     258            0 :       ALLOCATE (first_sgf(natom + 1))
     259            0 :       CALL get_basis_offsets(particle_set, qs_kind_set, first_sgf, dimen_ORB)
     260              : 
     261              :       ! =========================================================================
     262              :       ! 1. SETUP DBCSR MATRIX TOPOLOGY
     263              :       ! =========================================================================
     264              : 
     265              :       ! A. Define Column Block Sizes (1 Block = 1 Atom's full basis set)
     266            0 :       ALLOCATE (c_blk_sizes(natom))
     267            0 :       DO iatom = 1, natom
     268            0 :          c_blk_sizes(iatom) = first_sgf(iatom + 1) - first_sgf(iatom)
     269              :       END DO
     270              : 
     271              :       ! B. Define Row Block Sizes (Grid chunks of max size 256)
     272            0 :       num_grid_chunks = CEILING(REAL(total_grid_npts, KIND=dp)/REAL(chunk_size, KIND=dp))
     273            0 :       ALLOCATE (r_blk_sizes(num_grid_chunks))
     274            0 :       r_blk_sizes = chunk_size
     275            0 :       IF (MOD(total_grid_npts, chunk_size) /= 0) THEN
     276            0 :          r_blk_sizes(num_grid_chunks) = MOD(total_grid_npts, chunk_size)
     277              :       END IF
     278              : 
     279              :       ! C. Fetch CP2K's Default Process Grid Configuration
     280            0 :       CALL get_qs_env(qs_env, dbcsr_dist=dbcsr_dist_ks)
     281            0 :       CALL dbcsr_distribution_get(dbcsr_dist_ks, nprows=nprow, npcols=npcol)
     282              : 
     283              :       ! D. Build Custom Mappings using Round-Robin across the 2D process grid
     284              : 
     285            0 :       ALLOCATE (row_dist(num_grid_chunks))
     286            0 :       DO i = 1, num_grid_chunks
     287            0 :          row_dist(i) = MOD(i - 1, nprow)
     288              :       END DO
     289              : 
     290            0 :       ALLOCATE (col_dist(natom))
     291            0 :       DO i = 1, natom
     292            0 :          col_dist(i) = MOD(i - 1, npcol)
     293              :       END DO
     294              : 
     295              :       ! E. Create the DBCSR Distribution and Initialize the Matrix
     296              :       CALL dbcsr_distribution_new(dist, template=dbcsr_dist_ks, &
     297            0 :                                   row_dist=row_dist, col_dist=col_dist)
     298              : 
     299              :       CALL dbcsr_create(mat_phi_mu_l, name="phi_val_sparse", dist=dist, &
     300              :                         matrix_type=dbcsr_type_no_symmetry, &
     301            0 :                         row_blk_size=r_blk_sizes, col_blk_size=c_blk_sizes)
     302              : 
     303              :       ! =========================================================================
     304              :       ! 2. STREAM DATA DIRECTLY INTO SPARSE MATRIX
     305              :       ! =========================================================================
     306              :       ! Iterate over the atoms assigned to this specific MPI rank
     307            0 :       DO iatom = para_env%mepos + 1, natom, para_env%num_pe
     308              : 
     309            0 :          c_size = c_blk_sizes(iatom)
     310              : 
     311              :          ! Allocate a temporary dense buffer just for this specific atom
     312            0 :          ALLOCATE (atom_col_buffer(total_grid_npts, c_size))
     313            0 :          atom_col_buffer = 0.0_dp
     314              : 
     315              :          ! Evaluate the basis functions on the grid. Skip grid points outside the spatial
     316              :          ! extent of the most diffuse AO Gaussian on iatom; beyond that radius the contribution
     317              :          ! is guaranteed below eps_filter.
     318              :          CALL fill_phi_for_atom(atom_col_buffer, ri_rs_grid_points, total_grid_npts, &
     319              :                                 iatom, particle_set, qs_kind_set, cell, &
     320            0 :                                 r2_threshold=bs_env%ri_rs%radius_ao_per_atom(iatom)**2)
     321              : 
     322              :          ! Slice the dense column into chunks and insert into DBCSR
     323            0 :          DO i_blk = 1, num_grid_chunks
     324            0 :             r_start = (i_blk - 1)*chunk_size + 1
     325            0 :             r_end = MIN(i_blk*chunk_size, total_grid_npts)
     326              : 
     327              :             ! Apply dynamic sparsity filtering: Only store blocks with physical significance
     328            0 :             IF (MAXVAL(ABS(atom_col_buffer(r_start:r_end, 1:c_size))) > bs_env%eps_filter) THEN
     329              :                CALL dbcsr_put_block(mat_phi_mu_l, row=i_blk, col=iatom, &
     330            0 :                                     block=atom_col_buffer(r_start:r_end, 1:c_size))
     331              :             END IF
     332              :          END DO
     333              : 
     334            0 :          DEALLOCATE (atom_col_buffer)
     335              : 
     336              :       END DO
     337              : 
     338              :       ! Finalize triggers internal MPI communication to route blocks to their correct 2D process owners
     339            0 :       CALL dbcsr_finalize(mat_phi_mu_l)
     340              : 
     341            0 :       IF (bs_env%unit_nr > 0) THEN
     342            0 :          WRITE (bs_env%unit_nr, *) "Done with evaluation of phi"
     343              :       END IF
     344              : 
     345              :       ! -------------------------------------------------------------------------
     346              :       ! CLEANUP
     347              :       ! -------------------------------------------------------------------------
     348            0 :       DEALLOCATE (first_sgf, r_blk_sizes, c_blk_sizes, row_dist, col_dist)
     349            0 :       CALL dbcsr_distribution_release(dist)
     350              : 
     351            0 :       CALL timestop(handle)
     352              : 
     353            0 :    END SUBROUTINE atomic_basis_at_grid_point
     354              : 
     355              : ! **************************************************************************************************
     356              : !> \brief Compute value of all basis functions for a single atom across all grid points.
     357              : !>        Sums contributions from periodic images of `iatom` (loop over (ix, iy, iz) cells gated
     358              : !>        by `cell%perd`). Each per-image squared distance is compared against `r2_threshold`
     359              : !>        (per-atom AO Gaussian extent²); images beyond that radius contribute below eps_filter
     360              : !>        and are skipped.
     361              : !> \param phi_val ...
     362              : !> \param ri_rs_grid ...
     363              : !> \param npts ...
     364              : !> \param iatom ...
     365              : !> \param particle_set ...
     366              : !> \param qs_kind_set ...
     367              : !> \param cell ...
     368              : !> \param r2_threshold per-image squared-distance threshold; CYCLE if r² > r2_threshold. Pass
     369              : !>        HUGE(1.0_dp) to disable.
     370              : ! **************************************************************************************************
     371              : 
     372            0 :    SUBROUTINE fill_phi_for_atom(phi_val, ri_rs_grid, npts, iatom, &
     373              :                                 particle_set, qs_kind_set, cell, r2_threshold)
     374              : 
     375              :       REAL(KIND=dp), INTENT(INOUT)                       :: phi_val(:, :)
     376              :       INTEGER, INTENT(IN)                                :: npts
     377              :       REAL(KIND=dp), INTENT(IN)                          :: ri_rs_grid(3, npts)
     378              :       INTEGER, INTENT(IN)                                :: iatom
     379              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     380              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     381              :       TYPE(cell_type), POINTER                           :: cell
     382              :       REAL(KIND=dp), INTENT(IN)                          :: r2_threshold
     383              : 
     384              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'fill_phi_for_atom'
     385              : 
     386              :       INTEGER :: first_sgf, handle, i_pt, ico, iend_co, ikind, ipgf, iset, isgf, ishell, &
     387              :          istart_co, ix, ix_max, ix_min, iy, iy_max, iy_min, iz, iz_max, iz_min, l, last_sgf, lx, &
     388              :          ly, lz, n_cart_total, row_idx
     389              :       REAL(KIND=dp)                                      :: alpha, cell_vector(3), dist_vec(3), &
     390              :                                                             dist_vec_raw(3), exp_val, poly, r2, &
     391              :                                                             r_atom(3), weight
     392              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: hmat
     393              :       TYPE(gto_basis_set_type), POINTER                  :: orb_basis_set
     394              : 
     395            0 :       CALL timeset(routineN, handle)
     396              : 
     397              :       ! Get Atom Info
     398            0 :       ikind = particle_set(iatom)%atomic_kind%kind_number
     399            0 :       CALL get_qs_kind(qs_kind_set(ikind), basis_set=orb_basis_set, basis_type="ORB")
     400            0 :       CALL get_cell(cell=cell, h=hmat)
     401              : 
     402            0 :       IF (.NOT. ASSOCIATED(orb_basis_set)) THEN
     403            0 :          CALL timestop(handle)
     404            0 :          RETURN
     405              :       END IF
     406              : 
     407            0 :       IF (cell%perd(1) == 1) THEN; ix_min = -1; ix_max = 1; ELSE; ix_min = 0; ix_max = 0
     408              :       END IF
     409            0 :       IF (cell%perd(2) == 1) THEN; iy_min = -1; iy_max = 1; ELSE; iy_min = 0; iy_max = 0
     410              :       END IF
     411            0 :       IF (cell%perd(3) == 1) THEN; iz_min = -1; iz_max = 1; ELSE; iz_min = 0; iz_max = 0
     412              :       END IF
     413              : 
     414            0 :       r_atom = particle_set(iatom)%r
     415              : 
     416              :       !$OMP PARALLEL DO DEFAULT(NONE) &
     417              :       !$OMP SHARED(phi_val, ri_rs_grid, npts, orb_basis_set, r_atom, hmat, &
     418              :       !$OMP        ncoset, indco, cell, ix_min, ix_max, &
     419              :       !$OMP        iy_min, iy_max, iz_min, iz_max, r2_threshold) &
     420              :       !$OMP PRIVATE(i_pt, dist_vec_raw, ix, iy, iz, cell_vector, dist_vec, r2, iset, &
     421              :       !$OMP         n_cart_total, ishell, l, istart_co, iend_co, first_sgf, last_sgf, &
     422              :       !$OMP         ipgf, alpha, exp_val, isgf, ico, row_idx, weight, lx, ly, lz, poly) &
     423            0 :       !$OMP SCHEDULE(DYNAMIC)
     424              : 
     425              :       DO i_pt = 1, npts
     426              : 
     427              :          dist_vec_raw = ri_rs_grid(:, i_pt) - r_atom
     428              : 
     429              :          DO ix = ix_min, ix_max
     430              :          DO iy = iy_min, iy_max
     431              :          DO iz = iz_min, iz_max
     432              : 
     433              :             cell_vector(1:3) = MATMUL(hmat, REAL([ix, iy, iz], dp))
     434              : 
     435              :             dist_vec = dist_vec_raw - cell_vector
     436              : 
     437              :             r2 = DOT_PRODUCT(dist_vec, dist_vec)
     438              : 
     439              :             IF (r2 > r2_threshold) CYCLE
     440              : 
     441              :             DO iset = 1, orb_basis_set%nset
     442              :                n_cart_total = ncoset(orb_basis_set%lmax(iset))
     443              : 
     444              :                DO ishell = 1, orb_basis_set%nshell(iset)
     445              :                   l = orb_basis_set%l(ishell, iset)
     446              :                   istart_co = ncoset(l - 1) + 1
     447              :                   iend_co = ncoset(l)
     448              : 
     449              :                   first_sgf = orb_basis_set%first_sgf(ishell, iset)
     450              :                   last_sgf = orb_basis_set%last_sgf(ishell, iset)
     451              : 
     452              :                   DO ipgf = 1, orb_basis_set%npgf(iset)
     453              :                      alpha = orb_basis_set%zet(ipgf, iset)
     454              :                      exp_val = EXP(-alpha*r2)
     455              : 
     456              :                      DO isgf = first_sgf, last_sgf
     457              :                         DO ico = istart_co, iend_co
     458              :                            row_idx = (ipgf - 1)*n_cart_total + ico
     459              :                            weight = orb_basis_set%sphi(row_idx, isgf)
     460              :                            lx = indco(1, ico)
     461              :                            ly = indco(2, ico)
     462              :                            lz = indco(3, ico)
     463              :                            poly = (dist_vec(1)**lx)*(dist_vec(2)**ly)*(dist_vec(3)**lz)
     464              : 
     465              :                            phi_val(i_pt, isgf) = phi_val(i_pt, isgf) + (weight*poly*exp_val)
     466              : 
     467              :                         END DO
     468              :                      END DO
     469              :                   END DO
     470              :                END DO
     471              :             END DO
     472              :          END DO
     473              :          END DO
     474              :          END DO
     475              :       END DO
     476              :       !$OMP END PARALLEL DO
     477              : 
     478            0 :       CALL timestop(handle)
     479              : 
     480              :    END SUBROUTINE fill_phi_for_atom
     481              : 
     482              : ! **************************************************************************************************
     483              : !> \brief Compute RI-RS Coefficients (Z_lP)
     484              : !> \param qs_env ...
     485              : !> \param bs_env ...
     486              : !> \param ri_rs_grid_points ...
     487              : !> \param mat_phi_mu_l ...
     488              : !> \param mat_Z_lP ...
     489              : ! **************************************************************************************************
     490              : 
     491            0 :    SUBROUTINE compute_coeff_Z_lP(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l, mat_Z_lP)
     492              : 
     493              :       ! Arguments
     494              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     495              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     496              :       REAL(KIND=dp), ALLOCATABLE, INTENT(INOUT)          :: ri_rs_grid_points(:, :)
     497              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: mat_phi_mu_l
     498              :       TYPE(dbcsr_type), INTENT(OUT)                      :: mat_Z_lP
     499              : 
     500              :       CHARACTER(LEN=*), PARAMETER :: key = 'PROPERTIES%BANDSTRUCTURE%GW%PRINT%RESTART', &
     501              :          routineN = 'compute_coeff_Z_lP'
     502              : 
     503              :       INTEGER :: atom_j_mepos, atom_j_stride, atom_P, atom_P_start, atom_P_stride, col_end, &
     504              :          col_start, current_chunk_size, g, group_handle, handle, handle_dpotrf, handle_dpotrs, &
     505              :          handle_dsyrk, i, i_blk, ikind, info, j, j_ri, l, loc_idx, loc_ptr, max_ao_size, &
     506              :          max_loc_ri, my_group, n_ao_total, n_grid_total, n_groups, n_loc_ri, n_local_grid, &
     507              :          n_procs_per_atom, natom, nkind, npcol_phi, num_grid_chunks, P_loop_atom, r_end, r_start, &
     508              :          source_atom
     509            0 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: local_grid_idx, row_offset
     510            0 :       INTEGER, DIMENSION(:), POINTER                     :: col_dist_ri, r_blk_sizes, ri_blk_sizes, &
     511            0 :                                                             row_dist_grid
     512              :       REAL(KIND=dp)                                      :: cutoff_ri, cutoff_ri_2, d_sP, dist2_min, &
     513              :                                                             r2_threshold, r_c, t1, t2, t3
     514            0 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: cutoff_ri_per_atom, cutoff_ri_per_kind, &
     515            0 :                                                             d_vec_local
     516            0 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: D_local, d_lp_local, phi_local, &
     517            0 :                                                             sphere_grid, Z_blk
     518              :       REAL(KIND=dp), DIMENSION(3)                        :: dist_vec_raw, pos_P
     519            0 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     520              :       TYPE(cell_type), POINTER                           :: cell
     521              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env_sub
     522              :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct_b, fm_struct_D
     523              :       TYPE(cp_fm_type)                                   :: fm_b, fm_D
     524              :       TYPE(cp_logger_type), POINTER                      :: logger
     525              :       TYPE(dbcsr_distribution_type)                      :: dist_phi, dist_Z
     526            0 :       TYPE(gw_3c_ctx_type)                               :: ctx_3c
     527              :       TYPE(mp_para_env_type), POINTER                    :: para_env, para_env_sub
     528            0 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     529            0 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     530              :       TYPE(section_vals_type), POINTER                   :: input
     531              : 
     532            0 :       CALL timeset(routineN, handle)
     533              : 
     534            0 :       t1 = m_walltime()
     535              : 
     536              :       CALL get_qs_env(qs_env, para_env=para_env, particle_set=particle_set, input=input, &
     537            0 :                       cell=cell, qs_kind_set=qs_kind_set, atomic_kind_set=atomic_kind_set)
     538              : 
     539              :       ! ---------------------------------------------------------------------
     540              :       ! Subgroup setup. Default G=1 keeps the single-rank BLAS path; G>1 splits
     541              :       ! ranks into atom-groups so the Cholesky on D_local distributes across G
     542              :       ! ranks (memory ~1/G) and the compute_d_lp build also splits across the
     543              :       ! subgroup. G=1 leaves para_env_sub / blacs_env_sub NULL — no subgroup
     544              :       ! comms created, atom_P loop uses per-rank round-robin, compute_d_lp runs
     545              :       ! its full atom_j range on each rank, no allreduce.
     546              :       ! ---------------------------------------------------------------------
     547            0 :       n_procs_per_atom = MIN(bs_env%ri_rs%n_procs_per_atom_z_lp, para_env%num_pe)
     548            0 :       IF (n_procs_per_atom < 1) n_procs_per_atom = 1
     549              : 
     550            0 :       NULLIFY (para_env_sub, blacs_env_sub)
     551            0 :       IF (n_procs_per_atom > 1) THEN
     552            0 :          n_groups = para_env%num_pe/n_procs_per_atom
     553            0 :          my_group = MIN(para_env%mepos/n_procs_per_atom, n_groups - 1)
     554            0 :          ALLOCATE (para_env_sub)
     555            0 :          CALL para_env_sub%from_split(para_env, my_group)
     556            0 :          CALL cp_blacs_env_create(blacs_env=blacs_env_sub, para_env=para_env_sub)
     557            0 :          atom_P_start = my_group + 1
     558            0 :          atom_P_stride = n_groups
     559            0 :          atom_j_mepos = para_env_sub%mepos
     560            0 :          atom_j_stride = para_env_sub%num_pe
     561              :       ELSE
     562            0 :          atom_P_start = para_env%mepos + 1
     563            0 :          atom_P_stride = para_env%num_pe
     564            0 :          atom_j_mepos = 0
     565            0 :          atom_j_stride = 1
     566              :       END IF
     567              : 
     568            0 :       natom = SIZE(bs_env%i_RI_start_from_atom)
     569            0 :       n_ao_total = bs_env%i_ao_end_from_atom(natom)
     570            0 :       n_grid_total = SIZE(ri_rs_grid_points, 2)
     571              : 
     572              :       ! =========================================================================
     573              :       ! 1. SETUP DBCSR TOPOLOGY & EXACT OFFSETS
     574              :       ! =========================================================================
     575            0 :       CALL dbcsr_get_info(mat_phi_mu_l, row_blk_size=r_blk_sizes, distribution=dist_phi)
     576              :       CALL dbcsr_distribution_get(dist_phi, row_dist=row_dist_grid, &
     577            0 :                                   group=group_handle, npcols=npcol_phi)
     578              : 
     579            0 :       num_grid_chunks = SIZE(r_blk_sizes)
     580              : 
     581            0 :       ALLOCATE (row_offset(num_grid_chunks))
     582            0 :       row_offset(1) = 0
     583            0 :       DO i_blk = 2, num_grid_chunks
     584            0 :          row_offset(i_blk) = row_offset(i_blk - 1) + r_blk_sizes(i_blk - 1)
     585              :       END DO
     586              : 
     587            0 :       ALLOCATE (ri_blk_sizes(natom), col_dist_ri(natom))
     588            0 :       DO atom_P = 1, natom
     589            0 :          ri_blk_sizes(atom_P) = bs_env%i_RI_end_from_atom(atom_P) - bs_env%i_RI_start_from_atom(atom_P) + 1
     590            0 :          col_dist_ri(atom_P) = MOD(atom_P - 1, npcol_phi)
     591              :       END DO
     592              : 
     593            0 :       CALL dbcsr_distribution_new(dist_Z, template=dist_phi, row_dist=row_dist_grid, col_dist=col_dist_ri)
     594              : 
     595            0 :       IF (bs_env%ri_rs%Z_lP_exists) THEN
     596              :          CALL dbcsr_binary_read(filepath=TRIM(bs_env%prefix)//"Z_lP.matrix", &
     597              :                                 distribution=dist_Z, &
     598            0 :                                 matrix_new=mat_Z_lP)
     599            0 :          IF (bs_env%unit_nr > 0) THEN
     600              :             WRITE (bs_env%unit_nr, '(T2,A,T57,A,F7.1,A)') &
     601            0 :                'Read Z_lP from file ', ' Execution time', m_walltime() - t1, ' s'
     602            0 :             WRITE (bs_env%unit_nr, '(A)') ' '
     603              :          END IF
     604              :       ELSE
     605              : 
     606              :          CALL dbcsr_create(mat_Z_lP, name="mat_Z_lP", dist=dist_Z, &
     607              :                            matrix_type=dbcsr_type_no_symmetry, &
     608            0 :                            row_blk_size=r_blk_sizes, col_blk_size=ri_blk_sizes)
     609              : 
     610            0 :          max_ao_size = 0
     611            0 :          DO j = 1, SIZE(bs_env%i_ao_start_from_atom)
     612            0 :             max_ao_size = MAX(max_ao_size, bs_env%i_ao_end_from_atom(j) - bs_env%i_ao_start_from_atom(j) + 1)
     613              :          END DO
     614            0 :          max_loc_ri = MAXVAL(ri_blk_sizes)
     615              : 
     616              :          ! Per-atom RI-RS integration sphere:
     617              :          !   cutoff_ri(P) = r_c + r_AO(P)
     618              :          ! where r_c is the truncated-Coulomb cutoff of the RI metric. The
     619              :          ! CUTOFF_RADIUS_RI_RS keyword (when > 0) overrides the entire cutoff calculation.
     620            0 :          nkind = SIZE(atomic_kind_set)
     621            0 :          ALLOCATE (cutoff_ri_per_atom(natom))
     622              : 
     623            0 :          IF (bs_env%ri_rs%cutoff_radius_ri_rs > 0.0_dp) THEN
     624            0 :             cutoff_ri_per_atom(:) = bs_env%ri_rs%cutoff_radius_ri_rs
     625              :          ELSE
     626            0 :             r_c = bs_env%ri_metric%cutoff_radius
     627            0 :             DO P_loop_atom = 1, natom
     628              :                cutoff_ri_per_atom(P_loop_atom) = &
     629            0 :                   r_c + bs_env%ri_rs%radius_ao_per_atom(P_loop_atom)
     630              :             END DO
     631              :          END IF
     632              : 
     633            0 :          ALLOCATE (cutoff_ri_per_kind(nkind))
     634            0 :          cutoff_ri_per_kind(:) = 0.0_dp
     635            0 :          IF (bs_env%unit_nr > 0) THEN
     636            0 :             DO P_loop_atom = 1, natom
     637            0 :                ikind = particle_set(P_loop_atom)%atomic_kind%kind_number
     638              :                cutoff_ri_per_kind(ikind) = MAX(cutoff_ri_per_kind(ikind), &
     639            0 :                                                cutoff_ri_per_atom(P_loop_atom))
     640              :             END DO
     641            0 :             WRITE (bs_env%unit_nr, '(T2,A)') 'Per-kind maximum RI-RS sphere cutoff (Bohr):'
     642            0 :             WRITE (bs_env%unit_nr, '(T4,A4,A14)') 'Kind', 'max cutoff_ri'
     643            0 :             DO ikind = 1, nkind
     644              :                WRITE (bs_env%unit_nr, '(T4,A4,F14.4)') &
     645            0 :                   atomic_kind_set(ikind)%element_symbol, &
     646            0 :                   cutoff_ri_per_kind(ikind)
     647              :             END DO
     648            0 :             WRITE (bs_env%unit_nr, '(A)') ' '
     649            0 :             DEALLOCATE (cutoff_ri_per_kind)
     650              :          END IF
     651              : 
     652              :          ! Shared 3c-integral context: hoists libint / t_c_g0 / md_ftable / contracted
     653              :          ! sphi tables out of the per-triple call so compute_d_lp threads only allocate
     654              :          ! a lightweight per-thread workspace. MPI-collective; must be outside any
     655              :          ! OMP region.
     656              :          CALL gw_3c_ctx_create(ctx_3c, qs_env, bs_env%ri_metric, &
     657              :                                basis_j=bs_env%basis_set_AO, basis_k=bs_env%basis_set_AO, &
     658            0 :                                basis_i=bs_env%basis_set_RI)
     659              : 
     660              :          ! =========================================================================
     661              :          ! 2. MPI LOOP OVER ATOMS (Fully independent, no MPI barriers inside)
     662              :          ! phi_local for each atom_P's cutoff sphere is built on the fly via
     663              :          ! fill_phi_for_atom — no dense replicated phi_global, no allreduce.
     664              :          ! =========================================================================
     665            0 :          DO atom_P = atom_P_start, natom, atom_P_stride
     666              : 
     667            0 :             n_loc_ri = ri_blk_sizes(atom_P)
     668            0 :             pos_P(:) = particle_set(atom_P)%r(:)
     669              : 
     670            0 :             cutoff_ri = cutoff_ri_per_atom(atom_P)
     671            0 :             cutoff_ri_2 = cutoff_ri**2
     672              : 
     673              :             ! ---------------------------------------------------------------------
     674              :             ! A. Determine Local Grid Domain based on cutoff_ri (PBC distance)
     675              :             ! ---------------------------------------------------------------------
     676            0 :             n_local_grid = 0
     677            0 :             DO l = 1, n_grid_total
     678            0 :                dist_vec_raw = pbc(ri_rs_grid_points(1:3, l), pos_P(1:3), cell)
     679            0 :                dist2_min = DOT_PRODUCT(dist_vec_raw, dist_vec_raw)
     680            0 :                IF (dist2_min <= cutoff_ri_2) n_local_grid = n_local_grid + 1
     681              :             END DO
     682              : 
     683            0 :             ALLOCATE (local_grid_idx(n_local_grid))
     684              : 
     685            0 :             n_local_grid = 0
     686            0 :             DO l = 1, n_grid_total
     687            0 :                dist_vec_raw = pbc(ri_rs_grid_points(1:3, l), pos_P(1:3), cell)
     688            0 :                dist2_min = DOT_PRODUCT(dist_vec_raw, dist_vec_raw)
     689            0 :                IF (dist2_min <= cutoff_ri_2) THEN
     690            0 :                   n_local_grid = n_local_grid + 1
     691            0 :                   local_grid_idx(n_local_grid) = l
     692              :                END IF
     693              :             END DO
     694              : 
     695              :             ! ---------------------------------------------------------------------
     696              :             ! B. Build phi_local on the fly via fill_phi_for_atom.
     697              :             !    Only source atoms whose AO basis can reach the cutoff sphere of
     698              :             !    atom_P (MIC distance) contribute; the rest are pruned. The
     699              :             !    periodic fill_phi_for_atom sums over (ix, iy, iz) images of
     700              :             !    source_atom internally.
     701              :             ! ---------------------------------------------------------------------
     702            0 :             ALLOCATE (sphere_grid(3, n_local_grid))
     703            0 :             DO loc_idx = 1, n_local_grid
     704            0 :                sphere_grid(:, loc_idx) = ri_rs_grid_points(:, local_grid_idx(loc_idx))
     705              :             END DO
     706              : 
     707            0 :             ALLOCATE (phi_local(n_local_grid, n_ao_total))
     708            0 :             phi_local = 0.0_dp
     709              : 
     710            0 :             DO source_atom = 1, natom
     711            0 :                dist_vec_raw = pbc(particle_set(source_atom)%r(:), pos_P(:), cell)
     712            0 :                d_sP = NORM2(dist_vec_raw)
     713            0 :                IF (d_sP > bs_env%ri_rs%radius_ao_per_atom(source_atom) + cutoff_ri) CYCLE
     714              : 
     715            0 :                col_start = bs_env%i_ao_start_from_atom(source_atom)
     716            0 :                col_end = bs_env%i_ao_end_from_atom(source_atom)
     717            0 :                r2_threshold = bs_env%ri_rs%radius_ao_per_atom(source_atom)**2
     718              : 
     719              :                CALL fill_phi_for_atom(phi_local(:, col_start:col_end), sphere_grid, &
     720              :                                       n_local_grid, source_atom, particle_set, qs_kind_set, &
     721            0 :                                       cell, r2_threshold)
     722              :             END DO
     723              : 
     724            0 :             DEALLOCATE (sphere_grid)
     725              : 
     726              :             ! ---------------------------------------------------------------------
     727              :             ! C. Build Local RHS Matrix (d_lp_local) first so the subgroup-
     728              :             !    distributed compute_d_lp + allreduce is not entangled with the LHS
     729              :             !    build. compute_d_lp does not depend on D_local or d_vec_local.
     730              :             ! ---------------------------------------------------------------------
     731            0 :             ALLOCATE (d_lp_local(n_local_grid, n_loc_ri))
     732            0 :             d_lp_local = 0.0_dp
     733              : 
     734            0 :             t2 = m_walltime()
     735              : 
     736              :             CALL compute_d_lp(bs_env, ctx_3c, cell, phi_local, d_lp_local, n_local_grid, &
     737            0 :                               n_loc_ri, atom_P, max_ao_size, atom_j_mepos, atom_j_stride)
     738              : 
     739              :             ! Reduce per-subgroup-rank partials into the replicated d_lp_local.
     740              :             ! Skipped for G=1 (BLAS path): each rank has the full sum locally.
     741            0 :             IF (n_procs_per_atom > 1) THEN
     742            0 :                CALL para_env_sub%sum(d_lp_local)
     743              :             END IF
     744              : 
     745            0 :             t3 = m_walltime()
     746              : 
     747              :             ! ---------------------------------------------------------------------
     748              :             ! D. Build d_vec_local (Jacobi diagonal) + LHS — BLAS or ScaLAPACK
     749              :             ! ---------------------------------------------------------------------
     750            0 :             ALLOCATE (d_vec_local(n_local_grid))
     751              : 
     752            0 :             IF (n_procs_per_atom == 1) THEN
     753              :                ! BLAS path: build D_local densely, compute d_vec as side-effect
     754              :                ! of the Jacobi step (preserves bit-identical arithmetic with the
     755              :                ! previous branch).
     756            0 :                ALLOCATE (D_local(n_local_grid, n_local_grid))
     757            0 :                D_local = 0.0_dp
     758              : 
     759            0 :                CALL timeset(routineN//"_dsyrk", handle_dsyrk)
     760              :                CALL dsyrk("L", "N", n_local_grid, n_ao_total, 1.0_dp, phi_local, &
     761            0 :                           n_local_grid, 0.0_dp, D_local, n_local_grid)
     762            0 :                CALL timestop(handle_dsyrk)
     763              : 
     764              :                !$OMP PARALLEL DO DEFAULT(NONE) &
     765              :                !$OMP SHARED(n_local_grid, D_local, d_vec_local, bs_env) &
     766              :                !$OMP PRIVATE(i) &
     767            0 :                !$OMP SCHEDULE(STATIC)
     768              :                DO i = 1, n_local_grid
     769              :                   D_local(i, i) = D_local(i, i)**2
     770              :                   d_vec_local(i) = 1.0_dp/SQRT(MAX(D_local(i, i), 1.0E-16_dp))
     771              :                   D_local(i, i) = (D_local(i, i)*d_vec_local(i)**2) + bs_env%ri_rs%tikhonov
     772              :                END DO
     773              :                !$OMP END PARALLEL DO
     774              : 
     775              :                !$OMP PARALLEL DO DEFAULT(NONE) &
     776              :                !$OMP SHARED(n_local_grid, D_local, d_vec_local) &
     777              :                !$OMP PRIVATE(j, i) &
     778            0 :                !$OMP SCHEDULE(DYNAMIC)
     779              :                DO j = 1, n_local_grid
     780              :                   DO i = j + 1, n_local_grid
     781              :                      D_local(i, j) = D_local(i, j)**2
     782              :                      D_local(i, j) = D_local(i, j)*d_vec_local(i)*d_vec_local(j)
     783              :                      D_local(j, i) = D_local(i, j)
     784              :                   END DO
     785              :                END DO
     786              :                !$OMP END PARALLEL DO
     787              :             ELSE
     788              :                ! ScaLAPACK path: d_vec computed directly from phi (= 1/||phi_i||^2);
     789              :                ! solve_D_lp_distributed builds D block-cyclic internally with
     790              :                ! the squared+scaled values, so no dense D_local on this rank.
     791              :                !$OMP PARALLEL DO DEFAULT(NONE) &
     792              :                !$OMP SHARED(n_local_grid, n_ao_total, phi_local, d_vec_local) &
     793              :                !$OMP PRIVATE(i, j) &
     794            0 :                !$OMP SCHEDULE(STATIC)
     795              :                DO i = 1, n_local_grid
     796              :                   d_vec_local(i) = 0.0_dp
     797              :                   DO j = 1, n_ao_total
     798              :                      d_vec_local(i) = d_vec_local(i) + phi_local(i, j)*phi_local(i, j)
     799              :                   END DO
     800              :                   d_vec_local(i) = 1.0_dp/MAX(d_vec_local(i), 1.0E-16_dp)
     801              :                END DO
     802              :                !$OMP END PARALLEL DO
     803              :             END IF
     804              : 
     805              :             ! ---------------------------------------------------------------------
     806              :             ! E. Pre-scale d_lp by d_vec
     807              :             ! ---------------------------------------------------------------------
     808              :             !$OMP PARALLEL DO DEFAULT(NONE) &
     809              :             !$OMP SHARED(n_loc_ri, n_local_grid, d_lp_local, d_vec_local) &
     810              :             !$OMP PRIVATE(j_ri, i) &
     811            0 :             !$OMP SCHEDULE(STATIC)
     812              :             DO j_ri = 1, n_loc_ri
     813              :                DO i = 1, n_local_grid
     814              :                   d_lp_local(i, j_ri) = d_lp_local(i, j_ri)*d_vec_local(i)
     815              :                END DO
     816              :             END DO
     817              :             !$OMP END PARALLEL DO
     818              : 
     819              :             ! ---------------------------------------------------------------------
     820              :             ! F. Solve — BLAS dpotrf/dpotrs or ScaLAPACK pdpotrf/pdpotrs
     821              :             ! ---------------------------------------------------------------------
     822            0 :             IF (n_procs_per_atom == 1) THEN
     823            0 :                CALL timeset(routineN//"_dpotrf", handle_dpotrf)
     824            0 :                CALL dpotrf('L', n_local_grid, D_local, n_local_grid, info)
     825            0 :                CALL timestop(handle_dpotrf)
     826            0 :                CALL timeset(routineN//"_dpotrs", handle_dpotrs)
     827              :                CALL dpotrs('L', n_local_grid, n_loc_ri, D_local, n_local_grid, &
     828            0 :                            d_lp_local, n_local_grid, info)
     829            0 :                CALL timestop(handle_dpotrs)
     830            0 :                DEALLOCATE (D_local)
     831              :             ELSE
     832              :                CALL solve_D_lp_distributed(phi_local, d_vec_local, d_lp_local, &
     833              :                                            n_local_grid, n_ao_total, n_loc_ri, &
     834              :                                            bs_env%ri_rs%tikhonov, &
     835              :                                            para_env_sub, blacs_env_sub, &
     836            0 :                                            fm_struct_D, fm_struct_b, fm_D, fm_b, info)
     837              :             END IF
     838              : 
     839              :             ! ---------------------------------------------------------------------
     840              :             ! G. Post-scale solution by d_vec (common to both paths)
     841              :             ! ---------------------------------------------------------------------
     842              :             !$OMP PARALLEL DO DEFAULT(NONE) &
     843              :             !$OMP SHARED(n_loc_ri, n_local_grid, d_lp_local, d_vec_local) &
     844              :             !$OMP PRIVATE(j_ri, i) &
     845            0 :             !$OMP SCHEDULE(STATIC)
     846              :             DO j_ri = 1, n_loc_ri
     847              :                DO i = 1, n_local_grid
     848              :                   d_lp_local(i, j_ri) = d_lp_local(i, j_ri)*d_vec_local(i)
     849              :                END DO
     850              :             END DO
     851              :             !$OMP END PARALLEL DO
     852              : 
     853              :             ! ---------------------------------------------------------------------
     854              :             ! H. Scatter Local Solution Back to Global DBCSR Matrix.
     855              :             !    Under ScaLAPACK (G>1) the d_lp_local solution is identical on all
     856              :             !    G subgroup ranks (gathered via cp_fm_get_submatrix); only the
     857              :             !    subgroup root writes to mat_Z_lP so each atom column is emitted
     858              :             !    exactly once. DBCSR routes blocks to their global owner on finalize.
     859              :             !    local_grid_idx is ascending (built by the ordered scan above), so
     860              :             !    a single walking pointer over chunks works.
     861              :             ! ---------------------------------------------------------------------
     862            0 :             IF (n_procs_per_atom == 1 .OR. para_env_sub%mepos == 0) THEN
     863            0 :                ALLOCATE (Z_blk(MAXVAL(r_blk_sizes), n_loc_ri))
     864            0 :                loc_ptr = 1
     865              : 
     866            0 :                DO i_blk = 1, num_grid_chunks
     867            0 :                   r_start = row_offset(i_blk) + 1
     868            0 :                   r_end = row_offset(i_blk) + r_blk_sizes(i_blk)
     869            0 :                   current_chunk_size = r_blk_sizes(i_blk)
     870              : 
     871            0 :                   Z_blk = 0.0_dp
     872              : 
     873            0 :                   DO WHILE (loc_ptr <= n_local_grid)
     874            0 :                      g = local_grid_idx(loc_ptr)
     875            0 :                      IF (g > r_end) EXIT
     876            0 :                      Z_blk(g - r_start + 1, 1:n_loc_ri) = d_lp_local(loc_ptr, 1:n_loc_ri)
     877            0 :                      loc_ptr = loc_ptr + 1
     878              :                   END DO
     879              : 
     880            0 :                   IF (MAXVAL(ABS(Z_blk(1:current_chunk_size, 1:n_loc_ri))) > bs_env%eps_filter) THEN
     881              :                      CALL dbcsr_put_block(mat_Z_lP, row=i_blk, col=atom_P, &
     882            0 :                                           block=Z_blk(1:current_chunk_size, 1:n_loc_ri))
     883              :                   END IF
     884              :                END DO
     885              : 
     886            0 :                DEALLOCATE (Z_blk)
     887              :             END IF
     888              : 
     889            0 :             DEALLOCATE (d_vec_local, d_lp_local)
     890            0 :             DEALLOCATE (local_grid_idx, phi_local)
     891              : 
     892              :          END DO
     893              : 
     894            0 :          DEALLOCATE (cutoff_ri_per_atom)
     895            0 :          CALL gw_3c_ctx_release(ctx_3c)
     896              : 
     897            0 :          CALL dbcsr_finalize(mat_Z_lP)
     898              : 
     899            0 :          IF (bs_env%unit_nr > 0) THEN
     900              :             WRITE (bs_env%unit_nr, '(T2,A,T57,A,F7.1,A)') &
     901            0 :                'Computed Z_lP ', ' Execution time', m_walltime() - t1, ' s'
     902            0 :             WRITE (bs_env%unit_nr, '(A)') ' '
     903              :          END IF
     904              : 
     905            0 :          logger => cp_get_default_logger()
     906              : 
     907            0 :          IF (BTEST(cp_print_key_should_output(logger%iter_info, input, key), cp_p_file)) THEN
     908            0 :             CALL dbcsr_binary_write(matrix=mat_Z_lP, filepath=TRIM(bs_env%prefix)//"Z_lP.matrix")
     909              :          END IF
     910              : 
     911              :       END IF
     912              : 
     913            0 :       DEALLOCATE (row_offset, ri_blk_sizes, col_dist_ri)
     914            0 :       CALL dbcsr_distribution_release(dist_Z)
     915              : 
     916            0 :       IF (n_procs_per_atom > 1) THEN
     917            0 :          CALL cp_blacs_env_release(blacs_env_sub)
     918            0 :          CALL para_env_sub%free()
     919            0 :          DEALLOCATE (para_env_sub)
     920              :       END IF
     921              : 
     922            0 :       DEALLOCATE (ri_rs_grid_points)
     923              : 
     924            0 :       CALL timestop(handle)
     925              : 
     926            0 :    END SUBROUTINE compute_coeff_Z_lP
     927              : 
     928              : ! **************************************************************************************************
     929              : !> \brief Computes the dense localized RHS d_lp(l,P) = Σ_{μν,R,S} Φ_μ(r_l)·Φ_ν(r_l)·(μν|P) for one
     930              : !>        RI atom P. OMP-threaded over (atom_j, atom_k) AO-pair blocks: per thread, sweep all
     931              : !>        (cell_R, cell_S) periodic images of (atom_j, atom_k) about atom_P at cell (0,0,0); each
     932              : !>        3c block is built by build_3c_integral_block_ctx (cached libint / sphi tables in ctx,
     933              : !>        kind-radius triangle screen → `screened` short-circuits negligible image triples), and
     934              : !>        grid-chunked pair densities are contracted into a private d_lp partial that is reduced
     935              : !>        into d_lp at the end of the parallel region.
     936              : !> \param bs_env ...
     937              : !> \param ctx shared 3c-integral context (gw_3c_ctx_create)
     938              : !> \param cell ...
     939              : !> \param phi_val Φ_μ(r_l) on the local-sphere grid (n_grid_total × n_ao)
     940              : !> \param d_lp output (n_grid_total × n_loc_ri), zeroed by the caller, accumulated here
     941              : !> \param n_grid_total number of local-sphere grid rows
     942              : !> \param n_loc_ri number of RI functions of atom_P
     943              : !> \param atom_P RI atom (pinned to cell (0,0,0))
     944              : !> \param max_ao_size ...
     945              : !> \param atom_j_mepos ...
     946              : !> \param atom_j_stride ...
     947              : ! **************************************************************************************************
     948              : 
     949            0 :    SUBROUTINE compute_d_lp(bs_env, ctx, cell, phi_val, d_lp, n_grid_total, n_loc_ri, atom_P, &
     950              :                            max_ao_size, atom_j_mepos, atom_j_stride)
     951              : 
     952              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     953              :       TYPE(gw_3c_ctx_type), INTENT(IN)                   :: ctx
     954              :       TYPE(cell_type), POINTER                           :: cell
     955              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: phi_val
     956              :       INTEGER, INTENT(IN)                                :: n_grid_total, n_loc_ri
     957              :       REAL(KIND=dp), INTENT(INOUT)                       :: d_lp(n_grid_total, n_loc_ri)
     958              :       INTEGER, INTENT(IN)                                :: atom_P, max_ao_size, atom_j_mepos, &
     959              :                                                             atom_j_stride
     960              : 
     961              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'compute_d_lp'
     962              :       INTEGER, PARAMETER                                 :: grid_chunk = 1024
     963              : 
     964              :       INTEGER :: atom_j, atom_k, c, handle, handle_dgemm, ix_max, ix_min, ix_R, ix_S, iy_max, &
     965              :          iy_min, iy_R, iy_S, iz_max, iz_min, iz_R, iz_S, j, jk_idx, jsize, jstart, k, ksize, &
     966              :          kstart, l, l0, natom, ri
     967              :       INTEGER, DIMENSION(3)                              :: cell_R_vec, cell_S_vec
     968              :       LOGICAL                                            :: any_kept, screened
     969            0 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: d_lp_prv, int_2d_prv, rho_chunk
     970            0 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :)     :: int_3c_prv, int_3c_sum
     971            0 :       TYPE(gw_3c_ws_type)                                :: ws
     972              : 
     973            0 :       CALL timeset(routineN, handle)
     974              : 
     975            0 :       natom = SIZE(bs_env%i_ao_start_from_atom)
     976              : 
     977            0 :       IF (cell%perd(1) == 1) THEN; ix_min = -1; ix_max = 1; ELSE; ix_min = 0; ix_max = 0
     978              :       END IF
     979            0 :       IF (cell%perd(2) == 1) THEN; iy_min = -1; iy_max = 1; ELSE; iy_min = 0; iy_max = 0
     980              :       END IF
     981            0 :       IF (cell%perd(3) == 1) THEN; iz_min = -1; iz_max = 1; ELSE; iz_min = 0; iz_max = 0
     982              :       END IF
     983              : 
     984              :       !$OMP PARALLEL DEFAULT(NONE) &
     985              :       !$OMP SHARED(bs_env, ctx, phi_val, d_lp, n_grid_total, n_loc_ri, atom_P, max_ao_size, &
     986              :       !$OMP        natom, ix_min, ix_max, iy_min, iy_max, iz_min, iz_max, &
     987              :       !$OMP        atom_j_mepos, atom_j_stride) &
     988              :       !$OMP PRIVATE(any_kept, atom_j, atom_k, c, handle_dgemm, j, jk_idx, jsize, jstart, k, &
     989              :       !$OMP         ksize, kstart, l, l0, ri, ix_R, iy_R, iz_R, ix_S, iy_S, iz_S, cell_R_vec, &
     990            0 :       !$OMP         cell_S_vec, screened, d_lp_prv, int_2d_prv, rho_chunk, int_3c_prv, int_3c_sum, ws)
     991              : 
     992              :       CALL gw_3c_ws_create(ws, ctx)
     993              :       ALLOCATE (int_3c_prv(max_ao_size, max_ao_size, n_loc_ri))
     994              :       ALLOCATE (int_3c_sum(max_ao_size, max_ao_size, n_loc_ri))
     995              :       ALLOCATE (int_2d_prv(max_ao_size*max_ao_size, n_loc_ri))
     996              :       ALLOCATE (rho_chunk(grid_chunk, max_ao_size*max_ao_size))
     997              :       ALLOCATE (d_lp_prv(n_grid_total, n_loc_ri))
     998              :       d_lp_prv(:, :) = 0.0_dp
     999              : 
    1000              :       ! atom_P pinned at cell (0,0,0); enumerate (atom_j, cell_R) × (atom_k, cell_S). The ctx
    1001              :       ! integral builder's kind_radius triangle screen sets screened=.TRUE. for the bulk of
    1002              :       ! image triples (one or both AO atoms beyond the truncated-Coulomb reach of atom_P),
    1003              :       ! so the 27 × 27 = 729 candidate cells collapse to "adjacent cells" in practice.
    1004              :       ! MPI-stride atom_j over the subgroup (atom_j_stride = 1 for the BLAS path, > 1 for the
    1005              :       ! ScaLAPACK path). COLLAPSE(2) dropped because the outer stride is non-unit under
    1006              :       ! ScaLAPACK; the inner atom_k loop carries enough work for DYNAMIC.
    1007              :       !$OMP DO SCHEDULE(DYNAMIC)
    1008              :       DO atom_j = atom_j_mepos + 1, natom, atom_j_stride
    1009              :          DO atom_k = 1, natom
    1010              :             jstart = bs_env%i_ao_start_from_atom(atom_j)
    1011              :             jsize = bs_env%i_ao_end_from_atom(atom_j) - jstart + 1
    1012              :             kstart = bs_env%i_ao_start_from_atom(atom_k)
    1013              :             ksize = bs_env%i_ao_end_from_atom(atom_k) - kstart + 1
    1014              : 
    1015              :             int_3c_sum(1:jsize, 1:ksize, 1:n_loc_ri) = 0.0_dp
    1016              :             any_kept = .FALSE.
    1017              : 
    1018              :             DO ix_R = ix_min, ix_max
    1019              :             DO iy_R = iy_min, iy_max
    1020              :             DO iz_R = iz_min, iz_max
    1021              :                cell_R_vec = [ix_R, iy_R, iz_R]
    1022              :                DO ix_S = ix_min, ix_max
    1023              :                DO iy_S = iy_min, iy_max
    1024              :                DO iz_S = iz_min, iz_max
    1025              :                   cell_S_vec = [ix_S, iy_S, iz_S]
    1026              : 
    1027              :                   int_3c_prv(1:jsize, 1:ksize, 1:n_loc_ri) = 0.0_dp
    1028              : 
    1029              :                   CALL build_3c_integral_block_ctx( &
    1030              :                      int_3c_prv(1:jsize, 1:ksize, 1:n_loc_ri), ctx, ws, &
    1031              :                      atom_j=atom_j, atom_k=atom_k, atom_i=atom_P, &
    1032              :                      cell_j=cell_R_vec, cell_k=cell_S_vec, cell_i=[0, 0, 0], &
    1033              :                      screened=screened)
    1034              :                   IF (screened) CYCLE
    1035              : 
    1036              :                   any_kept = .TRUE.
    1037              :                   int_3c_sum(1:jsize, 1:ksize, 1:n_loc_ri) = &
    1038              :                      int_3c_sum(1:jsize, 1:ksize, 1:n_loc_ri) + &
    1039              :                      int_3c_prv(1:jsize, 1:ksize, 1:n_loc_ri)
    1040              :                END DO
    1041              :                END DO
    1042              :                END DO
    1043              :             END DO
    1044              :             END DO
    1045              :             END DO
    1046              : 
    1047              :             IF (.NOT. any_kept) CYCLE
    1048              : 
    1049              :             ! Flatten 3D B_{μν,P} → 2D B_{(μν),P}
    1050              :             DO ri = 1, n_loc_ri
    1051              :                DO k = 1, ksize
    1052              :                   DO j = 1, jsize
    1053              :                      jk_idx = (k - 1)*jsize + j
    1054              :                      int_2d_prv(jk_idx, ri) = int_3c_sum(j, k, ri)
    1055              :                   END DO
    1056              :                END DO
    1057              :             END DO
    1058              : 
    1059              :             ! Pair density ρ(l,μν) = Φ_μ(r_l)Φ_ν(r_l) in grid chunks, contracted on the fly:
    1060              :             ! d_{l,P} += ρ(l,μν) B_{(μν),P}  (dgemm runs serially inside the parallel region)
    1061              :             DO l0 = 1, n_grid_total, grid_chunk
    1062              :                c = MIN(grid_chunk, n_grid_total - l0 + 1)
    1063              :                DO k = 1, ksize
    1064              :                   DO j = 1, jsize
    1065              :                      jk_idx = (k - 1)*jsize + j
    1066              :                      DO l = 1, c
    1067              :                         rho_chunk(l, jk_idx) = phi_val(l0 + l - 1, jstart + j - 1)* &
    1068              :                                                phi_val(l0 + l - 1, kstart + k - 1)
    1069              :                      END DO
    1070              :                   END DO
    1071              :                END DO
    1072              :                CALL timeset(routineN//"_dgemm", handle_dgemm)
    1073              :                CALL dgemm("N", "N", c, n_loc_ri, jsize*ksize, &
    1074              :                           1.0_dp, rho_chunk, grid_chunk, &
    1075              :                           int_2d_prv, max_ao_size*max_ao_size, &
    1076              :                           1.0_dp, d_lp_prv(l0, 1), n_grid_total)
    1077              :                CALL timestop(handle_dgemm)
    1078              :             END DO
    1079              :          END DO
    1080              :       END DO
    1081              :       !$OMP END DO
    1082              : 
    1083              :       !$OMP CRITICAL (compute_d_lp_reduce)
    1084              :       d_lp(1:n_grid_total, 1:n_loc_ri) = d_lp(1:n_grid_total, 1:n_loc_ri) + &
    1085              :                                          d_lp_prv(1:n_grid_total, 1:n_loc_ri)
    1086              :       !$OMP END CRITICAL (compute_d_lp_reduce)
    1087              : 
    1088              :       DEALLOCATE (int_3c_prv, int_3c_sum, int_2d_prv, rho_chunk, d_lp_prv)
    1089              :       CALL gw_3c_ws_release(ws)
    1090              : 
    1091              :       !$OMP END PARALLEL
    1092              : 
    1093            0 :       CALL timestop(handle)
    1094              : 
    1095            0 :    END SUBROUTINE compute_d_lp
    1096              : 
    1097              : ! **************************************************************************************************
    1098              : !> \brief Computes the χ(iτ, k=0) matrix
    1099              : !> \param bs_env ...
    1100              : !> \param mat_chi_Gamma_tau ...
    1101              : !> \param mat_phi_mu_l ...
    1102              : !> \param mat_Z_lP ...
    1103              : ! **************************************************************************************************
    1104              : 
    1105            0 :    SUBROUTINE get_mat_chi_Gamma_tau(bs_env, mat_chi_Gamma_tau, mat_phi_mu_l, mat_Z_lP)
    1106              : 
    1107              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1108              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: mat_chi_Gamma_tau
    1109              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: mat_phi_mu_l, mat_Z_lP
    1110              : 
    1111              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'get_mat_chi_Gamma_tau'
    1112              : 
    1113              :       INTEGER                                            :: handle, i, i_t, ispin, npcol
    1114            0 :       INTEGER, DIMENSION(:), POINTER                     :: blk_ao, blk_grid, dist_col_grid, &
    1115            0 :                                                             dist_row_grid
    1116              :       REAL(KIND=dp)                                      :: t1, tau
    1117              :       TYPE(dbcsr_distribution_type)                      :: dist_grid_grid, dist_phi
    1118              :       TYPE(dbcsr_type)                                   :: matrix_chi_grid, matrix_chi_grid_spin, &
    1119              :                                                             matrix_G_occ_grid, matrix_G_vir_grid
    1120              : 
    1121            0 :       CALL timeset(routineN, handle)
    1122              : 
    1123              :       ! =========================================================================
    1124              :       ! 1. SETUP CORE TOPOLOGIES
    1125              :       ! =========================================================================
    1126            0 :       CALL dbcsr_get_info(mat_phi_mu_l, distribution=dist_phi, row_blk_size=blk_grid, col_blk_size=blk_ao)
    1127            0 :       CALL dbcsr_distribution_get(dist_phi, row_dist=dist_row_grid, npcols=npcol)
    1128              : 
    1129              :       ! Build a perfectly safe column distribution for the Grid dimension
    1130            0 :       ALLOCATE (dist_col_grid(SIZE(blk_grid)))
    1131            0 :       DO i = 1, SIZE(blk_grid)
    1132            0 :          dist_col_grid(i) = MOD(i - 1, npcol)
    1133              :       END DO
    1134              : 
    1135              :       CALL dbcsr_distribution_new(dist_grid_grid, template=dist_phi, &
    1136            0 :                                   row_dist=dist_row_grid, col_dist=dist_col_grid)
    1137              : 
    1138            0 :       CALL dbcsr_create(matrix_G_occ_grid, "G_occ_grid", dist_grid_grid, dbcsr_type_no_symmetry, blk_grid, blk_grid)
    1139            0 :       CALL dbcsr_create(matrix_G_vir_grid, "G_vir_grid", dist_grid_grid, dbcsr_type_no_symmetry, blk_grid, blk_grid)
    1140            0 :       CALL dbcsr_create(matrix_chi_grid, "chi_grid", dist_grid_grid, dbcsr_type_no_symmetry, blk_grid, blk_grid)
    1141            0 :       CALL dbcsr_create(matrix_chi_grid_spin, "chi_grid_spin", dist_grid_grid, dbcsr_type_no_symmetry, blk_grid, blk_grid)
    1142              : 
    1143              :       ! =========================================================================
    1144              :       ! 2. MAIN IMAGINARY TIME LOOP
    1145              :       ! =========================================================================
    1146            0 :       DO i_t = 1, bs_env%num_time_freq_points
    1147            0 :          t1 = m_walltime()
    1148              : 
    1149            0 :          tau = bs_env%imag_time_points(i_t)
    1150            0 :          CALL dbcsr_set(matrix_chi_grid, 0.0_dp)
    1151              : 
    1152              :          ! ----------------------------------------------------------------------
    1153              :          ! A. SPIN LOOP (Allocations safely encapsulated in wrappers)
    1154              :          ! ----------------------------------------------------------------------
    1155            0 :          DO ispin = 1, bs_env%n_spin
    1156              : 
    1157              :             ! G^occ_µλ(i|τ|,k=0) = sum_n^occ C_µn(k=0) e^(-|(ϵ_nk=0-ϵ_F)τ|) C_λn(k=0)
    1158              :             ! G^occ_ll'(i|τ|,k=0) = sum_µν Φ_µ(r_l) G^occ_µν Φ_ν(r_l')
    1159              :             CALL build_G_grid(bs_env, tau, ispin, .TRUE., .FALSE., mat_phi_mu_l, &
    1160            0 :                               matrix_G_occ_grid, bs_env%eps_filter)
    1161              : 
    1162              :             ! G^vir_µλ(i|τ|,k=0) = sum_n^vir C_µn(k=0) e^(-|(ϵ_nk=0-ϵ_F)τ|) C_λn(k=0)
    1163              :             ! G^vir_ll'(i|τ|,k=0) = sum_µν Φ_µ(r_l) G^vir_µν Φ_ν(r_l')
    1164              :             CALL build_G_grid(bs_env, tau, ispin, .FALSE., .TRUE., mat_phi_mu_l, &
    1165            0 :                               matrix_G_vir_grid, bs_env%eps_filter)
    1166              : 
    1167              :             ! -------------------------------------------------------------------
    1168              :             ! B. ELEMENT-WISE HADAMARD PRODUCT
    1169              :             ! -------------------------------------------------------------------
    1170              :             ! χ_ll'(iτ,k=0) = G^occ_ll'(i|τ|,k=0) * G^vir_ll'(i|τ|,k=0)
    1171            0 :             CALL hadamard_product(matrix_G_occ_grid, matrix_G_vir_grid, matrix_chi_grid_spin, bs_env%spin_degeneracy)
    1172              : 
    1173              :             ! Accumulate spin contributions
    1174            0 :             CALL dbcsr_add(matrix_chi_grid, matrix_chi_grid_spin, 1.0_dp, 1.0_dp)
    1175              : 
    1176              :          END DO ! ispin
    1177              : 
    1178              :          ! ----------------------------------------------------------------------
    1179              :          ! C. TRANSFORM TO AUXILIARY BASIS & EXPORT DIRECTLY
    1180              :          ! χ_aux        = Z^T * χ_grid * Z
    1181              :          ! χ_PQ(iτ,k=0) = sum_ll' Z_lP χ_ll'(iτ,k=0) Z_l'Q
    1182              :          ! Result is dumped directly into the final array mat_chi_Gamma_tau!
    1183              :          ! ----------------------------------------------------------------------
    1184              :          CALL contract_A_B_A("T", "N", mat_Z_lP, matrix_chi_grid, &
    1185            0 :                              mat_chi_Gamma_tau(i_t)%matrix, bs_env%eps_filter)
    1186              : 
    1187            0 :          IF (bs_env%unit_nr > 0) THEN
    1188              :             WRITE (bs_env%unit_nr, '(T2,A,I13,A,I3,A,F7.1,A)') &
    1189            0 :                'Computed χ(iτ,k=0) for time point', i_t, ' /', bs_env%num_time_freq_points, &
    1190            0 :                ',    Execution time', m_walltime() - t1, ' s'
    1191              :          END IF
    1192              : 
    1193              :       END DO ! i_t
    1194              : 
    1195              :       ! =========================================================================
    1196              :       ! 3. FINAL CLEANUP
    1197              :       ! =========================================================================
    1198            0 :       CALL dbcsr_release(matrix_G_occ_grid)
    1199            0 :       CALL dbcsr_release(matrix_G_vir_grid)
    1200            0 :       CALL dbcsr_release(matrix_chi_grid)
    1201            0 :       CALL dbcsr_release(matrix_chi_grid_spin)
    1202            0 :       CALL dbcsr_distribution_release(dist_grid_grid)
    1203            0 :       DEALLOCATE (dist_col_grid)
    1204              : 
    1205            0 :       IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
    1206              : 
    1207            0 :       CALL timestop(handle)
    1208              : 
    1209            0 :    END SUBROUTINE get_mat_chi_Gamma_tau
    1210              : 
    1211              : ! **************************************************************************************************
    1212              : !> \brief Computes Green's Function in grid basis
    1213              : !> \param bs_env ...
    1214              : !> \param tau ...
    1215              : !> \param ispin ...
    1216              : !> \param occ ...
    1217              : !> \param vir ...
    1218              : !> \param mat_phi_mu_l ...
    1219              : !> \param matrix_G_grid ...
    1220              : !> \param eps_filter ...
    1221              : ! **************************************************************************************************
    1222              : 
    1223            0 :    SUBROUTINE build_G_grid(bs_env, tau, ispin, occ, vir, mat_phi_mu_l, matrix_G_grid, eps_filter)
    1224              : 
    1225              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1226              :       REAL(KIND=dp), INTENT(IN)                          :: tau
    1227              :       INTEGER, INTENT(IN)                                :: ispin
    1228              :       LOGICAL, INTENT(IN)                                :: occ, vir
    1229              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: mat_phi_mu_l, matrix_G_grid
    1230              :       REAL(KIND=dp), INTENT(IN)                          :: eps_filter
    1231              : 
    1232              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'build_G_grid'
    1233              : 
    1234              :       INTEGER                                            :: handle
    1235            0 :       INTEGER, DIMENSION(:), POINTER                     :: blk_ao, dist_row_ao
    1236              :       TYPE(cp_fm_type), POINTER                          :: fm_G
    1237              :       TYPE(dbcsr_distribution_type)                      :: dist_ao_ao
    1238              :       TYPE(dbcsr_type)                                   :: matrix_G_ao
    1239              : 
    1240            0 :       CALL timeset(routineN, handle)
    1241              : 
    1242              :       ! 1. Select the correct FM matrix based on occ/vir flags
    1243            0 :       IF (occ) THEN
    1244            0 :          fm_G => bs_env%fm_Gocc
    1245              :       ELSE
    1246            0 :          fm_G => bs_env%fm_Gvir
    1247              :       END IF
    1248              : 
    1249              :       ! 2. Compute Dense FM Green's Function
    1250              :       ! G^occ/vir_µλ(i|τ|,k=0) = sum_G^occ/vir_µλn^occ/vir C_µn(k=0) e^(-|(ϵ_nk=0-ϵ_F)τ|) C_λn(k=0)
    1251            0 :       CALL G_occ_vir(bs_env, tau, fm_G, ispin, occ=occ, vir=vir)
    1252              : 
    1253              :       ! 3. Setup AO DBCSR Topology and Create Matrix dynamically
    1254            0 :       CALL setup_square_topology(mat_phi_mu_l, 'COL', dist_ao_ao, blk_ao, dist_row_ao)
    1255              : 
    1256              :       CALL dbcsr_create(matrix_G_ao, name="G_ao", dist=dist_ao_ao, &
    1257              :                         matrix_type=dbcsr_type_no_symmetry, &
    1258            0 :                         row_blk_size=blk_ao, col_blk_size=blk_ao)
    1259              : 
    1260              :       ! 4. Convert FM to Sparse DBCSR
    1261            0 :       CALL copy_fm_to_dbcsr(fm_G, matrix_G_ao, keep_sparsity=.FALSE.)
    1262              : 
    1263              :       ! 5. Transform to Grid Basis: G_grid = phi * G_ao * phi^T
    1264              :       ! G^occ/vir_ll'(i|τ|,k=0) = sum_µν Φ_µ(r_l) G^occ/vir_µν Φ_ν(r_l')
    1265            0 :       CALL contract_A_B_A("N", "T", mat_phi_mu_l, matrix_G_ao, matrix_G_grid, eps_filter)
    1266              : 
    1267              :       ! 6. Release AO matrix and topology
    1268            0 :       CALL release_dbcsr_topology_and_matrices(dist=dist_ao_ao, mapped_dist=dist_row_ao, m1=matrix_G_ao)
    1269              : 
    1270            0 :       CALL timestop(handle)
    1271              : 
    1272            0 :    END SUBROUTINE build_G_grid
    1273              : 
    1274              : ! **************************************************************************************************
    1275              : !> \brief Generalized routine to compute OUT = A * B * A^T  OR  OUT = A^T * B * A using DBCSR
    1276              : !> \param transA_left ...
    1277              : !> \param transA_right ...
    1278              : !> \param matrix_A ...
    1279              : !> \param matrix_B ...
    1280              : !> \param matrix_out ...
    1281              : !> \param eps_filter ...
    1282              : ! **************************************************************************************************
    1283              : 
    1284         7468 :    SUBROUTINE contract_A_B_A(transA_left, transA_right, matrix_A, matrix_B, matrix_out, eps_filter)
    1285              : 
    1286              :       CHARACTER(LEN=1), INTENT(IN)                       :: transA_left, transA_right
    1287              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: matrix_A, matrix_B, matrix_out
    1288              :       REAL(KIND=dp), INTENT(IN)                          :: eps_filter
    1289              : 
    1290              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'contract_A_B_A'
    1291              : 
    1292              :       INTEGER                                            :: handle
    1293              :       TYPE(dbcsr_type)                                   :: matrix_tmp
    1294              : 
    1295         7468 :       CALL timeset(routineN, handle)
    1296              : 
    1297         7468 :       CALL dbcsr_create(matrix_tmp, template=matrix_A)
    1298              : 
    1299         7468 :       IF (transA_left == "N" .AND. transA_right == "T") THEN
    1300              :          ! Path 1: Out = A * B * A^T
    1301              :          CALL dbcsr_multiply("N", "N", 1.0_dp, matrix_A, matrix_B, &
    1302         3964 :                              0.0_dp, matrix_tmp, filter_eps=eps_filter)
    1303              :          CALL dbcsr_multiply("N", "T", 1.0_dp, matrix_tmp, matrix_A, &
    1304         3964 :                              0.0_dp, matrix_out, filter_eps=eps_filter)
    1305              : 
    1306         3504 :       ELSE IF (transA_left == "T" .AND. transA_right == "N") THEN
    1307              :          ! Path 2: Out = A^T * B * A
    1308              :          CALL dbcsr_multiply("N", "N", 1.0_dp, matrix_B, matrix_A, &
    1309         3504 :                              0.0_dp, matrix_tmp, filter_eps=eps_filter)
    1310              :          CALL dbcsr_multiply("T", "N", 1.0_dp, matrix_A, matrix_tmp, &
    1311         3504 :                              0.0_dp, matrix_out, filter_eps=eps_filter)
    1312              :       ELSE
    1313            0 :          CPABORT("Unsupported transposition pair in contract_A_B_A")
    1314              :       END IF
    1315              : 
    1316         7468 :       CALL dbcsr_release(matrix_tmp)
    1317              : 
    1318         7468 :       CALL timestop(handle)
    1319              : 
    1320         7468 :    END SUBROUTINE contract_A_B_A
    1321              : 
    1322              : ! **************************************************************************************************
    1323              : !> \brief Computes C = A ◦ B (Element-wise Hadamard product) for sparse DBCSR matrices.
    1324              : !> \param matrix_A ...
    1325              : !> \param matrix_B ...
    1326              : !> \param matrix_C ...
    1327              : !> \param fac (Scaling factor applied to the product)
    1328              : ! **************************************************************************************************
    1329              : 
    1330            0 :    SUBROUTINE hadamard_product(matrix_A, matrix_B, matrix_C, fac)
    1331              : 
    1332              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: matrix_A, matrix_B, matrix_C
    1333              :       REAL(KIND=dp), INTENT(IN)                          :: fac
    1334              : 
    1335              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'hadamard_product'
    1336              : 
    1337              :       INTEGER                                            :: col, handle, row
    1338              :       LOGICAL                                            :: found
    1339            0 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: blk_B, blk_C
    1340              :       TYPE(dbcsr_iterator_type)                          :: iter
    1341              : 
    1342            0 :       CALL timeset(routineN, handle)
    1343              : 
    1344            0 :       CALL dbcsr_copy(matrix_C, matrix_A)
    1345              : 
    1346            0 :       CALL dbcsr_iterator_start(iter, matrix_C)
    1347            0 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1348            0 :          CALL dbcsr_iterator_next_block(iter, row, col, blk_C)
    1349              : 
    1350            0 :          CALL dbcsr_get_block_p(matrix_B, row, col, blk_B, found)
    1351              : 
    1352            0 :          IF (found) THEN
    1353            0 :             blk_C(:, :) = fac*blk_C(:, :)*blk_B(:, :)
    1354              :          ELSE
    1355              :             ! If B is sparse here, the product is zero
    1356            0 :             blk_C(:, :) = 0.0_dp
    1357              :          END IF
    1358              :       END DO
    1359            0 :       CALL dbcsr_iterator_stop(iter)
    1360              : 
    1361            0 :       CALL timestop(handle)
    1362              : 
    1363            0 :    END SUBROUTINE hadamard_product
    1364              : 
    1365              : ! **************************************************************************************************
    1366              : !> \brief In-place Hadamard A <- fac * (A ◦ B). Value mutation only (no block insert/delete),
    1367              : !>        so iterating A while writing through the block pointer is safe.
    1368              : !> \param matrix_A in/out factor (overwritten by the product)
    1369              : !> \param matrix_B second factor (looked up; blocks absent in B zero the A block)
    1370              : !> \param fac (Scaling factor applied to the product)
    1371              : ! **************************************************************************************************
    1372         7008 :    SUBROUTINE hadamard_product_inplace(matrix_A, matrix_B, fac)
    1373              : 
    1374              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: matrix_A, matrix_B
    1375              :       REAL(KIND=dp), INTENT(IN)                          :: fac
    1376              : 
    1377              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'hadamard_product_inplace'
    1378              : 
    1379              :       INTEGER                                            :: col, handle, row
    1380              :       LOGICAL                                            :: found
    1381         3504 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: blk_A, blk_B
    1382              :       TYPE(dbcsr_iterator_type)                          :: iter
    1383              : 
    1384         3504 :       CALL timeset(routineN, handle)
    1385              : 
    1386         3504 :       CALL dbcsr_iterator_start(iter, matrix_A)
    1387       214608 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1388       211104 :          CALL dbcsr_iterator_next_block(iter, row, col, blk_A)
    1389              : 
    1390       211104 :          CALL dbcsr_get_block_p(matrix_B, row, col, blk_B, found)
    1391              : 
    1392       214608 :          IF (found) THEN
    1393    332612544 :             blk_A(:, :) = fac*blk_A(:, :)*blk_B(:, :)
    1394              :          ELSE
    1395              :             ! If B is sparse here, the product is zero
    1396            0 :             blk_A(:, :) = 0.0_dp
    1397              :          END IF
    1398              :       END DO
    1399         3504 :       CALL dbcsr_iterator_stop(iter)
    1400              : 
    1401         3504 :       CALL timestop(handle)
    1402              : 
    1403         3504 :    END SUBROUTINE hadamard_product_inplace
    1404              : 
    1405              : ! **************************************************************************************************
    1406              : !> \brief Compute screened Coulomb interaction matrix
    1407              : !> \param bs_env ...
    1408              : !> \param qs_env ...
    1409              : !> \param mat_chi_Gamma_tau ...
    1410              : !> \param fm_W_time ...
    1411              : ! **************************************************************************************************
    1412              : 
    1413            0 :    SUBROUTINE compute_W(bs_env, qs_env, mat_chi_Gamma_tau, fm_W_time)
    1414              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1415              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1416              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: mat_chi_Gamma_tau
    1417              :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:)        :: fm_W_time
    1418              : 
    1419              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'compute_W'
    1420              : 
    1421              :       INTEGER                                            :: handle, i_t, j_w
    1422              :       REAL(KIND=dp)                                      :: t1
    1423              :       TYPE(cp_fm_type)                                   :: fm_M_inv_V_sqrt, fm_V, fm_V_sqrt
    1424              : 
    1425            0 :       CALL timeset(routineN, handle)
    1426              : 
    1427            0 :       t1 = m_walltime()
    1428              : 
    1429            0 :       CALL create_fm_W_MIC_time(bs_env, fm_W_time)
    1430              : 
    1431              :       ! 1. Allocate V and M matrices
    1432            0 :       CALL cp_fm_create(fm_V, bs_env%fm_RI_RI%matrix_struct)
    1433            0 :       CALL cp_fm_create(fm_V_sqrt, bs_env%fm_RI_RI%matrix_struct)
    1434            0 :       CALL cp_fm_create(fm_M_inv_V_sqrt, bs_env%fm_RI_RI%matrix_struct)
    1435              : 
    1436              :       ! Compute V and M^-1 * V^0.5
    1437            0 :       CALL compute_V_MinvVsqrt(bs_env, qs_env, fm_V, fm_V_sqrt, fm_M_inv_V_sqrt)
    1438              : 
    1439              :       ! 2. Loop over frequencies
    1440            0 :       DO j_w = 1, bs_env%num_time_freq_points
    1441              :          ! Fourier transformation of χ_PQ(iτ) to χ_PQ(iω_j)
    1442            0 :          CALL compute_fm_chi_Gamma_freq(bs_env, bs_env%fm_chi_Gamma_freq, j_w, mat_chi_Gamma_tau)
    1443              : 
    1444              :          ! ε(iω_j) = Id - V^0.5*M^-1*χ(iω_j)*M^-1*V^0.5
    1445              :          ! W(iω_j) = V^0.5*(ε^-1(iω_j)-Id)*V^0.5
    1446              :          CALL compute_fm_W_freq(bs_env, bs_env%fm_chi_Gamma_freq, fm_V_sqrt, &
    1447            0 :                                 fm_M_inv_V_sqrt, bs_env%fm_W_MIC_freq)
    1448              : 
    1449              :          ! Fourier transform from W_PQ^MIC(iω_j) to W_PQ^MIC(iτ)
    1450            0 :          CALL Fourier_transform_w_to_t(bs_env, fm_W_time, bs_env%fm_W_MIC_freq, j_w)
    1451              :       END DO
    1452              : 
    1453              :       ! M^-1*W^MIC(iτ)*M^-1
    1454            0 :       CALL multiply_fm_W_MIC_time_with_Minv_Gamma(bs_env, qs_env, fm_W_time)
    1455              : 
    1456            0 :       IF (bs_env%unit_nr > 0) THEN
    1457              :          WRITE (bs_env%unit_nr, '(T2,A,T55,A,F10.1,A)') &
    1458            0 :             'Computed W(iτ),', ' Execution time', m_walltime() - t1, ' s'
    1459              :       END IF
    1460              : 
    1461            0 :       CALL dbcsr_deallocate_matrix_set(mat_chi_Gamma_tau)
    1462              : 
    1463              :       ! Cleanup
    1464            0 :       CALL cp_fm_release(fm_V)
    1465            0 :       CALL cp_fm_release(fm_V_sqrt)
    1466            0 :       CALL cp_fm_release(fm_M_inv_V_sqrt)
    1467              : 
    1468              :       ! Marek : Fourier transform W^MIC(itau) back to get it at a specific im.frequency point - iomega = 0
    1469            0 :       IF (bs_env%rtp_method == rtp_method_bse) THEN
    1470            0 :          t1 = m_walltime()
    1471            0 :          CALL cp_fm_create(bs_env%fm_W_MIC_freq_zero, bs_env%fm_W_MIC_freq%matrix_struct)
    1472              :          ! Set to zero
    1473            0 :          CALL cp_fm_set_all(bs_env%fm_W_MIC_freq_zero, 0.0_dp)
    1474              :          ! Sum over all times
    1475            0 :          DO i_t = 1, bs_env%num_time_freq_points
    1476              :             ! Add the relevant structure with correct weight
    1477              :             CALL cp_fm_scale_and_add(1.0_dp, bs_env%fm_W_MIC_freq_zero, &
    1478            0 :                                      bs_env%imag_time_weights_freq_zero(i_t), fm_W_time(i_t))
    1479              :          END DO
    1480              :          ! Done, save to file
    1481            0 :          CALL fm_write(bs_env%fm_W_MIC_freq_zero, 0, "W_freq_rtp", qs_env)
    1482              :          ! Report calculation
    1483            0 :          IF (bs_env%unit_nr > 0) THEN
    1484              :             WRITE (bs_env%unit_nr, '(T2,A,T55,A,F10.1,A)') &
    1485            0 :                'Computed W(0),', ' Execution time', m_walltime() - t1, ' s'
    1486              :          END IF
    1487              :       END IF
    1488              : 
    1489            0 :       IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
    1490              : 
    1491            0 :       CALL timestop(handle)
    1492              : 
    1493            0 :    END SUBROUTINE compute_W
    1494              : 
    1495              : ! **************************************************************************************************
    1496              : !> \brief Computes V, V^0.5, and M^-1 * V^0.5
    1497              : !> \param bs_env ...
    1498              : !> \param qs_env ...
    1499              : !> \param fm_V ...
    1500              : !> \param fm_V_sqrt ...
    1501              : !> \param fm_Minv_Vsqrt ...
    1502              : ! **************************************************************************************************
    1503              : 
    1504            0 :    SUBROUTINE compute_V_MinvVsqrt(bs_env, qs_env, fm_V, fm_V_sqrt, fm_Minv_Vsqrt)
    1505              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1506              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1507              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: fm_V, fm_V_sqrt, fm_Minv_Vsqrt
    1508              : 
    1509              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_V_MinvVsqrt'
    1510              : 
    1511              :       INTEGER                                            :: handle, info, n_RI, ndep
    1512            0 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
    1513              :       TYPE(cell_type), POINTER                           :: cell
    1514              :       TYPE(cp_fm_type)                                   :: fm_work
    1515            0 :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :)     :: fm_M
    1516            0 :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: mat_V_kp
    1517            0 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
    1518            0 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
    1519              : 
    1520            0 :       CALL timeset(routineN, handle)
    1521              : 
    1522            0 :       n_RI = bs_env%n_RI
    1523            0 :       CALL cp_fm_create(fm_work, fm_V%matrix_struct)
    1524              : 
    1525              :       ! -----------------------------------------------------------------------
    1526              :       ! 1. Build Coulomb Matrix V(k=0) using the kp-routine but only for ikp=1
    1527              :       ! -----------------------------------------------------------------------
    1528              :       CALL get_qs_env(qs_env=qs_env, particle_set=particle_set, cell=cell, &
    1529            0 :                       qs_kind_set=qs_kind_set, atomic_kind_set=atomic_kind_set)
    1530              : 
    1531            0 :       ALLOCATE (mat_V_kp(1:1, 1:2))
    1532            0 :       NULLIFY (mat_V_kp(1, 1)%matrix, mat_V_kp(1, 2)%matrix)
    1533            0 :       ALLOCATE (mat_V_kp(1, 1)%matrix, mat_V_kp(1, 2)%matrix)
    1534              : 
    1535            0 :       CALL dbcsr_create(mat_V_kp(1, 1)%matrix, template=bs_env%mat_RI_RI%matrix)
    1536            0 :       CALL dbcsr_reserve_all_blocks(mat_V_kp(1, 1)%matrix)
    1537            0 :       CALL dbcsr_set(mat_V_kp(1, 1)%matrix, 0.0_dp)
    1538              : 
    1539              :       ! Dummy imaginary part just to satisfy the routine
    1540            0 :       CALL dbcsr_create(mat_V_kp(1, 2)%matrix, template=bs_env%mat_RI_RI%matrix)
    1541            0 :       CALL dbcsr_reserve_all_blocks(mat_V_kp(1, 2)%matrix)
    1542            0 :       CALL dbcsr_set(mat_V_kp(1, 2)%matrix, 0.0_dp)
    1543              : 
    1544            0 :       bs_env%kpoints_chi_eps_W%nkp_grid = bs_env%nkp_grid_chi_eps_W_orig
    1545              : 
    1546              :       CALL build_2c_coulomb_matrix_kp(mat_V_kp, bs_env%kpoints_chi_eps_W, "RI_AUX", cell, &
    1547              :                                       particle_set, qs_kind_set, atomic_kind_set, &
    1548            0 :                                       bs_env%size_lattice_sum_V, operator_coulomb, 1, 1)
    1549              : 
    1550              :       ! Copy real part to fm_V
    1551            0 :       CALL copy_dbcsr_to_fm(mat_V_kp(1, 1)%matrix, fm_V)
    1552              : 
    1553            0 :       CALL dbcsr_deallocate_matrix(mat_V_kp(1, 1)%matrix)
    1554            0 :       CALL dbcsr_deallocate_matrix(mat_V_kp(1, 2)%matrix)
    1555            0 :       DEALLOCATE (mat_V_kp)
    1556              : 
    1557              :       ! -----------------------------------------------------------------------
    1558              :       ! 2. Get RI-Metric Matrix M(k=0)
    1559              :       ! -----------------------------------------------------------------------
    1560              :       CALL RI_2c_integral_mat(qs_env, fm_M, fm_V, n_RI, bs_env%ri_metric, &
    1561            0 :                               do_kpoints=.FALSE., regularization_RI=bs_env%regularization_RI)
    1562              : 
    1563              :       ! -----------------------------------------------------------------------
    1564              :       ! 3. M -> M^-1
    1565              :       ! -----------------------------------------------------------------------
    1566            0 :       CALL cp_fm_cholesky_decompose(fm_M(1, 1), info_out=info)
    1567            0 :       IF (info == 0) THEN
    1568            0 :          CALL cp_fm_cholesky_invert(fm_M(1, 1))
    1569            0 :          CALL cp_fm_uplo_to_full(fm_M(1, 1), fm_work)
    1570              :       ELSE
    1571              :          ! Fallback if Cholesky fails due to conditioning
    1572            0 :          CALL cp_fm_power(fm_M(1, 1), fm_work, -1.0_dp, bs_env%eps_eigval_mat_RI, ndep)
    1573            0 :          CALL cp_fm_to_fm(fm_work, fm_M(1, 1))
    1574              :       END IF
    1575              : 
    1576              :       ! -----------------------------------------------------------------------
    1577              :       ! 4. V -> V^0.5
    1578              :       ! -----------------------------------------------------------------------
    1579            0 :       CALL cp_fm_to_fm(fm_V, fm_V_sqrt)
    1580            0 :       CALL cp_fm_cholesky_decompose(fm_V_sqrt, info_out=info)
    1581            0 :       IF (info == 0) THEN
    1582            0 :          CALL clean_lower_part(fm_V_sqrt)
    1583              :       ELSE
    1584            0 :          CALL cp_fm_power(fm_V, fm_V_sqrt, 0.5_dp, bs_env%eps_eigval_mat_RI, ndep)
    1585              :       END IF
    1586              : 
    1587              :       ! -----------------------------------------------------------------------
    1588              :       ! 5. M^-1 * V^0.5
    1589              :       ! -----------------------------------------------------------------------
    1590              :       CALL parallel_gemm("N", "T", n_RI, n_RI, n_RI, 1.0_dp, fm_M(1, 1), fm_V_sqrt, &
    1591            0 :                          0.0_dp, fm_Minv_Vsqrt)
    1592              : 
    1593            0 :       CALL cp_fm_release(fm_M)
    1594            0 :       CALL cp_fm_release(fm_work)
    1595              : 
    1596            0 :       CALL timestop(handle)
    1597              : 
    1598            0 :    END SUBROUTINE compute_V_MinvVsqrt
    1599              : 
    1600              : ! **************************************************************************************************
    1601              : !> \brief Computes W(iω) from χ_PQ(iω_j)
    1602              : !> \param bs_env ...
    1603              : !> \param fm_chi_freq_j ...
    1604              : !> \param fm_V_sqrt ...
    1605              : !> \param fm_Minv_Vsqrt ...
    1606              : !> \param fm_W_freq_j ...
    1607              : ! **************************************************************************************************
    1608              : 
    1609            0 :    SUBROUTINE compute_fm_W_freq(bs_env, fm_chi_freq_j, fm_V_sqrt, fm_Minv_Vsqrt, fm_W_freq_j)
    1610              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1611              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm_chi_freq_j, fm_V_sqrt, fm_Minv_Vsqrt
    1612              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: fm_W_freq_j
    1613              : 
    1614              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'compute_fm_W_freq'
    1615              : 
    1616              :       INTEGER                                            :: handle, info, n_RI, ndep
    1617              :       TYPE(cp_fm_type)                                   :: fm_eps_freq_j, fm_work
    1618              : 
    1619            0 :       CALL timeset(routineN, handle)
    1620              : 
    1621            0 :       n_RI = bs_env%n_RI
    1622              : 
    1623            0 :       CALL cp_fm_create(fm_eps_freq_j, fm_chi_freq_j%matrix_struct)
    1624            0 :       CALL cp_fm_create(fm_work, fm_chi_freq_j%matrix_struct)
    1625              : 
    1626              :       ! -----------------------------------------------------------------------
    1627              :       ! 1. ε(iω_j) = Id - (M^-1 * V^0.5)^T * χ(iω_j) * (M^-1 * V^0.5)
    1628              :       ! -----------------------------------------------------------------------
    1629              :       ! work = χ(iω_j) * (M^-1 * V^0.5)
    1630              :       CALL parallel_gemm('N', 'N', n_RI, n_RI, n_RI, 1.0_dp, &
    1631            0 :                          fm_chi_freq_j, fm_Minv_Vsqrt, 0.0_dp, fm_work)
    1632              : 
    1633              :       ! eps_work = (M^-1 * V^0.5)^T * work
    1634              :       CALL parallel_gemm('T', 'N', n_RI, n_RI, n_RI, 1.0_dp, &
    1635            0 :                          fm_Minv_Vsqrt, fm_work, 0.0_dp, fm_eps_freq_j)
    1636              : 
    1637              :       ! ε(iω_j) = Id - eps_work  -->  -eps_work + Id
    1638            0 :       CALL fm_add_on_diag(fm_eps_freq_j, 1.0_dp)
    1639              : 
    1640              :       ! Force perfect symmetry before Cholesky to avoid info != 0 due to GEMM noise
    1641            0 :       CALL cp_fm_uplo_to_full(fm_eps_freq_j, fm_work)
    1642              : 
    1643              :       ! -----------------------------------------------------------------------
    1644              :       ! 2. W(iω_j) = V^0.5^T * (ε^-1(iω_j) - Id) * V^0.5
    1645              :       ! -----------------------------------------------------------------------
    1646              : 
    1647              :       ! a) Cholesky decomposition of ε(iω_j)
    1648            0 :       CALL cp_fm_cholesky_decompose(fm_eps_freq_j, info_out=info)
    1649              : 
    1650              :       ! b) Inversion
    1651            0 :       IF (info == 0) THEN
    1652            0 :          CALL cp_fm_cholesky_invert(fm_eps_freq_j)
    1653            0 :          CALL cp_fm_uplo_to_full(fm_eps_freq_j, fm_work)
    1654              :       ELSE
    1655              :          ! Fallback to expensive diagonalization if Cholesky fails
    1656            0 :          CALL cp_fm_power(fm_eps_freq_j, fm_work, -1.0_dp, bs_env%eps_eigval_mat_RI, ndep)
    1657            0 :          CALL cp_fm_to_fm(fm_work, fm_eps_freq_j)
    1658              :       END IF
    1659              : 
    1660              :       ! c) ε^-1(iω_j) - Id
    1661            0 :       CALL fm_add_on_diag(fm_eps_freq_j, -1.0_dp)
    1662              : 
    1663              :       ! d) work = (ε^-1(iω_j) - Id) * V^0.5
    1664              :       CALL parallel_gemm('N', 'N', n_RI, n_RI, n_RI, 1.0_dp, fm_eps_freq_j, fm_V_sqrt, &
    1665            0 :                          0.0_dp, fm_work)
    1666              : 
    1667              :       ! e) W(iw) = V^0.5^T * work
    1668              :       CALL parallel_gemm('T', 'N', n_RI, n_RI, n_RI, 1.0_dp, fm_V_sqrt, fm_work, &
    1669            0 :                          0.0_dp, fm_W_freq_j)
    1670              : 
    1671              :       ! Cleanup
    1672            0 :       CALL cp_fm_release(fm_work)
    1673            0 :       CALL cp_fm_release(fm_eps_freq_j)
    1674              : 
    1675            0 :       CALL timestop(handle)
    1676              : 
    1677            0 :    END SUBROUTINE compute_fm_W_freq
    1678              : 
    1679              : ! **************************************************************************************************
    1680              : !> \brief Adds a real scalar value to the diagonal of a real full matrix (fm)
    1681              : !> \param fm ...
    1682              : !> \param alpha ...
    1683              : ! **************************************************************************************************
    1684              : 
    1685            0 :    SUBROUTINE fm_add_on_diag(fm, alpha)
    1686              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: fm
    1687              :       REAL(KIND=dp), INTENT(IN)                          :: alpha
    1688              : 
    1689              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'fm_add_on_diag'
    1690              : 
    1691              :       INTEGER                                            :: handle, i_global, i_row, j_col, &
    1692              :                                                             j_global, ncol_local, nrow_local
    1693            0 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
    1694              : 
    1695            0 :       CALL timeset(routineN, handle)
    1696              : 
    1697              :       CALL cp_fm_get_info(matrix=fm, &
    1698              :                           nrow_local=nrow_local, &
    1699              :                           ncol_local=ncol_local, &
    1700              :                           row_indices=row_indices, &
    1701            0 :                           col_indices=col_indices)
    1702              : 
    1703            0 :       DO j_col = 1, ncol_local
    1704            0 :          j_global = col_indices(j_col)
    1705            0 :          DO i_row = 1, nrow_local
    1706            0 :             i_global = row_indices(i_row)
    1707            0 :             IF (j_global == i_global) THEN
    1708            0 :                fm%local_data(i_row, j_col) = fm%local_data(i_row, j_col) + alpha
    1709              :             END IF
    1710              :          END DO
    1711              :       END DO
    1712              : 
    1713            0 :       CALL timestop(handle)
    1714              : 
    1715            0 :    END SUBROUTINE fm_add_on_diag
    1716              : 
    1717              : ! **************************************************************************************************
    1718              : !> \brief Zeroes out the strictly lower triangular part of a real matrix
    1719              : !> \param fm_mat ...
    1720              : ! **************************************************************************************************
    1721            0 :    SUBROUTINE clean_lower_part(fm_mat)
    1722              :       TYPE(cp_fm_type)                                   :: fm_mat
    1723              : 
    1724              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'clean_lower_part'
    1725              : 
    1726              :       INTEGER                                            :: handle, i_row, j_col, j_global, &
    1727              :                                                             ncol_local, nrow_local
    1728            0 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
    1729              : 
    1730            0 :       CALL timeset(routineN, handle)
    1731              : 
    1732              :       CALL cp_fm_get_info(matrix=fm_mat, &
    1733              :                           nrow_local=nrow_local, ncol_local=ncol_local, &
    1734            0 :                           row_indices=row_indices, col_indices=col_indices)
    1735              : 
    1736            0 :       DO j_col = 1, ncol_local
    1737            0 :          j_global = col_indices(j_col)
    1738            0 :          DO i_row = 1, nrow_local
    1739            0 :             IF (j_global < row_indices(i_row)) fm_mat%local_data(i_row, j_col) = 0.0_dp
    1740              :          END DO
    1741              :       END DO
    1742              : 
    1743            0 :       CALL timestop(handle)
    1744              : 
    1745            0 :    END SUBROUTINE clean_lower_part
    1746              : 
    1747              : ! **************************************************************************************************
    1748              : !> \brief Computes the exact exchange part of the GW self-energy
    1749              : !> \param bs_env ...
    1750              : !> \param qs_env ...
    1751              : !> \param mat_phi_mu_l ...
    1752              : !> \param mat_Z_lP ...
    1753              : !> \param fm_Sigma_x_Gamma ...
    1754              : ! **************************************************************************************************
    1755              : 
    1756            0 :    SUBROUTINE compute_Sigma_x(bs_env, qs_env, mat_phi_mu_l, mat_Z_lP, fm_Sigma_x_Gamma)
    1757              : 
    1758              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1759              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1760              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: mat_phi_mu_l, mat_Z_lP
    1761              :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:)        :: fm_Sigma_x_Gamma
    1762              : 
    1763              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'compute_Sigma_x'
    1764              : 
    1765              :       INTEGER                                            :: handle, ispin
    1766            0 :       INTEGER, DIMENSION(:), POINTER                     :: blk_aux, blk_grid, dist_col_grid, &
    1767            0 :                                                             dist_row_aux
    1768              :       REAL(KIND=dp)                                      :: t1
    1769            0 :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :)     :: fm_Vtr_Gamma
    1770              :       TYPE(dbcsr_distribution_type)                      :: dist_aux_aux, dist_grid_grid
    1771              :       TYPE(dbcsr_type)                                   :: mat_Sigma_x_Gamma, matrix_D_grid, &
    1772              :                                                             matrix_Sigma_x_grid, matrix_V_aux, &
    1773              :                                                             matrix_V_grid
    1774              : 
    1775            0 :       CALL timeset(routineN, handle)
    1776              : 
    1777            0 :       t1 = m_walltime()
    1778              : 
    1779            0 :       ALLOCATE (fm_Sigma_x_Gamma(bs_env%n_spin))
    1780            0 :       DO ispin = 1, bs_env%n_spin
    1781            0 :          CALL cp_fm_create(fm_Sigma_x_Gamma(ispin), bs_env%fm_s_Gamma%matrix_struct)
    1782              :       END DO
    1783              : 
    1784            0 :       CALL dbcsr_create(mat_Sigma_x_Gamma, template=bs_env%mat_ao_ao%matrix)
    1785              : 
    1786              :       ! =========================================================================
    1787              :       ! 1. SETUP CORE TOPOLOGIES
    1788              :       ! =========================================================================
    1789            0 :       CALL setup_square_topology(mat_phi_mu_l, 'ROW', dist_grid_grid, blk_grid, dist_col_grid)
    1790            0 :       CALL setup_square_topology(mat_Z_lP, 'COL', dist_aux_aux, blk_aux, dist_row_aux)
    1791              : 
    1792              :       ! =========================================================================
    1793              :       ! 2. COMPUTE V^tr_ll'
    1794              :       ! =========================================================================
    1795              :       CALL RI_2c_integral_mat(qs_env, fm_Vtr_Gamma, bs_env%fm_RI_RI, bs_env%n_RI, &
    1796            0 :                               bs_env%trunc_coulomb, do_kpoints=.FALSE.)
    1797              : 
    1798              :       ! M^-1 * V^tr * M^-1 directly modifies fm_Vtr_Gamma(:, 1)
    1799            0 :       CALL multiply_fm_W_MIC_time_with_Minv_Gamma(bs_env, qs_env, fm_Vtr_Gamma(:, 1))
    1800              : 
    1801            0 :       CALL dbcsr_create(matrix_V_aux, "V_aux", dist_aux_aux, dbcsr_type_no_symmetry, blk_aux, blk_aux)
    1802            0 :       CALL dbcsr_create(matrix_V_grid, "V_grid", dist_grid_grid, dbcsr_type_no_symmetry, blk_grid, blk_grid)
    1803              : 
    1804            0 :       CALL copy_fm_to_dbcsr(fm_Vtr_Gamma(1, 1), matrix_V_aux, keep_sparsity=.FALSE.)
    1805              : 
    1806              :       ! V^tr_ll' = sum_PQ Z_lP V^trunc_PQ Z_l'Q
    1807            0 :       CALL contract_A_B_A("N", "T", mat_Z_lP, matrix_V_aux, matrix_V_grid, bs_env%eps_filter)
    1808            0 :       CALL dbcsr_release(matrix_V_aux)
    1809              : 
    1810              :       ! =========================================================================
    1811              :       ! 3. SPIN LOOP FOR EXACT EXCHANGE
    1812              :       ! =========================================================================
    1813            0 :       DO ispin = 1, bs_env%n_spin
    1814              : 
    1815              :          ! Density matrix on grid is essentially G_occ at tau = 0.0
    1816              :          ! D_µν  = sum_n^occ C_µn(k=0) C_νn(k=0)
    1817              :          ! D_ll' = sum_µν Φ_µ(r_l) D_µν Φ_ν(r_l')
    1818            0 :          CALL dbcsr_create(matrix_D_grid, "D_grid", dist_grid_grid, dbcsr_type_no_symmetry, blk_grid, blk_grid)
    1819            0 :          CALL build_G_grid(bs_env, 0.0_dp, ispin, .TRUE., .FALSE., mat_phi_mu_l, matrix_D_grid, bs_env%eps_filter)
    1820              : 
    1821              :          ! Element-wise Hadamard product: Σ^x_grid = D_grid ◦ V_grid
    1822              :          ! Σ^x_ll' = D_ll' * V^tr_ll'
    1823            0 :          CALL dbcsr_create(matrix_Sigma_x_grid, template=matrix_V_grid)
    1824            0 :          CALL hadamard_product(matrix_D_grid, matrix_V_grid, matrix_Sigma_x_grid, 1.0_dp)
    1825              : 
    1826            0 :          CALL dbcsr_release(matrix_D_grid)
    1827              : 
    1828              :          ! Transform back to AO basis: Σ^x_ao = -1.0 * phi^T * Σ^x_grid * phi
    1829              :          ! Σ^x_λσ(k=0)   = -sum_ll' Φ_λ(r_l) Σ^x_ll' Φ_σ(r_l')
    1830            0 :          CALL contract_A_B_A("T", "N", mat_phi_mu_l, matrix_Sigma_x_grid, mat_Sigma_x_Gamma, bs_env%eps_filter)
    1831            0 :          CALL dbcsr_scale(mat_Sigma_x_Gamma, -1.0_dp)
    1832              : 
    1833            0 :          CALL dbcsr_release(matrix_Sigma_x_grid)
    1834              : 
    1835              :          ! Data I/O and Export to CP2K Full Matrices
    1836            0 :          CALL copy_dbcsr_to_fm(mat_Sigma_x_Gamma, fm_Sigma_x_Gamma(ispin))
    1837              : 
    1838              :       END DO ! ispin
    1839              : 
    1840            0 :       IF (bs_env%unit_nr > 0) THEN
    1841              :          WRITE (bs_env%unit_nr, '(T2,A,T58,A,F7.1,A)') &
    1842            0 :             'Computed Σ^x(k=0),', ' Execution time', m_walltime() - t1, ' s'
    1843            0 :          WRITE (bs_env%unit_nr, '(A)') ' '
    1844              :       END IF
    1845              : 
    1846              :       ! =========================================================================
    1847              :       ! 4. CLEANUP
    1848              :       ! =========================================================================
    1849              :       CALL release_dbcsr_topology_and_matrices(dist=dist_grid_grid, mapped_dist=dist_col_grid, &
    1850            0 :                                                m1=mat_Sigma_x_Gamma, m2=matrix_V_grid)
    1851            0 :       CALL release_dbcsr_topology_and_matrices(dist=dist_aux_aux, mapped_dist=dist_row_aux)
    1852              : 
    1853            0 :       CALL cp_fm_release(fm_Vtr_Gamma)
    1854              : 
    1855            0 :       CALL timestop(handle)
    1856              : 
    1857            0 :    END SUBROUTINE compute_Sigma_x
    1858              : 
    1859              : ! **************************************************************************************************
    1860              : !> \brief Computes the correlation part of the GW self-energy
    1861              : !> \param bs_env ...
    1862              : !> \param fm_W_time ...
    1863              : !> \param mat_phi_mu_l ...
    1864              : !> \param mat_Z_lP ...
    1865              : !> \param fm_Sigma_c_Gamma_time ...
    1866              : ! **************************************************************************************************
    1867              : 
    1868            0 :    SUBROUTINE compute_Sigma_c(bs_env, fm_W_time, mat_phi_mu_l, mat_Z_lP, fm_Sigma_c_Gamma_time)
    1869              : 
    1870              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1871              :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:)        :: fm_W_time
    1872              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: mat_phi_mu_l, mat_Z_lP
    1873              :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :, :)  :: fm_Sigma_c_Gamma_time
    1874              : 
    1875              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'compute_Sigma_c'
    1876              : 
    1877              :       INTEGER                                            :: handle, i_t, ispin
    1878            0 :       INTEGER, DIMENSION(:), POINTER                     :: blk_aux, blk_grid, dist_col_grid, &
    1879            0 :                                                             dist_row_aux
    1880              :       REAL(KIND=dp)                                      :: t1, tau
    1881              :       TYPE(dbcsr_distribution_type)                      :: dist_aux_aux, dist_grid_grid
    1882            0 :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: mat_Sigma_neg_tau, mat_Sigma_pos_tau
    1883              :       TYPE(dbcsr_type) :: matrix_G_occ_grid, matrix_G_vir_grid, matrix_Sigma_neg_grid, &
    1884              :          matrix_Sigma_pos_grid, matrix_W_aux, matrix_W_grid
    1885              : 
    1886            0 :       CALL timeset(routineN, handle)
    1887              : 
    1888              :       ! =========================================================================
    1889              :       ! 1. SETUP CORE TOPOLOGIES AND PRE-ALLOCATE OUTPUT ARRAYS
    1890              :       ! =========================================================================
    1891            0 :       CALL setup_square_topology(mat_phi_mu_l, 'ROW', dist_grid_grid, blk_grid, dist_col_grid)
    1892            0 :       CALL setup_square_topology(mat_Z_lP, 'COL', dist_aux_aux, blk_aux, dist_row_aux)
    1893              : 
    1894              :       ! Pre-allocate local DBCSR matrices to act as targets for final output
    1895            0 :       NULLIFY (mat_Sigma_neg_tau, mat_Sigma_pos_tau)
    1896            0 :       ALLOCATE (mat_Sigma_neg_tau(bs_env%num_time_freq_points, bs_env%n_spin))
    1897            0 :       ALLOCATE (mat_Sigma_pos_tau(bs_env%num_time_freq_points, bs_env%n_spin))
    1898              : 
    1899            0 :       DO i_t = 1, bs_env%num_time_freq_points
    1900            0 :          DO ispin = 1, bs_env%n_spin
    1901            0 :             ALLOCATE (mat_Sigma_neg_tau(i_t, ispin)%matrix)
    1902            0 :             ALLOCATE (mat_Sigma_pos_tau(i_t, ispin)%matrix)
    1903            0 :             CALL dbcsr_create(mat_Sigma_neg_tau(i_t, ispin)%matrix, template=bs_env%mat_ao_ao%matrix)
    1904            0 :             CALL dbcsr_create(mat_Sigma_pos_tau(i_t, ispin)%matrix, template=bs_env%mat_ao_ao%matrix)
    1905              :          END DO
    1906              :       END DO
    1907              : 
    1908              :       ! =========================================================================
    1909              :       ! 2. MAIN IMAGINARY TIME LOOP
    1910              :       ! =========================================================================
    1911            0 :       DO i_t = 1, bs_env%num_time_freq_points
    1912            0 :          tau = bs_env%imag_time_points(i_t)
    1913              : 
    1914              :          ! -------------------------------------------------------------------
    1915              :          ! Compute W_grid = Z * W_aux * Z^T
    1916              :          ! -------------------------------------------------------------------
    1917            0 :          CALL dbcsr_create(matrix_W_aux, "W_aux", dist_aux_aux, dbcsr_type_no_symmetry, blk_aux, blk_aux)
    1918            0 :          CALL dbcsr_create(matrix_W_grid, "W_grid", dist_grid_grid, dbcsr_type_no_symmetry, blk_grid, blk_grid)
    1919              : 
    1920            0 :          CALL copy_fm_to_dbcsr(fm_W_time(i_t), matrix_W_aux, keep_sparsity=.FALSE.)
    1921              : 
    1922              :          ! W^MIC_ll'(iτ,k=0) = sum_PQ Z_lP W^MIC_PQ(iτ) Z_l'Q
    1923            0 :          CALL contract_A_B_A("N", "T", mat_Z_lP, matrix_W_aux, matrix_W_grid, bs_env%eps_filter)
    1924              : 
    1925            0 :          CALL dbcsr_release(matrix_W_aux) ! Clean up aux basis immediately
    1926              : 
    1927            0 :          DO ispin = 1, bs_env%n_spin
    1928            0 :             t1 = m_walltime()
    1929              : 
    1930              :             ! -------------------------------------------------------------------
    1931              :             ! A. Transform Green's Functions to the Grid
    1932              :             ! -------------------------------------------------------------------
    1933            0 :             CALL dbcsr_create(matrix_G_occ_grid, "G_occ_grid", dist_grid_grid, dbcsr_type_no_symmetry, blk_grid, blk_grid)
    1934            0 :             CALL dbcsr_create(matrix_G_vir_grid, "G_vir_grid", dist_grid_grid, dbcsr_type_no_symmetry, blk_grid, blk_grid)
    1935              : 
    1936              :             ! G^occ_µλ(i|τ|,k=0) = sum_G^occ_µλn^occ C_µn(k=0) e^(-|(ϵ_nk=0-ϵ_F)τ|) C_λn(k=0)
    1937              :             ! G^occ_ll'(i|τ|,k=0) = sum_µν Φ_µ(r_l) G^occ_µν Φ_ν(r_l')
    1938            0 :             CALL build_G_grid(bs_env, tau, ispin, .TRUE., .FALSE., mat_phi_mu_l, matrix_G_occ_grid, bs_env%eps_filter)
    1939              : 
    1940              :             ! G^vir_µλ(i|τ|,k=0) = sum_n^vir C_µn(k=0) e^(-|(ϵ_nk=0-ϵ_F)τ|) C_λn(k=0)
    1941              :             ! G^vir_ll'(i|τ|,k=0) = sum_µν Φ_µ(r_l) G^vir_µν Φ_ν(r_l')
    1942            0 :             CALL build_G_grid(bs_env, tau, ispin, .FALSE., .TRUE., mat_phi_mu_l, matrix_G_vir_grid, bs_env%eps_filter)
    1943              : 
    1944              :             ! -------------------------------------------------------------------
    1945              :             ! B. Element-wise Hadamard Products for Sigma_c on Grid
    1946              :             ! Σ_neg_grid = G_occ_grid ◦ W_grid
    1947              :             ! Σ_pos_grid = G_vir_grid ◦ W_grid
    1948              :             ! -------------------------------------------------------------------
    1949            0 :             CALL dbcsr_create(matrix_Sigma_neg_grid, template=matrix_W_grid)
    1950            0 :             CALL dbcsr_create(matrix_Sigma_pos_grid, template=matrix_W_grid)
    1951              : 
    1952              :             ! Σ^c_ll'(iτ,k=0)   = -G^occ_ll'(i|τ|,k=0) * W^MIC_ll'(iτ,k=0), for τ < 0
    1953            0 :             CALL hadamard_product(matrix_G_occ_grid, matrix_W_grid, matrix_Sigma_neg_grid, 1.0_dp)
    1954              : 
    1955              :             ! Σ^c_ll'(iτ,k=0)   =  G^vir_ll'(i|τ|,k=0) * W^MIC_ll'(iτ,k=0), for τ > 0
    1956            0 :             CALL hadamard_product(matrix_G_vir_grid, matrix_W_grid, matrix_Sigma_pos_grid, 1.0_dp)
    1957              : 
    1958              :             ! Instantly purge massive G_grid arrays to save memory
    1959            0 :             CALL dbcsr_release(matrix_G_occ_grid)
    1960            0 :             CALL dbcsr_release(matrix_G_vir_grid)
    1961              : 
    1962              :             ! -------------------------------------------------------------------
    1963              :             ! C. Transform Sigma back to AO Basis
    1964              :             ! Σ_AO = phi^T * Σ_grid * phi
    1965              :             ! -------------------------------------------------------------------
    1966              : 
    1967              :             ! Σ^c_λσ(iτ,k=0)    = sum_ll' Φ_λ(r_l) Σ^c_ll'(iτ,k=0) Φ_σ(r_l'), for τ < 0
    1968              :             CALL contract_A_B_A("T", "N", mat_phi_mu_l, matrix_Sigma_neg_grid, &
    1969            0 :                                 mat_Sigma_neg_tau(i_t, ispin)%matrix, bs_env%eps_filter)
    1970            0 :             CALL dbcsr_scale(mat_Sigma_neg_tau(i_t, ispin)%matrix, -1.0_dp)
    1971              : 
    1972              :             ! Σ^c_λσ(iτ,k=0)    = sum_ll' Φ_λ(r_l) Σ^c_ll'(iτ,k=0) Φ_σ(r_l'), for τ > 0
    1973              :             CALL contract_A_B_A("T", "N", mat_phi_mu_l, matrix_Sigma_pos_grid, &
    1974            0 :                                 mat_Sigma_pos_tau(i_t, ispin)%matrix, bs_env%eps_filter)
    1975              : 
    1976              :             ! Purge Grid Sigma arrays
    1977            0 :             CALL dbcsr_release(matrix_Sigma_neg_grid)
    1978            0 :             CALL dbcsr_release(matrix_Sigma_pos_grid)
    1979              : 
    1980            0 :             IF (bs_env%unit_nr > 0) THEN
    1981              :                WRITE (bs_env%unit_nr, '(T2,A,I10,A,I3,A,F7.1,A)') &
    1982            0 :                   'Computed Σ^c(iτ,k=0) for time point ', i_t, ' /', bs_env%num_time_freq_points, &
    1983            0 :                   ',    Execution time', m_walltime() - t1, ' s'
    1984              :             END IF
    1985              : 
    1986              :          END DO ! ispin
    1987              : 
    1988              :          ! Release the W_grid for this time point
    1989            0 :          CALL dbcsr_release(matrix_W_grid)
    1990              : 
    1991              :       END DO ! i_t
    1992              : 
    1993            0 :       IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
    1994              : 
    1995              :       ! -------------------------------------------------------------------------
    1996              :       ! 3. FINALIZE AND CLEANUP
    1997              :       ! -------------------------------------------------------------------------
    1998              :       CALL fill_fm_Sigma_c_Gamma_time(fm_Sigma_c_Gamma_time, bs_env, &
    1999            0 :                                       mat_Sigma_pos_tau, mat_Sigma_neg_tau)
    2000              : 
    2001            0 :       CALL cp_fm_release(fm_W_time)
    2002              : 
    2003            0 :       CALL dbcsr_deallocate_matrix_set(mat_Sigma_neg_tau)
    2004            0 :       CALL dbcsr_deallocate_matrix_set(mat_Sigma_pos_tau)
    2005              : 
    2006            0 :       CALL release_dbcsr_topology_and_matrices(dist=dist_grid_grid, mapped_dist=dist_col_grid)
    2007            0 :       CALL release_dbcsr_topology_and_matrices(dist=dist_aux_aux, mapped_dist=dist_row_aux)
    2008              : 
    2009            0 :       CALL delete_unnecessary_files(bs_env)
    2010            0 :       CALL timestop(handle)
    2011              : 
    2012            0 :    END SUBROUTINE compute_Sigma_c
    2013              : 
    2014              : ! **************************************************************************************************
    2015              : !> \brief DBCSR Topology Generation
    2016              : !> \param matrix_template ...
    2017              : !> \param dim_type ...
    2018              : !> \param square_dist ...
    2019              : !> \param blk_sizes ...
    2020              : !> \param mapped_dist ...
    2021              : ! **************************************************************************************************
    2022              : 
    2023        30444 :    SUBROUTINE setup_square_topology(matrix_template, dim_type, square_dist, blk_sizes, mapped_dist)
    2024              : 
    2025              :       TYPE(dbcsr_type), INTENT(IN)                       :: matrix_template
    2026              :       CHARACTER(LEN=*), INTENT(IN)                       :: dim_type
    2027              :       TYPE(dbcsr_distribution_type), INTENT(OUT)         :: square_dist
    2028              :       INTEGER, DIMENSION(:), INTENT(OUT), POINTER        :: blk_sizes, mapped_dist
    2029              : 
    2030              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'setup_square_topology'
    2031              : 
    2032              :       INTEGER                                            :: handle, i, np, npcols, nprows
    2033        10148 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk, col_dist, row_blk, row_dist
    2034              :       TYPE(dbcsr_distribution_type)                      :: dist_template
    2035              : 
    2036        10148 :       CALL timeset(routineN, handle)
    2037              : 
    2038              :       CALL dbcsr_get_info(matrix_template, distribution=dist_template, &
    2039        10148 :                           row_blk_size=row_blk, col_blk_size=col_blk)
    2040              :       CALL dbcsr_distribution_get(dist_template, row_dist=row_dist, col_dist=col_dist, &
    2041        10148 :                                   nprows=nprows, npcols=npcols)
    2042              : 
    2043        10148 :       IF (TRIM(dim_type) == 'ROW') THEN
    2044              :          ! Creates ROW x ROW (e.g., Grid x Grid from mat_phi_mu_l)
    2045         3964 :          blk_sizes => row_blk
    2046         3964 :          np = npcols
    2047        11892 :          ALLOCATE (mapped_dist(SIZE(blk_sizes)))
    2048        47228 :          DO i = 1, SIZE(blk_sizes)
    2049        47228 :             mapped_dist(i) = MOD(i - 1, np)
    2050              :          END DO
    2051              :          CALL dbcsr_distribution_new(square_dist, template=dist_template, &
    2052         3964 :                                      row_dist=row_dist, col_dist=mapped_dist)
    2053              : 
    2054         6184 :       ELSE IF (TRIM(dim_type) == 'COL') THEN
    2055              :          ! Creates COL x COL (e.g., Aux x Aux from mat_Z_lP)
    2056         6184 :          blk_sizes => col_blk
    2057         6184 :          np = nprows
    2058        18552 :          ALLOCATE (mapped_dist(SIZE(blk_sizes)))
    2059        18552 :          DO i = 1, SIZE(blk_sizes)
    2060        18552 :             mapped_dist(i) = MOD(i - 1, np)
    2061              :          END DO
    2062              :          CALL dbcsr_distribution_new(square_dist, template=dist_template, &
    2063         6184 :                                      row_dist=mapped_dist, col_dist=col_dist)
    2064              :       END IF
    2065              : 
    2066        10148 :       CALL timestop(handle)
    2067              : 
    2068        10148 :    END SUBROUTINE setup_square_topology
    2069              : 
    2070              : ! **************************************************************************************************
    2071              : !> \brief DBCSR matrices deallocation
    2072              : !> \param dist ...
    2073              : !> \param mapped_dist    ...
    2074              : !> \param m1 ...
    2075              : !> \param m2 ...
    2076              : !> \param m3 ...
    2077              : !> \param m4 ...
    2078              : ! **************************************************************************************************
    2079              : 
    2080        10148 :    SUBROUTINE release_dbcsr_topology_and_matrices(dist, mapped_dist, m1, m2, m3, m4)
    2081              : 
    2082              :       TYPE(dbcsr_distribution_type), INTENT(INOUT), &
    2083              :          OPTIONAL                                        :: dist
    2084              :       INTEGER, DIMENSION(:), INTENT(INOUT), OPTIONAL, &
    2085              :          POINTER                                         :: mapped_dist
    2086              :       TYPE(dbcsr_type), INTENT(INOUT), OPTIONAL          :: m1, m2, m3, m4
    2087              : 
    2088              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'release_dbcsr_topology_and_matrices'
    2089              : 
    2090              :       INTEGER                                            :: handle
    2091              : 
    2092        10148 :       CALL timeset(routineN, handle)
    2093              : 
    2094        10148 :       IF (PRESENT(dist)) CALL dbcsr_distribution_release(dist)
    2095        10148 :       IF (PRESENT(mapped_dist)) THEN
    2096        10148 :          IF (ASSOCIATED(mapped_dist)) THEN
    2097        10148 :             DEALLOCATE (mapped_dist)
    2098              :             NULLIFY (mapped_dist)
    2099              :          END IF
    2100              :       END IF
    2101        10148 :       IF (PRESENT(m1)) CALL dbcsr_release(m1)
    2102        10148 :       IF (PRESENT(m2)) CALL dbcsr_release(m2)
    2103        10148 :       IF (PRESENT(m3)) CALL dbcsr_release(m3)
    2104        10148 :       IF (PRESENT(m4)) CALL dbcsr_release(m4)
    2105              : 
    2106        10148 :       CALL timestop(handle)
    2107              : 
    2108        10148 :    END SUBROUTINE release_dbcsr_topology_and_matrices
    2109              : 
    2110              : END MODULE gw_large_cell_Gamma_ri_rs
        

Generated by: LCOV version 2.0-1