LCOV - code coverage report
Current view: top level - src - xas_tdp_integrals.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9dda3dd) Lines: 97.6 % 535 522
Test Date: 2026-06-13 06:49:54 Functions: 100.0 % 10 10

            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 3-center integrals machinery for the XAS_TDP method
      10              : !> \author A. Bussy (03.2020)
      11              : ! **************************************************************************************************
      12              : 
      13              : MODULE xas_tdp_integrals
      14              :    USE OMP_LIB, ONLY: omp_get_max_threads, &
      15              :                       omp_get_num_threads, &
      16              :                       omp_get_thread_num
      17              :    USE ai_contraction_sphi, ONLY: ab_contract, &
      18              :                                   abc_contract_xsmm
      19              :    USE atomic_kind_types, ONLY: atomic_kind_type
      20              :    USE basis_set_types, ONLY: get_gto_basis_set, &
      21              :                               gto_basis_set_p_type, &
      22              :                               gto_basis_set_type
      23              :    USE cell_types, ONLY: cell_type
      24              :    USE constants_operator, ONLY: operator_coulomb
      25              :    USE cp_array_utils, ONLY: cp_1d_i_p_type, &
      26              :                              cp_2d_r_p_type
      27              :    USE cp_blacs_env, ONLY: cp_blacs_env_type
      28              :    USE cp_dbcsr_api, ONLY: dbcsr_distribution_get, &
      29              :                            dbcsr_distribution_release, &
      30              :                            dbcsr_distribution_type
      31              :    USE cp_dbcsr_operations, ONLY: cp_dbcsr_dist2d_to_dist
      32              :    USE cp_eri_mme_interface, ONLY: cp_eri_mme_param, &
      33              :                                    cp_eri_mme_set_params
      34              :    USE cp_files, ONLY: close_file, &
      35              :                        open_file
      36              :    USE dbt_api, ONLY: &
      37              :       dbt_create, dbt_distribution_destroy, dbt_distribution_new, dbt_distribution_type, &
      38              :       dbt_finalize, dbt_pgrid_create, dbt_pgrid_destroy, dbt_pgrid_type, dbt_put_block, &
      39              :       dbt_reserve_blocks, dbt_type
      40              :    USE distribution_1d_types, ONLY: distribution_1d_type
      41              :    USE distribution_2d_types, ONLY: distribution_2d_create, &
      42              :                                     distribution_2d_type
      43              :    USE eri_mme_integrate, ONLY: eri_mme_2c_integrate
      44              :    USE eri_mme_types, ONLY: eri_mme_init, &
      45              :                             eri_mme_release
      46              :    USE gamma, ONLY: init_md_ftable
      47              :    USE generic_os_integrals, ONLY: int_operators_r12_ab_os
      48              :    USE input_constants, ONLY: do_potential_coulomb, &
      49              :                               do_potential_id, &
      50              :                               do_potential_short, &
      51              :                               do_potential_truncated
      52              :    USE input_section_types, ONLY: section_vals_val_get
      53              :    USE kinds, ONLY: dp
      54              :    USE libint_2c_3c, ONLY: cutoff_screen_factor, &
      55              :                            eri_2center, &
      56              :                            eri_3center, &
      57              :                            libint_potential_type
      58              :    USE libint_wrapper, ONLY: cp_libint_cleanup_2eri, &
      59              :                              cp_libint_cleanup_3eri, &
      60              :                              cp_libint_init_2eri, &
      61              :                              cp_libint_init_3eri, &
      62              :                              cp_libint_set_contrdepth, &
      63              :                              cp_libint_t
      64              :    USE mathlib, ONLY: invmat_symm
      65              :    USE message_passing, ONLY: mp_comm_type, &
      66              :                               mp_para_env_type
      67              :    USE molecule_types, ONLY: molecule_type
      68              :    USE orbital_pointers, ONLY: ncoset
      69              :    USE particle_methods, ONLY: get_particle_set
      70              :    USE particle_types, ONLY: particle_type
      71              :    USE qs_environment_types, ONLY: get_qs_env, &
      72              :                                    qs_environment_type
      73              :    USE qs_integral_utils, ONLY: basis_set_list_setup
      74              :    USE qs_kind_types, ONLY: get_qs_kind, &
      75              :                             qs_kind_type
      76              :    USE qs_neighbor_list_types, ONLY: &
      77              :       get_iterator_info, get_neighbor_list_set_p, neighbor_list_iterate, &
      78              :       neighbor_list_iterator_create, neighbor_list_iterator_p_type, &
      79              :       neighbor_list_iterator_release, neighbor_list_set_p_type, nl_set_sub_iterator, &
      80              :       nl_sub_iterate, release_neighbor_list_sets
      81              :    USE qs_neighbor_lists, ONLY: atom2d_build, &
      82              :                                 atom2d_cleanup, &
      83              :                                 build_neighbor_lists, &
      84              :                                 local_atoms_type, &
      85              :                                 pair_radius_setup
      86              :    USE qs_o3c_types, ONLY: get_o3c_iterator_info, &
      87              :                            init_o3c_container, &
      88              :                            o3c_container_type, &
      89              :                            o3c_iterate, &
      90              :                            o3c_iterator_create, &
      91              :                            o3c_iterator_release, &
      92              :                            o3c_iterator_type, &
      93              :                            release_o3c_container
      94              :    USE t_c_g0, ONLY: get_lmax_init, &
      95              :                      init
      96              :    USE xas_tdp_types, ONLY: xas_tdp_control_type, &
      97              :                             xas_tdp_env_type
      98              : #if defined(__LIBXS)
      99              :    USE ISO_C_BINDING, ONLY: C_LOC
     100              :    USE libxs, ONLY: libxs_matcopy, &
     101              :                     libxs_otrans
     102              : #endif
     103              : #include "./base/base_uses.f90"
     104              : 
     105              :    IMPLICIT NONE
     106              :    PRIVATE
     107              : 
     108              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'xas_tdp_integrals'
     109              : 
     110              :    PUBLIC :: create_pqX_tensor, fill_pqX_tensor, compute_ri_3c_coulomb, compute_ri_3c_exchange, &
     111              :              build_xas_tdp_3c_nl, build_xas_tdp_ovlp_nl, get_opt_3c_dist2d, &
     112              :              compute_ri_coulomb2_int, compute_ri_exchange2_int
     113              : 
     114              : CONTAINS
     115              : 
     116              : ! **************************************************************************************************
     117              : !> \brief Prepares a tensor to hold 3-center integrals (pq|X), where p,q are distributed according
     118              : !>        to the given 2d dbcsr distribution of the given . The third dimension of the tensor is
     119              : !>        iteslf not distributed (i.e. the t_pgrid's third dimension has size 1). The blocks are
     120              : !>        reserved according to the neighbor lists
     121              : !> \param pq_X the tensor to store the integrals
     122              : !> \param ab_nl the 1st and 2nd center neighbor list
     123              : !> \param ac_nl the 1st and 3rd center neighbor list
     124              : !> \param matrix_dist ...
     125              : !> \param blk_size_1 the block size in the first dimension
     126              : !> \param blk_size_2 the block size in the second dimension
     127              : !> \param blk_size_3 the block size in the third dimension
     128              : !> \param only_bc_same_center only keep block if, for the corresponding integral (ab|c), b and c
     129              : !>        share the same center, i.e. r_bc = 0.0
     130              : ! **************************************************************************************************
     131         2024 :    SUBROUTINE create_pqX_tensor(pq_X, ab_nl, ac_nl, matrix_dist, blk_size_1, blk_size_2, &
     132          184 :                                 blk_size_3, only_bc_same_center)
     133              : 
     134              :       TYPE(dbt_type), INTENT(OUT)                        :: pq_X
     135              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     136              :          POINTER                                         :: ab_nl, ac_nl
     137              :       TYPE(dbcsr_distribution_type), INTENT(IN)          :: matrix_dist
     138              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: blk_size_1, blk_size_2, blk_size_3
     139              :       LOGICAL, INTENT(IN), OPTIONAL                      :: only_bc_same_center
     140              : 
     141              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'create_pqX_tensor'
     142              : 
     143              :       INTEGER                                            :: A, b, group_handle, handle, i, iatom, &
     144              :                                                             ikind, jatom, katom, kkind, nblk, &
     145              :                                                             nblk_3, nblk_per_thread, nkind
     146          184 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: idx1, idx2, idx3
     147              :       INTEGER, DIMENSION(3)                              :: pdims
     148          184 :       INTEGER, DIMENSION(:), POINTER                     :: col_dist, row_dist
     149          184 :       INTEGER, DIMENSION(:, :), POINTER                  :: mat_pgrid
     150              :       LOGICAL                                            :: my_sort_bc, symmetric
     151              :       REAL(dp), DIMENSION(3)                             :: rab, rac, rbc
     152         1656 :       TYPE(dbt_distribution_type)                        :: t_dist
     153          552 :       TYPE(dbt_pgrid_type)                               :: t_pgrid
     154              :       TYPE(mp_comm_type)                                 :: group
     155              :       TYPE(neighbor_list_iterator_p_type), &
     156          184 :          DIMENSION(:), POINTER                           :: ab_iter, ac_iter
     157              : 
     158          184 :       NULLIFY (ab_iter, ac_iter, col_dist, row_dist, mat_pgrid)
     159              : 
     160          184 :       CALL timeset(routineN, handle)
     161              : 
     162          184 :       my_sort_bc = .FALSE.
     163          184 :       IF (PRESENT(only_bc_same_center)) my_sort_bc = only_bc_same_center
     164              : 
     165          184 :       CALL get_neighbor_list_set_p(ab_nl, symmetric=symmetric)
     166          184 :       CPASSERT(symmetric)
     167              : 
     168              :       !get 2D distribution info from matrix_dist
     169              :       CALL dbcsr_distribution_get(matrix_dist, pgrid=mat_pgrid, group=group_handle, &
     170          184 :                                   row_dist=row_dist, col_dist=col_dist)
     171          184 :       CALL group%set_handle(group_handle)
     172              : 
     173              :       !create the corresponding tensor proc grid and dist
     174          184 :       pdims(1) = SIZE(mat_pgrid, 1); pdims(2) = SIZE(mat_pgrid, 2); pdims(3) = 1
     175          184 :       CALL dbt_pgrid_create(group, pdims, t_pgrid)
     176              : 
     177          184 :       nblk_3 = SIZE(blk_size_3)
     178              :       CALL dbt_distribution_new(t_dist, t_pgrid, nd_dist_1=row_dist, nd_dist_2=col_dist, &
     179         2964 :                                 nd_dist_3=[(0, i=1, nblk_3)])
     180              : 
     181              :       !create the tensor itself.
     182              :       CALL dbt_create(pq_X, name="(pq|X)", dist=t_dist, map1_2d=[1, 2], map2_2d=[3], &
     183          184 :                       blk_size_1=blk_size_1, blk_size_2=blk_size_2, blk_size_3=blk_size_3)
     184              : 
     185              :       !count the blocks to reserve !note: dbcsr takes care of only keeping unique indices
     186          184 :       CALL neighbor_list_iterator_create(ab_iter, ab_nl)
     187          184 :       CALL neighbor_list_iterator_create(ac_iter, ac_nl, search=.TRUE.)
     188          184 :       nblk = 0
     189        17538 :       DO WHILE (neighbor_list_iterate(ab_iter) == 0)
     190        17354 :          CALL get_iterator_info(ab_iter, ikind=ikind, iatom=iatom, nkind=nkind, r=rab)
     191              : 
     192        51004 :          DO kkind = 1, nkind
     193        33466 :             CALL nl_set_sub_iterator(ac_iter, ikind, kkind, iatom)
     194              : 
     195        72362 :             DO WHILE (nl_sub_iterate(ac_iter) == 0)
     196              : 
     197        21542 :                IF (my_sort_bc) THEN
     198              :                   !we check for rbc or rac because of symmetry in ab_nl
     199          724 :                   CALL get_iterator_info(ac_iter, r=rac)
     200         2896 :                   rbc(:) = rac(:) - rab(:)
     201         3196 :                   IF (.NOT. (ALL(ABS(rbc) <= 1.0E-8_dp) .OR. ALL(ABS(rac) <= 1.0E-8_dp))) CYCLE
     202              : 
     203              :                END IF
     204              : 
     205        21542 :                nblk = nblk + 1
     206              :             END DO !ac_iter
     207              :          END DO !kkind
     208              :       END DO !ab_iter
     209          184 :       CALL neighbor_list_iterator_release(ab_iter)
     210          184 :       CALL neighbor_list_iterator_release(ac_iter)
     211              : 
     212          833 :       ALLOCATE (idx1(nblk), idx2(nblk), idx3(nblk))
     213              : 
     214              :       !actually reserve the blocks
     215          184 :       CALL neighbor_list_iterator_create(ab_iter, ab_nl)
     216          184 :       CALL neighbor_list_iterator_create(ac_iter, ac_nl, search=.TRUE.)
     217          184 :       nblk = 0
     218        17538 :       DO WHILE (neighbor_list_iterate(ab_iter) == 0)
     219        17354 :          CALL get_iterator_info(ab_iter, ikind=ikind, iatom=iatom, jatom=jatom, nkind=nkind, r=rab)
     220              : 
     221        51004 :          DO kkind = 1, nkind
     222        33466 :             CALL nl_set_sub_iterator(ac_iter, ikind, kkind, iatom)
     223              : 
     224        72362 :             DO WHILE (nl_sub_iterate(ac_iter) == 0)
     225        21542 :                CALL get_iterator_info(ac_iter, jatom=katom, r=rac)
     226              : 
     227        21542 :                IF (my_sort_bc) THEN
     228              :                   !we check for rbc or rac because of symmetry in ab_nl
     229          724 :                   CALL get_iterator_info(ac_iter, r=rac)
     230         2896 :                   rbc(:) = rac(:) - rab(:)
     231         3196 :                   IF (.NOT. (ALL(ABS(rbc) <= 1.0E-8_dp) .OR. ALL(ABS(rac) <= 1.0E-8_dp))) CYCLE
     232              : 
     233              :                END IF
     234              : 
     235        21248 :                nblk = nblk + 1
     236              : 
     237        21248 :                idx1(nblk) = iatom
     238        21248 :                idx2(nblk) = jatom
     239        21542 :                idx3(nblk) = katom
     240              : 
     241              :             END DO !ac_iter
     242              :          END DO !kkind
     243              :       END DO !ab_iter
     244          184 :       CALL neighbor_list_iterator_release(ab_iter)
     245          184 :       CALL neighbor_list_iterator_release(ac_iter)
     246              : 
     247              : !TODO: Parallelize creation of block list.
     248          184 : !$OMP PARALLEL DEFAULT(NONE) SHARED(pq_X, nblk, idx1, idx2, idx3) PRIVATE(nblk_per_thread,A,b)
     249              :       nblk_per_thread = nblk/omp_get_num_threads() + 1
     250              :       a = omp_get_thread_num()*nblk_per_thread + 1
     251              :       b = MIN(a + nblk_per_thread, nblk)
     252              :       CALL dbt_reserve_blocks(pq_X, idx1(a:b), idx2(a:b), idx3(a:b))
     253              : !$OMP END PARALLEL
     254          184 :       CALL dbt_finalize(pq_X)
     255              : 
     256              :       !clean-up
     257          184 :       CALL dbt_distribution_destroy(t_dist)
     258          184 :       CALL dbt_pgrid_destroy(t_pgrid)
     259              : 
     260          184 :       CALL timestop(handle)
     261              : 
     262          920 :    END SUBROUTINE create_pqX_tensor
     263              : 
     264              : ! **************************************************************************************************
     265              : !> \brief Fills the given 3 dimensional (pq|X) tensor with integrals
     266              : !> \param pq_X the tensor to fill
     267              : !> \param ab_nl the neighbor list for the first 2 centers
     268              : !> \param ac_nl the neighbor list for the first and third centers
     269              : !> \param basis_set_list_a basis sets for first center
     270              : !> \param basis_set_list_b basis sets for second center
     271              : !> \param basis_set_list_c basis sets for third center
     272              : !> \param potential_parameter the operator for the integrals
     273              : !> \param qs_env ...
     274              : !> \param only_bc_same_center same as in create_pqX_tensor
     275              : !> \param eps_screen threshold for possible screening
     276              : !> \note The following indices are happily mixed within this routine: First center i,a,p
     277              : !>       Second center: j,b,q       Third center: k,c,X
     278              : ! **************************************************************************************************
     279          184 :    SUBROUTINE fill_pqX_tensor(pq_X, ab_nl, ac_nl, basis_set_list_a, basis_set_list_b, &
     280              :                               basis_set_list_c, potential_parameter, qs_env, &
     281              :                               only_bc_same_center, eps_screen)
     282              : 
     283              :       TYPE(dbt_type)                                     :: pq_X
     284              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     285              :          POINTER                                         :: ab_nl, ac_nl
     286              :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_set_list_a, basis_set_list_b, &
     287              :                                                             basis_set_list_c
     288              :       TYPE(libint_potential_type)                        :: potential_parameter
     289              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     290              :       LOGICAL, INTENT(IN), OPTIONAL                      :: only_bc_same_center
     291              :       REAL(dp), INTENT(IN), OPTIONAL                     :: eps_screen
     292              : 
     293              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'fill_pqX_tensor'
     294              : 
     295              :       INTEGER :: egfa, egfb, egfc, handle, i, iatom, ibasis, ikind, ilist, imax, iset, jatom, &
     296              :          jkind, jset, katom, kkind, kset, m_max, max_nset, maxli, maxlj, maxlk, mepos, nbasis, &
     297              :          ncoa, ncob, ncoc, ni, nj, nk, nseta, nsetb, nsetc, nthread, sgfa, sgfb, sgfc, unit_id
     298          184 :       INTEGER, DIMENSION(:), POINTER                     :: la_max, la_min, lb_max, lb_min, lc_max, &
     299          184 :                                                             lc_min, npgfa, npgfb, npgfc, nsgfa, &
     300          184 :                                                             nsgfb, nsgfc
     301          184 :       INTEGER, DIMENSION(:, :), POINTER                  :: first_sgfa, first_sgfb, first_sgfc
     302              :       LOGICAL                                            :: do_screen, my_sort_bc
     303              :       REAL(dp)                                           :: dij, dik, djk, my_eps_screen, ri(3), &
     304              :                                                             rij(3), rik(3), rj(3), rjk(3), rk(3), &
     305              :                                                             sabc_ext, screen_radius
     306          184 :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: ccp_buffer, cpp_buffer
     307          184 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: max_contr, max_contra, max_contrb, &
     308          184 :                                                             max_contrc
     309          184 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :, :)          :: iabc, sabc, work
     310          184 :       REAL(dp), DIMENSION(:), POINTER                    :: set_radius_a, set_radius_b, set_radius_c
     311          184 :       REAL(dp), DIMENSION(:, :), POINTER                 :: rpgf_a, rpgf_b, rpgf_c, zeta, zetb, zetc
     312          184 :       TYPE(cp_2d_r_p_type), DIMENSION(:, :), POINTER     :: spb, spc, tspa
     313              :       TYPE(cp_libint_t)                                  :: lib
     314          184 :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_set_list
     315              :       TYPE(gto_basis_set_type), POINTER                  :: basis_set, basis_set_a, basis_set_b, &
     316              :                                                             basis_set_c
     317              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     318              :       TYPE(o3c_container_type), POINTER                  :: o3c
     319              :       TYPE(o3c_iterator_type)                            :: o3c_iterator
     320              : 
     321          184 :       NULLIFY (basis_set, basis_set_list, para_env, la_max, la_min)
     322          184 :       NULLIFY (lb_max, lb_min, lc_max, lc_min, npgfa, npgfb, npgfc, nsgfa, nsgfb, nsgfc)
     323          184 :       NULLIFY (first_sgfa, first_sgfb, first_sgfc, set_radius_a, set_radius_b, set_radius_c)
     324          184 :       NULLIFY (rpgf_a, rpgf_b, rpgf_c, zeta, zetb, zetc)
     325          184 :       NULLIFY (basis_set_a, basis_set_b, basis_set_c, tspa, spb, spc)
     326              : 
     327          184 :       CALL timeset(routineN, handle)
     328              : 
     329              :       !Need the max l for each basis for libint (and overall max #of sets for screening)
     330          184 :       nbasis = SIZE(basis_set_list_a)
     331          184 :       max_nset = 0
     332          184 :       maxli = 0
     333          492 :       DO ibasis = 1, nbasis
     334              :          CALL get_gto_basis_set(gto_basis_set=basis_set_list_a(ibasis)%gto_basis_set, &
     335          308 :                                 maxl=imax, nset=iset, nsgf_set=nsgfa)
     336          308 :          maxli = MAX(maxli, imax)
     337          492 :          max_nset = MAX(max_nset, iset)
     338              :       END DO
     339              :       maxlj = 0
     340          492 :       DO ibasis = 1, nbasis
     341              :          CALL get_gto_basis_set(gto_basis_set=basis_set_list_b(ibasis)%gto_basis_set, &
     342          308 :                                 maxl=imax, nset=iset, nsgf_set=nsgfb, npgf=npgfb)
     343          308 :          maxlj = MAX(maxlj, imax)
     344          492 :          max_nset = MAX(max_nset, iset)
     345              :       END DO
     346              :       maxlk = 0
     347          492 :       DO ibasis = 1, nbasis
     348              :          CALL get_gto_basis_set(gto_basis_set=basis_set_list_c(ibasis)%gto_basis_set, &
     349          308 :                                 maxl=imax, nset=iset, npgf=npgfc)
     350          308 :          maxlk = MAX(maxlk, imax)
     351          492 :          max_nset = MAX(max_nset, iset)
     352              :       END DO
     353          184 :       m_max = maxli + maxlj + maxlk
     354              : 
     355              :       !Screening
     356          184 :       do_screen = .FALSE.
     357          184 :       IF (PRESENT(eps_screen)) THEN
     358           54 :          do_screen = .TRUE.
     359           54 :          my_eps_screen = eps_screen
     360              :       END IF
     361          184 :       screen_radius = 0.0_dp
     362          184 :       IF (potential_parameter%potential_type == do_potential_truncated .OR. &
     363              :           potential_parameter%potential_type == do_potential_short) THEN
     364              : 
     365           12 :          screen_radius = potential_parameter%cutoff_radius*cutoff_screen_factor
     366          172 :       ELSE IF (potential_parameter%potential_type == do_potential_coulomb) THEN
     367              : 
     368          102 :          screen_radius = 1000000.0_dp
     369              :       END IF
     370              : 
     371              :       !get maximum contraction values for abc_contract screening
     372          184 :       IF (do_screen) THEN
     373              : 
     374              :          !Allocate max_contraction arrays such that we have a specific value for each set/kind
     375            0 :          ALLOCATE (max_contr(max_nset, nbasis), max_contra(max_nset, nbasis), &
     376          540 :                    max_contrb(max_nset, nbasis), max_contrc(max_nset, nbasis))
     377              : 
     378              :          !Not the most elegent, but better than copying 3 times the same
     379          216 :          DO ilist = 1, 3
     380              : 
     381          162 :             IF (ilist == 1) basis_set_list => basis_set_list_a
     382          162 :             IF (ilist == 2) basis_set_list => basis_set_list_b
     383          162 :             IF (ilist == 3) basis_set_list => basis_set_list_c
     384              : 
     385          162 :             max_contr = 0.0_dp
     386              : 
     387          438 :             DO ibasis = 1, nbasis
     388          276 :                basis_set => basis_set_list(ibasis)%gto_basis_set
     389              : 
     390         1326 :                DO iset = 1, basis_set%nset
     391              : 
     392          888 :                   ncoa = basis_set%npgf(iset)*ncoset(basis_set%lmax(iset))
     393          888 :                   sgfa = basis_set%first_sgf(1, iset)
     394          888 :                   egfa = sgfa + basis_set%nsgf_set(iset) - 1
     395              : 
     396              :                   max_contr(iset, ibasis) = &
     397       561714 :                      MAXVAL([(SUM(ABS(basis_set%sphi(1:ncoa, i))), i=sgfa, egfa)])
     398              : 
     399              :                END DO !iset
     400              :             END DO !ibasis
     401              : 
     402          700 :             IF (ilist == 1) max_contra(:, :) = max_contr(:, :)
     403          700 :             IF (ilist == 2) max_contrb(:, :) = max_contr(:, :)
     404          754 :             IF (ilist == 3) max_contrc(:, :) = max_contr(:, :)
     405              :          END DO !ilist
     406           54 :          DEALLOCATE (max_contr)
     407              :       END IF !do_screen
     408              : 
     409              :       !To minimize memory ops in contraction, we need to pre-allocate buffers, pre-tranpose sphi_a
     410              :       !and also trim sphi in general to have contiguous arrays
     411         6872 :       ALLOCATE (tspa(max_nset, nbasis), spb(max_nset, nbasis), spc(max_nset, nbasis))
     412          492 :       DO ibasis = 1, nbasis
     413         1984 :          DO iset = 1, max_nset
     414         1492 :             NULLIFY (tspa(iset, ibasis)%array)
     415         1492 :             NULLIFY (spb(iset, ibasis)%array)
     416         1800 :             NULLIFY (spc(iset, ibasis)%array)
     417              :          END DO
     418              :       END DO
     419              : 
     420          736 :       DO ilist = 1, 3
     421              : 
     422         1660 :          DO ibasis = 1, nbasis
     423          924 :             IF (ilist == 1) basis_set => basis_set_list_a(ibasis)%gto_basis_set
     424          924 :             IF (ilist == 2) basis_set => basis_set_list_b(ibasis)%gto_basis_set
     425          924 :             IF (ilist == 3) basis_set => basis_set_list_c(ibasis)%gto_basis_set
     426              : 
     427         4536 :             DO iset = 1, basis_set%nset
     428              : 
     429         3060 :                ncoa = basis_set%npgf(iset)*ncoset(basis_set%lmax(iset))
     430         3060 :                sgfa = basis_set%first_sgf(1, iset)
     431         3060 :                egfa = sgfa + basis_set%nsgf_set(iset) - 1
     432              : 
     433         3984 :                IF (ilist == 1) THEN
     434         4584 :                   ALLOCATE (tspa(iset, ibasis)%array(basis_set%nsgf_set(iset), ncoa))
     435              : #if defined(__LIBXS)
     436              :                   CALL libxs_otrans(C_LOC(tspa(iset, ibasis)%array), &
     437              :                                     C_LOC(basis_set%sphi(1, sgfa)), &
     438              :                                     8, ncoa, egfa - sgfa + 1, &
     439              :                                     SIZE(basis_set%sphi, 1), &
     440         1146 :                                     basis_set%nsgf_set(iset))
     441              : #else
     442              :                   tspa(iset, ibasis)%array(:, :) = TRANSPOSE(basis_set%sphi(1:ncoa, sgfa:egfa))
     443              : #endif
     444         1914 :                ELSE IF (ilist == 2) THEN
     445         4584 :                   ALLOCATE (spb(iset, ibasis)%array(ncoa, basis_set%nsgf_set(iset)))
     446              : #if defined(__LIBXS)
     447              :                   CALL libxs_matcopy(C_LOC(spb(iset, ibasis)%array), &
     448              :                                      C_LOC(basis_set%sphi(1, sgfa)), &
     449              :                                      8, ncoa, egfa - sgfa + 1, &
     450         1146 :                                      SIZE(basis_set%sphi, 1), ncoa)
     451              : #else
     452              :                   spb(iset, ibasis)%array(:, :) = basis_set%sphi(1:ncoa, sgfa:egfa)
     453              : #endif
     454              :                ELSE
     455         3072 :                   ALLOCATE (spc(iset, ibasis)%array(ncoa, basis_set%nsgf_set(iset)))
     456              : #if defined(__LIBXS)
     457              :                   CALL libxs_matcopy(C_LOC(spc(iset, ibasis)%array), &
     458              :                                      C_LOC(basis_set%sphi(1, sgfa)), &
     459              :                                      8, ncoa, egfa - sgfa + 1, &
     460          768 :                                      SIZE(basis_set%sphi, 1), ncoa)
     461              : #else
     462              :                   spc(iset, ibasis)%array(:, :) = basis_set%sphi(1:ncoa, sgfa:egfa)
     463              : #endif
     464              :                END IF
     465              : 
     466              :             END DO !iset
     467              :          END DO !ibasis
     468              :       END DO !ilist
     469              : 
     470          184 :       my_sort_bc = .FALSE.
     471          184 :       IF (PRESENT(only_bc_same_center)) my_sort_bc = only_bc_same_center
     472              : 
     473              :       !Init the truncated Coulomb operator
     474          184 :       CALL get_qs_env(qs_env, para_env=para_env)
     475          184 :       IF (potential_parameter%potential_type == do_potential_truncated) THEN
     476              : 
     477              :          !open the file only if necessary
     478            6 :          IF (m_max > get_lmax_init()) THEN
     479            0 :             IF (para_env%mepos == 0) THEN
     480            0 :                CALL open_file(unit_number=unit_id, file_name=potential_parameter%filename)
     481              :             END IF
     482            0 :             CALL init(m_max, unit_id, para_env%mepos, para_env)
     483            0 :             IF (para_env%mepos == 0) THEN
     484            0 :                CALL close_file(unit_id)
     485              :             END IF
     486              :          END IF
     487              :       END IF
     488              : 
     489              :       !Inint the initial gamma function before the OMP region as it is not thread safe
     490          184 :       CALL init_md_ftable(nmax=m_max)
     491              : 
     492              :       !Strategy: we use the o3c iterator because it is OMP parallelized and also takes the
     493              :       !          only_bc_same_center argument. Only the dbcsr_put_block is critical
     494              : 
     495              :       nthread = 1
     496          184 : !$    nthread = omp_get_max_threads()
     497              : 
     498          184 :       ALLOCATE (o3c)
     499              :       CALL init_o3c_container(o3c, 1, basis_set_list_a, basis_set_list_b, basis_set_list_c, &
     500          184 :                               ab_nl, ac_nl, only_bc_same_center=my_sort_bc)
     501          184 :       CALL o3c_iterator_create(o3c, o3c_iterator, nthread=nthread)
     502              : 
     503              : !$OMP PARALLEL DEFAULT(NONE) &
     504              : !$OMP SHARED (pq_X,do_screen,max_nset,basis_set_list_a,max_contra,max_contrb,max_contrc,&
     505              : !$OMP         basis_set_list_b, basis_set_list_c,ncoset,screen_radius,potential_parameter,&
     506              : !$OMP         my_eps_screen,maxli,maxlj,maxlk,my_sort_bc,nthread,o3c,o3c_iterator,tspa,spb,spc) &
     507              : !$OMP PRIVATE (lib,i,mepos,work,iset,ncoa,sgfa,egfa,nseta,iatom,ikind,jatom,jkind,katom,kkind,&
     508              : !$OMP          rij,rik,rjk,basis_set_a,nsetb,la_max,la_min,lb_max,lb_min,lc_max,lc_min,npgfa,&
     509              : !$OMP          npgfb,npgfc,nsgfa,nsgfb,nsgfc,ri,rk,first_sgfa,first_sgfb,first_sgfc,nsetc,&
     510              : !$OMP          set_radius_a,set_radius_b,set_radius_c,rj,rpgf_a,rpgf_b,rpgf_c,zeta,zetb,&
     511              : !$OMP          zetc,basis_set_b,basis_set_c,dij,dik,djk,ni,nj,nk,iabc,sabc,jset,kset,&
     512          184 : !$OMP          ncob,ncoc,sgfb,sgfc,egfb,egfc,sabc_ext,cpp_buffer,ccp_buffer)
     513              : 
     514              :       mepos = 0
     515              : !$    mepos = omp_get_thread_num()
     516              : 
     517              :       !each thread need its own libint object (internals may change at different rates)
     518              :       CALL cp_libint_init_3eri(lib, MAX(maxli, maxlj, maxlk))
     519              :       CALL cp_libint_set_contrdepth(lib, 1)
     520              : 
     521              :       DO WHILE (o3c_iterate(o3c_iterator, mepos=mepos) == 0)
     522              :          CALL get_o3c_iterator_info(o3c_iterator, mepos=mepos, ikind=ikind, jkind=jkind, kkind=kkind, &
     523              :                                     iatom=iatom, jatom=jatom, katom=katom, rij=rij, rik=rik)
     524              : 
     525              :          !get first center basis info
     526              :          basis_set_a => basis_set_list_a(ikind)%gto_basis_set
     527              :          first_sgfa => basis_set_a%first_sgf
     528              :          la_max => basis_set_a%lmax
     529              :          la_min => basis_set_a%lmin
     530              :          npgfa => basis_set_a%npgf
     531              :          nseta = basis_set_a%nset
     532              :          nsgfa => basis_set_a%nsgf_set
     533              :          zeta => basis_set_a%zet
     534              :          rpgf_a => basis_set_a%pgf_radius
     535              :          set_radius_a => basis_set_a%set_radius
     536              :          ni = SUM(nsgfa)
     537              :          !second center basis info
     538              :          basis_set_b => basis_set_list_b(jkind)%gto_basis_set
     539              :          first_sgfb => basis_set_b%first_sgf
     540              :          lb_max => basis_set_b%lmax
     541              :          lb_min => basis_set_b%lmin
     542              :          npgfb => basis_set_b%npgf
     543              :          nsetb = basis_set_b%nset
     544              :          nsgfb => basis_set_b%nsgf_set
     545              :          zetb => basis_set_b%zet
     546              :          rpgf_b => basis_set_b%pgf_radius
     547              :          set_radius_b => basis_set_b%set_radius
     548              :          nj = SUM(nsgfb)
     549              :          !third center basis info
     550              :          basis_set_c => basis_set_list_c(kkind)%gto_basis_set
     551              :          first_sgfc => basis_set_c%first_sgf
     552              :          lc_max => basis_set_c%lmax
     553              :          lc_min => basis_set_c%lmin
     554              :          npgfc => basis_set_c%npgf
     555              :          nsetc = basis_set_c%nset
     556              :          nsgfc => basis_set_c%nsgf_set
     557              :          zetc => basis_set_c%zet
     558              :          rpgf_c => basis_set_c%pgf_radius
     559              :          set_radius_c => basis_set_c%set_radius
     560              :          nk = SUM(nsgfc)
     561              : 
     562              :          !position and distances, only relative pos matter for libint
     563              :          rjk = rik - rij
     564              :          ri = 0.0_dp
     565              :          rj = rij ! ri + rij
     566              :          rk = rik ! ri + rik
     567              : 
     568              :          djk = NORM2(rjk)
     569              :          dij = NORM2(rij)
     570              :          dik = NORM2(rik)
     571              : 
     572              :          !sgf integrals
     573              :          ALLOCATE (iabc(ni, nj, nk))
     574              :          iabc(:, :, :) = 0.0_dp
     575              : 
     576              :          DO iset = 1, nseta
     577              :             ncoa = npgfa(iset)*ncoset(la_max(iset))
     578              :             sgfa = first_sgfa(1, iset)
     579              :             egfa = sgfa + nsgfa(iset) - 1
     580              : 
     581              :             DO jset = 1, nsetb
     582              :                ncob = npgfb(jset)*ncoset(lb_max(jset))
     583              :                sgfb = first_sgfb(1, jset)
     584              :                egfb = sgfb + nsgfb(jset) - 1
     585              : 
     586              :                !screening (overlap)
     587              :                IF (set_radius_a(iset) + set_radius_b(jset) < dij) CYCLE
     588              : 
     589              :                DO kset = 1, nsetc
     590              :                   ncoc = npgfc(kset)*ncoset(lc_max(kset))
     591              :                   sgfc = first_sgfc(1, kset)
     592              :                   egfc = sgfc + nsgfc(kset) - 1
     593              : 
     594              :                   !screening (potential)
     595              :                   IF (set_radius_a(iset) + set_radius_c(kset) + screen_radius < dik) CYCLE
     596              :                   IF (set_radius_b(jset) + set_radius_c(kset) + screen_radius < djk) CYCLE
     597              : 
     598              :                   !pgf integrals
     599              :                   ALLOCATE (sabc(ncoa, ncob, ncoc))
     600              :                   sabc(:, :, :) = 0.0_dp
     601              : 
     602              :                   IF (do_screen) THEN
     603              :                      CALL eri_3center(sabc, la_min(iset), la_max(iset), npgfa(iset), zeta(:, iset), &
     604              :                                       rpgf_a(:, iset), ri, lb_min(jset), lb_max(jset), npgfb(jset), &
     605              :                                       zetb(:, jset), rpgf_b(:, jset), rj, lc_min(kset), lc_max(kset), &
     606              :                                       npgfc(kset), zetc(:, kset), rpgf_c(:, kset), rk, dij, dik, &
     607              :                                       djk, lib, potential_parameter, int_abc_ext=sabc_ext)
     608              :                      IF (my_eps_screen > sabc_ext*(max_contra(iset, ikind)* &
     609              :                                                    max_contrb(jset, jkind)* &
     610              :                                                    max_contrc(kset, kkind))) THEN
     611              :                         DEALLOCATE (sabc)
     612              :                         CYCLE
     613              :                      END IF
     614              :                   ELSE
     615              :                      CALL eri_3center(sabc, la_min(iset), la_max(iset), npgfa(iset), zeta(:, iset), &
     616              :                                       rpgf_a(:, iset), ri, lb_min(jset), lb_max(jset), npgfb(jset), &
     617              :                                       zetb(:, jset), rpgf_b(:, jset), rj, lc_min(kset), lc_max(kset), &
     618              :                                       npgfc(kset), zetc(:, kset), rpgf_c(:, kset), rk, dij, dik, &
     619              :                                       djk, lib, potential_parameter)
     620              :                   END IF
     621              : 
     622              :                   ALLOCATE (work(nsgfa(iset), nsgfb(jset), nsgfc(kset)))
     623              : 
     624              :                   CALL abc_contract_xsmm(work, sabc, tspa(iset, ikind)%array, spb(jset, jkind)%array, &
     625              :                                          spc(kset, kkind)%array, ncoa, ncob, ncoc, nsgfa(iset), &
     626              :                                          nsgfb(jset), nsgfc(kset), cpp_buffer, ccp_buffer)
     627              : 
     628              :                   iabc(sgfa:egfa, sgfb:egfb, sgfc:egfc) = work(:, :, :)
     629              :                   DEALLOCATE (sabc, work)
     630              : 
     631              :                END DO !kset
     632              :             END DO !jset
     633              :          END DO !iset
     634              : 
     635              :          !Add the integral to the proper tensor block
     636              : !$OMP CRITICAL(omp_fill_pqX_tensor)
     637              :          CALL dbt_put_block(pq_X, [iatom, jatom, katom], SHAPE(iabc), iabc, summation=.TRUE.)
     638              : !$OMP END CRITICAL(omp_fill_pqX_tensor)
     639              : 
     640              :          DEALLOCATE (iabc)
     641              :       END DO !o3c_iterator
     642              : 
     643              :       IF (ALLOCATED(ccp_buffer)) DEALLOCATE (ccp_buffer)
     644              :       IF (ALLOCATED(cpp_buffer)) DEALLOCATE (cpp_buffer)
     645              : 
     646              :       CALL cp_libint_cleanup_3eri(lib)
     647              : 
     648              : !$OMP END PARALLEL
     649          184 :       CALL o3c_iterator_release(o3c_iterator)
     650          184 :       CALL release_o3c_container(o3c)
     651          184 :       DEALLOCATE (o3c)
     652              : 
     653         1122 :       DO iset = 1, max_nset
     654         2614 :          DO ibasis = 1, nbasis
     655         1492 :             IF (ASSOCIATED(tspa(iset, ibasis)%array)) DEALLOCATE (tspa(iset, ibasis)%array)
     656         1492 :             IF (ASSOCIATED(spb(iset, ibasis)%array)) DEALLOCATE (spb(iset, ibasis)%array)
     657         2430 :             IF (ASSOCIATED(spc(iset, ibasis)%array)) DEALLOCATE (spc(iset, ibasis)%array)
     658              :          END DO
     659              :       END DO
     660          184 :       DEALLOCATE (tspa, spb, spc)
     661              : 
     662          184 :       CALL timestop(handle)
     663              : 
     664          368 :    END SUBROUTINE fill_pqX_tensor
     665              : 
     666              : ! **************************************************************************************************
     667              : !> \brief Builds a neighbor lists set for overlaping 2-center S_ab, where b is restricted on a
     668              : !>        a given list of atoms. Used for Coulomb RI where (aI|P) = sum_b C_bI (ab|P), where
     669              : !>        contraction coeff C_bI is assumed to be non-zero only on excited atoms
     670              : !> \param ab_list the neighbor list
     671              : !> \param basis_a basis set list for atom a
     672              : !> \param basis_b basis set list for atom b
     673              : !> \param qs_env ...
     674              : !> \param excited_atoms the indices of the excited atoms on which b is centered
     675              : !> \param ext_dist2d use an external distribution2d
     676              : ! **************************************************************************************************
     677          356 :    SUBROUTINE build_xas_tdp_ovlp_nl(ab_list, basis_a, basis_b, qs_env, excited_atoms, ext_dist2d)
     678              : 
     679              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     680              :          POINTER                                         :: ab_list
     681              :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_a, basis_b
     682              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     683              :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL        :: excited_atoms
     684              :       TYPE(distribution_2d_type), OPTIONAL, POINTER      :: ext_dist2d
     685              : 
     686              :       INTEGER                                            :: ikind, nkind
     687              :       LOGICAL                                            :: my_restrictb
     688              :       LOGICAL, ALLOCATABLE, DIMENSION(:)                 :: a_present, b_present
     689              :       REAL(dp)                                           :: subcells
     690              :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: a_radius, b_radius
     691          356 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: pair_radius
     692          356 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     693              :       TYPE(cell_type), POINTER                           :: cell
     694              :       TYPE(distribution_1d_type), POINTER                :: distribution_1d
     695              :       TYPE(distribution_2d_type), POINTER                :: distribution_2d
     696          356 :       TYPE(local_atoms_type), ALLOCATABLE, DIMENSION(:)  :: atom2d
     697          356 :       TYPE(molecule_type), DIMENSION(:), POINTER         :: molecule_set
     698          356 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     699              : 
     700          356 :       NULLIFY (atomic_kind_set, distribution_1d, distribution_2d, molecule_set, particle_set, cell)
     701              : 
     702              : !  Initialization
     703          356 :       CALL get_qs_env(qs_env, nkind=nkind)
     704          356 :       CALL section_vals_val_get(qs_env%input, "DFT%SUBCELLS", r_val=subcells)
     705              : 
     706          356 :       my_restrictb = .FALSE.
     707          356 :       IF (PRESENT(excited_atoms)) THEN
     708          124 :          my_restrictb = .TRUE.
     709              :       END IF
     710              : 
     711         1424 :       ALLOCATE (a_present(nkind), b_present(nkind))
     712          356 :       a_present = .FALSE.
     713          356 :       b_present = .FALSE.
     714         1424 :       ALLOCATE (a_radius(nkind), b_radius(nkind))
     715          356 :       a_radius = 0.0_dp
     716          356 :       b_radius = 0.0_dp
     717              : 
     718              : !  Set up the radii
     719          946 :       DO ikind = 1, nkind
     720          590 :          IF (ASSOCIATED(basis_a(ikind)%gto_basis_set)) THEN
     721          590 :             a_present(ikind) = .TRUE.
     722          590 :             CALL get_gto_basis_set(basis_a(ikind)%gto_basis_set, kind_radius=a_radius(ikind))
     723              :          END IF
     724              : 
     725          946 :          IF (ASSOCIATED(basis_b(ikind)%gto_basis_set)) THEN
     726          590 :             b_present(ikind) = .TRUE.
     727          590 :             CALL get_gto_basis_set(basis_b(ikind)%gto_basis_set, kind_radius=b_radius(ikind))
     728              :          END IF
     729              :       END DO !ikind
     730              : 
     731         1424 :       ALLOCATE (pair_radius(nkind, nkind))
     732          356 :       pair_radius = 0.0_dp
     733          356 :       CALL pair_radius_setup(a_present, b_present, a_radius, b_radius, pair_radius)
     734              : 
     735              : !  Set up the nl
     736              :       CALL get_qs_env(qs_env, atomic_kind_set=atomic_kind_set, cell=cell, &
     737              :                       distribution_2d=distribution_2d, local_particles=distribution_1d, &
     738          356 :                       particle_set=particle_set, molecule_set=molecule_set)
     739              : 
     740              :       !use an external distribution_2d if required
     741          356 :       IF (PRESENT(ext_dist2d)) distribution_2d => ext_dist2d
     742              : 
     743         1658 :       ALLOCATE (atom2d(nkind))
     744              :       CALL atom2d_build(atom2d, distribution_1d, distribution_2d, atomic_kind_set, &
     745          356 :                         molecule_set, .FALSE., particle_set)
     746              : 
     747          356 :       IF (my_restrictb) THEN
     748              : 
     749              :          CALL build_neighbor_lists(ab_list, particle_set, atom2d, cell, pair_radius, subcells, &
     750          124 :                                    atomb_to_keep=excited_atoms, nlname="XAS_TDP_ovlp_nl")
     751              : 
     752              :       ELSE
     753              : 
     754              :          CALL build_neighbor_lists(ab_list, particle_set, atom2d, cell, pair_radius, subcells, &
     755          232 :                                    nlname="XAS_TDP_ovlp_nl")
     756              : 
     757              :       END IF
     758              : !  Clean-up
     759          356 :       CALL atom2d_cleanup(atom2d)
     760              : 
     761          712 :    END SUBROUTINE build_xas_tdp_ovlp_nl
     762              : 
     763              : ! **************************************************************************************************
     764              : !> \brief Builds a neighbor lists set taylored for 3-center integral within XAS TDP, such that only
     765              : !>        excited atoms are taken into account for the list_c
     766              : !> \param ac_list the neighbor list ready for 3-center integrals
     767              : !> \param basis_a basis set list for atom a
     768              : !> \param basis_c basis set list for atom c
     769              : !> \param op_type to indicate whther the list should be built with overlap, Coulomb or else in mind
     770              : !> \param qs_env ...
     771              : !> \param excited_atoms the indices of the excited atoms to consider (if not given, all atoms are taken)
     772              : !> \param x_range in case some truncated/screened operator is used, gives its range
     773              : !> \param ext_dist2d external distribution_2d to be used
     774              : !> \note Based on setup_neighbor_list with added features
     775              : ! **************************************************************************************************
     776          300 :    SUBROUTINE build_xas_tdp_3c_nl(ac_list, basis_a, basis_c, op_type, qs_env, excited_atoms, &
     777              :                                   x_range, ext_dist2d)
     778              : 
     779              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     780              :          POINTER                                         :: ac_list
     781              :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_a, basis_c
     782              :       INTEGER, INTENT(IN)                                :: op_type
     783              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     784              :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL        :: excited_atoms
     785              :       REAL(dp), INTENT(IN), OPTIONAL                     :: x_range
     786              :       TYPE(distribution_2d_type), OPTIONAL, POINTER      :: ext_dist2d
     787              : 
     788              :       INTEGER                                            :: ikind, nkind
     789              :       LOGICAL                                            :: sort_atoms
     790              :       LOGICAL, ALLOCATABLE, DIMENSION(:)                 :: a_present, c_present
     791              :       REAL(dp)                                           :: subcells
     792              :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: a_radius, c_radius
     793          300 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: pair_radius
     794          300 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     795              :       TYPE(cell_type), POINTER                           :: cell
     796              :       TYPE(distribution_1d_type), POINTER                :: distribution_1d
     797              :       TYPE(distribution_2d_type), POINTER                :: distribution_2d
     798          300 :       TYPE(local_atoms_type), ALLOCATABLE, DIMENSION(:)  :: atom2d
     799          300 :       TYPE(molecule_type), DIMENSION(:), POINTER         :: molecule_set
     800          300 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     801              : 
     802          300 :       NULLIFY (atomic_kind_set, distribution_1d, distribution_2d, molecule_set, particle_set, cell)
     803              : 
     804              : !  Initialization
     805          300 :       CALL get_qs_env(qs_env, nkind=nkind)
     806          300 :       CALL section_vals_val_get(qs_env%input, "DFT%SUBCELLS", r_val=subcells)
     807          300 :       sort_atoms = .FALSE.
     808          300 :       IF ((PRESENT(excited_atoms))) sort_atoms = .TRUE.
     809              : 
     810         1200 :       ALLOCATE (a_present(nkind), c_present(nkind))
     811          300 :       a_present = .FALSE.
     812          300 :       c_present = .FALSE.
     813         1200 :       ALLOCATE (a_radius(nkind), c_radius(nkind))
     814          300 :       a_radius = 0.0_dp
     815          300 :       c_radius = 0.0_dp
     816              : 
     817              : !  Set up the radii, depending on the operator type
     818          300 :       IF (op_type == do_potential_id) THEN
     819              : 
     820              :          !overlap => use the kind radius for both a and c
     821          514 :          DO ikind = 1, nkind
     822              :             !orbital basis set
     823          318 :             IF (ASSOCIATED(basis_a(ikind)%gto_basis_set)) THEN
     824          318 :                a_present(ikind) = .TRUE.
     825          318 :                CALL get_gto_basis_set(basis_a(ikind)%gto_basis_set, kind_radius=a_radius(ikind))
     826              :             END IF
     827              :             !RI_XAS basis set
     828          514 :             IF (ASSOCIATED(basis_c(ikind)%gto_basis_set)) THEN
     829          318 :                c_present(ikind) = .TRUE.
     830          318 :                CALL get_gto_basis_set(basis_c(ikind)%gto_basis_set, kind_radius=c_radius(ikind))
     831              :             END IF
     832              :          END DO !ikind
     833              : 
     834          104 :       ELSE IF (op_type == do_potential_coulomb) THEN
     835              : 
     836              :          !Coulomb operator, virtually infinite range => set c_radius to arbitrarily large number
     837          236 :          DO ikind = 1, nkind
     838          156 :             IF (ASSOCIATED(basis_c(ikind)%gto_basis_set)) THEN
     839          156 :                c_present(ikind) = .TRUE.
     840          156 :                c_radius(ikind) = 1000000.0_dp
     841              :             END IF
     842          236 :             IF (ASSOCIATED(basis_a(ikind)%gto_basis_set)) a_present(ikind) = .TRUE.
     843              :          END DO !ikind
     844              : 
     845           24 :       ELSE IF (op_type == do_potential_truncated .OR. op_type == do_potential_short) THEN
     846              : 
     847              :          !Truncated coulomb/short range: set c_radius to x_range + the kind_radii
     848           48 :          DO ikind = 1, nkind
     849           24 :             IF (ASSOCIATED(basis_a(ikind)%gto_basis_set)) THEN
     850           24 :                a_present(ikind) = .TRUE.
     851           24 :                CALL get_gto_basis_set(basis_a(ikind)%gto_basis_set, kind_radius=a_radius(ikind))
     852              :             END IF
     853           48 :             IF (ASSOCIATED(basis_c(ikind)%gto_basis_set)) THEN
     854           24 :                c_present(ikind) = .TRUE.
     855           24 :                CALL get_gto_basis_set(basis_c(ikind)%gto_basis_set, kind_radius=c_radius(ikind))
     856           24 :                c_radius(ikind) = c_radius(ikind) + x_range
     857              :             END IF
     858              :          END DO !ikind
     859              : 
     860              :       ELSE
     861            0 :          CPABORT("Operator not known")
     862              :       END IF
     863              : 
     864         1200 :       ALLOCATE (pair_radius(nkind, nkind))
     865          300 :       pair_radius = 0.0_dp
     866          300 :       CALL pair_radius_setup(a_present, c_present, a_radius, c_radius, pair_radius)
     867              : 
     868              : !  Actually setup the list
     869              :       CALL get_qs_env(qs_env, atomic_kind_set=atomic_kind_set, cell=cell, &
     870              :                       distribution_2d=distribution_2d, local_particles=distribution_1d, &
     871          300 :                       particle_set=particle_set, molecule_set=molecule_set)
     872              : 
     873              :       !use an external distribution_2d if required
     874          300 :       IF (PRESENT(ext_dist2d)) distribution_2d => ext_dist2d
     875              : 
     876         1398 :       ALLOCATE (atom2d(nkind))
     877              :       CALL atom2d_build(atom2d, distribution_1d, distribution_2d, atomic_kind_set, &
     878          300 :                         molecule_set, .FALSE., particle_set)
     879              : 
     880          300 :       IF (sort_atoms) THEN
     881              :          CALL build_neighbor_lists(ac_list, particle_set, atom2d, cell, pair_radius, subcells, &
     882              :                                    operator_type="ABC", atomb_to_keep=excited_atoms, &
     883          300 :                                    nlname="XAS_TDP_3c_nl")
     884              :       ELSE
     885              :          CALL build_neighbor_lists(ac_list, particle_set, atom2d, cell, pair_radius, subcells, &
     886            0 :                                    operator_type="ABC", nlname="XAS_TDP_3c_nl")
     887              :       END IF
     888              : 
     889              : !  Clean-up
     890          300 :       CALL atom2d_cleanup(atom2d)
     891              : 
     892          600 :    END SUBROUTINE build_xas_tdp_3c_nl
     893              : 
     894              : ! **************************************************************************************************
     895              : !> \brief Returns an optimized distribution_2d for the given neighbor lists based on an evaluation
     896              : !>        of the cost of the corresponding 3-center integrals
     897              : !> \param opt_3c_dist2d the optimized distribution_2d
     898              : !> \param ab_list ...
     899              : !> \param ac_list ...
     900              : !> \param basis_set_a ...
     901              : !> \param basis_set_b ...
     902              : !> \param basis_set_c ...
     903              : !> \param qs_env ...
     904              : !> \param only_bc_same_center ...
     905              : ! **************************************************************************************************
     906          116 :    SUBROUTINE get_opt_3c_dist2d(opt_3c_dist2d, ab_list, ac_list, basis_set_a, basis_set_b, &
     907              :                                 basis_set_c, qs_env, only_bc_same_center)
     908              : 
     909              :       TYPE(distribution_2d_type), POINTER                :: opt_3c_dist2d
     910              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     911              :          POINTER                                         :: ab_list, ac_list
     912              :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_set_a, basis_set_b, basis_set_c
     913              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     914              :       LOGICAL, INTENT(IN), OPTIONAL                      :: only_bc_same_center
     915              : 
     916              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'get_opt_3c_dist2d'
     917              : 
     918              :       INTEGER                                            :: handle, i, iatom, ikind, ip, jatom, &
     919              :                                                             jkind, kkind, mypcol, myprow, n, &
     920              :                                                             natom, nkind, npcol, nprow, nsgfa, &
     921              :                                                             nsgfb, nsgfc
     922          116 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: nparticle_local_col, nparticle_local_row
     923          116 :       INTEGER, DIMENSION(:, :), POINTER                  :: col_dist, row_dist
     924              :       LOGICAL                                            :: my_sort_bc
     925              :       REAL(dp)                                           :: cost, rab(3), rac(3), rbc(3)
     926          116 :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: col_cost, col_proc_cost, row_cost, &
     927          116 :                                                             row_proc_cost
     928          116 :       TYPE(cp_1d_i_p_type), DIMENSION(:), POINTER        :: local_particle_col, local_particle_row
     929              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
     930              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     931              :       TYPE(neighbor_list_iterator_p_type), &
     932          116 :          DIMENSION(:), POINTER                           :: ab_iter, ac_iter
     933          116 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     934          116 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     935              : 
     936          116 :       NULLIFY (para_env, col_dist, row_dist, blacs_env, qs_kind_set, particle_set)
     937          116 :       NULLIFY (local_particle_col, local_particle_row, ab_iter, ac_iter)
     938              : 
     939          116 :       CALL timeset(routineN, handle)
     940              : 
     941              :       !Idea: create a,b and a,c nl_iterators in the original dist, then loop over them and compute the
     942              :       !      cost of each ab pairs and project on the col/row. Then distribute the atom col/row to
     943              :       !      the proc col/row in order to spread out the cost as much as possible
     944              : 
     945          116 :       my_sort_bc = .FALSE.
     946          116 :       IF (PRESENT(only_bc_same_center)) my_sort_bc = only_bc_same_center
     947              : 
     948              :       CALL get_qs_env(qs_env, natom=natom, para_env=para_env, blacs_env=blacs_env, &
     949          116 :                       qs_kind_set=qs_kind_set, particle_set=particle_set, nkind=nkind)
     950              : 
     951          116 :       myprow = blacs_env%mepos(1) + 1
     952          116 :       mypcol = blacs_env%mepos(2) + 1
     953          116 :       nprow = blacs_env%num_pe(1)
     954          116 :       npcol = blacs_env%num_pe(2)
     955              : 
     956          464 :       ALLOCATE (col_cost(natom), row_cost(natom))
     957          116 :       col_cost = 0.0_dp; row_cost = 0.0_dp
     958              : 
     959          464 :       ALLOCATE (row_dist(natom, 2), col_dist(natom, 2))
     960         3212 :       row_dist = 1; col_dist = 1
     961              : 
     962          116 :       CALL neighbor_list_iterator_create(ab_iter, ab_list)
     963          116 :       CALL neighbor_list_iterator_create(ac_iter, ac_list, search=.TRUE.)
     964         1256 :       DO WHILE (neighbor_list_iterate(ab_iter) == 0)
     965         1140 :          CALL get_iterator_info(ab_iter, ikind=ikind, jkind=jkind, iatom=iatom, jatom=jatom, r=rab)
     966              : 
     967         2846 :          DO kkind = 1, nkind
     968         1590 :             CALL nl_set_sub_iterator(ac_iter, ikind, kkind, iatom)
     969              : 
     970         7135 :             DO WHILE (nl_sub_iterate(ac_iter) == 0)
     971              : 
     972         4405 :                IF (my_sort_bc) THEN
     973              :                   !only take a,b,c if b,c (or a,c because of symmetry) share the same center
     974          724 :                   CALL get_iterator_info(ac_iter, r=rac)
     975         2896 :                   rbc(:) = rac(:) - rab(:)
     976         3196 :                   IF (.NOT. (ALL(ABS(rbc) <= 1.0E-8_dp) .OR. ALL(ABS(rac) <= 1.0E-8_dp))) CYCLE
     977              : 
     978              :                END IF
     979              : 
     980              :                !Use the size of integral as measure as contraciton cost seems to dominate
     981         4111 :                nsgfa = basis_set_a(ikind)%gto_basis_set%nsgf
     982         4111 :                nsgfb = basis_set_b(jkind)%gto_basis_set%nsgf
     983         4111 :                nsgfc = basis_set_c(kkind)%gto_basis_set%nsgf
     984              : 
     985         4111 :                cost = REAL(nsgfa*nsgfb*nsgfc, dp)
     986              : 
     987         4111 :                row_cost(iatom) = row_cost(iatom) + cost
     988         4405 :                col_cost(jatom) = col_cost(jatom) + cost
     989              : 
     990              :             END DO !ac_iter
     991              :          END DO !kkind
     992              :       END DO !ab_iter
     993          116 :       CALL neighbor_list_iterator_release(ab_iter)
     994          116 :       CALL neighbor_list_iterator_release(ac_iter)
     995              : 
     996          116 :       CALL para_env%sum(row_cost)
     997          116 :       CALL para_env%sum(col_cost)
     998              : 
     999              :       !Distribute the cost as evenly as possible
    1000          580 :       ALLOCATE (col_proc_cost(npcol), row_proc_cost(nprow))
    1001          116 :       col_proc_cost = 0.0_dp; row_proc_cost = 0.0_dp
    1002          774 :       DO i = 1, natom
    1003        21708 :          iatom = MAXLOC(row_cost, 1)
    1004         2632 :          ip = MINLOC(row_proc_cost, 1)
    1005          658 :          row_proc_cost(ip) = row_proc_cost(ip) + row_cost(iatom)
    1006          658 :          row_dist(iatom, 1) = ip
    1007          658 :          row_cost(iatom) = 0.0_dp
    1008              : 
    1009        21708 :          iatom = MAXLOC(col_cost, 1)
    1010         1974 :          ip = MINLOC(col_proc_cost, 1)
    1011          658 :          col_proc_cost(ip) = col_proc_cost(ip) + col_cost(iatom)
    1012          658 :          col_dist(iatom, 1) = ip
    1013          774 :          col_cost(iatom) = 0.0_dp
    1014              :       END DO
    1015              : 
    1016              :       !the usual stuff
    1017          844 :       ALLOCATE (local_particle_col(nkind), local_particle_row(nkind))
    1018          464 :       ALLOCATE (nparticle_local_row(nkind), nparticle_local_col(nkind))
    1019          116 :       nparticle_local_row = 0; nparticle_local_col = 0
    1020              : 
    1021          774 :       DO iatom = 1, natom
    1022          658 :          ikind = particle_set(iatom)%atomic_kind%kind_number
    1023              : 
    1024          658 :          IF (row_dist(iatom, 1) == myprow) nparticle_local_row(ikind) = nparticle_local_row(ikind) + 1
    1025          774 :          IF (col_dist(iatom, 1) == mypcol) nparticle_local_col(ikind) = nparticle_local_col(ikind) + 1
    1026              :       END DO
    1027              : 
    1028          306 :       DO ikind = 1, nkind
    1029          190 :          n = nparticle_local_row(ikind)
    1030          505 :          ALLOCATE (local_particle_row(ikind)%array(n))
    1031              : 
    1032          190 :          n = nparticle_local_col(ikind)
    1033          686 :          ALLOCATE (local_particle_col(ikind)%array(n))
    1034              :       END DO
    1035              : 
    1036          116 :       nparticle_local_row = 0; nparticle_local_col = 0
    1037          774 :       DO iatom = 1, natom
    1038          658 :          ikind = particle_set(iatom)%atomic_kind%kind_number
    1039              : 
    1040          658 :          IF (row_dist(iatom, 1) == myprow) THEN
    1041          329 :             nparticle_local_row(ikind) = nparticle_local_row(ikind) + 1
    1042          329 :             local_particle_row(ikind)%array(nparticle_local_row(ikind)) = iatom
    1043              :          END IF
    1044          774 :          IF (col_dist(iatom, 1) == mypcol) THEN
    1045          658 :             nparticle_local_col(ikind) = nparticle_local_col(ikind) + 1
    1046          658 :             local_particle_col(ikind)%array(nparticle_local_col(ikind)) = iatom
    1047              :          END IF
    1048              :       END DO
    1049              : 
    1050              :       !Finally create the dist_2d
    1051          774 :       row_dist(:, 1) = row_dist(:, 1) - 1
    1052          774 :       col_dist(:, 1) = col_dist(:, 1) - 1
    1053              :       CALL distribution_2d_create(opt_3c_dist2d, row_distribution_ptr=row_dist, &
    1054              :                                   col_distribution_ptr=col_dist, local_rows_ptr=local_particle_row, &
    1055          116 :                                   local_cols_ptr=local_particle_col, blacs_env=blacs_env)
    1056              : 
    1057          116 :       CALL timestop(handle)
    1058              : 
    1059          232 :    END SUBROUTINE get_opt_3c_dist2d
    1060              : 
    1061              : ! **************************************************************************************************
    1062              : !> \brief Computes the RI exchange 3-center integrals (ab|c), where c is from the RI_XAS basis and
    1063              : !>        centered on excited atoms and kind. The operator used is that of the RI metric
    1064              : !> \param ex_atoms excited atoms on which the third center is located
    1065              : !> \param xas_tdp_env ...
    1066              : !> \param xas_tdp_control ...
    1067              : !> \param qs_env ...
    1068              : !> \note  This routine is called once for each excited atom. Because there are many different a,b
    1069              : !>        pairs involved, load balance is ok. This allows memory saving
    1070              : ! **************************************************************************************************
    1071           54 :    SUBROUTINE compute_ri_3c_exchange(ex_atoms, xas_tdp_env, xas_tdp_control, qs_env)
    1072              : 
    1073              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: ex_atoms
    1074              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
    1075              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
    1076              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1077              : 
    1078              :       CHARACTER(len=*), PARAMETER :: routineN = 'compute_ri_3c_exchange'
    1079              : 
    1080              :       INTEGER                                            :: handle, natom, nkind
    1081              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: blk_size_orb, blk_size_ri
    1082              :       TYPE(dbcsr_distribution_type)                      :: opt_dbcsr_dist
    1083           54 :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_set_orb, basis_set_ri
    1084              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1085              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
    1086           54 :          POINTER                                         :: ab_list, ac_list
    1087           54 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
    1088           54 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
    1089              : 
    1090           54 :       NULLIFY (basis_set_ri, basis_set_orb, ac_list, ab_list, qs_kind_set, para_env, particle_set)
    1091              : 
    1092           54 :       CALL timeset(routineN, handle)
    1093              : 
    1094              : !  Take what we need from the qs_env
    1095              :       CALL get_qs_env(qs_env, nkind=nkind, qs_kind_set=qs_kind_set, para_env=para_env, &
    1096           54 :                       natom=natom, particle_set=particle_set)
    1097              : 
    1098              : !  Build the basis set lists
    1099          254 :       ALLOCATE (basis_set_ri(nkind))
    1100          200 :       ALLOCATE (basis_set_orb(nkind))
    1101           54 :       CALL basis_set_list_setup(basis_set_ri, "RI_XAS", qs_kind_set)
    1102           54 :       CALL basis_set_list_setup(basis_set_orb, "ORB", qs_kind_set)
    1103              : 
    1104              : !  Get the optimized distribution 2d for theses integrals (and store it in xas_tdp_env)
    1105           54 :       CALL build_xas_tdp_ovlp_nl(ab_list, basis_set_orb, basis_set_orb, qs_env)
    1106              :       CALL build_xas_tdp_3c_nl(ac_list, basis_set_orb, basis_set_ri, &
    1107              :                                xas_tdp_control%ri_m_potential%potential_type, qs_env, &
    1108           54 :                                excited_atoms=ex_atoms, x_range=xas_tdp_control%ri_m_potential%cutoff_radius)
    1109              : 
    1110              :       CALL get_opt_3c_dist2d(xas_tdp_env%opt_dist2d_ex, ab_list, ac_list, basis_set_orb, &
    1111           54 :                              basis_set_orb, basis_set_ri, qs_env)
    1112           54 :       CALL release_neighbor_list_sets(ab_list)
    1113           54 :       CALL release_neighbor_list_sets(ac_list)
    1114              : 
    1115              : !  Build the ab and ac centers neighbor lists based on the optimized distribution
    1116              :       CALL build_xas_tdp_ovlp_nl(ab_list, basis_set_orb, basis_set_orb, qs_env, &
    1117           54 :                                  ext_dist2d=xas_tdp_env%opt_dist2d_ex)
    1118              :       CALL build_xas_tdp_3c_nl(ac_list, basis_set_orb, basis_set_ri, &
    1119              :                                xas_tdp_control%ri_m_potential%potential_type, qs_env, &
    1120              :                                excited_atoms=ex_atoms, x_range=xas_tdp_control%ri_m_potential%cutoff_radius, &
    1121           54 :                                ext_dist2d=xas_tdp_env%opt_dist2d_ex)
    1122              : 
    1123              : !  Allocate, init and compute the integrals.
    1124          216 :       ALLOCATE (blk_size_orb(natom), blk_size_ri(natom))
    1125           54 :       CALL cp_dbcsr_dist2d_to_dist(xas_tdp_env%opt_dist2d_ex, opt_dbcsr_dist)
    1126           54 :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=blk_size_orb, basis=basis_set_orb)
    1127           54 :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=blk_size_ri, basis=basis_set_ri)
    1128              : 
    1129          540 :       ALLOCATE (xas_tdp_env%ri_3c_ex)
    1130              :       CALL create_pqX_tensor(xas_tdp_env%ri_3c_ex, ab_list, ac_list, opt_dbcsr_dist, blk_size_orb, &
    1131           54 :                              blk_size_orb, blk_size_ri)
    1132              :       CALL fill_pqX_tensor(xas_tdp_env%ri_3c_ex, ab_list, ac_list, basis_set_orb, basis_set_orb, &
    1133              :                            basis_set_ri, xas_tdp_control%ri_m_potential, qs_env, &
    1134           54 :                            eps_screen=xas_tdp_control%eps_screen)
    1135              : 
    1136              : ! Clean-up
    1137           54 :       CALL release_neighbor_list_sets(ab_list)
    1138           54 :       CALL release_neighbor_list_sets(ac_list)
    1139           54 :       CALL dbcsr_distribution_release(opt_dbcsr_dist)
    1140           54 :       DEALLOCATE (basis_set_ri, basis_set_orb)
    1141              : 
    1142              :       !not strictly necessary but avoid having any load unbalance here being reported in the
    1143              :       !timings for other routines
    1144           54 :       CALL para_env%sync()
    1145              : 
    1146           54 :       CALL timestop(handle)
    1147              : 
    1148          162 :    END SUBROUTINE compute_ri_3c_exchange
    1149              : 
    1150              : ! **************************************************************************************************
    1151              : !> \brief Computes the RI Coulomb 3-center integrals (ab|c), where c is from the RI_XAS basis and
    1152              : !>        centered on the excited atoms of xas_tdp_env
    1153              : !> \param xas_tdp_env ...
    1154              : !> \param qs_env ...
    1155              : !> \note  The ri_3c_coul tensor of xas_tdp_env is defined and allocated here. Only computed once
    1156              : !>        for the whole system (for optimized load balance). Ok because not too much memory needed
    1157              : ! **************************************************************************************************
    1158           62 :    SUBROUTINE compute_ri_3c_coulomb(xas_tdp_env, qs_env)
    1159              : 
    1160              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
    1161              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1162              : 
    1163              :       CHARACTER(len=*), PARAMETER :: routineN = 'compute_ri_3c_coulomb'
    1164              : 
    1165              :       INTEGER                                            :: handle, natom, nkind
    1166              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: blk_size_orb, blk_size_ri
    1167              :       TYPE(dbcsr_distribution_type)                      :: opt_dbcsr_dist
    1168           62 :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_set_orb, basis_set_ri
    1169              :       TYPE(libint_potential_type)                        :: pot
    1170              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1171              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
    1172           62 :          POINTER                                         :: ab_list, ac_list
    1173           62 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
    1174           62 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
    1175              : 
    1176           62 :       NULLIFY (basis_set_ri, basis_set_orb, ac_list, ab_list, qs_kind_set, para_env, particle_set)
    1177              : 
    1178           62 :       CALL timeset(routineN, handle)
    1179              : 
    1180              : !  Take what we need from the qs_env
    1181              :       CALL get_qs_env(qs_env, nkind=nkind, qs_kind_set=qs_kind_set, para_env=para_env, &
    1182           62 :                       natom=natom, particle_set=particle_set)
    1183              : 
    1184              : !  Build the basis set lists
    1185          284 :       ALLOCATE (basis_set_ri(nkind))
    1186          222 :       ALLOCATE (basis_set_orb(nkind))
    1187           62 :       CALL basis_set_list_setup(basis_set_ri, "RI_XAS", qs_kind_set)
    1188           62 :       CALL basis_set_list_setup(basis_set_orb, "ORB", qs_kind_set)
    1189              : 
    1190              : !  Get the optimized distribution 2d for these integrals (and store it in xas_tdp_env)
    1191              :       CALL build_xas_tdp_ovlp_nl(ab_list, basis_set_orb, basis_set_orb, qs_env, &
    1192           62 :                                  excited_atoms=xas_tdp_env%ex_atom_indices)
    1193              :       CALL build_xas_tdp_3c_nl(ac_list, basis_set_orb, basis_set_ri, do_potential_id, &
    1194           62 :                                qs_env, excited_atoms=xas_tdp_env%ex_atom_indices)
    1195              :       CALL get_opt_3c_dist2d(xas_tdp_env%opt_dist2d_coul, ab_list, ac_list, basis_set_orb, &
    1196           62 :                              basis_set_orb, basis_set_ri, qs_env, only_bc_same_center=.TRUE.)
    1197           62 :       CALL release_neighbor_list_sets(ab_list)
    1198           62 :       CALL release_neighbor_list_sets(ac_list)
    1199              : 
    1200              : !  Build a neighbor list for the ab centers. Assume (aI|c) = sum_b c_bI (ab|c), with c_bI only
    1201              : !  non-zero for b centered on the same atom as c => build overlap nl, but only keeping b if centered
    1202              : !  on an excited atom
    1203              :       CALL build_xas_tdp_ovlp_nl(ab_list, basis_set_orb, basis_set_orb, qs_env, &
    1204              :                                  excited_atoms=xas_tdp_env%ex_atom_indices, &
    1205           62 :                                  ext_dist2d=xas_tdp_env%opt_dist2d_coul)
    1206              : 
    1207              : !  Build a neighbor list for the ac centers. Since we later contract as (aI|c) and we assume I is
    1208              : !  very localized on the same atom as c, we take a,c as neighbors if they overlap
    1209              :       CALL build_xas_tdp_3c_nl(ac_list, basis_set_orb, basis_set_ri, do_potential_id, &
    1210              :                                qs_env, excited_atoms=xas_tdp_env%ex_atom_indices, &
    1211           62 :                                ext_dist2d=xas_tdp_env%opt_dist2d_coul)
    1212              : 
    1213              : !  Allocate, init and compute the integrals
    1214          248 :       ALLOCATE (blk_size_orb(natom), blk_size_ri(natom))
    1215           62 :       CALL cp_dbcsr_dist2d_to_dist(xas_tdp_env%opt_dist2d_coul, opt_dbcsr_dist)
    1216           62 :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=blk_size_orb, basis=basis_set_orb)
    1217           62 :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=blk_size_ri, basis=basis_set_ri)
    1218              :       pot%potential_type = do_potential_coulomb
    1219              : 
    1220          620 :       ALLOCATE (xas_tdp_env%ri_3c_coul)
    1221              :       CALL create_pqX_tensor(xas_tdp_env%ri_3c_coul, ab_list, ac_list, opt_dbcsr_dist, blk_size_orb, &
    1222           62 :                              blk_size_orb, blk_size_ri, only_bc_same_center=.TRUE.)
    1223              :       CALL fill_pqX_tensor(xas_tdp_env%ri_3c_coul, ab_list, ac_list, basis_set_orb, basis_set_orb, &
    1224           62 :                            basis_set_ri, pot, qs_env, only_bc_same_center=.TRUE.)
    1225              : 
    1226              : ! Clean-up
    1227           62 :       CALL release_neighbor_list_sets(ab_list)
    1228           62 :       CALL release_neighbor_list_sets(ac_list)
    1229           62 :       CALL dbcsr_distribution_release(opt_dbcsr_dist)
    1230           62 :       DEALLOCATE (basis_set_ri, basis_set_orb)
    1231              : 
    1232              :       !not strictly necessary but avoid having any load unbalance here being reported in the
    1233              :       !timings for other routines
    1234           62 :       CALL para_env%sync()
    1235              : 
    1236           62 :       CALL timestop(handle)
    1237              : 
    1238          186 :    END SUBROUTINE compute_ri_3c_coulomb
    1239              : 
    1240              : ! **************************************************************************************************
    1241              : !> \brief Computes the two-center Exchange integral needed for the RI in kernel calculation. Stores
    1242              : !>        the integrals in the xas_tdp_env as global (small) arrays. Does that for a given excited
    1243              : !>        kind. The quantity stored is M^-1 (P|Q) M^-1, where M is the RI metric. If the metric is
    1244              : !>        the same as the exchange potential, then we end up with the V-approximation (P|Q)^-1
    1245              : !>        By default (if no metric), the ri_m_potential is a copy of the x_potential
    1246              : !> \param ex_kind ...
    1247              : !> \param xas_tdp_env ...
    1248              : !> \param xas_tdp_control ...
    1249              : !> \param qs_env ...
    1250              : !> \note Computes all these integrals in non-PBCs as we assume that the range is short enough that
    1251              : !>       atoms do not exchange with their periodic images
    1252              : ! **************************************************************************************************
    1253           54 :    SUBROUTINE compute_ri_exchange2_int(ex_kind, xas_tdp_env, xas_tdp_control, qs_env)
    1254              : 
    1255              :       INTEGER, INTENT(IN)                                :: ex_kind
    1256              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
    1257              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
    1258              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1259              : 
    1260              :       INTEGER                                            :: egfp, egfq, maxl, ncop, ncoq, nset, &
    1261              :                                                             nsgf, pset, qset, sgfp, sgfq, unit_id
    1262           54 :       INTEGER, DIMENSION(:), POINTER                     :: lmax, lmin, npgf_set, nsgf_set
    1263           54 :       INTEGER, DIMENSION(:, :), POINTER                  :: first_sgf
    1264              :       REAL(dp)                                           :: r(3)
    1265           54 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: metric, pq, work
    1266           54 :       REAL(dp), DIMENSION(:, :), POINTER                 :: rpgf, sphi, zet
    1267              :       TYPE(cp_libint_t)                                  :: lib
    1268              :       TYPE(gto_basis_set_type), POINTER                  :: ri_basis
    1269              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1270           54 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
    1271              : 
    1272           54 :       NULLIFY (ri_basis, qs_kind_set, para_env, lmin, lmax, npgf_set, zet, rpgf, first_sgf)
    1273           54 :       NULLIFY (sphi, nsgf_set)
    1274              : 
    1275              : !  Initialization
    1276           54 :       CALL get_qs_env(qs_env, qs_kind_set=qs_kind_set, para_env=para_env)
    1277           54 :       IF (ASSOCIATED(xas_tdp_env%ri_inv_ex)) THEN
    1278            8 :          DEALLOCATE (xas_tdp_env%ri_inv_ex)
    1279              :       END IF
    1280              : 
    1281              : !  Get the RI basis of interest and its quantum numbers
    1282           54 :       CALL get_qs_kind(qs_kind_set(ex_kind), basis_set=ri_basis, basis_type="RI_XAS")
    1283              :       CALL get_gto_basis_set(ri_basis, nsgf=nsgf, maxl=maxl, npgf=npgf_set, lmin=lmin, &
    1284              :                              lmax=lmax, zet=zet, pgf_radius=rpgf, first_sgf=first_sgf, &
    1285           54 :                              nsgf_set=nsgf_set, sphi=sphi, nset=nset)
    1286          216 :       ALLOCATE (metric(nsgf, nsgf))
    1287           54 :       metric = 0.0_dp
    1288              : 
    1289           54 :       r = 0.0_dp
    1290              : 
    1291              :       !init the libint 2-center object
    1292           54 :       CALL cp_libint_init_2eri(lib, maxl)
    1293           54 :       CALL cp_libint_set_contrdepth(lib, 1)
    1294              : 
    1295              :       !make sure the truncted coulomb is initialized
    1296           54 :       IF (xas_tdp_control%ri_m_potential%potential_type == do_potential_truncated) THEN
    1297              : 
    1298            6 :          IF (2*maxl + 1 > get_lmax_init()) THEN
    1299            6 :             IF (para_env%mepos == 0) THEN
    1300            3 :                CALL open_file(unit_number=unit_id, file_name=xas_tdp_control%ri_m_potential%filename)
    1301              :             END IF
    1302            6 :             CALL init(2*maxl + 1, unit_id, para_env%mepos, para_env)
    1303            6 :             IF (para_env%mepos == 0) THEN
    1304            3 :                CALL close_file(unit_id)
    1305              :             END IF
    1306              :          END IF
    1307              :       END IF
    1308              : 
    1309              : !  Compute the RI metric
    1310          162 :       DO pset = 1, nset
    1311          108 :          ncop = npgf_set(pset)*ncoset(lmax(pset))
    1312          108 :          sgfp = first_sgf(1, pset)
    1313          108 :          egfp = sgfp + nsgf_set(pset) - 1
    1314              : 
    1315          378 :          DO qset = 1, nset
    1316          216 :             ncoq = npgf_set(qset)*ncoset(lmax(qset))
    1317          216 :             sgfq = first_sgf(1, qset)
    1318          216 :             egfq = sgfq + nsgf_set(qset) - 1
    1319              : 
    1320          864 :             ALLOCATE (work(ncop, ncoq))
    1321          216 :             work = 0.0_dp
    1322              : 
    1323              :             CALL eri_2center(work, lmin(pset), lmax(pset), npgf_set(pset), zet(:, pset), rpgf(:, pset), &
    1324              :                              r, lmin(qset), lmax(qset), npgf_set(qset), zet(:, qset), rpgf(:, qset), &
    1325          216 :                              r, 0.0_dp, lib, xas_tdp_control%ri_m_potential)
    1326              : 
    1327              :             CALL ab_contract(metric(sgfp:egfp, sgfq:egfq), work, sphi(:, sgfp:), sphi(:, sgfq:), &
    1328          216 :                              ncop, ncoq, nsgf_set(pset), nsgf_set(qset))
    1329              : 
    1330          324 :             DEALLOCATE (work)
    1331              :          END DO !qset
    1332              :       END DO !pset
    1333              : 
    1334              : !  Inverting (to M^-1)
    1335           54 :       CALL invmat_symm(metric)
    1336              : 
    1337           54 :       IF (.NOT. xas_tdp_control%do_ri_metric) THEN
    1338              : 
    1339              :          !If no metric, then x_pot = ri_m_pot and (P|Q)^-1 = M^-1 (V-approximation)
    1340          144 :          ALLOCATE (xas_tdp_env%ri_inv_ex(nsgf, nsgf))
    1341       445396 :          xas_tdp_env%ri_inv_ex(:, :) = metric(:, :)
    1342           48 :          CALL cp_libint_cleanup_2eri(lib)
    1343           48 :          RETURN
    1344              : 
    1345              :       END IF
    1346              : 
    1347              :       !make sure the truncted coulomb is initialized
    1348            6 :       IF (xas_tdp_control%x_potential%potential_type == do_potential_truncated) THEN
    1349              : 
    1350            2 :          IF (2*maxl + 1 > get_lmax_init()) THEN
    1351            0 :             IF (para_env%mepos == 0) THEN
    1352            0 :                CALL open_file(unit_number=unit_id, file_name=xas_tdp_control%x_potential%filename)
    1353              :             END IF
    1354            0 :             CALL init(2*maxl + 1, unit_id, para_env%mepos, para_env)
    1355            0 :             IF (para_env%mepos == 0) THEN
    1356            0 :                CALL close_file(unit_id)
    1357              :             END IF
    1358              :          END IF
    1359              :       END IF
    1360              : 
    1361              : !  Compute the proper exchange 2-center
    1362           18 :       ALLOCATE (pq(nsgf, nsgf))
    1363            6 :       pq = 0.0_dp
    1364              : 
    1365           18 :       DO pset = 1, nset
    1366           12 :          ncop = npgf_set(pset)*ncoset(lmax(pset))
    1367           12 :          sgfp = first_sgf(1, pset)
    1368           12 :          egfp = sgfp + nsgf_set(pset) - 1
    1369              : 
    1370           42 :          DO qset = 1, nset
    1371           24 :             ncoq = npgf_set(qset)*ncoset(lmax(qset))
    1372           24 :             sgfq = first_sgf(1, qset)
    1373           24 :             egfq = sgfq + nsgf_set(qset) - 1
    1374              : 
    1375           96 :             ALLOCATE (work(ncop, ncoq))
    1376           24 :             work = 0.0_dp
    1377              : 
    1378              :             CALL eri_2center(work, lmin(pset), lmax(pset), npgf_set(pset), zet(:, pset), rpgf(:, pset), &
    1379              :                              r, lmin(qset), lmax(qset), npgf_set(qset), zet(:, qset), rpgf(:, qset), &
    1380           24 :                              r, 0.0_dp, lib, xas_tdp_control%x_potential)
    1381              : 
    1382              :             CALL ab_contract(pq(sgfp:egfp, sgfq:egfq), work, sphi(:, sgfp:), sphi(:, sgfq:), &
    1383           24 :                              ncop, ncoq, nsgf_set(pset), nsgf_set(qset))
    1384              : 
    1385           36 :             DEALLOCATE (work)
    1386              :          END DO !qset
    1387              :       END DO !pset
    1388              : 
    1389              : !  Compute and store M^-1 (P|Q) M^-1
    1390           18 :       ALLOCATE (xas_tdp_env%ri_inv_ex(nsgf, nsgf))
    1391        22698 :       xas_tdp_env%ri_inv_ex = 0.0_dp
    1392              : 
    1393              :       CALL dgemm('N', 'N', nsgf, nsgf, nsgf, 1.0_dp, metric, nsgf, pq, nsgf, &
    1394            6 :                  0.0_dp, xas_tdp_env%ri_inv_ex, nsgf)
    1395              :       CALL dgemm('N', 'N', nsgf, nsgf, nsgf, 1.0_dp, xas_tdp_env%ri_inv_ex, nsgf, metric, nsgf, &
    1396            6 :                  0.0_dp, pq, nsgf)
    1397        22698 :       xas_tdp_env%ri_inv_ex(:, :) = pq(:, :)
    1398              : 
    1399            6 :       CALL cp_libint_cleanup_2eri(lib)
    1400              : 
    1401          162 :    END SUBROUTINE compute_ri_exchange2_int
    1402              : 
    1403              : ! **************************************************************************************************
    1404              : !> \brief Computes the two-center Coulomb integral needed for the RI in kernel calculation. Stores
    1405              : !>        the integrals (P|Q)^-1 in the xas_tdp_env as global (small) arrays. Does that for a given
    1406              : !>        excited kind
    1407              : !> \param ex_kind ...
    1408              : !> \param xas_tdp_env ...
    1409              : !> \param xas_tdp_control ...
    1410              : !> \param qs_env ...
    1411              : ! **************************************************************************************************
    1412           74 :    SUBROUTINE compute_ri_coulomb2_int(ex_kind, xas_tdp_env, xas_tdp_control, qs_env)
    1413              : 
    1414              :       INTEGER, INTENT(IN)                                :: ex_kind
    1415              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
    1416              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
    1417              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1418              : 
    1419              :       INTEGER                                            :: nsgf
    1420              :       TYPE(gto_basis_set_type), POINTER                  :: ri_basis
    1421           74 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
    1422              : 
    1423           74 :       NULLIFY (ri_basis, qs_kind_set)
    1424              : 
    1425              : !  Initialization
    1426           74 :       CALL get_qs_env(qs_env, qs_kind_set=qs_kind_set)
    1427           74 :       IF (ASSOCIATED(xas_tdp_env%ri_inv_coul)) THEN
    1428            8 :          DEALLOCATE (xas_tdp_env%ri_inv_coul)
    1429              :       END IF
    1430              : 
    1431              : !  Get the RI basis of interest and its quantum numbers
    1432           74 :       CALL get_qs_kind(qs_kind_set(ex_kind), basis_set=ri_basis, basis_type="RI_XAS")
    1433           74 :       CALL get_gto_basis_set(ri_basis, nsgf=nsgf)
    1434          296 :       ALLOCATE (xas_tdp_env%ri_inv_coul(nsgf, nsgf))
    1435       660186 :       xas_tdp_env%ri_inv_coul = 0.0_dp
    1436              : 
    1437           74 :       IF (.NOT. xas_tdp_control%is_periodic) THEN
    1438              :          CALL int_operators_r12_ab_os(r12_operator=operator_coulomb, vab=xas_tdp_env%ri_inv_coul, &
    1439              :                                       rab=[0.0_dp, 0.0_dp, 0.0_dp], fba=ri_basis, fbb=ri_basis, &
    1440           58 :                                       calculate_forces=.FALSE.)
    1441           58 :          CPASSERT(ASSOCIATED(xas_tdp_control))
    1442              :       ELSE
    1443           16 :          CALL periodic_ri_coulomb2(xas_tdp_env%ri_inv_coul, ri_basis, qs_env)
    1444              :       END IF
    1445              : 
    1446              : !  Inverting
    1447           74 :       CALL invmat_symm(xas_tdp_env%ri_inv_coul)
    1448              : 
    1449          148 :    END SUBROUTINE compute_ri_coulomb2_int
    1450              : 
    1451              : ! **************************************************************************************************
    1452              : !> \brief Computes the two-center inverse coulomb integral in the case of PBCs
    1453              : !> \param ri_coul2 ...
    1454              : !> \param ri_basis ...
    1455              : !> \param qs_env ...
    1456              : ! **************************************************************************************************
    1457           16 :    SUBROUTINE periodic_ri_coulomb2(ri_coul2, ri_basis, qs_env)
    1458              : 
    1459              :       REAL(dp), DIMENSION(:, :), INTENT(INOUT)           :: ri_coul2
    1460              :       TYPE(gto_basis_set_type), POINTER                  :: ri_basis
    1461              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1462              : 
    1463              :       INTEGER                                            :: maxco, ncop, ncoq, nset, op, oq, ppgf, &
    1464              :                                                             pset, qpgf, qset, sgfp, sgfq
    1465           32 :       INTEGER, DIMENSION(:), POINTER                     :: lmax, lmin, npgf, nsgf
    1466           16 :       INTEGER, DIMENSION(:, :), POINTER                  :: first_sgf
    1467              :       REAL(dp)                                           :: r(3)
    1468           16 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: hpq
    1469           16 :       REAL(dp), DIMENSION(:, :), POINTER                 :: sphi, zet
    1470              :       TYPE(cell_type), POINTER                           :: cell
    1471          416 :       TYPE(cp_eri_mme_param)                             :: mme_param
    1472              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1473           16 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
    1474              : 
    1475           16 :       NULLIFY (cell, qs_kind_set, lmin, lmax, nsgf, npgf, zet, sphi, first_sgf)
    1476              : 
    1477              :       ! Use eri_mme for this. Don't want to annoy the user with a full input section just for this
    1478              :       ! tiny bit => initialize our own eri_mme section with the defaults
    1479              : 
    1480           16 :       CALL get_qs_env(qs_env, cell=cell, qs_kind_set=qs_kind_set, para_env=para_env)
    1481              : 
    1482              :       CALL eri_mme_init(mme_param%par, n_minimax=20, cutoff=300.0_dp, do_calib_cutoff=.TRUE., &
    1483              :                         cutoff_min=10.0_dp, cutoff_max=10000.0_dp, cutoff_eps=0.01_dp, &
    1484              :                         cutoff_delta=0.9_dp, sum_precision=1.0E-12_dp, debug=.FALSE., &
    1485              :                         debug_delta=1.0E-6_dp, debug_nsum=1000000, unit_nr=0, print_calib=.FALSE., &
    1486           16 :                         do_error_est=.FALSE.)
    1487           16 :       mme_param%do_calib = .TRUE.
    1488              : 
    1489           16 :       CALL cp_eri_mme_set_params(mme_param, cell, qs_kind_set, basis_type_1="RI_XAS", para_env=para_env)
    1490              : 
    1491              :       CALL get_gto_basis_set(ri_basis, lmax=lmax, npgf=npgf, zet=zet, lmin=lmin, nset=nset, &
    1492           16 :                              nsgf_set=nsgf, sphi=sphi, first_sgf=first_sgf, maxco=maxco)
    1493              : 
    1494           16 :       r = 0.0_dp
    1495           64 :       ALLOCATE (hpq(nset*maxco, nset*maxco))
    1496           16 :       hpq = 0.0_dp
    1497              : 
    1498           48 :       DO pset = 1, nset
    1499           32 :          ncop = npgf(pset)*ncoset(lmax(pset))
    1500           32 :          sgfp = first_sgf(1, pset)
    1501              : 
    1502          112 :          DO qset = 1, nset
    1503           64 :             ncoq = npgf(qset)*ncoset(lmax(qset))
    1504           64 :             sgfq = first_sgf(1, qset)
    1505              : 
    1506          608 :             DO ppgf = 1, npgf(pset)
    1507          544 :                op = (pset - 1)*maxco + (ppgf - 1)*ncoset(lmax(pset))
    1508         5232 :                DO qpgf = 1, npgf(qset)
    1509         4624 :                   oq = (qset - 1)*maxco + (qpgf - 1)*ncoset(lmax(qset))
    1510              : 
    1511              :                   CALL eri_mme_2c_integrate(mme_param%par, lmin(pset), lmax(pset), lmin(qset), &
    1512              :                                             lmax(qset), zet(ppgf, pset), zet(qpgf, qset), r, hpq, &
    1513         5168 :                                             op, oq)
    1514              : 
    1515              :                END DO !qpgf
    1516              :             END DO ! ppgf
    1517              : 
    1518              :             !contraction into sgfs
    1519           64 :             op = (pset - 1)*maxco + 1
    1520           64 :             oq = (qset - 1)*maxco + 1
    1521              : 
    1522              :             CALL ab_contract(ri_coul2(sgfp:sgfp + nsgf(pset) - 1, sgfq:sgfq + nsgf(qset) - 1), &
    1523              :                              hpq(op:op + ncop - 1, oq:oq + ncoq - 1), sphi(:, sgfp:), sphi(:, sgfq:), &
    1524           96 :                              ncop, ncoq, nsgf(pset), nsgf(qset))
    1525              : 
    1526              :          END DO !qset
    1527              :       END DO !pset
    1528              : 
    1529              :       !celan-up
    1530           16 :       CALL eri_mme_release(mme_param%par)
    1531              : 
    1532           64 :    END SUBROUTINE periodic_ri_coulomb2
    1533              : 
    1534              : END MODULE xas_tdp_integrals
        

Generated by: LCOV version 2.0-1