radial_grid_types.F90 Source File


This file depends on

sourcefile~~radial_grid_types.f90~~EfferentGraph sourcefile~radial_grid_types.f90 radial_grid_types.F90 sourcefile~precision.f90 precision.F90 sourcefile~radial_grid_types.f90->sourcefile~precision.f90

Files dependent on this one

sourcefile~~radial_grid_types.f90~~AfferentGraph sourcefile~radial_grid_types.f90 radial_grid_types.F90 sourcefile~dft.f90 dft.F90 sourcefile~dft.f90->sourcefile~radial_grid_types.f90 sourcefile~hf_energy.f90 hf_energy.f90 sourcefile~hf_energy.f90->sourcefile~dft.f90 sourcefile~scf.f90 scf.F90 sourcefile~hf_energy.f90->sourcefile~scf.f90 sourcefile~hf_gradient.f90 hf_gradient.F90 sourcefile~hf_gradient.f90->sourcefile~dft.f90 sourcefile~scf.f90->sourcefile~dft.f90 sourcefile~tdhf_energy.f90 tdhf_energy.F90 sourcefile~tdhf_energy.f90->sourcefile~dft.f90 sourcefile~tdhf_gradient.f90 tdhf_gradient.F90 sourcefile~tdhf_gradient.f90->sourcefile~dft.f90 sourcefile~tdhf_z_vector.f90 tdhf_z_vector.F90 sourcefile~tdhf_z_vector.f90->sourcefile~dft.f90

Source Code

module dft_radial_grid_types
  use precision, only: dp
  implicit none

  private
  public :: get_radial_grid
  public :: dft_radial_grid_mhl
  public :: dft_radial_grid_mk3
  public :: dft_radial_grid_ta
  public :: dft_radial_grid_becke
  public :: dft_radial_grid_map_uniform
  public :: dft_radial_grid_map_cheb2

!> No grid
  integer, parameter :: dft_radial_grid_none = -1

!> Murray-Handy-Laming grid
  integer, parameter :: dft_radial_grid_mhl = 0

!> Mura-Knowles Log3 grid
  integer, parameter :: dft_radial_grid_mk3 = 1

!> Treutler-Ahlrichs grid
  integer, parameter :: dft_radial_grid_ta = 2

!> Becke grid
  integer, parameter :: dft_radial_grid_becke = 3

!> Use default mapping
  integer, parameter :: dft_radial_grid_map_default = 0

!> Map the uniform grid
  integer, parameter :: dft_radial_grid_map_uniform = 1

!> Map the Chebyshev 2nd kind grid
  integer, parameter :: dft_radial_grid_map_cheb2 = 2

!> @brief Base class for radial grids calculation
  type, abstract :: rad_grid_transform_t
    real(kind=dp) :: interval(2)
    integer :: map_grid = 0
  contains
    procedure, nopass :: empty => rad_grid_transform_t_empty
    procedure(set_radial_grid), deferred :: set
    procedure, nopass  :: query => base_query
    procedure(int_1d_transformation), deferred :: transform
  end type

  abstract interface
    subroutine set_radial_grid(this, map_grid, opt)
      import
      class(rad_grid_transform_t) :: this
      integer, optional :: map_grid
      real(kind=dp), optional :: opt
    end subroutine

    function int_1d_transformation(this, r) result(rdr)
      import
      class(rad_grid_transform_t) :: this
      real(kind=dp), intent(inout) :: r !< root
      real(kind=dp) :: rdr(2) !< transformed root and the derivative
    end function
  end interface

  type, extends(rad_grid_transform_t) :: mhl_grid
    real(kind=dp) :: mr
  contains
    procedure, pass :: set => mhl_set
    procedure, nopass :: query => mhl_query
    procedure, pass :: transform => mhl_transform
  end type

  type, extends(rad_grid_transform_t) :: mk3_grid
    real(kind=dp) :: alpha0
  contains
    procedure :: set => mk3_set
    procedure, nopass :: query => mk3_query
    procedure :: transform => mk3_transform
  end type

  type, extends(rad_grid_transform_t) :: ta_grid
    real(kind=dp) :: pwr
  contains
    procedure :: set => ta_set
    procedure, nopass :: query => ta_query
    procedure :: transform => ta_transform
  end type

  type, extends(rad_grid_transform_t) :: becke_grid
  contains
    procedure :: set => becke_set
    procedure, nopass :: query => becke_query
    procedure :: transform => becke_transform
  end type

