Source code for utils
import numpy as np
import ext_potentials
import constants
[docs]def get_dx(grids):
"""Gets the grid spacing from a grids array.
Args:
grids: Numpy array with shape (num_grids,).
Returns:
h: Grid spacing.
"""
h = (grids[-1] - grids[0]) / (len(grids) - 1)
if np.isclose(h, grids[1] - grids[0]):
return h
else:
raise ValueError('grids is not uniformly spaced.')
[docs]def vw_grid(density, dx):
"""von Weizsacker kinetic energy functional on grid.
Args:
density: numpy array, density on grid.
(num_grids,)
dx: grid spacing.
Returns:
kinetic_energy: von Weizsacker kinetic energy.
"""
gradient = np.gradient(density) / dx
return np.sum(0.125 * gradient * gradient / density) * dx
[docs]def quadratic(mat, x):
"""Compute the quadratic form of matrix and vector.
Args:
mat: matrix.
(n, n)
x: vector.
(n,)
Returns:
output: scalar value as result of x A x.T.
"""
return np.dot(x, np.dot(mat, x))
[docs]class IntegralTool:
'''Containing integral matrices.
'''
def __init__(self, grids, A=constants.EXPONENTIAL_COULOMB_AMPLITUDE,
k=constants.EXPONENTIAL_COULOMB_KAPPA, init=True):
self.grids = grids
self.num_grids = len(grids)
self.dx = np.abs(self.grids[-1] - self.grids[0]) / (self.num_grids - 1)
self.A = A
self.k = k
if init:
self.simpson_vector = self._get_simpson_vector()
self.hartree_matrix = self._get_hartree_matrix()
[docs] def integral(self, func_grids):
if len(func_grids) != self.num_grids:
raise ValueError(f"Input grids has dimension {func_grids}, "
f"it should have dimension {self.num_grids}.")
return self.simpson_vector.dot(func_grids)
def _simpson_coeff(self, index):
if index == 0 or index == self.num_grids - 1:
return 1
elif index % 2 == 1:
return 4
else:
return 2
def _get_simpson_vector(self):
v = np.empty(self.num_grids)
for i in range(self.num_grids):
v[i] = self._simpson_coeff(i) * self.dx / 3
return v
def _get_hartree_matrix(self):
mat = np.empty([self.num_grids, self.num_grids])
for i in range(self.num_grids):
for j in range(self.num_grids):
mat[i, j] = - self._simpson_coeff(j) * \
ext_potentials.exp_hydrogenic(
self.grids[i] - self.grids[j], self.A, self.k) * \
self.dx / 3
return mat
[docs]class DerivativeTool:
'''Containing derivative matrices.
'''
def __init__(self, grids, n_point_stencil=5, init=True):
self.num_grids = len(grids)
self.dx = (grids[-1] - grids[0]) / (self.num_grids - 1)
self.n_point_stencil = n_point_stencil
if init:
self.d1_mat = self._get_d1_matrix()
self.d2_mat = self._get_d2_matrix()
def _get_d1_matrix(self):
"""get 1st order derivative matrix
"""
# create identity matrix
mat = np.eye(self.num_grids)
if self.n_point_stencil == 5:
# for centered 1st-order derivatives
for i in range(2, self.num_grids - 2):
mat[i][i - 2:i + 3] = [1 / 12, -2 / 3, 0., 2 / 3, -1 / 12]
# 0 means the first (last) row, 1 means the second (second-last) row
# 0 and 1 are for forward/backward formulas in two ends of the matrix
end_0 = np.array([-25 / 12, 4., -3., 4 / 3, -1 / 4])
end_1 = np.array([-1 / 4, -5 / 6, 3 / 2, -1 / 2, 1 / 12])
# end_1 = np.array([0, -25/12, 4., -3., 4/3, -1/4]) #noend
# end_0 = [0., 2/3, -1/12, 0, 0] #open
# end_1 = [-2/3, 0., 2/3, -1/12, 0] #open
# end_0 = [0, 0, 0, 0, 0] #zero
# end_1 = [0, 0, 0, 0, 0] #zero
mat[0][:5] = end_0
mat[-1][-5:] = -end_0[::-1]
mat[1][:5] = end_1
mat[-2][-5:] = -end_1[::-1]
# mat[1][:6] = end_1 #noend
# mat[-2][-6:] = -end_1[::-1] #noend
elif self.n_point_stencil == 3:
for i in range(1, self.num_grids - 1):
mat[i][i - 1:i + 2] = [-0.5, 0., 0.5]
# 0 means the first (last) row
end_0 = np.array([-3 / 2, 2., -1 / 2])
# end_0 = [2., -1/2, 0] #open
# end_0 = [0, 0, 0] #zero
mat[0][:3] = end_0
mat[-1][-3:] = -end_0[::-1]
else:
raise ValueError(
'n_point_stencil = %d is not supported' % self.n_point_stencil)
return mat / self.dx
def _get_d2_matrix(self):
"""get 2nd order derivative matrix
"""
# create identity matrix
mat = np.eye(self.num_grids)
if self.n_point_stencil == 5:
# for centered 1st-order derivatives
for i in range(2, self.num_grids - 2):
mat[i][i - 2:i + 3] = [-1 / 12, 4 / 3, -5 / 2, 4 / 3, -1 / 12]
# 0 means the first (last) row, 1 means the second (second-last) row
# 0 and 1 are for forward/backward formulas in two ends of the matrix
end_0 = [15 / 4, -77 / 6, 107 / 6, -13., 61 / 12, -5 / 6]
end_1 = [5 / 6, -5 / 4, -1 / 3, 7 / 6, -1 / 2, 1 / 12]
# end_0 = [35/12, -26/3, 19/2, -14/3, 11/12] #5
# end_1 = [11/12, -5/3, 0.5, 1/3, -1/12] #5
# end_0 = [-5/2, 4/3, -1/12, 0, 0, 0] #open
# end_1 = [4/3, -5/2, 4/3, -1/12, 0, 0] #open
# end_0 = [0, 0, 0, 0, 0, 0] #zero
# end_1 = [0, 0, 0, 0, 0, 0] #zero
mat[0][:6] = end_0
mat[-1][-6:] = end_0[::-1]
mat[1][:6] = end_1
mat[-2][-6:] = end_1[::-1]
# mat[0][:5] = end_0 #5
# mat[-1][-5:] = end_0[::-1] #5
# mat[1][:5] = end_1 #5
# mat[-2][-5:] = end_1[::-1] #5
elif self.n_point_stencil == 3:
for i in range(1, self.num_grids - 1):
mat[i][i - 1:i + 2] = [1., -2., 1.]
# 0 means the first (last) row
end_0 = [2., -5., 4., -1.]
# end_0 = [-2., 1., 0, 0] #open
# end_0 = [0, 0, 0, 0] #zero
mat[0][:4] = end_0
mat[-1][-4:] = end_0[::-1]
else:
raise ValueError(
'n_point_stencil = %d is not supported' % self.n_point_stencil)
return mat / (self.dx * self.dx)