mirror of https://gitlab.com/QEF/q-e.git
513 lines
15 KiB
Fortran
513 lines
15 KiB
Fortran
!
|
|
! Copyright (C) 2004 PWSCF group
|
|
! This file is distributed under the terms of the
|
|
! GNU General Public License. See the file `License'
|
|
! in the root directory of the present distribution,
|
|
! or http://www.gnu.org/copyleft/gpl.txt .
|
|
!
|
|
!
|
|
! ---------------------------------------------------------------
|
|
subroutine find_coefficients &
|
|
(ik,psi,energy,r,dx,aenorm,vpot,c,c2,lam,ndm)
|
|
! -----------------------------------
|
|
!
|
|
! recherche des coefficients du polynome
|
|
!
|
|
use kinds, only : DP
|
|
USE random_numbers, ONLY : rndm
|
|
implicit none
|
|
integer, intent(in) :: ndm, lam, ik
|
|
real(DP), intent(in):: vpot(ndm), psi(ndm), r(ndm), dx, energy
|
|
real(DP), intent(out):: c2, c(6)
|
|
!
|
|
real(DP ):: amat(6,6), y(6), rc, aenorm
|
|
integer ipvt(6), i, info, n, ndiv
|
|
real(DP):: c2o, dc2, dcmin, newvalue, oldvalue, precision
|
|
real(DP), external:: funz
|
|
character(len=10) :: prec
|
|
!
|
|
do i = 1,6
|
|
c(i) = 0.0_dp
|
|
end do
|
|
rc = r(ik)
|
|
|
|
call fill_matrix(amat,rc,lam)
|
|
!
|
|
! LU factorization of matrix A = amat
|
|
!
|
|
call DGETRF(6,6,amat,6,ipvt,info)
|
|
!
|
|
! calculate coefficients y in linear system Ax = y
|
|
!
|
|
call eval_coeff(r,psi,ik,lam,energy,dx,vpot,y)
|
|
!
|
|
! find value of c2 solving norm-conservation requirement
|
|
! This is done by miniming with a random search funz**2
|
|
! We are looking for the smallest solution
|
|
!
|
|
c2o = 0.0_dp ! starting point
|
|
dc2 = 0.1_dp ! starting range
|
|
dcmin= 1.0e-10_dp ! minimum range
|
|
n = 0 ! counter of failed attempts
|
|
ndiv= 200 ! afte ndiv failed attempts reduce range
|
|
!
|
|
precision = 7.e-10_dp! a small number
|
|
!
|
|
oldvalue = funz(amat,ipvt,y,rc,ik,aenorm,c2o,c,c2, &
|
|
lam,r,dx,ndm)**2
|
|
10 continue
|
|
c2 = c2o + (0.5_dp - rndm())*dc2
|
|
newvalue = funz(amat,ipvt,y,rc,ik,aenorm,c2,c,c2,lam, &
|
|
r,dx,ndm)**2
|
|
if (newvalue < precision) return
|
|
if (newvalue < oldvalue) then
|
|
n=0
|
|
c2o = c2
|
|
oldvalue=newvalue
|
|
else
|
|
n=n+1
|
|
! after ndiv failed attempts reduce the size of the interval
|
|
if (n > ndiv) then
|
|
n=0
|
|
dc2=dc2/10.0_dp
|
|
end if
|
|
! if the size of the interval is too small quit
|
|
if (dc2 < 1.0d-12) then
|
|
c2=c2o
|
|
newvalue = funz(amat,ipvt,y,rc,ik, &
|
|
aenorm,c2,c,c2,lam,r,dx,ndm)**2
|
|
write(prec,'(e10.4)') newvalue
|
|
call infomsg('find_coeff','giving up minimization, ', &
|
|
'the error is still '//prec,-1)
|
|
return
|
|
end if
|
|
end if
|
|
go to 10
|
|
|
|
return
|
|
end subroutine find_coefficients
|
|
|
|
!
|
|
! ---------------------------------------------------------------
|
|
subroutine fill_matrix(amat,rc,l)
|
|
! ---------------------------------------------------------------
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP) :: amat(6,6),rc
|
|
integer :: l
|
|
!
|
|
! this routine fills the matrix with the coefficients taken
|
|
! from p,p',p'',p''', p(iv), where p is
|
|
! p(r)= c0 + c4 r^4 + c6 r^6 + c8 r^8 + ...
|
|
integer pr1(6),cr1(6),pr(6),cr(6),i,j
|
|
! matrices representing the coefficients (cr) and the powers of r ( pr)
|
|
data pr1/0,4,6,8,10,12/
|
|
data cr1/1,1,1,1,1,1/
|
|
|
|
do i=1,6
|
|
pr(i) = pr1(i)
|
|
cr(i) = cr1(i)
|
|
end do
|
|
do i = 1,5
|
|
! fill matrix row by row
|
|
do j = 1,6
|
|
amat(i,j) = cr(j) * rc**(pr(j)*1.0_dp)
|
|
end do
|
|
! derivate polynomial expression
|
|
do j = 1,6
|
|
cr(j) = cr(j) * pr(j)
|
|
pr(j) = max(0,pr(j)-1)
|
|
end do
|
|
end do
|
|
do j = 1,6
|
|
amat(6,j) = 0.0_dp
|
|
end do
|
|
amat(6,2) = 2.0_dp*l +5.0_dp
|
|
|
|
return
|
|
end subroutine fill_matrix
|
|
!
|
|
! ----------------------------------------------------------------
|
|
subroutine eval_coeff (r,psi,ik,l,energy,dx,vpot,y)
|
|
! ----------------------------------------------------------------
|
|
! calcule les coefficients dependant de la fct d'onde calculee
|
|
! avec tous les electrons. ces coefficients servent a la resolution
|
|
! du systeme lineaire.
|
|
! en entree : ik,nx,vpot comme dans le programme principal
|
|
! en sortie : une matrice colonne y contenant les coefficients
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
integer :: ik,l,lp1
|
|
real(DP):: r(ik+3),psi(ik+3),vpot(ik+3),energy,dx,y(6)
|
|
real(DP) p,dpp,d,vae,dvae,ddvae,rc,rc2,rc3
|
|
real(DP) deriv_7pts,deriv2_7pts
|
|
external deriv_7pts,deriv2_7pts
|
|
!
|
|
! evaluer p et p' ( dpp )
|
|
rc = r(ik)
|
|
p = psi(ik)
|
|
dpp = deriv_7pts(psi,ik,rc,dx)
|
|
if (p.lt.0.0) then
|
|
p = -p
|
|
dpp = -dpp
|
|
endif
|
|
d = dpp /p
|
|
! evaluer vae, dvae, ddvae
|
|
vae = vpot(ik)
|
|
dvae = deriv_7pts(vpot,ik,rc,dx)
|
|
ddvae = deriv2_7pts(vpot,ik,rc,dx)
|
|
! calcul de parametres intervenant dans les calculs successifs
|
|
lp1 = l + 1
|
|
rc2 = rc * rc
|
|
rc3 = rc2* rc
|
|
!
|
|
y(1) = log ( p / rc**lp1 )
|
|
y(2) = dpp/p - (lp1 / rc)
|
|
y(3) = vae - energy + (lp1*lp1)/rc2 - d*d
|
|
y(4) = dvae - 2.0_dp*(vae - energy + l*lp1/rc2)*d - &
|
|
2.0_dp*(lp1*lp1)/rc3 &
|
|
+ 2.0_dp*(d*d*d)
|
|
y(5) = ddvae - 2.0_dp*(dvae - 2.0_dp*l*lp1/rc3)*d &
|
|
+ 6.0_dp*lp1*lp1/(rc3*rc) &
|
|
- 2.0_dp*(vae - energy + l*lp1/rc2 - 3.0_dp*d*d)* &
|
|
(vae - energy + l*lp1/rc2 - d*d)
|
|
return
|
|
end subroutine eval_coeff
|
|
! ----------------------------------------------------------------
|
|
function deriv2_7pts(f,ik,rc,h)
|
|
! ----------------------------------------------------------------
|
|
!
|
|
! evaluates the second derivative of function f, the function
|
|
! is given numerically on logarithmic mesh r.
|
|
! nm = dimension of mesh
|
|
! ik = integer : position of the point in which the derivative
|
|
! will be evaluated.
|
|
! h is distance between x(i) and x(i+1) where
|
|
! r(i) = exp(x(i))/znesh & r(j) = exp(x(j))/znesh
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
integer :: a(7),n,nm,i,ik
|
|
real(DP) :: f(ik+3),rc,h,sum,sum1,deriv_7pts,deriv2_7pts
|
|
! coefficients for the formula in abramowitz & stegun p.914
|
|
! the formula is used for 7 points.
|
|
data a/4,-54,540,-980,540,-54,4/ ! these are coefficients
|
|
|
|
! formula for linear mesh
|
|
sum = 0.0_dp
|
|
do i=1,7
|
|
sum = sum + a(i)*f(i-4+ik)
|
|
end do
|
|
sum = 2.0_dp*sum/(720.0_dp*h**2)
|
|
! transform to logarithmic mesh
|
|
sum1 = deriv_7pts(f,ik,rc,h)
|
|
deriv2_7pts = sum/(rc*rc) - sum1 /rc
|
|
|
|
return
|
|
end function deriv2_7pts
|
|
! ---------------------------------------------------------------
|
|
function deriv_7pts(f,ik,rc,h)
|
|
! ---------------------------------------------------------------
|
|
! evaluates the first derivative of function f, the function
|
|
! is given numerically on logarithmic mesh r.
|
|
! nm = dimension of mesh
|
|
! ik = integer : position of the point in which the derivative
|
|
! will be evaluated.
|
|
! h is distance between x(i) and x(i+1) where
|
|
! r(i) = exp(x(i))/znesh & r(j) = exp(x(j))/znesh
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
integer :: a(7),n,ik,i
|
|
real(DP) :: f(ik+3),rc,h,sum,deriv_7pts
|
|
! coefficients for the formula in abramowitz & stegun p.914
|
|
data a/-12,108,-540,0,540,-108,12/
|
|
|
|
! formula for linear mesh
|
|
sum = 0
|
|
do i=1,7
|
|
sum = sum + a(i)*f(i-4+ik)
|
|
end do
|
|
deriv_7pts = sum/(720.0*h)
|
|
! transform to logarithmic mesh
|
|
deriv_7pts = deriv_7pts /rc
|
|
|
|
return
|
|
end function deriv_7pts
|
|
|
|
! --------------------------------------------------------
|
|
function funz(amat,ipvt,y,rc,ik,aenorm,x,c,c2, &
|
|
lam,r,dx,ndm)
|
|
! --------------------------------------------------------
|
|
! cette fonction est la fonction de c2 qu'il faut annuler
|
|
! pour trouver une valeur de c2 qui verifie la conservation
|
|
! de la charge de coeur.
|
|
! cette fonction calcule de plus a chaque fois les ci qui
|
|
! verifient les equations lineaires avec un c2 donne.
|
|
! en entree : une valeur de c2 donnee dans x
|
|
! en sortie, la valeur de la fonction pour cette valeur de x:
|
|
! c'est la fonction qui correspond a l'equation integrale
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
integer :: ndm,lam,ipvt(6),ik,mesh,i,istart,info
|
|
real(DP) :: funz, x, c(6), c2, amat(6,6), y(6), rc, aenorm, &
|
|
r(ndm), dx, chip2, psnorm, f0, f1, f2
|
|
external chip2
|
|
|
|
|
|
! resolution du systeme lineaire pour cette valeur de x (=c2)
|
|
c(1) = y(1) - x*rc**2 ! ajoute les termes en c2
|
|
c(2) = y(2) - 2*x*rc
|
|
c(3) = y(3) - 2*x
|
|
c(4) = y(4) ! pas de coeff en c2
|
|
c(5) = y(5)
|
|
c(6) = -x*x
|
|
! call dges(amat,6,6,ipvt,c,0) ! resoud le systeme
|
|
call DGETRS('N',6,1,amat,6,ipvt,c,6,info) ! resoud le systeme
|
|
! calcul de la norme de la pseudo-fonction d'onde
|
|
psnorm = 0.0_DP
|
|
istart= 2-mod(ik,2)
|
|
f2 = r(istart) * chip2(c,x,lam,r(istart))
|
|
do i = istart,ik-2,2
|
|
f0 = f2
|
|
f1 = r(i+1)*chip2(c,x,lam,r(i+1))
|
|
f2 = r(i+2)*chip2(c,x,lam,r(i+2))
|
|
psnorm = psnorm + f0 + 4*f1 + f2
|
|
end do
|
|
psnorm = psnorm * dx/3.0_dp + r(1)**(2*lam+3)/(2*lam+3)
|
|
funz = log( psnorm / aenorm )
|
|
!
|
|
return
|
|
end function funz
|
|
|
|
! --------------------------------------------------------
|
|
function chip2(c,c2,l,r)
|
|
! --------------------------------------------------------
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP):: chip2,c(6),c2,r,r2
|
|
integer :: l
|
|
|
|
r2 = r**2
|
|
chip2 = r2**(l+1) * exp(2.0_dp* &
|
|
(((((((c(6)*r2+c(5))*r2+c(4))*r2+c(3))*r2+c(2))*r2+c2)*r2)+c(1)))
|
|
|
|
return
|
|
end function chip2
|
|
|
|
! ----------------------------------------------------------------
|
|
function der3num(r,f,ik,nm,h)
|
|
! ----------------------------------------------------------------
|
|
! calcule la 3eme derivee d'une fonction donnee numeriquement
|
|
! sur un maillage logarithmique.
|
|
! en entree : r maillage; f fonction ; ik : position de l'eval.
|
|
! nm : dimension de f et r ; h comme dx dans
|
|
! le programme principal
|
|
! attention ! precision pas fantastique !
|
|
use kinds, only : DP
|
|
implicit none
|
|
integer :: nm, ik,i
|
|
real(DP):: r(nm),f(nm), y(7), h, deriv_7pts, deriv2_7pts
|
|
real(DP) :: der3num
|
|
|
|
do i=1,7
|
|
y(i) = deriv2_7pts(f,i+ik-4,r(i+ik-4),h)
|
|
end do
|
|
der3num = deriv_7pts(y,4,r(ik),h)
|
|
|
|
return
|
|
end function der3num
|
|
! ----------------------------------------------------------------
|
|
function der4num(r,f,ik,nm,h)
|
|
! ----------------------------------------------------------------
|
|
! idem que der3num, mais pour la 4eme derivee
|
|
! attention ! precision pas terrible !!
|
|
use kinds, only : DP
|
|
implicit none
|
|
integer:: nm,i,ik
|
|
real(DP) :: der4num, r(nm),f(nm),y(7),h,deriv2_7pts
|
|
|
|
do i=1,7
|
|
y(i) = deriv2_7pts(f,i+ik-4,r(i+ik-4),h)
|
|
end do
|
|
der4num= deriv2_7pts(y,4,r(ik),h)
|
|
|
|
return
|
|
end function der4num
|
|
|
|
! ----------------------------------------------------------------
|
|
function der3an(l,c,c2,rc)
|
|
! ----------------------------------------------------------------
|
|
!
|
|
! 3rd derivative of r^(l+1) e^p(r)
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
integer :: l
|
|
real(DP):: c(6), c2, rc,pr,dexpr,d2expr,d3expr, der3an
|
|
external pr,dexpr,d2expr,d3expr
|
|
|
|
der3an= (l-1)*l*(l+1)*rc**(l-2) *exp(pr(c,c2,rc)) + &
|
|
3.0_DP * l*(l+1)*rc**(l-1) * dexpr(c,c2,rc) + &
|
|
3.0_DP * (l+1)*rc**(l ) *d2expr(c,c2,rc) + &
|
|
rc**(l+1) *d3expr(c,c2,rc)
|
|
|
|
return
|
|
end function der3an
|
|
|
|
! ----------------------------------------------------------------
|
|
function der4an(l,c,c2,rc)
|
|
! ----------------------------------------------------------------
|
|
!
|
|
! 4rd derivative of r^(l+1) e^p(r)
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
integer l
|
|
real(DP):: c(6), c2, rc,pr,dexpr,d2expr,d3expr,d4expr,der4an
|
|
external pr,dexpr,d2expr,d3expr,d4expr
|
|
|
|
der4an = (l-2)*(l-1)*l*(l+1)*rc**(l-3) * exp(pr(c,c2,rc)) + &
|
|
4.0_DP *(l-1)*l*(l+1)*rc**(l-2) * dexpr(c,c2,rc) + &
|
|
6.0_DP * l*(l+1)*rc**(l-1) * d2expr(c,c2,rc) + &
|
|
4.0_DP * (l+1)*rc**(l ) * d3expr(c,c2,rc) + &
|
|
rc**(l+1) * d4expr(c,c2,rc)
|
|
|
|
return
|
|
end function der4an
|
|
! ----------------------------------------------------------------
|
|
function dexpr(c,c2,rc)
|
|
! ----------------------------------------------------------------
|
|
!
|
|
! 1st derivative of e^p(r)
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP) :: c(6), c2, rc, pr, dpr, dexpr
|
|
external pr, dpr
|
|
|
|
dexpr= exp(pr(c,c2,rc)) * dpr(c,c2,rc)
|
|
|
|
return
|
|
end function dexpr
|
|
! ----------------------------------------------------------------
|
|
function d2expr(c,c2,rc)
|
|
! ----------------------------------------------------------------
|
|
!
|
|
! 2nd derivative of e^p(r)
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP) :: c(6), c2, rc, pr, dpr, d2pr, d2expr
|
|
external pr, dpr, d2pr
|
|
|
|
d2expr= exp(pr(c,c2,rc)) * ( dpr(c,c2,rc)**2+d2pr(c,c2,rc) )
|
|
|
|
return
|
|
end function d2expr
|
|
! ----------------------------------------------------------------
|
|
function d3expr(c,c2,rc)
|
|
! ----------------------------------------------------------------
|
|
!
|
|
! 3nd derivative of e^p(r)
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP):: c(6), c2, rc, pr, dpr, d2pr, d3pr, d3expr
|
|
external pr, dpr, d2pr, d3pr
|
|
|
|
d3expr= exp(pr(c,c2,rc)) * ( dpr(c,c2,rc)**3 &
|
|
+ 3*dpr(c,c2,rc)*d2pr(c,c2,rc) &
|
|
+ d3pr(c,c2,rc) )
|
|
|
|
return
|
|
end function d3expr
|
|
! ----------------------------------------------------------------
|
|
function d4expr(c,c2,rc)
|
|
! ----------------------------------------------------------------
|
|
!
|
|
! 4th derivative of e^p(r)
|
|
!
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP):: c(6), c2, rc, pr, dpr, d2pr, d3pr, d4pr, d4expr
|
|
external pr, dpr, d2pr, d3pr, d4pr
|
|
|
|
d4expr= exp(pr(c,c2,rc)) * ( dpr(c,c2,rc)**4 &
|
|
+ 6.0_DP* dpr(c,c2,rc)**2*d2pr(c,c2,rc) &
|
|
+ 3.0_DP*d2pr(c,c2,rc)**2 &
|
|
+ 4.0_DP* dpr(c,c2,rc)*d3pr(c,c2,rc) &
|
|
+ d4pr(c,c2,rc) )
|
|
|
|
return
|
|
end function d4expr
|
|
!
|
|
! --------------------------------------------------------
|
|
function pr(c,c2,x)
|
|
! --------------------------------------------------------
|
|
! cette fonction evalue le polynome dont les coefficients
|
|
! sont dans c d'apres la forme suivante
|
|
! p(x) = c(2)*x^4 + c(3) x^6 + c(4) x^8 + c(5) x^10
|
|
! +c(6) x^12 + c2*x^2 + c(1)
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP) :: c(6), c2, x, y, pr
|
|
|
|
y = x*x
|
|
pr = (((((c(6)*y+c(5))*y+c(4))*y+c(3))*y+c(2))*y+c2)*y &
|
|
+ c(1)
|
|
|
|
return
|
|
end function pr
|
|
!
|
|
function dpr(c,c2,x)
|
|
! --------------------------------------------------------
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP) :: c(6),c2,x,dpr
|
|
|
|
dpr = 2*c2*x + 4*c(2)*x**3 + 6*c(3)*x**5 + 8*c(4)*x**7 &
|
|
+ 10*c(5)*x**9 + 12*c(6)*x**11
|
|
|
|
return
|
|
end function dpr
|
|
!
|
|
function d2pr(c,c2,x)
|
|
! --------------------------------------------------------
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP) :: c(6),c2,x,d2pr
|
|
|
|
d2pr = 2*c2 + 12*c(2)*x**2 + 30*c(3)*x**4 + 56*c(4)*x**6 &
|
|
+ 90*c(5)*x**8 + 132*c(6)*x**10
|
|
|
|
return
|
|
end function d2pr
|
|
!
|
|
function d3pr(c,c2,x)
|
|
! --------------------------------------------------------
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP) :: c(6),c2,x,d3pr
|
|
|
|
d3pr = 24*c(2)*x + 120*c(3)*x**3 + 336*c(4)*x**5 &
|
|
+ 720*c(5)*x**7 + 1320*c(6)*x**9
|
|
|
|
return
|
|
end function d3pr
|
|
!
|
|
function d4pr(c,c2,x)
|
|
! --------------------------------------------------------
|
|
use kinds, only : DP
|
|
implicit none
|
|
real(DP) :: c(6),c2,x,d4pr
|
|
|
|
d4pr = 24.0_dp*c(2) + 360.0_dp*c(3)*x**2 + 1680.0_dp*c(4)*x**4 &
|
|
+ 5040.0_dp*c(5)*x**6 + 11880.0_dp*c(6)*x**8
|
|
|
|
return
|
|
end function d4pr
|