def __init__(self, r_s, N, N_k, kappa_scale): """ jellium in a cubic cell, unpolarized r_s is the usual usual length scale N is the number of up electrons i.e. the total number of electrons is 2N and N should be chosen for a closed shell N_k (indirectly) sets the number of kvectors to be kept and used for ewald sums kappa_scale (indirectly) sets the smearing parameter for ewald sums: kappa = kappa_scale / L should always be tested for convergence everything begins uninitialized """ self.L = 1.612*r_s*((2*N)**(1./3)) self.electron = pbc.electron(N, N) v = self.L*np.eye(3) self.supercell = pbc.cell(v[0], v[1], v[2]) self.slater_up = np.zeros((N, N)) self.slater_dn = np.zeros((N, N)) self.inverse_up = np.zeros_like(self.slater_up) self.inverse_dn = np.zeros_like(self.slater_dn) #ewald tables, called u self.u_sr_uu = np.zeros_like(self.electron.uu_table) self.u_sr_dd = np.zeros_like(self.electron.dd_table) self.u_sr_ud = np.zeros_like(self.electron.ud_table) self.u_lr_uu = np.zeros_like(self.u_sr_uu) self.u_lr_dd = np.zeros_like(self.u_sr_dd) self.u_lr_ud = np.zeros_like(self.u_sr_ud) #parameters and additional tables for jastrow n = 2*N/self.supercell.volume #number density, for RPA self.A = 1/np.sqrt(4*np.pi*n) self.F_uu = np.sqrt(2*self.A) self.F_ud = np.sqrt(self.A) self.u_exp_uu = np.zeros_like(self.electron.uu_table) self.u_exp_dd = np.zeros_like(self.electron.dd_table) self.u_exp_ud = np.zeros_like(self.electron.ud_table) self.N = N self.k = self.supercell.kvecs(N_k)[:,:3] self.kappa = kappa_scale / self.L
def test_is_greater_than(): """ the whole reason is_greater_than() exists in the first place is that, in pbc.cell.kvecs(), the sorting results were machine dependent, presumably because the behavior of (a>b) when a and b are supposed to be equal is unpredictable (?). hence this test directly targets pbc.cell.kvecs() by comparing its output to one generated on a particular machine. at the time of writing, the machine is my 2020 macbook air """ #generate list of k from pytools.pbc import cell v = np.sqrt(2) * np.eye(3) supercell = cell(v[0], v[1], v[2]) klist1 = supercell.kvecs(3)[:, :3] with open('data/klist.dat', 'r') as f: klist2 = np.loadtxt(f) msg = 'pbc.cell.kvecs() did not match test data' assert np.allclose(klist1, klist2), msg
def test_ewald_madelung(): """ test ewald sum to see if it can get madelung const for CsCl using a 3x3x3 supercell the charges are created with the electron class, where up and down refer to opposite charges """ alpha = 1.7627 #desired answer L = 4.12*3 #4.12 is the lattice const for CsCl charge = pbc.electron(27, 27) #make structure count = 0 for x in range(3): for y in range(3): for z in range(3): charge.up[count] = [x/3, y/3, z/3] count += 1 charge.dn = np.copy(charge.up) + 1/6 charge.update_displacement() #construct k vectors to sum over v = L*np.eye(3) supercell = pbc.cell(v[0], v[1], v[2]) k = supercell.kvecs(4)[1:,:3] k *= 2*np.pi/L kappa = 6/L #convergence is around here #begin ewald sums vol = supercell.volume ppterm = pbc.ewald(charge.uu_table*L, kappa, k, vol,\ one_species=True) mmterm = pbc.ewald(charge.dd_table*L, kappa, k, vol,\ one_species=True) pmterm = pbc.ewald(charge.ud_table*L, kappa, k, vol,\ one_species=False) self_term = 54*kappa/np.sqrt(np.pi) energy = ppterm + mmterm - pmterm - self_term #compute madelung constant r = L*np.min(np.linalg.norm(charge.ud_table, axis=-1)) madelung = -energy*r/27 assert isclose(madelung, alpha, abs_tol=0.0001), \ 'ewald failed to compute madelung constant for CsCl'
def test_ewald_lr(): """ test ewald_lr, which performs the sum over k with matmul against a manual sum over k """ #make supercell L = 5*np.random.rand() v = L*np.eye(3) supercell = pbc.cell(v[0], v[1], v[2]) volume = supercell.volume kvecs = supercell.kvecs(3)[1:,:3] kvecs *= 2*np.pi/L kappa = 5/L r = L*np.random.rand(17, 3) u = np.zeros(17) for n in range(17): for k in kvecs: ksq = np.dot(k, k) kr = np.dot(k, r[n]) u[n] += np.cos(kr)*np.exp(-ksq / (4*kappa**2)) / ksq u *= 4*np.pi/volume assert np.isclose(pbc.ewald_lr(r, kappa, kvecs, volume), u).all()
def test_laplacian_ewald_lr(): L = 5*np.random.rand() v = L*np.eye(3) supercell = pbc.cell(v[0], v[1], v[2]) volume = supercell.volume kvecs = supercell.kvecs(3)[1:,:3] kvecs *= 2*np.pi/L kappa = 5/L r = L*np.random.rand(3) #compute derivatives f = pbc.ewald_lr(r, kappa, kvecs, volume) #h = 0 h = 0.00001 #finite difference step lap = 0 for i in range(3): r[i] += h f_fwd = pbc.ewald_lr(r, kappa, kvecs, volume) r[i] -= 2*h f_bwd = pbc.ewald_lr(r, kappa, kvecs, volume) r[i] += h #restore original r lap += (f_fwd + f_bwd - 2*f) / h**2 assert np.isclose(lap, pbc.laplacian_ewald_lr(r, kappa, kvecs, volume),\ rtol=1e-2)
def test_grad_ewald_lr(): L = 5*np.random.rand() v = L*np.eye(3) supercell = pbc.cell(v[0], v[1], v[2]) volume = supercell.volume kvecs = supercell.kvecs(3)[1:,:3] kvecs *= 2*np.pi/L kappa = 5/L r = L*np.random.rand(3) #compute derivatives f = pbc.ewald_lr(r, kappa, kvecs, volume) #h = 0 h = 0.00001 #finite difference step grad = np.zeros(3) for i in range(3): r[i] += h fwd = pbc.ewald_lr(r, kappa, kvecs, volume) r[i] -= 2*h bwd = pbc.ewald_lr(r, kappa, kvecs, volume) r[i] += h grad[i] = (fwd - bwd) / (2*h) assert np.isclose(grad, pbc.grad_ewald_lr(r, kappa, kvecs, volume),\ rtol=1e-2).all()
def test_coulomb_potential(): #this test also relies on electron and cell classes from pytools.pbc import electron, cell elec = electron(17, 19) elec.start_random() v = 6.9 * np.eye(3) #cubic cell, length 6.9 geometry = cell(v[0], v[1], v[2]) up_displacement = geometry.crystal_to_cart(elec.uu_table) down_displacement = geometry.crystal_to_cart(elec.dd_table) updown_displacement = geometry.crystal_to_cart(elec.ud_table) #begin computing potentials up_potential = pm.coulomb_potential(up_displacement) down_potential = pm.coulomb_potential(down_displacement) updown_potential = pm.coulomb_potential(updown_displacement, False) #computation using loops up_manual = 0 down_manual = 0 updown_manual = 0 r_up = np.linalg.norm(up_displacement, axis=-1) r_down = np.linalg.norm(down_displacement, axis=-1) r_updown = np.linalg.norm(updown_displacement, axis=-1) for i in range(elec.N_up): for j in range(i): up_manual += 1. / r_up[i, j] for i in range(elec.N_dn): for j in range(i): down_manual += 1. / r_down[i, j] for i in range(elec.N_up): for j in range(elec.N_dn): updown_manual += 1. / r_updown[i, j] assert isclose(up_potential, up_manual), \ 'coulomb_potential failed for up electrons' assert isclose(down_potential, down_manual), \ 'coulomb_potential failed for down electrons' assert isclose(updown_potential, updown_manual), \ 'coulomb_potential failed for up-down electrons'