contains

!> @brief Set the radial points and weights
!> @param ptrad [inout]  quadrature points
!> @param wtrad [inout]  quadrature weights
!> @param rad_grid_type [in]  type of radial grid
!> @param rad_grid_type [in]  which quadrature to map on the selected grid
  subroutine get_radial_grid(ptrad, wtrad, nrad, rad_grid_type, map_grid)
    real(kind=dp), intent(inout) :: ptrad(nrad), wtrad(nrad)
    integer, intent(in) :: nrad, rad_grid_type
    integer, intent(in), optional :: map_grid

    real(kind=dp) :: rdr(2)
    integer :: i

    class(rad_grid_transform_t), allocatable :: rad_grid_transform

    select case (rad_grid_type)
    case (dft_radial_grid_mhl)
      allocate (mhl_grid :: rad_grid_transform)
    case (dft_radial_grid_mk3)
      allocate (mk3_grid :: rad_grid_transform)
    case (dft_radial_grid_ta)
      allocate (ta_grid :: rad_grid_transform)
    case (dft_radial_grid_becke)
      allocate (becke_grid :: rad_grid_transform)
!   Unknown grid type
    case default
      write (*, *) 'unknown radial grid type =', rad_grid_type
      call abort
    end select

    call rad_grid_transform%set(map_grid)

    select case (rad_grid_transform%map_grid)
    case (dft_radial_grid_map_uniform)
      call getuniform(nrad, ptrad, wtrad, rad_grid_transform%interval)
    case (dft_radial_grid_map_cheb2)
      call getcheb2(nrad, ptrad, wtrad, rad_grid_transform%interval)
!   Unknown map_grid type
    case default
      write (*, *) 'unknown map grid type =', rad_grid_transform%map_grid
      call abort
    end select

!   Apply variable transformation and scale weights of the original
!   quadrature
    do i = 1, nrad
      rdr = rad_grid_transform%transform(ptrad(i))
      ptrad(i) = rdr(1)
      wtrad(i) = rdr(2)*wtrad(i)
    end do

!   Include spherical r**2 factor
    wtrad = wtrad*ptrad**2

  end subroutine

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

!> @brief  Murray-Handy-Laming grid variable transformation.
!> @detail  This grid is based on the following variable transformation:
!>   ri = R0 * (r / (1-r) )**m_r
!>   to map interval (0, +1) to (0, +inf).
!>   R0 is a scaling coefficient a.k.a. Bragg-Slater radius
  function mhl_transform(this, r) result(rdr)
    class(mhl_grid) :: this
    real(kind=dp), intent(inout) :: r !< root
    real(kind=dp) :: rdr(2)
    rdr(1) = (r/(1.0d0-r))**this%mr ! r
    rdr(2) = this%mr*r**(this%mr-1)/(1.0d0-r)**(this%mr+1) ! dr
  end function

!> @brief  Murray-Handy-Laming grid init
  subroutine mhl_set(this, map_grid, opt)
    class(mhl_grid) :: this
    integer, optional :: map_grid
    real(kind=dp), optional :: opt
!   Murray-Handy-Laming grid parameter `m_r`:
    integer, parameter :: m_r = 2

    this%mr = m_r
    if (present(opt)) this%mr = opt

!   Default uniform grid
    this%map_grid = dft_radial_grid_map_uniform
    if (present(map_grid)) then
        if (map_grid/=dft_radial_grid_map_default) this%map_grid = map_grid
    end if

    this%interval = [0.0d0, 1.0d0]
  end subroutine

  subroutine mhl_query(grid_type, map_default)
    integer, optional :: grid_type
    integer, optional :: map_default
    grid_type = dft_radial_grid_mhl
    map_default = dft_radial_grid_map_uniform
  end subroutine

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

