use std::f64::consts::PI;
pub fn svwn(rho: f64) -> (f64, f64) {
if rho < 1e-20 {
return (0.0, 0.0);
}
let (ex, vx) = slater_exchange(rho);
let (ec, vc) = vwn5_correlation(rho);
(ex + ec, vx + vc)
}
fn slater_exchange(rho: f64) -> (f64, f64) {
let cx = -0.75 * (3.0 / PI).powf(1.0 / 3.0);
let rho_third = rho.powf(1.0 / 3.0);
let ex = cx * rho_third;
let vx = 4.0 / 3.0 * ex;
(ex, vx)
}
fn vwn5_correlation(rho: f64) -> (f64, f64) {
let rs = (3.0 / (4.0 * PI * rho)).powf(1.0 / 3.0);
let a = 0.0621814;
let x0 = -0.10498;
let b = 3.72744;
let c = 12.9352;
let x = rs.sqrt();
let x_x0 = x - x0;
let xx = x * x + b * x + c;
let xx0 = x0 * x0 + b * x0 + c;
let q = (4.0 * c - b * b).sqrt();
let _ec = a
* (x.powi(2).ln() / xx.ln() + 2.0 * b / q * (2.0 * x + b).atan2(q).atan()
- b * x0 / xx0 * ((x_x0).powi(2) / xx).ln()
+ 2.0 * (b + 2.0 * x0) / q * (2.0 * x + b).atan2(q).atan());
let atan_term = (q / (2.0 * x + b)).atan();
let ec_real = 0.5
* a
* (2.0 * (x * x / xx).ln() + 2.0 * b / q * atan_term
- b * x0 / xx0
* (2.0 * ((x - x0).powi(2) / xx).ln() + 2.0 * (b + 2.0 * x0) / q * atan_term));
let delta = 1e-6 * rs.max(1e-10);
let rho_plus = 3.0 / (4.0 * PI * (rs + delta).powi(3));
let rho_minus = 3.0 / (4.0 * PI * (rs - delta).powi(3));
let (ec_plus, _) = vwn5_correlation_energy(rho_plus);
let (ec_minus, _) = vwn5_correlation_energy(rho_minus);
let vc = ec_real + rho * (ec_plus - ec_minus) / (rho_plus - rho_minus);
(ec_real, vc)
}
fn vwn5_correlation_energy(rho: f64) -> (f64, f64) {
if rho < 1e-20 {
return (0.0, 0.0);
}
let rs = (3.0 / (4.0 * PI * rho)).powf(1.0 / 3.0);
let a = 0.0621814;
let x0 = -0.10498;
let b = 3.72744;
let c = 12.9352;
let x = rs.sqrt();
let xx = x * x + b * x + c;
let xx0 = x0 * x0 + b * x0 + c;
let q = (4.0 * c - b * b).sqrt();
let atan_term = (q / (2.0 * x + b)).atan();
let ec = 0.5
* a
* (2.0 * (x * x / xx).ln() + 2.0 * b / q * atan_term
- b * x0 / xx0
* (2.0 * ((x - x0).powi(2) / xx).ln() + 2.0 * (b + 2.0 * x0) / q * atan_term));
(ec, 0.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn svwn_zero_density() {
let (exc, vxc) = svwn(0.0);
assert_eq!(exc, 0.0);
assert_eq!(vxc, 0.0);
}
#[test]
fn slater_exchange_is_negative() {
let (ex, vx) = slater_exchange(0.1);
assert!(ex < 0.0);
assert!(vx < 0.0);
}
}