tdhf_lib.F90 Source File


This file depends on

sourcefile~~tdhf_lib.f90~~EfferentGraph sourcefile~tdhf_lib.f90 tdhf_lib.F90 sourcefile~basis_tools.f90 basis_tools.F90 sourcefile~tdhf_lib.f90->sourcefile~basis_tools.f90 sourcefile~constants_io.f90 constants_io.F90 sourcefile~tdhf_lib.f90->sourcefile~constants_io.f90 sourcefile~eigen.f90 eigen.F90 sourcefile~tdhf_lib.f90->sourcefile~eigen.f90 sourcefile~int2.f90 int2.F90 sourcefile~tdhf_lib.f90->sourcefile~int2.f90 sourcefile~mathlib.f90 mathlib.F90 sourcefile~tdhf_lib.f90->sourcefile~mathlib.f90 sourcefile~oqp_linalg.f90 oqp_linalg.F90 sourcefile~tdhf_lib.f90->sourcefile~oqp_linalg.f90 sourcefile~physical_constants.f90 physical_constants.F90 sourcefile~tdhf_lib.f90->sourcefile~physical_constants.f90 sourcefile~precision.f90 precision.F90 sourcefile~tdhf_lib.f90->sourcefile~precision.f90 sourcefile~types.f90 types.F90 sourcefile~tdhf_lib.f90->sourcefile~types.f90 sourcefile~basis_tools.f90->sourcefile~constants_io.f90 sourcefile~basis_tools.f90->sourcefile~precision.f90 sourcefile~atomic_structure.f90 atomic_structure.F90 sourcefile~basis_tools.f90->sourcefile~atomic_structure.f90 sourcefile~basis_library.f90 basis_library.F90 sourcefile~basis_tools.f90->sourcefile~basis_library.f90 sourcefile~constants.f90 constants.F90 sourcefile~basis_tools.f90->sourcefile~constants.f90 sourcefile~elements.f90 elements.F90 sourcefile~basis_tools.f90->sourcefile~elements.f90 sourcefile~messages.f90 messages.F90 sourcefile~basis_tools.f90->sourcefile~messages.f90 sourcefile~parallel.f90 parallel.F90 sourcefile~basis_tools.f90->sourcefile~parallel.f90 sourcefile~eigen.f90->sourcefile~oqp_linalg.f90 sourcefile~eigen.f90->sourcefile~precision.f90 sourcefile~mathlib_types.f90 mathlib_types.F90 sourcefile~eigen.f90->sourcefile~mathlib_types.f90 sourcefile~eigen.f90->sourcefile~messages.f90 sourcefile~int2.f90->sourcefile~basis_tools.f90 sourcefile~int2.f90->sourcefile~constants_io.f90 sourcefile~int2.f90->sourcefile~precision.f90 sourcefile~int2.f90->sourcefile~types.f90 sourcefile~int2.f90->sourcefile~atomic_structure.f90 sourcefile~int2.f90->sourcefile~constants.f90 sourcefile~int2_pairs.f90 int2_pairs.F90 sourcefile~int2.f90->sourcefile~int2_pairs.f90 sourcefile~int_libint.f90 int_libint.F90 sourcefile~int2.f90->sourcefile~int_libint.f90 sourcefile~int_rotaxis.f90 int_rotaxis.F90 sourcefile~int2.f90->sourcefile~int_rotaxis.f90 sourcefile~int_rys.f90 int_rys.F90 sourcefile~int2.f90->sourcefile~int_rys.f90 sourcefile~int2.f90->sourcefile~messages.f90 sourcefile~int2.f90->sourcefile~parallel.f90 sourcefile~tagarray_driver.f90 tagarray_driver.F90 sourcefile~int2.f90->sourcefile~tagarray_driver.f90 sourcefile~mathlib.f90->sourcefile~eigen.f90 sourcefile~mathlib.f90->sourcefile~oqp_linalg.f90 sourcefile~mathlib.f90->sourcefile~precision.f90 sourcefile~mathlib.f90->sourcefile~messages.f90 sourcefile~blas_wrap.f90 blas_wrap.F90 sourcefile~oqp_linalg.f90->sourcefile~blas_wrap.f90 sourcefile~lapack_wrap.f90 lapack_wrap.F90 sourcefile~oqp_linalg.f90->sourcefile~lapack_wrap.f90 sourcefile~types.f90->sourcefile~basis_tools.f90 sourcefile~types.f90->sourcefile~precision.f90 sourcefile~types.f90->sourcefile~atomic_structure.f90 sourcefile~functionals.f90 functionals.F90 sourcefile~types.f90->sourcefile~functionals.f90 sourcefile~types.f90->sourcefile~parallel.f90 sourcefile~strings.f90 strings.F90 sourcefile~atomic_structure.f90->sourcefile~strings.f90 sourcefile~basis_library.f90->sourcefile~constants_io.f90 sourcefile~basis_library.f90->sourcefile~constants.f90 sourcefile~basis_library.f90->sourcefile~elements.f90 sourcefile~basis_library.f90->sourcefile~strings.f90 sourcefile~blas_wrap.f90->sourcefile~precision.f90 sourcefile~blas_wrap.f90->sourcefile~mathlib_types.f90 sourcefile~blas_wrap.f90->sourcefile~messages.f90 sourcefile~constants.f90->sourcefile~precision.f90 sourcefile~elements.f90->sourcefile~physical_constants.f90 sourcefile~elements.f90->sourcefile~strings.f90 sourcefile~functionals.f90->sourcefile~precision.f90 sourcefile~functionals.f90->sourcefile~messages.f90 sourcefile~int2_pairs.f90->sourcefile~basis_tools.f90 sourcefile~int2_pairs.f90->sourcefile~precision.f90 sourcefile~int_libint.f90->sourcefile~basis_tools.f90 sourcefile~int_libint.f90->sourcefile~precision.f90 sourcefile~int_libint.f90->sourcefile~constants.f90 sourcefile~int_libint.f90->sourcefile~int2_pairs.f90 sourcefile~libint_f.f90 libint_f.F90 sourcefile~int_libint.f90->sourcefile~libint_f.f90 sourcefile~int_rotaxis.f90->sourcefile~basis_tools.f90 sourcefile~int_rotaxis.f90->sourcefile~precision.f90 sourcefile~int_rotaxis.f90->sourcefile~constants.f90 sourcefile~int_rotaxis.f90->sourcefile~int2_pairs.f90 sourcefile~int_rys.f90->sourcefile~basis_tools.f90 sourcefile~int_rys.f90->sourcefile~precision.f90 sourcefile~int_rys.f90->sourcefile~constants.f90 sourcefile~int_rys.f90->sourcefile~int2_pairs.f90 sourcefile~rys.f90 rys.F90 sourcefile~int_rys.f90->sourcefile~rys.f90 sourcefile~lapack_wrap.f90->sourcefile~precision.f90 sourcefile~lapack_wrap.f90->sourcefile~mathlib_types.f90 sourcefile~lapack_wrap.f90->sourcefile~messages.f90 sourcefile~messages.f90->sourcefile~constants_io.f90 sourcefile~messages.f90->sourcefile~precision.f90 sourcefile~parallel.f90->sourcefile~precision.f90 sourcefile~tagarray_driver.f90->sourcefile~messages.f90 sourcefile~rys.f90->sourcefile~precision.f90 sourcefile~rys.f90->sourcefile~constants.f90 sourcefile~rys_lut.f90 rys_lut.F90 sourcefile~rys.f90->sourcefile~rys_lut.f90

