!
! Copyright (C) 2001-2016 Quantum ESPRESSO group
! This file is distributed under the terms of the
! GNU General Public License. See the file `License'
! in the root directory of the present distribution,
! or http://www.gnu.org/copyleft/gpl.txt .
!
!----------------------------------------------------------------------------

MODULE ph_rVV10
  
  USE kinds,             ONLY : dp
  USE constants,         ONLY : pi, e2
  USE mp,                ONLY : mp_bcast, mp_sum, mp_barrier
  USE mp_pools,          ONLY : me_pool, nproc_pool, intra_pool_comm, root_pool
  USE io_global,         ONLY : ionode
  USE fft_base,          ONLY : dfftp
  USE fft_interfaces,    ONLY : fwfft, invfft 
  USE control_flags,     ONLY : iverbosity, gamma_only
  USE io_global,         ONLY : stdout
  USE rVV10,             ONLY : b_value, initialize_spline_interpolation, &
                                interpolate_kernel, q_mesh, Nr_points, Nqs, r_max
  USE gc_lr,             ONLY : grho
  

  IMPLICIT NONE
  
  !! -------------------------------------------------------------------------
  !! Rho and gradient rhos
  !! -------------------------------------------------------------------------
  real(dp), allocatable :: total_rho(:)
  real(dp), allocatable :: gradient_rho(:,:)
  complex(dp), allocatable :: gradient_drho(:,:)

  !! ------------------------------------------------------------------------- 
  real(dp), allocatable       :: q0(:), q(:)
  real(dp), allocatable       :: dq0_dq(:), d2q0_dq2(:)
  real(dp), allocatable       :: dq_dn_n(:), dn_dq_dn_n_n(:), dq_dgradn_n_gmod(:) 
  real(dp), allocatable       :: dn_dq_dgradn_n_gmod_n(:), dgradn_dq_dgradn_n_gmod_n_gmod(:)
  real(dp), allocatable, save :: d2y_dx2(:,:)
 
  real(DP), parameter :: epsr = 1.0d-6

  private  
  public :: dv_drho_rvv10

CONTAINS

!! #####################################################################################################
!!                                        |                      |
!!                                        |  dv_drho_vdw_test    |
!!                                        |______________________|


subroutine dv_drho_rvv10(rho, drho, nspin, q_point, dv_drho)


    USE gvect,               ONLY : g, ngm
    USE cell_base,           ONLY : tpiba, omega

    integer, intent(IN)        :: nspin
    real(dp), intent(IN)       :: rho(:,:), q_point(3)
    complex(DP), intent(IN)    :: drho (dfftp%nnr, nspin)
    complex(DP), intent(INOUT) :: dv_drho(dfftp%nnr, nspin)
    
    !!
    complex(dp), allocatable :: delta_v(:)
    integer  :: i_grid
    character(len=70) :: fn
 
    !!
    allocate(delta_v(dfftp%nnr))

    CALL get_delta_v(rho, drho, nspin, q_point, delta_v) 
    
    dv_drho(:,1) = delta_v(:)

    deallocate(delta_v)

end subroutine dv_drho_rvv10

!! ###############################################################################################################
!!                            |                          |
!!                            |  get_thetas_derivatives  |
!!                            |__________________________|

subroutine get_delta_v(rho, drho, nspin, q_point, delta_v)

    USE gvect,               ONLY : g, ngm
    USE cell_base,           ONLY : tpiba, omega

    integer, intent(IN) :: nspin
    real(dp),    intent(IN) :: rho(:,:), q_point(3)       !
    complex(DP), intent(IN) :: drho (dfftp%nnr, nspin)

    complex(DP), intent(OUT) :: delta_v(dfftp%nnr)

    !! Variables needed for calculations

    real(dp)    :: gmod, gmod2
    real(dp)    :: theta, dtheta_dn, dtheta_dgradn, d2theta_dn2, dn_dtheta_dgradn, dgradn_dtheta_dgradn 
    complex(dp) :: gradn_graddeltan

    !! -------------------------------------------------------------------------
    !! Terms for the delta_b part
    !! -------------------------------------------------------------------------        
    real(dp), allocatable :: b1(:,:)
    complex(dp), allocatable :: b2(:,:)    

    !! -------------------------------------------------------------------------
    !! Terms for the delta_h part
    !! -------------------------------------------------------------------------        
    complex(dp) :: h1, h1part2
    complex(dp), allocatable :: h1t(:), h2t(:)

    !! -------------------------------------------------------------------------
    !! For the interpolation
    !! -------------------------------------------------------------------------        
    integer :: q_low, q_hi, qbin
    real(dp) :: dq, a, b, c, d, e, f, temp
    
    !! -------------------------------------------------------------------------
    !! Indexes and 
    !! -------------------------------------------------------------------------        
    integer :: P_i, icar, i_grid, theta_i, i_proc, I

    !! -------------------------------------------------------------------------
    !! Delta u and delta_h
    !! -------------------------------------------------------------------------        

    complex(dp), allocatable :: u(:,:), delta_u(:,:)
    complex(dp), allocatable :: delta_h(:), delta_h1(:), delta_h2(:) , delta_h_aux(:), delta_h1_aux(:), delta_h2_aux(:)

    !! -------------------------------------------------------------------------
    !! Delta u and delta_h
    !! -------------------------------------------------------------------------        
    
    character(len=70) :: fn
    integer :: temp_unit
    
    !! -------------------------------------------------------------------------
    !! Allocations
    !! -------------------------------------------------------------------------

    !! Global variables
    allocate(total_rho(dfftp%nnr) )
    allocate(gradient_rho(3,dfftp%nnr))
    allocate(gradient_drho(3,dfftp%nnr))
    allocate(q0(dfftp%nnr), q(dfftp%nnr))    
    allocate(dq0_dq(dfftp%nnr), d2q0_dq2(dfftp%nnr))
    allocate(dq_dn_n(dfftp%nnr), dn_dq_dn_n_n(dfftp%nnr), dq_dgradn_n_gmod(dfftp%nnr))
    allocate(dn_dq_dgradn_n_gmod_n(dfftp%nnr), dgradn_dq_dgradn_n_gmod_n_gmod(dfftp%nnr))

    !! Local variables 
    allocate(b1(dfftp%nnr, Nqs), b2(dfftp%nnr, Nqs))
    allocate(u(dfftp%nnr, Nqs), delta_u(dfftp%nnr, Nqs))

    !! -------------------------------------------------------------------------
    !! Zero all values
    !! -------------------------------------------------------------------------
    theta = 0.0D0
    dtheta_dn = 0.0D0
    dtheta_dgradn = 0.0D0
    d2theta_dn2 = 0.0D0
    dn_dtheta_dgradn = 0.0D0
    dgradn_dtheta_dgradn = 0.0D0
    
    b1(:,:) = 0.0D0
    b2(:,:) = 0.0D0
    u(:,:) = (0.0D0, 0.0D0)
    delta_u(:,:) = (0.0D0, 0.0D0)

    ! Empty the output vector    
    delta_v(:) = (0.0D0, 0.0_DP)
    gradient_drho(:,:) = (0.0D0, 0.0D0)
    
    !! -------------------------------------------------------------------------
    !! Gradients
    !! -------------------------------------------------------------------------     

    total_rho(:) = rho(:,1)
    call fft_gradient_r2r( dfftp, total_rho, g, gradient_rho )

    CALL fft_qgradient (dfftp, drho(:,1), q_point, g, gradient_drho)

    call fill_q0_extended_on_grid ()

    call mp_barrier(intra_pool_comm)
    
    !! ---------------------------------------------------------------------------------------------
    !!  Initialize spline
    !! ---------------------------------------------------------------------------------------------
    
    if (.not. allocated( d2y_dx2) ) then
        allocate( d2y_dx2(Nqs, Nqs) )
        call initialize_spline_interpolation(q_mesh, d2y_dx2(:,:))
    end if

    !! --------------------------------------------------------------------------------------------- 
    !! Begin integral for the delta_b part
    !!--------------------------------------------------------------------------------------------- 
 
    do i_grid = 1,dfftp%nnr

        gmod2 = gradient_rho(1,i_grid)**2+gradient_rho(2,i_grid)**2+gradient_rho(3,i_grid)**2 
        if (total_rho(i_grid) <= epsr ) cycle
        
        CALL get_abcdef (q0, i_grid, q_hi, q_low, dq, a,b,c,d,e,f ) 
 
        do P_i = 1, Nqs
        
          CALL get_thetas_exentended( q_hi, q_low, dq, a,b,c,d,e,f, P_i, i_grid, &   ! Input
                                      gmod, gradn_graddeltan,                    &   ! Output
                                      theta, dtheta_dn, dtheta_dgradn,           &   ! Output - first derivatives
                                      d2theta_dn2, dn_dtheta_dgradn, dgradn_dtheta_dgradn, .true., total_rho) ! Output - second derivatives
          !!
          !! Terms needed later
          !!         
          b1(i_grid, P_i) = dtheta_dn 
          b2(i_grid, P_i) = d2theta_dn2*(drho(i_grid,1)/total_rho(i_grid)) + &
                          dn_dtheta_dgradn*(gradn_graddeltan/total_rho(i_grid))          
          
          !! I need complex variable
          u(i_grid, P_i) =  CMPLX(theta, 0.0D0, KIND=dp)  

          !! Here gradn_graddeltan IS complex, the cast is automatic
          delta_u(i_grid, P_i) =  dtheta_dn*drho(i_grid,1) +  dtheta_dgradn*gradn_graddeltan

        end do
        
    end do
  
    !! -------------------------------------------------------------------------
    !! Delta u part
    !! -------------------------------------------------------------------------

    CALL get_u_delta_u(u, delta_u, q_point)

    do i_grid = 1,dfftp%nnr
        do P_i = 1, Nqs
            delta_v(i_grid) = delta_v(i_grid) + &
                              delta_u(i_grid, P_i) * b1(i_grid, P_i) + &
                              u(i_grid, P_i) * b2(i_grid, P_i)
        enddo
    enddo

    call mp_barrier(intra_pool_comm)
   
    !! -------------------------------------------------------------------------
    !! Deallocate something
    !! -------------------------------------------------------------------------
 
    deallocate(b1,b2)
    
    allocate(h1t(dfftp%nnr),h2t(dfftp%nnr))
    allocate(delta_h(dfftp%nnr))
    allocate(delta_h1(dfftp%nnr))
    allocate(delta_h2(dfftp%nnr))

    !! --------------------------------------------------------------------------------------------- 
    !! Begin h
    !!--------------------------------------------------------------------------------------------- 
   
    delta_h(:) = 0.0_DP
    delta_h1(:) = 0.0_DP
    delta_h2(:) = 0.0_DP

    h1t(:) = (0.0D0, 0.0D0)
    h2t(:) = (0.0D0, 0.0D0)
 
    do i_grid = 1,dfftp%nnr

        gmod2 = gradient_rho(1,i_grid)**2+gradient_rho(2,i_grid)**2+gradient_rho(3,i_grid)**2 
        if (total_rho(i_grid) <= epsr) cycle
    
        CALL get_abcdef (q0, i_grid, q_hi, q_low, dq, a,b,c,d,e,f )
 
        do P_i = 1, Nqs
          
          CALL get_thetas_exentended( q_hi, q_low, dq, a,b,c,d,e,f, P_i, i_grid, &   ! Input
                                      gmod, gradn_graddeltan,                    &   ! Output
                                      theta, dtheta_dn, dtheta_dgradn,           &   ! Output - first derivatives
                                      d2theta_dn2, dn_dtheta_dgradn, dgradn_dtheta_dgradn, .false., total_rho) ! Output - second derivatives
          !!
          !! Terms nedded later
          !!
          h1part2 = dn_dtheta_dgradn*(drho(i_grid,1)/total_rho(i_grid)) + dgradn_dtheta_dgradn*(gradn_graddeltan/total_rho(i_grid))
          
          h1t(i_grid) = h1t(i_grid) + delta_u(i_grid,P_i)*dtheta_dgradn + u(i_grid,P_i)*h1part2
          h2t(i_grid) = h2t(i_grid) + u(i_grid,P_i)*dtheta_dgradn
        
        end do

    end do

    allocate (delta_h_aux(dfftp%nnr))
    allocate (delta_h1_aux(dfftp%nnr))
    allocate (delta_h2_aux(dfftp%nnr))

    do icar = 1,3
       delta_h(:) = (h1t(:) * gradient_rho(icar,:)+ h2t(:) * gradient_drho(icar,:))

       CALL fwfft ('Rho', delta_h, dfftp) 

       delta_h_aux(:) = (0.0_DP, 0.0_DP)
       delta_h_aux(dfftp%nl(:)) = CMPLX(0.0_DP,(g(icar,:)+q_point(icar)),kind=DP ) * delta_h(dfftp%nl(:))
       
       if (gamma_only) delta_h_aux(dfftp%nlm(:)) = CONJG(delta_h_aux(dfftp%nl(:)))

       CALL invfft ('Rho', delta_h_aux, dfftp) 

       delta_h_aux(:) = delta_h_aux(:)*tpiba

       delta_v(:) = delta_v(:) - delta_h_aux(:)


    end do

    !! -------------------------------------------------------------------------
    !! Deallocate everything
    !! -------------------------------------------------------------------------

    call mp_barrier(intra_pool_comm)

    call mp_barrier(intra_pool_comm)

    deallocate(total_rho, gradient_drho)
    deallocate(gradient_rho)
    deallocate(q0, q, dq0_dq, d2q0_dq2)
    deallocate(dq_dn_n, dn_dq_dn_n_n, dq_dgradn_n_gmod)
    deallocate(dn_dq_dgradn_n_gmod_n, dgradn_dq_dgradn_n_gmod_n_gmod)
    deallocate(h1t, h2t)
    deallocate(delta_h_aux, delta_h)
    deallocate(delta_h1_aux, delta_h1)
    deallocate(delta_h2_aux, delta_h2)
    deallocate(u, delta_u)
 
end subroutine get_delta_v

  !! ###############################################################################################################
  !!                                    |                  |
  !!                                    |    get_abcdef    |
  !!                                    |                  |
  !! ###############################################################################################################

  SUBROUTINE get_abcdef (q0, i_grid, q_hi, q_low, dq, a,b,c,d,e,f )

    real(dp),  intent(IN)    :: q0(:)
    integer, INTENT(IN)      :: i_grid
    integer, INTENT(OUT)     :: q_hi, q_low
    real(dp),  intent(OUT)   :: a,b,c,d,e,f, dq

    integer  :: qbin

    q_low = 1
    q_hi = Nqs 

    do while ( (q_hi - q_low) > 1)

       qbin = int((q_hi + q_low)/2)
       if (q_mesh(qbin) > q0(i_grid)) then
           q_hi = qbin
       else
           q_low = qbin
       end if

    end do

    if (q_hi == q_low) call errore('get_potential','qhi == qlow',1)

    ! ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    dq = q_mesh(q_hi) - q_mesh(q_low)

    a = (q_mesh(q_hi) - q0(i_grid))/dq
    b = (q0(i_grid) - q_mesh(q_low))/dq
    c = (a**3 - a)*dq**2/6.0D0
    d = (b**3 - b)*dq**2/6.0D0
    e = (3.0D0*a**2 - 1.0D0)*dq/6.0D0
    f = (3.0D0*b**2 - 1.0D0)*dq/6.0D0

  END SUBROUTINE get_abcdef

  SUBROUTINE get_thetas_exentended (q_hi, q_low, dq, a,b,c,d,e,f, P_i, i_grid, &
                                    gmod, gradn_graddeltan, &
                                    theta, dtheta_dn, dtheta_dgradn, d2theta_dn2, &
                                    dn_dtheta_dgradn, dgradn_dtheta_dgradn, do_write, total_rho)
  
    integer,  intent(IN)  :: q_low, q_hi, P_i, i_grid
    real(dp),  intent(IN) :: dq, a, b, c, d, e, f
    real(dp),  intent(IN) :: total_rho(dfftp%nnr) 
    logical :: do_write

    real(dp), INTENT(OUT)    :: gmod, theta, dtheta_dn, dtheta_dgradn, d2theta_dn2, dn_dtheta_dgradn, dgradn_dtheta_dgradn
    complex(dp), INTENT(OUT) :: gradn_graddeltan

    real(dp) :: y(Nqs), d2P_dq02, dP_dq0, P, const, rho_34, rho_m14
    character(len=70) :: fn

    y = 0.0D0
    y(P_i) = 1.0D0

    !!
    !! P_alpha and derivatives | Num. Recip. Fortran 2nd Ed. p.108
    !!
    d2P_dq02 = a*d2y_dx2(P_i,q_low) + b*d2y_dx2(P_i,q_hi)
    dP_dq0 = (y(q_hi) - y(q_low))/dq - e*d2y_dx2(P_i,q_low) + f*d2y_dx2(P_i,q_hi)
    P = a*y(q_low) + b*y(q_hi) + c*d2y_dx2(P_i,q_low) + d*d2y_dx2(P_i,q_hi)
          
    !!
    !! Thetas
    !!
    const = 1.0D0 / (3.0D0 * b_value**(3.0D0/2.0D0) * pi**(5.0D0/4.0D0) )
    rho_34 = total_rho(i_grid)**(3.0D0/4.0D0)
    rho_m14 = total_rho(i_grid)**(-1.0D0/4.0D0)

    theta     = const*rho_34*P 
 
    dtheta_dn = const*rho_m14*((3.0D0/4.0D0)*P + dP_dq0*dq0_dq(i_grid)*dq_dn_n(i_grid) )
    
    dtheta_dgradn = const*rho_m14*dP_dq0*dq0_dq(i_grid)*dq_dgradn_n_gmod(i_grid)

    d2theta_dn2 = const*rho_m14*( &
                  -(3.0D0/16.0D0)*P &
                  +(1.0D0/2.0D0)*dP_dq0*dq0_dq(i_grid)*dq_dn_n(i_grid) + &
                  d2P_dq02*(dq0_dq(i_grid)**2)*(dq_dn_n(i_grid)**2) + &
                  dP_dq0*d2q0_dq2(i_grid)*(dq_dn_n(i_grid)**2) + &
                  dP_dq0*dq0_dq(i_grid)*dn_dq_dn_n_n(i_grid) )

    dn_dtheta_dgradn = const*rho_m14*( &
                       -(1.0D0/4.0D0)*dP_dq0*dq0_dq(i_grid)*dq_dgradn_n_gmod(i_grid) + &
                       d2P_dq02*(dq0_dq(i_grid)**2)*dq_dn_n(i_grid)*dq_dgradn_n_gmod(i_grid) + &
                       dP_dq0*d2q0_dq2(i_grid)*dq_dn_n(i_grid)*dq_dgradn_n_gmod(i_grid) + &
                       dP_dq0*dq0_dq(i_grid)*dn_dq_dgradn_n_gmod_n(i_grid) )

    dgradn_dtheta_dgradn = const*rho_m14*( &
                           d2P_dq02*(dq0_dq(i_grid)**2)*(dq_dgradn_n_gmod(i_grid)**2) + &
                           dP_dq0*d2q0_dq2(i_grid)*(dq_dgradn_n_gmod(i_grid)**2) + &
                           dP_dq0*dq0_dq(i_grid)*dgradn_dq_dgradn_n_gmod_n_gmod(i_grid))
    !!
    !! Fractions
    !!
    gmod = sqrt(gradient_rho(1,i_grid)**2+gradient_rho(2,i_grid)**2+gradient_rho(3,i_grid)**2)
    gradn_graddeltan = gradient_rho(1,i_grid)*gradient_drho(1,i_grid) + &
                       gradient_rho(2,i_grid)*gradient_drho(2,i_grid) + &
                       gradient_rho(3,i_grid)*gradient_drho(3,i_grid)

  END SUBROUTINE get_thetas_exentended


!! ###############################################################################################################
  !!                                    |                  |
  !!                                    |  GET_Q0_ON_GRID  |
  !!                                    |__________________|

  !! This routine first calculates the q value defined in (DION equations 11 and 12), then
  !! saturates it according to (SOLER equation 7).  
  
  SUBROUTINE fill_q0_extended_on_grid ()
  !!
  !! more specifically it calcultates the following
  !!
  !!     q0(ir) = q0 as defined above
  !!     dq0_dq(ir) =  d q0 /d q
  !!     dq_drho(ir) = total_rho * d q /d rho 
  !!     dq_dgradrho = total_rho / |gradient_rho| * d q / d |gradient_rho|
  !!
    
  USE rVV10,           ONLY : q_cut, q_min
    
  !                                                                  
  !                                                                        _
  real(dp),   parameter      :: LDA_A  = 0.031091D0, LDA_a1 = 0.2137D0    !
  real(dp),   parameter      :: LDA_b1 = 7.5957D0  , LDA_b2 = 3.5876D0    ! see J.P. Perdew and Yue Wang, Phys. Rev. B 45, 13244 (1992). 
  real(dp),   parameter      :: LDA_b3 = 1.6382D0  , LDA_b4 = 0.49294D0   !_ 
  real(dp)                   :: Z_ab = -0.8491D0                          !! see DION

  integer,    parameter      :: m_cut = 12                                !! How many terms to include in the sum
  real,    parameter         :: C_value = 0.0093                                !! How many terms to include in the sum
  !                                                                       !! of SOLER equation 7
  
  
  real(dp)                   :: kF, r_s, sqrt_r_s, gc                     !! Intermediate variables needed to get q and q0
  real(dp)                   :: LDA_1, LDA_2, exponent, gmod, gmod2       !!
 
  real(dp)                   :: expTemp1, expTemp2
  real(dp)                   :: k, wp2, wg2, w02, w0, alpha
  
  !                                                                       !! Needed by dq0_drho and dq0_dgradrho by the chain rule.
  
  integer                    :: i_grid, index, count=0                    !! Indexing variables
  character(len=70) :: fn

  ! initialize q0-related arrays ... 
  q0(:) = q_cut
  q = 0.0_DP 
  
  dq0_dq(:)           = 0.0_DP ! 
  d2q0_dq2(:)         = 0.0_DP
  dq_dn_n(:)          = 0.0_DP ! total_rho * d q/d rho
  dn_dq_dn_n_n(:)     = 0.0_DP 
  dq_dgradn_n_gmod(:) = 0.0_DP ! total_rho / |gradient_rho| * d q / d |gradient_rho|
  dn_dq_dgradn_n_gmod_n(:) = 0.0_DP ! 
  dgradn_dq_dgradn_n_gmod_n_gmod(:) = 0.0_DP ! 
  
  do i_grid = 1, dfftp%nnr
          
     !! This prevents numerical problems.  If the charge density is negative (an
     !! unphysical situation), we simply treat it as very small.  In that case,
     !! q0 will be very large and will be saturated.  For a saturated q0 the derivative
     !! dq0_dq will be 0 so we set q0 = q_cut and dq0_drho = dq0_dgradrho = 0 and go on
     !! to the next point.
     !! ------------------------------------------------------------------------------------
     
     if (total_rho(i_grid) < epsr)cycle

     gmod2 = gradient_rho(1,i_grid)**2+gradient_rho(2,i_grid)**2+gradient_rho(3,i_grid)**2
     gmod = sqrt(gmod2)


     !! ------------------------------------------------------------------------------------
     
     !! Calculate some intermediate values needed to find q
     !! ------------------------------------------------------------------------------------

     wp2 = 16.0D0*pi*total_rho(i_grid)
     wg2 = 4.0D0*C_value* (gmod/total_rho(i_grid))**4.0D0
     w02 = wg2 + wp2/3.0D0 
     w0 = sqrt( w02 )
     alpha = wg2/w02

     k = b_value*3.0D0* pi* ((total_rho(i_grid)/(9.0D0*pi))**(1.0D0/6.0D0))

     !! ---------------------------------------------------------------
     
     q(i_grid) = w0 / k 
     
     !! ---------------------------------------------------------------
     
     !! Here, we calculate q0 by saturating q according to equation 7 of SOLER.  Also, we find 
     !! the derivative dq0_dq needed for the derivatives dq0_drho and dq0_dgradrh0 discussed below.
     !! ---------------------------------------------------------------------------------------

     exponent = 0.0D0
     dq0_dq(i_grid) = 0.0D0
     expTemp1 = 0.0D0
     expTemp2 = 0.0D0

     do index = 1, m_cut
        
        exponent = exponent + ( (q(i_grid)/q_cut)**index)/index
        dq0_dq(i_grid) = dq0_dq(i_grid) + ( (q(i_grid)/q_cut)**(index-1))
        
        expTemp1 = expTemp1 + ( (q(i_grid)/q_cut)**(index-1))
        expTemp2 = expTemp2 + ( ((index-1)/q_cut)*(q(i_grid)/q_cut)**(index-2))
        
     end do
     

     q0(i_grid) = q_cut*(1.0D0 - exp(-exponent))
     dq0_dq(i_grid) = dq0_dq(i_grid) * exp(-exponent)
     d2q0_dq2(i_grid) = expTemp2*exp(-exponent) - (expTemp1**2)*(1.0D0/q_cut)*exp(-exponent)
   
     
     !! ---------------------------------------------------------------------------------------
     
     !! This is to handle a case with q0 too small.  We simply set it to the smallest q value in
     !! out q_mesh.  Hopefully this doesn't get used often (ever)
     !! ---------------------------------------------------------------------------------------

     if (q0(i_grid) < q_min) then
        
        q0(i_grid) = q_min
        
     end if

     !! ---------------------------------------------------------------------------------------
     
     !! Here we find derivatives.  These are actually the density times the derivative of q0 with respect
     !! to rho and gradient_rho.  The density factor comes in since we are really differentiating
     !! theta = (rho)*P(q0) with respect to density (or its gradient) which will be
     !! dtheta_drho = P(q0) + dP_dq0 * [rho * dq0_dq * dq_drho]   and
     !! dtheta_dgradient_rho =  dP_dq0  * [rho * dq0_dq * dq_dgradient_rho]
     !! The parts in square brackets are what is calculated here.  The dP_dq0 term will be interpolated
     !! later.  There should actually be a factor of the magnitude of the gradient in the gradient_rho derivative
     !! but that cancels out when we differentiate the magnitude of the gradient with respect to a particular
     !! component.
     !! -------------------------------------------------------------------------------------------------------------------------
  
     dq_dn_n(i_grid)        = q(i_grid)*(1.0D0/3.0D0 -2.5D0 * alpha)
     
     dn_dq_dn_n_n(i_grid)   = q(i_grid) * (1.0D0/9.0D0 + 65.0D0/6.0D0*alpha - 6.25D0*alpha**2.0D0)
 
     dq_dgradn_n_gmod(i_grid) = 8.0D0*q(i_grid)*C_value*(gmod**2.0D0/total_rho(i_grid)**3.0D0)/w02

     dn_dq_dgradn_n_gmod_n(i_grid) = dq_dgradn_n_gmod(i_grid)*(2.5D0*alpha-11.0D0/3.0D0)

     dgradn_dq_dgradn_n_gmod_n_gmod(i_grid) = 16.0D0*q(i_grid)*C_value/total_rho(i_grid)**2.0D0/w02*(1.0D0-alpha) 

  end do

end SUBROUTINE fill_q0_extended_on_grid

!! #####################################################################################################
!!                                        |           |
!!                                        |  delta_u  |
!!                                        |___________|

subroutine get_u_delta_u(u, delta_u, q_point)
  
  USE gvect,           ONLY : g, gg, ngm, igtongl, gl, ngl, gstart
  USE cell_base,       ONLY : tpiba, omega

  complex(dp), intent(inout)  :: u(dfftp%nnr,Nqs), delta_u(dfftp%nnr,Nqs)
  real(dp), intent(in)   :: q_point(3)
  
  !!
  !! Valirables
  !!
  real(dp), allocatable :: kernel_of_g(:,:), kernel_of_gq(:,:)   
  complex(dp), allocatable :: temp_u(:,:), temp_delta_u(:,:)
  real(dp) :: gmod, gqmod
  integer :: last_g, g_i, q1_i, q2_i, count, i_grid, final_g    !! Index variables
  
  !! -------------------------------------------------------------------------------------------------
  !! Allocate variables
  !!  
  allocate( kernel_of_g(Nqs, Nqs), kernel_of_gq(Nqs, Nqs) )
  allocate( temp_u(dfftp%nnr, Nqs), temp_delta_u(dfftp%nnr, Nqs) )

  temp_u(:,:) = (0.0D0, 0.0D0)
  temp_delta_u(:,:) = (0.0D0, 0.0D0)
  !!
  !! Get argument in reciprocal space
  !!
  call start_clock( 'vdW_ffts')
  do q1_i = 1, Nqs
     CALL fwfft ('Rho', u(:,q1_i), dfftp)
     CALL fwfft ('Rho', delta_u(:,q1_i), dfftp)
  end do
  call stop_clock( 'vdW_ffts')
  
  !!
  !! Integrate in reciprocal space
  !!
  last_g = -1 

  do g_i = 1, ngm
    
     if ( igtongl(g_i) .ne. last_g) then
        gmod = sqrt(gl(igtongl(g_i))) * tpiba
        call interpolate_kernel(gmod, kernel_of_g)
        last_g = igtongl(g_i)

     end if
    
     gqmod = sqrt( (g(1,g_i)+q_point(1))**2 + (g(2,g_i)+q_point(2))**2 + (g(3,g_i)+q_point(3))**2 )*tpiba
     call interpolate_kernel(gqmod, kernel_of_gq)
 
     !! Loop over alpha
     do q2_i = 1, Nqs
        !! Sum over beta
        do q1_i = 1, Nqs
        
           temp_u(dfftp%nl(g_i), q2_i) = temp_u(dfftp%nl(g_i), q2_i) + kernel_of_g(q2_i,q1_i)*u(dfftp%nl(g_i), q1_i)
       
           temp_delta_u(dfftp%nl(g_i), q2_i) = temp_delta_u(dfftp%nl(g_i), q2_i) + &
                                        kernel_of_gq(q2_i,q1_i)*delta_u(dfftp%nl(g_i), q1_i)
        end do
     end do

  end do

  if (gamma_only) then
    temp_u(dfftp%nlm(:),:) = CONJG(temp_u(dfftp%nl(:),:))
    temp_delta_u(dfftp%nlm(:),:) = CONJG(temp_delta_u(dfftp%nl(:),:))
  endif

  !!
  !! Put everything in real space
  !!
  call start_clock( 'vdW_ffts')
  do q1_i = 1, Nqs
     CALL invfft ('Rho', temp_u(:,q1_i), dfftp)
     CALL invfft ('Rho', temp_delta_u(:,q1_i), dfftp)
  end do
  call stop_clock( 'vdW_ffts')

  u(:,:) = temp_u(:,:)
  delta_u(:,:) = temp_delta_u(:,:)
    
  deallocate(temp_u, temp_delta_u, kernel_of_g, kernel_of_gq)
     
  !! -----------------------------------------------------------------------------------------------
  
end subroutine get_u_delta_u

   ! ####################################################################
   !                          |              |
   !                          | thetas_to_uk |
END MODULE ph_rVV10 

