dft_gridint_fxc.F90 Source File


This file depends on

sourcefile~~dft_gridint_fxc.f90~~EfferentGraph sourcefile~dft_gridint_fxc.f90 dft_gridint_fxc.F90 sourcefile~basis_tools.f90 basis_tools.F90 sourcefile~dft_gridint_fxc.f90->sourcefile~basis_tools.f90 sourcefile~dft_gridint.f90 dft_gridint.F90 sourcefile~dft_gridint_fxc.f90->sourcefile~dft_gridint.f90 sourcefile~dft_molgrid.f90 dft_molgrid.F90 sourcefile~dft_gridint_fxc.f90->sourcefile~dft_molgrid.f90 sourcefile~mathlib.f90 mathlib.F90 sourcefile~dft_gridint_fxc.f90->sourcefile~mathlib.f90 sourcefile~oqp_linalg.f90 oqp_linalg.F90 sourcefile~dft_gridint_fxc.f90->sourcefile~oqp_linalg.f90 sourcefile~precision.f90 precision.F90 sourcefile~dft_gridint_fxc.f90->sourcefile~precision.f90 sourcefile~types.f90 types.F90 sourcefile~dft_gridint_fxc.f90->sourcefile~types.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~constants_io.f90 constants_io.F90 sourcefile~basis_tools.f90->sourcefile~constants_io.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~dft_gridint.f90->sourcefile~basis_tools.f90 sourcefile~dft_gridint.f90->sourcefile~dft_molgrid.f90 sourcefile~dft_gridint.f90->sourcefile~oqp_linalg.f90 sourcefile~dft_gridint.f90->sourcefile~precision.f90 sourcefile~dft_gridint.f90->sourcefile~constants_io.f90 sourcefile~dft_xc_libxc.f90 dft_xc_libxc.F90 sourcefile~dft_gridint.f90->sourcefile~dft_xc_libxc.f90 sourcefile~functionals.f90 functionals.F90 sourcefile~dft_gridint.f90->sourcefile~functionals.f90 sourcefile~dft_gridint.f90->sourcefile~parallel.f90 sourcefile~dft_molgrid.f90->sourcefile~precision.f90 sourcefile~bragg_slater.f90 bragg_slater.F90 sourcefile~dft_molgrid.f90->sourcefile~bragg_slater.f90 sourcefile~dft_molgrid.f90->sourcefile~constants.f90 sourcefile~dft_partfunc.f90 dft_partfunc.F90 sourcefile~dft_molgrid.f90->sourcefile~dft_partfunc.f90 sourcefile~grid_storage.f90 grid_storage.F90 sourcefile~dft_molgrid.f90->sourcefile~grid_storage.f90 sourcefile~lebedev.f90 lebedev.F90 sourcefile~dft_molgrid.f90->sourcefile~lebedev.f90 sourcefile~mathlib.f90->sourcefile~oqp_linalg.f90 sourcefile~mathlib.f90->sourcefile~precision.f90 sourcefile~eigen.f90 eigen.F90 sourcefile~mathlib.f90->sourcefile~eigen.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~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.f90 sourcefile~basis_library.f90->sourcefile~constants_io.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~messages.f90 sourcefile~mathlib_types.f90 mathlib_types.F90 sourcefile~blas_wrap.f90->sourcefile~mathlib_types.f90 sourcefile~bragg_slater.f90->sourcefile~precision.f90 sourcefile~constants.f90->sourcefile~precision.f90 sourcefile~dft_partfunc.f90->sourcefile~precision.f90 sourcefile~dft_xc_libxc.f90->sourcefile~precision.f90 sourcefile~dft_xc_libxc.f90->sourcefile~functionals.f90 sourcefile~dft_xclib.f90 dft_xclib.F90 sourcefile~dft_xc_libxc.f90->sourcefile~dft_xclib.f90 sourcefile~eigen.f90->sourcefile~oqp_linalg.f90 sourcefile~eigen.f90->sourcefile~precision.f90 sourcefile~eigen.f90->sourcefile~messages.f90 sourcefile~eigen.f90->sourcefile~mathlib_types.f90 sourcefile~physical_constants.f90 physical_constants.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~grid_storage.f90->sourcefile~precision.f90 sourcefile~lapack_wrap.f90->sourcefile~precision.f90 sourcefile~lapack_wrap.f90->sourcefile~messages.f90 sourcefile~lapack_wrap.f90->sourcefile~mathlib_types.f90 sourcefile~lebedev.f90->sourcefile~precision.f90 sourcefile~lebedev.f90->sourcefile~messages.f90 sourcefile~messages.f90->sourcefile~precision.f90 sourcefile~messages.f90->sourcefile~constants_io.f90 sourcefile~parallel.f90->sourcefile~precision.f90 sourcefile~dft_xclib.f90->sourcefile~precision.f90 sourcefile~dft_xclib.f90->sourcefile~functionals.f90