Files dependent on this one

sourcefile~~tdhf_lib.f90~~AfferentGraph sourcefile~tdhf_lib.f90 tdhf_lib.F90 sourcefile~tdhf_energy.f90 tdhf_energy.F90 sourcefile~tdhf_energy.f90->sourcefile~tdhf_lib.f90 sourcefile~tdhf_gradient.f90 tdhf_gradient.F90 sourcefile~tdhf_gradient.f90->sourcefile~tdhf_lib.f90 sourcefile~tdhf_mrsf_energy.f90 tdhf_mrsf_energy.F90 sourcefile~tdhf_mrsf_energy.f90->sourcefile~tdhf_lib.f90 sourcefile~tdhf_sf_lib.f90 tdhf_sf_lib.F90 sourcefile~tdhf_mrsf_energy.f90->sourcefile~tdhf_sf_lib.f90 sourcefile~tdhf_sf_energy.f90 tdhf_sf_energy.F90 sourcefile~tdhf_sf_energy.f90->sourcefile~tdhf_lib.f90 sourcefile~tdhf_sf_energy.f90->sourcefile~tdhf_sf_lib.f90 sourcefile~tdhf_sf_lib.f90->sourcefile~tdhf_lib.f90 sourcefile~tdhf_z_vector.f90 tdhf_z_vector.F90 sourcefile~tdhf_z_vector.f90->sourcefile~tdhf_lib.f90 sourcefile~tdhf_z_vector.f90->sourcefile~tdhf_sf_lib.f90

Source Code

module tdhf_lib

    use precision, only : dp
    use int2_compute, only: int2_fock_data_t, int2_storage_t
    use basis_tools, only: basis_set
    use oqp_linalg

    type, extends(int2_fock_data_t) :: int2_td_data_t
      real(kind=dp), allocatable :: apb(:,:,:,:)
      real(kind=dp), allocatable :: amb(:,:,:,:)
      real(kind=dp), pointer :: d2(:,:,:) => null()
      logical :: int_apb = .true. !< do A+B part
      logical :: int_amb = .false. !< do A-B part, needed for hybrid functionals
      logical :: tamm_dancoff = .false. !< Tamm-Dancoff approximation
      logical :: tamm_dancoff_coulomb = .false. !< Whetehr to includ Coulomb terms with TDA, not needed in SFDFT
    contains
        procedure :: parallel_start => int2_td_data_t_parallel_start
        procedure :: parallel_stop => int2_td_data_t_parallel_stop
        procedure :: init_screen => int2_td_data_t_init_screen
        procedure :: update => int2_td_data_t_update
        procedure :: clean => int2_td_data_t_clean
    end type

    type, extends(int2_td_data_t) :: int2_tdgrd_data_t
    contains
      procedure :: update => int2_tdgrd_data_t_update
    end type

    type, extends(int2_fock_data_t) :: int2_rpagrd_data_t
      real(kind=dp), allocatable :: hpp(:,:,:,:,:)  ! H+[X+Y]
      real(kind=dp), allocatable :: hpt(:,:,:,:,:)  ! H+[T]
      real(kind=dp), allocatable :: hmm(:,:,:,:,:)  ! H-[X-Y]
      real(kind=dp), pointer :: xpy(:,:,:,:) => null() ! X+Y
      real(kind=dp), pointer :: xmy(:,:,:,:) => null() ! X-Y
      real(kind=dp), pointer :: t(:,:,:,:) => null() ! T
      integer :: np = 0
      integer :: nm = 0
      integer :: nt = 0
      integer :: nspin = 0
      integer :: nbf = 0
      logical :: tamm_dancoff = .false. !< Tamm-Dancoff approximation
    contains
        procedure :: parallel_start => int2_rpagrd_data_t_parallel_start
        procedure :: parallel_stop  => int2_rpagrd_data_t_parallel_stop
        procedure :: init_screen    => int2_rpagrd_data_t_init_screen
        procedure :: update         => int2_rpagrd_data_t_update
        procedure :: clean          => int2_rpagrd_data_t_clean

        procedure, non_overridable :: hplus  => int2_rpagrd_data_t_update_hplus
        procedure, non_overridable :: hminus => int2_rpagrd_data_t_update_hminus
    end type
contains

!###############################################################################

  subroutine int2_td_data_t_parallel_start(this, basis, nthreads)
    implicit none
    class(int2_td_data_t), target, intent(inout) :: this
    type(basis_set), intent(in) :: basis
    integer, intent(in) :: nthreads
    integer :: nbf, nsh

    nbf = basis%nbf
    this%fockdim = nbf*(nbf+1) / 2
    this%nfocks = ubound(this%d2,size(shape(this%d2)))
    this%nthreads = nthreads
    nsh = basis%nshell

    if (this%cur_pass == 1) then
      if (allocated(this%apb)) deallocate(this%apb)
      if (allocated(this%amb)) deallocate(this%amb)
      if (allocated(this%dsh)) deallocate(this%dsh)

      allocate(this%apb(nbf, nbf, this%nfocks, nthreads), &
               this%amb(nbf, nbf, this%nfocks, nthreads), &
               this%dsh(nsh,nsh), &
               source=0.0d0)
    end if

    call this%init_screen(basis)

  end subroutine

!###############################################################################

  subroutine int2_td_data_t_parallel_stop(this)
    use mathlib, only: symmetrize_matrix
    implicit none
    integer :: flast, amblast, nbf, i
    class(int2_td_data_t), intent(inout) :: this
    if (this%cur_pass /= this%num_passes) return
    flast  = size(shape(this%apb))
    amblast = size(shape(this%amb))
    nbf = ubound(this%amb, 1)
    if (this%nthreads /= 1) then
      this%apb(:,:,:,lbound(this%apb, flast)) = sum(this%apb, dim=flast)
      this%amb(:,:,:,lbound(this%amb, amblast)) = sum(this%amb, dim=amblast)
    end if
    call this%pe%allreduce(this%apb(:,:,:,1), &
                       size(this%apb(:,:,:,1)))
    call this%pe%allreduce(this%amb(:,:,:,1), &
                         size(this%amb(:,:,:,1)))

    do i = lbound(this%apb,3), ubound(this%apb,3)
      call symmetrize_matrix(this%apb(:,:,i,1), nbf)
    end do
    this%nthreads = 1
  end subroutine

!###############################################################################

  subroutine int2_td_data_t_clean(this)
    implicit none
    class(int2_td_data_t), intent(inout) :: this
    deallocate(this%apb)
    deallocate(this%amb)
    deallocate(this%dsh)
    nullify(this%d)
    nullify(this%d2)
  end subroutine

