Example #1
0
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
Example #2
0
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
Example #3
0
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)
Example #4
0
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
Example #5
0
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
Example #6
0
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
Example #7
0
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
Example #8
0
def kinetic(params):
    """
    Kinetic energy for a given particle. -0.5del^2 on a grid.
    """

    return -0.5*discrete_Laplace(params)