Files dependent on this one

sourcefile~~dft_gridint_fxc.f90~~AfferentGraph sourcefile~dft_gridint_fxc.f90 dft_gridint_fxc.F90 sourcefile~dft_gridint_gxc.f90 dft_gridint_gxc.F90 sourcefile~dft_gridint_gxc.f90->sourcefile~dft_gridint_fxc.f90 sourcefile~tdhf_energy.f90 tdhf_energy.F90 sourcefile~tdhf_energy.f90->sourcefile~dft_gridint_fxc.f90 sourcefile~tdhf_z_vector.f90 tdhf_z_vector.F90 sourcefile~tdhf_z_vector.f90->sourcefile~dft_gridint_fxc.f90 sourcefile~tdhf_z_vector.f90->sourcefile~dft_gridint_gxc.f90

Source Code

module mod_dft_gridint_fxc

  use precision, only: fp
  use mod_dft_gridint, only: xc_engine_t, xc_consumer_t
  use mod_dft_gridint, only: X__, Y__, Z__
  use mod_dft_gridint, only: OQP_FUNTYP_LDA, OQP_FUNTYP_GGA, OQP_FUNTYP_MGGA
  use oqp_linalg

  implicit none

!-------------------------------------------------------------------------------

  type, extends(xc_consumer_t) :: xc_consumer_tde_t
    integer :: nMtx = 1
    real(kind=fp), pointer :: da(:,:,:) => null()
    real(kind=fp), pointer :: db(:,:,:) => null()
    real(kind=fp), allocatable :: focks(:,:,:,:,:)
    real(kind=fp), allocatable :: mo(:,:,:,:,:)
    real(kind=fp), allocatable :: rRho(:,:,:,:)
    real(kind=fp), allocatable :: drRho(:,:,:,:,:)
    real(kind=fp), allocatable :: rTau(:,:,:,:)
!   Temporary storage
    real(kind=fp), allocatable :: focks_(:,:)
    real(kind=fp), allocatable :: tmpMO_(:,:)
    real(kind=fp), allocatable :: tmpDensity_(:,:,:)
    real(kind=fp), allocatable :: moG1_(:,:)
    real(kind=fp), allocatable :: tmp_(:,:)
  contains
    procedure :: parallel_start
    procedure :: parallel_stop
    procedure :: update
    procedure :: postUpdate
    procedure :: clean
    procedure :: resetXCPointers
    procedure :: computeRAll
    procedure :: resetOrbPointers
    procedure :: RUpdate
    procedure :: UUpdate
  end type

!-------------------------------------------------------------------------------

  private
  public tddft_fxc
  public utddft_fxc
  public xc_consumer_tde_t

!-------------------------------------------------------------------------------

contains

!-------------------------------------------------------------------------------

  subroutine parallel_start(self, xce, nThreads)
    implicit none
    class(xc_consumer_tde_t), target, intent(inout) :: self
    class(xc_engine_t), intent(in) :: xce
    integer, intent(in) :: nThreads
    integer :: nSpin
    call self%clean()

    nSpin = 1
    if (xce%hasBeta) nSpin = 2

    allocate( &
        self%focks(xce%numAOs, xce%numAOs, self%nMtx, nSpin, nThreads) &
      , self%mo(xce%numAOs, xce%maxPts, self%nMtx, nSpin, nThreads) &
      , self%rRho(nSpin, xce%maxPts, self%nMtx, nThreads) &
      , self%drRho(4, nSpin, xce%maxPts, self%nMtx, nThreads) &
!       Temporary storage
      , self%focks_(xce%numAOs * xce%numAOs * self%nMtx * nSpin, nThreads) &
      , self%tmpMO_(xce%numAOs * xce%maxPts * self%nMtx * nSpin, nThreads) &
      , self%tmpDensity_(xce%numAOs * xce%numAOs * self%nMtx, nSpin, nThreads) &
      , self%tmp_(xce%numAOs * xce%maxPts * xce%numTmpVec, nthreads) &
      , source=0.0d0)

    if (xce%funTyp == OQP_FUNTYP_MGGA) then
        allocate( &
            self%rTau(nSpin, xce%maxPts, self%nMtx, nThreads) &
!       Temporary storage
          , self%moG1_(xce%numAOs*xce%maxPts*3*self%nMtx, nThreads) &
          , source=0.0d0)
    end if
  end subroutine

!-------------------------------------------------------------------------------

  subroutine parallel_stop(self)
    implicit none
    class(xc_consumer_tde_t), intent(inout) :: self

    if (ubound(self%focks,5) /= 1) then
      self%focks(:,:,:,:,lbound(self%focks,5)) = sum(self%focks, dim=5)
    end if
    call self%pe%allreduce(self%focks(:,:,:,:,1), &
              size(self%focks(:,:,:,:,1)))
  end subroutine