!###############################################################################

  subroutine int2_td_data_t_init_screen(this, basis)
    implicit none
    class(int2_td_data_t), target, intent(inout) :: this
    type(basis_set), intent(in) :: basis

!   Form shell density
    call shltd(this%dsh, this%d2, basis)
    this%max_den = maxval(abs(this%dsh))

  end subroutine

!###############################################################################

  subroutine int2_td_data_t_update(this, buf)
    implicit none
    class(int2_td_data_t), intent(inout) :: this
    type(int2_storage_t), intent(inout) :: buf
    integer :: i, j, k, l, n
    real(kind=dp) :: xval1, cval2, val2c, cval4, &
                     val, val1, val4c
    integer :: ifock, mythread

    xval1 = this%scale_exchange
    cval2 = 2 * this%scale_coulomb
    cval4 = 4 * this%scale_coulomb

    mythread = buf%thread_id

    associate (&
                apb => this%apb(:,:,:,mythread), &
                amb => this%amb(:,:,:,mythread), &
                d2 => this%d2 &
                )

      do ifock = 1, this%nfocks
        do n = 1, buf%ncur
          i  = buf%ids(1,n)
          j  = buf%ids(2,n)
          k  = buf%ids(3,n)
          l  = buf%ids(4,n)
          val = buf%ints(n)

          if (this%tamm_dancoff) then
            val1 = val*xval1
            val2c = val*cval2
            !A
            amb(i,k,ifock) = amb(i,k,ifock) - val1 * d2(j,l,ifock)
            amb(k,i,ifock) = amb(k,i,ifock) - val1 * d2(l,j,ifock)
            amb(i,l,ifock) = amb(i,l,ifock) - val1 * d2(j,k,ifock)
            amb(l,i,ifock) = amb(l,i,ifock) - val1 * d2(k,j,ifock)
            amb(j,k,ifock) = amb(j,k,ifock) - val1 * d2(i,l,ifock)
            amb(k,j,ifock) = amb(k,j,ifock) - val1 * d2(l,i,ifock)
            amb(j,l,ifock) = amb(j,l,ifock) - val1 * d2(i,k,ifock)
            amb(l,j,ifock) = amb(l,j,ifock) - val1 * d2(k,i,ifock)
            if (this%tamm_dancoff_coulomb) then
              amb(i,j,ifock) = amb(i,j,ifock) + val2c * (d2(k,l,ifock)+d2(l,k,ifock))
              amb(j,i,ifock) = amb(j,i,ifock) + val2c * (d2(k,l,ifock)+d2(l,k,ifock))
              amb(k,l,ifock) = amb(k,l,ifock) + val2c * (d2(i,j,ifock)+d2(j,i,ifock))
              amb(l,k,ifock) = amb(l,k,ifock) + val2c * (d2(i,j,ifock)+d2(j,i,ifock))
            end if
          else
            val1 = val*xval1
            val4c = val*cval4

            if (this%int_apb) then
              ! A+B
              ! Coulomb
              apb(i,j,ifock) = apb(i,j,ifock) + val4c * (d2(k,l,ifock)+d2(l,k,ifock))
              apb(k,l,ifock) = apb(k,l,ifock) + val4c * (d2(i,j,ifock)+d2(j,i,ifock))

              ! Exchange
              apb(i,k,ifock) = apb(i,k,ifock) - val1 * (d2(j,l,ifock)+d2(l,j,ifock))
              apb(i,l,ifock) = apb(i,l,ifock) - val1 * (d2(j,k,ifock)+d2(k,j,ifock))
              apb(j,k,ifock) = apb(j,k,ifock) - val1 * (d2(i,l,ifock)+d2(l,i,ifock))
              apb(j,l,ifock) = apb(j,l,ifock) - val1 * (d2(i,k,ifock)+d2(k,i,ifock))
            end if

            if (this%int_amb) then
              ! A-B
              amb(i,k,ifock) = amb(i,k,ifock) + val1 * (d2(l,j,ifock)-d2(j,l,ifock))
              amb(i,l,ifock) = amb(i,l,ifock) + val1 * (d2(k,j,ifock)-d2(j,k,ifock))
              amb(j,k,ifock) = amb(j,k,ifock) + val1 * (d2(l,i,ifock)-d2(i,l,ifock))
              amb(j,l,ifock) = amb(j,l,ifock) + val1 * (d2(k,i,ifock)-d2(i,k,ifock))

              amb(k,i,ifock) = amb(k,i,ifock) - val1 * (d2(l,j,ifock)-d2(j,l,ifock))
              amb(l,i,ifock) = amb(l,i,ifock) - val1 * (d2(k,j,ifock)-d2(j,k,ifock))
              amb(k,j,ifock) = amb(k,j,ifock) - val1 * (d2(l,i,ifock)-d2(i,l,ifock))
              amb(l,j,ifock) = amb(l,j,ifock) - val1 * (d2(k,i,ifock)-d2(i,k,ifock))
            end if
          end if
        end do
      end do

    end associate

    buf%ncur = 0

  end subroutine

!###############################################################################

  subroutine int2_tdgrd_data_t_update(this, buf)
    implicit none
    class(int2_tdgrd_data_t), intent(inout) :: this
    type(int2_storage_t), intent(inout) :: buf
    integer :: i, j, k, l, n
    real(kind=dp) :: xval1, xval2, &
                     val, val1, val2
    integer :: mythread

    xval1 = 1 * this%scale_exchange
    xval2 = 2 * this%scale_coulomb
    mythread = buf%thread_id

    associate (&
                apb => this%apb(:,:,:,mythread), &
                amb => this%amb(:,:,:,mythread), &
                d2 => this%d2 &
                )

      do n = 1, buf%ncur
        i  = buf%ids(1,n)
        j  = buf%ids(2,n)
        k  = buf%ids(3,n)
        l  = buf%ids(4,n)
        val = buf%ints(n)

        val1 = val*xval1
        val2 = val*xval2

        if (this%int_apb) then
          ! A+B
          ! Coulomb
          apb(i,j,1) = apb(i,j,1)+val2*(d2(k,l,1)+d2(l,k,1)+d2(k,l,2)+d2(l,k,2))
          apb(k,l,1) = apb(k,l,1)+val2*(d2(i,j,1)+d2(j,i,1)+d2(i,j,2)+d2(j,i,2))

          apb(i,j,2) = apb(i,j,2)+val2*(d2(k,l,1)+d2(l,k,1)+d2(k,l,2)+d2(l,k,2))
          apb(k,l,2) = apb(k,l,2)+val2*(d2(i,j,1)+d2(j,i,1)+d2(i,j,2)+d2(j,i,2))

!         ! Exchange
          apb(i,k,1) = apb(i,k,1)-val1*(d2(j,l,1)+d2(l,j,1))
          apb(i,l,1) = apb(i,l,1)-val1*(d2(j,k,1)+d2(k,j,1))
          apb(j,k,1) = apb(j,k,1)-val1*(d2(i,l,1)+d2(l,i,1))
          apb(j,l,1) = apb(j,l,1)-val1*(d2(i,k,1)+d2(k,i,1))
