def H(W): ''' H dot W where H is Harmiton operator @input: W: Expansion coefficients for Ns unconstrained wave functions, stored as an (S0 * S1 * S2, Ns) matrix @global variables Vdual: Dual potential coefficients stored as a (S0 * S1 * S2, 1) column vector. @return: matrix of shape (S0 * S1 * S2, Ns) ''' f = 2 U = np.dot(W.conj().T, op.O(W)) U_inv = np.linalg.inv(U) # each col of W_real represents W's value in sampled real space W_real = op.cI(W) n = f * op.diagouter(np.dot(W_real, U_inv), W_real) exc = excVWN(n) exc_prime = excpVWN(n) # electrostatic potential (phi) in freqency space phi = op.poisson(n, real_phi=False) Veff = gb_Vdual \ + op.cJdag(op.O(phi)) \ + op.cJdag(op.O(op.cJ(exc))) \ + op.diagprod(exc_prime, op.cJdag(op.O(op.cJ(n)))) HW_potential = op.cIdag(op.diagprod(Veff, op.cI(W))) HW_kinetic = -0.5 * op.L(W) HW = HW_kinetic + HW_potential return HW
def getE(W): ''' get the energy from expansion coefficients for Ns unconstrained wave function @input: W: Expansion coefficients for Ns unconstrained wave functions, stored as an (S0 * S1 * S2, Ns) matrix @global variables Vdual: Dual potential coefficients stored as a (S0 * S1 * S2, 1) column vector. @return: E: Energies summed over Ns states ''' f = 2 U = np.dot(W.conj().T, op.O(W)) U_inv = np.linalg.inv(U) # each col of W_real represents W's value in sampled real space W_real = op.cI(W) n = f * op.diagouter(np.dot(W_real, U_inv), W_real) E_potential = np.dot(gb_Vdual.T, n) E_kinetic = -0.5 * np.trace(np.dot(W.conj().T, op.L(np.dot(W, U_inv)))) * f # electrostatic potential (phi) in freqency space phi = op.poisson(n, real_phi=False) # E_hartree = 0.5 * np.dot(n.conj().T, op.cJdag(op.O(phi))) E_hartree = 0.5 * np.dot(op.cJ(n).conj().T, op.O(phi)) exc = excVWN(n) # the Exc is negative here, which seems problematic. Exc = np.dot(op.cJ(n).conj().T, op.O(op.cJ(exc))) E = (E_potential + E_kinetic + E_hartree + Exc)[0, 0] E = E.real return E
def H(W): ''' H dot W where H is Harmiton operator @input: W: Expansion coefficients for Ns unconstrained wave functions, stored as an (S0 * S1 * S2, Ns) matrix @global variables Vdual: Dual potential coefficients stored as a (S0 * S1 * S2, 1) column vector. @return: matrix of shape (S0 * S1 * S2, Ns) ''' HW_kinetic = -0.5 * op.L(W) HW_potential = op.cIdag(op.diagprod(gb_Vdual, op.cI(W))) HW = HW_kinetic + HW_potential return HW
def test_all(): ns = 4 W = np.random.rand(np.prod(global_vars.S), ns) + 1j * np.random.rand(np.prod(global_vars.S), ns) W = orthonormalizeW(W) sd(W, 500) epsilon, Psi = getPsi(W) # in harmonic potential with w = 2, the energy level should be 3, 5, 5, 5 print('energy levels: {:.3f}, {:.3f}, {:.3f}, {:.3f}'.format( epsilon[0], epsilon[1], epsilon[2], epsilon[3])) W_real = op.cI(W) electron_density = W_real.conj() * W_real vis_cell_3slice(electron_density[:, 0]) # s orbital vis_cell_3slice(electron_density[:, 1]) # p orbital vis_cell_3slice(electron_density[:, 2]) # p orbital return
def getE(W): ''' get the energy from expansion coefficients for Ns unconstrained wave function @input: W: Expansion coefficients for Ns unconstrained wave functions, stored as an (S0 * S1 * S2, Ns) matrix @global variables Vdual: Dual potential coefficients stored as a (S0 * S1 * S2, 1) column vector. @return: E: Energies summed over Ns states ''' U = np.dot(W.conj().T, op.O(W)) U_inv = np.linalg.inv(U) W_real = op.cI( W) # each col of W_real represents W's value in sampled real space n = op.diagouter(np.dot(W_real, U_inv), W_real) E_potential = np.dot(gb_Vdual.T, n) E_kinetic = -0.5 * np.trace(np.dot(W.conj().T, op.L(np.dot(W, U_inv)))) E = (E_potential + E_kinetic)[0, 0] E = E.real return E
sum_over_cell = lambda x: np.sum(x) * np.linalg.det(global_vars.R) / \ (global_vars.S[0] * global_vars.S[1] * global_vars.S[2]) # sum(xdv) sigma1, sigma2 = 0.75, 0.5 g1 = gen_gaussian3(dr, sigma1) g2 = gen_gaussian3(dr, sigma2) n = g2 - g1 if global_vars.is_debug: print(sum_over_cell(g1)) print(sum_over_cell(g2)) net_charge = sum_over_cell(n) print('net charge is {:.4f}'.format(net_charge)) assert net_charge < 1e-3, 'Current charge is not zero' phi = op.cI(op.Linv(-4 * np.pi * op.O(op.cJ(n)))) # Due to rounding, tiny imaginary parts creep into the solution. Eliminate # by taking the real part. phi = np.real(phi) # Unum = (0.5 * np.real(np.dot(op.cJ(phi).conj().T, op.O(op.cJ(n)))))[0, 0] Unum = (0.5 * np.dot(phi.T, n)[0]) * (np.linalg.det(global_vars.R) / np.prod(global_vars.S)) Uanal = ((1 / sigma1 + 1 / sigma2) / 2 - np.sqrt(2) / np.sqrt(sigma1**2 + sigma2**2)) / np.sqrt(np.pi) res_deviation = np.abs(Unum - Uanal) print('Deviation between numerical and analytical results is {:.4f}'.format( res_deviation)) if global_vars.is_debug: print(Unum) print(Uanal)
import numpy as np import global_vars import op import vis lattice_center = np.reshape(np.sum(global_vars.R / 2, axis=1), (1, -1)) dr = np.sqrt(np.sum((global_vars.r - lattice_center)**2, axis=1)) gen_gaussian3 = lambda x, sigma: np.exp(-(x**2) / (2 * sigma**2)) / ( 2 * np.pi * sigma**2)**(3 / 2) sum_over_cell = lambda x: np.sum(x) * np.linalg.det(global_vars.R) / \ (global_vars.S[0] * global_vars.S[1] * global_vars.S[2]) # sum(xdv) sigma = 0.25 g1 = global_vars.Z * gen_gaussian3(dr, sigma) n = op.cI(op.cJ(g1) * global_vars.Sf) n = np.real(n) net_charge = sum_over_cell(n) print('net charge is {:.4f}'.format(net_charge)) vis.slice(n.reshape(global_vars.S[2], global_vars.S[1], global_vars.S[0]), 'xy', global_vars.S[2] // 2) # phi = op.cI(op.Linv(-4 * np.pi * op.O( op.cJ(n) ))) # # Due to rounding, tiny imaginary parts creep into the solution. Eliminate # # by taking the real part. # phi = np.real(phi) # Unum = (0.5 * np.real(np.dot(op.cJ(phi).conj().T, op.O(op.cJ(n)))))[0, 0] # Uself = global_vars.Z ** 2 / (2 * np.sqrt(np.pi)) * (1 / sigma) * global_vars.X.shape[0] # res_deviation = np.abs(Unum - Uself) # print('Deviation between numerical and analytical results is {:.4f}'.format(res_deviation))