!-------------------------------------------------------------------------------

  subroutine clean(self)
    implicit none
    class(xc_consumer_tde_t), intent(inout) :: self
    if (allocated(self%focks)) deallocate(self%focks)
    if (allocated(self%mo)) deallocate(self%mo)
    if (allocated(self%rRho)) deallocate(self%rRho)
    if (allocated(self%drRho)) deallocate(self%drRho)
    if (allocated(self%rTau)) deallocate(self%rTau)
!       Temporary storage
    if (allocated(self%focks_)) deallocate(self%focks_)
    if (allocated(self%tmpMO_)) deallocate(self%tmpMO_)
    if (allocated(self%tmpDensity_)) deallocate(self%tmpDensity_)
    if (allocated(self%moG1_)) deallocate(self%moG1_)
    if (allocated(self%tmp_)) deallocate(self%tmp_)
  end subroutine

!-------------------------------------------------------------------------------

 subroutine update(self, xce, myThread)

    class(xc_consumer_tde_t), intent(inout) :: self
    class(xc_engine_t), intent(in) :: xce
    integer :: myThread

    call self%computeRAll(xce, myThread)

    if (xce%hasBeta) then
      call self%UUpdate(xce, myThread)
    else
      call self%RUpdate(xce, myThread)
    end if
 end subroutine

 subroutine postUpdate(self, xce, myThread)

    class(xc_consumer_tde_t), intent(inout) :: self
    class(xc_engine_t), intent(in) :: xce
    integer :: myThread

    real(kind=fp), pointer :: focks(:,:,:,:)

    call self%resetOrbPointers(xce, focks=focks, myThread=myThread)

    associate(  numAOs  => xce%numAOs_p &  ! number of pruned AOs
              , indices => xce%indices_p &
      )
      if (xce%skip_p) then

         self%focks(:,:,:,:,myThread) = self%focks(:,:,:,:,myThread) + focks

      else

         self%focks(indices(1:numAOs), indices(1:numAOs), :, :, myThread) &
            = self%focks(indices(1:numAOs), indices(1:numAOs), :, :, myThread) &
            + focks

      end if

    end associate

 end subroutine

!-------------------------------------------------------------------------------
!> @brief Adjust internal memory storage for a given
!>  number of pruned grid points
!> @author Konstantin Komarov
 subroutine resetXCPointers(self, xce, da, db, mo, moG1, myThread)
    class(xc_consumer_tde_t), target, intent(inout) :: self
    class(xc_engine_t), intent(in) :: xce
    real(kind=fp), intent(out), pointer :: da(:,:,:)
    real(kind=fp), intent(out), pointer :: db(:,:,:)
    real(kind=fp), intent(out), pointer :: mo(:,:,:,:)
    real(kind=fp), intent(out), pointer :: moG1(:,:,:,:)
    integer, intent(in) :: myThread
    integer :: nSpin

    if (xce%skip_p) then

!     no pruned AOs
      da => self%da
      if (xce%hasBeta) db => self%db
      mo => self%mo(:,:,:,:,myThread)

    else

!     pruned AOs
      nSpin = 1
      if (xce%hasBeta) nSpin = 2

      associate ( indices => xce%indices_p &
                , numAOs  => xce%numAOs_p & ! number of pruned AOs
                , numPts  => xce%numPts &
                , nMtx    => self%nMtx)
        ! Set pointer for Dens A
        da(1:numAOs, 1:numAOs, 1:nMtx) => &
              self%tmpDensity_(1:numAOs*numAOs*nMtx, 1, myThread)
        ! Compress Dens A
        da(1:numAOs, 1:numAOs,:) = &
              self%da(indices(1:numAOs), indices(1:numAOs), :)
        ! Set pointer for MOs
        da(1:numAOs, 1:numAOs, 1:nMtx) => &
              self%tmpDensity_(1:numAOs*numAOs*nMtx, 1, myThread)
        mo(1:numAOs, 1:numPts, 1:nMtx, 1:nSpin) => &
              self%tmpMO_(1:numAOs*numPts*nMtx*nSpin, myThread)

        if (xce%funTyp == OQP_FUNTYP_MGGA) then
          ! Set pointer for tmp mGGA
          moG1(1:numAOs, 1:numPts, 1:3, 1:nMtx) => &
              self%moG1_(1:numAOs*numPts*3*nMtx, myThread)
        end if

        if (xce%hasBeta) then
            ! Set pointer for Dens B
            db(1:numAOs, 1:numAOs, 1:nMtx) => &
                  self%tmpDensity_(1:numAOs*numAOs*nMtx, 2, myThread)
            ! Compress Dens B
            db(1:numAOs, 1:numAOs, :) = &
                  self%db(indices(1:numAOs), indices(1:numAOs), :)
        end if

      end associate

    end if
    ! In any case set pointer for moG1
    if (xce%funTyp == OQP_FUNTYP_MGGA) then
      ! Set pointer for tmp mGGA
      moG1(1:xce%numAOs_p, 1:xce%numPts, 1:3, 1:self%nMtx) => &
          self%moG1_(1:xce%numAOs_p*xce%numPts*3*self%nMtx, myThread)
    end if


 end subroutine

 subroutine resetOrbPointers(self, xce, focks, tmp, myThread)
    class(xc_consumer_tde_t), target, intent(inout) :: self
    class(xc_engine_t), intent(in) :: xce
    real(kind=fp), intent(out), pointer :: focks(:,:,:,:)
    real(kind=fp), intent(out), pointer, optional :: tmp(:,:,:)
    integer, intent(in) :: myThread
    integer :: nSpin

    nSpin = 1
    if (xce%hasBeta) nSpin = 2