!
          apb(i,k,2) = apb(i,k,2)-val1*(d2(j,l,2)+d2(l,j,2))
          apb(i,l,2) = apb(i,l,2)-val1*(d2(j,k,2)+d2(k,j,2))
          apb(j,k,2) = apb(j,k,2)-val1*(d2(i,l,2)+d2(l,i,2))
          apb(j,l,2) = apb(j,l,2)-val1*(d2(i,k,2)+d2(k,i,2))
        end if

        if (this%int_amb) then
          ! A-B
          amb(i,k,1) = amb(i,k,1)+val1*(d2(l,j,1)-d2(j,l,1))
          amb(i,l,1) = amb(i,l,1)+val1*(d2(k,j,1)-d2(j,k,1))
          amb(j,k,1) = amb(j,k,1)+val1*(d2(l,i,1)-d2(i,l,1))
          amb(j,l,1) = amb(j,l,1)+val1*(d2(k,i,1)-d2(i,k,1))
          amb(k,i,1) = amb(k,i,1)-val1*(d2(l,j,1)-d2(j,l,1))
          amb(l,i,1) = amb(l,i,1)-val1*(d2(k,j,1)-d2(j,k,1))
          amb(k,j,1) = amb(k,j,1)-val1*(d2(l,i,1)-d2(i,l,1))
          amb(l,j,1) = amb(l,j,1)-val1*(d2(k,i,1)-d2(i,k,1))
        end if
      end do

    end associate

    buf%ncur = 0

  end subroutine

!###############################################################################
!###############################################################################

  subroutine shltd(dsh,da,basis)
    use precision, only: dp
    use types, only: information
    use basis_tools, only: basis_set

    implicit none

    type(basis_set), intent(in) :: basis
    real(kind=dp), intent(out) :: dsh(:,:)
    real(kind=dp), intent(in), dimension(:,:,:) :: da

    integer :: ish, jsh, maxi, maxj, mini, &
               minj

!   RHF
    do ish = 1, basis%nshell
      mini = basis%ao_offset(ish)
      maxi = mini + basis%naos(ish) - 1
      do jsh = 1, ish
        minj = basis%ao_offset(jsh)
        maxj = minj + basis%naos(jsh) - 1
        dsh(ish,jsh) = maxval(abs(da(minj:maxj,mini:maxi,:)))
        dsh(jsh,ish) = dsh(ish,jsh)
      end do
    end do
  end subroutine shltd


  subroutine inivec(eiga,eigb,bvec_mo,xm, &
                    nocca,noccb,nvec)
    use precision, only: dp

    implicit none

    real(kind=dp), intent(in), dimension(:) :: eiga, eigb
    real(kind=dp), intent(out), dimension(:,:) :: bvec_mo
    real(kind=dp), intent(out), dimension(:) :: xm
    integer, intent(in) :: nocca, noccb
    integer, intent(in) :: nvec

    integer :: i, ij, j, k, nbf, mxvec

    integer :: itmp(nvec)
    real(kind=dp) :: xtmp(nvec)

    nbf = ubound(eiga, 1)
    mxvec = ubound(bvec_mo, 2)

  ! -- Set xm(xvec_dim)
    do j = noccb+1, nbf
      do i = 1, nocca
        ij = (j-noccb-1)*nocca+i
        xm(ij) = eigb(j)-eiga(i)
      end do
    end do

!   Find indices of the first `nvec` smallest values in the `xm` array
    itmp = 0 ! indices
    xtmp = huge(1.0d0) ! values

    do i = 1, ubound(xm,1)
      do j = 1, nvec
        if (xtmp(j) > xm(i)) exit
      end do
      if (j <= nvec) then
        ! new small value found, insert it into temporary arrays
        xtmp(j+1:nvec) = xtmp(j:nvec-1)
        itmp(j+1:nvec) = itmp(j:nvec-1)
        xtmp(j) = xm(i)
        itmp(j) = i
      end if
    end do

  ! -- Get initial vectors: bvec(xvec_dim,nvec)
    bvec_mo = 0.0_dp
    do k = 1, nvec
      bvec_mo(itmp(k),k) = 1.0_dp
    end do

  end subroutine inivec

  subroutine iatogen(pv,av,nocca,noccb)
    use precision, only: dp

    implicit none

    real(kind=dp), intent(in), contiguous, target :: pv(:)
    real(kind=dp), intent(out) :: av(:,:)
    integer, intent(in) :: nocca, noccb

    real(kind=dp), pointer :: ppv(:,:)
    integer :: nbf

    nbf = ubound(av, 1)
    ppv(1:nocca, noccb+1:nbf) => pv(:)

    av = 0.0_dp
    av(1:nocca, noccb+1:nbf) = ppv(1:nocca, noccb+1:nbf)

  end subroutine iatogen

  subroutine mntoia(pao,pmo,va,vb,nocca,noccb)
    use precision, only: dp

    implicit none

    real(kind=dp), intent(in), dimension(:,:) :: pao
    real(kind=dp), intent(out), dimension(*) :: pmo
    real(kind=dp), intent(in), target, dimension(:,:) :: va, vb
    integer, intent(in) :: nocca, noccb

    integer :: nbf
    real(kind=dp), allocatable :: scr(:,:)
    real(kind=dp), pointer :: vap(:,:), vbp(:,:)

    nbf = ubound(pao, 1)

    allocate(scr(nocca,nbf))

    vap => va(:,1:nocca)
    vbp => vb(:,noccb+1:)

    call dgemm('t','n',nocca,nbf,nbf, &
               1.0_dp,vap,nbf,pao,nbf, &
               0.0_dp,scr,nocca)

    call dgemm('n','n',nocca,nbf-noccb,nbf, &
               1.0_dp,scr,nocca,vbp,nbf, &
               0.0_dp,pmo,nocca)

    deallocate(scr)
  end subroutine mntoia

  subroutine rparedms(b,ap_b,am_b,xm_p,xm_m,nvec,tamm_dancoff)
    use precision, only: dp

    implicit none

    real(kind=dp), intent(in), dimension(:,:) :: b
    real(kind=dp), intent(in), dimension(:,:) :: am_b, ap_b
    real(kind=dp), intent(out), dimension(:,:) :: xm_m, xm_p
    integer, intent(in) :: nvec
    logical :: tamm_dancoff

    integer :: xvec_dim

    xvec_dim = ubound(b, 1)

    if (tamm_dancoff) then
      call dgemm('t','n',nvec,nvec,xvec_dim, &
                 1.0_dp,b,xvec_dim,ap_b,xvec_dim, &
                 0.0_dp,xm_p,nvec)
    else
      call dgemm('t','n',nvec,nvec,xvec_dim, &
                 1.0_dp,b,xvec_dim,ap_b,xvec_dim, &
                 0.0_dp,xm_p,nvec)
      call dgemm('t','n',nvec,nvec,xvec_dim, &
                 1.0_dp,b,xvec_dim,am_b,xvec_dim, &
                 0.0_dp,xm_m,nvec)
    end if
  end subroutine rparedms