!> @brief  Mura-Knowles Log-3 grid
!> @detail  This grid is based on the following variable transformation:
!>   r_i = -alpha*log(1-(x_i)**3),
!>   Alpha is a scaling factor
!>   In the original paper authors suggest alpha=7.0 for 1st and 2nd groups
!>   of periodic table and alpha=5.0 otherwise. Here different value
!>   is used:
!>   alpha=alpha0*Rbs,
!>   where Rbs is Bragg-Slater radius. Similar approach is used in NWChem.
  function mk3_transform(this, r) result(rdr)
    class(mk3_grid) :: this
    real(kind=dp), intent(inout) :: r !< root
    real(kind=dp) :: rdr(2)
    real(kind=dp) :: y, dy

    y = 1.0d0-r**3
    dy = 3.0d0*r**2
    rdr(1) = -this%alpha0*log(y) ! r
    rdr(2) = this%alpha0*dy/y ! dr
  end function

!> @brief  Mura-Knowles Log-3 grid init
  subroutine mk3_set(this, map_grid, opt)
    class(mk3_grid) :: this
    integer, optional :: map_grid
    real(kind=dp), optional :: opt

    this%alpha0 = 3.95d0
    if (present(opt)) this%alpha0 = opt

!   Default uniform grid
    this%map_grid = dft_radial_grid_map_uniform
    if (present(map_grid)) then
        if (map_grid/=dft_radial_grid_map_default) this%map_grid = map_grid
    end if

    this%interval = [0.0d0, 1.0d0]
  end subroutine

  subroutine mk3_query(grid_type, map_default)
    integer, optional :: grid_type
    integer, optional :: map_default
    grid_type = dft_radial_grid_mhl
    map_default = dft_radial_grid_map_uniform
  end subroutine


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

!> @brief  Treutler-Ahlrichs grid variable transformation.
!> @detail  This grid is based on the following variable transformation:
!>   ri = R0/log(2) * (1+(xi))**ta_pow * log(2/(1-xi))
!>   to map interval (-1, +1) to (0, +inf).
!>   In the original paper, Chebyshev 2nd kind grid is
!>   used. R0 are per-atom scaling coefficients.
  function ta_transform(this, r) result(rdr)
    class(ta_grid) :: this
    real(kind=dp), intent(inout) :: r !< root
    real(kind=dp) :: rdr(2)

    real(kind=dp), parameter :: log2m1 = 1.0d0/log(2.0d0)
    real(kind=dp) :: tpow, tlog

    tpow = (1.0d0+r)**this%pwr
    tlog = log(2.0d0/(1.0d0-r))
    rdr(1) = log2m1*tpow*tlog ! r
    rdr(2) = log2m1*(this%pwr*tpow*tlog/(1.0d0+r)+tpow/(1.0d0-r)) ! dr
  end function

!> @brief  Treutler-Ahlrichs grid init
  subroutine ta_set(this, map_grid, opt)
    class(ta_grid) :: this
    integer, optional :: map_grid
    real(kind=dp), optional :: opt
!   Treutler-Ahlrichs grid parameter:
    real(kind=dp), parameter :: ta_pow = 0.6d0

    this%pwr = ta_pow
    if (present(opt)) this%pwr = opt

!   Default Chebyshev grid
    this%map_grid = dft_radial_grid_map_cheb2
    if (present(map_grid)) then
        if (map_grid/=dft_radial_grid_map_default) this%map_grid = map_grid
    end if

    this%interval = [-1.0d0, 1.0d0]
  end subroutine

  subroutine ta_query(grid_type, map_default)
    integer, optional :: grid_type
    integer, optional :: map_default
    grid_type = dft_radial_grid_mhl
    map_default = dft_radial_grid_map_cheb2
  end subroutine

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

