LCOV - code coverage report
Current view: top level - src - xas_tdp_kernel.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 90.2 % 437 394
Test Date: 2026-07-25 06:35:44 Functions: 93.3 % 15 14

            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 All the kernel specific subroutines for XAS TDP calculations
      10              : !> \author A. Bussy (03.2019)
      11              : ! **************************************************************************************************
      12              : 
      13              : MODULE xas_tdp_kernel
      14              :    USE basis_set_types,                 ONLY: gto_basis_set_p_type
      15              :    USE cp_dbcsr_api,                    ONLY: &
      16              :         dbcsr_add, dbcsr_complete_redistribute, dbcsr_copy, dbcsr_create, dbcsr_desymmetrize, &
      17              :         dbcsr_distribution_get, dbcsr_distribution_new, dbcsr_distribution_release, &
      18              :         dbcsr_distribution_type, dbcsr_filter, dbcsr_finalize, dbcsr_get_block_p, dbcsr_get_info, &
      19              :         dbcsr_get_num_blocks, dbcsr_get_stored_coordinates, dbcsr_iterator_blocks_left, &
      20              :         dbcsr_iterator_next_block, dbcsr_iterator_start, dbcsr_iterator_stop, dbcsr_iterator_type, &
      21              :         dbcsr_multiply, dbcsr_p_type, dbcsr_put_block, dbcsr_release, dbcsr_reserve_blocks, &
      22              :         dbcsr_set, dbcsr_transposed, dbcsr_type, dbcsr_type_no_symmetry, dbcsr_type_symmetric
      23              :    USE cp_dbcsr_operations,             ONLY: cp_dbcsr_dist2d_to_dist,&
      24              :                                               dbcsr_deallocate_matrix_set
      25              :    USE dbt_api,                         ONLY: dbt_get_block,&
      26              :                                               dbt_iterator_blocks_left,&
      27              :                                               dbt_iterator_next_block,&
      28              :                                               dbt_iterator_start,&
      29              :                                               dbt_iterator_stop,&
      30              :                                               dbt_iterator_type,&
      31              :                                               dbt_type
      32              :    USE distribution_2d_types,           ONLY: distribution_2d_type
      33              :    USE kinds,                           ONLY: dp
      34              :    USE message_passing,                 ONLY: mp_para_env_type
      35              :    USE particle_methods,                ONLY: get_particle_set
      36              :    USE particle_types,                  ONLY: particle_type
      37              :    USE qs_environment_types,            ONLY: get_qs_env,&
      38              :                                               qs_environment_type
      39              :    USE qs_integral_utils,               ONLY: basis_set_list_setup
      40              :    USE qs_kind_types,                   ONLY: qs_kind_type
      41              :    USE util,                            ONLY: get_limit
      42              :    USE xas_tdp_types,                   ONLY: donor_state_type,&
      43              :                                               get_proc_batch_sizes,&
      44              :                                               xas_tdp_control_type,&
      45              :                                               xas_tdp_env_type
      46              : 
      47              : !$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num
      48              : #include "./base/base_uses.f90"
      49              : 
      50              :    IMPLICIT NONE
      51              :    PRIVATE
      52              : 
      53              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'xas_tdp_kernel'
      54              : 
      55              :    PUBLIC :: kernel_coulomb_xc, kernel_exchange, contract2_AO_to_doMO, &
      56              :              reserve_contraction_blocks, ri_all_blocks_mm
      57              : 
      58              : CONTAINS
      59              : 
      60              : ! **************************************************************************************************
      61              : !> \brief Computes, if asked for it, the Coulomb and XC kernel matrices, in the usuall matrix format
      62              : !> \param coul_ker pointer the the Coulomb kernel matrix (can be void pointer)
      63              : !> \param xc_ker array of pointer to the different xc kernels (5 of them):
      64              : !>               1) the restricted closed-shell singlet kernel
      65              : !>               2) the restricted closed-shell triplet kernel
      66              : !>               3) the spin-conserving open-shell xc kernel
      67              : !>               4) the on-diagonal spin-flip open-shell xc kernel
      68              : !> \param donor_state ...
      69              : !> \param xas_tdp_env ...
      70              : !> \param xas_tdp_control ...
      71              : !> \param qs_env ...
      72              : !> \note Coulomb and xc kernel are put together in the same routine because they use the same RI
      73              : !>       Coulomb: (aI|Jb) = (aI|P) (P|Q)^-1 (Q|Jb)
      74              : !>       XC : (aI|fxc|Jb) = (aI|P) (P|Q)^-1 (Q|fxc|R) (R|S)^-1 (S|Jb)
      75              : !>       In the above formula, a,b label the sgfs
      76              : !>       The routine analyses the xas_tdp_control to know which kernel must be computed and how
      77              : !>       (open-shell, singlet, triplet, ROKS, LSD, etc...)
      78              : !>       On entry, the pointers should be allocated
      79              : ! **************************************************************************************************
      80          156 :    SUBROUTINE kernel_coulomb_xc(coul_ker, xc_ker, donor_state, xas_tdp_env, xas_tdp_control, qs_env)
      81              : 
      82              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: coul_ker
      83              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: xc_ker
      84              :       TYPE(donor_state_type), POINTER                    :: donor_state
      85              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
      86              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
      87              :       TYPE(qs_environment_type), POINTER                 :: qs_env
      88              : 
      89              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'kernel_coulomb_xc'
      90              : 
      91              :       INTEGER                                            :: batch_size, bo(2), handle, i, ibatch, &
      92              :                                                             iex, lb, natom, nbatch, ndo_mo, &
      93              :                                                             ndo_so, nex_atom, nsgfp, ri_atom, &
      94              :                                                             source, ub
      95           78 :       INTEGER, DIMENSION(:), POINTER                     :: blk_size
      96              :       LOGICAL                                            :: do_coulomb, do_sc, do_sf, do_sg, do_tp, &
      97              :                                                             do_xc, found
      98              :       REAL(dp), DIMENSION(:, :), POINTER                 :: PQ
      99              :       TYPE(dbcsr_distribution_type), POINTER             :: dist
     100           78 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: contr1_int
     101              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     102              : 
     103           78 :       NULLIFY (contr1_int, PQ, para_env, dist, blk_size)
     104              : 
     105              : !  Initialization
     106           78 :       ndo_mo = donor_state%ndo_mo
     107           78 :       do_xc = xas_tdp_control%do_xc
     108           78 :       do_sg = xas_tdp_control%do_singlet
     109           78 :       do_tp = xas_tdp_control%do_triplet
     110           78 :       do_sc = xas_tdp_control%do_spin_cons
     111           78 :       do_sf = xas_tdp_control%do_spin_flip
     112           78 :       ndo_so = ndo_mo; IF (xas_tdp_control%do_uks) ndo_so = 2*ndo_mo
     113           78 :       ri_atom = donor_state%at_index
     114           78 :       CALL get_qs_env(qs_env, natom=natom, para_env=para_env)
     115           78 :       do_coulomb = xas_tdp_control%do_coulomb
     116           78 :       dist => donor_state%dbcsr_dist
     117           78 :       blk_size => donor_state%blk_size
     118              : 
     119              : !  If no Coulomb nor xc, simply exit
     120           78 :       IF ((.NOT. do_coulomb) .AND. (.NOT. do_xc)) RETURN
     121              : 
     122           78 :       CALL timeset(routineN, handle)
     123              : 
     124              : !  Contract the RI 3-center integrals once to get (aI|P)
     125           78 :       CALL contract2_AO_to_doMO(contr1_int, "COULOMB", donor_state, xas_tdp_env, xas_tdp_control, qs_env)
     126              : 
     127              : !  Deal with the Coulomb case
     128           78 :       IF (do_coulomb) CALL coulomb(coul_ker, contr1_int, dist, blk_size, xas_tdp_env, &
     129           78 :                                    xas_tdp_control, qs_env)
     130              : 
     131              : !  Deal with the XC case
     132           78 :       IF (do_xc) THEN
     133              : 
     134              :          ! In the end, we compute: (aI|fxc|Jb) = (aI|P) (P|Q)^-1 (Q|fxc|R) (R|S)^-1 (S|Jb)
     135              :          ! where fxc can take different spin contributions.
     136              : 
     137              :          ! Precompute the product (aI|P) * (P|Q)^-1 and store it in contr1_int
     138           70 :          PQ => xas_tdp_env%ri_inv_coul
     139           70 :          CALL ri_all_blocks_mm(contr1_int, PQ)
     140              : 
     141              :          ! If not already done (e.g. when multpile donor states for a given excited atom), broadcast
     142              :          ! the RI matrix (Q|fxc|R) on all procs
     143           70 :          IF (.NOT. xas_tdp_env%fxc_avail) THEN
     144              :             ! Find on which processor the integrals (Q|fxc|R) for this atom are stored
     145           68 :             nsgfp = SIZE(PQ, 1)
     146           68 :             CALL get_qs_env(qs_env, para_env=para_env)
     147           68 :             found = .FALSE.
     148           68 :             nex_atom = SIZE(xas_tdp_env%ex_atom_indices)
     149           68 :             CALL get_proc_batch_sizes(batch_size, nbatch, nex_atom, para_env%num_pe)
     150              : 
     151           80 :             DO ibatch = 0, nbatch - 1
     152              : 
     153           80 :                bo = get_limit(nex_atom, nbatch, ibatch)
     154           92 :                DO iex = bo(1), bo(2)
     155              : 
     156           92 :                   IF (xas_tdp_env%ex_atom_indices(iex) == ri_atom) THEN
     157           68 :                      source = ibatch*batch_size
     158              :                      found = .TRUE. !but simply take the first
     159              :                      EXIT
     160              :                   END IF
     161              :                END DO !iex
     162            0 :                IF (found) EXIT
     163              :             END DO !ip
     164              : 
     165              :             ! Broadcast the integrals to all procs (deleted after all donor states for this atoms are treated)
     166           68 :             lb = 1; IF (do_sf .AND. .NOT. do_sc) lb = 4
     167           68 :             ub = 2; IF (do_sc) ub = 3
     168           68 :             IF (do_sf) ub = 4
     169          212 :             DO i = lb, ub
     170          144 :                IF (.NOT. ASSOCIATED(xas_tdp_env%ri_fxc(ri_atom, i)%array)) THEN
     171           96 :                   ALLOCATE (xas_tdp_env%ri_fxc(ri_atom, i)%array(nsgfp, nsgfp))
     172              :                END IF
     173      1992692 :                CALL para_env%bcast(xas_tdp_env%ri_fxc(ri_atom, i)%array, source)
     174              :             END DO
     175              : 
     176           68 :             xas_tdp_env%fxc_avail = .TRUE.
     177              :          END IF
     178              : 
     179              :          ! Case study on the calculation type
     180           70 :          IF (do_sg .OR. do_tp) THEN
     181              :             CALL rcs_xc(xc_ker(1)%matrix, xc_ker(2)%matrix, contr1_int, dist, blk_size, &
     182           62 :                         donor_state, xas_tdp_env, xas_tdp_control, qs_env)
     183              :          END IF
     184              : 
     185           70 :          IF (do_sc) THEN
     186              :             CALL sc_os_xc(xc_ker(3)%matrix, contr1_int, dist, blk_size, donor_state, &
     187            8 :                           xas_tdp_env, xas_tdp_control, qs_env)
     188              :          END IF
     189              : 
     190           70 :          IF (do_sf) THEN
     191              :             CALL ondiag_sf_os_xc(xc_ker(4)%matrix, contr1_int, dist, blk_size, donor_state, &
     192            0 :                                  xas_tdp_env, xas_tdp_control, qs_env)
     193              :          END IF
     194              : 
     195              :       END IF ! do_xc
     196              : 
     197              : !  Clean-up
     198           78 :       CALL dbcsr_deallocate_matrix_set(contr1_int)
     199              : 
     200           78 :       CALL timestop(handle)
     201              : 
     202           78 :    END SUBROUTINE kernel_coulomb_xc
     203              : 
     204              : ! **************************************************************************************************
     205              : !> \brief Create the matrix containing the Coulomb kernel, which is:
     206              : !>        (aI_sigma|J_tau b) ~= (aI_sigma|P) * (P|Q) * (Q|J_tau b)
     207              : !> \param coul_ker the Coulomb kernel
     208              : !> \param contr1_int the once contracted RI integrals (aI|P)
     209              : !> \param dist the inherited dbcsr ditribution
     210              : !> \param blk_size the inherited block sizes
     211              : !> \param xas_tdp_env ...
     212              : !> \param xas_tdp_control ...
     213              : !> \param qs_env ...
     214              : ! **************************************************************************************************
     215           78 :    SUBROUTINE coulomb(coul_ker, contr1_int, dist, blk_size, xas_tdp_env, xas_tdp_control, qs_env)
     216              : 
     217              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: coul_ker
     218              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: contr1_int
     219              :       TYPE(dbcsr_distribution_type), POINTER             :: dist
     220              :       INTEGER, DIMENSION(:), POINTER                     :: blk_size
     221              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
     222              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
     223              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     224              : 
     225              :       LOGICAL                                            :: quadrants(3)
     226              :       REAL(dp), DIMENSION(:, :), POINTER                 :: PQ
     227           78 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: lhs_int, rhs_int
     228              :       TYPE(dbcsr_type)                                   :: work_mat
     229              : 
     230           78 :       NULLIFY (PQ, rhs_int, lhs_int)
     231              : 
     232              :       ! Get the inver RI coulomb
     233           78 :       PQ => xas_tdp_env%ri_inv_coul
     234              : 
     235              :       ! Create a normal type work matrix
     236              :       CALL dbcsr_create(work_mat, name="WORK", matrix_type=dbcsr_type_no_symmetry, dist=dist, &
     237           78 :                         row_blk_size=blk_size, col_blk_size=blk_size)
     238              : 
     239              :       ! Compute the product (aI|P) * (P|Q)^-1 * (Q|Jb) = (aI|Jb)
     240           78 :       rhs_int => contr1_int ! the incoming contr1_int is not modified
     241          336 :       ALLOCATE (lhs_int(SIZE(contr1_int)))
     242           78 :       CALL copy_ri_contr_int(lhs_int, rhs_int) ! RHS containts (Q|JB)^T
     243           78 :       CALL ri_all_blocks_mm(lhs_int, PQ) ! LHS contatins (aI|P)*(P|Q)^-1
     244              : 
     245              :       !In the special case of ROKS, same MOs for each spin => put same (aI|Jb) product on the
     246              :       !alpha-alpha, alpha-beta and beta-beta quadrants of the kernel matrix.
     247           78 :       IF (xas_tdp_control%do_roks) THEN
     248            2 :          quadrants = [.TRUE., .TRUE., .TRUE.]
     249              :       ELSE
     250           76 :          quadrants = [.TRUE., .FALSE., .FALSE.]
     251              :       END IF
     252              :       CALL ri_int_product(work_mat, lhs_int, rhs_int, quadrants, qs_env, &
     253           78 :                           eps_filter=xas_tdp_control%eps_filter)
     254           78 :       CALL dbcsr_finalize(work_mat)
     255              : 
     256              :       !Create the symmetric kernel matrix and redistribute work_mat into it
     257              :       CALL dbcsr_create(coul_ker, name="COULOMB KERNEL", matrix_type=dbcsr_type_symmetric, dist=dist, &
     258           78 :                         row_blk_size=blk_size, col_blk_size=blk_size)
     259           78 :       CALL dbcsr_complete_redistribute(work_mat, coul_ker)
     260              : 
     261              :       !clean-up
     262           78 :       CALL dbcsr_release(work_mat)
     263           78 :       CALL dbcsr_deallocate_matrix_set(lhs_int)
     264              : 
     265           78 :    END SUBROUTINE coulomb
     266              : 
     267              : ! **************************************************************************************************
     268              : !> \brief Create the matrix containing the XC kenrel in the spin-conserving open-shell case:
     269              : !>        (aI_sigma|fxc|J_tau b) ~= (aI_sigma|P) (P|Q)^-1 (Q|fxc|R) (R|S)^-1 (S|J_tau b)
     270              : !> \param xc_ker the kernel matrix
     271              : !> \param contr1_int_PQ the once contracted RI integrals, with inverse coulomb: (aI_sigma|P) (P|Q)^-1
     272              : !> \param dist inherited dbcsr dist
     273              : !> \param blk_size inherited block sizes
     274              : !> \param donor_state ...
     275              : !> \param xas_tdp_env ...
     276              : !> \param xas_tdp_control ...
     277              : !> \param qs_env ...
     278              : !> note Prior to calling this function, the (Q|fxc|R) integral must be brodcasted to all procs
     279              : ! **************************************************************************************************
     280            8 :    SUBROUTINE sc_os_xc(xc_ker, contr1_int_PQ, dist, blk_size, donor_state, xas_tdp_env, &
     281              :                        xas_tdp_control, qs_env)
     282              : 
     283              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: xc_ker
     284              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: contr1_int_PQ
     285              :       TYPE(dbcsr_distribution_type), POINTER             :: dist
     286              :       INTEGER, DIMENSION(:), POINTER                     :: blk_size
     287              :       TYPE(donor_state_type), POINTER                    :: donor_state
     288              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
     289              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
     290              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     291              : 
     292              :       INTEGER                                            :: ndo_mo, ri_atom
     293              :       LOGICAL                                            :: quadrants(3)
     294            8 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: lhs_int, rhs_int
     295              :       TYPE(dbcsr_type)                                   :: work_mat
     296              : 
     297            8 :       NULLIFY (lhs_int, rhs_int)
     298              : 
     299              :       ! Initialization
     300            8 :       ndo_mo = donor_state%ndo_mo
     301            8 :       ri_atom = donor_state%at_index
     302              :       !normal type work matrix such that distribution of all spin quadrants match
     303              :       CALL dbcsr_create(work_mat, name="WORK", matrix_type=dbcsr_type_no_symmetry, dist=dist, &
     304            8 :                         row_blk_size=blk_size, col_blk_size=blk_size)
     305              : 
     306            8 :       rhs_int => contr1_int_PQ ! contains [ (aI|P)*(P|Q)^-1 ]^T
     307           38 :       ALLOCATE (lhs_int(SIZE(contr1_int_PQ))) ! will contain (aI|P)*(P|Q)^-1 * (Q|fxc|R)
     308              : 
     309              :       ! Case study: UKS or ROKS ?
     310            8 :       IF (xas_tdp_control%do_uks) THEN
     311              : 
     312              :          ! In the case of UKS, donor MOs might be different for different spins. Moreover, the
     313              :          ! fxc itself might change since fxc = fxc_sigma,tau
     314              :          ! => Carfully treat each spin-quadrant separately
     315              : 
     316              :          ! alpha-alpha spin quadrant (upper-lefet)
     317            6 :          quadrants = [.TRUE., .FALSE., .FALSE.]
     318              : 
     319              :          ! Copy the alpha part into lhs_int, multiply by the alpha-alpha (Q|fxc|R) and then
     320              :          ! by the alpha part of rhs_int
     321            6 :          CALL copy_ri_contr_int(lhs_int(1:ndo_mo), rhs_int(1:ndo_mo))
     322            6 :          CALL ri_all_blocks_mm(lhs_int(1:ndo_mo), xas_tdp_env%ri_fxc(ri_atom, 1)%array)
     323              :          CALL ri_int_product(work_mat, lhs_int(1:ndo_mo), rhs_int(1:ndo_mo), quadrants, qs_env, &
     324            6 :                              eps_filter=xas_tdp_control%eps_filter)
     325              : 
     326              :          ! alpha-beta spin quadrant (upper-right)
     327            6 :          quadrants = [.FALSE., .TRUE., .FALSE.]
     328              : 
     329              :          !Copy the alpha part into LHS, multiply by the alpha-beta kernel and the beta part of RHS
     330            6 :          CALL copy_ri_contr_int(lhs_int(1:ndo_mo), rhs_int(1:ndo_mo))
     331            6 :          CALL ri_all_blocks_mm(lhs_int(1:ndo_mo), xas_tdp_env%ri_fxc(ri_atom, 2)%array)
     332              :          CALL ri_int_product(work_mat, lhs_int(1:ndo_mo), rhs_int(ndo_mo + 1:2*ndo_mo), &
     333            6 :                              quadrants, qs_env, eps_filter=xas_tdp_control%eps_filter)
     334              : 
     335              :          ! beta-beta spin quadrant (lower-right)
     336            6 :          quadrants = [.FALSE., .FALSE., .TRUE.]
     337              : 
     338              :          !Copy the beta part into LHS, multiply by the beta-beta kernel and the beta part of RHS
     339            6 :          CALL copy_ri_contr_int(lhs_int(ndo_mo + 1:2*ndo_mo), rhs_int(ndo_mo + 1:2*ndo_mo))
     340            6 :          CALL ri_all_blocks_mm(lhs_int(ndo_mo + 1:2*ndo_mo), xas_tdp_env%ri_fxc(ri_atom, 3)%array)
     341              :          CALL ri_int_product(work_mat, lhs_int(ndo_mo + 1:2*ndo_mo), rhs_int(ndo_mo + 1:2*ndo_mo), &
     342            6 :                              quadrants, qs_env, eps_filter=xas_tdp_control%eps_filter)
     343              : 
     344            2 :       ELSE IF (xas_tdp_control%do_roks) THEN
     345              : 
     346              :          ! In the case of ROKS, fxc = fxc_sigma,tau is different for each spin quadrant, but the
     347              :          ! donor MOs remain the same
     348              : 
     349              :          ! alpha-alpha kernel in the upper left quadrant
     350            2 :          quadrants = [.TRUE., .FALSE., .FALSE.]
     351              : 
     352              :          !Copy the LHS and multiply by alpha-alpha kernel
     353            2 :          CALL copy_ri_contr_int(lhs_int, rhs_int)
     354            2 :          CALL ri_all_blocks_mm(lhs_int, xas_tdp_env%ri_fxc(ri_atom, 1)%array)
     355              :          CALL ri_int_product(work_mat, lhs_int, rhs_int, quadrants, qs_env, &
     356            2 :                              eps_filter=xas_tdp_control%eps_filter)
     357              : 
     358              :          ! alpha-beta kernel in the upper-right quadrant
     359            2 :          quadrants = [.FALSE., .TRUE., .FALSE.]
     360              : 
     361              :          !Copy LHS and multiply by the alpha-beta kernel
     362            2 :          CALL copy_ri_contr_int(lhs_int, rhs_int)
     363            2 :          CALL ri_all_blocks_mm(lhs_int, xas_tdp_env%ri_fxc(ri_atom, 2)%array)
     364              :          CALL ri_int_product(work_mat, lhs_int, rhs_int, quadrants, qs_env, &
     365            2 :                              eps_filter=xas_tdp_control%eps_filter)
     366              : 
     367              :          ! beta-beta kernel in the lower-right quadrant
     368            2 :          quadrants = [.FALSE., .FALSE., .TRUE.]
     369              : 
     370              :          !Copy the LHS and multiply by the beta-beta kernel
     371            2 :          CALL copy_ri_contr_int(lhs_int, rhs_int)
     372            2 :          CALL ri_all_blocks_mm(lhs_int, xas_tdp_env%ri_fxc(ri_atom, 3)%array)
     373              :          CALL ri_int_product(work_mat, lhs_int, rhs_int, quadrants, qs_env, &
     374            2 :                              eps_filter=xas_tdp_control%eps_filter)
     375              : 
     376              :       END IF
     377            8 :       CALL dbcsr_finalize(work_mat)
     378              : 
     379              :       ! Create a symmetric kernel matrix and redistribute the normal work matrix into it
     380              :       CALL dbcsr_create(xc_ker, name="SC OS XC KERNEL", matrix_type=dbcsr_type_symmetric, dist=dist, &
     381            8 :                         row_blk_size=blk_size, col_blk_size=blk_size)
     382            8 :       CALL dbcsr_complete_redistribute(work_mat, xc_ker)
     383              : 
     384              :       !clean-up
     385            8 :       CALL dbcsr_deallocate_matrix_set(lhs_int)
     386            8 :       CALL dbcsr_release(work_mat)
     387              : 
     388            8 :    END SUBROUTINE sc_os_xc
     389              : 
     390              : ! **************************************************************************************************
     391              : !> \brief Create the matrix containing the on-diagonal spin-flip XC kernel (open-shell), which is:
     392              : !>        (a I_sigma|fxc|J_tau b) * delta_sigma,tau, fxc = 1/(rhoa-rhob) * (dE/drhoa - dE/drhob)
     393              : !>        with RI: (a I_sigma|fxc|J_tau b) ~= (aI_sigma|P) (P|Q)^-1 (Q|fxc|R) (R|S)^-1 (S|J_tau b)
     394              : !> \param xc_ker the kernel matrix
     395              : !> \param contr1_int_PQ the once contracted RI integrals with coulomb product: (aI_sigma|P) (P|Q)^-1
     396              : !> \param dist inherited dbcsr dist
     397              : !> \param blk_size inherited block sizes
     398              : !> \param donor_state ...
     399              : !> \param xas_tdp_env ...
     400              : !> \param xas_tdp_control ...
     401              : !> \param qs_env ...
     402              : !> \note  It must be later on multiplied by the spin-swapped Q projector
     403              : !>        Prior to calling this function, the (Q|fxc|R) integral must be brodcasted to all procs
     404              : ! **************************************************************************************************
     405            0 :    SUBROUTINE ondiag_sf_os_xc(xc_ker, contr1_int_PQ, dist, blk_size, donor_state, xas_tdp_env, &
     406              :                               xas_tdp_control, qs_env)
     407              : 
     408              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: xc_ker
     409              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: contr1_int_PQ
     410              :       TYPE(dbcsr_distribution_type), POINTER             :: dist
     411              :       INTEGER, DIMENSION(:), POINTER                     :: blk_size
     412              :       TYPE(donor_state_type), POINTER                    :: donor_state
     413              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
     414              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
     415              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     416              : 
     417              :       INTEGER                                            :: ndo_mo, ri_atom
     418              :       LOGICAL                                            :: quadrants(3)
     419            0 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: lhs_int, rhs_int
     420              :       TYPE(dbcsr_type)                                   :: work_mat
     421              : 
     422            0 :       NULLIFY (lhs_int, rhs_int)
     423              : 
     424              :       ! Initialization
     425            0 :       ndo_mo = donor_state%ndo_mo
     426            0 :       ri_atom = donor_state%at_index
     427              :       !normal type work matrix such that distribution of all spin quadrants match
     428              :       CALL dbcsr_create(work_mat, name="WORK", matrix_type=dbcsr_type_no_symmetry, dist=dist, &
     429            0 :                         row_blk_size=blk_size, col_blk_size=blk_size)
     430              : 
     431              :       !Create a lhs_int, in which the whole (aI_sigma|P) (P|Q)^-1 (Q|fxc|R) will be put
     432              :       !because in spin-flip fxc is spin-independent, can take the product once and for all
     433            0 :       rhs_int => contr1_int_PQ
     434            0 :       ALLOCATE (lhs_int(SIZE(contr1_int_PQ)))
     435            0 :       CALL copy_ri_contr_int(lhs_int, rhs_int)
     436            0 :       CALL ri_all_blocks_mm(lhs_int, xas_tdp_env%ri_fxc(ri_atom, 4)%array)
     437              : 
     438              :       ! Case study: UKS or ROKS ?
     439            0 :       IF (xas_tdp_control%do_uks) THEN
     440              : 
     441              :          ! In the case of UKS, donor MOs might be different for different spins
     442              :          ! => Carfully treat each spin-quadrant separately
     443              :          ! NO alpha-beta because of the delta_sigma,tau
     444              : 
     445              :          ! alpha-alpha spin quadrant (upper-lefet)
     446            0 :          quadrants = [.TRUE., .FALSE., .FALSE.]
     447              :          CALL ri_int_product(work_mat, lhs_int(1:ndo_mo), rhs_int(1:ndo_mo), quadrants, qs_env, &
     448            0 :                              eps_filter=xas_tdp_control%eps_filter)
     449              : 
     450              :          ! beta-beta spin quadrant (lower-right)
     451            0 :          quadrants = [.FALSE., .FALSE., .TRUE.]
     452              :          CALL ri_int_product(work_mat, lhs_int(ndo_mo + 1:2*ndo_mo), rhs_int(ndo_mo + 1:2*ndo_mo), &
     453            0 :                              quadrants, qs_env, eps_filter=xas_tdp_control%eps_filter)
     454              : 
     455            0 :       ELSE IF (xas_tdp_control%do_roks) THEN
     456              : 
     457              :          ! In the case of ROKS, same donor MOs for both spins => can do it all at once
     458              :          ! But NOT the alpha-beta quadrant because of delta_sigma,tau
     459              : 
     460            0 :          quadrants = [.TRUE., .FALSE., .TRUE.]
     461              :          CALL ri_int_product(work_mat, lhs_int, rhs_int, quadrants, qs_env, &
     462            0 :                              eps_filter=xas_tdp_control%eps_filter)
     463              : 
     464              :       END IF
     465            0 :       CALL dbcsr_finalize(work_mat)
     466              : 
     467              :       ! Create a symmetric kernel matrix and redistribute the normal work matrix into it
     468              :       CALL dbcsr_create(xc_ker, name="ON-DIAG SF OS XC KERNEL", matrix_type=dbcsr_type_symmetric, &
     469            0 :                         dist=dist, row_blk_size=blk_size, col_blk_size=blk_size)
     470            0 :       CALL dbcsr_complete_redistribute(work_mat, xc_ker)
     471              : 
     472              :       !clean-up
     473            0 :       CALL dbcsr_deallocate_matrix_set(lhs_int)
     474            0 :       CALL dbcsr_release(work_mat)
     475              : 
     476            0 :    END SUBROUTINE ondiag_sf_os_xc
     477              : 
     478              : ! **************************************************************************************************
     479              : !> \brief Create the matrix containing the XC kernel in the restricted closed-shell case, for
     480              : !>        singlets: (aI|fxc|Jb) = (aI|P) (P|Q)^-1 (Q|fxc_alp,alp+fxc_alp,bet|R) (R|S)^-1 (S|Jb)
     481              : !>        triplets: (aI|fxc|Jb) = (aI|P) (P|Q)^-1 (Q|fxc_alp,alp-fxc_alp,bet|R) (R|S)^-1 (S|Jb)
     482              : !> \param sg_xc_ker the singlet kernel matrix
     483              : !> \param tp_xc_ker the triplet kernel matrix
     484              : !> \param contr1_int_PQ the once contracted RI integrals including inverse 2-center Coulomb prodcut:
     485              : !>                      (aI|P)*(P|Q)^-1
     486              : !> \param dist inherited dbcsr dist
     487              : !> \param blk_size inherited block sizes
     488              : !> \param donor_state ...
     489              : !> \param xas_tdp_env ...
     490              : !> \param xas_tdp_control ...
     491              : !> \param qs_env ...
     492              : ! **************************************************************************************************
     493           62 :    SUBROUTINE rcs_xc(sg_xc_ker, tp_xc_ker, contr1_int_PQ, dist, blk_size, donor_state, &
     494              :                      xas_tdp_env, xas_tdp_control, qs_env)
     495              : 
     496              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: sg_xc_ker, tp_xc_ker
     497              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: contr1_int_PQ
     498              :       TYPE(dbcsr_distribution_type), POINTER             :: dist
     499              :       INTEGER, DIMENSION(:), POINTER                     :: blk_size
     500              :       TYPE(donor_state_type), POINTER                    :: donor_state
     501              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
     502              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
     503              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     504              : 
     505              :       INTEGER                                            :: nsgfp, ri_atom
     506              :       LOGICAL                                            :: quadrants(3)
     507              :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: fxc
     508           62 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: lhs_int, rhs_int
     509              :       TYPE(dbcsr_type)                                   :: work_mat
     510              : 
     511           62 :       NULLIFY (lhs_int, rhs_int)
     512              : 
     513              :       ! Initialization
     514           62 :       ri_atom = donor_state%at_index
     515           62 :       nsgfp = SIZE(xas_tdp_env%ri_fxc(ri_atom, 1)%array, 1)
     516           62 :       rhs_int => contr1_int_PQ ! RHS contains [ (aI|P)*(P|Q)^-1 ]^T
     517          248 :       ALLOCATE (lhs_int(SIZE(contr1_int_PQ))) ! LHS will contatin (aI|P)*(P|Q)^-1 * (Q|fxc|R)
     518              : 
     519              :       ! Work structures
     520          248 :       ALLOCATE (fxc(nsgfp, nsgfp))
     521              :       CALL dbcsr_create(work_mat, name="WORK", matrix_type=dbcsr_type_no_symmetry, dist=dist, &
     522           62 :                         row_blk_size=blk_size, col_blk_size=blk_size)
     523              : 
     524              :       ! Case study: singlet and/or triplet ?
     525           62 :       IF (xas_tdp_control%do_singlet) THEN
     526              : 
     527              :          ! Take the sum of fxc for alpha-alpha and alpha-beta
     528           62 :          CALL dcopy(nsgfp*nsgfp, xas_tdp_env%ri_fxc(ri_atom, 1)%array, 1, fxc, 1)
     529           62 :          CALL daxpy(nsgfp*nsgfp, 1.0_dp, xas_tdp_env%ri_fxc(ri_atom, 2)%array, 1, fxc, 1)
     530              : 
     531              :          ! Copy the fresh lhs_int = (aI|P) (P|Q)^-1 and multiply by (Q|fxc|R)
     532           62 :          CALL copy_ri_contr_int(lhs_int, rhs_int)
     533           62 :          CALL ri_all_blocks_mm(lhs_int, fxc)
     534              : 
     535              :          ! Compute the final LHS RHS product => spin-restricted, only upper-left quadrant
     536           62 :          quadrants = [.TRUE., .FALSE., .FALSE.]
     537              :          CALL ri_int_product(work_mat, lhs_int, rhs_int, quadrants, qs_env, &
     538           62 :                              eps_filter=xas_tdp_control%eps_filter)
     539           62 :          CALL dbcsr_finalize(work_mat)
     540              : 
     541              :          !Create the symmetric kernel matrix and redistribute work_mat into it
     542              :          CALL dbcsr_create(sg_xc_ker, name="XC SINGLET KERNEL", matrix_type=dbcsr_type_symmetric, &
     543           62 :                            dist=dist, row_blk_size=blk_size, col_blk_size=blk_size)
     544           62 :          CALL dbcsr_complete_redistribute(work_mat, sg_xc_ker)
     545              : 
     546              :       END IF
     547              : 
     548           62 :       IF (xas_tdp_control%do_triplet) THEN
     549              : 
     550              :          ! Take the difference of fxc for alpha-alpha and alpha-beta
     551            0 :          CALL dcopy(nsgfp*nsgfp, xas_tdp_env%ri_fxc(ri_atom, 1)%array, 1, fxc, 1)
     552            0 :          CALL daxpy(nsgfp*nsgfp, -1.0_dp, xas_tdp_env%ri_fxc(ri_atom, 2)%array, 1, fxc, 1)
     553              : 
     554              :          ! Copy the fresh lhs_int = (aI|P) (P|Q)^-1 and multiply by (Q|fxc|R)
     555            0 :          CALL copy_ri_contr_int(lhs_int, rhs_int)
     556            0 :          CALL ri_all_blocks_mm(lhs_int, fxc)
     557              : 
     558              :          ! Compute the final LHS RHS product => spin-restricted, only upper-left quadrant
     559            0 :          quadrants = [.TRUE., .FALSE., .FALSE.]
     560              :          CALL ri_int_product(work_mat, lhs_int, rhs_int, quadrants, qs_env, &
     561            0 :                              eps_filter=xas_tdp_control%eps_filter)
     562            0 :          CALL dbcsr_finalize(work_mat)
     563              : 
     564              :          !Create the symmetric kernel matrix and redistribute work_mat into it
     565              :          CALL dbcsr_create(tp_xc_ker, name="XC TRIPLET KERNEL", matrix_type=dbcsr_type_symmetric, &
     566            0 :                            dist=dist, row_blk_size=blk_size, col_blk_size=blk_size)
     567            0 :          CALL dbcsr_complete_redistribute(work_mat, tp_xc_ker)
     568              : 
     569              :       END IF
     570              : 
     571              :       ! clean-up
     572           62 :       CALL dbcsr_deallocate_matrix_set(lhs_int)
     573           62 :       CALL dbcsr_release(work_mat)
     574           62 :       DEALLOCATE (fxc)
     575              : 
     576           62 :    END SUBROUTINE rcs_xc
     577              : 
     578              : ! **************************************************************************************************
     579              : !> \brief Computes the exact exchange kernel matrix using RI. Returns an array of 2 matrices,
     580              : !>        which are:
     581              : !>        1) the on-diagonal kernel: (ab|I_sigma J_tau) * delta_sigma,tau
     582              : !>        2) the off-diagonal spin-conserving kernel: (aJ_sigma|I_tau b) * delta_sigma,tau
     583              : !>        An internal analysis determines which of the above are computed (can range from 0 to 2),
     584              : !> \param ex_ker ...
     585              : !> \param donor_state ...
     586              : !> \param xas_tdp_env ...
     587              : !> \param xas_tdp_control ...
     588              : !> \param qs_env ...
     589              : !> \note In the case of spin-conserving excitation, the kernel must later be multiplied by the
     590              : !>       usual Q projector. In the case of spin-flip, one needs to project the excitations coming
     591              : !>       from alpha donor MOs on the unoccupied beta MOs. This is done by multiplying by a Q
     592              : !>       projector where the alpha-alpha and beta-beta quadrants are swapped
     593              : !>       The ex_ker array should be allocated on entry (not the internals)
     594              : ! **************************************************************************************************
     595          132 :    SUBROUTINE kernel_exchange(ex_ker, donor_state, xas_tdp_env, xas_tdp_control, qs_env)
     596              : 
     597              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: ex_ker
     598              :       TYPE(donor_state_type), POINTER                    :: donor_state
     599              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
     600              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
     601              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     602              : 
     603              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'kernel_exchange'
     604              : 
     605              :       INTEGER                                            :: handle
     606           78 :       INTEGER, DIMENSION(:), POINTER                     :: blk_size
     607              :       LOGICAL                                            :: do_off_sc
     608              :       TYPE(dbcsr_distribution_type), POINTER             :: dist
     609           78 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: contr1_int
     610              : 
     611           78 :       NULLIFY (contr1_int, dist, blk_size)
     612              : 
     613              :       !Don't do anything if no hfx
     614           24 :       IF (.NOT. xas_tdp_control%do_hfx) RETURN
     615              : 
     616           54 :       CALL timeset(routineN, handle)
     617              : 
     618           54 :       dist => donor_state%dbcsr_dist
     619           54 :       blk_size => donor_state%blk_size
     620              : 
     621              :       !compute the off-diag spin-conserving only if not TDA and anything that is spin-conserving
     622              :       do_off_sc = (.NOT. xas_tdp_control%tamm_dancoff) .AND. &
     623           54 :                   (xas_tdp_control%do_spin_cons .OR. xas_tdp_control%do_singlet .OR. xas_tdp_control%do_triplet)
     624              : 
     625              :       ! Need the once contracted integrals (aI|P)
     626           54 :       CALL contract2_AO_to_doMO(contr1_int, "EXCHANGE", donor_state, xas_tdp_env, xas_tdp_control, qs_env)
     627              : 
     628              : !  The on-diagonal exchange : (ab|P) * (P|Q)^-1 * (Q|I_sigma J_tau) * delta_sigma,tau
     629              :       CALL ondiag_ex(ex_ker(1)%matrix, contr1_int, dist, blk_size, donor_state, xas_tdp_env, &
     630           54 :                      xas_tdp_control, qs_env)
     631              : 
     632              : !  The off-diag spin-conserving case: (aJ_sigma|P) * (P|Q)^-1 * (Q|I_tau b) * delta_sigma,tau
     633           54 :       IF (do_off_sc) THEN
     634              :          CALL offdiag_ex_sc(ex_ker(2)%matrix, contr1_int, dist, blk_size, donor_state, &
     635            6 :                             xas_tdp_env, xas_tdp_control, qs_env)
     636              :       END IF
     637              : 
     638              :       !clean-up
     639           54 :       CALL dbcsr_deallocate_matrix_set(contr1_int)
     640              : 
     641           54 :       CALL timestop(handle)
     642              : 
     643           78 :    END SUBROUTINE kernel_exchange
     644              : 
     645              : ! **************************************************************************************************
     646              : !> \brief Create the matrix containing the on-diagonal exact exchange kernel, which is:
     647              : !>        (ab|I_sigma J_tau) * delta_sigma,tau, where a,b are AOs, I_sigma and J_tau are the donor
     648              : !>        spin-orbitals. A RI is done: (ab|I_sigma J_tau) = (ab|P) * (P|Q)^-1 * (Q|I_sigma J_tau)
     649              : !> \param ondiag_ex_ker the on-diagonal exchange kernel in dbcsr format
     650              : !> \param contr1_int the already once-contracted RI 3-center integrals (aI_sigma|P)
     651              : !>        where each matrix of the array contains the contraction for the donor spin-orbital I_sigma
     652              : !> \param dist the inherited dbcsr distribution
     653              : !> \param blk_size the inherited dbcsr block sizes
     654              : !> \param donor_state ...
     655              : !> \param xas_tdp_env ...
     656              : !> \param xas_tdp_control ...
     657              : !> \param qs_env ...
     658              : !> \note In the presence of a RI metric, we have instead M^-1 * (P|Q) * M^-1
     659              : ! **************************************************************************************************
     660           54 :    SUBROUTINE ondiag_ex(ondiag_ex_ker, contr1_int, dist, blk_size, donor_state, xas_tdp_env, &
     661              :                         xas_tdp_control, qs_env)
     662              : 
     663              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: ondiag_ex_ker
     664              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: contr1_int
     665              :       TYPE(dbcsr_distribution_type), POINTER             :: dist
     666              :       INTEGER, DIMENSION(:), POINTER                     :: blk_size
     667              :       TYPE(donor_state_type), POINTER                    :: donor_state
     668              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
     669              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
     670              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     671              : 
     672              :       INTEGER                                            :: group, iblk, iso, jblk, jso, nblk, &
     673              :                                                             ndo_mo, ndo_so, nsgfa, nsgfp, ri_atom, &
     674              :                                                             source
     675           54 :       INTEGER, DIMENSION(:), POINTER                     :: col_dist, col_dist_work, row_dist, &
     676           54 :                                                             row_dist_work
     677           54 :       INTEGER, DIMENSION(:, :), POINTER                  :: pgrid
     678              :       LOGICAL                                            :: do_roks, do_uks, found
     679           54 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: coeffs, ri_coeffs
     680           54 :       REAL(dp), DIMENSION(:, :), POINTER                 :: aIQ, pblock, PQ
     681              :       TYPE(dbcsr_distribution_type)                      :: opt_dbcsr_dist, work_dbcsr_dist
     682              :       TYPE(dbcsr_iterator_type)                          :: iter
     683           54 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     684              :       TYPE(dbcsr_type)                                   :: abIJ, mats_desymm, work_mat
     685              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     686              : 
     687           54 :       NULLIFY (para_env, matrix_s, pblock, aIQ, row_dist, col_dist, row_dist_work, col_dist_work, pgrid)
     688              : 
     689              :       ! We want to compute (ab|I_sigma J_tau) = (ab|P) * (P|Q)^-1 * (Q|I_sigma J_tau)
     690              :       ! Already have (cJ_tau|P)  stored in contr1_int. Need to further contract the
     691              :       ! AOs with the coeff of the I_alpha spin-orbital.
     692              : 
     693              :       ! Initialization
     694           54 :       ndo_mo = donor_state%ndo_mo
     695           54 :       ri_atom = donor_state%at_index
     696           54 :       do_roks = xas_tdp_control%do_roks
     697           54 :       do_uks = xas_tdp_control%do_uks
     698            6 :       ndo_so = ndo_mo; IF (do_uks) ndo_so = 2*ndo_mo !if not UKS, same donor MOs for both spins
     699           54 :       PQ => xas_tdp_env%ri_inv_ex
     700              : 
     701           54 :       CALL get_qs_env(qs_env, para_env=para_env, matrix_s=matrix_s, natom=nblk)
     702           54 :       nsgfp = SIZE(PQ, 1)
     703           54 :       nsgfa = SIZE(donor_state%contract_coeffs, 1)
     704          324 :       ALLOCATE (coeffs(nsgfp, ndo_so), ri_coeffs(nsgfp, ndo_so))
     705              : 
     706              :       ! a and b need to overlap for non-zero (ab|IJ) => same block structure as overlap S
     707              :       ! need compatible distribution_2d with 3c tensor + normal type
     708           54 :       CALL cp_dbcsr_dist2d_to_dist(xas_tdp_env%opt_dist2d_ex, opt_dbcsr_dist)
     709              : 
     710           54 :       CALL dbcsr_desymmetrize(matrix_s(1)%matrix, mats_desymm)
     711              : 
     712           54 :       CALL dbcsr_create(abIJ, template=mats_desymm, name="(ab|IJ)", dist=opt_dbcsr_dist)
     713           54 :       CALL dbcsr_complete_redistribute(mats_desymm, abIJ)
     714              : 
     715           54 :       CALL dbcsr_release(mats_desymm)
     716              : 
     717              :       ! Create a work distribution based on opt_dbcsr_dist, but for full size matrices
     718              :       CALL dbcsr_distribution_get(opt_dbcsr_dist, row_dist=row_dist, col_dist=col_dist, group=group, &
     719           54 :                                   pgrid=pgrid)
     720              : 
     721          162 :       ALLOCATE (row_dist_work(ndo_so*nblk))
     722          108 :       ALLOCATE (col_dist_work(ndo_so*nblk))
     723          126 :       DO iso = 1, ndo_so
     724          632 :          row_dist_work((iso - 1)*nblk + 1:iso*nblk) = row_dist(:)
     725          686 :          col_dist_work((iso - 1)*nblk + 1:iso*nblk) = col_dist(:)
     726              :       END DO
     727              : 
     728              :       CALL dbcsr_distribution_new(work_dbcsr_dist, group=group, pgrid=pgrid, row_dist=row_dist_work, &
     729           54 :                                   col_dist=col_dist_work)
     730              : 
     731              :       CALL dbcsr_create(work_mat, name="WORK", matrix_type=dbcsr_type_no_symmetry, dist=work_dbcsr_dist, &
     732           54 :                         row_blk_size=blk_size, col_blk_size=blk_size)
     733              : 
     734              :       ! Loop over donor spin-orbitals. End matrix is symmetric => span only upper half
     735          126 :       DO iso = 1, ndo_so
     736              : 
     737              :          ! take the (aI|Q) block in contr1_int that has a centered on the excited atom
     738           72 :          CALL dbcsr_get_stored_coordinates(contr1_int(iso)%matrix, ri_atom, ri_atom, source)
     739           72 :          IF (para_env%mepos == source) THEN
     740           36 :             CALL dbcsr_get_block_p(contr1_int(iso)%matrix, ri_atom, ri_atom, aIQ, found)
     741              :          ELSE
     742          144 :             ALLOCATE (aIQ(nsgfa, nsgfp))
     743              :          END IF
     744       188164 :          CALL para_env%bcast(aIQ, source)
     745              : 
     746              :          ! get the contraction (Q|IJ) by taking (Q|Ia)*contract_coeffs and put it in coeffs
     747              :          CALL dgemm('T', 'N', nsgfp, ndo_so, nsgfa, 1.0_dp, aIQ, nsgfa, donor_state%contract_coeffs, &
     748           72 :                     nsgfa, 0.0_dp, coeffs, nsgfp)
     749              : 
     750              :          ! take (P|Q)^-1 * (Q|IJ) and put that in ri_coeffs
     751              :          CALL dgemm('N', 'N', nsgfp, ndo_so, nsgfp, 1.0_dp, PQ, nsgfp, coeffs, nsgfp, 0.0_dp, &
     752           72 :                     ri_coeffs, nsgfp)
     753              : 
     754           72 :          IF (.NOT. para_env%mepos == source) DEALLOCATE (aIQ)
     755              : 
     756          310 :          DO jso = iso, ndo_so
     757              : 
     758              :             ! There is no alpha-beta exchange. In case of UKS, iso,jso span all spin-orbitals
     759              :             ! => CYCLE if iso and jso are indexing MOs with different spin (and we have UKS)
     760          112 :             IF (do_uks .AND. (iso <= ndo_mo .AND. jso > ndo_mo)) CYCLE
     761              : 
     762              :             ! compute (ab|IJ) = sum_P (ab|P) * (P|Q)^-1 * (Q|IJ)
     763           90 :             CALL dbcsr_set(abIJ, 0.0_dp)
     764           90 :             CALL contract3_RI_to_doMOs(xas_tdp_env%ri_3c_ex, ri_coeffs(:, jso), abIJ, ri_atom)
     765              : 
     766              :             ! Loop over (ab|IJ) and copy into work. OK because dist are made to match
     767           90 :             CALL dbcsr_iterator_start(iter, abIJ)
     768          685 :             DO WHILE (dbcsr_iterator_blocks_left(iter))
     769              : 
     770          595 :                CALL dbcsr_iterator_next_block(iter, row=iblk, column=jblk)
     771          595 :                IF (iso == jso .AND. jblk < iblk) CYCLE
     772              : 
     773          363 :                CALL dbcsr_get_block_p(abIJ, iblk, jblk, pblock, found)
     774              : 
     775          453 :                IF (found) THEN
     776          363 :                   CALL dbcsr_put_block(work_mat, (iso - 1)*nblk + iblk, (jso - 1)*nblk + jblk, pblock)
     777              : 
     778              :                   !In case of ROKS, we have (ab|IJ) for alpha-alpha spin, but it is the same for
     779              :                   !beta-beta => replicate the blocks (alpha-beta is zero)
     780          363 :                   IF (do_roks) THEN
     781              :                      !the beta-beta block
     782              :                      CALL dbcsr_put_block(work_mat, (ndo_so + iso - 1)*nblk + iblk, &
     783            0 :                                           (ndo_so + jso - 1)*nblk + jblk, pblock)
     784              :                   END IF
     785              :                END IF
     786              : 
     787              :             END DO !iterator
     788          274 :             CALL dbcsr_iterator_stop(iter)
     789              : 
     790              :          END DO !jso
     791              :       END DO !iso
     792              : 
     793           54 :       CALL dbcsr_finalize(work_mat)
     794              :       CALL dbcsr_create(ondiag_ex_ker, name="ONDIAG EX KERNEL", matrix_type=dbcsr_type_symmetric, &
     795           54 :                         dist=dist, row_blk_size=blk_size, col_blk_size=blk_size)
     796           54 :       CALL dbcsr_complete_redistribute(work_mat, ondiag_ex_ker)
     797              : 
     798              :       !Clean-up
     799           54 :       CALL dbcsr_release(work_mat)
     800           54 :       CALL dbcsr_release(abIJ)
     801           54 :       CALL dbcsr_distribution_release(opt_dbcsr_dist)
     802           54 :       CALL dbcsr_distribution_release(work_dbcsr_dist)
     803           54 :       DEALLOCATE (col_dist_work, row_dist_work)
     804              : 
     805          270 :    END SUBROUTINE ondiag_ex
     806              : 
     807              : ! **************************************************************************************************
     808              : !> \brief Create the matrix containing the off-diagonal exact exchange kernel in the spin-conserving
     809              : !>        case (which also includes excitations from the closed=shell ref state ) This matrix reads:
     810              : !>        (aJ_sigma|I_tau b) * delta_sigma,tau , where a, b are AOs and J_sigma, I_tau are the donor
     811              : !>        spin-orbital. A RI is done: (aJ_sigma|I_tau b) = (aJ_sigma|P) * (P|Q)^-1 * (Q|I_tau b)
     812              : !> \param offdiag_ex_ker the off-diagonal, spin-conserving exchange kernel in dbcsr format
     813              : !> \param contr1_int the once-contracted RI integrals: (aJ_sigma|P)
     814              : !> \param dist the inherited dbcsr ditribution
     815              : !> \param blk_size the inherited block sizes
     816              : !> \param donor_state ...
     817              : !> \param xas_tdp_env ...
     818              : !> \param xas_tdp_control ...
     819              : !> \param qs_env ...
     820              : ! **************************************************************************************************
     821            6 :    SUBROUTINE offdiag_ex_sc(offdiag_ex_ker, contr1_int, dist, blk_size, donor_state, xas_tdp_env, &
     822              :                             xas_tdp_control, qs_env)
     823              : 
     824              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: offdiag_ex_ker
     825              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: contr1_int
     826              :       TYPE(dbcsr_distribution_type), POINTER             :: dist
     827              :       INTEGER, DIMENSION(:), POINTER                     :: blk_size
     828              :       TYPE(donor_state_type), POINTER                    :: donor_state
     829              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
     830              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
     831              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     832              : 
     833              :       INTEGER                                            :: ndo_mo
     834              :       LOGICAL                                            :: do_roks, do_uks, quadrants(3)
     835              :       REAL(dp), DIMENSION(:, :), POINTER                 :: PQ
     836            6 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: lhs_int, rhs_int
     837              :       TYPE(dbcsr_type)                                   :: work_mat
     838              : 
     839            6 :       NULLIFY (PQ, lhs_int, rhs_int)
     840              : 
     841              :       !Initialization
     842            6 :       ndo_mo = donor_state%ndo_mo
     843            6 :       do_roks = xas_tdp_control%do_roks
     844            6 :       do_uks = xas_tdp_control%do_uks
     845            6 :       PQ => xas_tdp_env%ri_inv_ex
     846              : 
     847            6 :       rhs_int => contr1_int
     848           24 :       ALLOCATE (lhs_int(SIZE(contr1_int)))
     849            6 :       CALL copy_ri_contr_int(lhs_int, rhs_int)
     850            6 :       CALL ri_all_blocks_mm(lhs_int, PQ)
     851              : 
     852              :       !Given the lhs_int and rhs_int, all we need to do is multiply elements from the former by
     853              :       !the transpose of the later, and put the result in the correct spin quadrants
     854              : 
     855              :       !Create a normal type work matrix
     856              :       CALL dbcsr_create(work_mat, name="WORK", matrix_type=dbcsr_type_no_symmetry, dist=dist, &
     857            6 :                         row_blk_size=blk_size, col_blk_size=blk_size)
     858              : 
     859              :       !Case study on closed-shell, ROKS or UKS
     860            6 :       IF (do_roks) THEN
     861              :          !In ROKS, the donor MOs for each spin are the same => copy the product in both the
     862              :          !alpha-alpha and the beta-beta quadrants
     863            0 :          quadrants = [.TRUE., .FALSE., .TRUE.]
     864              :          CALL ri_int_product(work_mat, lhs_int, rhs_int, quadrants, qs_env, &
     865            0 :                              eps_filter=xas_tdp_control%eps_filter, mo_transpose=.TRUE.)
     866              : 
     867            6 :       ELSE IF (do_uks) THEN
     868              :          !In UKS, the donor MOs are possibly different for each spin => start with the
     869              :          !alpha-alpha product and the perform the beta-beta product separately
     870            0 :          quadrants = [.TRUE., .FALSE., .FALSE.]
     871              :          CALL ri_int_product(work_mat, lhs_int(1:ndo_mo), rhs_int(1:ndo_mo), quadrants, &
     872            0 :                              qs_env, eps_filter=xas_tdp_control%eps_filter, mo_transpose=.TRUE.)
     873              : 
     874            0 :          quadrants = [.FALSE., .FALSE., .TRUE.]
     875              :          CALL ri_int_product(work_mat, lhs_int(ndo_mo + 1:2*ndo_mo), rhs_int(ndo_mo + 1:2*ndo_mo), &
     876            0 :                              quadrants, qs_env, eps_filter=xas_tdp_control%eps_filter, mo_transpose=.TRUE.)
     877              :       ELSE
     878              :          !In the restricted closed-shell case, only have one spin and a single qudarant
     879            6 :          quadrants = [.TRUE., .FALSE., .FALSE.]
     880              :          CALL ri_int_product(work_mat, lhs_int, rhs_int, quadrants, qs_env, &
     881            6 :                              eps_filter=xas_tdp_control%eps_filter, mo_transpose=.TRUE.)
     882              :       END IF
     883            6 :       CALL dbcsr_finalize(work_mat)
     884              : 
     885              :       !Create the symmetric kernel matrix and redistribute work_mat into it
     886              :       CALL dbcsr_create(offdiag_ex_ker, name="OFFDIAG EX KERNEL", matrix_type=dbcsr_type_symmetric, &
     887            6 :                         dist=dist, row_blk_size=blk_size, col_blk_size=blk_size)
     888            6 :       CALL dbcsr_complete_redistribute(work_mat, offdiag_ex_ker)
     889              : 
     890              :       !clean-up
     891            6 :       CALL dbcsr_release(work_mat)
     892            6 :       CALL dbcsr_deallocate_matrix_set(lhs_int)
     893              : 
     894            6 :    END SUBROUTINE offdiag_ex_sc
     895              : 
     896              : ! **************************************************************************************************
     897              : !> \brief Reserves the blocks in of a dbcsr matrix as needed for RI 3-center contraction (aI|P)
     898              : !> \param matrices the matrices for which blocks are reserved
     899              : !> \param ri_atom the index of the atom on which RI is done (= all coeffs of I are there, and P too)
     900              : !> \param qs_env ...
     901              : !> \note the end product are normal type matrices that are possibly slightly spraser as matrix_s
     902              : ! **************************************************************************************************
     903          166 :    SUBROUTINE reserve_contraction_blocks(matrices, ri_atom, qs_env)
     904              : 
     905              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrices
     906              :       INTEGER, INTENT(IN)                                :: ri_atom
     907              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     908              : 
     909              :       INTEGER                                            :: i, iblk, jblk, max_nblks, nblks
     910          166 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: reserve_cols, reserve_rows
     911              :       TYPE(dbcsr_distribution_type)                      :: dist
     912              :       TYPE(dbcsr_iterator_type)                          :: iter
     913          166 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     914              :       TYPE(dbcsr_type)                                   :: template, work
     915              : 
     916          166 :       NULLIFY (matrix_s)
     917              : 
     918              :       ! Initialization
     919          166 :       CALL get_qs_env(qs_env, matrix_s=matrix_s)
     920          166 :       CALL dbcsr_get_info(matrices(1)%matrix, distribution=dist)
     921              : 
     922              :       ! Need to redistribute matrix_s in the distribution of matrices
     923          166 :       CALL dbcsr_create(work, template=matrix_s(1)%matrix, dist=dist)
     924          166 :       CALL dbcsr_complete_redistribute(matrix_s(1)%matrix, work)
     925              : 
     926              :       ! Need to desymmetrize matrix as as a template
     927          166 :       CALL dbcsr_desymmetrize(work, template)
     928              : 
     929              :       ! Allocate space for block indicies to reserve.
     930          166 :       max_nblks = dbcsr_get_num_blocks(template)
     931          648 :       ALLOCATE (reserve_rows(max_nblks), reserve_cols(max_nblks))
     932              : 
     933              :       ! Loop over matrix_s as need a,b to overlap
     934          166 :       nblks = 0
     935          166 :       CALL dbcsr_iterator_start(iter, template)
     936        19522 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
     937        19356 :          CALL dbcsr_iterator_next_block(iter, row=iblk, column=jblk)
     938              :          !only have a,b pair if one of them is the ri_atom
     939        19522 :          IF (iblk == ri_atom .OR. jblk == ri_atom) THEN
     940          875 :             nblks = nblks + 1
     941          875 :             reserve_rows(nblks) = iblk
     942          875 :             reserve_cols(nblks) = jblk
     943              :          END IF
     944              :       END DO
     945          166 :       CALL dbcsr_iterator_stop(iter)
     946              : 
     947          498 :       DO i = 1, SIZE(matrices)
     948          498 :          CALL dbcsr_reserve_blocks(matrices(i)%matrix, rows=reserve_rows(1:nblks), cols=reserve_cols(1:nblks))
     949              :       END DO
     950              : 
     951              :       ! Clean-up
     952          166 :       CALL dbcsr_release(template)
     953          166 :       CALL dbcsr_release(work)
     954              : 
     955          498 :    END SUBROUTINE reserve_contraction_blocks
     956              : 
     957              : ! **************************************************************************************************
     958              : !> \brief Contract the ri 3-center integrals stored in a tensor with repect to the donor MOs coeffs,
     959              : !>        for a given excited atom k => (aI|k) = sum_b c_Ib (ab|k)
     960              : !> \param contr_int the contracted integrals as array of dbcsr matrices
     961              : !> \param op_type for which operator type we contract (COULOMB or EXCHANGE)
     962              : !> \param donor_state ...
     963              : !> \param xas_tdp_env ...
     964              : !> \param xas_tdp_control ...
     965              : !> \param qs_env ...
     966              : !> \note  In the output matrices, (aI_b|k) is stored at block a,b where I_b is the partial
     967              : !>        contraction that only includes coeffs from atom b. Note that the contracted matrix is
     968              : !>        not symmetric. To get the fully contracted matrix over b, one need to add the block
     969              : !>        columns of (aI_b|k) (get an array of size nao*nsgfp). This step is unnessary in our case
     970              : !>        because we assume locality of donor state, and only one column od (aI_b|k) is pouplated
     971              : ! **************************************************************************************************
     972          166 :    SUBROUTINE contract2_AO_to_doMO(contr_int, op_type, donor_state, xas_tdp_env, xas_tdp_control, qs_env)
     973              : 
     974              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: contr_int
     975              :       CHARACTER(len=*), INTENT(IN)                       :: op_type
     976              :       TYPE(donor_state_type), POINTER                    :: donor_state
     977              :       TYPE(xas_tdp_env_type), POINTER                    :: xas_tdp_env
     978              :       TYPE(xas_tdp_control_type), POINTER                :: xas_tdp_control
     979              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     980              : 
     981              :       CHARACTER(len=*), PARAMETER :: routineN = 'contract2_AO_to_doMO'
     982              : 
     983              :       INTEGER                                            :: handle, i, imo, ispin, katom, kkind, &
     984              :                                                             natom, ndo_mo, ndo_so, nkind, nspins
     985          166 :       INTEGER, DIMENSION(:), POINTER                     :: ri_blk_size, std_blk_size
     986              :       LOGICAL                                            :: do_uks
     987          166 :       REAL(dp), DIMENSION(:, :), POINTER                 :: coeffs
     988              :       TYPE(dbcsr_distribution_type)                      :: opt_dbcsr_dist
     989          166 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrices, matrix_s
     990              :       TYPE(dbcsr_type), POINTER                          :: aI_P, P_Ib, work
     991              :       TYPE(dbt_type), POINTER                            :: pq_X
     992              :       TYPE(distribution_2d_type), POINTER                :: opt_dist2d
     993          166 :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: ri_basis
     994              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     995          166 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     996          166 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     997              : 
     998          166 :       NULLIFY (matrix_s, std_blk_size, ri_blk_size, qs_kind_set, ri_basis, pq_X)
     999          166 :       NULLIFY (aI_P, P_Ib, work, matrices, coeffs, opt_dist2d, particle_set)
    1000              : 
    1001          166 :       CALL timeset(routineN, handle)
    1002              : 
    1003              : !  Initialization
    1004          166 :       CALL get_qs_env(qs_env, natom=natom, matrix_s=matrix_s, qs_kind_set=qs_kind_set, para_env=para_env)
    1005          166 :       ndo_mo = donor_state%ndo_mo
    1006          166 :       kkind = donor_state%kind_index
    1007          166 :       katom = donor_state%at_index
    1008              :       !by default contract for Coulomb
    1009          166 :       pq_X => xas_tdp_env%ri_3c_coul
    1010          166 :       opt_dist2d => xas_tdp_env%opt_dist2d_coul
    1011          166 :       IF (op_type == "EXCHANGE") THEN
    1012           88 :          CPASSERT(ASSOCIATED(xas_tdp_env%ri_3c_ex))
    1013           88 :          pq_X => xas_tdp_env%ri_3c_ex
    1014           88 :          opt_dist2d => xas_tdp_env%opt_dist2d_ex
    1015              :       END IF
    1016          166 :       do_uks = xas_tdp_control%do_uks
    1017          166 :       nspins = 1; IF (do_uks) nspins = 2
    1018          166 :       ndo_so = nspins*ndo_mo
    1019              : 
    1020              : !  contracted integrals block sizes
    1021          166 :       CALL dbcsr_get_info(matrix_s(1)%matrix, col_blk_size=std_blk_size)
    1022              :       ! getting the block dimensions for the RI basis
    1023          166 :       CALL get_qs_env(qs_env, particle_set=particle_set, nkind=nkind)
    1024         1110 :       ALLOCATE (ri_basis(nkind), ri_blk_size(natom))
    1025          166 :       CALL basis_set_list_setup(ri_basis, "RI_XAS", qs_kind_set)
    1026          166 :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=ri_blk_size, basis=ri_basis)
    1027              : 
    1028              : !  Create  work matrices. Everything that goes into a 3c routine must be compatible with the optimal dist_2d
    1029          166 :       CALL cp_dbcsr_dist2d_to_dist(opt_dist2d, opt_dbcsr_dist)
    1030              : 
    1031          498 :       ALLOCATE (aI_P, P_Ib, work, matrices(2))
    1032              :       CALL dbcsr_create(aI_P, dist=opt_dbcsr_dist, matrix_type=dbcsr_type_no_symmetry, name="(aI|P)", &
    1033          166 :                         row_blk_size=std_blk_size, col_blk_size=ri_blk_size)
    1034              : 
    1035              :       CALL dbcsr_create(P_Ib, dist=opt_dbcsr_dist, matrix_type=dbcsr_type_no_symmetry, name="(P|Ib)", &
    1036          166 :                         row_blk_size=ri_blk_size, col_blk_size=std_blk_size)
    1037              : 
    1038              :       !reserve the blocks (needed for 3c contraction routines)
    1039          166 :       matrices(1)%matrix => aI_P; matrices(2)%matrix => P_Ib
    1040          166 :       CALL reserve_contraction_blocks(matrices, katom, qs_env)
    1041          166 :       DEALLOCATE (matrices)
    1042              : 
    1043              :       ! Create the contracted integral matrices
    1044          722 :       ALLOCATE (contr_int(ndo_so))
    1045          390 :       DO i = 1, ndo_so
    1046          224 :          ALLOCATE (contr_int(i)%matrix)
    1047              :          CALL dbcsr_create(matrix=contr_int(i)%matrix, template=matrix_s(1)%matrix, &
    1048              :                            matrix_type=dbcsr_type_no_symmetry, row_blk_size=std_blk_size, &
    1049          390 :                            col_blk_size=ri_blk_size)
    1050              :       END DO
    1051              : 
    1052              :       ! Only take the coeffs for atom on which MOs I,J are localized
    1053          166 :       coeffs => donor_state%contract_coeffs
    1054              : 
    1055          358 :       DO ispin = 1, nspins
    1056              : 
    1057              : !     Loop over the donor MOs and contract
    1058          582 :          DO imo = 1, ndo_mo
    1059              : 
    1060              :             ! do the contraction
    1061          224 :             CALL dbcsr_set(aI_P, 0.0_dp); CALL dbcsr_set(P_Ib, 0.0_dp)
    1062          224 :             CALL contract2_AO_to_doMO_low(pq_X, coeffs(:, (ispin - 1)*ndo_mo + imo), aI_P, P_Ib, katom)
    1063              : 
    1064              :             ! Get the full (aI|P) contracted integrals
    1065          224 :             CALL dbcsr_transposed(work, P_Ib)
    1066          224 :             CALL dbcsr_add(work, aI_P, 1.0_dp, 1.0_dp)
    1067          224 :             CALL dbcsr_complete_redistribute(work, contr_int((ispin - 1)*ndo_mo + imo)%matrix)
    1068          224 :             CALL dbcsr_filter(contr_int((ispin - 1)*ndo_mo + imo)%matrix, 1.0E-16_dp)
    1069              : 
    1070          416 :             CALL dbcsr_release(work)
    1071              :          END DO !imo
    1072              :       END DO !ispin
    1073              : 
    1074              : !  Clean-up
    1075          166 :       CALL dbcsr_release(aI_P)
    1076          166 :       CALL dbcsr_release(P_Ib)
    1077          166 :       CALL dbcsr_distribution_release(opt_dbcsr_dist)
    1078          166 :       DEALLOCATE (ri_blk_size, aI_P, P_Ib, work, ri_basis)
    1079              : 
    1080          166 :       CALL timestop(handle)
    1081              : 
    1082          332 :    END SUBROUTINE contract2_AO_to_doMO
    1083              : 
    1084              : ! **************************************************************************************************
    1085              : !> \brief Contraction of the 3-center integrals (ab|Q) over the RI basis elements Q to get donor MOS
    1086              : !>        => (ab|IJ) = sum_X (ab|Q) coeffs_Q
    1087              : !> \param ab_Q the tensor holding the integrals
    1088              : !> \param vec the contraction coefficients
    1089              : !> \param mat_abIJ the matrix holding the (ab|IJ) integrals (blocks must be reserved)
    1090              : !> \param atom_k the atom for which we contract, i.e. we only take RI basis Q centered on atom_k
    1091              : !> \note By construction, distribution of tensor and matrix match, also for OMP threads
    1092              : ! **************************************************************************************************
    1093           90 :    SUBROUTINE contract3_RI_to_doMOs(ab_Q, vec, mat_abIJ, atom_k)
    1094              : 
    1095              :       TYPE(dbt_type)                                     :: ab_Q
    1096              :       REAL(dp), DIMENSION(:), INTENT(IN)                 :: vec
    1097              :       TYPE(dbcsr_type)                                   :: mat_abIJ
    1098              :       INTEGER, INTENT(IN)                                :: atom_k
    1099              : 
    1100              :       CHARACTER(len=*), PARAMETER :: routineN = 'contract3_RI_to_doMOs'
    1101              : 
    1102              :       INTEGER                                            :: handle, i, iatom, ind(3), j, jatom, katom
    1103              :       LOGICAL                                            :: found, t_found
    1104              :       REAL(dp)                                           :: prefac
    1105           90 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :, :)          :: iabc
    1106           90 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: pblock
    1107              :       TYPE(dbcsr_type)                                   :: work
    1108              :       TYPE(dbt_iterator_type)                            :: iter
    1109              : 
    1110           90 :       NULLIFY (pblock)
    1111              : 
    1112           90 :       CALL timeset(routineN, handle)
    1113              : 
    1114              : !$OMP PARALLEL DEFAULT(NONE) &
    1115              : !$OMP SHARED(ab_Q,vec,mat_abIJ,atom_k) &
    1116           90 : !$OMP PRIVATE(iter,ind,iatom,jatom,katom,prefac,iabc,t_found,found,pblock,i,j)
    1117              :       CALL dbt_iterator_start(iter, ab_Q)
    1118              :       DO WHILE (dbt_iterator_blocks_left(iter))
    1119              :          CALL dbt_iterator_next_block(iter, ind)
    1120              : 
    1121              :          iatom = ind(1)
    1122              :          jatom = ind(2)
    1123              :          katom = ind(3)
    1124              : 
    1125              :          IF (.NOT. atom_k == katom) CYCLE
    1126              : 
    1127              :          prefac = 1.0_dp
    1128              :          IF (iatom == jatom) prefac = 0.5_dp
    1129              : 
    1130              :          CALL dbt_get_block(ab_Q, ind, iabc, t_found)
    1131              : 
    1132              :          CALL dbcsr_get_block_p(mat_abIJ, iatom, jatom, pblock, found)
    1133              :          IF ((.NOT. found) .OR. (.NOT. t_found)) CYCLE
    1134              : 
    1135              :          DO i = 1, SIZE(pblock, 1)
    1136              :             DO j = 1, SIZE(pblock, 2)
    1137              : !$OMP ATOMIC
    1138              :                pblock(i, j) = pblock(i, j) + prefac*DOT_PRODUCT(vec(:), iabc(i, j, :))
    1139              :             END DO
    1140              :          END DO
    1141              : 
    1142              :          DEALLOCATE (iabc)
    1143              :       END DO !iter
    1144              :       CALL dbt_iterator_stop(iter)
    1145              : !$OMP END PARALLEL
    1146              : 
    1147              :       !matrix only half filled => need to add its transpose
    1148           90 :       CALL dbcsr_create(work, template=mat_abIJ)
    1149           90 :       CALL dbcsr_transposed(work, mat_abIJ)
    1150           90 :       CALL dbcsr_add(mat_abIJ, work, 1.0_dp, 1.0_dp)
    1151           90 :       CALL dbcsr_release(work)
    1152              : 
    1153           90 :       CALL timestop(handle)
    1154              : 
    1155          180 :    END SUBROUTINE contract3_RI_to_doMOs
    1156              : 
    1157              : ! **************************************************************************************************
    1158              : !> \brief Contraction of the 3-center integrals over index 1 and 2, for a given atom_k. The results
    1159              : !>        are stored in two matrices, such that (a,b are block indices):
    1160              : !>        mat_aIb(ab) = mat_aIb(ab) + sum j_b (i_aj_b|k)*v(j_b) and
    1161              : !>        mat_bIa(ba) = mat_bIa(ba) + sum i_a (i_aj_b|k)*v(i_a)
    1162              : !>        The block size of the columns of mat_aIb and the rows of mat_bIa are the size of k (RI)
    1163              : !> \param ab_Q the tensor containing the 3-center integrals
    1164              : !> \param vec the contraction coefficients
    1165              : !> \param mat_aIb normal type dbcsr matrix
    1166              : !> \param mat_bIa normal type dbcsr matrix
    1167              : !> \param atom_k the atom for which we contract
    1168              : !>       It is assumed that the contraction coefficients for MO I are all on atom_k
    1169              : !>       We do the classic thing when we fill half the matrix and add its transposed to get the full
    1170              : !>       one, but here, the matrix is not symmetric, hence we explicitely have 2 input matrices
    1171              : !>       The distribution of the integrals and the normal dbcsr matrix are compatible out of the box
    1172              : ! **************************************************************************************************
    1173          224 :    SUBROUTINE contract2_AO_to_doMO_low(ab_Q, vec, mat_aIb, mat_bIa, atom_k)
    1174              : 
    1175              :       TYPE(dbt_type)                                     :: ab_Q
    1176              :       REAL(dp), DIMENSION(:), INTENT(IN)                 :: vec
    1177              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: mat_aIb, mat_bIa
    1178              :       INTEGER, INTENT(IN)                                :: atom_k
    1179              : 
    1180              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'contract2_AO_to_doMO_low'
    1181              : 
    1182              :       INTEGER                                            :: handle, i, iatom, ind(3), j, jatom, &
    1183              :                                                             katom, s1, s2
    1184          224 :       INTEGER, DIMENSION(:), POINTER                     :: atom_blk_size
    1185              :       LOGICAL                                            :: found, t_found
    1186          224 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :, :)          :: iabc
    1187          224 :       REAL(dp), DIMENSION(:, :), POINTER                 :: pblock
    1188              :       TYPE(dbt_iterator_type)                            :: iter
    1189              : 
    1190          224 :       NULLIFY (atom_blk_size, pblock)
    1191              : 
    1192          224 :       CALL timeset(routineN, handle)
    1193              : 
    1194          224 :       CALL dbcsr_get_info(mat_aIb, row_blk_size=atom_blk_size)
    1195              : 
    1196              : !$OMP PARALLEL DEFAULT(NONE) &
    1197              : !$OMP SHARED(ab_Q,vec,mat_aIb,mat_bIa,atom_k,atom_blk_size) &
    1198          224 : !$OMP PRIVATE(iter,ind,iatom,jatom,katom,iabc,t_found,found,s1,s2,j,i,pblock)
    1199              :       CALL dbt_iterator_start(iter, ab_Q)
    1200              :       DO WHILE (dbt_iterator_blocks_left(iter))
    1201              :          CALL dbt_iterator_next_block(iter, ind)
    1202              : 
    1203              :          iatom = ind(1)
    1204              :          jatom = ind(2)
    1205              :          katom = ind(3)
    1206              : 
    1207              :          IF (atom_k /= katom) CYCLE
    1208              : 
    1209              :          CALL dbt_get_block(ab_Q, ind, iabc, t_found)
    1210              :          IF (.NOT. t_found) CYCLE
    1211              : 
    1212              :          ! Deal with mat_aIb
    1213              :          IF (jatom == atom_k) THEN
    1214              :             s1 = atom_blk_size(iatom)
    1215              :             s2 = SIZE(iabc, 3)
    1216              : 
    1217              :             CALL dbcsr_get_block_p(matrix=mat_aIb, row=iatom, col=jatom, BLOCK=pblock, found=found)
    1218              : 
    1219              :             IF (found) THEN
    1220              :                DO i = 1, s1
    1221              :                   DO j = 1, s2
    1222              : !$OMP ATOMIC
    1223              :                      pblock(i, j) = pblock(i, j) + DOT_PRODUCT(vec, iabc(i, :, j))
    1224              :                   END DO
    1225              :                END DO
    1226              :             END IF
    1227              :          END IF ! jatom == atom_k
    1228              : 
    1229              :          ! Deal with mat_bIa, keep block diagonal empty
    1230              :          IF (iatom == jatom) CYCLE
    1231              :          IF (iatom == atom_k) THEN
    1232              :             s1 = SIZE(iabc, 3)
    1233              :             s2 = atom_blk_size(jatom)
    1234              : 
    1235              :             CALL dbcsr_get_block_p(matrix=mat_bIa, row=iatom, col=jatom, BLOCK=pblock, found=found)
    1236              : 
    1237              :             IF (found) THEN
    1238              :                DO i = 1, s1
    1239              :                   DO j = 1, s2
    1240              : !$OMP ATOMIC
    1241              :                      pblock(i, j) = pblock(i, j) + DOT_PRODUCT(vec, iabc(:, j, i))
    1242              :                   END DO
    1243              :                END DO
    1244              :             END IF
    1245              :          END IF !iatom== atom_k
    1246              : 
    1247              :          DEALLOCATE (iabc)
    1248              :       END DO !iter
    1249              :       CALL dbt_iterator_stop(iter)
    1250              : !$OMP END PARALLEL
    1251              : 
    1252          224 :       CALL timestop(handle)
    1253              : 
    1254          448 :    END SUBROUTINE contract2_AO_to_doMO_low
    1255              : 
    1256              : ! **************************************************************************************************
    1257              : !> \brief Multiply all the blocks of a contracted RI integral (aI|P) by a matrix of type (P|...|Q)
    1258              : !> \param contr_int the integral array
    1259              : !> \param PQ the smaller matrix to multiply all blocks
    1260              : !> \note  It is assumed that all non-zero blocks have the same number of columns. Can pass partial
    1261              : !>        arrays, e.g. contr_int(1:3)
    1262              : ! **************************************************************************************************
    1263          274 :    SUBROUTINE ri_all_blocks_mm(contr_int, PQ)
    1264              : 
    1265              :       TYPE(dbcsr_p_type), DIMENSION(:)                   :: contr_int
    1266              :       REAL(dp), DIMENSION(:, :), INTENT(IN)              :: PQ
    1267              : 
    1268              :       INTEGER                                            :: iblk, imo, jblk, ndo_mo, s1, s2
    1269              :       LOGICAL                                            :: found
    1270          274 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: work
    1271          274 :       REAL(dp), DIMENSION(:, :), POINTER                 :: pblock
    1272              :       TYPE(dbcsr_iterator_type)                          :: iter
    1273              : 
    1274          274 :       NULLIFY (pblock)
    1275              : 
    1276          274 :       ndo_mo = SIZE(contr_int)
    1277              : 
    1278          594 :       DO imo = 1, ndo_mo
    1279          320 :          CALL dbcsr_iterator_start(iter, contr_int(imo)%matrix)
    1280         1368 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    1281              : 
    1282         1048 :             CALL dbcsr_iterator_next_block(iter, row=iblk, column=jblk)
    1283         1048 :             CALL dbcsr_get_block_p(contr_int(imo)%matrix, iblk, jblk, pblock, found)
    1284              : 
    1285         1368 :             IF (found) THEN
    1286         1048 :                s1 = SIZE(pblock, 1)
    1287         1048 :                s2 = SIZE(pblock, 2)
    1288         4192 :                ALLOCATE (work(s1, s2))
    1289         1048 :                CALL dgemm('N', 'N', s1, s2, s2, 1.0_dp, pblock, s1, PQ, s2, 0.0_dp, work, s1)
    1290         1048 :                CALL dcopy(s1*s2, work, 1, pblock, 1)
    1291         1048 :                DEALLOCATE (work)
    1292              :             END IF
    1293              : 
    1294              :          END DO ! dbcsr iterator
    1295          914 :          CALL dbcsr_iterator_stop(iter)
    1296              :       END DO !imo
    1297              : 
    1298          548 :    END SUBROUTINE ri_all_blocks_mm
    1299              : 
    1300              : ! **************************************************************************************************
    1301              : !> \brief Copies an (partial) array of contracted RI integrals into anoter one
    1302              : !> \param new_int where the copy is stored
    1303              : !> \param ref_int what is copied
    1304              : !> \note Allocate the matrices of new_int if not done already
    1305              : ! **************************************************************************************************
    1306          170 :    SUBROUTINE copy_ri_contr_int(new_int, ref_int)
    1307              : 
    1308              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(INOUT)    :: new_int
    1309              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(IN)       :: ref_int
    1310              : 
    1311              :       INTEGER                                            :: iso, ndo_so
    1312              : 
    1313          170 :       CPASSERT(SIZE(new_int) == SIZE(ref_int))
    1314          170 :       ndo_so = SIZE(ref_int)
    1315              : 
    1316          364 :       DO iso = 1, ndo_so
    1317          194 :          IF (.NOT. ASSOCIATED(new_int(iso)%matrix)) ALLOCATE (new_int(iso)%matrix)
    1318          364 :          CALL dbcsr_copy(new_int(iso)%matrix, ref_int(iso)%matrix)
    1319              :       END DO
    1320              : 
    1321          170 :    END SUBROUTINE copy_ri_contr_int
    1322              : 
    1323              : ! **************************************************************************************************
    1324              : !> \brief Takes the product of contracted integrals and put them in a kernel matrix
    1325              : !> \param kernel the matrix where the products are stored
    1326              : !> \param lhs_int the left-hand side contracted integrals
    1327              : !> \param rhs_int the right-hand side contracted integrals
    1328              : !> \param quadrants on which quadrant(s) on the kernel matrix the product is stored
    1329              : !> \param qs_env ...
    1330              : !> \param eps_filter filter for dbcsr matrix multiplication
    1331              : !> \param mo_transpose whether the MO blocks should be transpose, i.e. (aI|Jb) => (aJ|Ib)
    1332              : !> \note It is assumed that the kerenl matrix is NOT symmetric
    1333              : !>       There are three quadrants, corresponding to 1: the upper-left (diagonal), 2: the
    1334              : !>       upper-right (off-diagonal) and 3: the lower-right (diagonal).
    1335              : !>       Need to finalize the kernel matrix after calling this routine (possibly multiple times)
    1336              : ! **************************************************************************************************
    1337          170 :    SUBROUTINE ri_int_product(kernel, lhs_int, rhs_int, quadrants, qs_env, eps_filter, mo_transpose)
    1338              : 
    1339              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: kernel
    1340              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(IN)       :: lhs_int, rhs_int
    1341              :       LOGICAL, DIMENSION(3), INTENT(IN)                  :: quadrants
    1342              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1343              :       REAL(dp), INTENT(IN), OPTIONAL                     :: eps_filter
    1344              :       LOGICAL, INTENT(IN), OPTIONAL                      :: mo_transpose
    1345              : 
    1346              :       INTEGER                                            :: i, iblk, iso, j, jblk, jso, nblk, ndo_so
    1347              :       LOGICAL                                            :: found, my_mt
    1348          170 :       REAL(dp), DIMENSION(:, :), POINTER                 :: pblock
    1349              :       TYPE(dbcsr_iterator_type)                          :: iter
    1350          170 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
    1351              :       TYPE(dbcsr_type)                                   :: prod
    1352              : 
    1353          170 :       NULLIFY (matrix_s, pblock)
    1354              : 
    1355              : !  Initialization
    1356            0 :       CPASSERT(SIZE(lhs_int) == SIZE(rhs_int))
    1357          194 :       CPASSERT(ANY(quadrants))
    1358          170 :       ndo_so = SIZE(lhs_int)
    1359          170 :       CALL get_qs_env(qs_env, matrix_s=matrix_s, natom=nblk)
    1360          170 :       CALL dbcsr_create(prod, template=matrix_s(1)%matrix, matrix_type=dbcsr_type_no_symmetry)
    1361          170 :       my_mt = .FALSE.
    1362          170 :       IF (PRESENT(mo_transpose)) my_mt = mo_transpose
    1363              : 
    1364              :       ! The kernel matrix is symmetric (even if normal type) => only fill upper half on diagonal
    1365              :       ! quadrants, but the whole thing on upper-right quadrant
    1366          364 :       DO iso = 1, ndo_so
    1367          650 :          DO jso = 1, ndo_so
    1368              : 
    1369              :             ! If on-diagonal quadrants only, can skip jso < iso
    1370          286 :             IF (.NOT. quadrants(2) .AND. jso < iso) CYCLE
    1371              : 
    1372          240 :             i = iso; j = jso
    1373          240 :             IF (my_mt) THEN
    1374            6 :                i = jso; j = iso
    1375              :             END IF
    1376              : 
    1377              :             ! Take the product lhs*rhs^T
    1378              :             CALL dbcsr_multiply('N', 'T', 1.0_dp, lhs_int(i)%matrix, rhs_int(j)%matrix, &
    1379          240 :                                 0.0_dp, prod, filter_eps=eps_filter)
    1380              : 
    1381              :             ! Loop over blocks of prod and fill kernel matrix => ok cuz same (but replicated) dist
    1382          240 :             CALL dbcsr_iterator_start(iter, prod)
    1383         8201 :             DO WHILE (dbcsr_iterator_blocks_left(iter))
    1384              : 
    1385         7961 :                CALL dbcsr_iterator_next_block(iter, row=iblk, column=jblk)
    1386         7961 :                IF ((iso == jso .AND. jblk < iblk) .AND. .NOT. quadrants(2)) CYCLE
    1387              : 
    1388         4227 :                CALL dbcsr_get_block_p(prod, iblk, jblk, pblock, found)
    1389              : 
    1390         4467 :                IF (found) THEN
    1391              : 
    1392              :                   ! Case study on quadrant
    1393              :                   !upper-left
    1394         4227 :                   IF (quadrants(1)) THEN
    1395         4123 :                      CALL dbcsr_put_block(kernel, (iso - 1)*nblk + iblk, (jso - 1)*nblk + jblk, pblock)
    1396              :                   END IF
    1397              : 
    1398              :                   !upper-right
    1399         4227 :                   IF (quadrants(2)) THEN
    1400           80 :                      CALL dbcsr_put_block(kernel, (iso - 1)*nblk + iblk, (ndo_so + jso - 1)*nblk + jblk, pblock)
    1401              :                   END IF
    1402              : 
    1403              :                   !lower-right
    1404         4227 :                   IF (quadrants(3)) THEN
    1405           56 :                      CALL dbcsr_put_block(kernel, (ndo_so + iso - 1)*nblk + iblk, (ndo_so + jso - 1)*nblk + jblk, pblock)
    1406              :                   END IF
    1407              : 
    1408              :                END IF
    1409              : 
    1410              :             END DO ! dbcsr iterator
    1411          720 :             CALL dbcsr_iterator_stop(iter)
    1412              : 
    1413              :          END DO !jso
    1414              :       END DO !iso
    1415              : 
    1416              : !  Clean-up
    1417          170 :       CALL dbcsr_release(prod)
    1418              : 
    1419          170 :    END SUBROUTINE ri_int_product
    1420              : 
    1421              : END MODULE xas_tdp_kernel
        

Generated by: LCOV version 2.0-1