!   pruned AOs or no pruned AOs
    associate ( numAOs => xce%numAOs_p &  ! number of pruned AOs
      )
      focks(1:numAOs, 1:numAOs, 1:self%nMtx, 1:nSpin) => &
         self%focks_(1:numAOs * numAOs * self%nMtx * nSpin, myThread)

      if (present(tmp)) &
        tmp(1:numAOs, 1:xce%numPts, 1:xce%numTmpVec) => &
          self%tmp_(1:numAOs * xce%numPts * xce%numTmpVec, myThread)
    end associate

 end subroutine

!> @brief Compute required MO-like values as well as \rho, \sigma, and \tau
 subroutine computeRAll(self, xce, myThread)

    class(xc_consumer_tde_t), intent(inout) :: self
    class(xc_engine_t), intent(in) :: xce
    integer :: myThread
    real(kind=fp), pointer :: da(:,:,:)
    real(kind=fp), pointer :: db(:,:,:)
    real(kind=fp), pointer :: mo(:,:,:,:)
    real(kind=fp), pointer :: moG1(:,:,:,:)

    call self%resetXCPointers( &
             xce, da, db, mo, moG1, myThread)

    associate ( hasBeta => xce%hasBeta &
              , numAOs  => xce%numAOs_p &  ! number of pruned AOs
              , rrho    => self%rrho(:,:,:,mythread) &
              , drrho   => self%drrho(:,:,:,:,mythread) &
              , rtau    => self%rtau(:,:,:,mythread) &
              , indices => xce%indices_p &
      )

      call xce%compRMOs(da, mo(:,:,:,1))
      if (hasBeta) then
        call xce%compRMOs(db, mo(:,:,:,2))
      end if

      if (.not. xce%skip_p) self%mo(indices(1:numAOs),:,:,:,myThread) = mo(1:numAOs,:,:,:)

      call xce%compRRho(mo, rRho)
      if (xce%funTyp /= OQP_FUNTYP_LDA) then
        call xce%compRDRho(mo, drRho)
      end if

      if (xce%funTyp == OQP_FUNTYP_MGGA) then
        call xce%compRMOGs(da, moG1)
        call compRTau(xce, moG1, rTau, 1)
        if (xce%hasBeta) then
          call xce%compRMOGs(db, moG1)
          call compRTau(xce, moG1, rTau, 2)
        end if
      end if
    end associate

 end subroutine

!-------------------------------------------------------------------------------

!> @brief Compute Tau: (MO)' times (AO)'
!> @param[in]  xce      XC engine, parameters
!> @param[in]  moG1     MO directional derivatives
!> @param[out] rTau     kinetic energy density
!> @author Vladimir Mironov
 subroutine compRTau(xce, moG1, rTau, nSpin)

    class(xc_engine_t) :: xce
    real(kind=fp), intent(out) :: rTau(:,:,:)
    real(kind=fp), intent(in) :: moG1(:,:,:,:)
    integer :: i, j, nSpin, nMtx

    nMtx = ubound(moG1, 4)

    do j = 1, nMtx
      do i = 1, xce%numPts
        rTau(nSpin,i,j) = 0.5*sum(xce%aoG1(:,i,1:3)*moG1(:,i,1:3,j))
      end do
    end do

 end subroutine

!-------------------------------------------------------------------------------