!> @brief Diagonalize small reduced RPA matrix
  subroutine rpaeig(ee,vl,vr,apb,amb,scr,tamm_dancoff)

    use precision, only: dp
    use eigen, only: diag_symm_packed

    implicit none

    real(kind=dp), intent(out), dimension(:) :: ee
    real(kind=dp), intent(out), dimension(:,:) :: vl, vr
    real(kind=dp), intent(in), dimension(:,:) :: apb
    real(kind=dp), intent(inout), dimension(:,:) :: amb
    real(kind=dp), intent(inout), dimension(:) :: scr
    logical, intent(in) :: tamm_dancoff

    integer :: ierr, j, nvec

    nvec = ubound(vl, 2)

    if (tamm_dancoff) then

  !   Diagonailze A: VR
      if (nvec==1) then
        ee(1) = vr(1,1)
        vr(1,1) = 1.0_dp
      else
        call dtrttp('u', nvec, apb, nvec, scr, ierr)
        call diag_symm_packed(1,nvec,nvec,nvec,scr,ee,vr,ierr)
      end if
      return
    end if

!   sqrt(A-B)
    if (nvec==1) then
      amb(1,1) = sqrt(amb(1,1))
    else
      call dtrttp('u', nvec, amb, nvec, scr, ierr)
      call diag_symm_packed(1,nvec,nvec,nvec,scr,ee,vr,ierr)
      ee(1:nvec) = sign(sqrt(abs(ee(1:nvec))),ee(1:nvec))
      do j = 1, nvec
        vl(:,j) = vr(:,j)*ee(j)
      end do
      call dgemm('n','t',nvec,nvec,nvec, &
                 1.0_dp,vr,nvec,vl,nvec, &
                 0.0_dp,amb,nvec)
    end if

!   Form sqrt(A-B)*(A+B)*sqrt(A-B)
!   (A+B)*sqrt(A-B) : VL
    call dgemm('n','n',nvec,nvec,nvec, &
               1.0_dp,apb,nvec,amb,nvec, &
               0.0_dp,vl,nvec)
!   sqrt(A-B)*(A+B)*sqrt(A-B) : VR
    call dgemm('n','n',nvec,nvec,nvec, &
               1.0_dp,amb,nvec,vl,nvec, &
               0.0_dp,vr,nvec)

!   Diagonailze sqrt(A-B)*(A+B)*sqrt(A-B) : VR
    if (nvec==1) then
      ee(1) = vr(1,1)
      vl(1,1) = 1.0_dp
    else
      call dtrttp('u', nvec, vr, nvec, scr, ierr)
      call diag_symm_packed(1,nvec,nvec,nvec,scr,ee,vl,ierr)
    end if

!   Current vector VL is sqrt(1/(A-B)))|X+Y>.

!   VL into right eigenvector  VR = |X+Y>
    call dgemm('n','n',nvec,nvec,nvec, &
               1.0_dp,amb,nvec,vl,nvec, &
               0.0_dp,vr,nvec)

!   Left eigenvector   VL = = 1/E (A+B)|X+Y>
    call dgemm('n','n',nvec,nvec,nvec, &
               1.0_dp,apb,nvec,vr,nvec, &
               0.0_dp,vl,nvec)

    do j = 1, nvec
      vl(1:nvec,j) = vl(1:nvec,j)/sign(sqrt(abs(ee(j))),ee(j))
    end do

  end subroutine rpaeig

!> @brief Normalize `V1` and `V2` by biorthogonality condition
  subroutine rpavnorm(vr,vl,tamm_dancoff)

    use precision, only: dp

    implicit none

    real(kind=dp), intent(out), dimension(:,:) :: vr, vl
    logical, intent(in) :: tamm_dancoff

    real(kind=dp) :: scal, vrl, vrr
    integer :: ivec, nvec

    nvec = ubound(vl, 2)

    if (tamm_dancoff) then
      do ivec = 1, nvec
        vrr = dot_product(vr(:,ivec),vr(:,ivec))
        scal = sqrt(1.0_dp/vrr)
        vr(:,ivec) = vr(:,ivec)*scal
      end do
    else
      do ivec = 1, nvec
        vrl = dot_product(vr(:,ivec),vl(1:nvec,ivec))
        scal = sqrt(1.0D+00/vrl)
        vr(:,ivec)=vr(:,ivec)*scal
        vl(:,ivec)=vl(:,ivec)*scal
      end do
    end if
  end subroutine rpavnorm

!> @brief Remove negative eigenvalues
  subroutine rpaechk(ee,nvec,ndsr,imax,tamm_dancoff)

    use precision, only: dp

    implicit none

    real(kind=dp), intent(inout), dimension(:) :: ee
    integer, intent(in) :: nvec, ndsr
    integer, intent(inout) :: imax
    logical, intent(in) :: tamm_dancoff

  ! Number of negative eigenvalues : imax
    imax = count(ee(1:nvec)<0.0_dp)

    if (.not.tamm_dancoff) ee(1:ndsr) = sqrt(abs(ee(1:ndsr)))
  end subroutine rpaechk

!> @brief Print current excitation energies and errors
  subroutine rpaprint(ee,err,cnvtol,iter,imax,ndsr, do_neg)

    use precision, only: dp
    use physical_constants, only: UNITS_EV
    use io_constants, only: iw

    implicit none

    real(kind=dp), intent(in) :: ee(:)
    real(kind=dp), intent(in) :: cnvtol
    real(kind=dp), intent(in) :: err(:)
    integer, intent(in) :: iter
    integer, intent(in) :: imax
    integer, intent(in) :: ndsr
    logical, optional :: do_neg

    integer :: istat
    logical :: neg
    integer :: first

    neg = .false.
    if (present(do_neg)) neg = do_neg

    write(*,fmt='(/,4X,"Davidson iteration #",I4)') iter

    if (imax/=0.and..not.neg)  write(*,'(4X,"Number of negative eigenvalues =",I4)') imax
    first = imax + 1
    if (neg) first = 1

    do istat = 1, ndsr
      write(*,fmt='(4X,"State ",I4, &
             &3x,"E =",F12.6," eV",&
             &4x,"err. =",F10.6)') &
        istat, ee(istat)/UNITS_EV, err(istat)
    end do
    write(*,'(10X,"Max error =",1X,1P,E10.3,1X,"/",1P,E10.3)') maxval(err(first:ndsr)), cnvtol
    call flush(iw)
  end subroutine rpaprint

