//////////////////////////////////////////////////////////////////////////////// // // 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 . // //////////////////////////////////////////////////////////////////////////////// // // RSHFunctional.C // //////////////////////////////////////////////////////////////////////////////// // // Range-separated hybrid functional (RSH) // J.H.Skone et al. Phys. Rev. B93, 235106 (2016) // RSH is defined by alpha_RSH, beta_RSH, mu_RSH // sigma = alpha_RSH * rho(r,r') * erf(r-r')/(r-r') + // beta_RSH * rho(r,r') * erfc(r-r')/(r-r') + // (1 - alpha_RSH) * Vx_LR(r,mu_RSH) + // (1 - beta_RSH) * Vx_SR(r,mu_RSH) // The HSE functional is obtained using alpha_RSH=0, beta_RSH=0.25, mu_RSH=0.11 // Heyd et al., J. Chem. Phys. 118, 8207 (2003) // Heyd, Scuseria J. Chem. Phys. 120, 7274 (2004) // Krukau et al., J. Chem. Phys. 125, 224106 (2006) // The PBE exchange hole J is defined here // Ernzerhof, Perdew J. Chem. Phys. 109, 3313 (1998) // Parts of this code are taken from the implementation in FLAPW // Schlipf et al. Phys. Rev. B 84, 125142 (2011) // //////////////////////////////////////////////////////////////////////////////// #include "RSHFunctional.h" #include "ExponentialIntegral.h" #include #include #include #include using namespace std; // parameters of the exchange hole const double A = 1.0161144, B = -0.37170836, C = -0.077215461, D = 0.57786348, E = -0.051955731; // constructor RSHFunctional::RSHFunctional(const vector > &rhoe, double alpha_RSH, double beta_RSH, double mu_RSH): alpha_RSH_(alpha_RSH), beta_RSH_(beta_RSH), mu_RSH_(mu_RSH), omega(mu_RSH), x_coeff_(1.0-beta_RSH), c_coeff_(1.0) { // nonmagnetic or magnetic _nspin = rhoe.size(); // in magnetic calculations both density arrays must have the same size if ( _nspin > 1 ) assert(rhoe[0].size() == rhoe[1].size()); // allocate arrays _np = rhoe[0].size(); if ( _nspin == 1 ) { // nonmagnetic arrays used _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 { // magnetic arrays used _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]; } } // helper function to calculate the integral // // inf // / / 2 \ // | / w y \ | A -Dy A | / 2 2 \ // | dy Erfc( ----- ) | --- e - -------------- | Exp( - H(s) s y ) // | \ k / | y / 4 2\ | \ / // / F \ y( 1 + - Ay ) / // 0 \ 9 / // // complementary error function is approximated by polynomial // 8 // ----- 2 // \ i - b x // erfc(x) = ) a x e for x < 14 // / i // ----- // i = 1 // and by exp( -2 x^2 ) above x = 14 // then the integrals with the first part of the exchange hole // become analytically solvable void RSHFunctional::approximateIntegral(const double omega_kF, const double Hs2, const double D_term, const double dHs2_ds, double *appInt, double *dAppInt_ds, double *dAppInt_dkF) { // constant parameterization of error function const double a[] = { 1.0, -1.128223946706117, 1.452736265762971, -1.243162299390327, 0.971824836115601, -0.568861079687373, 0.246880514820192, -0.065032363850763, 0.008401793031216 }; const double b = 1.455915450052607, cutoff = 14.0; // helper variables const double SQRT_PI = sqrt(M_PI); const double A_2 = 0.5 * A, r9_4A = 2.25 / A, sqrtA = sqrt(A); const double w2 = omega_kF * omega_kF; const double bw2 = b * w2; const double r2bw2 = 2.0 * bw2; const double bw2_Hs2 = bw2 + Hs2; const double bw2_D_term = bw2 + D_term; if ( bw2_Hs2 < cutoff ) { // small x limit // ### begin calculation of integrals ### // inf // / / 2 \ / 2 \ // | n | A -Dy A | | / w 2\ 2 | // | dy y | --- e - -------------- | Exp| - ( b --2-- + H s ) y | // | | y / 4 2\ | | \ k / | // / \ y( 1 + - Ay ) / \ F / // 0 \ 9 / // // maximum n in above expression // note: to calculate higher derivatives you may need to increase this value const int no_integral = 11; // Calculate more helper variables const double sqrt_bw2_Hs2 = sqrt(bw2_Hs2); const double sqrt_bw2_D_term = sqrt(bw2_D_term); // arg = 9/4 * (b w^2/kF^2 + H s^2) / A const double arg = r9_4A * bw2_Hs2; const double sqrt_arg = sqrt(arg); // calculate e^(arg), E1(arg), and erfc(sqrt(arg)) const double exp_arg = exp(arg); const double exp_erfc = exp_arg * erfc(sqrt_arg); // evaluate exponenential integral double term2 = ( arg < util::series_cutoff ) ? exp_arg * util::E1(arg) : util::gauss_laguerre(arg); // allocate array vector integral(no_integral); // The n = 0 integral is // A/2 ( ln((b (w/kF)^2 + H s^2) / (b (w/kF)^2 + D + H s^2)) // + e^(arg) E1(arg) ) integral[0] = A_2 * ( log(bw2_Hs2 / bw2_D_term) + term2 ); // Calculate now all even n's by successive derivation // The log(...) term gives term proportional to 1/(b (w/kF)^2 + D + H s^2)^i // The e^(arg) E1(arg) term reproduces itself with a prefactor and // generates an additional 1/arg term which produces higher 1/arg^i terms // when further deriviated double term1 = A_2 / bw2_D_term; double factor2 = -1.125; double arg_n = -1.0 / arg; integral[2] = term1 + factor2 * term2; for ( int i = 1; i < no_integral / 2; i++ ) { term1 = i * term1 / bw2_D_term; factor2 = -factor2 * r9_4A; term2 = term2 + arg_n; integral[2 * ( i + 1 )] = term1 + factor2 * term2; arg_n = -arg_n * i / arg; } // The n = 1 integral is // A/2 ( sqrt(pi) / sqrt( b (w/kF)^2 + D + H s2 ) // - 3/4 sqrt(A) pi e^(arg) erfc(sqrt(arg)) term1 = A_2 * SQRT_PI / sqrt_bw2_D_term; term2 = M_PI * exp_erfc; factor2 = -0.75 * sqrtA; integral[1] = term1 + factor2 * term2; // Calculate now all uneven n's by successive derivation // The 1 / sqrt(...) term gives higher orders of 1 / (...)^((2i+1)/2) // The e^(arg) erfc(sqrt(arg)) term reproduces itself with a prefactor // and generates an additional 1/sqrt(arg) term which produces higher // 1/(arg)^((2i+1)/2) terms when further deriviated double sum_term = -1.125 * SQRT_PI / sqrt_bw2_Hs2; double add_term = sum_term; double half_i2_1 = -0.5; for ( int i = 3; i < no_integral; i += 2 ) { factor2 = -factor2 * r9_4A; term1 = -term1 * half_i2_1 / bw2_D_term; integral[i] = term1 + term2 * factor2 + sum_term; add_term = -add_term * half_i2_1 / bw2_Hs2; sum_term = -sum_term * r9_4A + add_term; half_i2_1 = half_i2_1 - 1.0; } // ### end calculation of integrals ### const int no_coeff = sizeof( a ) / sizeof(double); // allocate vector vector a_wi, ai_wi; // will contain a[i] * (w/kF)^i a_wi.reserve(no_coeff); // will contain a[i] * i * (w/kF)^i ai_wi.reserve(no_coeff); // initialize array double wi = 1.0; for ( int i = 0; i < no_coeff; i++ ) { a_wi.push_back(a[i] * wi); ai_wi.push_back(a_wi[i] * i); wi *= omega_kF; } // combine the solutions of the integrals with the appropriate prefactors *appInt = inner_product(a_wi.begin(),a_wi.end(),integral.begin(),0.0); // for derivative shift integral index by 2 const double dotpr = inner_product(a_wi.begin(),a_wi.end(),integral.begin() + 2,0.0); *dAppInt_ds = -dotpr * dHs2_ds; *dAppInt_dkF = -inner_product(ai_wi.begin(),ai_wi.end(),integral.begin(), -r2bw2 * dotpr); } else { // large x limit // inf // / / 2 \ / 2 \ // | | A -Dy A | | / w 2\ 2 | // | dy | --- e - -------------- | Exp| - ( 2 --2-- + H s ) y | // | | y / 4 2\ | | \ k / | // / \ y( 1 + - Ay ) / \ F / // 0 \ 9 / const double r2w2 = 2.0 * w2; const double r4w2 = 4.0 * w2; const double r2w2_Hs2 = r2w2 + Hs2; const double r2w2_D_term = r2w2 + D_term; const double arg = r9_4A * r2w2_Hs2; const double exp_e1 = util::gauss_laguerre(arg); *appInt = A_2 * ( log(r2w2_Hs2 / r2w2_D_term) + exp_e1 ); const double dAppInt_dh = -A_2 / r2w2_D_term + 1.125 * exp_e1; *dAppInt_ds = dAppInt_dh * dHs2_ds; *dAppInt_dkF = -dAppInt_dh * r4w2; } } // helper function to calculate the HSE enhancement factor // and its derivative w.r.t. s and k_F // inf // / // HSE 8 | w y // F (s,k ) = - - | dy y J(s,y) erfc( ----- ) // x F 9 | k // / F // 0 // where s: reduced density gradient // k_F: Fermi vector // w: screening of the functional // the PBE exchange hole is defined by the following expression // / // | A 1 / A 2 2 // J(s,y) = | - --2- ------------2- + | --2- + B + C [ 1 + s F(s) ] y // | y 1 + (4/9)Ay \ y // \ // 2 \ 2 2 // 2 4 \ -D y | - s H(s) y // + E [ 1 + s G(s) ] y | e | e // / | // / // see references for a definition of the functions F(s), G(s), and H(s) void RSHFunctional::RSH_enhance(const double s_inp, const double kF, const double w, double *fx, double *dfx_ds, double* dfx_dkf) { // Correction of the reduced gradient to ensure Lieb-Oxford bound // If a large value of s would violate the Lieb-Oxford bound, the // value of s is reduced, so that this condition is fullfilled const double s_thresh = 8.3, s_max = 8.572844, s_chg = 18.796223; const bool correction = s_inp > s_thresh; const double s = ( correction ) ? s_max - s_chg / ( s_inp * s_inp ) : s_inp; // sanity check assert(s > 0); // prefactor for exchange hole const double r8_9 = 8.0 / 9.0; // derived quantities const double SQRT_PI = sqrt(M_PI); const double sqrtA = sqrt(A); const double r9_4A = 2.25 / A; // evaluate // 2 4 // a1 s + a2 s // H(s) = ---------4------5------6- // 1 + a3 s + a4 s + a5 s // and // 2 3 5 3 4 5 2 // d(s H(s)) ( 4 a1 s + 6 a2 s ) - ( 4 a3 s + 5 a4 s + 6 a5 s ) * H(s) s // --------- = ---------------------4------5------6-------------------------- // d s 1 + a3 s + a4 s + a5 s const double a1 = 0.00979681, a2 = 0.0410834, a3 = 0.187440, a4 = 0.00120824, a5 = 0.0347188; // helper variables const double s2 = s * s; const double s3 = s2 * s; const double s4 = s2 * s2; const double s5 = s3 * s2; const double s6 = s3 * s3; // calculate numerator and reciprocal of denominator const double numerator = a1 * s2 + a2 * s4; const double r_denom = 1.0 / ( 1.0 + a3 * s4 + a4 * s5 + a5 * s6 ); // helper for derivatives const double first = 4.0 * a1 * s3 + 6.0 * a2 * s5; const double second = 4.0 * a3 * s3 + 5.0 * a4 * s4 + 6.0 * a5 * s5; // put everything together const double H = numerator * r_denom; const double Hs2 = H * s2; const double dHs2_ds = ( first - second * Hs2 ) * r_denom; // evaluate // F(s) = Int + Sl * H(s) // d( s^2 F(s) ) d( s^2 H(s) ) // ------------- = 2 * Int *s + Sl * ------------- // d s d s // Int - intercept // Sl - slope const double slope = 6.4753871; const double intercept = 0.4796583; const double F = slope * H + intercept; const double Fs2 = F * s2; const double dFs2_ds = slope * dHs2_ds + 2 * intercept * s; // evaluate alpha and beta // alpha = part1 - part2 // __ 2 // 15 VPi s // beta = ----------------- // 2 7/2 // 16 (D + H s ) // with // // 2 2 2 2 2 3 // __ 15E + 6C(1+Fs )(D+Hs ) + 4B(D+Hs ) + 8A(D+Hs ) // part1 = VPi ------------------------------------------------ // 2 7/2 // 16 ( D + H s ) // and // ________ // / 2 \ / / 2 \ // 3 Pi ___ | 9 H s | | / 9 H s | // part2 = ---- V A Exp| ------- | Erfc| / ------- | // 4 | 4 A | | V 4 A | // \ / \ / // and the derivatives with respect to s // calculate the helper variables const double AHs2_1_2 = sqrtA * sqrt(Hs2); const double r1_Fs2 = 1.0 + Fs2; const double D_Hs2 = D + Hs2; const double D_Hs2Sqr = D_Hs2 * D_Hs2; const double D_Hs2Cub = D_Hs2 * D_Hs2Sqr; const double D_Hs2_5_2 = D_Hs2Sqr * sqrt(D_Hs2); const double D_Hs2_7_2 = D_Hs2_5_2 * D_Hs2; const double D_Hs2_9_2 = D_Hs2_5_2 * D_Hs2Sqr; // part 1 and derivatives w.r.t. Hs^2 and Fs^2 const double part1 = SQRT_PI * ( 15.0 * E + 6.0 * C * r1_Fs2 * D_Hs2 + 4.0 * B * D_Hs2Sqr + 8.0 * A * D_Hs2Cub ) / ( 16.0 * D_Hs2_7_2 ); const double dpart1_dh = -SQRT_PI * ( 105.0 * E + 30.0 * C * r1_Fs2 * D_Hs2 + 12.0 * B * D_Hs2Sqr + 8.0 * A * D_Hs2Cub ) / ( 32.0 * D_Hs2_9_2 ); const double dpart1_df = SQRT_PI * 0.375 * C / D_Hs2_5_2; // part 2 and derivative w.r.t. Hs^2 const double arg1 = r9_4A * Hs2; const double arg2 = sqrt(arg1); const double exp_erfc = exp(arg1) * erfc(arg2); const double part2 = 0.75 * M_PI * sqrtA * exp_erfc; const double dpart2_dh = 0.75 * M_PI * sqrtA * ( r9_4A * exp_erfc - 1.5 / ( SQRT_PI * AHs2_1_2 ) ); // combine parts and derivatives const double alpha = part1 - part2; const double dalpha_dh = dpart1_dh - dpart2_dh; const double dalpha_df = dpart1_df; // calculate beta / s^2, its derivative w.r.t. Hs^2 and E * beta const double Ebeta_s2 = E * 0.9375 * SQRT_PI / D_Hs2_7_2; const double dEbeta_dh = -3.5 * Ebeta_s2 / D_Hs2; // combine alpha and beta to function G // 3 Pi / 4 + alpha // G = - ---------------- // E beta const double r3Pi_4_alpha = 0.75 * M_PI + alpha; // Gs2 = G * s^2 const double Gs2 = -r3Pi_4_alpha / Ebeta_s2; // calculate derivative w.r.t. s // // / /3 Pi \ d(b/s^2) / d a \ d(Hs^2) // < ( ---- + a ) ------- / b/s^2 - ------- > ------- // d (Gs^2) \ \ 4 / d(Hs^2) / d(Hs^2) / d s // -------- = -------------------------------------------------------- // ds E b/s^2 // // // d a d(Fs^2) // ------- ------- // d(Fs^2) ds // - ----------------- // E b/s^2 // // notice that alpha and beta are abbreviated by a and b in this equation const double dGs2_ds = ( ( r3Pi_4_alpha * dEbeta_dh / Ebeta_s2 - dalpha_dh ) * dHs2_ds - dalpha_df * dFs2_ds ) / Ebeta_s2; // helper variables for the integration of the exchange hole const double C_term = C * ( 1 + s2 * F ); const double dCt_ds = C * dFs2_ds; const double E_term = E * ( 1 + Gs2 ); const double dEt_ds = E * dGs2_ds; const double D_term = D + Hs2; const double r1_D_term = 1.0 / D_term; const double r1_kF = 1.0 / kF; const double w_kF = w * r1_kF; const double w_kF_Sqr = w_kF * w_kF; // approximate the integral using an expansion of the error function double appInt, dAppInt_ds, dAppInt_dkF; approximateIntegral(w_kF,Hs2,D_term,dHs2_ds,&appInt,&dAppInt_ds,&dAppInt_dkF); // Calculate the integrals // // inf // / 2 2 / \ // | 2 n + 1 -(D+H(s)s ) y | w | // | dy y e Erfc| --- y | // | | k | // / \ F / // 0 // we use that // inf // / 2 // | 2 n + 1 - q y n! // | dy y e Erfc(y) = ----n+1- * // | 2 q // / // 0 // n // / ----- \ // | \ (2m - 1)!! m -(2m + 1) | // | 1 - ) ---------- q sqrt( q + 1 ) | // | / (2m)!! | // \ ----- / // m = 0 // with q = (D + H(s)s^2) k_F^2 / w^2 // and n!! = n * (n-2) * (n-4) ...; if (n <= 0) := 1 // allocate array vector intYExpErfc(0); intYExpErfc.reserve(4); // helper variables const double q = D_term / w_kF_Sqr; const double q_q_1 = q / ( q + 1 ); const double sqrtq_1 = sqrt(q + 1); // initialize double prefactor = 0.5 / D_term; // note w_kF cancels in transformation double summand = 1.0 / sqrtq_1; double sum = 1 - summand; intYExpErfc.push_back(prefactor * sum); // calculate higher n integrals for ( int i = 1; i < 4; i++ ) { // update values prefactor *= i * r1_D_term; summand *= (2.0 * i - 1.0) / (2.0 * i) * q_q_1; sum -= summand; intYExpErfc.push_back(prefactor * sum); } // Calculate the integrals // // inf // / / 2 \ // 2 | 2 (n+1) | 2 2 w 2 | // ----- | dy y Exp| -(D+H(s)s ) y - ------ y | // ____ | | k ^2 | // V Pi / \ F / // 0 // for n = 0, 1, 2 // const double r1_arg = 1.0 / ( D_term + w_kF_Sqr ); // allocate array vector intYGauss(3); intYGauss[0] = 0.5 * sqrt(r1_arg) * r1_arg; intYGauss[1] = 1.5 * intYGauss[0] * r1_arg; intYGauss[2] = 2.5 * intYGauss[1] * r1_arg; // put everything together // Calculate the integral // inf // / / \ // | | w | // | dy y J(s,y) Erfc| ---- y | // | | k | // / \ F / // 0 // where J(s, y) is the exchange hole defined in the references // the exchange factor is proportional to this integral *fx = -r8_9 * ( appInt + B * intYExpErfc[0] + C_term * intYExpErfc[1] + E_term * intYExpErfc[2] ); // Calculate the derivatives with respect to s using that the derivatative // of the integral yields higher orders of the same kind of integral // intY1 -> -intY3 -> intY5 ... times the derivative of the exponent *dfx_ds = -r8_9 * ( dAppInt_ds - ( B * intYExpErfc[1] + C_term * intYExpErfc[2] + E_term * intYExpErfc[3] ) * dHs2_ds + dCt_ds * intYExpErfc[1] + dEt_ds * intYExpErfc[2] ); *dfx_dkf = -r8_9 * r1_kF * ( w_kF * ( B * intYGauss[0] + C_term * intYGauss[1] + E_term * intYGauss[2] ) + dAppInt_dkF ); // if the value of s has been corrected to satisfy Lieb-Oxford bound, // derivative must be adjusted as well if ( correction ) { *dfx_ds *= 2.0 * s_chg * pow(s_inp,-3); } } // exchange helper function // input: // rho - charge density // grad - absolute value of gradient // a_ex - amount of HF exchange // w - screening // output: // ex - exchange energy // vx1, vx2 - exchange potential such that vx = vx1 + div( vx2 * grad(n) ) void RSHFunctional::RSH_exchange(const double rho, const double grad, const double a_ex, const double w, double *ex, double *vx1, double *vx2) { // constants employed in the PBE/HSE exchange 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) */ // intialize *ex = 0; *vx1 = 0; *vx2 = 0; // very small densities do not contribute if ( rho < 1e-18 ) return; // LDA exchange energy const double rho13 = pow(rho,third); const double exLDA = ax * rho13; // Fermi wave vector kF = ( 3 * pi^2 n )^(1/3) const double kF = pi32third * rho13; // reduced density gradient const double s = grad / ( 2.0 * kF * rho ); // calculate PBE enhancement factor const double s2 = s * s; const double p0 = 1.0 + ul * s2; const double fxpbe = 1.0 + uk - uk / p0; // fs = (1/s) * d Fx / d s const double fs = 2.0 * uk * ul / ( p0 * p0 ); // calculate HSE enhancement factor and derivatives w.r.t. s and kF double fxhse, dfx_ds, dfx_dkf; RSH_enhance(s,kF,w,&fxhse,&dfx_ds,&dfx_dkf); // calculate exchange energy // ex = (1 - a) ex,SR + ex,LR // = (1 - a) ex,SR + ex,PBE - ex,SR // = ex,PBE - a ex,SR *ex = exLDA * ( fxpbe - a_ex * fxhse ); // calculate potential *vx1 = third4 * exLDA * ( fxpbe - s2 * fs - a_ex * ( fxhse - s * dfx_ds + 0.25 * kF * dfx_dkf ) ); *vx2 = -exLDA * ( fs / ( rho * 4.0 * kF * kF ) - a_ex * dfx_ds / ( 2.0 * kF * grad ) ); } //////////////////////////////////////////////////////////////////////////////// // // gcor2.c: Interpolate LSD correlation energy // as given by (10) of Perdew & Wang, Phys Rev B45 13244 (1992) // Translated into C by F.Gygi, Dec 9, 1996 // //////////////////////////////////////////////////////////////////////////////// void RSHFunctional::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 ) ); } //////////////////////////////////////////////////////////////////////////////// // // calculate correlation energy of the PBE functional // 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 by F.Gygi, 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 RSHFunctional::PBE_correlation(const double rho, const double grad, double *ec, double *vc1, double *vc2) { *ec = 0.0; *vc1 = 0.0; *vc2 = 0.0; if ( rho < 1.e-18 ) { return; } const double third = 1.0 / 3.0; 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; // Fermi wave vector kF = ( 3 * pi^2 n )^(1/3) const double rho13 = pow(rho,third); const double fk = pi32third * rho13; /* 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) */ const double rs = alpha / fk; const double twoks = 2.0 * sqrt(four_over_pi * fk); const double t = grad / ( twoks * rho ); const double rtrs = sqrt(rs); double ecrs; 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) */ const double vc = *ec - rs * ecrs * third; /* PBE correlation energy */ /* b = A of [a] (8) */ const double pon = -*ec / gamma; const double b = delt / ( exp(pon) - 1.0 ); const double b2 = b * b; const double t2 = t * t; const double t4 = t2 * t2; const double q4 = 1.0 + b * t2; const double q5 = q4 + b2 * t4; const double h = gamma * log(1.0 + delt * q4 * t2 / q5); // Energy done, now the potential, using appendix E of [b] const double t6 = t4 * t2; const double rsthrd = rs * third; const double fac = delt / b + 1.0; const double bec = b2 * fac / bet; const double q8 = q5 * q5 + delt * q4 * q5 * t2; const double q9 = 1.0 + 2.0 * b * t2; const double hb = -bet * b * t6 * ( 2.0 + b * t2 ) / q8; const double hrs = -rsthrd * hb * bec * ecrs; const double ht = 2.0 * bet * q9 / q8; *ec += h; *vc1 = vc + h + hrs - t2 * ht * seven_sixth; *vc2 = -ht / ( rho * twoks * twoks ); } // spin polarized case void RSHFunctional::PBE_correlation_sp(const double rho_up, const double rho_dn, const double grad_up, const double grad_dn, const double grad, double *ec, double *vc1_up, double *vc1_dn, double *vc2) { *ec = 0.0; *vc1_up = 0.0; *vc1_dn = 0.0; *vc2 = 0.0; const double rh_up = ( rho_up < 1.e-18 ) ? 0.0 : rho_up; const double rh_dn = ( rho_dn < 1.e-18 ) ? 0.0 : rho_dn; 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 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 /* 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) const double rhotot = rh_up + rh_dn; const double rh13 = pow(rhotot,third); const double zet = ( rh_up - rh_dn ) / rhotot; const double g = 0.5 * ( pow(1.0 + zet,third2) + pow(1.0 - zet,third2) ); const double fk = pi32third * rh13; const double rs = alpha / fk; const double twoksg = 2.0 * sqrt(four_over_pi * fk) * g; const double t = grad / ( twoksg * rhotot ); const double rtrs = sqrt(rs); double eu, eurs, ep, eprs, alfm, alfrsm; 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); const double z4 = zet * zet * zet * zet; const 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) */ const double ecrs = eurs * ( 1.0 - f * z4 ) + eprs * f * z4 - alfrsm * f * ( 1.0 - z4 ) / fzz; const double fz = third4 * ( pow(1.0 + zet,third) - pow(1.0 - zet,third) ) / gam; const double eczet = 4.0 * ( zet * zet * zet ) * f * ( ep - eu + alfm / fzz ) + fz * ( z4 * ep - z4 * eu - ( 1.0 - z4 ) * alfm / fzz ); const double comm = *ec - rs * ecrs * third - zet * eczet; *vc1_up = comm + eczet; *vc1_dn = comm - eczet; /* PBE correlation energy */ /* b = A of [a] (8) */ const double g3 = g * g * g; const double pon = -*ec / ( g3 * gamma ); const double b = delt / ( exp(pon) - 1.0 ); const double b2 = b * b; const double t2 = t * t; const double t4 = t2 * t2; const double q4 = 1.0 + b * t2; const double q5 = q4 + b2 * t4; const double h = g3 * gamma * log(1.0 + delt * q4 * t2 / q5); /* Energy done, now the potential, using appendix E of [b] */ const double g4 = g3 * g; const double t6 = t4 * t2; const double rsthrd = rs * third; const double gz = ( pow(( 1.0 + zet ) * ( 1.0 + zet ) + eta,sixthm) - pow( ( 1.0 - zet ) * ( 1.0 - zet ) + eta,sixthm) ) * third; const double fac = delt / b + 1.0; const double bg = -3.0 * b2 * *ec * fac / ( bet * g4 ); const double bec = b2 * fac / ( bet * g3 ); const double q8 = q5 * q5 + delt * q4 * q5 * t2; const double q9 = 1.0 + 2.0 * b * t2; const double hb = -bet * g3 * b * t6 * ( 2.0 + b * t2 ) / q8; const double hrs = -rsthrd * hb * bec * ecrs; const double hzed = 3.0 * gz * h / g + hb * ( bg * gz + bec * eczet ); const double ht = 2.0 * bet * g3 * q9 / q8; double ccomm = h + hrs - t2 * ht * seven_sixth; const double pref = hzed - gz * t2 * ht / g; ccomm -= pref * zet; *ec += h; *vc1_up += ccomm + pref; *vc1_dn += ccomm - pref; *vc2 = -ht / ( rhotot * twoksg * twoksg ); } // update exchange correlation energy and potential void RSHFunctional::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++ ) { // evaluate gradient const 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]); // calculate HSE exchange and PBE correlation double ex, vx1, vx2, ec, vc1, vc2; RSH_exchange(rho[i],grad,1 - x_coeff_,omega,&ex,&vx1,&vx2); PBE_correlation(rho[i],grad,&ec,&vc1,&vc2); // combine exchange and correlation energy exc[i] = ex + c_coeff_ * ec; vxc1[i] = vx1 + c_coeff_ * vc1; vxc2[i] = vx2 + c_coeff_ * vc2; } } 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++ ) { // evaluate gradient 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); // calculate HSE exchange and PBE correlation double ex_up, vx1_up, vx2_up, ex_dn, vx1_dn, vx2_dn; double ec, vc1_up, vc1_dn, vc2; RSH_exchange(2.0 * rho_up[i],2.0 * grad_up,1 - x_coeff_,omega,&ex_up, &vx1_up,&vx2_up); RSH_exchange(2.0 * rho_dn[i],2.0 * grad_dn,1 - x_coeff_,omega,&ex_dn, &vx1_dn,&vx2_dn); PBE_correlation_sp(rho_up[i],rho_dn[i],grad_up,grad_dn,grad,&ec,&vc1_up, &vc1_dn,&vc2); // combine exchange and correlation energy exc_up[i] = ex_up + c_coeff_ * ec; exc_dn[i] = ex_dn + c_coeff_ * ec; vxc1_up[i] = vx1_up + c_coeff_ * vc1_up; vxc1_dn[i] = vx1_dn + c_coeff_ * vc1_dn; vxc2_upup[i] = 2 * vx2_up + c_coeff_ * vc2; vxc2_dndn[i] = 2 * vx2_dn + c_coeff_ * vc2; vxc2_updn[i] = c_coeff_ * vc2; vxc2_dnup[i] = c_coeff_ * vc2; } } }