!> @brief Add XC derivative contribution to the Kohn-Sham-like matrices
!> @param[inout] fa   Kohn-Sham matrices
!> @author Vladimir Mironov
 subroutine UUpdate(dat, xce, myThread)
    use mod_dft_gridint, only: xc_der1, xc_der2_contr
    class(xc_engine_t) :: xce
    class(xc_consumer_tde_t) :: dat
    integer :: myThread

    integer :: i, j, k
    real(kind=fp) :: cs(3)
    real(kind=fp) :: d_r(2), d_s(3), d_t(2)
    real(kind=fp) :: f_r(2), f_s(3), f_t(2)
    real(kind=fp) :: rhoab(2), tauab(2), Sigma(3), dsaa, dsab, dsba, dsbb
    real(kind=fp), pointer :: focks(:,:,:,:)
    real(kind=fp), pointer :: tmp(:,:,:)

    call dat%resetOrbPointers(xce, focks, tmp, myThread)

    associate ( aoV    => xce%aoV &
              , aoG1   => xce%aoG1 &
              , rRho   => dat%rRho(:,:,:,myThread)  &
              , drRho  => dat%drRho(:,:,:,:,myThread)  &
              , rTau   => dat%rTau(:,:,:,myThread)  &
              , dRho   => xce%xclib%dRho  &
              , numAOs => xce%numAOs_p &  ! number of pruned AOs
              , numPts => xce%numPts &
              , nMtx   => dat%nMtx &
              , xc     => xce%XCLib &
              , ids    => xce%XCLib%ids &
      )
      do j = 1, nMtx

        do i = 1, numPts

          rhoab = rRho(1:2,i,j)

          Sigma = 0
          tauab = 0

          if (xce%funTyp /= OQP_FUNTYP_LDA) then
            dsaa = dot_product(drRho(:,1,i,j), dRho(1:3,i))
            dsab = dot_product(drRho(:,1,i,j), dRho(4:6,i))
            dsbb = dot_product(drRho(:,2,i,j), dRho(4:6,i))
            dsba = dot_product(drRho(:,2,i,j), dRho(1:3,i))
            Sigma = [2*dsaa, 2*dsbb, (dsab+dsba)]
          end if

          if (xce%funTyp == OQP_FUNTYP_MGGA) tauab = rTau(1:2,i,j)

          call xc_der1(xce, .true., i, d_r, d_s, d_t)
          call xc_der2_contr(xce, .true., i, &
                  rhoab, Sigma, tauab, &
                  f_r, f_s, f_t)

          if (maxval(abs([dsaa,dsbb,dsab,dsba])) < xce%threshold) then
            f_s = 0
          end if
          if (maxval(abs(tauab)) < xce%threshold) then
            f_t = 0
          end if

          tmp(:,i,1) = 0.5_fp*f_r(1)*aoV(:,i)

          if (xce%funTyp /= OQP_FUNTYP_LDA) then
            cs =    2*f_s(1) * dRho(1:3,i) &
                  +   f_s(3) * dRho(4:6,i) &
                  + 2*d_s(1) * drRho(:,1,i,j) &
                  +   d_s(3) * drRho(:,2,i,j)
            tmp(:,i,1) = tmp(:,i,1) &
                   +   cs(X__)*aoG1(:,i,X__) &
                   +   cs(Y__)*aoG1(:,i,Y__) &
                   +   cs(Z__)*aoG1(:,i,Z__)
          end if

          if (xce%funTyp == OQP_FUNTYP_MGGA) then
            tmp(:,i,2:4) = f_t(1)*aoG1(:,i,X__:Z__)
          end if

        end do

        call dsyr2k('u', 'n', numAOs, numPts, 1.0_fp, &
                             aoV, numAOs, &
                             tmp(:,:,1), numAOs, &
                     0.0_fp, focks(:,:,j,1), numAOs)

        if (xce%funTyp == OQP_FUNTYP_MGGA) then
          do k = 1, 3
              call dsyr2k('u', 'n', numAOs, numPts, 0.25_fp, &
                                   aoG1(:,:,k), numAOs, &
                                   tmp(:,:,k+1), numAOs, &
                           1.0_fp, focks(:,:,j,1), numAOs)
          end do
        end if

        do i = 1, numPts

          rhoab = rRho(1:2,i,j)

          Sigma = 0
          tauab = 0

          if (xce%funTyp /= OQP_FUNTYP_LDA) then
            dsaa = dot_product(drRho(:,1,i,j), dRho(1:3,i))
            dsab = dot_product(drRho(:,1,i,j), dRho(4:6,i))
            dsbb = dot_product(drRho(:,2,i,j), dRho(4:6,i))
            dsba = dot_product(drRho(:,2,i,j), dRho(1:3,i))
            Sigma = [2*dsaa, 2*dsbb, (dsab+dsba)]
          end if

          if (xce%funTyp == OQP_FUNTYP_MGGA) tauab = rTau(1:2,i,j)

          call xc_der1(xce, .true., i, d_r, d_s, d_t)
          call xc_der2_contr(xce, .true., i, &
                  rhoab, Sigma, tauab, &
                  f_r, f_s, f_t)

          if (maxval(abs([dsaa,dsbb,dsab,dsba])) < xce%threshold) then
            f_s = 0
          end if
          if (maxval(abs(tauab)) < xce%threshold) then
            f_t = 0
          end if

          tmp(:,i,1) = 0.5_fp*f_r(2)*aoV(:,i)

          if (xce%funTyp /= OQP_FUNTYP_LDA) then
            cs =    2*f_s(2) * dRho(4:6,i) &
                  +   f_s(3) * dRho(1:3,i) &
                  + 2*d_s(2) * drRho(:,2,i,j) &
                  +   d_s(3) * drRho(:,1,i,j)
            tmp(:,i,1) = tmp(:,i,1) &
                   +   cs(X__)*aoG1(:,i,X__) &
                   +   cs(Y__)*aoG1(:,i,Y__) &
                   +   cs(Z__)*aoG1(:,i,Z__)
          end if

          if (xce%funTyp == OQP_FUNTYP_MGGA) then
            tmp(:,i,2:4) = f_t(2)*aoG1(:,i,X__:Z__)
          end if

        end do

        call dsyr2k('u', 'n', numAOs, numPts, 1.0_fp, &
                             aoV, numAOs, &
                             tmp(:,:,1), numAOs, &
                     0.0_fp, focks(:,:,j,2), numAOs)

        if (xce%funTyp == OQP_FUNTYP_MGGA) then
          do k = 1, 3
              call dsyr2k('u', 'n', numAOs, numPts, 0.25_fp, &
                                   aoG1(:,:,k), numAOs, &
                                   tmp(:,:,k+1), numAOs, &
                           1.0_fp, focks(:,:,j,2), numAOs)
          end do
        end if

      end do

    end associate

 end subroutine