!> @brief Expand reduced vectors to real size space
  subroutine rpaexpndv(vr,vl,vro,vlo,br,bl,ndsr,tamm_dancoff)
    use precision, only: dp

    implicit none

    real(kind=dp), intent(in) :: vr(:,:), vl(:,:)
    real(kind=dp), intent(out) :: vro(:,:), vlo(:,:)
    real(kind=dp), intent(in) :: br(:,:), bl(:,:)
    integer, intent(in) :: ndsr
    logical, intent(in) :: tamm_dancoff

    integer :: xvec_dim
    integer :: nvec

    nvec = ubound(vr, 2)
    xvec_dim = ubound(vro, 1)

    call dgemm('n','n',xvec_dim,ndsr,nvec, &
               1.0_dp,br,xvec_dim,vr,nvec, &
               0.0_dp,vro,xvec_dim)

    if (tamm_dancoff) then
      vlo(:,1:ndsr) = vro(:,1:ndsr)
    else
      call dgemm('n','n',xvec_dim,ndsr,nvec, &
                 1.0_dp,bl,xvec_dim,vl,nvec, &
                 0.0_dp,vlo,xvec_dim)
    end if

  end subroutine rpaexpndv

!> @brief Construct residual vectors and check convergence
  subroutine rparesvec(q,w_l,w_r,v_l,v_r,ee,abd, &
                       ndsr,errors,tol,imax,tamm_dancoff)

    use precision, only: dp

    implicit none

    real(kind=dp), intent(inout), dimension(:,:) :: w_l, w_r, v_l, v_r
    real(kind=dp), intent(out), &
      dimension(ubound(w_l,1),ndsr,*) :: q
    real(kind=dp), intent(in), dimension(:) :: ee
    real(kind=dp), intent(in), dimension(:) :: abd
    integer, intent(in) :: ndsr
    real(kind=dp), intent(inout) :: errors(:)
    real(kind=dp), intent(in) :: tol
    integer, intent(in) :: imax
    logical :: tamm_dancoff

    real(kind=dp) :: er_l, er_r
    integer :: ivec, xvec_dim

    xvec_dim = ubound(w_l,1)

!   Residual vector W_l and W_r
    do ivec = 1, ndsr
      w_r(1:xvec_dim,ivec) = w_r(1:xvec_dim,ivec) - ee(ivec)*v_r(1:xvec_dim,ivec)
    end do
    if (.not.tamm_dancoff) then
      do ivec = 1, ndsr
        w_l(1:xvec_dim,ivec) = w_l(1:xvec_dim,ivec) - ee(ivec)*v_l(1:xvec_dim,ivec)
      end do
    end if

!   Norms of W
    errors = 0.0_dp

    if (tamm_dancoff) then
      do ivec = imax+1, ndsr
        errors(ivec) = dot_product(w_r(:,ivec),w_r(:,ivec))
        if (errors(ivec)>1.0_dp) then
          write(*,'(4X,"Large error detected")')
          write(*,'(4X,"State#=",I4)') ivec
          write(*,'(4X,"Error right =",1X,1P,E10.3)') errors(ivec)
          errors(ivec) = 0.0_dp
        end if

!       Get new vectors Q
        if (errors(ivec)>tol) then
          q(:,ivec,1) = w_r(:,ivec)/(ee(ivec)-abd(:))
        else
          q(:,ivec,1) = 0.0_dp
        end if
      end do
    else
      do ivec = imax+1, ndsr
        er_l = dot_product(w_l(:,ivec),w_l(:,ivec))
        er_r = dot_product(w_r(:,ivec),w_r(:,ivec))
        errors(ivec) = max(er_l,er_r)
        if (errors(ivec)>1.0_dp) then
          write(*,'(4X,"Large error detected")')
          write(*,'(4X,"State#=",I4)') ivec
          write(*,'(4X,"Error left/right =",1X,1P,E10.3,"/",1X,1P,E10.3)') er_l, er_r
          errors(ivec) = 0.0_dp
        end if

!       Get new vectors Q
        if (errors(ivec)>tol) then
          q(:,ivec,1) = 1.0d0/(ee(ivec)-abd(:))
          q(:,ivec,2) = q(:,ivec,1)

          q(:,ivec,1) = q(:,ivec,1)*w_l(:,ivec)
          q(:,ivec,2) = q(:,ivec,2)*w_r(:,ivec)
        else
          q(:,ivec,1:2) = 0.0_dp
        end if
      end do
    end if

  end subroutine rparesvec

!> @brief Orthonormalize q(xvec_dim,ndsr*2) and append to bvec
  subroutine rpanewb(ndsr,bvec,q,novec,nvec,ick,tamm_dancoff)
    use precision, only: dp

    implicit none

    integer, intent(in) :: ndsr
    real(kind=dp), intent(out), dimension(:,:) :: bvec
    real(kind=dp), intent(inout), dimension(:,:) :: q
    integer, intent(inout) :: novec, nvec
    integer, intent(out) :: ick
    logical, intent(in) :: tamm_dancoff

    real(kind=dp) :: bq, fnorm
    integer :: istat, k, ms, ndsrt, mxvec
    real(kind=dp), parameter :: norm_threshold = 1.0D-09

    mxvec = ubound(bvec, 2)

!   Save nvec as novec
    novec = nvec
    ick = 0

!   Modified Gram-Schmidt orthonormalization
    ndsrt = ndsr*2
    if (tamm_dancoff) ndsrt = ndsr

    do k = 1, ndsrt
!     MGS: orthonormalize next vector w.r.t. all
!     previous vectors
      do istat = 1, nvec
        bq = dot_product(bvec(:,istat),q(:,k))
        q(:,k) = q(:,k) - bq*bvec(:,istat)
      end do

      fnorm = norm2(q(:,k))

!     Possible linear dependency, skip this vector
      if (fnorm<norm_threshold) cycle

      if (nvec==mxvec) then
!       Error termination, no space left for new vectors
        ick = 2
        return
      end if

!     Append new b vector
      nvec = nvec+1
      bvec(:,nvec) = q(:,k)/fnorm
    end do

!   Error termination, no vectors added
    ms = nvec-novec
    if (ms==0) ick = 3
  end subroutine rpanewb

!> @breif Add (E_a-E_i)*Z_ai to Pmo
  subroutine esum(e,pmo,z,nocc,ivec)

    use precision, only: dp

    implicit none

    real(kind=dp), intent(in) :: e(:)
    real(kind=dp), intent(inout) :: pmo(:,:)
    real(kind=dp), intent(in) :: z(:,:)
    integer, intent(in) :: nocc, ivec

    integer :: i, ij, j, nbf

    nbf = ubound(e, 1)

    do j = nocc+1, nbf
      do i = 1, nocc
        ij = (j-nocc-1)*nocc + i
        pmo(ij,ivec) = pmo(ij,ivec) + (e(j)-e(i))*z(ij,ivec)
      end do
    end do
  end subroutine esum