!> @brief  Becke grid variable transformation.
!> @detail  This grid is based on the following variable transformation:
!>   ri = R0*(1+xi)/(1-xi)
!>   to map interval (-1, +1) to (0, +inf).
!>   In the original paper, Chebyshev 2nd kind grid is
!>   used. R0 are per-atom scaling coefficients.
  function becke_transform(this, r) result(rdr)
    class(becke_grid) :: this
    real(kind=dp), intent(inout) :: r !< root
    real(kind=dp) :: rdr(2)

    call this%empty !dummy, avoid warnings

    rdr(1) = (1.0d0+r)/(1.0d0-r) ! r
    rdr(2) = 2.0d0/(1.0d0-r)**2 ! dr
  end function

!> @brief  Becke grid init
  subroutine becke_set(this, map_grid, opt)
    class(becke_grid) :: this
    integer, optional :: map_grid
    real(kind=dp), optional :: opt

    if (present(opt)) continue

!   Default Chebyshev grid
    this%map_grid = dft_radial_grid_map_cheb2
    if (present(map_grid)) then
        if (map_grid/=dft_radial_grid_map_default) this%map_grid = map_grid
    end if

    this%interval = [-1.0d0, 1.0d0]
  end subroutine

  subroutine becke_query(grid_type, map_default)
    integer, optional :: grid_type
    integer, optional :: map_default
    grid_type = dft_radial_grid_mhl
    map_default = dft_radial_grid_map_cheb2
  end subroutine

!----------------------------------------------------------------------
! Helper subroutines
!----------------------------------------------------------------------

!> @brief Get Chebyshev 2nd kind grid for numerical integration
!> @param n [in] order
!> @param r [out] roots
!> @param w [out] weights
!> @param d [in] optional initial interval
  subroutine getcheb2(n, r, w, d)
    integer :: n !< order
    real(kind=dp), intent(out) :: r(:) !< roots
    real(kind=dp), intent(out) :: w(:) !< weights
    real(kind=dp), intent(in), optional :: d(2) !< initial interval
    real(kind=dp), parameter :: d0(2) = [-1.0d0, 1.0d0]
    real(kind=dp), parameter :: pi = 3.141592653589793238d0

    integer :: i

    r = [(cos(pi*(n-i+1.0d0)/(n+1)), i=1, n)] ! to ensure ascending root order
    w = [(pi/(n+1)*sin(pi*(n-i+1.0d0)/(n+1)), i=1, n)]

    if (present(d)) call linear_map(r, w, d0, d)

  end subroutine

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

!> @brief Get uniform grid for numerical integration
!> @param n [in] order
!> @param r [out] roots
!> @param w [out] weights
!> @param d [in] optional initial interval
  subroutine getuniform(n, r, w, d)
    integer :: n !< order
    real(kind=dp), intent(out) :: r(:) !< roots
    real(kind=dp), intent(out) :: w(:) !< weights
    real(kind=dp), intent(in), optional :: d(2) !< initial interval
    real(kind=dp), parameter :: d0(2) = [0.0d0, 1.0d0]

    integer :: i

    r = [(i*1.0d0/(n+1), i=1, n)]
    w = [(1.0d0/(n+1), i=1, n)]

    if (present(d)) call linear_map(r, w, d0, d)

  end subroutine

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

!> @brief Linear map of roots and weights to a new interval
!> @param r [out] roots
!> @param w [out] weights
!> @param d0 [in] interval to map from
!> @param d [in] interval to map to
  subroutine linear_map(r, w, d0, d)
    real(kind=dp), intent(inout) :: r(:) !< roots
    real(kind=dp), intent(inout) :: w(:) !< weights
    real(kind=dp), intent(in) :: d0(2)   !< initial interval
    real(kind=dp), intent(in) :: d(2)    !< new interval

    r = (d(2)-d(1))/(d0(2)-d0(1))*(r-d0(1))+d(1)
    w = (d(2)-d(1))/(d0(2)-d0(1))*w
  end subroutine

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

  subroutine rad_grid_transform_t_empty
  end subroutine

  subroutine base_query(grid_type, map_default)
    integer, optional :: grid_type
    integer, optional :: map_default
    grid_type = dft_radial_grid_none
    map_default = dft_radial_grid_map_default
  end subroutine
end module