!-------------------------------------------------------------------------------

!> @brief Add XC derivative contribution to the Kohn-Sham-like matrices
!> @param[inout] fa   Kohn-Sham matrices
!> @author Vladimir Mironov
 subroutine RUpdate(dat, xce, myThread)
    use mod_dft_gridint, only: xc_der1, xc_der2_contr
    class(xc_engine_t) :: xce
    class(xc_consumer_tde_t) :: dat
    integer :: myThread

    integer :: i, j, k
    real(kind=fp) :: c3(3)
    real(kind=fp) :: d_r(2), d_s(3), d_t(2)
    real(kind=fp) :: f_r(2), f_s(3), f_t(2)
    real(kind=fp) :: rhoab(2), tauab(2), Sigma(3)
    real(kind=fp), pointer :: focks(:,:,:,:)
    real(kind=fp), pointer :: tmp(:,:,:)

    call dat%resetOrbPointers(xce, focks, tmp, myThread)

    associate ( aoV    => xce%aoV &
              , aoG1   => xce%aoG1 &
              , rRho   => dat%rRho(:,:,:,myThread)  &
              , drRho  => dat%drRho(:,:,:,:,myThread)  &
              , rTau   => dat%rTau(:,:,:,myThread)  &
              , dRho   => xce%xclib%dRho  &
              , numAOs => xce%numAOs_p &  ! number of pruned AOs
              , numPts => xce%numPts &
              , nMtx   => dat%nMtx &
      )
      do j = 1, nMtx

        do i = 1, numPts
            rhoab = rRho(1,i,j)
            Sigma = 0
            tauab = 0
            if (xce%funTyp /= OQP_FUNTYP_LDA) Sigma = 2*sum(drRho(1:3,1,i,j)*dRho(1:3,i))
            if (xce%funTyp == OQP_FUNTYP_MGGA) tauab = rTau(1,i,j)

            call xc_der1(xce, .false., i, d_r, d_s, d_t)
            call xc_der2_contr(xce, .false., i, &
                    rhoab, Sigma, tauab, &
                    f_r, f_s, f_t)
            ! LDA
            tmp(:,i,1) = 0.5_fp*f_r(1)*aoV(:,i)
            if (xce%funTyp /= OQP_FUNTYP_LDA) then
              ! GGA
              c3 = (2*f_s(1)+f_s(3)) * dRho(1:3,i) &
                 + (2*d_s(1)+d_s(3)) * drRho(1:3,1,i,j)

              tmp(:,i,1) = tmp(:,i,1) &
                   +   c3(X__)*aoG1(:,i,X__) &
                   +   c3(Y__)*aoG1(:,i,Y__) &
                   +   c3(Z__)*aoG1(:,i,Z__)
            end if

            if (xce%funTyp == OQP_FUNTYP_MGGA) then
              ! mGGA
              tmp(:,i,2) = f_t(1)*aoG1(:,i,X__)
              tmp(:,i,3) = f_t(1)*aoG1(:,i,Y__)
              tmp(:,i,4) = f_t(1)*aoG1(:,i,Z__)
            end if
        end do

        call dsyr2k('U', 'N', numAOs, numPts, 1.0_fp, &
                             aoV, numAOs, &
                             tmp(:,:,1), numAOs, &
                     0.0_fp, focks(:,:,j,1), numAOs)

        if (xce%funTyp == OQP_FUNTYP_MGGA) then
          do k = 1, 3
            call dsyr2k('U', 'N', numAOs, numPts, 0.25_fp, &
                                 aoG1(:,:,k), numAOs, &
                                 tmp(:,:,k+1), numAOs, &
                         1.0_fp, focks(:,:,j,1), numAOs)
          end do
        end if

      end do

    end associate

 end subroutine

