def construct_ks_hamiltonian(params, density): r""" Constructs the KS Hamiltonian given a density, particle number, external potential, etc. """ laplace = discrete_Laplace(params) hamiltonian = np.zeros((params.Nspace_dft, params.Nspace_dft)) # Add Kinetic energy hamiltonian += -0.5 * laplace # Add external potential v_ext = construct_v_ext(params) hamiltonian += np.diag(v_ext) # Add Hartree potential v_h = np.zeros(params.Nspace_dft) for i in range(0, params.Nspace_dft): for j in range(0, params.Nspace_dft): v_h[i] += density[j] / ( abs(params.grid_dft[i] - params.grid_dft[j]) + params.soft) v_h *= params.dx_dft hamiltonian += np.diag(v_h) # Add XC potential (Entwistle 2017) #v_xc = (-1.24 + 2.1*density - 1.7*density**2)*density**0.61 #hamiltonian += np.diag(v_xc) return hamiltonian
def construct_H(params,v_ks): r""" Constructs the discretised Hamiltonian with an N-point stencil and the given KS potential """ # Kinetic energy hamiltonian = -0.5*discrete_Laplace(params) # Potential energy hamiltonian += np.diag(v_ks) return hamiltonian
def construct_H_sparse(params): r""" Constructs the Hamiltonian directly in sparse form using an antisymmetric delta function (position) basis """ potential = np.zeros((params.Nspace, params.Nspace)) laplace = discrete_Laplace(params) hamiltonian = np.zeros((params.Nspace**2, params.Nspace**2)) # Construct local potential matrix for i in range(0, params.Nspace): for j in range(0, params.Nspace): # Add Coulomb potential[i, j] += 1 / ( abs(params.space_grid[i] - params.space_grid[j]) + 1) # Add external potential[i, j] += params.v_ext[i] + params.v_ext[j] # Construct Hamiltonian as KE + V for i in range(0, params.Nspace): for j in range(0, params.Nspace): # Add potential term (V) hamiltonian[i * params.Nspace + j, j * params.Nspace + i] -= potential[i, j] hamiltonian[i * params.Nspace + j, i * params.Nspace + j] += potential[i, j] for k in range(0, params.Nspace): # Add (non-local) kinetic term (KE) hamiltonian[i * params.Nspace + j, i * params.Nspace + k] += -0.5 * laplace[j, k] hamiltonian[j * params.Nspace + i, k * params.Nspace + i] += -0.5 * laplace[j, k] hamiltonian[i * params.Nspace + j, k * params.Nspace + i] -= -0.5 * laplace[j, k] hamiltonian[j * params.Nspace + i, i * params.Nspace + k] -= -0.5 * laplace[j, k] hamiltonian *= 0.5 return sp.sparse.csr_matrix(hamiltonian)
def evaluate_energy_functional(params, wavefunctions_ks, density, dmatrix='optional'): r""" Evaluates the KS energy functional E[n] for a given density + orbitals """ # Kinetic energy kinetic_energy = 0 laplace = discrete_Laplace(params) for i in range(0,params.num_particles): del_sq_phi = np.dot(laplace,wavefunctions_ks[:,i]) kinetic_energy += np.sum(np.conj(wavefunctions_ks[:,i])*del_sq_phi) kinetic_energy *= -0.5*params.dx # Hartree energy hartree_pot = v_h(params, density) hartree_energy = np.sum(density*hartree_pot)*params.dx # External external_pot = v_ext(params) external_energy = np.sum(density*external_pot)*params.dx if params.method == 'dft': # XC energy xc_pot = v_xc(density) xc_energy = np.sum(density*xc_pot)*params.dx total_energy = kinetic_energy + hartree_energy + external_energy + xc_energy elif params.method == 'hf': # Exchange energy exchange_energy = 0 for i in range(0,params.Nspace): for j in range(0,params.Nspace): exchange_energy -= dmatrix[i,j]**2 / (abs(params.grid[i] - params.grid[j]) + params.soft) exchange_energy *= params.dx**2 total_energy = kinetic_energy + hartree_energy + external_energy + exchange_energy else: # Hartree energy total_energy = kinetic_energy + hartree_energy + external_energy return total_energy
def construct_H_dense(params,basis_type): r""" Constructs the (dense) Hamiltonian in a delta function basis (tensor form) for an N particle system. Position space basis (basis_type = position) or (basis_type =)position_antisymmetric -- delta function. N.b. if antisymmetric delta function basis is used, consider shifting the external potential to be entirely negative """ # Store Laplacian stencil for kinetic energy operator laplace = discrete_Laplace(params) if params.num_electrons == 1: # One-particle Hamiltonian hamiltonian = np.zeros((params.Nspace,params.Nspace)) # Kinetic energy hamiltonian[:,:] += -0.5*laplace # External potential hamiltonian[:,:] += np.diag(params.v_ext) elif params.num_electrons == 2: # Two-particle Hamiltonian hamiltonian = np.zeros((params.Nspace,params.Nspace,params.Nspace,params.Nspace)) if basis_type == 'position': # Kinetic energy for i in range(0,params.Nspace): hamiltonian[i,:,i,:] += -0.5*laplace hamiltonian[:,i,:,i] += -0.5*laplace for j in range(0,params.Nspace): for i in range(0,params.Nspace): # External Potential hamiltonian[i,j,i,j] += params.v_ext[i] + params.v_ext[j] # Softened Coulomb potential hamiltonian[i,j,i,j] += 1 / (abs(params.space_grid[i] - params.space_grid[j]) + 1) # Add components corresponding to the antisymmetric parts of the basis elif basis_type == 'position_antisymmetric': print('Warning: antisymmetric position basis used.') print('Consider shifting potential to be entirely negative for stability.') # Kinetic energy for i in range(0,params.Nspace): hamiltonian[i,:,i,:] += -0.5*laplace hamiltonian[:,i,:,i] += -0.5*laplace hamiltonian[i,:,:,i] -= -0.5*laplace hamiltonian[:,i,i,:] -= -0.5*laplace for j in range(0,params.Nspace): for i in range(0,params.Nspace): # External potential hamiltonian[i,j,j,i] -= params.v_ext[i] + params.v_ext[j] hamiltonian[i,j,i,j] += params.v_ext[i] + params.v_ext[j] # Softened Coulomb potential hamiltonian[i,j,j,i] -= 1 / (abs(params.space_grid[i] - params.space_grid[j]) + 1) hamiltonian[i,j,i,j] += 1 / (abs(params.space_grid[i] - params.space_grid[j]) + 1) hamiltonian *= 0.5 else: raise RuntimeError('Cannot construct the Hamiltonian for the given particle number: {}'.format(params.num_electrons)) return hamiltonian
def construct_H_sparse(params, basis_type): r""" Constructs the Hamiltonian directly in sparse form using an (anti)-symmetric delta function (position) basis """ # Init the potential, laplacian, and hamiltonian hamiltonian = 0 potential = np.zeros((params.Nspace,params.Nspace)) laplace = discrete_Laplace(params) # Construct local potential matrix for i in range(0,params.Nspace): for j in range(0,params.Nspace): # Add Coulomb potential[i,j] += 1 / (abs(params.space_grid[i] - params.space_grid[j]) + 1.0) # Add external potential[i,j] += params.v_ext[i] + params.v_ext[j] if basis_type == 'position' and params.num_electrons == 1: # One-particle Hamiltonian hamiltonian = np.zeros((params.Nspace,params.Nspace)) # Kinetic energy hamiltonian[:,:] += -0.5*laplace # External potential hamiltonian[:,:] += np.diag(params.v_ext) hamiltonian = sp.sparse.csr_matrix(hamiltonian) elif basis_type == 'position' and params.num_electrons == 2: # Number of distinct elements of the antisymmetric wavefunction num_distinct_elements = int(params.Nspace**2) for i in range(1,params.stencil-1): num_distinct_elements += int(4*params.Nspace*(params.Nspace - i)) # Number of matrix (A) elements required to make the transformation psi_full(x,t) = A*psi_reduced(x,t) col, row, entries = np.zeros(num_distinct_elements, dtype=np.int), \ np.zeros(num_distinct_elements, dtype=np.int), \ np.zeros(num_distinct_elements) # Construct Hamiltonian as KE + V counter = 0 for i in range(0,params.Nspace): for j in range(0,params.Nspace): # Add local potential, and local part of K.E. operator col[counter] += i*params.Nspace + j row[counter] += i*params.Nspace + j entries[counter] += potential[i,j] - laplace[i,i] counter += 1 # Add non-local part of K.E. operator for k in range(j-(params.stencil-2),j+(params.stencil-1)): if j != k and k >= 0 and k < params.Nspace: col[counter] += i*params.Nspace + j row[counter] += i*params.Nspace + k entries[counter] += -0.5*laplace[j,k] counter += 1 col[counter] += j*params.Nspace + i row[counter] += k*params.Nspace + i entries[counter] += -0.5*laplace[j,k] counter += 1 hamiltonian = sp.sparse.csr_matrix((entries, (row, col))) elif basis_type == 'position_antisymmetric' and params.num_electrons == 2: # Init H hamiltonian = np.zeros((params.Nspace**2,params.Nspace**2)) # Construct Hamiltonian as KE + V for i in range(0,params.Nspace): for j in range(0,params.Nspace): # Add potential term (V) hamiltonian[i * params.Nspace + j, j * params.Nspace + i] -= potential[i,j] hamiltonian[i * params.Nspace + j, i * params.Nspace + j] += potential[i,j] for k in range(0,params.Nspace): # Add (non-local) kinetic term (KE) hamiltonian[i*params.Nspace + j,i*params.Nspace+k] += -0.5*laplace[j,k] hamiltonian[j*params.Nspace + i,k*params.Nspace+i] += -0.5*laplace[j,k] hamiltonian[i*params.Nspace + j,k*params.Nspace+i] -= -0.5*laplace[j,k] hamiltonian[j*params.Nspace + i,i*params.Nspace+k] -= -0.5*laplace[j,k] hamiltonian = sp.sparse.csr_matrix(0.5*hamiltonian) return hamiltonian
def construct_H_dense(params, basis_type): r""" Constructs the (dense) Hamiltonian in a delta function basis (tensor form) for an N particle system. Position space basis (basis_type = position) or (basis_type =)position_antisymmetric -- delta function. """ if params.num_electrons == 1: # One-particle Hamiltonian hamiltonian = np.zeros((params.Nspace, params.Nspace)) # Kinetic energy hamiltonian[:, :] += -0.5 * discrete_Laplace(params) # External potential hamiltonian[:, :] += np.diag(params.v_ext) elif params.num_electrons == 2: # Two-particle Hamiltonian hamiltonian = np.zeros( (params.Nspace, params.Nspace, params.Nspace, params.Nspace)) if basis_type == 'position': # Kinetic energy for i in range(0, params.Nspace): hamiltonian[i, :, i, :] += -0.5 * discrete_Laplace(params) hamiltonian[:, i, :, i] += -0.5 * discrete_Laplace(params) for j in range(0, params.Nspace): for i in range(0, params.Nspace): # External Potential hamiltonian[i, j, i, j] += params.v_ext[i] + params.v_ext[j] # Softened Coulomb potential hamiltonian[i, j, i, j] += 1 / ( abs(params.space_grid[i] - params.space_grid[j]) + 1) # Add components corresponding to the antisymmetric parts of the basis elif basis_type == 'position_antisymmetric': laplace = discrete_Laplace(params) # Kinetic energy for i in range(0, params.Nspace): hamiltonian[i, :, i, :] += -0.5 * laplace[:, :] hamiltonian[:, i, :, i] += -0.5 * laplace[:, :] hamiltonian[i, :, :, i] -= -0.5 * laplace[:, :] hamiltonian[:, i, i, :] -= -0.5 * laplace[:, :] for j in range(0, params.Nspace): for i in range(0, params.Nspace): # External potential hamiltonian[i, j, j, i] -= params.v_ext[i] + params.v_ext[j] hamiltonian[i, j, i, j] += params.v_ext[i] + params.v_ext[j] # Softened Coulomb potential hamiltonian[i, j, j, i] -= 1 / ( abs(params.space_grid[i] - params.space_grid[j]) + 1) hamiltonian[i, j, i, j] += 1 / ( abs(params.space_grid[i] - params.space_grid[j]) + 1) hamiltonian *= 0.5 else: raise RuntimeError( 'Cannot construct the Hamiltonian for the given particle number: {}' .format(params.num_electrons)) return hamiltonian
def kinetic(params): """ Kinetic energy for a given particle. -0.5del^2 on a grid. """ return -0.5*discrete_Laplace(params)