LCOV - code coverage report
Current view: top level - src - qs_dispersion_nonloc.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:6d276e9) Lines: 94.8 % 305 289
Test Date: 2026-09-10 07:29:18 Functions: 93.3 % 15 14

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief Calculation of non local dispersion functionals
      10              : !>  Some routines adapted from:
      11              : !>  Copyright (C) 2001-2009 Quantum ESPRESSO group
      12              : !>  Copyright (C) 2009 Brian Kolb, Timo Thonhauser - Wake Forest University
      13              : !>  This file is distributed under the terms of the
      14              : !>  GNU General Public License. See the file `License'
      15              : !>  in the root directory of the present distribution,
      16              : !>  or http://www.gnu.org/copyleft/gpl.txt .
      17              : !> \author JGH
      18              : ! **************************************************************************************************
      19              : MODULE qs_dispersion_nonloc
      20              :    USE bibliography,                    ONLY: Dion2004,&
      21              :                                               Romanperez2009,&
      22              :                                               Sabatini2013,&
      23              :                                               cite_reference
      24              :    USE cp_files,                        ONLY: close_file,&
      25              :                                               open_file
      26              :    USE input_constants,                 ONLY: vdw_nl_DRSLL,&
      27              :                                               vdw_nl_LMKLL,&
      28              :                                               vdw_nl_RVV10,&
      29              :                                               xc_vdw_fun_nonloc
      30              :    USE kinds,                           ONLY: default_path_length,&
      31              :                                               dp
      32              :    USE mathconstants,                   ONLY: pi,&
      33              :                                               rootpi
      34              :    USE message_passing,                 ONLY: mp_para_env_type
      35              :    USE pw_grid_types,                   ONLY: HALFSPACE,&
      36              :                                               pw_grid_type
      37              :    USE pw_methods,                      ONLY: pw_axpy,&
      38              :                                               pw_derive,&
      39              :                                               pw_transfer
      40              :    USE pw_pool_types,                   ONLY: pw_pool_type
      41              :    USE pw_types,                        ONLY: pw_c1d_gs_type,&
      42              :                                               pw_r3d_rs_type
      43              :    USE qs_dispersion_types,             ONLY: qs_dispersion_type
      44              :    USE virial_types,                    ONLY: virial_type
      45              : #include "./base/base_uses.f90"
      46              : 
      47              :    IMPLICIT NONE
      48              : 
      49              :    PRIVATE
      50              : 
      51              :    REAL(KIND=dp), PARAMETER :: epsr = 1.e-12_dp
      52              : 
      53              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_dispersion_nonloc'
      54              : 
      55              :    PUBLIC :: qs_dispersion_nonloc_init, calculate_dispersion_nonloc
      56              : 
      57              : ! **************************************************************************************************
      58              : 
      59              : CONTAINS
      60              : 
      61              : ! **************************************************************************************************
      62              : !> \brief ...
      63              : !> \param dispersion_env ...
      64              : !> \param para_env ...
      65              : ! **************************************************************************************************
      66           50 :    SUBROUTINE qs_dispersion_nonloc_init(dispersion_env, para_env)
      67              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
      68              :       TYPE(mp_para_env_type), POINTER                    :: para_env
      69              : 
      70              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_dispersion_nonloc_init'
      71              : 
      72              :       CHARACTER(LEN=default_path_length)                 :: filename
      73              :       INTEGER                                            :: funit, handle, ipair, itable, nqs, &
      74              :                                                             nr_points, vdw_type
      75              : 
      76           50 :       CALL timeset(routineN, handle)
      77              : 
      78           50 :       SELECT CASE (dispersion_env%nl_type)
      79              :       CASE DEFAULT
      80            0 :          CPABORT("Unknown vdW-DF functional")
      81              :       CASE (vdw_nl_DRSLL, vdw_nl_LMKLL)
      82           34 :          CALL cite_reference(Dion2004)
      83              :       CASE (vdw_nl_RVV10)
      84           50 :          CALL cite_reference(Sabatini2013)
      85              :       END SELECT
      86           50 :       CALL cite_reference(RomanPerez2009)
      87              : 
      88           50 :       vdw_type = dispersion_env%type
      89           50 :       SELECT CASE (vdw_type)
      90              :       CASE DEFAULT
      91              :          ! do nothing
      92              :       CASE (xc_vdw_fun_nonloc)
      93              :          ! setup information on non local functionals
      94           50 :          filename = dispersion_env%kernel_file_name
      95           50 :          IF (para_env%is_source()) THEN
      96              :             ! Read the kernel information from file "filename"
      97           25 :             CALL open_file(file_name=filename, unit_number=funit, file_form="FORMATTED")
      98           25 :             READ (funit, *) nqs, nr_points
      99           25 :             READ (funit, *) dispersion_env%r_max
     100              :          END IF
     101           50 :          CALL para_env%bcast(nqs)
     102           50 :          CALL para_env%bcast(nr_points)
     103           50 :          CALL para_env%bcast(dispersion_env%r_max)
     104          350 :          ALLOCATE (dispersion_env%q_mesh(nqs), dispersion_env%kernel_table(nqs*(nqs + 1)/2, 0:nr_points, 2))
     105           50 :          dispersion_env%nqs = nqs
     106           50 :          dispersion_env%nr_points = nr_points
     107           50 :          IF (para_env%is_source()) THEN
     108              :             !! Read in the values of the q points used to generate this kernel
     109          525 :             READ (funit, "(1p, 4e23.14)") dispersion_env%q_mesh
     110              :             ! The file stores kernel values followed by second derivatives, both in
     111              :             ! lower-triangular pair order. Keep this immutable table in packed form.
     112           75 :             DO itable = 1, 2
     113        10575 :                DO ipair = 1, nqs*(nqs + 1)/2
     114     10773050 :                   READ (funit, "(1p, 4e23.14)") dispersion_env%kernel_table(ipair, 0:nr_points, itable)
     115              :                END DO
     116              :             END DO
     117           25 :             CALL close_file(unit_number=funit)
     118              :          END IF
     119         2050 :          CALL para_env%bcast(dispersion_env%q_mesh)
     120     43255250 :          CALL para_env%bcast(dispersion_env%kernel_table)
     121              :          ! 2nd derivates for interpolation
     122          200 :          ALLOCATE (dispersion_env%d2y_dx2(nqs, nqs))
     123           50 :          CALL initialize_spline_interpolation(dispersion_env%q_mesh, dispersion_env%d2y_dx2)
     124              :          !
     125           50 :          dispersion_env%q_cut = dispersion_env%q_mesh(nqs)
     126           50 :          dispersion_env%q_min = dispersion_env%q_mesh(1)
     127          100 :          dispersion_env%dk = 2.0_dp*pi/dispersion_env%r_max
     128              : 
     129              :       END SELECT
     130              : 
     131           50 :       CALL timestop(handle)
     132              : 
     133           50 :    END SUBROUTINE qs_dispersion_nonloc_init
     134              : 
     135              : ! **************************************************************************************************
     136              : !> \brief Calculates the non-local vdW functional using the method of Soler
     137              : !>        For spin polarized cases we use E(a,b) = E(a+b), i.e. total density
     138              : !> \param vxc_rho ...
     139              : !> \param rho_r ...
     140              : !> \param rho_g ...
     141              : !> \param edispersion ...
     142              : !> \param dispersion_env ...
     143              : !> \param energy_only ...
     144              : !> \param pw_pool ...
     145              : !> \param xc_pw_pool ...
     146              : !> \param para_env ...
     147              : !> \param virial ...
     148              : ! **************************************************************************************************
     149          422 :    SUBROUTINE calculate_dispersion_nonloc(vxc_rho, rho_r, rho_g, edispersion, &
     150              :                                           dispersion_env, energy_only, pw_pool, xc_pw_pool, para_env, virial)
     151              :       TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER        :: vxc_rho, rho_r
     152              :       TYPE(pw_c1d_gs_type), DIMENSION(:), POINTER        :: rho_g
     153              :       REAL(KIND=dp), INTENT(OUT)                         :: edispersion
     154              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
     155              :       LOGICAL, INTENT(IN)                                :: energy_only
     156              :       TYPE(pw_pool_type), POINTER                        :: pw_pool, xc_pw_pool
     157              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     158              :       TYPE(virial_type), OPTIONAL, POINTER               :: virial
     159              : 
     160              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'calculate_dispersion_nonloc'
     161              :       INTEGER, DIMENSION(3, 3), PARAMETER :: nd = RESHAPE([1, 0, 0, 0, 1, 0, 0, 0, 1], [3, 3])
     162              : 
     163              :       INTEGER                                            :: handle, handle_fft, i, i_grid, idir, &
     164              :                                                             ispin, nl_type, np, nspin, p, q, r, s
     165          422 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: q_low
     166              :       INTEGER, DIMENSION(1:3)                            :: hi, lo, n
     167              :       LOGICAL                                            :: use_virial
     168              :       REAL(KIND=dp)                                      :: b_value, beta, Ec_nl, sumnp
     169          422 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: dq0_dgradrho, dq0_drho, hpot, q0, rho, &
     170          422 :                                                             theta_scale
     171          422 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: drho, spline_coeff, u_contract
     172          422 :       REAL(KIND=dp), CONTIGUOUS, POINTER                 :: tmp_1d(:), vxc_1d(:)
     173              :       TYPE(pw_c1d_gs_type)                               :: div_g, rho_tot_g, tmp_g, vxc_g
     174          422 :       TYPE(pw_c1d_gs_type), ALLOCATABLE, DIMENSION(:)    :: thetas_g
     175              :       TYPE(pw_grid_type), POINTER                        :: grid
     176              :       TYPE(pw_r3d_rs_type)                               :: tmp_r, vxc_r
     177              : 
     178          422 :       CALL timeset(routineN, handle)
     179              : 
     180          422 :       CPASSERT(ASSOCIATED(rho_r))
     181          422 :       CPASSERT(ASSOCIATED(rho_g))
     182          422 :       CPASSERT(ASSOCIATED(pw_pool))
     183              : 
     184          422 :       IF (PRESENT(virial)) THEN
     185          416 :          use_virial = virial%pv_calculate .AND. (.NOT. virial%pv_numer)
     186              :       ELSE
     187              :          use_virial = .FALSE.
     188              :       END IF
     189              :       IF (use_virial) THEN
     190          112 :          CPASSERT(.NOT. energy_only)
     191              :       END IF
     192          422 :       IF (.NOT. energy_only) THEN
     193          416 :          CPASSERT(ASSOCIATED(vxc_rho))
     194              :       END IF
     195              : 
     196          422 :       nl_type = dispersion_env%nl_type
     197              : 
     198          422 :       b_value = dispersion_env%b_value
     199          422 :       beta = 0.03125_dp*(3.0_dp/(b_value**2.0_dp))**0.75_dp
     200          422 :       nspin = SIZE(rho_r)
     201              : 
     202              :       ! temporary arrays for FFT
     203          422 :       CALL pw_pool%create_pw(tmp_g)
     204          422 :       CALL pw_pool%create_pw(tmp_r)
     205              : 
     206              :       ! Sum the spin densities on the vdW grid before transforming or differentiating.
     207          422 :       CALL pw_pool%create_pw(rho_tot_g)
     208          422 :       CALL pw_transfer(rho_g(1), rho_tot_g)
     209          442 :       DO ispin = 2, nspin
     210           20 :          CALL pw_transfer(rho_g(ispin), tmp_g)
     211          442 :          CALL pw_axpy(tmp_g, rho_tot_g, 1._dp)
     212              :       END DO
     213          422 :       CALL pw_transfer(rho_tot_g, tmp_r)
     214              : 
     215         1688 :       np = SIZE(tmp_r%array)
     216          422 :       tmp_1d(1:np) => tmp_r%array
     217         2110 :       ALLOCATE (rho(np), drho(np, 3))
     218         1688 :       DO i = 1, 3
     219         1266 :          lo(i) = LBOUND(tmp_r%array, i)
     220         1266 :          hi(i) = UBOUND(tmp_r%array, i)
     221         1688 :          n(i) = hi(i) - lo(i) + 1
     222              :       END DO
     223              : !$OMP PARALLEL DO DEFAULT(NONE) &
     224          422 : !$OMP             SHARED(n, lo, rho, tmp_r) PRIVATE(s) COLLAPSE(3)
     225              :       DO r = 0, n(3) - 1
     226              :          DO q = 0, n(2) - 1
     227              :             DO p = 0, n(1) - 1
     228              :                s = r*n(2)*n(1) + q*n(1) + p + 1
     229              :                rho(s) = tmp_r%array(p + lo(1), q + lo(2), r + lo(3))
     230              :             END DO
     231              :          END DO
     232              :       END DO
     233              : !$OMP END PARALLEL DO
     234         1688 :       DO idir = 1, 3
     235         1266 :          CALL pw_transfer(rho_tot_g, tmp_g)
     236         1266 :          CALL pw_derive(tmp_g, nd(:, idir))
     237         1266 :          CALL pw_transfer(tmp_g, tmp_r)
     238              : !$OMP PARALLEL DO DEFAULT(NONE) &
     239         1688 : !$OMP             SHARED(idir, n, lo, drho, tmp_r) PRIVATE(s) COLLAPSE(3)
     240              :          DO r = 0, n(3) - 1
     241              :             DO q = 0, n(2) - 1
     242              :                DO p = 0, n(1) - 1
     243              :                   s = r*n(2)*n(1) + q*n(1) + p + 1
     244              :                   drho(s, idir) = tmp_r%array(p + lo(1), q + lo(2), r + lo(3))
     245              :                END DO
     246              :             END DO
     247              :          END DO
     248              : !$OMP END PARALLEL DO
     249              :       END DO
     250          422 :       CALL pw_pool%give_back_pw(rho_tot_g)
     251              : 
     252              :       !! ---------------------------------------------------------------------------------
     253              :       !! Find the value of q0 for all assigned grid points.  q is defined in equations
     254              :       !! 11 and 12 of DION and q0 is the saturated version of q defined in equation
     255              :       !! 5 of SOLER.  This routine also returns the derivatives of the q0s with respect
     256              :       !! to the charge-density and the gradient of the charge-density.  These are needed
     257              :       !! for the potential calculated below.
     258              :       !! ---------------------------------------------------------------------------------
     259              : 
     260          422 :       IF (energy_only) THEN
     261           12 :          ALLOCATE (q0(np))
     262            0 :          SELECT CASE (nl_type)
     263              :          CASE DEFAULT
     264            0 :             CPABORT("Unknown vdW-DF functional")
     265              :          CASE (vdw_nl_DRSLL, vdw_nl_LMKLL)
     266            0 :             CALL get_q0_on_grid_eo_vdw(rho, drho, q0, dispersion_env)
     267              :          CASE (vdw_nl_RVV10)
     268            6 :             CALL get_q0_on_grid_eo_rvv10(rho, drho, q0, dispersion_env)
     269              :          END SELECT
     270              :       ELSE
     271         1664 :          ALLOCATE (q0(np), dq0_drho(np), dq0_dgradrho(np))
     272            0 :          SELECT CASE (nl_type)
     273              :          CASE DEFAULT
     274            0 :             CPABORT("Unknown vdW-DF functional")
     275              :          CASE (vdw_nl_DRSLL, vdw_nl_LMKLL)
     276          246 :             CALL get_q0_on_grid_vdw(rho, drho, q0, dq0_drho, dq0_dgradrho, dispersion_env)
     277              :          CASE (vdw_nl_RVV10)
     278          416 :             CALL get_q0_on_grid_rvv10(rho, drho, q0, dq0_drho, dq0_dgradrho, dispersion_env)
     279              :          END SELECT
     280              :       END IF
     281              : 
     282              :       ! Generate one theta channel directly in the FFT workspace. Only reciprocal-space
     283              :       ! channels must coexist for the convolution; no real-space np-by-nqs array is needed.
     284         2532 :       ALLOCATE (q_low(np), spline_coeff(np, 4), theta_scale(np))
     285          422 :       CALL prepare_splines(q0, rho, dispersion_env, q_low, spline_coeff, theta_scale)
     286         9706 :       ALLOCATE (thetas_g(dispersion_env%nqs))
     287          422 :       CALL timeset("vdW_theta_forward", handle_fft)
     288         8862 :       DO i = 1, dispersion_env%nqs
     289         8440 :          CALL build_theta(i, q_low, spline_coeff, theta_scale, dispersion_env, tmp_1d)
     290         8440 :          CALL pw_pool%create_pw(thetas_g(i))
     291         8862 :          CALL pw_transfer(tmp_r, thetas_g(i))
     292              :       END DO
     293          422 :       CALL timestop(handle_fft)
     294          422 :       DEALLOCATE (spline_coeff, theta_scale)
     295          422 :       grid => thetas_g(1)%pw_grid
     296              :       !! ---------------------------------------------------------------------------------------------
     297              :       !! Carry out the integration in equation 8 of SOLER.  This also turns the thetas array into the
     298              :       !! precursor to the u_i(k) array which is inverse fourier transformed to get the u_i(r) functions
     299              :       !! of SOLER equation 11.  Add the energy we find to the output variable etxc.
     300              :       !! --------------------------------------------------------------------------------------------------
     301          422 :       sumnp = np
     302          422 :       CALL para_env%sum(sumnp)
     303          422 :       IF (use_virial) THEN
     304              :          ! calculates kernel contribution to stress
     305          112 :          CALL vdW_energy(thetas_g, dispersion_env, Ec_nl, energy_only, virial)
     306           42 :          SELECT CASE (nl_type)
     307              :          CASE (vdw_nl_RVV10)
     308       290416 :             Ec_nl = 0.5_dp*Ec_nl + beta*SUM(rho(:))*grid%vol/sumnp
     309              :          END SELECT
     310              :          ! calculates energy contribution to stress
     311              :          ! potential contribution to stress is calculated together with other potentials (Hxc)
     312          448 :          DO idir = 1, 3
     313          448 :             virial%pv_xc(idir, idir) = virial%pv_xc(idir, idir) + Ec_nl
     314              :          END DO
     315              :       ELSE
     316          310 :          CALL vdW_energy(thetas_g, dispersion_env, Ec_nl, energy_only)
     317          134 :          SELECT CASE (nl_type)
     318              :          CASE (vdw_nl_RVV10)
     319       662166 :             Ec_nl = 0.5_dp*Ec_nl + beta*SUM(rho(:))*grid%vol/sumnp
     320              :          END SELECT
     321              :       END IF
     322          422 :       CALL para_env%sum(Ec_nl)
     323          422 :       IF (nl_type == vdw_nl_RVV10) Ec_nl = Ec_nl*dispersion_env%scale_rvv10
     324          422 :       edispersion = Ec_nl
     325              : 
     326          422 :       IF (energy_only) THEN
     327            6 :          DEALLOCATE (q0, q_low)
     328              :       ELSE
     329              :          ! Accumulate the two spline contractions and their endpoint values as each
     330              :          ! inverse FFT finishes. Keep the original potential-side knot convention.
     331         1248 :          ALLOCATE (u_contract(np, 4), hpot(np))
     332          416 : !$OMP PARALLEL DO DEFAULT(NONE) SHARED(np, q_low, q0, dispersion_env, u_contract) PRIVATE(s)
     333              :          DO i_grid = 1, np
     334              :             s = q_low(i_grid)
     335              :             IF (s > 0 .AND. s < dispersion_env%nqs - 1) THEN
     336              :                IF (q0(i_grid) == dispersion_env%q_mesh(s + 1)) q_low(i_grid) = s + 1
     337              :             END IF
     338              :             u_contract(i_grid, :) = 0.0_dp
     339              :          END DO
     340              : !$OMP END PARALLEL DO
     341          416 :          CALL timeset("vdW_theta_inverse", handle_fft)
     342         8736 :          DO i = 1, dispersion_env%nqs
     343         8320 :             CALL pw_transfer(thetas_g(i), tmp_r)
     344         8736 :             CALL accumulate_potential(i, q_low, dispersion_env, tmp_1d, u_contract)
     345              :          END DO
     346          416 :          CALL timestop(handle_fft)
     347              : 
     348              :          ! Write the local potential directly into its PW object.
     349          416 :          CALL pw_pool%create_pw(vxc_r)
     350          416 :          vxc_1d(1:np) => vxc_r%array
     351          416 :          IF (use_virial) THEN
     352          112 :             grid => tmp_g%pw_grid
     353              :             CALL get_potential(q0, dq0_drho, dq0_dgradrho, rho, q_low, u_contract, vxc_1d, hpot, &
     354          112 :                                dispersion_env, drho, grid%dvol, virial)
     355              :          ELSE
     356              :             CALL get_potential(q0, dq0_drho, dq0_dgradrho, rho, q_low, u_contract, vxc_1d, hpot, &
     357          304 :                                dispersion_env)
     358              :          END IF
     359          416 :          DEALLOCATE (u_contract, q_low, q0, dq0_drho, dq0_dgradrho)
     360          170 :          SELECT CASE (nl_type)
     361              :          CASE (vdw_nl_RVV10)
     362          416 : !$OMP PARALLEL DO DEFAULT(NONE) SHARED(np, vxc_1d, hpot, beta, dispersion_env)
     363              :             DO i_grid = 1, np
     364              :                vxc_1d(i_grid) = (0.5_dp*vxc_1d(i_grid) + beta)*dispersion_env%scale_rvv10
     365              :                hpot(i_grid) = 0.5_dp*dispersion_env%scale_rvv10*hpot(i_grid)
     366              :             END DO
     367              : !$OMP END PARALLEL DO
     368              :          END SELECT
     369          416 :          NULLIFY (vxc_1d)
     370              :          ! Sum the derivatives before the inverse FFT. Keep the real-space projection
     371              :          ! before transferring the potential to a possibly different XC grid.
     372          416 :          CALL pw_pool%create_pw(div_g)
     373         1664 :          DO idir = 1, 3
     374              : !$OMP PARALLEL DO DEFAULT(NONE) &
     375         1248 : !$OMP             SHARED(n, lo, tmp_r, hpot, drho, idir) PRIVATE(s) COLLAPSE(3)
     376              :             DO r = 0, n(3) - 1
     377              :                DO q = 0, n(2) - 1
     378              :                   DO p = 0, n(1) - 1
     379              :                      s = r*n(2)*n(1) + q*n(1) + p + 1
     380              :                      tmp_r%array(p + lo(1), q + lo(2), r + lo(3)) = hpot(s)*drho(s, idir)
     381              :                   END DO
     382              :                END DO
     383              :             END DO
     384              : !$OMP END PARALLEL DO
     385         1248 :             CALL pw_transfer(tmp_r, tmp_g)
     386         1248 :             CALL pw_derive(tmp_g, nd(:, idir))
     387         1664 :             IF (idir == 1) THEN
     388          416 :                CALL pw_transfer(tmp_g, div_g)
     389              :             ELSE
     390          832 :                CALL pw_axpy(tmp_g, div_g, 1._dp)
     391              :             END IF
     392              :          END DO
     393          416 :          CALL pw_transfer(div_g, tmp_r)
     394          416 :          CALL pw_pool%give_back_pw(div_g)
     395          416 :          CALL pw_axpy(tmp_r, vxc_r, -1._dp)
     396          416 :          CALL pw_transfer(vxc_r, tmp_g)
     397          416 :          CALL pw_pool%give_back_pw(vxc_r)
     398          416 :          CALL xc_pw_pool%create_pw(vxc_r)
     399          416 :          CALL xc_pw_pool%create_pw(vxc_g)
     400          416 :          CALL pw_transfer(tmp_g, vxc_g)
     401          416 :          CALL pw_transfer(vxc_g, vxc_r)
     402          852 :          DO ispin = 1, nspin
     403          852 :             CALL pw_axpy(vxc_r, vxc_rho(ispin), 1._dp)
     404              :          END DO
     405          416 :          CALL xc_pw_pool%give_back_pw(vxc_r)
     406          832 :          CALL xc_pw_pool%give_back_pw(vxc_g)
     407              :       END IF
     408              : 
     409              :       NULLIFY (tmp_1d)
     410              : 
     411         8862 :       DO i = 1, dispersion_env%nqs
     412         8862 :          CALL pw_pool%give_back_pw(thetas_g(i))
     413              :       END DO
     414          422 :       CALL pw_pool%give_back_pw(tmp_r)
     415          422 :       CALL pw_pool%give_back_pw(tmp_g)
     416              : 
     417          422 :       DEALLOCATE (rho, drho, thetas_g)
     418              : 
     419          422 :       CALL timestop(handle)
     420              : 
     421         1266 :    END SUBROUTINE calculate_dispersion_nonloc
     422              : 
     423              : ! **************************************************************************************************
     424              : !> \brief This routine carries out the integration of equation 8 of SOLER.  It returns the non-local
     425              : !> exchange-correlation energy and the u_alpha(k) arrays used to find the u_alpha(r) arrays via
     426              : !> equations 11 and 12 in SOLER.
     427              : !> energy contribution to stress is added in qs_force
     428              : !> \param thetas_g ...
     429              : !> \param dispersion_env ...
     430              : !> \param vdW_xc_energy ...
     431              : !> \param energy_only ...
     432              : !> \param virial ...
     433              : !> \par History
     434              : !>    OpenMP added: Aug 2016  MTucker
     435              : ! **************************************************************************************************
     436          422 :    SUBROUTINE vdW_energy(thetas_g, dispersion_env, vdW_xc_energy, energy_only, virial)
     437              :       TYPE(pw_c1d_gs_type), DIMENSION(:), INTENT(IN)     :: thetas_g
     438              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
     439              :       REAL(KIND=dp), INTENT(OUT)                         :: vdW_xc_energy
     440              :       LOGICAL, INTENT(IN)                                :: energy_only
     441              :       TYPE(virial_type), OPTIONAL, POINTER               :: virial
     442              : 
     443              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'vdW_energy'
     444              : 
     445              :       INTEGER                                            :: handle, ig, iq, l, m, nl_type, nqs, &
     446              :                                                             q1_i, q2_i
     447              :       LOGICAL                                            :: use_virial
     448              :       REAL(KIND=dp)                                      :: g, g2, g2_last, g_multiplier, gm
     449          422 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: theta_im, theta_re, u_im, u_re
     450          422 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: dkernel_of_dk, kernel_of_k
     451              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: virial_thread
     452              :       TYPE(pw_grid_type), POINTER                        :: grid
     453              : 
     454          422 :       CALL timeset(routineN, handle)
     455          422 :       nqs = dispersion_env%nqs
     456              : 
     457          422 :       use_virial = PRESENT(virial)
     458          422 :       virial_thread(:, :) = 0.0_dp ! always initialize to avoid floating point exceptions in OMP REDUCTION
     459              : 
     460          422 :       vdW_xc_energy = 0._dp
     461          422 :       grid => thetas_g(1)%pw_grid
     462              : 
     463          422 :       IF (grid%grid_span == HALFSPACE) THEN
     464              :          g_multiplier = 2._dp
     465              :       ELSE
     466          422 :          g_multiplier = 1._dp
     467              :       END IF
     468              : 
     469          422 :       nl_type = dispersion_env%nl_type
     470              : 
     471              : !$OMP PARALLEL DEFAULT(NONE) &
     472              : !$OMP          SHARED(nqs, energy_only, grid, dispersion_env, use_virial, thetas_g, &
     473              : !$OMP                 g_multiplier, nl_type) &
     474              : !$OMP          PRIVATE(g2_last, kernel_of_k, dkernel_of_dk, theta_re, theta_im, &
     475              : !$OMP                  g2, g, iq, q2_i, u_re, u_im, q1_i, gm, l, m) &
     476          422 : !$OMP          REDUCTION(+:vdW_xc_energy, virial_thread)
     477              : 
     478              :       g2_last = HUGE(0._dp)
     479              : 
     480              :       ALLOCATE (kernel_of_k(nqs, nqs))
     481              :       IF (use_virial) ALLOCATE (dkernel_of_dk(nqs, nqs))
     482              :       ALLOCATE (theta_re(nqs), theta_im(nqs), u_re(nqs), u_im(nqs))
     483              : 
     484              : !$OMP DO
     485              :       DO ig = 1, grid%ngpts_cut_local
     486              :          g2 = grid%gsq(ig)
     487              :          IF (ABS(g2 - g2_last) > 1.e-10) THEN
     488              :             g2_last = g2
     489              :             g = SQRT(g2)
     490              :             IF (use_virial) THEN
     491              :                CALL interpolate_kernel(g, kernel_of_k, dispersion_env, dispersion_env%kernel_table, dkernel_of_dk)
     492              :             ELSE
     493              :                CALL interpolate_kernel(g, kernel_of_k, dispersion_env, dispersion_env%kernel_table)
     494              :             END IF
     495              :          END IF
     496              :          ! Save all inputs before in-place output. Vectorize over output channels;
     497              :          ! the input-channel accumulation order remains unchanged for every output.
     498              :          DO iq = 1, nqs
     499              :             theta_re(iq) = REAL(thetas_g(iq)%array(ig), KIND=dp)
     500              :             theta_im(iq) = AIMAG(thetas_g(iq)%array(ig))
     501              :          END DO
     502              :          u_re(:) = 0.0_dp
     503              :          u_im(:) = 0.0_dp
     504              :          DO q1_i = 1, nqs
     505              : !$OMP SIMD
     506              :             DO q2_i = 1, nqs
     507              :                u_re(q2_i) = u_re(q2_i) + kernel_of_k(q2_i, q1_i)*theta_re(q1_i)
     508              :                u_im(q2_i) = u_im(q2_i) + kernel_of_k(q2_i, q1_i)*theta_im(q1_i)
     509              :             END DO
     510              : !$OMP END SIMD
     511              :          END DO
     512              :          DO q2_i = 1, nqs
     513              :             IF (ig < grid%first_gne0) THEN
     514              :                vdW_xc_energy = vdW_xc_energy + (u_re(q2_i)*theta_re(q2_i) + u_im(q2_i)*theta_im(q2_i))
     515              :             ELSE
     516              :                vdW_xc_energy = vdW_xc_energy &
     517              :                                + g_multiplier*(u_re(q2_i)*theta_re(q2_i) + u_im(q2_i)*theta_im(q2_i))
     518              :             END IF
     519              :             IF (.NOT. energy_only) thetas_g(q2_i)%array(ig) = CMPLX(u_re(q2_i), u_im(q2_i), KIND=dp)
     520              :          END DO
     521              : 
     522              :          IF (use_virial .AND. ig >= grid%first_gne0) THEN
     523              :             ! Reduce over all channel pairs before assembling the stress tensor.
     524              :             gm = 0.0_dp
     525              :             DO q2_i = 1, nqs
     526              : !$OMP SIMD REDUCTION(+:gm)
     527              :                DO q1_i = 1, nqs
     528              :                   gm = gm + dkernel_of_dk(q1_i, q2_i) &
     529              :                        *(theta_re(q1_i)*theta_re(q2_i) + theta_im(q1_i)*theta_im(q2_i))
     530              :                END DO
     531              : !$OMP END SIMD
     532              :             END DO
     533              :             gm = 0.5_dp*g_multiplier*grid%vol*gm
     534              :             IF (nl_type == vdw_nl_RVV10) gm = 0.5_dp*gm
     535              :             DO l = 1, 3
     536              :                DO m = 1, l
     537              :                   virial_thread(l, m) = virial_thread(l, m) - gm*(grid%g(l, ig)*grid%g(m, ig))/g
     538              :                END DO
     539              :             END DO
     540              :          END IF
     541              :       END DO
     542              : !$OMP END DO
     543              : 
     544              :       DEALLOCATE (theta_re, theta_im, u_re, u_im, kernel_of_k)
     545              :       IF (use_virial) DEALLOCATE (dkernel_of_dk)
     546              : 
     547              : !$OMP END PARALLEL
     548              : 
     549          422 :       vdW_xc_energy = vdW_xc_energy*grid%vol*0.5_dp
     550              : 
     551          422 :       IF (use_virial) THEN
     552          448 :          DO l = 1, 3
     553          672 :             DO m = 1, (l - 1)
     554          336 :                virial%pv_xc(l, m) = virial%pv_xc(l, m) + virial_thread(l, m)
     555          672 :                virial%pv_xc(m, l) = virial%pv_xc(l, m)
     556              :             END DO
     557          336 :             m = l
     558          448 :             virial%pv_xc(l, m) = virial%pv_xc(l, m) + virial_thread(l, m)
     559              :          END DO
     560              :       END IF
     561              : 
     562          422 :       CALL timestop(handle)
     563              : 
     564          422 :    END SUBROUTINE vdW_energy
     565              : 
     566              : ! **************************************************************************************************
     567              : !> \brief This routine finds the non-local correlation contribution to the potential
     568              : !> (i.e. the derivative of the non-local piece of the energy with respect to
     569              : !> density) given in SOLER equation 10.  The u_alpha(k) functions were found
     570              : !> while calculating the energy. Their spline contractions are accumulated during the inverse FFTs.
     571              : !> Most of the required derivatives were calculated in the "get_q0_on_grid"
     572              : !> routine, but the derivative of the interpolation polynomials, P_alpha(q),
     573              : !> (SOLER equation 3) with respect to q is interpolated here, along with the
     574              : !> polynomials themselves.
     575              : !> \param q0 ...
     576              : !> \param dq0_drho ...
     577              : !> \param dq0_dgradrho ...
     578              : !> \param total_rho ...
     579              : !> \param q_low_grid Lower spline endpoint at each grid point
     580              : !> \param u_contract Second-derivative contractions and endpoint values
     581              : !> \param potential ...
     582              : !> \param h_prefactor ...
     583              : !> \param dispersion_env ...
     584              : !> \param drho ...
     585              : !> \param dvol ...
     586              : !> \param virial ...
     587              : !> \par History
     588              : !> OpenMP added: Aug 2016  MTucker
     589              : ! **************************************************************************************************
     590          416 :    SUBROUTINE get_potential(q0, dq0_drho, dq0_dgradrho, total_rho, q_low_grid, u_contract, potential, h_prefactor, &
     591          416 :                             dispersion_env, drho, dvol, virial)
     592              : 
     593              :       REAL(dp), DIMENSION(:), INTENT(in)                 :: q0, dq0_drho, dq0_dgradrho, total_rho
     594              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: q_low_grid
     595              :       REAL(dp), DIMENSION(:, :), INTENT(in)              :: u_contract
     596              :       REAL(dp), DIMENSION(:), INTENT(out)                :: potential, h_prefactor
     597              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
     598              :       REAL(dp), DIMENSION(:, :), INTENT(in), OPTIONAL    :: drho
     599              :       REAL(dp), INTENT(IN), OPTIONAL                     :: dvol
     600              :       TYPE(virial_type), OPTIONAL, POINTER               :: virial
     601              : 
     602              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'get_potential'
     603              : 
     604              :       INTEGER                                            :: handle, i_grid, l, m, nl_type, nqs, &
     605              :                                                             q_hi, q_low
     606              :       LOGICAL                                            :: use_virial
     607              :       REAL(dp)                                           :: a, b, b_value, c, const, d, dq, dq_6, e, &
     608              :                                                             f, prefactor, tmp_1_2, tmp_1_4, &
     609              :                                                             tmp_3_4, u_dp_dq0, u_p
     610              :       REAL(dp), DIMENSION(3, 3)                          :: virial_thread
     611          416 :       REAL(dp), DIMENSION(:), POINTER                    :: q_mesh
     612              : 
     613          416 :       CALL timeset(routineN, handle)
     614              : 
     615          416 :       use_virial = PRESENT(virial)
     616          416 :       CPASSERT(.NOT. use_virial .OR. PRESENT(drho))
     617          416 :       CPASSERT(.NOT. use_virial .OR. PRESENT(dvol))
     618              : 
     619          416 :       virial_thread(:, :) = 0.0_dp ! always initialize to avoid floating point exceptions in OMP REDUCTION
     620          416 :       b_value = dispersion_env%b_value
     621          416 :       const = 1.0_dp/(3.0_dp*b_value**(3.0_dp/2.0_dp)*pi**(5.0_dp/4.0_dp))
     622              : 
     623          416 :       q_mesh => dispersion_env%q_mesh
     624          416 :       nqs = dispersion_env%nqs
     625          416 :       nl_type = dispersion_env%nl_type
     626              : 
     627              : !$OMP PARALLEL DEFAULT(NONE) &
     628              : !$OMP          SHARED(nqs, u_contract, q_low_grid, q_mesh, q0, nl_type, potential, h_prefactor, &
     629              : !$OMP                 dq0_drho, dq0_dgradrho, total_rho, const, use_virial, drho, dvol, virial) &
     630              : !$OMP          PRIVATE(q_low, q_hi, dq, dq_6, A, b, c, d, e, f, u_p, u_dp_dq0, &
     631              : !$OMP                  prefactor, l, m, tmp_1_2, tmp_1_4, tmp_3_4) &
     632          416 : !$OMP          REDUCTION(+:virial_thread)
     633              : 
     634              : !$OMP DO
     635              :       DO i_grid = 1, SIZE(q0)
     636              :          potential(i_grid) = 0.0_dp
     637              :          h_prefactor(i_grid) = 0.0_dp
     638              :          IF (nl_type == vdw_nl_RVV10 .AND. total_rho(i_grid) <= epsr) CYCLE
     639              :          q_low = q_low_grid(i_grid)
     640              :          q_hi = q_low + 1
     641              : 
     642              :          dq = q_mesh(q_hi) - q_mesh(q_low)
     643              :          dq_6 = dq/6.0_dp
     644              : 
     645              :          a = (q_mesh(q_hi) - q0(i_grid))/dq
     646              :          b = (q0(i_grid) - q_mesh(q_low))/dq
     647              :          c = (a**3 - a)*dq*dq_6
     648              :          d = (b**3 - b)*dq*dq_6
     649              :          e = (3.0_dp*a**2 - 1.0_dp)*dq_6
     650              :          f = (3.0_dp*b**2 - 1.0_dp)*dq_6
     651              : 
     652              :          u_p = a*u_contract(i_grid, 3) + b*u_contract(i_grid, 4) &
     653              :                + c*u_contract(i_grid, 1) + d*u_contract(i_grid, 2)
     654              :          u_dp_dq0 = (u_contract(i_grid, 4) - u_contract(i_grid, 3))/dq &
     655              :                     - e*u_contract(i_grid, 1) + f*u_contract(i_grid, 2)
     656              : 
     657              :          !! The first term in equation 13 of SOLER
     658              :          SELECT CASE (nl_type)
     659              :          CASE DEFAULT
     660              :             CPABORT("Unknown vdW-DF functional")
     661              :          CASE (vdw_nl_DRSLL, vdw_nl_LMKLL)
     662              :             potential(i_grid) = u_p + u_dp_dq0*dq0_drho(i_grid)
     663              :             prefactor = u_dp_dq0*dq0_dgradrho(i_grid)
     664              :          CASE (vdw_nl_RVV10)
     665              :             tmp_1_2 = SQRT(total_rho(i_grid))
     666              :             tmp_1_4 = SQRT(tmp_1_2)
     667              :             tmp_3_4 = tmp_1_4*tmp_1_4*tmp_1_4
     668              :             potential(i_grid) = const*0.75_dp/tmp_1_4*u_p + const*tmp_3_4*u_dp_dq0*dq0_drho(i_grid)
     669              :             prefactor = const*tmp_3_4*u_dp_dq0*dq0_dgradrho(i_grid)
     670              :          END SELECT
     671              :          IF (q0(i_grid) /= q_mesh(nqs)) THEN
     672              :             h_prefactor(i_grid) = prefactor
     673              :          END IF
     674              : 
     675              :          ! The saturation guard applies only to h_prefactor, not to the virial.
     676              :          IF (use_virial .AND. ABS(prefactor) > 0.0_dp) THEN
     677              :             IF (nl_type == vdw_nl_RVV10) prefactor = 0.5_dp*prefactor
     678              :             prefactor = prefactor*dvol
     679              :             DO l = 1, 3
     680              :                DO m = 1, l
     681              :                   virial_thread(l, m) = virial_thread(l, m) - prefactor*drho(i_grid, l)*drho(i_grid, m)
     682              :                END DO
     683              :             END DO
     684              :          END IF
     685              :       END DO ! i_grid = 1, SIZE(q0)
     686              : !$OMP END DO
     687              : 
     688              : !$OMP END PARALLEL
     689              : 
     690          416 :       IF (use_virial) THEN
     691          448 :          DO l = 1, 3
     692          672 :             DO m = 1, (l - 1)
     693          336 :                virial%pv_xc(l, m) = virial%pv_xc(l, m) + virial_thread(l, m)
     694          672 :                virial%pv_xc(m, l) = virial%pv_xc(l, m)
     695              :             END DO
     696          336 :             m = l
     697          448 :             virial%pv_xc(l, m) = virial%pv_xc(l, m) + virial_thread(l, m)
     698              :          END DO
     699              :       END IF
     700              : 
     701          416 :       CALL timestop(handle)
     702          416 :    END SUBROUTINE get_potential
     703              : 
     704              : ! **************************************************************************************************
     705              : !> \brief calculates exponent = sum(from i=1 to hi, ((alpha)**i)/i) ) without <<< calling power >>>
     706              : !> \param hi = upper index for sum
     707              : !> \param alpha ...
     708              : !> \param exponent = output value
     709              : !> \par History
     710              : !>     Created:  MTucker, Aug 2016
     711              : ! **************************************************************************************************
     712        72326 :    ELEMENTAL SUBROUTINE calculate_exponent(hi, alpha, exponent)
     713              :       INTEGER, INTENT(in)                                :: hi
     714              :       REAL(dp), INTENT(in)                               :: alpha
     715              :       REAL(dp), INTENT(out)                              :: exponent
     716              : 
     717              :       INTEGER                                            :: i
     718              :       REAL(dp)                                           :: multiplier
     719              : 
     720        72326 :       multiplier = alpha
     721        72326 :       exponent = alpha
     722              : 
     723       867912 :       DO i = 2, hi
     724       795586 :          multiplier = multiplier*alpha
     725       867912 :          exponent = exponent + (multiplier/i)
     726              :       END DO
     727        72326 :    END SUBROUTINE calculate_exponent
     728              : 
     729              : ! **************************************************************************************************
     730              : !> \brief calculate exponent = sum(from i=1 to hi, ((alpha)**i)/i) )  without calling power
     731              : !>        also calculates derivative using similar series
     732              : !> \param hi = upper index for sum
     733              : !> \param alpha ...
     734              : !> \param exponent = output value
     735              : !> \param derivative ...
     736              : !> \par History
     737              : !>     Created:  MTucker, Aug 2016
     738              : ! **************************************************************************************************
     739      5076074 :    ELEMENTAL SUBROUTINE calculate_exponent_derivative(hi, alpha, exponent, derivative)
     740              :       INTEGER, INTENT(in)                                :: hi
     741              :       REAL(dp), INTENT(in)                               :: alpha
     742              :       REAL(dp), INTENT(out)                              :: exponent, derivative
     743              : 
     744              :       INTEGER                                            :: i
     745              :       REAL(dp)                                           :: multiplier
     746              : 
     747      5076074 :       derivative = 0.0d0
     748      5076074 :       multiplier = 1.0d0
     749      5076074 :       exponent = 0.0d0
     750              : 
     751     65988962 :       DO i = 1, hi
     752     60912888 :          derivative = derivative + multiplier
     753     60912888 :          multiplier = multiplier*alpha
     754     65988962 :          exponent = exponent + (multiplier/i)
     755              :       END DO
     756      5076074 :    END SUBROUTINE calculate_exponent_derivative
     757              : 
     758              :    !! This routine first calculates the q value defined in (DION equations 11 and 12), then
     759              :    !! saturates it according to (SOLER equation 5).
     760              : ! **************************************************************************************************
     761              : !> \brief This routine first calculates the q value defined in (DION equations 11 and 12), then
     762              : !> saturates it according to (SOLER equation 5).
     763              : !> \param total_rho ...
     764              : !> \param gradient_rho ...
     765              : !> \param q0 ...
     766              : !> \param dq0_drho ...
     767              : !> \param dq0_dgradrho ...
     768              : !> \param dispersion_env ...
     769              : ! **************************************************************************************************
     770          246 :    SUBROUTINE get_q0_on_grid_vdw(total_rho, gradient_rho, q0, dq0_drho, dq0_dgradrho, dispersion_env)
     771              :       !!
     772              :       !! more specifically it calculates the following
     773              :       !!
     774              :       !!     q0(ir) = q0 as defined above
     775              :       !!     dq0_drho(ir) = total_rho * d q0 /d rho
     776              :       !!     dq0_dgradrho = total_rho / |gradient_rho| * d q0 / d |gradient_rho|
     777              :       !!
     778              :       REAL(dp), INTENT(IN)                               :: total_rho(:), gradient_rho(:, :)
     779              :       REAL(dp), INTENT(OUT)                              :: q0(:), dq0_drho(:), dq0_dgradrho(:)
     780              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
     781              : 
     782              :       INTEGER, PARAMETER                                 :: m_cut = 12
     783              :       REAL(dp), PARAMETER :: LDA_A = 0.031091_dp, LDA_a1 = 0.2137_dp, LDA_b1 = 7.5957_dp, &
     784              :          LDA_b2 = 3.5876_dp, LDA_b3 = 1.6382_dp, LDA_b4 = 0.49294_dp
     785              : 
     786              :       INTEGER                                            :: i_grid
     787              :       REAL(dp)                                           :: dq0_dq, exponent, gradient_correction, &
     788              :                                                             kF, LDA_1, LDA_2, q, q__q_cut, q_cut, &
     789              :                                                             q_min, r_s, sqrt_r_s, Z_ab
     790              : 
     791          246 :       q_cut = dispersion_env%q_cut
     792          246 :       q_min = dispersion_env%q_min
     793          246 :       SELECT CASE (dispersion_env%nl_type)
     794              :       CASE DEFAULT
     795            0 :          CPABORT("Unknown vdW-DF functional")
     796              :       CASE (vdw_nl_DRSLL)
     797           48 :          Z_ab = -0.8491_dp
     798              :       CASE (vdw_nl_LMKLL)
     799          246 :          Z_ab = -1.887_dp
     800              :       END SELECT
     801              : 
     802              : !$OMP PARALLEL DO DEFAULT(NONE) &
     803              : !$OMP             SHARED(total_rho, gradient_rho, q0, dq0_drho, dq0_dgradrho, q_cut, q_min, Z_ab) &
     804              : !$OMP             PRIVATE(dq0_dq, exponent, gradient_correction, kF, LDA_1, LDA_2, q, q__q_cut, r_s, sqrt_r_s) &
     805          246 : !$OMP             SCHEDULE(STATIC)
     806              :       DO i_grid = 1, SIZE(total_rho)
     807              :          q0(i_grid) = q_cut
     808              :          dq0_drho(i_grid) = 0.0_dp
     809              :          dq0_dgradrho(i_grid) = 0.0_dp
     810              : 
     811              :          !! This prevents numerical problems.  If the charge density is negative (an
     812              :          !! unphysical situation), we simply treat it as very small.  In that case,
     813              :          !! q0 will be very large and will be saturated.  For a saturated q0 the derivative
     814              :          !! dq0_dq will be 0 so we set q0 = q_cut and dq0_drho = dq0_dgradrho = 0 and go on
     815              :          !! to the next point.
     816              :          !! ------------------------------------------------------------------------------------
     817              :          IF (total_rho(i_grid) < epsr) CYCLE
     818              :          !! ------------------------------------------------------------------------------------
     819              :          !! Calculate some intermediate values needed to find q
     820              :          !! ------------------------------------------------------------------------------------
     821              :          kF = (3.0_dp*pi*pi*total_rho(i_grid))**(1.0_dp/3.0_dp)
     822              :          r_s = (3.0_dp/(4.0_dp*pi*total_rho(i_grid)))**(1.0_dp/3.0_dp)
     823              :          sqrt_r_s = SQRT(r_s)
     824              : 
     825              :          gradient_correction = -Z_ab/(36.0_dp*kF*total_rho(i_grid)**2) &
     826              :                                *(gradient_rho(i_grid, 1)**2 + gradient_rho(i_grid, 2)**2 + gradient_rho(i_grid, 3)**2)
     827              : 
     828              :          LDA_1 = 8.0_dp*pi/3.0_dp*(LDA_A*(1.0_dp + LDA_a1*r_s))
     829              :          LDA_2 = 2.0_dp*LDA_A*(LDA_b1*sqrt_r_s + LDA_b2*r_s + LDA_b3*r_s*sqrt_r_s + LDA_b4*r_s*r_s)
     830              :          !! ---------------------------------------------------------------
     831              :          !! This is the q value defined in equations 11 and 12 of DION
     832              :          !! ---------------------------------------------------------------
     833              :          q = kF + LDA_1*LOG(1.0_dp + 1.0_dp/LDA_2) + gradient_correction
     834              :          !! ---------------------------------------------------------------
     835              :          !! Here, we calculate q0 by saturating q according to equation 5 of SOLER.  Also, we find
     836              :          !! the derivative dq0_dq needed for the derivatives dq0_drho and dq0_dgradrh0 discussed below.
     837              :          !! ---------------------------------------------------------------------------------------
     838              :          q__q_cut = q/q_cut
     839              :          CALL calculate_exponent_derivative(m_cut, q__q_cut, exponent, dq0_dq)
     840              :          q0(i_grid) = q_cut*(1.0_dp - EXP(-exponent))
     841              :          dq0_dq = dq0_dq*EXP(-exponent)
     842              :          !! ---------------------------------------------------------------------------------------
     843              :          !! This is to handle a case with q0 too small.  We simply set it to the smallest q value in
     844              :          !! out q_mesh.  Hopefully this doesn't get used often (ever)
     845              :          !! ---------------------------------------------------------------------------------------
     846              :          IF (q0(i_grid) < q_min) THEN
     847              :             q0(i_grid) = q_min
     848              :          END IF
     849              :          !! ---------------------------------------------------------------------------------------
     850              :          !! Here we find derivatives.  These are actually the density times the derivative of q0 with respect
     851              :          !! to rho and gradient_rho.  The density factor comes in since we are really differentiating
     852              :          !! theta = (rho)*P(q0) with respect to density (or its gradient) which will be
     853              :          !! dtheta_drho = P(q0) + dP_dq0 * [rho * dq0_dq * dq_drho]   and
     854              :          !! dtheta_dgradient_rho =  dP_dq0  * [rho * dq0_dq * dq_dgradient_rho]
     855              :          !! The parts in square brackets are what is calculated here.  The dP_dq0 term will be interpolated
     856              :          !! later.  There should actually be a factor of the magnitude of the gradient in the gradient_rho derivative
     857              :          !! but that cancels out when we differentiate the magnitude of the gradient with respect to a particular
     858              :          !! component.
     859              :          !! ------------------------------------------------------------------------------------------------
     860              : 
     861              :          dq0_drho(i_grid) = dq0_dq*(kF/3.0_dp - 7.0_dp/3.0_dp*gradient_correction &
     862              :                                     - 8.0_dp*pi/9.0_dp*LDA_A*LDA_a1*r_s*LOG(1.0_dp + 1.0_dp/LDA_2) &
     863              :                                     + LDA_1/(LDA_2*(1.0_dp + LDA_2)) &
     864              :                                     *(2.0_dp*LDA_A*(LDA_b1/6.0_dp*sqrt_r_s + LDA_b2/3.0_dp*r_s + LDA_b3/2.0_dp*r_s*sqrt_r_s &
     865              :                                                     + 2.0_dp*LDA_b4/3.0_dp*r_s**2)))
     866              : 
     867              :          dq0_dgradrho(i_grid) = total_rho(i_grid)*dq0_dq*2.0_dp*(-Z_ab)/(36.0_dp*kF*total_rho(i_grid)**2)
     868              : 
     869              :       END DO
     870              : !$OMP END PARALLEL DO
     871              : 
     872          246 :    END SUBROUTINE get_q0_on_grid_vdw
     873              : 
     874              : ! **************************************************************************************************
     875              : !> \brief ...
     876              : !> \param total_rho ...
     877              : !> \param gradient_rho ...
     878              : !> \param q0 ...
     879              : !> \param dq0_drho ...
     880              : !> \param dq0_dgradrho ...
     881              : !> \param dispersion_env ...
     882              : ! **************************************************************************************************
     883          170 :    SUBROUTINE get_q0_on_grid_rvv10(total_rho, gradient_rho, q0, dq0_drho, dq0_dgradrho, dispersion_env)
     884              :       !!
     885              :       !! more specifically it calculates the following
     886              :       !!
     887              :       !!     q0(ir) = q0 as defined above
     888              :       !!     dq0_drho(ir) = total_rho * d q0 /d rho
     889              :       !!     dq0_dgradrho = total_rho / |gradient_rho| * d q0 / d |gradient_rho|
     890              :       !!
     891              :       REAL(dp), INTENT(IN)                               :: total_rho(:), gradient_rho(:, :)
     892              :       REAL(dp), INTENT(OUT)                              :: q0(:), dq0_drho(:), dq0_dgradrho(:)
     893              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
     894              : 
     895              :       INTEGER, PARAMETER                                 :: m_cut = 12
     896              : 
     897              :       INTEGER                                            :: i_grid
     898              :       REAL(dp)                                           :: b_value, C_value, dk_dn, dq0_dq, dw0_dn, &
     899              :                                                             exponent, gmod2, k, mod_grad, q, &
     900              :                                                             q__q_cut, q_cut, q_min, w0, wg2, wp2
     901              : 
     902          170 :       q_cut = dispersion_env%q_cut
     903          170 :       q_min = dispersion_env%q_min
     904          170 :       b_value = dispersion_env%b_value
     905          170 :       C_value = dispersion_env%c_value
     906              : 
     907              : !$OMP PARALLEL DO DEFAULT(NONE) &
     908              : !$OMP             SHARED(total_rho, gradient_rho, q0, dq0_drho, dq0_dgradrho, q_cut, q_min, b_value, C_value) &
     909              : !$OMP             PRIVATE(dk_dn, dq0_dq, dw0_dn, exponent, gmod2, k, mod_grad, q, q__q_cut, w0, wg2, wp2) &
     910          170 : !$OMP             SCHEDULE(STATIC)
     911              :       DO i_grid = 1, SIZE(total_rho)
     912              :          q0(i_grid) = q_cut
     913              :          dq0_drho(i_grid) = 0.0_dp
     914              :          dq0_dgradrho(i_grid) = 0.0_dp
     915              : 
     916              :          gmod2 = gradient_rho(i_grid, 1)**2 + gradient_rho(i_grid, 2)**2 + gradient_rho(i_grid, 3)**2
     917              : 
     918              :          !if (total_rho(i_grid) > epsr .and. gmod2 > epsr) cycle
     919              :          IF (total_rho(i_grid) > epsr) THEN
     920              : 
     921              :             !! Calculate some intermediate values needed to find q
     922              :             !! ------------------------------------------------------------------------------------
     923              :             mod_grad = SQRT(gmod2)
     924              : 
     925              :             wp2 = 16.0_dp*pi*total_rho(i_grid)
     926              :             wg2 = 4_dp*C_value*(mod_grad/total_rho(i_grid))**4
     927              : 
     928              :             k = b_value*3.0_dp*pi*((total_rho(i_grid)/(9.0_dp*pi))**(1.0_dp/6.0_dp))
     929              :             w0 = SQRT(wg2 + wp2/3.0_dp)
     930              : 
     931              :             q = w0/k
     932              : 
     933              :             !! Here, we calculate q0 by saturating q according
     934              :             !! ---------------------------------------------------------------------------------------
     935              :             q__q_cut = q/q_cut
     936              :             CALL calculate_exponent_derivative(m_cut, q__q_cut, exponent, dq0_dq)
     937              :             q0(i_grid) = q_cut*(1.0_dp - EXP(-exponent))
     938              :             dq0_dq = dq0_dq*EXP(-exponent)
     939              : 
     940              :             !! ---------------------------------------------------------------------------------------
     941              :             IF (q0(i_grid) < q_min) THEN
     942              :                q0(i_grid) = q_min
     943              :             END IF
     944              : 
     945              :             !!---------------------------------Final values---------------------------------
     946              :             dw0_dn = 1.0_dp/(2.0_dp*w0)*(16.0_dp/3.0_dp*pi - 4.0_dp*wg2/total_rho(i_grid))
     947              :             dk_dn = k/(6.0_dp*total_rho(i_grid))
     948              : 
     949              :             dq0_drho(i_grid) = dq0_dq*1.0_dp/(k**2.0)*(dw0_dn*k - dk_dn*w0)
     950              :             ! wg2 is proportional to |gradient_rho|**4, so this limit is zero.
     951              :             IF (gmod2 > 0.0_dp) THEN
     952              :                dq0_dgradrho(i_grid) = dq0_dq*1.0_dp/(2.0_dp*k*w0)*4.0_dp*wg2/gmod2
     953              :             END IF
     954              :          END IF
     955              : 
     956              :       END DO
     957              : !$OMP END PARALLEL DO
     958              : 
     959          170 :    END SUBROUTINE get_q0_on_grid_rvv10
     960              : 
     961              : ! **************************************************************************************************
     962              : !> \brief ...
     963              : !> \param total_rho ...
     964              : !> \param gradient_rho ...
     965              : !> \param q0 ...
     966              : !> \param dispersion_env ...
     967              : ! **************************************************************************************************
     968            0 :    SUBROUTINE get_q0_on_grid_eo_vdw(total_rho, gradient_rho, q0, dispersion_env)
     969              : 
     970              :       REAL(dp), INTENT(IN)                               :: total_rho(:), gradient_rho(:, :)
     971              :       REAL(dp), INTENT(OUT)                              :: q0(:)
     972              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
     973              : 
     974              :       INTEGER, PARAMETER                                 :: m_cut = 12
     975              :       REAL(dp), PARAMETER :: LDA_A = 0.031091_dp, LDA_a1 = 0.2137_dp, LDA_b1 = 7.5957_dp, &
     976              :          LDA_b2 = 3.5876_dp, LDA_b3 = 1.6382_dp, LDA_b4 = 0.49294_dp
     977              : 
     978              :       INTEGER                                            :: i_grid
     979              :       REAL(dp)                                           :: exponent, gradient_correction, kF, &
     980              :                                                             LDA_1, LDA_2, q, q__q_cut, q_cut, &
     981              :                                                             q_min, r_s, sqrt_r_s, Z_ab
     982              : 
     983            0 :       q_cut = dispersion_env%q_cut
     984            0 :       q_min = dispersion_env%q_min
     985            0 :       SELECT CASE (dispersion_env%nl_type)
     986              :       CASE DEFAULT
     987            0 :          CPABORT("Unknown vdW-DF functional")
     988              :       CASE (vdw_nl_DRSLL)
     989            0 :          Z_ab = -0.8491_dp
     990              :       CASE (vdw_nl_LMKLL)
     991            0 :          Z_ab = -1.887_dp
     992              :       END SELECT
     993              : 
     994              : !$OMP PARALLEL DO DEFAULT(NONE) &
     995              : !$OMP             SHARED(total_rho, gradient_rho, q0, q_cut, q_min, Z_ab) &
     996              : !$OMP             PRIVATE(exponent, gradient_correction, kF, LDA_1, LDA_2, q, q__q_cut, r_s, sqrt_r_s) &
     997            0 : !$OMP             SCHEDULE(STATIC)
     998              :       DO i_grid = 1, SIZE(total_rho)
     999              :          q0(i_grid) = q_cut
    1000              :          !! This prevents numerical problems.  If the charge density is negative (an
    1001              :          !! unphysical situation), we simply treat it as very small.  In that case,
    1002              :          !! q0 will be very large and will be saturated.  For a saturated q0 the derivative
    1003              :          !! dq0_dq will be 0 so we set q0 = q_cut and dq0_drho = dq0_dgradrho = 0 and go on
    1004              :          !! to the next point.
    1005              :          !! ------------------------------------------------------------------------------------
    1006              :          IF (total_rho(i_grid) < epsr) CYCLE
    1007              :          !! ------------------------------------------------------------------------------------
    1008              :          !! Calculate some intermediate values needed to find q
    1009              :          !! ------------------------------------------------------------------------------------
    1010              :          kF = (3.0_dp*pi*pi*total_rho(i_grid))**(1.0_dp/3.0_dp)
    1011              :          r_s = (3.0_dp/(4.0_dp*pi*total_rho(i_grid)))**(1.0_dp/3.0_dp)
    1012              :          sqrt_r_s = SQRT(r_s)
    1013              : 
    1014              :          gradient_correction = -Z_ab/(36.0_dp*kF*total_rho(i_grid)**2) &
    1015              :                                *(gradient_rho(i_grid, 1)**2 + gradient_rho(i_grid, 2)**2 + gradient_rho(i_grid, 3)**2)
    1016              : 
    1017              :          LDA_1 = 8.0_dp*pi/3.0_dp*(LDA_A*(1.0_dp + LDA_a1*r_s))
    1018              :          LDA_2 = 2.0_dp*LDA_A*(LDA_b1*sqrt_r_s + LDA_b2*r_s + LDA_b3*r_s*sqrt_r_s + LDA_b4*r_s*r_s)
    1019              :          !! ------------------------------------------------------------------------------------
    1020              :          !! This is the q value defined in equations 11 and 12 of DION
    1021              :          !! ---------------------------------------------------------------
    1022              :          q = kF + LDA_1*LOG(1.0_dp + 1.0_dp/LDA_2) + gradient_correction
    1023              : 
    1024              :          !! ---------------------------------------------------------------
    1025              :          !! Here, we calculate q0 by saturating q according to equation 5 of SOLER.  Also, we find
    1026              :          !! the derivative dq0_dq needed for the derivatives dq0_drho and dq0_dgradrh0 discussed below.
    1027              :          !! ---------------------------------------------------------------------------------------
    1028              :          q__q_cut = q/q_cut
    1029              :          CALL calculate_exponent(m_cut, q__q_cut, exponent)
    1030              :          q0(i_grid) = q_cut*(1.0_dp - EXP(-exponent))
    1031              : 
    1032              :          !! ---------------------------------------------------------------------------------------
    1033              :          !! This is to handle a case with q0 too small.  We simply set it to the smallest q value in
    1034              :          !! out q_mesh.  Hopefully this doesn't get used often (ever)
    1035              :          !! ---------------------------------------------------------------------------------------
    1036              :          IF (q0(i_grid) < q_min) THEN
    1037              :             q0(i_grid) = q_min
    1038              :          END IF
    1039              :       END DO
    1040              : !$OMP END PARALLEL DO
    1041              : 
    1042            0 :    END SUBROUTINE get_q0_on_grid_eo_vdw
    1043              : 
    1044              : ! **************************************************************************************************
    1045              : !> \brief ...
    1046              : !> \param total_rho ...
    1047              : !> \param gradient_rho ...
    1048              : !> \param q0 ...
    1049              : !> \param dispersion_env ...
    1050              : ! **************************************************************************************************
    1051            6 :    SUBROUTINE get_q0_on_grid_eo_rvv10(total_rho, gradient_rho, q0, dispersion_env)
    1052              : 
    1053              :       REAL(dp), INTENT(IN)                               :: total_rho(:), gradient_rho(:, :)
    1054              :       REAL(dp), INTENT(OUT)                              :: q0(:)
    1055              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
    1056              : 
    1057              :       INTEGER, PARAMETER                                 :: m_cut = 12
    1058              : 
    1059              :       INTEGER                                            :: i_grid
    1060              :       REAL(dp)                                           :: b_value, C_value, exponent, gmod2, k, q, &
    1061              :                                                             q__q_cut, q_cut, q_min, w0, wg2, wp2
    1062              : 
    1063            6 :       q_cut = dispersion_env%q_cut
    1064            6 :       q_min = dispersion_env%q_min
    1065            6 :       b_value = dispersion_env%b_value
    1066            6 :       C_value = dispersion_env%c_value
    1067              : 
    1068              : !$OMP PARALLEL DO DEFAULT(NONE) &
    1069              : !$OMP             SHARED(total_rho, gradient_rho, q0, q_cut, q_min, b_value, C_value) &
    1070              : !$OMP             PRIVATE(exponent, gmod2, k, q, q__q_cut, w0, wg2, wp2) &
    1071            6 : !$OMP             SCHEDULE(STATIC)
    1072              :       DO i_grid = 1, SIZE(total_rho)
    1073              :          q0(i_grid) = q_cut
    1074              : 
    1075              :          gmod2 = gradient_rho(i_grid, 1)**2 + gradient_rho(i_grid, 2)**2 + gradient_rho(i_grid, 3)**2
    1076              : 
    1077              :          !if (total_rho(i_grid) > epsr .and. gmod2 > epsr) cycle
    1078              :          IF (total_rho(i_grid) > epsr) THEN
    1079              : 
    1080              :             !! Calculate some intermediate values needed to find q
    1081              :             !! ------------------------------------------------------------------------------------
    1082              :             wp2 = 16.0_dp*pi*total_rho(i_grid)
    1083              :             wg2 = 4_dp*C_value*(gmod2*gmod2)/(total_rho(i_grid)**4)
    1084              : 
    1085              :             k = b_value*3.0_dp*pi*((total_rho(i_grid)/(9.0_dp*pi))**(1.0_dp/6.0_dp))
    1086              :             w0 = SQRT(wg2 + wp2/3.0_dp)
    1087              : 
    1088              :             q = w0/k
    1089              : 
    1090              :             !! Here, we calculate q0 by saturating q according
    1091              :             !! ---------------------------------------------------------------------------------------
    1092              :             q__q_cut = q/q_cut
    1093              :             CALL calculate_exponent(m_cut, q__q_cut, exponent)
    1094              :             q0(i_grid) = q_cut*(1.0_dp - EXP(-exponent))
    1095              : 
    1096              :             IF (q0(i_grid) < q_min) THEN
    1097              :                q0(i_grid) = q_min
    1098              :             END IF
    1099              : 
    1100              :          END IF
    1101              : 
    1102              :       END DO
    1103              : !$OMP END PARALLEL DO
    1104              : 
    1105            6 :    END SUBROUTINE get_q0_on_grid_eo_rvv10
    1106              : 
    1107              : ! **************************************************************************************************
    1108              : !> \brief Prepare the spline intervals, weights and density factors for streamed theta construction.
    1109              : !> \param q0 Saturated interpolation points
    1110              : !> \param rho Total density
    1111              : !> \param dispersion_env Non-local functional parameters
    1112              : !> \param q_low Lower spline endpoint; zero denotes an inactive rVV10 point
    1113              : !> \param coeff Cubic spline coefficients a, b, c, d
    1114              : !> \param theta_scale Density factor multiplying the interpolated spline
    1115              : ! **************************************************************************************************
    1116          422 :    SUBROUTINE prepare_splines(q0, rho, dispersion_env, q_low, coeff, theta_scale)
    1117              :       REAL(dp), INTENT(IN)                               :: q0(:), rho(:)
    1118              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
    1119              :       INTEGER, INTENT(OUT)                               :: q_low(:)
    1120              :       REAL(dp), INTENT(OUT)                              :: coeff(:, :), theta_scale(:)
    1121              : 
    1122              :       INTEGER                                            :: i, j, lower, nqs, upper
    1123              :       LOGICAL                                            :: rvv10
    1124              :       REAL(dp)                                           :: a, b, const, dx, dx2_6
    1125              :       REAL(dp), POINTER                                  :: q_mesh(:)
    1126              : 
    1127          422 :       q_mesh => dispersion_env%q_mesh
    1128          422 :       nqs = dispersion_env%nqs
    1129          422 :       CPASSERT(nqs >= 2)
    1130          422 :       rvv10 = dispersion_env%nl_type == vdw_nl_RVV10
    1131          422 :       const = 1.0_dp/(3.0_dp*rootpi*dispersion_env%b_value**1.5_dp)/(pi**0.75_dp)
    1132              : !$OMP PARALLEL DO DEFAULT(NONE) &
    1133              : !$OMP             SHARED(q0, rho, q_mesh, nqs, rvv10, const, q_low, coeff, theta_scale) &
    1134          422 : !$OMP             PRIVATE(j, lower, upper, A, b, dx, dx2_6) SCHEDULE(STATIC)
    1135              :       DO i = 1, SIZE(q0)
    1136              :          IF (rvv10 .AND. rho(i) <= epsr) THEN
    1137              :             q_low(i) = 0
    1138              :             coeff(i, :) = 0.0_dp
    1139              :             theta_scale(i) = 0.0_dp
    1140              :             CYCLE
    1141              :          END IF
    1142              :          lower = 1
    1143              :          upper = nqs
    1144              :          DO WHILE (upper - lower > 1)
    1145              :             j = (upper + lower)/2
    1146              :             IF (q0(i) > q_mesh(j)) THEN
    1147              :                lower = j
    1148              :             ELSE
    1149              :                upper = j
    1150              :             END IF
    1151              :          END DO
    1152              :          q_low(i) = lower
    1153              :          dx = q_mesh(upper) - q_mesh(lower)
    1154              :          dx2_6 = dx*dx/6.0_dp
    1155              :          a = (q_mesh(upper) - q0(i))/dx
    1156              :          b = (q0(i) - q_mesh(lower))/dx
    1157              :          coeff(i, 1) = a
    1158              :          coeff(i, 2) = b
    1159              :          coeff(i, 3) = (a**3 - a)*dx2_6
    1160              :          coeff(i, 4) = (b**3 - b)*dx2_6
    1161              :          IF (rvv10) THEN
    1162              :             theta_scale(i) = const*rho(i)**0.75_dp
    1163              :          ELSE
    1164              :             theta_scale(i) = rho(i)
    1165              :          END IF
    1166              :       END DO
    1167              : !$OMP END PARALLEL DO
    1168          422 :    END SUBROUTINE prepare_splines
    1169              : 
    1170              : ! **************************************************************************************************
    1171              : !> \brief Generate one real-space theta channel directly in the existing FFT workspace.
    1172              : !> \param iq Channel index
    1173              : !> \param q_low Lower spline endpoint
    1174              : !> \param coeff Spline coefficients
    1175              : !> \param theta_scale Density factor
    1176              : !> \param dispersion_env Non-local functional parameters
    1177              : !> \param theta FFT input, viewed as a contiguous one-dimensional array
    1178              : ! **************************************************************************************************
    1179         8440 :    SUBROUTINE build_theta(iq, q_low, coeff, theta_scale, dispersion_env, theta)
    1180              :       INTEGER, INTENT(IN)                                :: iq, q_low(:)
    1181              :       REAL(dp), INTENT(IN)                               :: coeff(:, :), theta_scale(:)
    1182              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
    1183              :       REAL(dp), INTENT(OUT)                              :: theta(:)
    1184              : 
    1185              :       INTEGER                                            :: i, lower
    1186              :       REAL(dp)                                           :: p
    1187              :       REAL(dp), POINTER                                  :: d2y(:, :)
    1188              : 
    1189         8440 :       d2y => dispersion_env%d2y_dx2
    1190              : !$OMP PARALLEL DO SIMD DEFAULT(NONE) &
    1191         8440 : !$OMP             SHARED(iq, q_low, coeff, theta_scale, d2y, theta) PRIVATE(lower, p) SCHEDULE(STATIC)
    1192              :       DO i = 1, SIZE(theta)
    1193              :          lower = q_low(i)
    1194              :          theta(i) = 0.0_dp
    1195              :          IF (lower == 0) CYCLE
    1196              :          p = coeff(i, 1)*MERGE(1.0_dp, 0.0_dp, iq == lower) &
    1197              :              + coeff(i, 2)*MERGE(1.0_dp, 0.0_dp, iq == lower + 1) &
    1198              :              + (coeff(i, 3)*d2y(iq, lower) + coeff(i, 4)*d2y(iq, lower + 1))
    1199              :          theta(i) = p*theta_scale(i)
    1200              :       END DO
    1201              : !$OMP END PARALLEL DO SIMD
    1202         8440 :    END SUBROUTINE build_theta
    1203              : 
    1204              : ! **************************************************************************************************
    1205              : !> \brief Accumulate one inverse-transformed channel without storing a real-space channel matrix.
    1206              : !> \param iq Channel index
    1207              : !> \param q_low Lower spline endpoint, using the potential-side knot convention
    1208              : !> \param dispersion_env Non-local functional parameters
    1209              : !> \param u Inverse FFT output
    1210              : !> \param u_contract Sums against the two second-derivative columns, then the two endpoint values
    1211              : ! **************************************************************************************************
    1212         8320 :    SUBROUTINE accumulate_potential(iq, q_low, dispersion_env, u, u_contract)
    1213              :       INTEGER, INTENT(IN)                                :: iq, q_low(:)
    1214              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
    1215              :       REAL(dp), INTENT(IN)                               :: u(:)
    1216              :       REAL(dp), INTENT(INOUT)                            :: u_contract(:, :)
    1217              : 
    1218              :       INTEGER                                            :: i, lower
    1219              :       REAL(dp), POINTER                                  :: d2y(:, :)
    1220              : 
    1221         8320 :       d2y => dispersion_env%d2y_dx2
    1222              : !$OMP PARALLEL DO SIMD DEFAULT(NONE) &
    1223         8320 : !$OMP             SHARED(iq, q_low, d2y, u, u_contract) PRIVATE(lower) SCHEDULE(STATIC)
    1224              :       DO i = 1, SIZE(u)
    1225              :          lower = q_low(i)
    1226              :          IF (lower == 0) CYCLE
    1227              :          u_contract(i, 1) = u_contract(i, 1) + u(i)*d2y(iq, lower)
    1228              :          u_contract(i, 2) = u_contract(i, 2) + u(i)*d2y(iq, lower + 1)
    1229              :          IF (iq == lower) u_contract(i, 3) = u(i)
    1230              :          IF (iq == lower + 1) u_contract(i, 4) = u(i)
    1231              :       END DO
    1232              : !$OMP END PARALLEL DO SIMD
    1233         8320 :    END SUBROUTINE accumulate_potential
    1234              : 
    1235              : ! **************************************************************************************************
    1236              : !> \brief This routine is modeled after an algorithm from "Numerical Recipes in C" by Cambridge
    1237              : !> University Press, pages 96-97.  It was adapted for Fortran and for the problem at hand.
    1238              : !> \param x ...
    1239              : !> \param d2y_dx2 ...
    1240              : !> \par History
    1241              : !>     OpenMP added: Aug 2016  MTucker
    1242              : ! **************************************************************************************************
    1243           50 :    SUBROUTINE initialize_spline_interpolation(x, d2y_dx2)
    1244              : 
    1245              :       REAL(dp), INTENT(in)                               :: x(:)
    1246              :       REAL(dp), INTENT(inout)                            :: d2y_dx2(:, :)
    1247              : 
    1248              :       INTEGER                                            :: index, Nx, P_i
    1249              :       REAL(dp)                                           :: temp1, temp2
    1250           50 :       REAL(dp), ALLOCATABLE                              :: temp_array(:), y(:)
    1251              : 
    1252           50 :       Nx = SIZE(x)
    1253              : 
    1254              : !$OMP PARALLEL DEFAULT( NONE )                  &
    1255              : !$OMP           SHARED( x, d2y_dx2, Nx )        &
    1256              : !$OMP          PRIVATE( temp_array, y           &
    1257              : !$OMP                 , index, temp1, temp2     &
    1258           50 : !$OMP                 )
    1259              : 
    1260              :       ALLOCATE (temp_array(Nx), y(Nx))
    1261              : 
    1262              : !$OMP DO
    1263              :       DO P_i = 1, Nx
    1264              :          !! In the Soler method, the polynomials that are interpolated are Kronecker delta functions
    1265              :          !! at a particular q point.  So, we set all y values to 0 except the one corresponding to
    1266              :          !! the particular function P_i.
    1267              :          !! ----------------------------------------------------------------------------------------
    1268              :          y = 0.0_dp
    1269              :          y(P_i) = 1.0_dp
    1270              :          !! ----------------------------------------------------------------------------------------
    1271              : 
    1272              :          d2y_dx2(P_i, 1) = 0.0_dp
    1273              :          temp_array(1) = 0.0_dp
    1274              :          DO index = 2, Nx - 1
    1275              :             temp1 = (x(index) - x(index - 1))/(x(index + 1) - x(index - 1))
    1276              :             temp2 = temp1*d2y_dx2(P_i, index - 1) + 2.0_dp
    1277              :             d2y_dx2(P_i, index) = (temp1 - 1.0_dp)/temp2
    1278              :             temp_array(index) = (y(index + 1) - y(index))/(x(index + 1) - x(index)) &
    1279              :                                 - (y(index) - y(index - 1))/(x(index) - x(index - 1))
    1280              :             temp_array(index) = (6.0_dp*temp_array(index)/(x(index + 1) - x(index - 1)) &
    1281              :                                  - temp1*temp_array(index - 1))/temp2
    1282              :          END DO
    1283              :          d2y_dx2(P_i, Nx) = 0.0_dp
    1284              :          DO index = Nx - 1, 1, -1
    1285              :             d2y_dx2(P_i, index) = d2y_dx2(P_i, index)*d2y_dx2(P_i, index + 1) + temp_array(index)
    1286              :          END DO
    1287              :       END DO
    1288              : !$OMP END DO
    1289              : 
    1290              :       DEALLOCATE (temp_array, y)
    1291              : !$OMP END PARALLEL
    1292              : 
    1293           50 :    END SUBROUTINE initialize_spline_interpolation
    1294              : 
    1295              : ! **************************************************************************************************
    1296              : !> \brief Interpolate the symmetric kernel and optionally its radial derivative from packed tables.
    1297              : !> \param k Reciprocal-vector length
    1298              : !> \param kernel_of_k Interpolated kernel matrix
    1299              : !> \param dispersion_env Radial mesh and channel count
    1300              : !> \param kernel_table Packed channel pairs, radial points, and value/second derivative
    1301              : !> \param dkernel_of_dk Optional derivative matrix for the kernel virial
    1302              : ! **************************************************************************************************
    1303       405916 :    SUBROUTINE interpolate_kernel(k, kernel_of_k, dispersion_env, kernel_table, dkernel_of_dk)
    1304              :       REAL(dp), INTENT(IN)                               :: k
    1305              :       REAL(dp), INTENT(OUT)                              :: kernel_of_k(:, :)
    1306              :       TYPE(qs_dispersion_type), POINTER                  :: dispersion_env
    1307              :       REAL(dp), INTENT(IN)                               :: kernel_table(:, 0:, :)
    1308              :       REAL(dp), INTENT(OUT), OPTIONAL                    :: dkernel_of_dk(:, :)
    1309              : 
    1310              :       INTEGER                                            :: ipair, k_i, q1_i, q2_i
    1311              :       LOGICAL                                            :: on_mesh
    1312              :       REAL(dp)                                           :: a, b, c, d, da, db, dc, dd, dk, dk_6, &
    1313              :                                                             value
    1314              : 
    1315       405916 :       dk = dispersion_env%dk
    1316       405916 :       CPASSERT(k < dispersion_env%nr_points*dk)
    1317       405916 :       k_i = INT(k/dk)
    1318       405916 :       on_mesh = MOD(k, dk) == 0.0_dp
    1319       405916 :       a = (dk*(k_i + 1.0_dp) - k)/dk
    1320       405916 :       b = (k - dk*k_i)/dk
    1321       405916 :       c = (a**3 - a)*(dk*dk/6.0_dp)
    1322       405916 :       d = (b**3 - b)*(dk*dk/6.0_dp)
    1323      8524236 :       DO q1_i = 1, dispersion_env%nqs
    1324      8524236 : !$OMP SIMD PRIVATE(ipair, value)
    1325              :          DO q2_i = 1, q1_i
    1326     85242360 :             ipair = q1_i*(q1_i - 1)/2 + q2_i
    1327     85242360 :             IF (on_mesh) THEN
    1328        44310 :                value = kernel_table(ipair, k_i, 1)
    1329              :             ELSE
    1330              :                value = a*kernel_table(ipair, k_i, 1) + b*kernel_table(ipair, k_i + 1, 1) &
    1331     85198050 :                        + (c*kernel_table(ipair, k_i, 2) + d*kernel_table(ipair, k_i + 1, 2))
    1332              :             END IF
    1333     85242360 :             kernel_of_k(q2_i, q1_i) = value
    1334     85242360 :             kernel_of_k(q1_i, q2_i) = value
    1335              :          END DO
    1336              : !$OMP END SIMD
    1337              :       END DO
    1338       405916 :       IF (PRESENT(dkernel_of_dk)) THEN
    1339       299487 :          dk_6 = dk/6.0_dp
    1340       299487 :          da = -1.0_dp/dk
    1341       299487 :          db = 1.0_dp/dk
    1342       299487 :          dc = -(3*a**2 - 1.0_dp)*dk_6
    1343       299487 :          dd = (3*b**2 - 1.0_dp)*dk_6
    1344      6289227 :          DO q1_i = 1, dispersion_env%nqs
    1345      6289227 : !$OMP SIMD PRIVATE(ipair, value)
    1346              :             DO q2_i = 1, q1_i
    1347     62892270 :                ipair = q1_i*(q1_i - 1)/2 + q2_i
    1348              :                value = da*kernel_table(ipair, k_i, 1) + db*kernel_table(ipair, k_i + 1, 1) &
    1349     62892270 :                        + dc*kernel_table(ipair, k_i, 2) + dd*kernel_table(ipair, k_i + 1, 2)
    1350     62892270 :                dkernel_of_dk(q2_i, q1_i) = value
    1351     62892270 :                dkernel_of_dk(q1_i, q2_i) = value
    1352              :             END DO
    1353              : !$OMP END SIMD
    1354              :          END DO
    1355              :       END IF
    1356       405916 :    END SUBROUTINE interpolate_kernel
    1357              : 
    1358              : ! **************************************************************************************************
    1359              : 
    1360              : END MODULE qs_dispersion_nonloc
        

Generated by: LCOV version 2.0-1