!-------------------------------------------------------------------------------

!> @brief Compute derivative XC contribution to the TD-DFT KS-like matrices
!> @param[in]    basis     basis set
!> @param[in]    isVecs    .true. if orbitals are provided instead of density matrix
!> @param[in]    wf        density matrix/orbitals
!> @param[inout] fx        fock-like matrices
!> @param[inout] dx        densities
!> @param[in]    nMtx      number of density/Fock-like matrices
!> @param[in]    threshold tolerance
!> @param[in]    isGGA     .TRUE. if GGA/mGGA functional used
!> @param[in]    infos     OQP metadata
!> @author Vladimir Mironov
  subroutine utddft_fxc(basis, molGrid, isVecs, &
                  wfa, wfb, &
                  fxa, fxb, &
                  dxa, dxb, &
                  nMtx, threshold, infos)
!$  use omp_lib, only: omp_get_num_threads, omp_get_thread_num
    use basis_tools, only: basis_set
    use mod_dft_gridint, only: xc_options_t, run_xc
    use types, only: information
    use mod_dft_molgrid, only: dft_grid_t
    use mathlib, only: triangular_to_full

    implicit none

    type(information), target, intent(in) :: infos
    type(dft_grid_t), target, intent(in) :: molGrid

    type(basis_set) :: basis
    logical, intent(in) :: isVecs
    integer, intent(in) :: nMtx
    real(kind=fp), intent(in) :: wfa(:,:), wfb(:,:)
    real(kind=fp), intent(inout), target :: dxa(:,:,:), dxb(:,:,:)
    real(kind=fp), intent(inout) :: fxa(:,:,:), fxb(:,:,:)
    real(kind=fp), intent(in) :: threshold

    type(xc_consumer_tde_t) :: dat
    type(xc_options_t) :: xc_opts

    integer :: i, j, nbf

    real(kind=fp), allocatable, target :: d2a(:,:), d2b(:,:)

    nbf = ubound(wfa,1)

    ! Scale w.f. by B.F. norms
    allocate(d2a(nbf,nbf), d2b(nbf,nbf))
    if (isVecs) then
      do i = 1, nbf
        d2a(:,i) = wfa(:,i) * basis%bfnrm(:)
        d2b(:,i) = wfb(:,i) * basis%bfnrm(:)
      end do
    else
      do i = 1, nbf
        d2a(:,i) = wfa(:,i) &
                * basis%bfnrm(i) &
                * basis%bfnrm(:)
        d2b(:,i) = wfb(:,i) &
                * basis%bfnrm(i) &
                * basis%bfnrm(:)
      end do
    end if

    ! Scale densities by B.F. norms
    do j = 1, nMtx
      do i = 1, nbf
        dxa(:,i,j) = dxa(:,i,j) &
                  * basis%bfnrm(i) &
                  * basis%bfnrm(:)
        dxb(:,i,j) = dxb(:,i,j) &
                  * basis%bfnrm(i) &
                  * basis%bfnrm(:)
      end do
    end do

    xc_opts%isGGA = infos%functional%needGrd
    xc_opts%needTau = infos%functional%needTau
    xc_opts%functional => infos%functional
    xc_opts%hasBeta = .true.
    xc_opts%isWFVecs = isVecs
    xc_opts%numAOs = nbf
    xc_opts%maxPts = molGrid%maxSlicePts
    xc_opts%limPts = molGrid%maxNRadTimesNAng
    xc_opts%numAtoms = infos%mol_prop%natom
    xc_opts%maxAngMom = basis%mxam
    xc_opts%nDer = 0
    xc_opts%nXCDer = 2
    xc_opts%numOccAlpha = infos%mol_prop%nelec_A
    xc_opts%numOccBeta = infos%mol_prop%nelec_B
    xc_opts%wfAlpha => d2a
    xc_opts%wfBeta  => d2b
    xc_opts%dft_threshold = threshold
    xc_opts%molGrid => molGrid

    dat%da => dxa
    dat%db => dxb
    dat%nMtx = nMtx

    call run_xc(xc_opts, dat, basis)

    deallocate (d2a, d2b)

    do j = 1, nMtx
      call triangular_to_full(dat%focks(:,:,j,1,1), nbf, 'u')
      do i = 1, nbf
        fxa(:,i,j) = fxa(:,i,j) &
                    +   dat%focks(:,i,j,1,1) &
                      * basis%bfnrm(i) &
                      * basis%bfnrm(:)
      end do
    end do

    do j = 1, nMtx
      call triangular_to_full(dat%focks(:,:,j,2,1), nbf, 'u')
      do i = 1, nbf
        fxb(:,i,j) = fxb(:,i,j) &
                    +   dat%focks(:,i,j,2,1) &
                      * basis%bfnrm(i) &
                      * basis%bfnrm(:)
      end do
    end do

    ! Scale densities back
    do j = 1, nMtx
      do i = 1, nbf
        dxa(:,i,j) = dxa(:,i,j) &
                  / basis%bfnrm(i) &
                  / basis%bfnrm(:)
        dxb(:,i,j) = dxb(:,i,j) &
                  / basis%bfnrm(i) &
                  / basis%bfnrm(:)
      end do
    end do

    call dat%clean()

  end subroutine

