Source code for n2v.methods.direct
"""
direct.py
"""
import numpy as np
np.seterr(divide='ignore', invalid='ignore')
[docs]class Direct():
[docs] def direct_inversion(self, grid, correction=True):
"""
Given a set of canonical kohn-sham orbitals and energies,
constructs vxc by inverting the Kohn-Sham Equations.
If correction is added, it will be performed according to:
Removal of Basis-Set Artifacts in Kohn–Sham Potentials Recovered from Electron Densities
Gaiduk + Ryabinkin + Staroverov
https://pubs.acs.org/doi/abs/10.1021/ct4004146
Parameters:
-----------
grid: np.ndarray. Shape: (3xnpoints)
Grid used to compute correction.
If None, dft grid is used.
correction: bool
Adds correction for spurious basis set artifacts
Returns:
--------
vxc_inverted: np.ndarray
Vxc obtained from inverting kohn sham equations.
Equation (3)
vxc_lda: np.ndarray
LDA potential from forward calculation.
osc_profile: np.ndarray
Oscillatory profile that corrects inverted potentials.
Equation (5)
"""
wfn = self.wfn
#Sanity check.
try:
functional = wfn.functional()
except Exception as ex:
raise ValueError(f"{ex}. Direct inversion only available for DFT methods. ")
if functional.name() == 'HF':
raise ValueError(f"Direct inversion only available for DFT methods. ")
#Build components on grid:
if grid is not None:
#Use user-defined grid
orb = self.on_grid_orbitals(Ca=wfn.Ca().np, Cb=wfn.Cb().np, grid=grid)
lap = self.on_grid_lap_phi(Ca=wfn.Ca().np, Cb=wfn.Cb().np, grid=grid)
vex, vha = self.on_grid_esp(Da=wfn.Da().np, Db=wfn.Db().np, grid=grid)[:2]
den = self.on_grid_density(Da=wfn.Da().np, Db=wfn.Db().np, grid=grid)
eig_a = wfn.epsilon_a_subset("AO", "ALL").np
eig_b = wfn.epsilon_b_subset("AO", "ALL").np
#Calculate correction
if correction is True:
osc_profile = self.get_basis_set_correction(grid)
else:
#Use DFT grid
Vpot = wfn.V_potential()
orb = self.on_grid_orbitals(Ca=wfn.Ca().np, Cb=wfn.Cb().np, Vpot=Vpot)
lap = self.on_grid_lap_phi(Ca=wfn.Ca().np, Cb=wfn.Cb().np, Vpot=Vpot)
vex, vha = self.on_grid_esp(Da=wfn.Da().np, Db=wfn.Db().np, Vpot=Vpot)[:2]
den = self.on_grid_density(Da=wfn.Da().np, Db=wfn.Db().np, Vpot=Vpot)
eig_a = wfn.epsilon_a_subset("AO", "ALL").np
#Calculate correction
if correction is True:
dft_grid = self.generate_dft_grid(Vpot)
osc_profile = self.get_basis_set_correction(dft_grid)
#Build Reversed LDA from orbitals and density
kinetic = np.zeros_like(den)
propios = np.zeros_like(den)
#Build v_eff. equation (2) for restricted and unrestricted
if self.ref == 1:
for i in range(wfn.nalpha()):
#Alpha orbitals
kinetic += 2.0 * (0.5 * orb[i] * lap[i])
propios += 2.0 * eig_a[i] * np.abs(orb[i])**2
with np.errstate(divide='ignore', invalid='ignore'):
veff = (kinetic + propios) / den
vxc_inverted = veff - vha - vex
elif self.ref== 2:
for i in range(wfn.nalpha()):
#Alpha orbitals
kinetic[:,0] += (0.5 * orb[i][:,0] * lap[i][:,0])
propios[:,0] += eig_a[i] * np.abs(orb[i][:,0])**2
for i in range(wfn.nbeta()):
#Beta orbitals
kinetic[:,1] += (0.5 * orb[i][:,1] * lap[i][:,1])
propios[:,1] += eig_b[i] * np.abs(orb[i][:,1])**2
with np.errstate(divide='ignore', invalid='ignore'):
veff = (kinetic + propios) / den
vxc_inverted = veff - np.repeat(vha[:,None], 2, axis=1) - np.repeat(vex[:,None], 2, axis=1)
#Add correction
if correction is True:
vxc_inverted -= osc_profile
self.grid.vxc = vxc_inverted
return vxc_inverted