!> @brief Compute unrelaxed difference density matrix for R-TDDFT
  subroutine tdhf_unrelaxed_density(xmy, xpy, mo, t, nocc, tda)
    use precision, only: dp
    use mathlib, only: orthogonal_transform
    use mathlib, only: pack_matrix

    implicit none

    real(kind=dp), intent(in) :: xpy(*), xmy(*)
    real(kind=dp), intent(in) :: mo(:,:)
    real(kind=dp), intent(out) :: t(*)
    logical, intent(in) :: tda
    integer, intent(in) :: nocc

    integer :: nbf, nvir
    real(kind=dp), allocatable :: t_mo(:,:), t_ao(:,:), scr(:,:)

    nbf= ubound(mo,1)
    allocate(t_mo(nbf,nbf), t_ao(nbf,nbf), scr(nbf,nbf), source=0.0_dp)

    nvir = nbf-nocc

    if (tda) then
      ! vir->vib block
      call dgemm('t', 'n', nvir, nvir, nocc, &
                  1.0d0, xpy, nocc, &
                         xpy, nocc, &
                  0.0d0, t_mo(nocc+1:,nocc+1), nbf)

      ! occ->occ block
      call dgemm('n', 't', nocc, nocc, nvir,  &
                 -1.0d0, xpy, nocc, &
                         xpy, nocc,  &
                  0.0d0, t_mo,  nbf)
    else
      ! vir->vib block
      call dgemm('t', 'n', nvir, nvir, nocc, &
                 0.5d0, xpy, nocc, &
                        xpy, nocc,  &
                 0.0d0, t_mo(nocc+1:, nocc+1), nbf)
      call dgemm('t', 'n', nvir, nvir, nocc,  &
                 0.5d0, xmy, nocc, &
                        xmy, nocc,  &
                 1.0d0, t_mo(nocc+1:, nocc+1), nbf)

      ! occ->occ block
      call dgemm('n', 't', nocc, nocc, nvir,  &
                 -0.5d0, xpy, nocc, &
                         xpy, nocc,  &
                  0.0d0, t_mo, nbf)
      call dgemm('n', 't', nocc, nocc, nvir,  &
                 -0.5d0, xmy, nocc, &
                         xmy, nocc,  &
                  1.0d0, t_mo, nbf)
    endif

    ! MO->AO
    call orthogonal_transform('t', nbf, mo, t_mo, t_ao, scr)

    call pack_matrix(t_ao, t(1:(nbf+1)*nbf/2) )

  end subroutine

!###############################################################################

  subroutine int2_rpagrd_data_t_parallel_start(this, basis, nthreads)
    implicit none
    class(int2_rpagrd_data_t), target, intent(inout) :: this
    type(basis_set), intent(in) :: basis
    integer, intent(in) :: nthreads
    integer :: nbf, nsh

    this%nbf = basis%nbf
    nbf = this%nbf
    this%np = 0
    this%nm = 0
    this%nt = 0
    if (associated(this%xpy)) this%np = ubound(this%xpy, size(shape(this%xpy)))
    if (associated(this%xmy)) this%nm = ubound(this%xmy, size(shape(this%xmy)))
    if (associated(this%t))   this%nt = ubound(this%t, size(shape(this%t)))
    this%nthreads = nthreads
    nsh = basis%nshell

    if (this%cur_pass == 1) then
      if (allocated(this%hpp)) deallocate(this%hpp)
      if (allocated(this%hpt)) deallocate(this%hpt)
      if (allocated(this%hmm)) deallocate(this%hmm)
      if (allocated(this%dsh)) deallocate(this%dsh)

      if (this%np > 0) &
        allocate(this%hpp(nbf, nbf, this%nspin, this%np, nthreads), &
                 source=0.0d0)

      if (this%nt > 0) &
        allocate(this%hpt(nbf, nbf, this%nspin, this%nt, nthreads),  &
                 source=0.0d0)

      if (this%nm > 0) &
        allocate(this%hmm(nbf, nbf, this%nspin, this%nm, nthreads),  &
                 source=0.0d0)

      allocate(this%dsh(nsh,nsh), source=0.0d0)
    end if

    call this%init_screen(basis)

  end subroutine

!###############################################################################

  subroutine int2_rpagrd_data_t_parallel_stop(this)
    implicit none
    integer :: plast, tlast, mlast, nbf
    class(int2_rpagrd_data_t), intent(inout) :: this

    if (this%cur_pass /= this%num_passes) return
    plast = size(shape(this%hpp))
    tlast = size(shape(this%hpt))
    mlast = size(shape(this%hmm))
    nbf = this%nbf

    if (this%nthreads /= 1) then
      if (this%np > 0) this%hpp(:,:,:,:,lbound(this%hpp, plast )) = sum(this%hpp, dim=plast)
      if (this%nt > 0) this%hpt(:,:,:,:,lbound(this%hpt, tlast )) = sum(this%hpt, dim=tlast)
      if (this%nm > 0) this%hmm(:,:,:,:,lbound(this%hmm, mlast )) = sum(this%hmm, dim=mlast)
      this%nthreads = 1
    end if
    if (this%np > 0) call this%pe%allreduce(this%hpp(:,:,:,:,1),&
            size(this%hpp(:,:,:,:,1)))
    if (this%nt > 0) call this%pe%allreduce(this%hpt(:,:,:,:,1),&
            size(this%hpt(:,:,:,:,1)))
    if (this%nm > 0) call this%pe%allreduce(this%hmm(:,:,:,:,1),&
            size(this%hmm(:,:,:,:,1)))


    if (this%cur_pass == this%num_passes) then
      if (this%np > 0) call symmetrize_matrices(this%hpp, nbf, this%np*this%nspin)
      if (this%nt > 0) call symmetrize_matrices(this%hpt, nbf, this%nt*this%nspin)
    end if

  end subroutine

!###############################################################################

  subroutine int2_rpagrd_data_t_clean(this)
    implicit none
    class(int2_rpagrd_data_t), intent(inout) :: this
    if (allocated(this%hpp)) deallocate(this%hpp)
    if (allocated(this%hpt)) deallocate(this%hpt)
    if (allocated(this%hmm)) deallocate(this%hmm)
    if (allocated(this%dsh)) deallocate(this%dsh)
    nullify(this%xpy)
    nullify(this%xmy)
    nullify(this%t)
  end subroutine

!###############################################################################

  subroutine int2_rpagrd_data_t_init_screen(this, basis)
    implicit none
    class(int2_rpagrd_data_t), target, intent(inout) :: this
    type(basis_set), intent(in) :: basis

!   Form shell density
    this%dsh = 0
    if (this%np > 0) call shlrpagrd(this%dsh, this%xpy, basis)
    if (this%np > 0) call shlrpagrd(this%dsh, this%xmy, basis)
    if (this%nt > 0) call shlrpagrd(this%dsh, this%t, basis)
    this%max_den = maxval(abs(this%dsh))

  end subroutine

!###############################################################################

  subroutine int2_rpagrd_data_t_update(this, buf)
    implicit none
    class(int2_rpagrd_data_t), intent(inout) :: this
    type(int2_storage_t), intent(inout) :: buf
    integer :: mythread
    integer :: i

    mythread = buf%thread_id

    ! H+[X+Y]
    do i = 1, this%np
      call this%hplus(buf, this%hpp(:,:,:,i, mythread), this%xpy(:,:,:,i))
    end do

    ! H+[T]
    do i = 1, this%nt
      call this%hplus(buf, this%hpt(:,:,:,i,mythread), this%t(:,:,:,i))
    end do

    ! H-[X-Y]
    do i = 1, this%nm
      call this%hminus(buf, this%hmm(:,:,:,i,mythread), this%xmy(:,:,:,i))
    end do

    buf%ncur = 0

  end subroutine