!-------------------------------------------------------------------------------

!> @brief Compute derivative XC contribution to the TD-DFT KS-like matrices
!> @param[in]    basis     basis set
!> @param[in]    isVecs    .true. if orbitals are provided instead of density matrix
!> @param[in]    wf        density matrix/orbitals
!> @param[inout] fx        fock-like matrices
!> @param[inout] dx        densities
!> @param[in]    nMtx      number of density/Fock-like matrices
!> @param[in]    threshold tolerance
!> @param[in]    infos     OQP metadata
!> @author Vladimir Mironov
  subroutine tddft_fxc(basis, molGrid, isVecs, wf, fx, dx, &
                       nMtx, threshold, infos)
!$  use omp_lib, only: omp_get_num_threads, omp_get_thread_num
    use basis_tools, only: basis_set
    use mod_dft_gridint, only: xc_options_t, run_xc
    use types, only: information
    use mod_dft_molgrid, only: dft_grid_t
    use mathlib, only: triangular_to_full

    implicit none

    type(information), target, intent(in) :: infos
    type(dft_grid_t), target, intent(in) :: molGrid

    type(basis_set) :: basis
    logical, intent(in) :: isVecs
    integer, intent(in) :: nMtx
    real(kind=fp), intent(in) :: wf(:,:)
    real(kind=fp), intent(inout), target :: dx(:,:,:)
    real(kind=fp), intent(inout) :: fx(:,:,:)
    real(kind=fp), intent(in) :: threshold

    type(xc_consumer_tde_t) :: dat
    type(xc_options_t) :: xc_opts

    integer :: i, j, nbf

    real(kind=fp), allocatable, target :: d2(:,:)

    nbf = ubound(wf,1)

    ! Scale w.f. by B.F. norms
    allocate(d2(nbf,nbf))
    if (isVecs) then
      do i = 1, nbf
        d2(:,i) = wf(:,i) * basis%bfnrm(:)
      end do
    else
      do i = 1, nbf
        d2(:,i) = wf(:,i) &
                * basis%bfnrm(i) &
                * basis%bfnrm(:)
      end do
    end if

    ! Scale densities by B.F. norms
    do j = 1, nMtx
      do i = 1, nbf
        dx(:,i,j) = dx(:,i,j) &
                  * basis%bfnrm(i) &
                  * basis%bfnrm(:)
      end do
    end do

    xc_opts%isGGA = infos%functional%needGrd
    xc_opts%needTau = infos%functional%needTau
    xc_opts%functional => infos%functional
    xc_opts%hasBeta = .false.
    xc_opts%isWFVecs = isVecs
    xc_opts%numAOs = nbf
    xc_opts%maxPts = molGrid%maxSlicePts
    xc_opts%limPts = molGrid%maxNRadTimesNAng
    xc_opts%numAtoms = infos%mol_prop%natom
    xc_opts%maxAngMom = basis%mxam
    xc_opts%nDer = 0
    xc_opts%nXCDer = 2
    xc_opts%numOccAlpha = infos%mol_prop%nelec_A
    xc_opts%numOccBeta = infos%mol_prop%nelec_B
    xc_opts%wfAlpha => d2
    xc_opts%dft_threshold = threshold
    xc_opts%molGrid => molGrid

    dat%da => dx
    dat%nMtx = nMtx

    call dat%pe%init(infos%mpiinfo%comm, infos%mpiinfo%usempi)

    call run_xc(xc_opts, dat, basis)

    deallocate (d2)

    do j = 1, nMtx
      call triangular_to_full(dat%focks(:,:,j,1,1), nbf, 'u')
      do i = 1, nbf
        fx(:,i,j) = fx(:,i,j) &
                    +   dat%focks(:,i,j,1,1) &
                      * basis%bfnrm(i) &
                      * basis%bfnrm(:)
      end do
    end do

    ! Scale densities back
    do j = 1, nMtx
      do i = 1, nbf
        dx(:,i,j) = dx(:,i,j) &
                  / basis%bfnrm(i) &
                  / basis%bfnrm(:)
      end do
    end do

    call dat%clean()

  end subroutine

!-------------------------------------------------------------------------------

end module mod_dft_gridint_fxc