mirror of
https://github.com/cp2k/cp2k.git
synced 2026-07-24 03:55:21 -04:00
2754 lines
110 KiB
Fortran
2754 lines
110 KiB
Fortran
!--------------------------------------------------------------------------------------------------!
|
|
! CP2K: A general program to perform molecular dynamics simulations !
|
|
! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
|
|
! !
|
|
! SPDX-License-Identifier: GPL-2.0-or-later !
|
|
!--------------------------------------------------------------------------------------------------!
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Some basic routines for atomic calculations
|
|
!> \author jgh
|
|
!> \date 01.04.2008
|
|
!> \version 1.0
|
|
!>
|
|
! **************************************************************************************************
|
|
MODULE atom_utils
|
|
USE ai_onecenter, ONLY: sg_overlap,&
|
|
sto_overlap
|
|
USE ai_overlap, ONLY: overlap_ab_s,&
|
|
overlap_ab_sp
|
|
USE ao_util, ONLY: exp_radius
|
|
USE atom_types, ONLY: &
|
|
CGTO_BASIS, GTO_BASIS, NUM_BASIS, STO_BASIS, atom_basis_type, atom_gthpot_type, &
|
|
atom_hfx_type, atom_potential_type, atom_state, atom_type, ecp_pseudo, eri, gth_pseudo, &
|
|
lmat, no_pseudo, sgp_pseudo, upf_pseudo
|
|
USE basis_set_types, ONLY: srules
|
|
USE cp_files, ONLY: close_file,&
|
|
get_unit_number,&
|
|
open_file
|
|
USE input_constants, ONLY: do_rhf_atom,&
|
|
do_rks_atom,&
|
|
do_rohf_atom,&
|
|
do_uhf_atom,&
|
|
do_uks_atom
|
|
USE kahan_sum, ONLY: accurate_dot_product
|
|
USE kinds, ONLY: default_string_length,&
|
|
dp
|
|
USE lapack, ONLY: lapack_ssyev
|
|
USE mathconstants, ONLY: dfac,&
|
|
fac,&
|
|
fourpi,&
|
|
maxfac,&
|
|
pi,&
|
|
rootpi
|
|
USE mathlib, ONLY: binomial_gen,&
|
|
invmat_symm
|
|
USE orbital_pointers, ONLY: deallocate_orbital_pointers,&
|
|
init_orbital_pointers
|
|
USE orbital_transformation_matrices, ONLY: deallocate_spherical_harmonics,&
|
|
init_spherical_harmonics
|
|
USE periodic_table, ONLY: nelem,&
|
|
ptable
|
|
USE physcon, ONLY: bohr
|
|
USE qs_grid_atom, ONLY: grid_atom_type
|
|
USE splines, ONLY: spline3ders
|
|
USE string_utilities, ONLY: uppercase
|
|
#include "./base/base_uses.f90"
|
|
|
|
IMPLICIT NONE
|
|
|
|
PRIVATE
|
|
|
|
CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'atom_utils'
|
|
|
|
PUBLIC :: atom_condnumber, atom_completeness, atom_basis_condnum
|
|
PUBLIC :: atom_set_occupation, get_maxl_occ, get_maxn_occ
|
|
PUBLIC :: atom_denmat, atom_density, atom_core_density
|
|
PUBLIC :: integrate_grid, atom_trace, atom_solve
|
|
PUBLIC :: coulomb_potential_numeric, coulomb_potential_analytic
|
|
PUBLIC :: exchange_numeric, exchange_semi_analytic
|
|
PUBLIC :: numpot_matrix, ceri_contract, eeri_contract, err_matrix
|
|
PUBLIC :: slater_density, wigner_slater_functional, atom_local_potential
|
|
PUBLIC :: atom_orbital_charge, atom_orbital_nodes, atom_consistent_method
|
|
PUBLIC :: atom_orbital_max, atom_wfnr0, get_rho0
|
|
PUBLIC :: contract2, contract4, contract2add
|
|
! ZMP added public subroutines
|
|
PUBLIC :: atom_read_external_density
|
|
PUBLIC :: atom_read_external_vxc
|
|
PUBLIC :: atom_read_zmp_restart
|
|
PUBLIC :: atom_write_zmp_restart
|
|
|
|
!-----------------------------------------------------------------------------!
|
|
|
|
INTERFACE integrate_grid
|
|
MODULE PROCEDURE integrate_grid_function1, &
|
|
integrate_grid_function2, &
|
|
integrate_grid_function3
|
|
END INTERFACE
|
|
|
|
! **************************************************************************************************
|
|
|
|
CONTAINS
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Set occupation of atomic orbitals.
|
|
!> \param ostring list of electronic states
|
|
!> \param occupation ...
|
|
!> \param wfnocc ...
|
|
!> \param multiplicity ...
|
|
!> \par History
|
|
!> * 11.2009 unrestricted KS and HF methods [Juerg Hutter]
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_set_occupation(ostring, occupation, wfnocc, multiplicity)
|
|
CHARACTER(LEN=default_string_length), &
|
|
DIMENSION(:), POINTER :: ostring
|
|
REAL(Kind=dp), DIMENSION(0:lmat, 10) :: occupation, wfnocc
|
|
INTEGER, INTENT(OUT), OPTIONAL :: multiplicity
|
|
|
|
CHARACTER(len=2) :: elem
|
|
CHARACTER(LEN=default_string_length) :: pstring
|
|
INTEGER :: i, i1, i2, ielem, is, jd, jf, jp, js, k, &
|
|
l, mult, n, no
|
|
REAL(Kind=dp) :: e0, el, oo
|
|
|
|
occupation = 0._dp
|
|
|
|
CPASSERT(ASSOCIATED(ostring))
|
|
CPASSERT(SIZE(ostring) > 0)
|
|
|
|
no = SIZE(ostring)
|
|
|
|
is = 1
|
|
! look for multiplicity
|
|
mult = -1 !not specified
|
|
IF (is <= no) THEN
|
|
IF (INDEX(ostring(is), "(") /= 0) THEN
|
|
i1 = INDEX(ostring(is), "(")
|
|
i2 = INDEX(ostring(is), ")")
|
|
CPASSERT((i2 - i1 - 1 > 0) .AND. (i2 - i1 - 1 < 3))
|
|
elem = ostring(is) (i1 + 1:i2 - 1)
|
|
IF (INDEX(elem, "HS") /= 0) THEN
|
|
mult = -2 !High spin
|
|
ELSE IF (INDEX(elem, "LS") /= 0) THEN
|
|
mult = -3 !Low spin
|
|
ELSE
|
|
READ (elem, *) mult
|
|
END IF
|
|
is = is + 1
|
|
END IF
|
|
END IF
|
|
|
|
IF (is <= no) THEN
|
|
IF (INDEX(ostring(is), "CORE") /= 0) is = is + 1 !Pseudopotential detected
|
|
END IF
|
|
IF (is <= no) THEN
|
|
IF (INDEX(ostring(is), "none") /= 0) is = is + 1 !no electrons, used with CORE
|
|
END IF
|
|
IF (is <= no) THEN
|
|
IF (INDEX(ostring(is), "[") /= 0) THEN
|
|
! core occupation from element [XX]
|
|
i1 = INDEX(ostring(is), "[")
|
|
i2 = INDEX(ostring(is), "]")
|
|
CPASSERT((i2 - i1 - 1 > 0) .AND. (i2 - i1 - 1 < 3))
|
|
elem = ostring(is) (i1 + 1:i2 - 1)
|
|
ielem = 0
|
|
DO k = 1, nelem
|
|
IF (elem == ptable(k)%symbol) THEN
|
|
ielem = k
|
|
EXIT
|
|
END IF
|
|
END DO
|
|
CPASSERT(ielem /= 0)
|
|
DO l = 0, MIN(lmat, UBOUND(ptable(ielem)%e_conv, 1))
|
|
el = 2._dp*(2._dp*REAL(l, dp) + 1._dp)
|
|
e0 = ptable(ielem)%e_conv(l)
|
|
DO k = 1, 10
|
|
occupation(l, k) = MIN(el, e0)
|
|
e0 = e0 - el
|
|
IF (e0 <= 0._dp) EXIT
|
|
END DO
|
|
END DO
|
|
is = is + 1
|
|
END IF
|
|
|
|
END IF
|
|
|
|
DO i = is, no
|
|
pstring = ostring(i)
|
|
CALL uppercase(pstring)
|
|
js = INDEX(pstring, "S")
|
|
jp = INDEX(pstring, "P")
|
|
jd = INDEX(pstring, "D")
|
|
jf = INDEX(pstring, "F")
|
|
CPASSERT(js + jp + jd + jf > 0)
|
|
IF (js > 0) THEN
|
|
CPASSERT(jp + jd + jf == 0)
|
|
READ (pstring(1:js - 1), *) n
|
|
READ (pstring(js + 1:), *) oo
|
|
CPASSERT(n > 0)
|
|
CPASSERT(oo >= 0._dp)
|
|
CPASSERT(occupation(0, n) == 0)
|
|
occupation(0, n) = oo
|
|
END IF
|
|
IF (jp > 0) THEN
|
|
CPASSERT(js + jd + jf == 0)
|
|
READ (pstring(1:jp - 1), *) n
|
|
READ (pstring(jp + 1:), *) oo
|
|
CPASSERT(n > 1)
|
|
CPASSERT(oo >= 0._dp)
|
|
CPASSERT(occupation(1, n - 1) == 0)
|
|
occupation(1, n - 1) = oo
|
|
END IF
|
|
IF (jd > 0) THEN
|
|
CPASSERT(js + jp + jf == 0)
|
|
READ (pstring(1:jd - 1), *) n
|
|
READ (pstring(jd + 1:), *) oo
|
|
CPASSERT(n > 2)
|
|
CPASSERT(oo >= 0._dp)
|
|
CPASSERT(occupation(2, n - 2) == 0)
|
|
occupation(2, n - 2) = oo
|
|
END IF
|
|
IF (jf > 0) THEN
|
|
CPASSERT(js + jp + jd == 0)
|
|
READ (pstring(1:jf - 1), *) n
|
|
READ (pstring(jf + 1:), *) oo
|
|
CPASSERT(n > 3)
|
|
CPASSERT(oo >= 0._dp)
|
|
CPASSERT(occupation(3, n - 3) == 0)
|
|
occupation(3, n - 3) = oo
|
|
END IF
|
|
|
|
END DO
|
|
|
|
wfnocc = 0._dp
|
|
DO l = 0, lmat
|
|
k = 0
|
|
DO i = 1, 10
|
|
IF (occupation(l, i) /= 0._dp) THEN
|
|
k = k + 1
|
|
wfnocc(l, k) = occupation(l, i)
|
|
END IF
|
|
END DO
|
|
END DO
|
|
|
|
!Check for consistency with multiplicity
|
|
IF (mult /= -1) THEN
|
|
! count open shells
|
|
js = 0
|
|
DO l = 0, lmat
|
|
k = 2*(2*l + 1)
|
|
DO i = 1, 10
|
|
IF (wfnocc(l, i) /= 0._dp .AND. wfnocc(l, i) /= REAL(k, dp)) THEN
|
|
js = js + 1
|
|
i1 = l
|
|
i2 = i
|
|
END IF
|
|
END DO
|
|
END DO
|
|
|
|
IF (js == 0 .AND. mult == -2) mult = 1
|
|
IF (js == 0 .AND. mult == -3) mult = 1
|
|
IF (js == 0) THEN
|
|
CPASSERT(mult == 1)
|
|
END IF
|
|
IF (js == 1) THEN
|
|
l = i1
|
|
i = i2
|
|
k = NINT(wfnocc(l, i))
|
|
IF (k > (2*l + 1)) k = 2*(2*l + 1) - k
|
|
IF (mult == -2) mult = k + 1
|
|
IF (mult == -3) mult = MOD(k, 2) + 1
|
|
CPASSERT(MOD(k + 1 - mult, 2) == 0)
|
|
END IF
|
|
IF (js > 1 .AND. mult /= -2) THEN
|
|
CPASSERT(mult == -2)
|
|
END IF
|
|
|
|
END IF
|
|
|
|
IF (PRESENT(multiplicity)) multiplicity = mult
|
|
|
|
END SUBROUTINE atom_set_occupation
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Return the maximum orbital quantum number of occupied orbitals.
|
|
!> \param occupation ...
|
|
!> \return ...
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE FUNCTION get_maxl_occ(occupation) RESULT(maxl)
|
|
REAL(Kind=dp), DIMENSION(0:lmat, 10), INTENT(IN) :: occupation
|
|
INTEGER :: maxl
|
|
|
|
INTEGER :: l
|
|
|
|
maxl = 0
|
|
DO l = 0, lmat
|
|
IF (SUM(occupation(l, :)) /= 0._dp) maxl = l
|
|
END DO
|
|
|
|
END FUNCTION get_maxl_occ
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Return the maximum principal quantum number of occupied orbitals.
|
|
!> \param occupation ...
|
|
!> \return ...
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE FUNCTION get_maxn_occ(occupation) RESULT(maxn)
|
|
REAL(Kind=dp), DIMENSION(0:lmat, 10), INTENT(IN) :: occupation
|
|
INTEGER, DIMENSION(0:lmat) :: maxn
|
|
|
|
INTEGER :: k, l
|
|
|
|
maxn = 0
|
|
DO l = 0, lmat
|
|
DO k = 1, 10
|
|
IF (occupation(l, k) /= 0._dp) maxn(l) = maxn(l) + 1
|
|
END DO
|
|
END DO
|
|
|
|
END FUNCTION get_maxn_occ
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate a density matrix using atomic orbitals.
|
|
!> \param pmat electron density matrix
|
|
!> \param wfn atomic wavefunctions
|
|
!> \param nbas number of basis functions
|
|
!> \param occ occupation numbers
|
|
!> \param maxl maximum angular momentum to consider
|
|
!> \param maxn maximum principal quantum number for each angular momentum
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE atom_denmat(pmat, wfn, nbas, occ, maxl, maxn)
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(INOUT) :: pmat
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(IN) :: wfn
|
|
INTEGER, DIMENSION(0:lmat), INTENT(IN) :: nbas
|
|
REAL(KIND=dp), DIMENSION(0:, :), INTENT(IN) :: occ
|
|
INTEGER, INTENT(IN) :: maxl
|
|
INTEGER, DIMENSION(0:lmat), INTENT(IN) :: maxn
|
|
|
|
INTEGER :: i, j, k, l, n
|
|
|
|
pmat = 0._dp
|
|
n = SIZE(wfn, 2)
|
|
DO l = 0, maxl
|
|
DO i = 1, MIN(n, maxn(l))
|
|
DO k = 1, nbas(l)
|
|
DO j = 1, nbas(l)
|
|
pmat(j, k, l) = pmat(j, k, l) + occ(l, i)*wfn(j, i, l)*wfn(k, i, l)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END DO
|
|
|
|
END SUBROUTINE atom_denmat
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Map the electron density on an atomic radial grid.
|
|
!> \param density computed electron density
|
|
!> \param pmat electron density matrix
|
|
!> \param basis atomic basis set
|
|
!> \param maxl maximum angular momentum to consider
|
|
!> \param typ type of the matrix to map:
|
|
!> RHO -- density matrix;
|
|
!> DER -- first derivatives of the electron density;
|
|
!> KIN -- kinetic energy density;
|
|
!> LAP -- second derivatives of the electron density.
|
|
!> \param rr abscissa on the radial grid (required for typ == 'KIN')
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_density(density, pmat, basis, maxl, typ, rr)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: density
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(IN) :: pmat
|
|
TYPE(atom_basis_type), INTENT(IN) :: basis
|
|
INTEGER, INTENT(IN) :: maxl
|
|
CHARACTER(LEN=*), OPTIONAL :: typ
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL :: rr
|
|
|
|
CHARACTER(LEN=3) :: my_typ
|
|
INTEGER :: i, j, l, n
|
|
REAL(KIND=dp) :: ff
|
|
|
|
my_typ = "RHO"
|
|
IF (PRESENT(typ)) my_typ = typ(1:3)
|
|
IF (my_typ == "KIN") THEN
|
|
CPASSERT(PRESENT(rr))
|
|
END IF
|
|
|
|
density = 0._dp
|
|
DO l = 0, maxl
|
|
n = basis%nbas(l)
|
|
DO i = 1, n
|
|
DO j = i, n
|
|
ff = pmat(i, j, l)
|
|
IF (i /= j) ff = 2._dp*pmat(i, j, l)
|
|
IF (my_typ == "RHO") THEN
|
|
density(:) = density(:) + ff*basis%bf(:, i, l)*basis%bf(:, j, l)
|
|
ELSE IF (my_typ == "DER") THEN
|
|
density(:) = density(:) + ff*basis%dbf(:, i, l)*basis%bf(:, j, l) &
|
|
+ ff*basis%bf(:, i, l)*basis%dbf(:, j, l)
|
|
ELSE IF (my_typ == "KIN") THEN
|
|
density(:) = density(:) + 0.5_dp*ff*( &
|
|
basis%dbf(:, i, l)*basis%dbf(:, j, l) + &
|
|
REAL(l*(l + 1), dp)*basis%bf(:, i, l)*basis%bf(:, j, l)/rr(:))
|
|
ELSE IF (my_typ == "LAP") THEN
|
|
density(:) = density(:) + ff*basis%ddbf(:, i, l)*basis%bf(:, j, l) &
|
|
+ ff*basis%bf(:, i, l)*basis%ddbf(:, j, l) &
|
|
+ 2._dp*ff*basis%dbf(:, i, l)*basis%bf(:, j, l)/rr(:) &
|
|
+ 2._dp*ff*basis%bf(:, i, l)*basis%dbf(:, j, l)/rr(:)
|
|
ELSE
|
|
CPABORT("")
|
|
END IF
|
|
END DO
|
|
END DO
|
|
END DO
|
|
! this factor from the product of two spherical harmonics
|
|
density = density/fourpi
|
|
|
|
END SUBROUTINE atom_density
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ZMP subroutine to write external restart file.
|
|
!> \param atom information about the atomic kind
|
|
!> \date 07.10.2013
|
|
!> \author D. Varsano [daniele.varsano@nano.cnr.it]
|
|
!> \version 1.0
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_write_zmp_restart(atom)
|
|
TYPE(atom_type), INTENT(IN) :: atom
|
|
|
|
INTEGER :: extunit, i, k, l, n
|
|
|
|
extunit = get_unit_number()
|
|
CALL open_file(file_name=atom%zmp_restart_file, file_status="UNKNOWN", &
|
|
file_form="FORMATTED", file_action="WRITE", &
|
|
unit_number=extunit)
|
|
|
|
n = SIZE(atom%orbitals%wfn, 2)
|
|
WRITE (extunit, *) atom%basis%nbas
|
|
DO l = 0, atom%state%maxl_occ
|
|
DO i = 1, MIN(n, atom%state%maxn_occ(l))
|
|
DO k = 1, atom%basis%nbas(l)
|
|
WRITE (extunit, *) atom%orbitals%wfn(k, i, l)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
|
|
CALL close_file(unit_number=extunit)
|
|
|
|
END SUBROUTINE atom_write_zmp_restart
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ZMP subroutine to read external restart file.
|
|
!> \param atom information about the atomic kind
|
|
!> \param doguess flag that indicates that the restart file has not been read,
|
|
!> so the initial guess is required
|
|
!> \param iw output file unit
|
|
!> \date 07.10.2013
|
|
!> \author D. Varsano [daniele.varsano@nano.cnr.it]
|
|
!> \version 1.0
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_read_zmp_restart(atom, doguess, iw)
|
|
TYPE(atom_type), INTENT(INOUT) :: atom
|
|
LOGICAL, INTENT(INOUT) :: doguess
|
|
INTEGER, INTENT(IN) :: iw
|
|
|
|
INTEGER :: er, extunit, i, k, l, maxl, n
|
|
INTEGER, DIMENSION(0:lmat) :: maxn, nbas
|
|
|
|
INQUIRE (file=atom%zmp_restart_file, exist=atom%doread)
|
|
|
|
IF (atom%doread) THEN
|
|
WRITE (iw, fmt="(' ZMP | Restart file found')")
|
|
extunit = get_unit_number()
|
|
CALL open_file(file_name=atom%zmp_restart_file, file_status="OLD", &
|
|
file_form="FORMATTED", file_action="READ", &
|
|
unit_number=extunit)
|
|
|
|
READ (extunit, *, IOSTAT=er) nbas
|
|
|
|
IF (er .NE. 0) THEN
|
|
WRITE (iw, fmt="(' ZMP | ERROR! Restart file unreadable')")
|
|
WRITE (iw, fmt="(' ZMP | ERROR! Starting ZMP calculation form initial atomic guess')")
|
|
doguess = .TRUE.
|
|
atom%doread = .FALSE.
|
|
ELSE IF (nbas(1) .NE. atom%basis%nbas(1)) THEN
|
|
WRITE (iw, fmt="(' ZMP | ERROR! Restart file contains a different basis set')")
|
|
WRITE (iw, fmt="(' ZMP | ERROR! Starting ZMP calculation form initial atomic guess')")
|
|
doguess = .TRUE.
|
|
atom%doread = .FALSE.
|
|
ELSE
|
|
nbas = atom%basis%nbas
|
|
maxl = atom%state%maxl_occ
|
|
maxn = atom%state%maxn_occ
|
|
n = SIZE(atom%orbitals%wfn, 2)
|
|
DO l = 0, atom%state%maxl_occ
|
|
DO i = 1, MIN(n, atom%state%maxn_occ(l))
|
|
DO k = 1, atom%basis%nbas(l)
|
|
READ (extunit, *) atom%orbitals%wfn(k, i, l)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
doguess = .FALSE.
|
|
END IF
|
|
CALL close_file(unit_number=extunit)
|
|
ELSE
|
|
WRITE (iw, fmt="(' ZMP | WARNING! Restart file not found')")
|
|
WRITE (iw, fmt="(' ZMP | WARNING! Starting ZMP calculation form initial atomic guess')")
|
|
END IF
|
|
END SUBROUTINE atom_read_zmp_restart
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ZMP subroutine to read external density from linear grid of density matrix.
|
|
!> \param density external density
|
|
!> \param atom information about the atomic kind
|
|
!> \param iw output file unit
|
|
!> \date 07.10.2013
|
|
!> \author D. Varsano [daniele.varsano@nano.cnr.it]
|
|
!> \version 1.0
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_read_external_density(density, atom, iw)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: density
|
|
TYPE(atom_type), INTENT(INOUT) :: atom
|
|
INTEGER, INTENT(IN) :: iw
|
|
|
|
CHARACTER(LEN=default_string_length) :: filename
|
|
INTEGER :: extunit, ir, j, k, l, maxl_occ, maxnbas, &
|
|
nbas, nr
|
|
LOGICAL :: ldm
|
|
REAL(KIND=dp) :: rr
|
|
REAL(KIND=dp), ALLOCATABLE :: pmatread(:, :, :)
|
|
|
|
filename = atom%ext_file
|
|
ldm = atom%dm
|
|
extunit = get_unit_number()
|
|
|
|
CALL open_file(file_name=filename, file_status="OLD", &
|
|
file_form="FORMATTED", file_action="READ", &
|
|
unit_number=extunit)
|
|
|
|
IF (.NOT. ldm) THEN
|
|
READ (extunit, *) nr
|
|
|
|
IF (nr .NE. atom%basis%grid%nr) THEN
|
|
IF (iw > 0) WRITE (iw, fmt="(' ZMP | ERROR! External grid dimension ',I4,' differs from internal grid ',I4)") &
|
|
nr, atom%basis%grid%nr
|
|
IF (iw > 0) WRITE (iw, fmt="(' ZMP | ERROR! Stopping ZMP calculation')")
|
|
CPABORT("")
|
|
END IF
|
|
|
|
DO ir = 1, nr
|
|
READ (extunit, *) rr, density(ir)
|
|
IF (ABS(rr - atom%basis%grid%rad(ir)) .GT. atom%zmpgrid_tol) THEN
|
|
IF (iw > 0) WRITE (iw, fmt="(' ZMP | ERROR! Grid points do not coincide: ')")
|
|
IF (iw > 0) WRITE (iw, fmt='(" ZMP |",T20,"R_out[bohr]",T36,"R_in[bohr]",T61,"R_diff[bohr]")')
|
|
IF (iw > 0) WRITE (iw, fmt='(" ZMP |",T14,E24.15,T39,E24.15,T64,E24.15)') &
|
|
rr, atom%basis%grid%rad(ir), ABS(rr - atom%basis%grid%rad(ir))
|
|
CPABORT("")
|
|
END IF
|
|
END DO
|
|
CALL close_file(unit_number=extunit)
|
|
ELSE
|
|
READ (extunit, *) maxl_occ
|
|
maxnbas = MAXVAL(atom%basis%nbas)
|
|
ALLOCATE (pmatread(maxnbas, maxnbas, 0:maxl_occ))
|
|
pmatread = 0.0
|
|
DO l = 0, maxl_occ
|
|
nbas = atom%basis%nbas(l)
|
|
READ (extunit, *) ! Read empty line
|
|
DO k = 1, nbas
|
|
READ (extunit, *) (pmatread(j, k, l), j=1, k)
|
|
DO j = 1, k
|
|
pmatread(k, j, l) = pmatread(j, k, l)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
|
|
CALL close_file(unit_number=extunit)
|
|
|
|
CALL atom_density(density, pmatread, atom%basis, maxl_occ, typ="RHO")
|
|
|
|
extunit = get_unit_number()
|
|
CALL open_file(file_name="rho_target.dat", file_status="UNKNOWN", &
|
|
file_form="FORMATTED", file_action="WRITE", unit_number=extunit)
|
|
|
|
IF (iw > 0) WRITE (iw, fmt="(' ZMP | Writing target density from density matrix')")
|
|
|
|
WRITE (extunit, fmt='("# Target density built from density matrix : ",A20)') filename
|
|
WRITE (extunit, fmt='("#",T10,"R[bohr]",T36,"Rho[au]")')
|
|
|
|
nr = atom%basis%grid%nr
|
|
|
|
DO ir = 1, nr
|
|
WRITE (extunit, fmt='(T1,E24.15,T26,E24.15)') &
|
|
atom%basis%grid%rad(ir), density(ir)
|
|
END DO
|
|
DEALLOCATE (pmatread)
|
|
|
|
CALL close_file(unit_number=extunit)
|
|
|
|
END IF
|
|
|
|
END SUBROUTINE atom_read_external_density
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ZMP subroutine to read external v_xc in the atomic code.
|
|
!> \param vxc external exchange-correlation potential
|
|
!> \param atom information about the atomic kind
|
|
!> \param iw output file unit
|
|
!> \author D. Varsano [daniele.varsano@nano.cnr.it]
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_read_external_vxc(vxc, atom, iw)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: vxc
|
|
TYPE(atom_type), INTENT(INOUT) :: atom
|
|
INTEGER, INTENT(IN) :: iw
|
|
|
|
CHARACTER(LEN=default_string_length) :: adum, filename
|
|
INTEGER :: extunit, ir, nr
|
|
REAL(KIND=dp) :: rr
|
|
|
|
filename = atom%ext_vxc_file
|
|
extunit = get_unit_number()
|
|
|
|
CALL open_file(file_name=filename, file_status="OLD", &
|
|
file_form="FORMATTED", file_action="READ", &
|
|
unit_number=extunit)
|
|
|
|
READ (extunit, *)
|
|
READ (extunit, *) adum, nr
|
|
|
|
IF (nr .NE. atom%basis%grid%nr) THEN
|
|
IF (iw > 0) WRITE (iw, fmt="(' ZMP | ERROR! External grid dimension ',I4,' differs from internal grid ',I4)") &
|
|
nr, atom%basis%grid%nr
|
|
IF (iw > 0) WRITE (iw, fmt="(' ZMP | ERROR! Stopping ZMP calculation')")
|
|
CPABORT("")
|
|
END IF
|
|
DO ir = 1, nr
|
|
READ (extunit, *) rr, vxc(ir)
|
|
IF (ABS(rr - atom%basis%grid%rad(ir)) .GT. atom%zmpvxcgrid_tol) THEN
|
|
IF (iw > 0) WRITE (iw, fmt="(' ZMP | ERROR! Grid points do not coincide: ')")
|
|
IF (iw > 0) WRITE (iw, fmt='(" ZMP |",T20,"R_out[bohr]",T36,"R_in[bohr]",T61,"R_diff[bohr]")')
|
|
IF (iw > 0) WRITE (iw, fmt='(" ZMP |",T14,E24.15,T39,E24.15,T64,E24.15)') &
|
|
rr, atom%basis%grid%rad(ir), ABS(rr - atom%basis%grid%rad(ir))
|
|
CPABORT("")
|
|
END IF
|
|
END DO
|
|
|
|
END SUBROUTINE atom_read_external_vxc
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param charge ...
|
|
!> \param wfn ...
|
|
!> \param rcov ...
|
|
!> \param l ...
|
|
!> \param basis ...
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE atom_orbital_charge(charge, wfn, rcov, l, basis)
|
|
REAL(KIND=dp), INTENT(OUT) :: charge
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: wfn
|
|
REAL(KIND=dp), INTENT(IN) :: rcov
|
|
INTEGER, INTENT(IN) :: l
|
|
TYPE(atom_basis_type), INTENT(IN) :: basis
|
|
|
|
INTEGER :: i, j, m, n
|
|
REAL(KIND=dp) :: ff
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: den
|
|
|
|
charge = 0._dp
|
|
m = SIZE(basis%bf, 1)
|
|
ALLOCATE (den(m))
|
|
n = basis%nbas(l)
|
|
den = 0._dp
|
|
DO i = 1, n
|
|
DO j = 1, n
|
|
ff = wfn(i)*wfn(j)
|
|
den(1:m) = den(1:m) + ff*basis%bf(1:m, i, l)*basis%bf(1:m, j, l)
|
|
END DO
|
|
END DO
|
|
DO i = 1, m
|
|
IF (basis%grid%rad(i) > rcov) den(i) = 0._dp
|
|
END DO
|
|
charge = SUM(den(1:m)*basis%grid%wr(1:m))
|
|
DEALLOCATE (den)
|
|
|
|
END SUBROUTINE atom_orbital_charge
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param corden ...
|
|
!> \param potential ...
|
|
!> \param typ ...
|
|
!> \param rr ...
|
|
!> \par History
|
|
!> * 01.2017 rewritten [Juerg Hutter]
|
|
!> * 03.2010 extension of GTH pseudo-potential definition [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_core_density(corden, potential, typ, rr)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: corden
|
|
TYPE(atom_potential_type), INTENT(IN) :: potential
|
|
CHARACTER(LEN=*), OPTIONAL :: typ
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: rr
|
|
|
|
CHARACTER(LEN=3) :: my_typ
|
|
INTEGER :: i, j, m, n
|
|
LOGICAL :: reverse
|
|
REAL(KIND=dp) :: a, a2, cval, fb
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: fe, rc, rhoc, rval
|
|
|
|
my_typ = "RHO"
|
|
IF (PRESENT(typ)) my_typ = typ(1:3)
|
|
|
|
SELECT CASE (potential%ppot_type)
|
|
CASE (no_pseudo, ecp_pseudo)
|
|
! we do nothing
|
|
CASE (gth_pseudo)
|
|
IF (potential%gth_pot%nlcc) THEN
|
|
m = SIZE(corden)
|
|
ALLOCATE (fe(m), rc(m))
|
|
n = potential%gth_pot%nexp_nlcc
|
|
DO i = 1, n
|
|
a = potential%gth_pot%alpha_nlcc(i)
|
|
a2 = a*a
|
|
! note that all terms are computed with rc, not rr
|
|
rc(:) = rr(:)/a
|
|
fe(:) = EXP(-0.5_dp*rc(:)*rc(:))
|
|
DO j = 1, potential%gth_pot%nct_nlcc(i)
|
|
cval = potential%gth_pot%cval_nlcc(j, i)
|
|
IF (my_typ == "RHO") THEN
|
|
corden(:) = corden(:) + fe(:)*rc**(2*j - 2)*cval
|
|
ELSE IF (my_typ == "DER") THEN
|
|
corden(:) = corden(:) - fe(:)*rc**(2*j - 1)*cval/a
|
|
IF (j > 1) THEN
|
|
corden(:) = corden(:) + REAL(2*j - 2, dp)*fe(:)*rc**(2*j - 3)*cval/a
|
|
END IF
|
|
ELSE IF (my_typ == "LAP") THEN
|
|
fb = 2._dp*cval/a
|
|
corden(:) = corden(:) - fb*fe(:)/rr(:)*rc**(2*j - 1)
|
|
corden(:) = corden(:) + fe(:)*rc**(2*j)*cval/a2
|
|
IF (j > 1) THEN
|
|
corden(:) = corden(:) + fb*REAL(2*j - 2, dp)*fe(:)/rr(:)*rc**(2*j - 3)
|
|
corden(:) = corden(:) + REAL((2*j - 2)*(2*j - 3), dp)*fe(:)*rc**(2*j - 4)*cval/a2
|
|
corden(:) = corden(:) - REAL(2*j - 2, dp)*fe(:)*rc**(2*j - 2)*cval/a2
|
|
END IF
|
|
ELSE
|
|
CPABORT("")
|
|
END IF
|
|
END DO
|
|
END DO
|
|
DEALLOCATE (fe, rc)
|
|
END IF
|
|
CASE (upf_pseudo)
|
|
IF (potential%upf_pot%core_correction) THEN
|
|
m = SIZE(corden)
|
|
n = potential%upf_pot%mesh_size
|
|
reverse = .FALSE.
|
|
IF (rr(1) > rr(m)) reverse = .TRUE.
|
|
ALLOCATE (rhoc(m), rval(m))
|
|
IF (reverse) THEN
|
|
DO i = 1, m
|
|
rval(i) = rr(m - i + 1)
|
|
END DO
|
|
ELSE
|
|
rval(1:m) = rr(1:m)
|
|
END IF
|
|
IF (my_typ == "RHO") THEN
|
|
CALL spline3ders(potential%upf_pot%r(1:n), potential%upf_pot%rho_nlcc(1:n), rval(1:m), &
|
|
ynew=rhoc(1:m))
|
|
ELSE IF (my_typ == "DER") THEN
|
|
CALL spline3ders(potential%upf_pot%r(1:n), potential%upf_pot%rho_nlcc(1:n), rval(1:m), &
|
|
dynew=rhoc(1:m))
|
|
ELSE IF (my_typ == "LAP") THEN
|
|
CALL spline3ders(potential%upf_pot%r(1:n), potential%upf_pot%rho_nlcc(1:n), rval(1:m), &
|
|
d2ynew=rhoc(1:m))
|
|
ELSE
|
|
CPABORT("")
|
|
END IF
|
|
IF (reverse) THEN
|
|
DO i = 1, m
|
|
rval(i) = rr(m - i + 1)
|
|
corden(i) = corden(i) + rhoc(m - i + 1)
|
|
END DO
|
|
ELSE
|
|
corden(1:m) = corden(1:m) + rhoc(1:m)
|
|
END IF
|
|
DEALLOCATE (rhoc, rval)
|
|
END IF
|
|
CASE (sgp_pseudo)
|
|
IF (potential%sgp_pot%has_nlcc) THEN
|
|
CPABORT("not implemented")
|
|
END IF
|
|
CASE DEFAULT
|
|
CPABORT("Unknown PP type")
|
|
END SELECT
|
|
|
|
END SUBROUTINE atom_core_density
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param locpot ...
|
|
!> \param gthpot ...
|
|
!> \param rr ...
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE atom_local_potential(locpot, gthpot, rr)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: locpot
|
|
TYPE(atom_gthpot_type), INTENT(IN) :: gthpot
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: rr
|
|
|
|
INTEGER :: i, j, m, n
|
|
REAL(KIND=dp) :: a
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: fe, rc
|
|
|
|
m = SIZE(locpot)
|
|
ALLOCATE (fe(m), rc(m))
|
|
rc(:) = rr(:)/gthpot%rc
|
|
DO i = 1, m
|
|
locpot(i) = -gthpot%zion*erf(rc(i)/SQRT(2._dp))/rr(i)
|
|
END DO
|
|
n = gthpot%ncl
|
|
fe(:) = EXP(-0.5_dp*rc(:)*rc(:))
|
|
DO i = 1, n
|
|
locpot(:) = locpot(:) + fe(:)*rc**(2*i - 2)*gthpot%cl(i)
|
|
END DO
|
|
IF (gthpot%lpotextended) THEN
|
|
DO j = 1, gthpot%nexp_lpot
|
|
a = gthpot%alpha_lpot(j)
|
|
rc(:) = rr(:)/a
|
|
fe(:) = EXP(-0.5_dp*rc(:)*rc(:))
|
|
n = gthpot%nct_lpot(j)
|
|
DO i = 1, n
|
|
locpot(:) = locpot(:) + fe(:)*rc**(2*i - 2)*gthpot%cval_lpot(i, j)
|
|
END DO
|
|
END DO
|
|
END IF
|
|
DEALLOCATE (fe, rc)
|
|
|
|
END SUBROUTINE atom_local_potential
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param rmax ...
|
|
!> \param wfn ...
|
|
!> \param rcov ...
|
|
!> \param l ...
|
|
!> \param basis ...
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE atom_orbital_max(rmax, wfn, rcov, l, basis)
|
|
REAL(KIND=dp), INTENT(OUT) :: rmax
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: wfn
|
|
REAL(KIND=dp), INTENT(IN) :: rcov
|
|
INTEGER, INTENT(IN) :: l
|
|
TYPE(atom_basis_type), INTENT(IN) :: basis
|
|
|
|
INTEGER :: i, m, n
|
|
REAL(KIND=dp) :: ff
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: dorb
|
|
|
|
m = SIZE(basis%bf, 1)
|
|
ALLOCATE (dorb(m))
|
|
n = basis%nbas(l)
|
|
dorb = 0._dp
|
|
DO i = 1, n
|
|
ff = wfn(i)
|
|
dorb(1:m) = dorb(1:m) + ff*basis%dbf(1:m, i, l)
|
|
END DO
|
|
rmax = -1._dp
|
|
DO i = 1, m - 1
|
|
IF (basis%grid%rad(i) < 2*rcov) THEN
|
|
IF (dorb(i)*dorb(i + 1) < 0._dp) THEN
|
|
rmax = MAX(rmax, basis%grid%rad(i))
|
|
END IF
|
|
END IF
|
|
END DO
|
|
DEALLOCATE (dorb)
|
|
|
|
END SUBROUTINE atom_orbital_max
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param node ...
|
|
!> \param wfn ...
|
|
!> \param rcov ...
|
|
!> \param l ...
|
|
!> \param basis ...
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE atom_orbital_nodes(node, wfn, rcov, l, basis)
|
|
INTEGER, INTENT(OUT) :: node
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: wfn
|
|
REAL(KIND=dp), INTENT(IN) :: rcov
|
|
INTEGER, INTENT(IN) :: l
|
|
TYPE(atom_basis_type), INTENT(IN) :: basis
|
|
|
|
INTEGER :: i, m, n
|
|
REAL(KIND=dp) :: ff
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: orb
|
|
|
|
node = 0
|
|
m = SIZE(basis%bf, 1)
|
|
ALLOCATE (orb(m))
|
|
n = basis%nbas(l)
|
|
orb = 0._dp
|
|
DO i = 1, n
|
|
ff = wfn(i)
|
|
orb(1:m) = orb(1:m) + ff*basis%bf(1:m, i, l)
|
|
END DO
|
|
DO i = 1, m - 1
|
|
IF (basis%grid%rad(i) < rcov) THEN
|
|
IF (orb(i)*orb(i + 1) < 0._dp) node = node + 1
|
|
END IF
|
|
END DO
|
|
DEALLOCATE (orb)
|
|
|
|
END SUBROUTINE atom_orbital_nodes
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param value ...
|
|
!> \param wfn ...
|
|
!> \param basis ...
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE atom_wfnr0(value, wfn, basis)
|
|
REAL(KIND=dp), INTENT(OUT) :: value
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: wfn
|
|
TYPE(atom_basis_type), INTENT(IN) :: basis
|
|
|
|
INTEGER :: i, m, n
|
|
|
|
value = 0._dp
|
|
m = MAXVAL(MINLOC(basis%grid%rad))
|
|
n = basis%nbas(0)
|
|
DO i = 1, n
|
|
value = value + wfn(i)*basis%bf(m, i, 0)
|
|
END DO
|
|
END SUBROUTINE atom_wfnr0
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Solve the generalised eigenproblem for every angular momentum.
|
|
!> \param hmat Hamiltonian matrix
|
|
!> \param umat transformation matrix which reduces the overlap matrix to its unitary form
|
|
!> \param orb atomic wavefunctions
|
|
!> \param ener atomic orbital energies
|
|
!> \param nb number of contracted basis functions
|
|
!> \param nv number of linear-independent contracted basis functions
|
|
!> \param maxl maximum angular momentum to consider
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_solve(hmat, umat, orb, ener, nb, nv, maxl)
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(IN) :: hmat, umat
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(INOUT) :: orb
|
|
REAL(KIND=dp), DIMENSION(:, 0:), INTENT(INOUT) :: ener
|
|
INTEGER, DIMENSION(0:), INTENT(IN) :: nb, nv
|
|
INTEGER, INTENT(IN) :: maxl
|
|
|
|
INTEGER :: info, l, lwork, m, n
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: w, work
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: a
|
|
|
|
CPASSERT(ALL(nb >= nv))
|
|
|
|
orb = 0._dp
|
|
DO l = 0, maxl
|
|
n = nb(l)
|
|
m = nv(l)
|
|
IF (n > 0 .AND. m > 0) THEN
|
|
lwork = 10*m
|
|
ALLOCATE (a(n, n), w(n), work(lwork))
|
|
a(1:m, 1:m) = MATMUL(TRANSPOSE(umat(1:n, 1:m, l)), MATMUL(hmat(1:n, 1:n, l), umat(1:n, 1:m, l)))
|
|
CALL lapack_ssyev("V", "U", m, a(1:m, 1:m), m, w(1:m), work, lwork, info)
|
|
a(1:n, 1:m) = MATMUL(umat(1:n, 1:m, l), a(1:m, 1:m))
|
|
|
|
m = MIN(m, SIZE(orb, 2))
|
|
orb(1:n, 1:m, l) = a(1:n, 1:m)
|
|
ener(1:m, l) = w(1:m)
|
|
|
|
DEALLOCATE (a, w, work)
|
|
END IF
|
|
END DO
|
|
|
|
END SUBROUTINE atom_solve
|
|
|
|
! **************************************************************************************************
|
|
!> \brief THIS FUNCTION IS NO LONGER IN USE.
|
|
!> \param fun ...
|
|
!> \param deps ...
|
|
!> \return ...
|
|
! **************************************************************************************************
|
|
FUNCTION prune_grid(fun, deps) RESULT(nc)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: fun
|
|
REAL(KIND=dp), INTENT(IN), OPTIONAL :: deps
|
|
INTEGER :: nc
|
|
|
|
INTEGER :: i, nr
|
|
REAL(KIND=dp) :: meps
|
|
|
|
meps = 1.e-14_dp
|
|
IF (PRESENT(deps)) meps = deps
|
|
|
|
nr = SIZE(fun)
|
|
nc = 0
|
|
DO i = nr, 1, -1
|
|
IF (ABS(fun(i)) > meps) THEN
|
|
nc = i
|
|
EXIT
|
|
END IF
|
|
END DO
|
|
|
|
END FUNCTION prune_grid
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Integrate a function on an atomic radial grid.
|
|
!> \param fun function to integrate
|
|
!> \param grid atomic radial grid
|
|
!> \return value of the integral
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE FUNCTION integrate_grid_function1(fun, grid) RESULT(integral)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: fun
|
|
TYPE(grid_atom_type), INTENT(IN) :: grid
|
|
REAL(KIND=dp) :: integral
|
|
|
|
INTEGER :: nc
|
|
|
|
nc = SIZE(fun)
|
|
integral = SUM(fun(1:nc)*grid%wr(1:nc))
|
|
|
|
END FUNCTION integrate_grid_function1
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Integrate the product of two functions on an atomic radial grid.
|
|
!> \param fun1 first function
|
|
!> \param fun2 second function
|
|
!> \param grid atomic radial grid
|
|
!> \return value of the integral
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE FUNCTION integrate_grid_function2(fun1, fun2, grid) RESULT(integral)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: fun1, fun2
|
|
TYPE(grid_atom_type), INTENT(IN) :: grid
|
|
REAL(KIND=dp) :: integral
|
|
|
|
INTEGER :: nc
|
|
|
|
nc = MIN(SIZE(fun1), SIZE(fun2))
|
|
integral = SUM(fun1(1:nc)*fun2(1:nc)*grid%wr(1:nc))
|
|
|
|
END FUNCTION integrate_grid_function2
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Integrate the product of three functions on an atomic radial grid.
|
|
!> \param fun1 first function
|
|
!> \param fun2 second function
|
|
!> \param fun3 third function
|
|
!> \param grid atomic radial grid
|
|
!> \return value of the integral
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE FUNCTION integrate_grid_function3(fun1, fun2, fun3, grid) RESULT(integral)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: fun1, fun2, fun3
|
|
TYPE(grid_atom_type), INTENT(IN) :: grid
|
|
REAL(KIND=dp) :: integral
|
|
|
|
INTEGER :: nc
|
|
|
|
nc = MIN(SIZE(fun1), SIZE(fun2), SIZE(fun3))
|
|
integral = SUM(fun1(1:nc)*fun2(1:nc)*fun3(1:nc)*grid%wr(1:nc))
|
|
|
|
END FUNCTION integrate_grid_function3
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Numerically compute the Coulomb potential on an atomic radial grid.
|
|
!> \param cpot Coulomb potential on the radial grid
|
|
!> \param density electron density on the radial grid
|
|
!> \param grid atomic radial grid
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE coulomb_potential_numeric(cpot, density, grid)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: cpot
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: density
|
|
TYPE(grid_atom_type), INTENT(IN) :: grid
|
|
|
|
INTEGER :: i, nc
|
|
REAL(dp) :: int1, int2
|
|
REAL(dp), DIMENSION(:), POINTER :: r, wr
|
|
|
|
nc = MIN(SIZE(cpot), SIZE(density))
|
|
r => grid%rad
|
|
wr => grid%wr
|
|
|
|
int1 = fourpi*integrate_grid(density, grid)
|
|
int2 = 0._dp
|
|
cpot(nc:) = int1/r(nc:)
|
|
! IF (log_unit>0) WRITE(log_unit,"(A,2F10.8)") "r", int1, cpot(nc:)
|
|
|
|
! test that grid is decreasing
|
|
CPASSERT(r(1) > r(nc))
|
|
DO i = 1, nc
|
|
cpot(i) = int1/r(i) + int2
|
|
int1 = int1 - fourpi*density(i)*wr(i)
|
|
int2 = int2 + fourpi*density(i)*wr(i)/r(i)
|
|
END DO
|
|
|
|
END SUBROUTINE coulomb_potential_numeric
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Analytically compute the Coulomb potential on an atomic radial grid.
|
|
!> \param cpot Coulomb potential on the radial grid
|
|
!> \param pmat density matrix
|
|
!> \param basis atomic basis set
|
|
!> \param grid atomic radial grid
|
|
!> \param maxl maximum angular momentum to consider
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE coulomb_potential_analytic(cpot, pmat, basis, grid, maxl)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: cpot
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(IN) :: pmat
|
|
TYPE(atom_basis_type), INTENT(IN) :: basis
|
|
TYPE(grid_atom_type) :: grid
|
|
INTEGER, INTENT(IN) :: maxl
|
|
|
|
INTEGER :: i, j, k, l, m, n
|
|
REAL(KIND=dp) :: a, b, ff, oab, sab
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: erfa, expa, z
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: unp
|
|
|
|
m = SIZE(cpot)
|
|
ALLOCATE (erfa(1:m), expa(1:m), z(1:m))
|
|
|
|
cpot = 0._dp
|
|
|
|
DO l = 0, maxl
|
|
IF (MAXVAL(ABS(pmat(:, :, l))) < 1.e-14_dp) CYCLE
|
|
SELECT CASE (basis%basis_type)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (GTO_BASIS)
|
|
DO i = 1, basis%nbas(l)
|
|
DO j = i, basis%nbas(l)
|
|
IF (ABS(pmat(i, j, l)) < 1.e-14_dp) CYCLE
|
|
ff = pmat(i, j, l)
|
|
IF (i /= j) ff = 2._dp*ff
|
|
a = basis%am(i, l)
|
|
b = basis%am(j, l)
|
|
sab = SQRT(a + b)
|
|
oab = rootpi/(a + b)**(l + 1.5_dp)*ff
|
|
z(:) = sab*grid%rad(:)
|
|
DO k = 1, SIZE(erfa)
|
|
erfa(k) = oab*erf(z(k))/grid%rad(k)
|
|
END DO
|
|
expa(:) = EXP(-z(:)**2)*ff/(a + b)**(l + 1)
|
|
SELECT CASE (l)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (0)
|
|
cpot(:) = cpot(:) + 0.25_dp*erfa(:)
|
|
CASE (1)
|
|
cpot(:) = cpot(:) + 0.375_dp*erfa(:) - 0.25_dp*expa(:)
|
|
CASE (2)
|
|
cpot(:) = cpot(:) + 0.9375_dp*erfa(:) - expa(:)*(0.875_dp + 0.25_dp*z(:)**2)
|
|
CASE (3)
|
|
cpot(:) = cpot(:) + 3.28125_dp*erfa(:) - expa(:)*(3.5625_dp + 1.375_dp*z(:)**2 + 0.25*z(:)**4)
|
|
END SELECT
|
|
END DO
|
|
END DO
|
|
CASE (CGTO_BASIS)
|
|
n = basis%nprim(l)
|
|
m = basis%nbas(l)
|
|
ALLOCATE (unp(n, n))
|
|
|
|
unp(1:n, 1:n) = MATMUL(MATMUL(basis%cm(1:n, 1:m, l), pmat(1:m, 1:m, l)), &
|
|
TRANSPOSE(basis%cm(1:n, 1:m, l)))
|
|
DO i = 1, basis%nprim(l)
|
|
DO j = i, basis%nprim(l)
|
|
IF (ABS(unp(i, j)) < 1.e-14_dp) CYCLE
|
|
ff = unp(i, j)
|
|
IF (i /= j) ff = 2._dp*ff
|
|
a = basis%am(i, l)
|
|
b = basis%am(j, l)
|
|
sab = SQRT(a + b)
|
|
oab = rootpi/(a + b)**(l + 1.5_dp)*ff
|
|
z(:) = sab*grid%rad(:)
|
|
DO k = 1, SIZE(erfa)
|
|
erfa(k) = oab*erf(z(k))/grid%rad(k)
|
|
END DO
|
|
expa(:) = EXP(-z(:)**2)*ff/(a + b)**(l + 1)
|
|
SELECT CASE (l)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (0)
|
|
cpot(:) = cpot(:) + 0.25_dp*erfa(:)
|
|
CASE (1)
|
|
cpot(:) = cpot(:) + 0.375_dp*erfa(:) - 0.25_dp*expa(:)
|
|
CASE (2)
|
|
cpot(:) = cpot(:) + 0.9375_dp*erfa(:) - expa(:)*(0.875_dp + 0.25_dp*z(:)**2)
|
|
CASE (3)
|
|
cpot(:) = cpot(:) + 3.28125_dp*erfa(:) - expa(:)*(3.5625_dp + 1.375_dp*z(:)**2 + 0.25*z(:)**4)
|
|
END SELECT
|
|
END DO
|
|
END DO
|
|
|
|
DEALLOCATE (unp)
|
|
END SELECT
|
|
END DO
|
|
DEALLOCATE (erfa, expa, z)
|
|
|
|
END SUBROUTINE coulomb_potential_analytic
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate the exchange potential numerically.
|
|
!> \param kmat Kohn-Sham matrix
|
|
!> \param state electronic state
|
|
!> \param occ occupation numbers
|
|
!> \param wfn wavefunctions
|
|
!> \param basis atomic basis set
|
|
!> \param hfx_pot potential parameters from Hartree-Fock section
|
|
!> \par History
|
|
!> * 01.2009 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE exchange_numeric(kmat, state, occ, wfn, basis, hfx_pot)
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(INOUT) :: kmat
|
|
TYPE(atom_state), INTENT(IN) :: state
|
|
REAL(KIND=dp), DIMENSION(0:, :), INTENT(IN) :: occ
|
|
REAL(KIND=dp), DIMENSION(:, :, :), POINTER :: wfn
|
|
TYPE(atom_basis_type), INTENT(IN) :: basis
|
|
TYPE(atom_hfx_type), INTENT(IN) :: hfx_pot
|
|
|
|
INTEGER :: i, ia, ib, k, lad, lbc, lh, ll, nbas, &
|
|
norb, nr, nu
|
|
REAL(KIND=dp) :: almn
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: cpot, nai, nbi, pot
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: orb
|
|
REAL(KIND=dp), DIMENSION(0:maxfac) :: arho
|
|
|
|
arho = 0._dp
|
|
DO ll = 0, maxfac, 2
|
|
lh = ll/2
|
|
arho(ll) = fac(ll)/fac(lh)**2
|
|
END DO
|
|
|
|
kmat = 0._dp
|
|
|
|
nr = basis%grid%nr
|
|
ALLOCATE (nai(nr), nbi(nr), cpot(nr), pot(nr))
|
|
|
|
DO lad = 0, state%maxl_calc
|
|
DO lbc = 0, state%maxl_occ
|
|
norb = state%maxn_occ(lbc)
|
|
nbas = basis%nbas(lbc)
|
|
! calculate orbitals for angmom lbc
|
|
ALLOCATE (orb(nr, norb))
|
|
orb = 0._dp
|
|
DO i = 1, norb
|
|
DO k = 1, nbas
|
|
orb(:, i) = orb(:, i) + wfn(k, i, lbc)*basis%bf(:, k, lbc)
|
|
END DO
|
|
END DO
|
|
DO nu = ABS(lad - lbc), lad + lbc, 2
|
|
almn = arho(-lad + lbc + nu)*arho(lad - lbc + nu)*arho(lad + lbc - nu)/(REAL(lad + lbc + nu + 1, dp) &
|
|
*arho(lad + lbc + nu))
|
|
almn = -0.5_dp*almn
|
|
|
|
DO ia = 1, basis%nbas(lad)
|
|
DO i = 1, norb
|
|
nai(:) = orb(:, i)*basis%bf(:, ia, lad)
|
|
cpot = 0.0_dp
|
|
IF (hfx_pot%scale_coulomb /= 0.0_dp) THEN
|
|
CALL potential_coulomb_numeric(pot, nai, nu, basis%grid)
|
|
cpot(:) = cpot(:) + pot(:)*hfx_pot%scale_coulomb
|
|
END IF
|
|
IF (hfx_pot%scale_longrange /= 0.0_dp) THEN
|
|
IF (hfx_pot%do_gh) THEN
|
|
CALL potential_longrange_numeric_gh(pot, nai, nu, basis%grid, hfx_pot%omega, &
|
|
hfx_pot%kernel(:, :, nu))
|
|
ELSE
|
|
CALL potential_longrange_numeric(pot, nai, nu, basis%grid, hfx_pot%omega, &
|
|
hfx_pot%kernel(:, :, nu))
|
|
END IF
|
|
cpot(:) = cpot(:) + pot(:)*hfx_pot%scale_longrange
|
|
END IF
|
|
DO ib = 1, basis%nbas(lad)
|
|
kmat(ia, ib, lad) = kmat(ia, ib, lad) + almn*occ(lbc, i)* &
|
|
integrate_grid(cpot, orb(:, i), basis%bf(:, ib, lad), basis%grid)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
|
|
END DO
|
|
DEALLOCATE (orb)
|
|
END DO
|
|
END DO
|
|
|
|
DEALLOCATE (nai, nbi, cpot)
|
|
|
|
END SUBROUTINE exchange_numeric
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate the exchange potential semi-analytically.
|
|
!> \param kmat Kohn-Sham matrix
|
|
!> \param state electronic state
|
|
!> \param occ occupation numbers
|
|
!> \param wfn wavefunctions
|
|
!> \param basis atomic basis set
|
|
!> \param hfx_pot properties of the Hartree-Fock potential
|
|
!> \par History
|
|
!> * 01.2009 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE exchange_semi_analytic(kmat, state, occ, wfn, basis, hfx_pot)
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(INOUT) :: kmat
|
|
TYPE(atom_state), INTENT(IN) :: state
|
|
REAL(KIND=dp), DIMENSION(0:, :), INTENT(IN) :: occ
|
|
REAL(KIND=dp), DIMENSION(:, :, :), POINTER :: wfn
|
|
TYPE(atom_basis_type), INTENT(IN) :: basis
|
|
TYPE(atom_hfx_type), INTENT(IN) :: hfx_pot
|
|
|
|
INTEGER :: i, ia, ib, k, lad, lbc, lh, ll, nbas, &
|
|
norb, nr, nu
|
|
REAL(KIND=dp) :: almn
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: cpot, nai, nbi
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: orb
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: pot
|
|
REAL(KIND=dp), DIMENSION(0:maxfac) :: arho
|
|
|
|
arho = 0._dp
|
|
DO ll = 0, maxfac, 2
|
|
lh = ll/2
|
|
arho(ll) = fac(ll)/fac(lh)**2
|
|
END DO
|
|
|
|
kmat = 0._dp
|
|
|
|
nr = basis%grid%nr
|
|
nbas = MAXVAL(basis%nbas)
|
|
ALLOCATE (pot(nr, nbas, nbas))
|
|
ALLOCATE (nai(nr), nbi(nr), cpot(nr))
|
|
|
|
DO lad = 0, state%maxl_calc
|
|
DO lbc = 0, state%maxl_occ
|
|
norb = state%maxn_occ(lbc)
|
|
nbas = basis%nbas(lbc)
|
|
! calculate orbitals for angmom lbc
|
|
ALLOCATE (orb(nr, norb))
|
|
orb = 0._dp
|
|
DO i = 1, norb
|
|
DO k = 1, nbas
|
|
orb(:, i) = orb(:, i) + wfn(k, i, lbc)*basis%bf(:, k, lbc)
|
|
END DO
|
|
END DO
|
|
DO nu = ABS(lad - lbc), lad + lbc, 2
|
|
almn = arho(-lad + lbc + nu)*arho(lad - lbc + nu) &
|
|
*arho(lad + lbc - nu)/(REAL(lad + lbc + nu + 1, dp)*arho(lad + lbc + nu))
|
|
almn = -0.5_dp*almn
|
|
! calculate potential for basis function pair (lad,lbc)
|
|
pot = 0._dp
|
|
CALL potential_analytic(pot, lad, lbc, nu, basis, hfx_pot)
|
|
DO ia = 1, basis%nbas(lad)
|
|
DO i = 1, norb
|
|
cpot = 0._dp
|
|
DO k = 1, nbas
|
|
cpot(:) = cpot(:) + pot(:, ia, k)*wfn(k, i, lbc)
|
|
END DO
|
|
DO ib = 1, basis%nbas(lad)
|
|
kmat(ia, ib, lad) = kmat(ia, ib, lad) + almn*occ(lbc, i)* &
|
|
integrate_grid(cpot, orb(:, i), basis%bf(:, ib, lad), basis%grid)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END DO
|
|
DEALLOCATE (orb)
|
|
END DO
|
|
END DO
|
|
|
|
DEALLOCATE (pot)
|
|
DEALLOCATE (nai, nbi, cpot)
|
|
|
|
END SUBROUTINE exchange_semi_analytic
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate the electrostatic potential using numerical differentiation.
|
|
!> \param cpot computed potential
|
|
!> \param density electron density on the atomic radial grid
|
|
!> \param nu integer parameter [ABS(la-lb) .. la+lb];
|
|
!> see potential_analytic() and exchange_numeric()
|
|
!> \param grid atomic radial grid
|
|
!> \par History
|
|
!> * 01.2009 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE potential_coulomb_numeric(cpot, density, nu, grid)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: cpot
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: density
|
|
INTEGER, INTENT(IN) :: nu
|
|
TYPE(grid_atom_type), INTENT(IN) :: grid
|
|
|
|
INTEGER :: i, nc
|
|
REAL(dp) :: int1, int2
|
|
REAL(dp), DIMENSION(:), POINTER :: r, wr
|
|
|
|
nc = MIN(SIZE(cpot), SIZE(density))
|
|
r => grid%rad
|
|
wr => grid%wr
|
|
|
|
int1 = integrate_grid(density, r**nu, grid)
|
|
int2 = 0._dp
|
|
cpot(nc:) = int1/r(nc:)**(nu + 1)
|
|
|
|
! test that grid is decreasing
|
|
CPASSERT(r(1) > r(nc))
|
|
DO i = 1, nc
|
|
cpot(i) = int1/r(i)**(nu + 1) + int2*r(i)**nu
|
|
int1 = int1 - r(i)**(nu)*density(i)*wr(i)
|
|
int2 = int2 + density(i)*wr(i)/r(i)**(nu + 1)
|
|
END DO
|
|
|
|
END SUBROUTINE potential_coulomb_numeric
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param cpot ...
|
|
!> \param density ...
|
|
!> \param nu ...
|
|
!> \param grid ...
|
|
!> \param omega ...
|
|
!> \param kernel ...
|
|
! **************************************************************************************************
|
|
SUBROUTINE potential_longrange_numeric(cpot, density, nu, grid, omega, kernel)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: cpot
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: density
|
|
INTEGER, INTENT(IN) :: nu
|
|
TYPE(grid_atom_type), INTENT(IN) :: grid
|
|
REAL(KIND=dp), INTENT(IN) :: omega
|
|
REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: kernel
|
|
|
|
INTEGER :: nc
|
|
REAL(dp), DIMENSION(:), POINTER :: r, wr
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: work_inp, work_out
|
|
|
|
nc = MIN(SIZE(cpot), SIZE(density))
|
|
r => grid%rad
|
|
wr => grid%wr
|
|
|
|
ALLOCATE (work_inp(nc), work_out(nc))
|
|
|
|
cpot = 0.0_dp
|
|
|
|
! First Bessel transform
|
|
work_inp(:nc) = density(:nc)*wr(:nc)
|
|
CALL DSYMV('U', nc, 1.0_dp, kernel, nc, work_inp, 1, 0.0_dp, work_out, 1)
|
|
|
|
! Second Bessel transform
|
|
work_inp(:nc) = work_out(:nc)*EXP(-r(:nc)**2)/r(:nc)**2*wr(:nc)
|
|
CALL DSYMV('U', nc, 1.0_dp, kernel, nc, work_inp, 1, 0.0_dp, work_out, 1)
|
|
|
|
cpot(:nc) = work_out(:nc)*(2.0_dp*REAL(nu, dp) + 1.0_dp)*4.0_dp/pi*omega
|
|
|
|
END SUBROUTINE potential_longrange_numeric
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param cpot ...
|
|
!> \param density ...
|
|
!> \param nu ...
|
|
!> \param grid ...
|
|
!> \param omega ...
|
|
!> \param kernel ...
|
|
! **************************************************************************************************
|
|
SUBROUTINE potential_longrange_numeric_gh(cpot, density, nu, grid, omega, kernel)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: cpot
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: density
|
|
INTEGER, INTENT(IN) :: nu
|
|
TYPE(grid_atom_type), INTENT(IN) :: grid
|
|
REAL(KIND=dp), INTENT(IN) :: omega
|
|
REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: kernel
|
|
|
|
INTEGER :: n_max, nc, nc_kernel, nr_kernel
|
|
REAL(dp), DIMENSION(:), POINTER :: wr
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: work_inp, work_out
|
|
|
|
nc = MIN(SIZE(cpot), SIZE(density))
|
|
wr => grid%wr
|
|
|
|
nc_kernel = SIZE(kernel, 1)
|
|
nr_kernel = SIZE(kernel, 2)
|
|
n_max = MAX(nc, nc_kernel, nr_kernel)
|
|
|
|
ALLOCATE (work_inp(n_max), work_out(n_max))
|
|
|
|
cpot = 0.0_dp
|
|
|
|
! First Bessel transform
|
|
work_inp(:nc) = density(:nc)*wr(:nc)
|
|
CALL DGEMV('T', nc_kernel, nr_kernel, 1.0_dp, kernel, nc_kernel, work_inp, 1, 0.0_dp, work_out, 1)
|
|
|
|
! Second Bessel transform
|
|
work_inp(:nr_kernel) = work_out(:nr_kernel)
|
|
CALL DGEMV('N', nc_kernel, nr_kernel, 1.0_dp, kernel, nc_kernel, work_inp, 1, 0.0_dp, work_out, 1)
|
|
|
|
cpot(:nc) = work_out(:nc)*(2.0_dp*REAL(nu, dp) + 1.0_dp)*4.0_dp/pi*omega
|
|
|
|
END SUBROUTINE potential_longrange_numeric_gh
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate the electrostatic potential using analytical expressions.
|
|
!> The interaction potential has the form
|
|
!> V(r)=scale_coulomb*1/r+scale_lr*erf(omega*r)/r
|
|
!> \param cpot computed potential
|
|
!> \param la angular momentum of the calculated state
|
|
!> \param lb angular momentum of the occupied state
|
|
!> \param nu integer parameter [ABS(la-lb) .. la+lb] with the parity of 'la+lb'
|
|
!> \param basis atomic basis set
|
|
!> \param hfx_pot properties of the Hartree-Fock potential
|
|
!> \par History
|
|
!> * 01.2009 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE potential_analytic(cpot, la, lb, nu, basis, hfx_pot)
|
|
REAL(KIND=dp), DIMENSION(:, :, :), INTENT(OUT) :: cpot
|
|
INTEGER, INTENT(IN) :: la, lb, nu
|
|
TYPE(atom_basis_type), INTENT(IN) :: basis
|
|
TYPE(atom_hfx_type), INTENT(IN) :: hfx_pot
|
|
|
|
INTEGER :: i, j, k, l, ll, m
|
|
REAL(KIND=dp) :: a, b
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: erfa, pot
|
|
|
|
m = SIZE(cpot, 1)
|
|
ALLOCATE (erfa(1:m))
|
|
|
|
ll = la + lb
|
|
|
|
cpot = 0._dp
|
|
|
|
SELECT CASE (basis%basis_type)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (GTO_BASIS)
|
|
DO i = 1, basis%nbas(la)
|
|
DO j = 1, basis%nbas(lb)
|
|
a = basis%am(i, la)
|
|
b = basis%am(j, lb)
|
|
|
|
IF (hfx_pot%scale_coulomb /= 0.0_dp) THEN
|
|
CALL potential_coulomb_analytic(erfa, a, b, ll, nu, basis%grid%rad)
|
|
|
|
cpot(:, i, j) = cpot(:, i, j) + erfa(:)*hfx_pot%scale_coulomb
|
|
END IF
|
|
|
|
IF (hfx_pot%scale_longrange /= 0.0_dp) THEN
|
|
CALL potential_longrange_analytic(erfa, a, b, ll, nu, basis%grid%rad, hfx_pot%omega)
|
|
|
|
cpot(:, i, j) = cpot(:, i, j) + erfa(:)*hfx_pot%scale_longrange
|
|
END IF
|
|
END DO
|
|
END DO
|
|
CASE (CGTO_BASIS)
|
|
ALLOCATE (pot(1:m))
|
|
|
|
DO i = 1, basis%nprim(la)
|
|
DO j = 1, basis%nprim(lb)
|
|
a = basis%am(i, la)
|
|
b = basis%am(j, lb)
|
|
|
|
pot = 0.0_dp
|
|
|
|
IF (hfx_pot%scale_coulomb /= 0.0_dp) THEN
|
|
CALL potential_coulomb_analytic(erfa, a, b, ll, nu, basis%grid%rad)
|
|
|
|
pot(:) = pot(:) + erfa(:)*hfx_pot%scale_coulomb
|
|
END IF
|
|
|
|
IF (hfx_pot%scale_longrange /= 0.0_dp) THEN
|
|
CALL potential_longrange_analytic(erfa, a, b, ll, nu, basis%grid%rad, hfx_pot%omega)
|
|
|
|
pot(:) = pot(:) + erfa(:)*hfx_pot%scale_longrange
|
|
END IF
|
|
|
|
DO k = 1, basis%nbas(la)
|
|
DO l = 1, basis%nbas(lb)
|
|
cpot(:, k, l) = cpot(:, k, l) + pot(:)*basis%cm(i, k, la)*basis%cm(j, l, lb)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END DO
|
|
|
|
END SELECT
|
|
|
|
DEALLOCATE (erfa)
|
|
|
|
END SUBROUTINE potential_analytic
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param erfa Array will contain the potential
|
|
!> \param a Exponent of first Gaussian charge distribution
|
|
!> \param b Exponent of second Gaussian charge distribution
|
|
!> \param ll Sum of angular momentum quantum numbers building one charge distribution
|
|
!> \param nu Angular momentum of interaction, (ll-nu) should be even.
|
|
!> \param rad Radial grid
|
|
! **************************************************************************************************
|
|
SUBROUTINE potential_coulomb_analytic(erfa, a, b, ll, nu, rad)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: erfa
|
|
REAL(KIND=dp), INTENT(IN) :: a, b
|
|
INTEGER, INTENT(IN) :: ll, nu
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: rad
|
|
|
|
INTEGER :: nr
|
|
REAL(KIND=dp) :: oab, sab
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: expa, z
|
|
|
|
nr = SIZE(rad)
|
|
ALLOCATE (expa(nr), z(nr))
|
|
|
|
sab = SQRT(a + b)
|
|
oab = dfac(ll + nu + 1)*rootpi/sab**(ll + 2)/2._dp**((ll + nu)/2 + 2)
|
|
z(:) = sab*rad(:)
|
|
erfa(:) = oab*erf(z(:))/z(:)**(nu + 1)
|
|
expa(:) = EXP(-z(:)**2)/sab**(ll + 2)/2._dp**((ll + nu)/2 + 2)
|
|
SELECT CASE (ll)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (0)
|
|
CPASSERT(nu == 0)
|
|
CASE (1)
|
|
CPASSERT(nu == 1)
|
|
erfa(:) = erfa(:) - 6._dp*expa(:)/z(:)
|
|
CASE (2)
|
|
SELECT CASE (nu)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (0)
|
|
erfa(:) = erfa(:) - 2._dp*expa(:)
|
|
CASE (2)
|
|
erfa(:) = erfa(:) - expa(:)*(20._dp + 30._dp/z(:)**2)
|
|
END SELECT
|
|
CASE (3)
|
|
SELECT CASE (nu)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (1)
|
|
erfa(:) = erfa(:) - expa(:)*(12._dp*z(:) + 30._dp/z(:))
|
|
CASE (3)
|
|
erfa(:) = erfa(:) - expa(:)*(56._dp*z(:) + 140._dp/z(:) + 210._dp/z(:)**3)
|
|
END SELECT
|
|
CASE (4)
|
|
SELECT CASE (nu)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (0)
|
|
erfa(:) = erfa(:) - expa(:)*(4._dp*z(:)**2 + 14._dp)
|
|
CASE (2)
|
|
erfa(:) = erfa(:) - expa(:)*(40._dp*z(:)**2 + 140._dp + 210._dp/z(:)**2)
|
|
CASE (4)
|
|
erfa(:) = erfa(:) - expa(:)*(144._dp*z(:)**2 + 504._dp + 1260._dp/z(:)**2 + 1890._dp/z(:)**4)
|
|
END SELECT
|
|
CASE (5)
|
|
SELECT CASE (nu)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (1)
|
|
erfa(:) = erfa(:) - expa(:)*(24._dp*z(:)**3 + 108._dp*z(:) + 210._dp/z(:))
|
|
CASE (3)
|
|
erfa(:) = erfa(:) - expa(:)*(112._dp*z(:)**3 + 504._dp*z(:) + 1260._dp/z(:) + 1890._dp/z(:)**3)
|
|
CASE (5)
|
|
erfa(:) = erfa(:) - expa(:)*(352._dp*z(:)**3 + 1584._dp*z(:) + 5544._dp/z(:) + &
|
|
13860._dp/z(:)**3 + 20790._dp/z(:)**5)
|
|
END SELECT
|
|
CASE (6)
|
|
SELECT CASE (nu)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (0)
|
|
erfa(:) = erfa(:) - expa(:)*(8._dp*z(:)**4 + 44._dp*z(:)**2 + 114._dp)
|
|
CASE (2)
|
|
erfa(:) = erfa(:) - expa(:)*(80._dp*z(:)**4 + 440._dp*z(:)**2 + 1260._dp + 1896._dp/z(:)**2)
|
|
CASE (4)
|
|
erfa(:) = erfa(:) - expa(:)*(288._dp*z(:)**4 + 1584._dp*z(:)**2 + 5544._dp + &
|
|
13860._dp/z(:)**2 + 20790._dp/z(:)**4)
|
|
CASE (6)
|
|
erfa(:) = erfa(:) - expa(:)*(832._dp*z(:)**4 + 4576._dp*z(:)**2 + 20592._dp + &
|
|
72072._dp/z(:)**2 + 180180._dp/z(:)**4 + 270270._dp/z(:)**6)
|
|
END SELECT
|
|
END SELECT
|
|
|
|
DEALLOCATE (expa, z)
|
|
|
|
END SUBROUTINE potential_coulomb_analytic
|
|
|
|
! **************************************************************************************************
|
|
!> \brief This routine calculates the longrange Coulomb potential of a product of two Gaussian with given angular momentum
|
|
!> \param erfa Array will contain the potential
|
|
!> \param a Exponent of first Gaussian charge distribution
|
|
!> \param b Exponent of second Gaussian charge distribution
|
|
!> \param ll Sum of angular momentum quantum numbers building one charge distribution
|
|
!> \param nu Angular momentum of interaction, (ll-nu) should be even.
|
|
!> \param rad Radial grid
|
|
!> \param omega Range-separation parameter
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE potential_longrange_analytic(erfa, a, b, ll, nu, rad, omega)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: erfa
|
|
REAL(KIND=dp), INTENT(IN) :: a, b
|
|
INTEGER, INTENT(IN) :: ll, nu
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: rad
|
|
REAL(KIND=dp), INTENT(IN) :: omega
|
|
|
|
INTEGER :: i, lambda, nr
|
|
REAL(KIND=dp) :: ab, oab, pab, prel, sab
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: expa, z
|
|
|
|
IF (MOD(ll - nu, 2) == 0 .AND. ll >= nu .AND. nu >= 0) THEN
|
|
nr = SIZE(rad)
|
|
ALLOCATE (expa(nr), z(nr))
|
|
|
|
lambda = (ll - nu)/2
|
|
ab = a + b
|
|
sab = SQRT(ab)
|
|
pab = omega**2*ab/(omega**2 + ab)
|
|
prel = pab/ab
|
|
z(:) = SQRT(pab)*rad(:)
|
|
oab = fac(lambda)/SQRT(ab)**(ll + 2)/4.0_dp*SQRT(prel)**(nu + 1)*(2.0_dp*REAL(nu, KIND=dp) + 1.0_dp)
|
|
expa(:) = EXP(-z(:)**2)
|
|
lambda = (ll - nu)/2
|
|
|
|
IF (lambda > 0) THEN
|
|
erfa = 0.0_dp
|
|
DO i = 1, lambda
|
|
erfa = erfa + (-prel)**i/REAL(i, KIND=dp)*binomial_gen(lambda + nu + 0.5_dp, lambda - i)* &
|
|
assoc_laguerre(z, REAL(nu, KIND=dp) + 0.5_dp, i - 1)
|
|
END DO
|
|
erfa = erfa*expa*z**nu
|
|
|
|
erfa = erfa + 2.0_dp*binomial_gen(lambda + nu + 0.5_dp, lambda)*znFn(z, expa, nu)
|
|
ELSE
|
|
erfa = 2.0_dp*znFn(z, expa, nu)
|
|
END IF
|
|
|
|
erfa = erfa*oab
|
|
|
|
DEALLOCATE (expa, z)
|
|
ELSE
|
|
! Contribution to potential is zero (properties of spherical harmonics)
|
|
erfa = 0.0_dp
|
|
END IF
|
|
|
|
END SUBROUTINE potential_longrange_analytic
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Boys' function times z**n
|
|
!> \param z ...
|
|
!> \param expa ...
|
|
!> \param n ...
|
|
!> \return ...
|
|
! **************************************************************************************************
|
|
ELEMENTAL FUNCTION znFn(z, expa, n) RESULT(res)
|
|
|
|
REAL(KIND=dp), INTENT(IN) :: z, expa
|
|
INTEGER, INTENT(IN) :: n
|
|
REAL(KIND=dp) :: res
|
|
|
|
INTEGER :: i
|
|
REAL(KIND=dp) :: z_exp
|
|
|
|
IF (n >= 0) THEN
|
|
IF (ABS(z) < 1.0E-20) THEN
|
|
res = z**n/(2.0_dp*REAL(n, KIND=dp) + 1.0_dp)
|
|
ELSE IF (n == 0) THEN
|
|
res = rootpi/2.0_dp*ERF(z)/z
|
|
ELSE
|
|
res = rootpi/4.0_dp*ERF(z)/z**2 - expa/z/2.0_dp
|
|
z_exp = -expa*0.5_dp
|
|
|
|
DO i = 2, n
|
|
res = (REAL(i, KIND=dp) - 0.5_dp)*res/z + z_exp
|
|
z_exp = z_exp*z
|
|
END DO
|
|
END IF
|
|
ELSE ! Set it to zero (no Boys' function, to keep the ELEMENTAL keyword)
|
|
res = 0.0_dp
|
|
END IF
|
|
|
|
END FUNCTION znFn
|
|
|
|
! **************************************************************************************************
|
|
!> \brief ...
|
|
!> \param z ...
|
|
!> \param a ...
|
|
!> \param n ...
|
|
!> \return ...
|
|
! **************************************************************************************************
|
|
ELEMENTAL FUNCTION assoc_laguerre(z, a, n) RESULT(res)
|
|
|
|
REAL(KIND=dp), INTENT(IN) :: z, a
|
|
INTEGER, INTENT(IN) :: n
|
|
REAL(KIND=dp) :: res
|
|
|
|
INTEGER :: i
|
|
REAL(KIND=dp) :: f0, f1
|
|
|
|
IF (n == 0) THEN
|
|
res = 1.0_dp
|
|
ELSE IF (n == 1) THEN
|
|
res = a + 1.0_dp - z
|
|
ELSE IF (n > 0) THEN
|
|
f0 = 1.0_dp
|
|
f1 = a + 1.0_dp - z
|
|
|
|
DO i = 3, n
|
|
res = (2.0_dp + (a - 1.0_dp - z)/REAL(i, dp))*f1 - (1.0_dp + (a - 1.0_dp)/REAL(i, dp))*f0
|
|
f0 = f1
|
|
f1 = res
|
|
END DO
|
|
ELSE ! n is negative, set it zero (no polynomials, to keep the ELEMENTAL keyword)
|
|
res = 0.0_dp
|
|
END IF
|
|
|
|
END FUNCTION assoc_laguerre
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Compute Trace[opmat * pmat] == Trace[opmat * pmat^T] .
|
|
!> \param opmat operator matrix (e.g. Kohn-Sham matrix or overlap matrix)
|
|
!> \param pmat density matrix
|
|
!> \return value of trace
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE FUNCTION atom_trace(opmat, pmat) RESULT(trace)
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(IN) :: opmat, pmat
|
|
REAL(KIND=dp) :: trace
|
|
|
|
trace = accurate_dot_product(opmat, pmat)
|
|
|
|
END FUNCTION atom_trace
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate a potential matrix by integrating the potential on an atomic radial grid.
|
|
!> \param imat potential matrix
|
|
!> \param cpot potential on the atomic radial grid
|
|
!> \param basis atomic basis set
|
|
!> \param derivatives order of derivatives
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE numpot_matrix(imat, cpot, basis, derivatives)
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(INOUT) :: imat
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: cpot
|
|
TYPE(atom_basis_type), INTENT(INOUT) :: basis
|
|
INTEGER, INTENT(IN) :: derivatives
|
|
|
|
INTEGER :: i, j, l, n
|
|
|
|
SELECT CASE (derivatives)
|
|
CASE (0)
|
|
DO l = 0, lmat
|
|
n = basis%nbas(l)
|
|
DO i = 1, n
|
|
DO j = i, n
|
|
imat(i, j, l) = imat(i, j, l) + &
|
|
integrate_grid(cpot, basis%bf(:, i, l), basis%bf(:, j, l), basis%grid)
|
|
imat(j, i, l) = imat(i, j, l)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
CASE (1)
|
|
DO l = 0, lmat
|
|
n = basis%nbas(l)
|
|
DO i = 1, n
|
|
DO j = i, n
|
|
imat(i, j, l) = imat(i, j, l) + &
|
|
integrate_grid(cpot, basis%dbf(:, i, l), basis%bf(:, j, l), basis%grid)
|
|
imat(i, j, l) = imat(i, j, l) + &
|
|
integrate_grid(cpot, basis%bf(:, i, l), basis%dbf(:, j, l), basis%grid)
|
|
imat(j, i, l) = imat(i, j, l)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
CASE (2)
|
|
DO l = 0, lmat
|
|
n = basis%nbas(l)
|
|
DO i = 1, n
|
|
DO j = i, n
|
|
imat(i, j, l) = imat(i, j, l) + &
|
|
integrate_grid(cpot, basis%dbf(:, i, l), basis%dbf(:, j, l), basis%grid)
|
|
imat(j, i, l) = imat(i, j, l)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
END SELECT
|
|
|
|
END SUBROUTINE numpot_matrix
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Contract Coulomb Electron Repulsion Integrals.
|
|
!> \param jmat ...
|
|
!> \param erint ...
|
|
!> \param pmat ...
|
|
!> \param nsize ...
|
|
!> \param all_nu ...
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE ceri_contract(jmat, erint, pmat, nsize, all_nu)
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(INOUT) :: jmat
|
|
TYPE(eri), DIMENSION(:), INTENT(IN) :: erint
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(IN) :: pmat
|
|
INTEGER, DIMENSION(0:), INTENT(IN) :: nsize
|
|
LOGICAL, INTENT(IN), OPTIONAL :: all_nu
|
|
|
|
INTEGER :: i1, i2, ij1, ij2, j1, j2, l1, l2, ll, &
|
|
n1, n2
|
|
LOGICAL :: have_all_nu
|
|
REAL(KIND=dp) :: eint, f1, f2
|
|
|
|
IF (PRESENT(all_nu)) THEN
|
|
have_all_nu = all_nu
|
|
ELSE
|
|
have_all_nu = .FALSE.
|
|
END IF
|
|
|
|
jmat(:, :, :) = 0._dp
|
|
ll = 0
|
|
DO l1 = 0, lmat
|
|
n1 = nsize(l1)
|
|
DO l2 = 0, l1
|
|
n2 = nsize(l2)
|
|
ll = ll + 1
|
|
ij1 = 0
|
|
DO i1 = 1, n1
|
|
DO j1 = i1, n1
|
|
ij1 = ij1 + 1
|
|
f1 = 1._dp
|
|
IF (i1 /= j1) f1 = 2._dp
|
|
ij2 = 0
|
|
DO i2 = 1, n2
|
|
DO j2 = i2, n2
|
|
ij2 = ij2 + 1
|
|
f2 = 1._dp
|
|
IF (i2 /= j2) f2 = 2._dp
|
|
eint = erint(ll)%int(ij1, ij2)
|
|
IF (l1 == l2) THEN
|
|
jmat(i1, j1, l1) = jmat(i1, j1, l1) + f2*pmat(i2, j2, l2)*eint
|
|
ELSE
|
|
jmat(i1, j1, l1) = jmat(i1, j1, l1) + f2*pmat(i2, j2, l2)*eint
|
|
jmat(i2, j2, l2) = jmat(i2, j2, l2) + f1*pmat(i1, j1, l1)*eint
|
|
END IF
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END DO
|
|
IF (have_all_nu) THEN
|
|
! skip integral blocks with nu/=0
|
|
ll = ll + l2
|
|
END IF
|
|
END DO
|
|
END DO
|
|
DO l1 = 0, lmat
|
|
n1 = nsize(l1)
|
|
DO i1 = 1, n1
|
|
DO j1 = i1, n1
|
|
jmat(j1, i1, l1) = jmat(i1, j1, l1)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
|
|
END SUBROUTINE ceri_contract
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Contract exchange Electron Repulsion Integrals.
|
|
!> \param kmat ...
|
|
!> \param erint ...
|
|
!> \param pmat ...
|
|
!> \param nsize ...
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE eeri_contract(kmat, erint, pmat, nsize)
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(INOUT) :: kmat
|
|
TYPE(eri), DIMENSION(:), INTENT(IN) :: erint
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(IN) :: pmat
|
|
INTEGER, DIMENSION(0:), INTENT(IN) :: nsize
|
|
|
|
INTEGER :: i1, i2, ij1, ij2, j1, j2, l1, l2, lh, &
|
|
ll, n1, n2, nu
|
|
REAL(KIND=dp) :: almn, eint, f1, f2
|
|
REAL(KIND=dp), DIMENSION(0:maxfac) :: arho
|
|
|
|
arho = 0._dp
|
|
DO ll = 0, maxfac, 2
|
|
lh = ll/2
|
|
arho(ll) = fac(ll)/fac(lh)**2
|
|
END DO
|
|
|
|
kmat(:, :, :) = 0._dp
|
|
ll = 0
|
|
DO l1 = 0, lmat
|
|
n1 = nsize(l1)
|
|
DO l2 = 0, l1
|
|
n2 = nsize(l2)
|
|
DO nu = ABS(l1 - l2), l1 + l2, 2
|
|
almn = arho(-l1 + l2 + nu)*arho(l1 - l2 + nu)*arho(l1 + l2 - nu)/(REAL(l1 + l2 + nu + 1, dp)*arho(l1 + l2 + nu))
|
|
almn = -0.5_dp*almn
|
|
ll = ll + 1
|
|
ij1 = 0
|
|
DO i1 = 1, n1
|
|
DO j1 = i1, n1
|
|
ij1 = ij1 + 1
|
|
f1 = 1._dp
|
|
IF (i1 /= j1) f1 = 2._dp
|
|
ij2 = 0
|
|
DO i2 = 1, n2
|
|
DO j2 = i2, n2
|
|
ij2 = ij2 + 1
|
|
f2 = 1._dp
|
|
IF (i2 /= j2) f2 = 2._dp
|
|
eint = erint(ll)%int(ij1, ij2)
|
|
IF (l1 == l2) THEN
|
|
kmat(i1, j1, l1) = kmat(i1, j1, l1) + f2*almn*pmat(i2, j2, l2)*eint
|
|
ELSE
|
|
kmat(i1, j1, l1) = kmat(i1, j1, l1) + f2*almn*pmat(i2, j2, l2)*eint
|
|
kmat(i2, j2, l2) = kmat(i2, j2, l2) + f1*almn*pmat(i1, j1, l1)*eint
|
|
END IF
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END DO
|
|
DO l1 = 0, lmat
|
|
n1 = nsize(l1)
|
|
DO i1 = 1, n1
|
|
DO j1 = i1, n1
|
|
kmat(j1, i1, l1) = kmat(i1, j1, l1)
|
|
END DO
|
|
END DO
|
|
END DO
|
|
|
|
END SUBROUTINE eeri_contract
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate the error matrix for each angular momentum.
|
|
!> \param emat error matrix
|
|
!> \param demax the largest absolute value of error matrix elements
|
|
!> \param kmat Kohn-Sham matrix
|
|
!> \param pmat electron density matrix
|
|
!> \param umat transformation matrix which reduce overlap matrix to its unitary form
|
|
!> \param upmat transformation matrix which reduce density matrix to its unitary form
|
|
!> \param nval number of linear-independent contracted basis functions
|
|
!> \param nbs number of contracted basis functions
|
|
!> \par History
|
|
!> * 08.2008 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE err_matrix(emat, demax, kmat, pmat, umat, upmat, nval, nbs)
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(OUT) :: emat
|
|
REAL(KIND=dp), INTENT(OUT) :: demax
|
|
REAL(KIND=dp), DIMENSION(:, :, 0:), INTENT(IN) :: kmat, pmat, umat, upmat
|
|
INTEGER, DIMENSION(0:), INTENT(IN) :: nval, nbs
|
|
|
|
INTEGER :: l, m, n
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: tkmat, tpmat
|
|
|
|
emat = 0._dp
|
|
DO l = 0, lmat
|
|
n = nval(l)
|
|
m = nbs(l)
|
|
IF (m > 0) THEN
|
|
ALLOCATE (tkmat(1:m, 1:m), tpmat(1:m, 1:m))
|
|
tkmat = 0._dp
|
|
tpmat = 0._dp
|
|
tkmat(1:m, 1:m) = MATMUL(TRANSPOSE(umat(1:n, 1:m, l)), MATMUL(kmat(1:n, 1:n, l), umat(1:n, 1:m, l)))
|
|
tpmat(1:m, 1:m) = MATMUL(TRANSPOSE(umat(1:n, 1:m, l)), MATMUL(pmat(1:n, 1:n, l), umat(1:n, 1:m, l)))
|
|
tpmat(1:m, 1:m) = MATMUL(upmat(1:m, 1:m, l), MATMUL(tpmat(1:m, 1:m), upmat(1:m, 1:m, l)))
|
|
|
|
emat(1:m, 1:m, l) = MATMUL(tkmat(1:m, 1:m), tpmat(1:m, 1:m)) - MATMUL(tpmat(1:m, 1:m), tkmat(1:m, 1:m))
|
|
|
|
DEALLOCATE (tkmat, tpmat)
|
|
END IF
|
|
END DO
|
|
demax = MAXVAL(ABS(emat))
|
|
|
|
END SUBROUTINE err_matrix
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate Slater density on a radial grid.
|
|
!> \param density1 alpha-spin electron density
|
|
!> \param density2 beta-spin electron density
|
|
!> \param zcore nuclear charge
|
|
!> \param state electronic state
|
|
!> \param grid atomic radial grid
|
|
!> \par History
|
|
!> * 06.2018 bugfix [Rustam Khaliullin]
|
|
!> * 02.2010 unrestricted KS and HF methods [Juerg Hutter]
|
|
!> * 12.2008 created [Juerg Hutter]
|
|
!> \note An initial electron density to guess atomic orbitals.
|
|
! **************************************************************************************************
|
|
SUBROUTINE slater_density(density1, density2, zcore, state, grid)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: density1, density2
|
|
INTEGER, INTENT(IN) :: zcore
|
|
TYPE(atom_state), INTENT(IN) :: state
|
|
TYPE(grid_atom_type), INTENT(IN) :: grid
|
|
|
|
INTEGER :: counter, i, l, mc, mm(0:lmat), mo, n, ns
|
|
INTEGER, DIMENSION(lmat+1, 20) :: ne
|
|
REAL(KIND=dp) :: a, pf
|
|
|
|
! fill out a table with the total number of electrons
|
|
! core + valence. format ne(l,n)
|
|
ns = SIZE(ne, 2)
|
|
ne = 0
|
|
mm = get_maxn_occ(state%core)
|
|
DO l = 0, lmat
|
|
mo = state%maxn_occ(l)
|
|
IF (SUM(state%core(l, :)) == 0) THEN
|
|
CPASSERT(ns >= l + mo)
|
|
DO counter = 1, mo
|
|
ne(l + 1, l + counter) = NINT(state%occ(l, counter))
|
|
END DO
|
|
ELSE
|
|
mc = mm(l) ! number of levels in the core
|
|
CPASSERT(SUM(state%occ(l, 1:mc)) == 0)
|
|
CPASSERT(ns >= l + mc)
|
|
DO counter = 1, mc
|
|
ne(l + 1, l + counter) = NINT(state%core(l, counter))
|
|
END DO
|
|
CPASSERT(ns >= l + mc + mo)
|
|
DO counter = mc + 1, mc + mo
|
|
ne(l + 1, l + counter) = NINT(state%occ(l, counter))
|
|
END DO
|
|
END IF
|
|
END DO
|
|
|
|
density1 = 0._dp
|
|
density2 = 0._dp
|
|
DO l = 0, state%maxl_occ
|
|
DO i = 1, SIZE(state%occ, 2)
|
|
IF (state%occ(l, i) > 0._dp) THEN
|
|
n = i + l
|
|
a = srules(zcore, ne, n, l)
|
|
pf = 1._dp/SQRT(fac(2*n))*(2._dp*a)**(n + 0.5_dp)
|
|
IF (state%multiplicity == -1) THEN
|
|
density1(:) = density1(:) + state%occ(l, i)/fourpi*(grid%rad(:)**(n - 1)*EXP(-a*grid%rad(:))*pf)**2
|
|
ELSE
|
|
density1(:) = density1(:) + state%occa(l, i)/fourpi*(grid%rad(:)**(n - 1)*EXP(-a*grid%rad(:))*pf)**2
|
|
density2(:) = density2(:) + state%occb(l, i)/fourpi*(grid%rad(:)**(n - 1)*EXP(-a*grid%rad(:))*pf)**2
|
|
END IF
|
|
END IF
|
|
END DO
|
|
END DO
|
|
|
|
END SUBROUTINE slater_density
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate the functional derivative of the Wigner (correlation) - Slater (exchange)
|
|
!> density functional.
|
|
!> \param rho electron density on a radial grid
|
|
!> \param vxc (output) exchange-correlation potential
|
|
!> \par History
|
|
!> * 12.2008 created [Juerg Hutter]
|
|
!> \note A model XC-potential to guess atomic orbitals.
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE wigner_slater_functional(rho, vxc)
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: rho
|
|
REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: vxc
|
|
|
|
INTEGER :: i
|
|
REAL(KIND=dp) :: ec, ex, rs, vc, vx
|
|
|
|
vxc = 0._dp
|
|
DO i = 1, SIZE(rho)
|
|
IF (rho(i) > 1.e-20_dp) THEN
|
|
! 3/4 * (3/pi)^{1/3} == 0.7385588
|
|
ex = -0.7385588_dp*rho(i)**0.333333333_dp
|
|
vx = 1.333333333_dp*ex
|
|
rs = (3._dp/fourpi/rho(i))**0.333333333_dp
|
|
ec = -0.88_dp/(rs + 7.8_dp)
|
|
vc = ec*(1._dp + rs/(3._dp*(rs + 7.8_dp)))
|
|
vxc(i) = vx + vc
|
|
END IF
|
|
END DO
|
|
|
|
END SUBROUTINE wigner_slater_functional
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Check that the atomic multiplicity is consistent with the electronic structure method.
|
|
!> \param method electronic structure method
|
|
!> \param multiplicity multiplicity
|
|
!> \return consistency status
|
|
!> \par History
|
|
!> * 11.2009 unrestricted KS and HF methods [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
PURE FUNCTION atom_consistent_method(method, multiplicity) RESULT(consistent)
|
|
INTEGER, INTENT(IN) :: method, multiplicity
|
|
LOGICAL :: consistent
|
|
|
|
! multiplicity == -1 means it has not been specified explicitly;
|
|
! see the source code of the subroutine atom_set_occupation() for further details.
|
|
SELECT CASE (method)
|
|
CASE DEFAULT
|
|
consistent = .FALSE.
|
|
CASE (do_rks_atom)
|
|
consistent = (multiplicity == -1)
|
|
CASE (do_uks_atom)
|
|
consistent = (multiplicity /= -1)
|
|
CASE (do_rhf_atom)
|
|
consistent = (multiplicity == -1)
|
|
CASE (do_uhf_atom)
|
|
consistent = (multiplicity /= -1)
|
|
CASE (do_rohf_atom)
|
|
consistent = .FALSE.
|
|
END SELECT
|
|
|
|
END FUNCTION atom_consistent_method
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate the total electron density at R=0.
|
|
!> \param atom information about the atomic kind
|
|
!> \param rho0 (output) computed density
|
|
!> \par History
|
|
!> * 05.2016 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE get_rho0(atom, rho0)
|
|
TYPE(atom_type), INTENT(IN) :: atom
|
|
REAL(KIND=dp), INTENT(OUT) :: rho0
|
|
|
|
INTEGER :: m0, m1, m2, method, nr
|
|
LOGICAL :: nlcc, spinpol
|
|
REAL(KIND=dp) :: d0, d1, d2, r0, r1, r2, w0, w1
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: xfun
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: rho
|
|
|
|
method = atom%method_type
|
|
SELECT CASE (method)
|
|
CASE (do_rks_atom, do_rhf_atom)
|
|
spinpol = .FALSE.
|
|
CASE (do_uks_atom, do_uhf_atom)
|
|
spinpol = .TRUE.
|
|
CASE (do_rohf_atom)
|
|
CPABORT("")
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
END SELECT
|
|
|
|
nr = atom%basis%grid%nr
|
|
nlcc = .FALSE.
|
|
IF (atom%potential%ppot_type == gth_pseudo) THEN
|
|
nlcc = atom%potential%gth_pot%nlcc
|
|
ELSE IF (atom%potential%ppot_type == upf_pseudo) THEN
|
|
nlcc = atom%potential%upf_pot%core_correction
|
|
ELSE IF (atom%potential%ppot_type == sgp_pseudo) THEN
|
|
nlcc = atom%potential%sgp_pot%has_nlcc
|
|
END IF
|
|
IF (nlcc) THEN
|
|
ALLOCATE (xfun(nr))
|
|
END IF
|
|
|
|
m0 = MAXVAL(MINLOC(atom%basis%grid%rad))
|
|
IF (m0 == nr) THEN
|
|
m1 = m0 - 1
|
|
m2 = m0 - 2
|
|
ELSE IF (m0 == 1) THEN
|
|
m1 = 2
|
|
m2 = 3
|
|
ELSE
|
|
CPABORT("GRID Definition incompatible")
|
|
END IF
|
|
r0 = atom%basis%grid%rad(m0)
|
|
r1 = atom%basis%grid%rad(m1)
|
|
r2 = atom%basis%grid%rad(m2)
|
|
w0 = r1/(r1 - r0)
|
|
w1 = 1 - w0
|
|
|
|
IF (spinpol) THEN
|
|
ALLOCATE (rho(nr, 2))
|
|
CALL atom_density(rho(:, 1), atom%orbitals%pmata, atom%basis, atom%state%maxl_occ, typ="RHO")
|
|
CALL atom_density(rho(:, 2), atom%orbitals%pmatb, atom%basis, atom%state%maxl_occ, typ="RHO")
|
|
IF (nlcc) THEN
|
|
xfun = 0.0_dp
|
|
CALL atom_core_density(xfun(:), atom%potential, typ="RHO", rr=atom%basis%grid%rad)
|
|
rho(:, 1) = rho(:, 1) + 0.5_dp*xfun(:)
|
|
rho(:, 2) = rho(:, 2) + 0.5_dp*xfun(:)
|
|
END IF
|
|
rho(:, 1) = rho(:, 1) + rho(:, 2)
|
|
ELSE
|
|
ALLOCATE (rho(nr, 1))
|
|
CALL atom_density(rho(:, 1), atom%orbitals%pmat, atom%basis, atom%state%maxl_occ, typ="RHO")
|
|
IF (nlcc) THEN
|
|
CALL atom_core_density(rho(:, 1), atom%potential, typ="RHO", rr=atom%basis%grid%rad)
|
|
END IF
|
|
END IF
|
|
d0 = rho(m0, 1)
|
|
d1 = rho(m1, 1)
|
|
d2 = rho(m2, 1)
|
|
|
|
rho0 = w0*d0 + w1*d1
|
|
rho0 = MAX(rho0, 0.0_dp)
|
|
|
|
DEALLOCATE (rho)
|
|
IF (nlcc) THEN
|
|
DEALLOCATE (xfun)
|
|
END IF
|
|
|
|
END SUBROUTINE get_rho0
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Print condition numbers of the given atomic basis set.
|
|
!> \param basis atomic basis set
|
|
!> \param crad atomic covalent radius
|
|
!> \param iw output file unit
|
|
!> \par History
|
|
!> * 05.2016 created [Juerg Hutter]
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_condnumber(basis, crad, iw)
|
|
TYPE(atom_basis_type), POINTER :: basis
|
|
REAL(KIND=dp) :: crad
|
|
INTEGER, INTENT(IN) :: iw
|
|
|
|
INTEGER :: i
|
|
REAL(KIND=dp) :: ci
|
|
REAL(KIND=dp), DIMENSION(10) :: cnum, rad
|
|
|
|
WRITE (iw, '(/,A,F8.4)') " Basis Set Condition Numbers: 2*covalent radius=", 2*crad
|
|
CALL init_orbital_pointers(lmat)
|
|
CALL init_spherical_harmonics(lmat, 0)
|
|
cnum = 0.0_dp
|
|
DO i = 1, 9
|
|
ci = 2.0_dp*(0.85_dp + i*0.05_dp)
|
|
rad(i) = crad*ci
|
|
CALL atom_basis_condnum(basis, rad(i), cnum(i))
|
|
WRITE (iw, '(A,F15.3,T50,A,F14.4)') " Lattice constant:", &
|
|
rad(i), "Condition number:", cnum(i)
|
|
END DO
|
|
rad(10) = 0.01_dp
|
|
CALL atom_basis_condnum(basis, rad(10), cnum(10))
|
|
WRITE (iw, '(A,A,T50,A,F14.4)') " Lattice constant:", &
|
|
" Inf", "Condition number:", cnum(i)
|
|
CALL deallocate_orbital_pointers
|
|
CALL deallocate_spherical_harmonics
|
|
|
|
END SUBROUTINE atom_condnumber
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Estimate completeness of the given atomic basis set.
|
|
!> \param basis atomic basis set
|
|
!> \param zv atomic number
|
|
!> \param iw output file unit
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_completeness(basis, zv, iw)
|
|
TYPE(atom_basis_type), POINTER :: basis
|
|
INTEGER, INTENT(IN) :: zv, iw
|
|
|
|
INTEGER :: i, j, l, ll, m, n, nbas, nl, nr
|
|
INTEGER, DIMENSION(0:lmat) :: nelem, nlmax, nlmin
|
|
INTEGER, DIMENSION(lmat+1, 7) :: ne
|
|
REAL(KIND=dp) :: al, c1, c2, pf
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: sfun
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: bmat
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: omat
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :, :) :: sint
|
|
REAL(KIND=dp), DIMENSION(0:lmat, 10) :: snl
|
|
REAL(KIND=dp), DIMENSION(2) :: sse
|
|
REAL(KIND=dp), DIMENSION(lmat+1, 7) :: sexp
|
|
|
|
ne = 0
|
|
nelem = 0
|
|
nelem(0:3) = ptable(zv)%e_conv(0:3)
|
|
DO l = 0, lmat
|
|
ll = 2*(2*l + 1)
|
|
DO i = 1, 7
|
|
IF (nelem(l) >= ll) THEN
|
|
ne(l + 1, i) = ll
|
|
nelem(l) = nelem(l) - ll
|
|
ELSE IF (nelem(l) > 0) THEN
|
|
ne(l + 1, i) = nelem(l)
|
|
nelem(l) = 0
|
|
ELSE
|
|
EXIT
|
|
END IF
|
|
END DO
|
|
END DO
|
|
|
|
nlmin = 1
|
|
nlmax = 1
|
|
DO l = 0, lmat
|
|
nlmin(l) = l + 1
|
|
DO i = 1, 7
|
|
IF (ne(l + 1, i) > 0) THEN
|
|
nlmax(l) = i + l
|
|
END IF
|
|
END DO
|
|
nlmax(l) = MAX(nlmax(l), nlmin(l) + 1)
|
|
END DO
|
|
|
|
! Slater exponents
|
|
sexp = 0.0_dp
|
|
DO l = 0, lmat
|
|
sse(1) = 0.05_dp
|
|
sse(2) = 10.0_dp
|
|
DO i = l + 1, 7
|
|
sexp(l + 1, i) = srules(zv, ne, i, l)
|
|
IF (ne(l + 1, i - l) > 0) THEN
|
|
sse(1) = MAX(sse(1), sexp(l + 1, i))
|
|
sse(2) = MIN(sse(2), sexp(l + 1, i))
|
|
END IF
|
|
END DO
|
|
DO i = 1, 10
|
|
snl(l, i) = ABS(2._dp*sse(1) - 0.5_dp*sse(2))/9._dp*REAL(i - 1, KIND=dp) + 0.5_dp*MIN(sse(1), sse(2))
|
|
END DO
|
|
END DO
|
|
|
|
nbas = MAXVAL(basis%nbas)
|
|
ALLOCATE (omat(nbas, nbas, 0:lmat))
|
|
nr = SIZE(basis%bf, 1)
|
|
ALLOCATE (sfun(nr), sint(10, 2, nbas, 0:lmat))
|
|
sint = 0._dp
|
|
|
|
! calculate overlaps between test functions and basis
|
|
DO l = 0, lmat
|
|
DO i = 1, 10
|
|
al = snl(l, i)
|
|
nl = nlmin(l)
|
|
pf = (2._dp*al)**nl*SQRT(2._dp*al/fac(2*nl))
|
|
sfun(1:nr) = pf*basis%grid%rad(1:nr)**(nl - 1)*EXP(-al*basis%grid%rad(1:nr))
|
|
DO j = 1, basis%nbas(l)
|
|
sint(i, 1, j, l) = SUM(sfun(1:nr)*basis%bf(1:nr, j, l)*basis%grid%wr(1:nr))
|
|
END DO
|
|
nl = nlmax(l)
|
|
pf = (2._dp*al)**nl*SQRT(2._dp*al/fac(2*nl))
|
|
sfun(1:nr) = pf*basis%grid%rad(1:nr)**(nl - 1)*EXP(-al*basis%grid%rad(1:nr))
|
|
DO j = 1, basis%nbas(l)
|
|
sint(i, 2, j, l) = SUM(sfun(1:nr)*basis%bf(1:nr, j, l)*basis%grid%wr(1:nr))
|
|
END DO
|
|
END DO
|
|
END DO
|
|
|
|
DO l = 0, lmat
|
|
n = basis%nbas(l)
|
|
IF (n <= 0) CYCLE
|
|
m = basis%nprim(l)
|
|
SELECT CASE (basis%basis_type)
|
|
CASE DEFAULT
|
|
CPABORT("")
|
|
CASE (GTO_BASIS)
|
|
CALL sg_overlap(omat(1:n, 1:n, l), l, basis%am(1:n, l), basis%am(1:n, l))
|
|
CASE (CGTO_BASIS)
|
|
ALLOCATE (bmat(m, m))
|
|
CALL sg_overlap(bmat(1:m, 1:m), l, basis%am(1:m, l), basis%am(1:m, l))
|
|
CALL contract2(omat(1:n, 1:n, l), bmat(1:m, 1:m), basis%cm(1:m, 1:n, l))
|
|
DEALLOCATE (bmat)
|
|
CASE (STO_BASIS)
|
|
CALL sto_overlap(omat(1:n, 1:n, l), basis%ns(1:n, l), basis%as(1:n, l), &
|
|
basis%ns(1:n, l), basis%as(1:n, l))
|
|
CASE (NUM_BASIS)
|
|
CPABORT("")
|
|
END SELECT
|
|
CALL invmat_symm(omat(1:n, 1:n, l))
|
|
END DO
|
|
|
|
WRITE (iw, '(/,A)') " Basis Set Completeness Estimates"
|
|
DO l = 0, lmat
|
|
n = basis%nbas(l)
|
|
IF (n <= 0) CYCLE
|
|
WRITE (iw, '(A,I3)') " L-quantum number: ", l
|
|
WRITE (iw, '(A,T31,A,I2,T61,A,I2)') " Slater Exponent", "Completeness n-qm=", nlmin(l), &
|
|
"Completeness n-qm=", nlmax(l)
|
|
DO i = 10, 1, -1
|
|
c1 = DOT_PRODUCT(sint(i, 1, 1:n, l), MATMUL(omat(1:n, 1:n, l), sint(i, 1, 1:n, l)))
|
|
c2 = DOT_PRODUCT(sint(i, 2, 1:n, l), MATMUL(omat(1:n, 1:n, l), sint(i, 2, 1:n, l)))
|
|
WRITE (iw, "(T6,F14.6,T41,F10.6,T71,F10.6)") snl(l, i), c1, c2
|
|
END DO
|
|
END DO
|
|
|
|
DEALLOCATE (omat, sfun, sint)
|
|
|
|
END SUBROUTINE atom_completeness
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Calculate the condition number of the given atomic basis set.
|
|
!> \param basis atomic basis set
|
|
!> \param rad lattice constant (e.g. doubled atomic covalent radius)
|
|
!> \param cnum (output) condition number
|
|
! **************************************************************************************************
|
|
SUBROUTINE atom_basis_condnum(basis, rad, cnum)
|
|
TYPE(atom_basis_type) :: basis
|
|
REAL(KIND=dp), INTENT(IN) :: rad
|
|
REAL(KIND=dp), INTENT(OUT) :: cnum
|
|
|
|
INTEGER :: ia, ib, imax, info, ix, iy, iz, ja, jb, &
|
|
ka, kb, l, la, lb, lwork, na, nb, &
|
|
nbas, nna, nnb
|
|
INTEGER, ALLOCATABLE, DIMENSION(:, :) :: ibptr
|
|
REAL(KIND=dp) :: r1, r2, reps, rmax
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: weig, work
|
|
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: smat
|
|
REAL(KIND=dp), DIMENSION(2*lmat+1, 2*lmat+1) :: sab
|
|
REAL(KIND=dp), DIMENSION(3) :: rab
|
|
REAL(KIND=dp), DIMENSION(:), POINTER :: zeta, zetb
|
|
|
|
! Number of spherical Gaussian orbitals with angular momentum lmat: nso(lmat) = 2*lmat+1
|
|
|
|
! total number of basis functions
|
|
nbas = 0
|
|
DO l = 0, lmat
|
|
nbas = nbas + basis%nbas(l)*(2*l + 1)
|
|
END DO
|
|
|
|
ALLOCATE (smat(nbas, nbas), ibptr(nbas, 0:lmat))
|
|
smat = 0.0_dp
|
|
ibptr = 0
|
|
na = 0
|
|
DO l = 0, lmat
|
|
DO ia = 1, basis%nbas(l)
|
|
ibptr(ia, l) = na + 1
|
|
na = na + (2*l + 1)
|
|
END DO
|
|
END DO
|
|
|
|
reps = 1.e-14_dp
|
|
IF (basis%basis_type == GTO_BASIS .OR. &
|
|
basis%basis_type == CGTO_BASIS) THEN
|
|
DO la = 0, lmat
|
|
na = basis%nprim(la)
|
|
nna = 2*la + 1
|
|
IF (na == 0) CYCLE
|
|
zeta => basis%am(:, la)
|
|
DO lb = 0, lmat
|
|
nb = basis%nprim(lb)
|
|
nnb = 2*lb + 1
|
|
IF (nb == 0) CYCLE
|
|
zetb => basis%am(:, lb)
|
|
DO ia = 1, na
|
|
DO ib = 1, nb
|
|
IF (rad < 0.1_dp) THEN
|
|
imax = 0
|
|
ELSE
|
|
r1 = exp_radius(la, zeta(ia), reps, 1.0_dp)
|
|
r2 = exp_radius(lb, zetb(ib), reps, 1.0_dp)
|
|
rmax = MAX(2._dp*r1, 2._dp*r2)
|
|
imax = INT(rmax/rad) + 1
|
|
END IF
|
|
IF (imax > 1) THEN
|
|
CALL overlap_ab_sp(la, zeta(ia), lb, zetb(ib), rad, sab)
|
|
IF (basis%basis_type == GTO_BASIS) THEN
|
|
ja = ibptr(ia, la)
|
|
jb = ibptr(ib, lb)
|
|
smat(ja:ja + nna - 1, jb:jb + nnb - 1) = smat(ja:ja + nna - 1, jb:jb + nnb - 1) + sab(1:nna, 1:nnb)
|
|
ELSEIF (basis%basis_type == CGTO_BASIS) THEN
|
|
DO ka = 1, basis%nbas(la)
|
|
DO kb = 1, basis%nbas(lb)
|
|
ja = ibptr(ka, la)
|
|
jb = ibptr(kb, lb)
|
|
smat(ja:ja + nna - 1, jb:jb + nnb - 1) = smat(ja:ja + nna - 1, jb:jb + nnb - 1) + &
|
|
sab(1:nna, 1:nnb)*basis%cm(ia, ka, la)*basis%cm(ib, kb, lb)
|
|
END DO
|
|
END DO
|
|
END IF
|
|
ELSE
|
|
DO ix = -imax, imax
|
|
rab(1) = rad*ix
|
|
DO iy = -imax, imax
|
|
rab(2) = rad*iy
|
|
DO iz = -imax, imax
|
|
rab(3) = rad*iz
|
|
CALL overlap_ab_s(la, zeta(ia), lb, zetb(ib), rab, sab)
|
|
IF (basis%basis_type == GTO_BASIS) THEN
|
|
ja = ibptr(ia, la)
|
|
jb = ibptr(ib, lb)
|
|
smat(ja:ja + nna - 1, jb:jb + nnb - 1) = smat(ja:ja + nna - 1, jb:jb + nnb - 1) &
|
|
+ sab(1:nna, 1:nnb)
|
|
ELSEIF (basis%basis_type == CGTO_BASIS) THEN
|
|
DO ka = 1, basis%nbas(la)
|
|
DO kb = 1, basis%nbas(lb)
|
|
ja = ibptr(ka, la)
|
|
jb = ibptr(kb, lb)
|
|
smat(ja:ja + nna - 1, jb:jb + nnb - 1) = &
|
|
smat(ja:ja + nna - 1, jb:jb + nnb - 1) + &
|
|
sab(1:nna, 1:nnb)*basis%cm(ia, ka, la)*basis%cm(ib, kb, lb)
|
|
END DO
|
|
END DO
|
|
END IF
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END IF
|
|
END DO
|
|
END DO
|
|
END DO
|
|
END DO
|
|
ELSE
|
|
CPABORT("Condition number not available for this basis type")
|
|
END IF
|
|
|
|
info = 0
|
|
lwork = MAX(nbas*nbas, nbas + 100)
|
|
ALLOCATE (weig(nbas), work(lwork))
|
|
|
|
CALL lapack_ssyev("N", "U", nbas, smat, nbas, weig, work, lwork, info)
|
|
CPASSERT(info == 0)
|
|
IF (weig(1) < 0.0_dp) THEN
|
|
cnum = 100._dp
|
|
ELSE
|
|
cnum = ABS(weig(nbas)/weig(1))
|
|
cnum = LOG10(cnum)
|
|
END IF
|
|
|
|
DEALLOCATE (smat, ibptr, weig, work)
|
|
|
|
END SUBROUTINE atom_basis_condnum
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Transform a matrix expressed in terms of a uncontracted basis set to a contracted one.
|
|
!> \param int (output) contracted matrix
|
|
!> \param omat uncontracted matrix
|
|
!> \param cm matrix of contraction coefficients
|
|
! **************************************************************************************************
|
|
SUBROUTINE contract2(int, omat, cm)
|
|
REAL(dp), DIMENSION(:, :), INTENT(INOUT) :: int
|
|
REAL(dp), DIMENSION(:, :), INTENT(IN) :: omat, cm
|
|
|
|
CHARACTER(len=*), PARAMETER :: routineN = 'contract2'
|
|
|
|
INTEGER :: handle, m, n
|
|
|
|
CALL timeset(routineN, handle)
|
|
|
|
n = SIZE(int, 1)
|
|
m = SIZE(omat, 1)
|
|
|
|
INT(1:n, 1:n) = MATMUL(TRANSPOSE(cm(1:m, 1:n)), MATMUL(omat(1:m, 1:m), cm(1:m, 1:n)))
|
|
|
|
CALL timestop(handle)
|
|
|
|
END SUBROUTINE contract2
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Same as contract2(), but add the new contracted matrix to the old one
|
|
!> instead of overwriting it.
|
|
!> \param int (input/output) contracted matrix to be updated
|
|
!> \param omat uncontracted matrix
|
|
!> \param cm matrix of contraction coefficients
|
|
! **************************************************************************************************
|
|
SUBROUTINE contract2add(int, omat, cm)
|
|
REAL(dp), DIMENSION(:, :), INTENT(INOUT) :: int
|
|
REAL(dp), DIMENSION(:, :), INTENT(IN) :: omat, cm
|
|
|
|
CHARACTER(len=*), PARAMETER :: routineN = 'contract2add'
|
|
|
|
INTEGER :: handle, m, n
|
|
|
|
CALL timeset(routineN, handle)
|
|
|
|
n = SIZE(int, 1)
|
|
m = SIZE(omat, 1)
|
|
|
|
INT(1:n, 1:n) = INT(1:n, 1:n) + MATMUL(TRANSPOSE(cm(1:m, 1:n)), MATMUL(omat(1:m, 1:m), cm(1:m, 1:n)))
|
|
|
|
CALL timestop(handle)
|
|
|
|
END SUBROUTINE contract2add
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Contract a matrix of Electron Repulsion Integrals (ERI-s).
|
|
!> \param eri (output) contracted ERI-s
|
|
!> \param omat uncontracted ERI-s
|
|
!> \param cm1 first matrix of contraction coefficients
|
|
!> \param cm2 second matrix of contraction coefficients
|
|
! **************************************************************************************************
|
|
SUBROUTINE contract4(eri, omat, cm1, cm2)
|
|
REAL(dp), DIMENSION(:, :), INTENT(INOUT) :: eri
|
|
REAL(dp), DIMENSION(:, :), INTENT(IN) :: omat, cm1, cm2
|
|
|
|
CHARACTER(len=*), PARAMETER :: routineN = 'contract4'
|
|
|
|
INTEGER :: handle, i1, i2, m1, m2, mm1, mm2, n1, &
|
|
n2, nn1, nn2
|
|
REAL(dp), ALLOCATABLE, DIMENSION(:, :) :: amat, atran, bmat, btran, hint
|
|
|
|
CALL timeset(routineN, handle)
|
|
|
|
m1 = SIZE(cm1, 1)
|
|
n1 = SIZE(cm1, 2)
|
|
m2 = SIZE(cm2, 1)
|
|
n2 = SIZE(cm2, 2)
|
|
nn1 = SIZE(eri, 1)
|
|
nn2 = SIZE(eri, 2)
|
|
mm1 = SIZE(omat, 1)
|
|
mm2 = SIZE(omat, 2)
|
|
|
|
ALLOCATE (amat(m1, m1), atran(n1, n1), bmat(m2, m2), btran(n2, n2))
|
|
ALLOCATE (hint(mm1, nn2))
|
|
|
|
DO i1 = 1, mm1
|
|
CALL iunpack(bmat(1:m2, 1:m2), omat(i1, 1:mm2), m2)
|
|
CALL contract2(btran(1:n2, 1:n2), bmat(1:m2, 1:m2), cm2(1:m2, 1:n2))
|
|
CALL ipack(btran(1:n2, 1:n2), hint(i1, 1:nn2), n2)
|
|
END DO
|
|
|
|
DO i2 = 1, nn2
|
|
CALL iunpack(amat(1:m1, 1:m1), hint(1:mm1, i2), m1)
|
|
CALL contract2(atran(1:n1, 1:n1), amat(1:m1, 1:m1), cm1(1:m1, 1:n1))
|
|
CALL ipack(atran(1:n1, 1:n1), eri(1:nn1, i2), n1)
|
|
END DO
|
|
|
|
DEALLOCATE (amat, atran, bmat, btran)
|
|
DEALLOCATE (hint)
|
|
|
|
CALL timestop(handle)
|
|
|
|
END SUBROUTINE contract4
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Pack an n x n square real matrix into a 1-D vector.
|
|
!> \param mat matrix to pack
|
|
!> \param vec resulting vector
|
|
!> \param n size of the matrix
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE ipack(mat, vec, n)
|
|
REAL(dp), DIMENSION(:, :), INTENT(IN) :: mat
|
|
REAL(dp), DIMENSION(:), INTENT(INOUT) :: vec
|
|
INTEGER, INTENT(IN) :: n
|
|
|
|
INTEGER :: i, ij, j
|
|
|
|
ij = 0
|
|
DO i = 1, n
|
|
DO j = i, n
|
|
ij = ij + 1
|
|
vec(ij) = mat(i, j)
|
|
END DO
|
|
END DO
|
|
|
|
END SUBROUTINE ipack
|
|
|
|
! **************************************************************************************************
|
|
!> \brief Unpack a 1-D real vector as a n x n square matrix.
|
|
!> \param mat resulting matrix
|
|
!> \param vec vector to unpack
|
|
!> \param n size of the matrix
|
|
! **************************************************************************************************
|
|
PURE SUBROUTINE iunpack(mat, vec, n)
|
|
REAL(dp), DIMENSION(:, :), INTENT(INOUT) :: mat
|
|
REAL(dp), DIMENSION(:), INTENT(IN) :: vec
|
|
INTEGER, INTENT(IN) :: n
|
|
|
|
INTEGER :: i, ij, j
|
|
|
|
ij = 0
|
|
DO i = 1, n
|
|
DO j = i, n
|
|
ij = ij + 1
|
|
mat(i, j) = vec(ij)
|
|
mat(j, i) = vec(ij)
|
|
END DO
|
|
END DO
|
|
|
|
END SUBROUTINE iunpack
|
|
|
|
END MODULE atom_utils
|