////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2008 The Regents of the University of California
//
// This file is part of Qbox
//
// Qbox is distributed under the terms of the GNU General Public License
// as published by the Free Software Foundation, either version 2 of
// the License, or (at your option) any later version.
// See the file COPYING in the root directory of this distribution
// or .
//
////////////////////////////////////////////////////////////////////////////////
//
// PBEFunctional.C
//
////////////////////////////////////////////////////////////////////////////////
#include "PBEFunctional.h"
#include
#include
#include
#include
using namespace std;
PBEFunctional::PBEFunctional(const vector > &rhoe,
double x_coeff, double c_coeff)
{
x_coeff_ = x_coeff;
c_coeff_ = c_coeff;
_nspin = rhoe.size();
if ( _nspin > 1 ) assert(rhoe[0].size() == rhoe[1].size());
_np = rhoe[0].size();
if ( _nspin == 1 )
{
_exc.resize(_np);
_vxc1.resize(_np);
_vxc2.resize(_np);
_grad_rho[0].resize(_np);
_grad_rho[1].resize(_np);
_grad_rho[2].resize(_np);
rho = &rhoe[0][0];
grad_rho[0] = &_grad_rho[0][0];
grad_rho[1] = &_grad_rho[1][0];
grad_rho[2] = &_grad_rho[2][0];
exc = &_exc[0];
vxc1 = &_vxc1[0];
vxc2 = &_vxc2[0];
}
else
{
_exc_up.resize(_np);
_exc_dn.resize(_np);
_vxc1_up.resize(_np);
_vxc1_dn.resize(_np);
_vxc2_upup.resize(_np);
_vxc2_updn.resize(_np);
_vxc2_dnup.resize(_np);
_vxc2_dndn.resize(_np);
_grad_rho_up[0].resize(_np);
_grad_rho_up[1].resize(_np);
_grad_rho_up[2].resize(_np);
_grad_rho_dn[0].resize(_np);
_grad_rho_dn[1].resize(_np);
_grad_rho_dn[2].resize(_np);
rho_up = &rhoe[0][0];
rho_dn = &rhoe[1][0];
grad_rho_up[0] = &_grad_rho_up[0][0];
grad_rho_up[1] = &_grad_rho_up[1][0];
grad_rho_up[2] = &_grad_rho_up[2][0];
grad_rho_dn[0] = &_grad_rho_dn[0][0];
grad_rho_dn[1] = &_grad_rho_dn[1][0];
grad_rho_dn[2] = &_grad_rho_dn[2][0];
exc_up = &_exc_up[0];
exc_dn = &_exc_dn[0];
vxc1_up = &_vxc1_up[0];
vxc1_dn = &_vxc1_dn[0];
vxc2_upup = &_vxc2_upup[0];
vxc2_updn = &_vxc2_updn[0];
vxc2_dnup = &_vxc2_dnup[0];
vxc2_dndn = &_vxc2_dndn[0];
}
}
void PBEFunctional::setxc(void)
{
if ( _np == 0 ) return;
if ( _nspin == 1 )
{
assert( rho != 0 );
assert( grad_rho[0] != 0 && grad_rho[1] != 0 && grad_rho[2] != 0 );
assert( exc != 0 );
assert( vxc1 != 0 );
assert( vxc2 != 0 );
#pragma omp parallel for
for ( int i = 0; i < _np; i++ )
{
double grad = sqrt(grad_rho[0][i]*grad_rho[0][i] +
grad_rho[1][i]*grad_rho[1][i] +
grad_rho[2][i]*grad_rho[2][i] );
excpbe(rho[i],grad,&exc[i],&vxc1[i],&vxc2[i]);
}
}
else
{
assert( rho_up != 0 );
assert( rho_dn != 0 );
assert( grad_rho_up[0] != 0 && grad_rho_up[1] != 0 && grad_rho_up[2] != 0 );
assert( grad_rho_dn[0] != 0 && grad_rho_dn[1] != 0 && grad_rho_dn[2] != 0 );
assert( exc_up != 0 );
assert( exc_dn != 0 );
assert( vxc1_up != 0 );
assert( vxc1_dn != 0 );
assert( vxc2_upup != 0 );
assert( vxc2_updn != 0 );
assert( vxc2_dnup != 0 );
assert( vxc2_dndn != 0 );
#pragma omp parallel for
for ( int i = 0; i < _np; i++ )
{
double grx_up = grad_rho_up[0][i];
double gry_up = grad_rho_up[1][i];
double grz_up = grad_rho_up[2][i];
double grx_dn = grad_rho_dn[0][i];
double gry_dn = grad_rho_dn[1][i];
double grz_dn = grad_rho_dn[2][i];
double grx = grx_up + grx_dn;
double gry = gry_up + gry_dn;
double grz = grz_up + grz_dn;
double grad_up = sqrt(grx_up*grx_up + gry_up*gry_up + grz_up*grz_up);
double grad_dn = sqrt(grx_dn*grx_dn + gry_dn*gry_dn + grz_dn*grz_dn);
double grad = sqrt(grx*grx + gry*gry + grz*grz);
excpbe_sp(rho_up[i],rho_dn[i],grad_up,grad_dn,grad,&exc_up[i],&exc_dn[i],
&vxc1_up[i],&vxc1_dn[i],&vxc2_upup[i],&vxc2_dndn[i],
&vxc2_updn[i], &vxc2_dnup[i]);
}
}
}
////////////////////////////////////////////////////////////////////////////////
//
// excpbe: PBE exchange-correlation
// K.Burke's modification of PW91 codes, May 14, 1996.
// Modified again by K.Burke, June 29, 1996, with simpler Fx(s)
// Translated into C and modified Dec 9, 1996.
//
// input:
// rho: density
// grad: abs(grad(rho))
// output:
// exc: exchange-correlation energy per electron
// vxc1, vxc2 : quantities such that the total exchange potential is:
//
// vxc = vxc1 + div ( vxc2 * grad(n) )
//
// References:
// [a] J.P.Perdew, K.Burke, and M.Ernzerhof,
// "Generalized gradient approximation made simple,
// Phys.Rev.Lett. 77, 3865, (1996).
// [b] J.P.Perdew and Y.Wang, Phys.Rev. B33, 8800 (1986),
// Phys.Rev. B40, 3399 (1989) (E).
//
////////////////////////////////////////////////////////////////////////////////
void PBEFunctional::excpbe(double rho, double grad,
double *exc, double *vxc1, double *vxc2)
{
const double third = 1.0 / 3.0;
const double third4 = 4.0 / 3.0;
const double ax = -0.7385587663820224058; /* -0.75*pow(3.0/pi,third) */
const double um = 0.2195149727645171;
const double uk = 0.804;
const double ul = um / uk;
const double pi32third = 3.09366772628014; /* (3*pi^2 ) ^(1/3) */
const double alpha = 1.91915829267751; /* pow(9.0*pi/4.0, third)*/
const double seven_sixth = 7.0 / 6.0;
const double four_over_pi = 1.27323954473516;
const double gamma = 0.03109069086965489; /* gamma = (1-ln2)/pi^2 */
const double bet = 0.06672455060314922; /* see [a] (4) */
const double delt = bet / gamma;
double rtrs,fk,twoks,rs,t,h,
ecrs,pon,b,b2,t2,t4,q4,q5,
t6,rsthrd,fac,bec,q8,q9,hb,hrs,ht,vc;
double rh13,exunif,s,s2,p0,fxpbe,fs;
double ex,vx1,vx2,ec,vc1,vc2;
*exc = 0.0;
*vxc1 = 0.0;
*vxc2 = 0.0;
if ( rho < 1.e-18 )
{
return;
}
/* exchange */
rh13 = pow ( rho, third );
/* LDA exchange energy density */
exunif = ax * rh13;
/* Fermi wavevector kF = ( 3 * pi^2 n )^(1/3) */
fk = pi32third * rh13;
s = grad / ( 2.0 * fk * rho );
/* PBE enhancement factor */
s2 = s * s;
p0 = 1.0 + ul * s2;
fxpbe = 1.0 + uk - uk / p0;
ex = exunif * fxpbe;
/* energy done, now the potential */
/* find first derivative of Fx w.r.t the variable s. */
/* fs = (1/s) * d Fx / d s */
fs = 2.0 * uk * ul / ( p0 * p0 );
vx1 = third4 * exunif * ( fxpbe - s2 * fs );
vx2 = - exunif * fs / ( rho * 4.0 * fk * fk );
/* correlation */
/* Find LSD contributions, using [c] (10) and Table I of [c]. */
/* ec = unpolarized LSD correlation energy */
/* ecrs = d ec / d rs */
/* construct ec, using [c] (8) */
rs = alpha / fk;
twoks = 2.0 * sqrt( four_over_pi * fk );
t = grad / ( twoks * rho );
rtrs = sqrt(rs);
gcor2 ( 0.0310907, 0.2137, 7.5957, 3.5876, 1.6382, 0.49294,
rtrs, &ec, &ecrs );
/* LSD potential from [c] (A1) */
/* ecrs = d ec / d rs [c] (A2) */
vc = ec - rs * ecrs * third;
/* PBE correlation energy */
/* b = A of [a] (8) */
pon = - ec / gamma;
b = delt / ( exp ( pon ) - 1.0 );
b2 = b * b;
t2 = t * t;
t4 = t2 * t2;
q4 = 1.0 + b * t2;
q5 = q4 + b2 * t4;
h = gamma * log ( 1.0 + delt * q4 * t2 / q5 );
// Energy done, now the potential, using appendix E of [b]
t6 = t4 * t2;
rsthrd = rs * third;
fac = delt / b + 1.0;
bec = b2 * fac / bet;
q8 = q5 * q5 + delt * q4 * q5 * t2;
q9 = 1.0 + 2.0 * b * t2;
hb = - bet * b * t6 * ( 2.0 + b * t2 ) / q8;
hrs = -rsthrd * hb * bec * ecrs;
ht = 2.0 * bet * q9 / q8;
vc1 = vc + h + hrs - t2 * ht * seven_sixth;
vc2 = - ht / ( rho * twoks * twoks );
*exc = x_coeff_ * ex + c_coeff_ * ( ec + h );
*vxc1 = x_coeff_ * vx1 + c_coeff_ * vc1;
*vxc2 = x_coeff_ * vx2 + c_coeff_ * vc2;
}
////////////////////////////////////////////////////////////////////////////////
void PBEFunctional::excpbe_sp(double rho_up, double rho_dn,
double grad_up, double grad_dn, double grad, double *exc_up, double *exc_dn,
double *vxc1_up, double *vxc1_dn, double *vxc2_upup, double *vxc2_dndn,
double *vxc2_updn, double *vxc2_dnup)
{
const double third = 1.0 / 3.0;
const double third2 = 2.0 / 3.0;
const double third4 = 4.0 / 3.0;
const double sixthm = -1.0 / 6.0;
const double ax = -0.7385587663820224058; /* -0.75*pow(3.0/pi,third) */
const double um = 0.2195149727645171;
const double uk = 0.804;
const double ul = um / uk;
const double pi32third = 3.09366772628014; /* (3*pi^2 ) ^(1/3) */
const double alpha = 1.91915829267751; /* pow(9.0*pi/4.0, third)*/
const double seven_sixth = 7.0 / 6.0;
const double four_over_pi = 1.27323954473516;
const double gam = 0.5198420997897463; /* gam = 2^(4/3) - 2 */
const double fzz = 8.0 / ( 9.0 * gam );
const double gamma = 0.03109069086965489; /* gamma = (1-ln2)/pi^2 */
const double bet = 0.06672455060314922; /* see [a] (4) */
const double delt = bet / gamma;
const double eta = 1.e-12; // small number to avoid blowup as |zeta|->1
double eu,eurs,ep,eprs,alfm,alfrsm;
double ex_up,ex_dn,vx1_up,vx1_dn,vx2_up,vx2_dn,ec,vc1_up,vc1_dn,vc2;
*exc_up = 0.0;
*exc_dn = 0.0;
*vxc1_up = 0.0;
*vxc1_dn = 0.0;
*vxc2_upup = 0.0;
*vxc2_updn = 0.0;
*vxc2_dnup = 0.0;
*vxc2_dndn = 0.0;
if ( rho_up < 1.e-18 && rho_dn < 1.e-18 )
{
return;
}
/* exchange up */
ex_up = 0.0;
vx1_up = 0.0;
vx2_up = 0.0;
if ( rho_up > 1.e-18 )
{
double tworho = 2.0 * rho_up;
double gr = 2.0 * grad_up;
double rh13 = pow ( tworho, third );
/* LDA exchange energy density */
double exunif = ax * rh13;
/* Fermi wavevector kF = ( 3 * pi^2 n )^(1/3) */
double fk = pi32third * rh13;
double s = gr / ( 2.0 * fk * tworho );
/* PBE enhancement factor */
double s2 = s * s;
double p0 = 1.0 + ul * s2;
double fxpbe = 1.0 + uk - uk / p0;
ex_up = exunif * fxpbe;
/* energy done, now the potential */
/* find first derivative of Fx w.r.t the variable s. */
/* fs = (1/s) * d Fx / d s */
double fs = 2.0 * uk * ul / ( p0 * p0 );
vx1_up = third4 * exunif * ( fxpbe - s2 * fs );
vx2_up = - exunif * fs / ( tworho * 4.0 * fk * fk );
}
/* exchange dn */
ex_dn = 0.0;
vx1_dn = 0.0;
vx2_dn = 0.0;
if ( rho_dn > 1.e-18 )
{
double tworho = 2.0 * rho_dn;
double gr = 2.0 * grad_dn;
double rh13 = pow ( tworho, third );
/* LDA exchange energy density */
double exunif = ax * rh13;
/* Fermi wavevector kF = ( 3 * pi^2 n )^(1/3) */
double fk = pi32third * rh13;
double s = gr / ( 2.0 * fk * tworho );
/* PBE enhancement factor */
double s2 = s * s;
double p0 = 1.0 + ul * s2;
double fxpbe = 1.0 + uk - uk / p0;
ex_dn = exunif * fxpbe;
/* energy done, now the potential */
/* find first derivative of Fx w.r.t the variable s. */
/* fs = (1/s) * d Fx / d s */
double fs = 2.0 * uk * ul / ( p0 * p0 );
vx1_dn = third4 * exunif * ( fxpbe - s2 * fs );
vx2_dn = - exunif * fs / ( tworho * 4.0 * fk * fk );
}
/* set negative densities to 0 for correlation part */
if ( rho_up < 1.e-18 ) rho_up=0.0;
if ( rho_dn < 1.e-18 ) rho_dn=0.0;
/* correlation */
// Find LSD contributions, using [c] (10) and Table I of [c].
// eu = unpolarized LSD correlation energy
// eurs = d eu / d rs
// ep = fully polarized LSD correlation energy
// eprs = d ep / d rs
// alfm = - spin stiffness, [c] (3)
// alfrsm = -d alpha / d rs
// f = spin-scaling factor from [c] (9)
// construct ec, using [c] (8)
double rhotot = rho_up + rho_dn;
double rh13 = pow ( rhotot, third );
double zet = ( rho_up - rho_dn ) / rhotot;
double g = 0.5 * ( pow(1.0+zet, third2) + pow(1.0-zet, third2) );
double fk = pi32third * rh13;
double rs = alpha / fk;
double twoksg = 2.0 * sqrt( four_over_pi * fk ) *g;
double t = grad / ( twoksg * rhotot );
double rtrs = sqrt(rs);
gcor2 ( 0.0310907, 0.2137, 7.5957, 3.5876, 1.6382, 0.49294,
rtrs, &eu, &eurs );
gcor2 ( 0.01554535, 0.20548, 14.1189, 6.1977, 3.3662, 0.62517,
rtrs, &ep, &eprs );
gcor2 ( 0.0168869, 0.11125, 10.357, 3.6231, 0.88026, 0.49671,
rtrs, &alfm, &alfrsm );
double z4 = zet * zet * zet * zet;
double f = (pow(1.0+zet,third4)+pow(1.0-zet,third4)-2.0)/gam;
ec = eu * ( 1.0 - f * z4 ) + ep * f * z4 - alfm * f * (1.0-z4) / fzz;
/* LSD potential from [c] (A1) */
/* ecrs = d ec / d rs [c] (A2) */
double ecrs = eurs * ( 1.0 - f * z4 ) + eprs * f * z4
- alfrsm * f * (1.0-z4)/fzz;
double fz = third4 * ( pow(1.0+zet,third) - pow(1.0-zet,third))/gam;
double eczet = 4.0 * (zet*zet*zet) * f * ( ep - eu + alfm/fzz ) +
fz * ( z4 * ep - z4 * eu - (1.0-z4) * alfm/fzz );
double comm = ec - rs * ecrs * third - zet * eczet;
vc1_up = comm + eczet;
vc1_dn = comm - eczet;
/* PBE correlation energy */
/* b = A of [a] (8) */
double g3 = g * g * g;
double pon = - ec / (g3 * gamma);
double b = delt / ( exp ( pon ) - 1.0 );
double b2 = b * b;
double t2 = t * t;
double t4 = t2 * t2;
double q4 = 1.0 + b * t2;
double q5 = q4 + b2 * t4;
double h = g3 * gamma * log ( 1.0 + delt * q4 * t2 / q5 );
/* Energy done, now the potential, using appendix E of [b] */
double g4 = g3 * g;
double t6 = t4 * t2;
double rsthrd = rs * third;
double gz = ( pow ( (1.0+zet)*(1.0+zet) + eta, sixthm ) -
pow ( (1.0-zet)*(1.0-zet) + eta, sixthm ) ) * third;
double fac = delt / b + 1.0;
double bg = -3.0 * b2 * ec * fac / ( bet * g4 );
double bec = b2 * fac / ( bet * g3 );
double q8 = q5 * q5 + delt * q4 * q5 * t2;
double q9 = 1.0 + 2.0 * b * t2;
double hb = - bet * g3 * b * t6 * ( 2.0 + b * t2 ) / q8;
double hrs = -rsthrd * hb * bec * ecrs;
double hzed = 3.0 * gz * h / g + hb * ( bg * gz + bec * eczet );
double ht = 2.0 * bet * g3 * q9 / q8;
double ccomm = h + hrs - t2 * ht * seven_sixth;
double pref = hzed - gz * t2 * ht / g;
ccomm -= pref * zet;
vc1_up += ccomm + pref;
vc1_dn += ccomm - pref;
vc2 = - ht / ( rhotot * twoksg * twoksg );
*exc_up = x_coeff_ * ex_up + c_coeff_ * ( ec + h );
*exc_dn = x_coeff_ * ex_dn + c_coeff_ * ( ec + h );
*vxc1_up = x_coeff_ * vx1_up + c_coeff_ * vc1_up;
*vxc1_dn = x_coeff_ * vx1_dn + c_coeff_ * vc1_dn;
*vxc2_upup = x_coeff_ * 2 * vx2_up + c_coeff_ * vc2;
*vxc2_dndn = x_coeff_ * 2 * vx2_dn + c_coeff_ * vc2;
*vxc2_updn = c_coeff_ * vc2;
*vxc2_dnup = c_coeff_ * vc2;
}
////////////////////////////////////////////////////////////////////////////////
//
// gcor2.c: Interpolate LSD correlation energy
// as given by (10) of Perdew & Wang, Phys Rev B45 13244 (1992)
// Translated into C Dec 9, 1996
//
////////////////////////////////////////////////////////////////////////////////
void PBEFunctional::gcor2(double a, double a1, double b1, double b2, double b3,
double b4, double rtrs, double *gg, double *ggrs)
{
double q0,q1,q2,q3;
q0 = -2.0 * a * ( 1.0 + a1 * rtrs * rtrs );
q1 = 2.0 * a * rtrs * ( b1 + rtrs * ( b2 + rtrs * ( b3 + rtrs * b4 ) ) );
q2 = log ( 1.0 + 1.0 / q1 );
*gg = q0 * q2;
q3 = a * ( b1 / rtrs + 2.0 * b2 + rtrs * ( 3.0 * b3 + 4.0 * b4 * rtrs ));
*ggrs = -2.0 * a * a1 * q2 - q0 * q3 / ( q1 * ( 1.0 + q1 ));
}