!###############################################################################

!> @brief Compute H^+[V] over a set of integrals
  subroutine int2_rpagrd_data_t_update_hplus(this, buf, hp, v)
    implicit none
    class(int2_rpagrd_data_t), intent(inout) :: this
    type(int2_storage_t), intent(inout) :: buf
    real(kind=dp), intent(inout) :: hp(:,:,:)
    real(kind=dp), intent(in) :: v(:,:,:)
    integer :: i, j, k, l, n
    real(kind=dp) :: xfact, cfact, &
                     val, xval, cval
    integer :: mythread

    mythread = buf%thread_id

    if (this%nspin == 1) then

      xfact = 2 * this%scale_exchange
      cfact = 8 * this%scale_coulomb

      do n = 1, buf%ncur
        i  = buf%ids(1,n)
        j  = buf%ids(2,n)
        k  = buf%ids(3,n)
        l  = buf%ids(4,n)
        val = buf%ints(n)

        xval = val*xfact
        cval = val*cfact
        ! Coulomb
        hp(i,j,1) = hp(i,j,1)+cval*v(l,k,1)
        hp(k,l,1) = hp(k,l,1)+cval*v(j,i,1)

!       Exhange
        hp(i,k,1) = hp(i,k,1)-xval*v(l,j,1)
        hp(i,l,1) = hp(i,l,1)-xval*v(k,j,1)
        hp(j,k,1) = hp(j,k,1)-xval*v(l,i,1)
        hp(j,l,1) = hp(j,l,1)-xval*v(k,i,1)
      end do

    else if (this%nspin == 2) then

      xfact = 1 * this%scale_exchange
      cfact = 2 * this%scale_coulomb

      do n = 1, buf%ncur
        i  = buf%ids(1,n)
        j  = buf%ids(2,n)
        k  = buf%ids(3,n)
        l  = buf%ids(4,n)
        val = buf%ints(n)

        xval = val*xfact
        cval = val*cfact
        ! Coulomb
        hp(i,j,1) = hp(i,j,1)+cval*(v(k,l,1)+v(l,k,1)+v(k,l,2)+v(l,k,2))
        hp(k,l,1) = hp(k,l,1)+cval*(v(i,j,1)+v(j,i,1)+v(i,j,2)+v(j,i,2))

        hp(i,j,2) = hp(i,j,2)+cval*(v(k,l,1)+v(l,k,1)+v(k,l,2)+v(l,k,2))
        hp(k,l,2) = hp(k,l,2)+cval*(v(i,j,1)+v(j,i,1)+v(i,j,2)+v(j,i,2))

!       Exhange
        hp(i,k,1) = hp(i,k,1)-xval*(v(j,l,1)+v(l,j,1))
        hp(i,l,1) = hp(i,l,1)-xval*(v(j,k,1)+v(k,j,1))
        hp(j,k,1) = hp(j,k,1)-xval*(v(i,l,1)+v(l,i,1))
        hp(j,l,1) = hp(j,l,1)-xval*(v(i,k,1)+v(k,i,1))
!
        hp(i,k,2) = hp(i,k,2)-xval*(v(j,l,2)+v(l,j,2))
        hp(i,l,2) = hp(i,l,2)-xval*(v(j,k,2)+v(k,j,2))
        hp(j,k,2) = hp(j,k,2)-xval*(v(i,l,2)+v(l,i,2))
        hp(j,l,2) = hp(j,l,2)-xval*(v(i,k,2)+v(k,i,2))
      end do

    end if

  end subroutine

!###############################################################################

!> @brief Compute H^-[V] over a set of integrals
  subroutine int2_rpagrd_data_t_update_hminus(this, buf, hm, v)
    implicit none
    class(int2_rpagrd_data_t), intent(inout) :: this
    type(int2_storage_t), intent(inout) :: buf
    real(kind=dp), intent(inout) :: hm(:,:,:)
    real(kind=dp), intent(in) :: v(:,:,:)
    integer :: i, j, k, l, n
    real(kind=dp) :: xfact, val, xval
    integer :: mythread

    xfact = this%scale_exchange
    mythread = buf%thread_id

    do n = 1, buf%ncur
      i  = buf%ids(1,n)
      j  = buf%ids(2,n)
      k  = buf%ids(3,n)
      l  = buf%ids(4,n)
      val = buf%ints(n)

      xval = val*xfact

!     Exhange
      hm(i,k,1) = hm(i,k,1)+xval*(v(l,j,1)-v(j,l,1))
      hm(i,l,1) = hm(i,l,1)+xval*(v(k,j,1)-v(j,k,1))
      hm(j,k,1) = hm(j,k,1)+xval*(v(l,i,1)-v(i,l,1))
      hm(j,l,1) = hm(j,l,1)+xval*(v(k,i,1)-v(i,k,1))

!     Exhange
      hm(k,i,1) = hm(k,i,1)-xval*(v(l,j,1)-v(j,l,1))
      hm(l,i,1) = hm(l,i,1)-xval*(v(k,j,1)-v(j,k,1))
      hm(k,j,1) = hm(k,j,1)-xval*(v(l,i,1)-v(i,l,1))
      hm(l,j,1) = hm(l,j,1)-xval*(v(k,i,1)-v(i,k,1))
    end do
  end subroutine

!###############################################################################

  subroutine shlrpagrd(dsh,d,basis)
    use precision, only: dp
    use types, only: information
    use basis_tools, only: basis_set

    implicit none

    type(basis_set), intent(in) :: basis
    real(kind=dp), intent(out) :: dsh(:,:)
    real(kind=dp), intent(in), dimension(:,:,:,:) :: d

    integer :: ish, jsh, maxi, maxj, mini, minj
    real(kind=dp) :: mxv

!   RHF
    do ish = 1, basis%nshell
      mini = basis%ao_offset(ish)
      maxi = mini + basis%naos(ish)-1
      do jsh = 1, ish
        minj = basis%ao_offset(jsh)
        maxj = minj + basis%naos(jsh) - 1
        mxv = maxval(abs(d(minj:maxj,mini:maxi,:,:)))
        dsh(ish,jsh) = max(dsh(ish,jsh), mxv)
        dsh(jsh,ish) = dsh(ish,jsh)
      end do
    end do
  end subroutine shlrpagrd

!###############################################################################

  subroutine symmetrize_matrices(a, lda, nmtx)
    use mathlib, only: symmetrize_matrix
    implicit none
    real(kind=dp), intent(inout) :: a(lda,lda,*)
    integer, intent(in) :: lda, nmtx
    integer :: i

    do i = 1, nmtx
        call symmetrize_matrix(a(:,:,i), lda)
    end do
  end subroutine

!###############################################################################

end module tdhf_lib