"""Uses the [adiabatic] local density approximation ([A]LDA) to calculate the [time-dependent]
electron density [and current] for a system of N electrons.
Computes approximations to V_KS, V_H, V_xc using the LDA self-consistently. For ground state
calculations the code outputs the LDA orbitals and energies of the system, the ground-state
charge density and Kohn-Sham potential. For time dependent calculations the code also outputs
the time-dependent charge and current densities and the time-dependent Kohn-Sham potential.
Note: Uses the LDAs developed in [Entwistle2018]_ from finite slab systems and the HEG,
in one dimension.
"""
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import
import copy
import numpy as np
import scipy as sp
import scipy.sparse as sps
import scipy.linalg as spla
import scipy.sparse.linalg as spsla
from . import LDA_parameters
from . import RE_cython
from . import results as rs
from . import mix
from . import minimize
[docs]def groundstate(pm, H):
r"""Calculates the ground-state of the system for a given potential.
.. math::
\hat{H} \phi_{j} = \varepsilon_{j} \phi_{j}
parameters
----------
pm : object
Parameters object
H : array_like
2D array of the Hamiltonian matrix in band form, indexed as H[band,space_index]
returns
-------
density : array_like
1D array of the electron density, indexed as density[space_index]
orbitals : array_like
2D array of the Kohn-Sham orbitals, index as orbitals[space_index,orbital_number]
eigenvalues : array_like
1D array of the Kohn-Sham eigenvalues, indexed as eigenvalues[orbital_number]
"""
# Solve the Kohn-Sham equations
eigenvalues, orbitals = spla.eig_banded(H, lower=True)
# Normalise the orbitals
orbitals /= np.sqrt(pm.space.delta)
# Calculate the electron density
density = electron_density(pm, orbitals)
return density, orbitals, eigenvalues
[docs]def electron_density(pm, orbitals):
r"""Calculates the electron density from the set of orbitals.
.. math::
n(x) = \sum_{j=1}^{N}|\phi_{j}(x)|^{2}
parameters
----------
pm : object
Parameters object
orbitals : array_like
2D array of the Kohn-Sham orbitals, index as orbitals[space_index,orbital_number]
returns
-------
density : array_like
1D array of the electron density, indexed as density[space_index]
"""
density = np.sum(np.absolute(orbitals[:,:pm.sys.NE])**2, axis=1)
return density
[docs]def ks_potential(pm, density, perturbation=False):
r"""Calculates the Kohn-Sham potential from the electron density.
.. math::
V_{\mathrm{KS}} = V_{\mathrm{ext}} + V_{\mathrm{H}} + V_{\mathrm{xc}}
parameters
----------
pm : object
Parameters object
density : array_like
1D array of the electron density, indexed as density[space_index]
perturbation: bool
- True: Perturbed external potential
- False: Unperturbed external potential
returns
-------
v_ks : array_like
1D array of the Kohn-Sham potential, indexed as v_ks[space_index]
"""
v_ks = pm.space.v_ext + hartree_potential(pm, density) + xc_potential(pm, density)
if perturbation:
v_ks += pm.space.v_pert
return v_ks
[docs]def banded_to_full(pm, H):
r"""Converts the Hamiltonian matrix in band form to the full matrix.
parameters
----------
pm : object
Parameters object
H : array_like
2D array of the Hamiltonian matrix in band form, indexed as H[band,space_index]
returns
-------
H_full : array_like
2D array of the Hamiltonian matrix in full form, indexed as H_full[space_index,space_index]
"""
# Stencil used
sd = pm.space.second_derivative_band
nbnd = len(sd)
# Add the band elements to the full matrix
H_full = np.zeros((pm.space.npt,pm.space.npt), dtype=np.float)
for ioff in range(nbnd):
d = np.arange(pm.space.npt-ioff)
H_full[d,d+ioff] = H[ioff,d]
H_full[d+ioff,d] = H[ioff,d]
return H_full
[docs]def kinetic(pm):
r"""Stores the band elements of the kinetic energy matrix in lower form. The kinetic energy matrix
is constructed using a three-point, five-point or seven-point stencil. This yields an NxN band
matrix (where N is the number of grid points). For example with N=6 and a three-point stencil:
.. math::
K = -\frac{1}{2} \frac{d^2}{dx^2}=
-\frac{1}{2} \begin{pmatrix}
-2 & 1 & 0 & 0 & 0 & 0 \\
1 & -2 & 1 & 0 & 0 & 0 \\
0 & 1 & -2 & 1 & 0 & 0 \\
0 & 0 & 1 & -2 & 1 & 0 \\
0 & 0 & 0 & 1 & -2 & 1 \\
0 & 0 & 0 & 0 & 1 & -2
\end{pmatrix}
\frac{1}{\delta x^2}
= [\frac{1}{\delta x^2},-\frac{1}{2 \delta x^2}]
parameters
----------
pm : object
Parameters object
returns array_like
2D array containing the band elements of the kinetic energy matrix, indexed as
K[band,space_index]
"""
# Stencil to use
sd = pm.space.second_derivative_band
nbnd = len(sd)
# Band elements
K = np.zeros((nbnd, pm.space.npt), dtype=np.float)
for i in range(nbnd):
K[i,:] = -0.5 * sd[i]
return K
[docs]def hamiltonian(pm, v_ks=None, orbitals=None, perturbation=False):
r"""Constructs the Hamiltonian matrix in band form for a given Kohn-Sham potential.
.. math::
\hat{H} = \hat{K} + \hat{V}_{\mathrm{KS}}
parameters
----------
pm : object
Parameters object
v_ks : array_like
1D array of the Kohn-Sham potential, indexed as v_ks[space_index]
orbitals : array_like
2D array of the Kohn-Sham orbitals, index as orbitals[space_index,orbital_number]
perturbation: bool
- True: Perturbed external potential
- False: Unperturbed external potential
returns
-------
H : array_like
2D array of the Hamiltonian matrix in band form, indexed as H[band,space_index]
"""
# Kinetic energy matrix
H = kinetic(pm)
# Calculate the Kohn-Sham potential from the orbitals
if orbitals is not None:
density = electron_density(pm, orbitals)
if perturbation:
v_ks = ks_potential(pm, density, perturbation=True)
else:
v_ks = ks_potential(pm, density)
# Add the Kohn-Sham potential to the Hamiltonian
H[0,:] += v_ks
return H
[docs]def hartree_potential(pm, density):
r"""Calculates the Hartree potential for a given electron density.
.. math::
V_{\mathrm{H}}(x) = \int U(x,x') n(x') dx'
parameters
----------
pm : object
Parameters object
density : array_like
1D array of the electron density, indexed as density[space_index]
returns array_like
1D array of the Hartree potential, indexed as v_h[space_index]
"""
v_h = np.dot(pm.space.v_int,density)*pm.space.delta
return v_h
[docs]def hartree_energy(pm, v_h, density):
r"""Calculates the Hartree energy of the ground-state system.
.. math::
E_{\mathrm{H}}[n] = \frac{1}{2} \int \int U(x,x') n(x) n(x') dx dx'
= \frac{1}{2} \int V_{\mathrm{H}}(x) n(x) dx
parameters
----------
pm : object
Parameters object
v_h : array_like
1D array of the ground-state Hartree potential, indexed as v_h[space_index]
density : array_like
1D array of the ground-state electron density, indexed as density[space_index]
returns float
The Hartree energy of the ground-state system
"""
E_h = 0.5*np.dot(v_h,density)*pm.space.delta
return E_h
[docs]def xc_energy(pm, n, separate=False):
r"""LDA approximation for the exchange-correlation energy. Uses the LDAs developed in
[Entwistle et al. 2018] from finite slab systems and the HEG.
.. math ::
E_{\mathrm{xc}}^{\mathrm{LDA}}[n] = \int \varepsilon_{\mathrm{xc}}(n) n(x) dx
parameters
----------
pm : object
Parameters object
n : array_like
1D array of the electron density, indexed as n[space_index]
separate: bool
- True: Split the HEG exchange-correlation energy into separate exchange and correlation terms
- False: Just return the exchange-correlation energy
returns float
Exchange-correlation energy
"""
NE = pm.lda.NE
# Finite LDAs
if NE != 'heg':
p = LDA_parameters.exc_lda[NE]
e_xc = (p['a'] + p['b']*n + p['c']*n**2 + p['d']*n**3 + p['e']*n**4 + p['f']*n**5)*n**p['g']
# HEG LDA
else:
p = LDA_parameters.ex_lda[NE]
q = LDA_parameters.ec_lda[NE]
e_x = np.zeros(pm.space.npt, dtype=np.float)
e_c = np.copy(e_x)
for j in range(pm.space.npt):
if(n[j] != 0.0):
# Exchange energy per electron
e_x[j] = (p['a'] + p['b']*n[j] + p['c']*n[j]**2 + p['d']*n[j]**3 + p['e']*n[j]**4 + p['f']*n[j]**5)*n[j]**p['g']
# Correlation energy per electron
r_s = 0.5/n[j]
e_c[j] = -((q['a']*r_s + q['e']*r_s**2)/(1.0 + q['b']*r_s + q['c']*r_s**2 + q['d']*r_s**3))*np.log(1.0 + \
q['f']*r_s + q['g']*r_s**2)/q['f']
# Exchange-correlation energy per electron
e_xc = e_x + e_c
# Exchange-correlation energy
E_xc = np.dot(e_xc, n)*pm.space.delta
# Separate exchange and correlation contributions
if separate == True:
E_x = np.dot(e_x, n)*pm.space.delta
E_c = np.dot(e_c, n)*pm.space.delta
return E_xc, E_x, E_c
else:
return E_xc
[docs]def xc_potential(pm, n, separate=False):
r"""LDA approximation for the exchange-correlation potential. Uses the LDAs developed in
[Entwistle et al. 2018] from finite slab systems and the HEG.
.. math ::
V_{\mathrm{xc}}^{\mathrm{LDA}}(x) = \frac{\delta E_{\mathrm{xc}}^{\mathrm{LDA}}[n]}{\delta n(x)}
= \varepsilon_{\mathrm{xc}}(n(x)) + n(x)\frac{d\varepsilon_{\mathrm{xc}}}{dn} \bigg|_{n(x)}
parameters
----------
pm : object
Parameters object
n : array_like
1D array of the electron density, indexed as n[space_index]
separate: bool
- True: Split the HEG exchange-correlation potential into separate exchange and correlation terms
- False: Just return the exchange-correlation potential
returns array_like
1D array of the exchange-correlation potential, indexed as v_xc[space_index]
"""
NE = pm.lda.NE
# Finite LDAs
if NE != 'heg':
p = LDA_parameters.vxc_lda[NE]
v_xc = (p['a'] + p['b']*n + p['c']*n**2 + p['d']*n**3 + p['e']*n**4 + p['f']*n**5)*n**p['g']
# HEG LDA
else:
p = LDA_parameters.vx_lda[NE]
q = LDA_parameters.ec_lda[NE]
v_x = np.zeros(pm.space.npt, dtype=np.float)
v_c = np.copy(v_x)
for j in range(pm.space.npt):
if n[j] != 0.0:
# Exchange potential
v_x[j] = (p['a'] + p['b']*n[j] + p['c']*n[j]**2 + \
p['d']*n[j]**3 + p['e']*n[j]**4 + \
p['f']*n[j]**5)*n[j]**p['g']
# Correlation potential
r_s = 0.5/n[j]
energy = -((q['a']*r_s + q['e']*r_s**2)/(1.0 + q['b']*r_s + q['c']*r_s**2 + q['d']*r_s**3))*\
np.log(1.0 + q['f']*r_s + q['g']*r_s**2)/q['f']
derivative = ((r_s*(q['a'] + q['e']*r_s)*(q['b'] + r_s*(2.0*q['c'] + 3.0*q['d']*r_s))*np.log(1.0 + \
q['f']*r_s + q['g']*(r_s**2)) - (r_s*(q['a'] + q['e']*r_s)*(q['f'] + 2.0*q['g']*r_s)*\
(q['b']*r_s + q['c']*(r_s**2) + q['d']*(r_s**3) + 1.0)/(q['f']*r_s + q['g']*(r_s**2) + \
1.0)) - ((q['a'] + 2.0*q['e']*r_s)*(q['b']*r_s + q['c']*(r_s**2) + q['d']*(r_s**3) + 1.0)*\
np.log(1.0 + q['f']*r_s + q['g']*(r_s**2))))/(q['f']*(q['b']*r_s + q['c']*(r_s**2) + \
q['d']*(r_s**3) + 1.0)**2))
v_c[j] = energy - r_s*derivative
# Exchange-correlation potential
v_xc = v_x + v_c
if separate == True:
return v_xc, v_x, v_c
else:
return v_xc
[docs]def DXC(pm, n):
r"""Calculates the derivative of the exchange-correlation potential, necessary for the RPA
preconditioner.
parameters
----------
pm : object
Parameters object
n : array_like
1D array of the electron density, indexed as n[space_index]
returns array_like
1D array of the derivative of the exchange-correlation potential, indexed as D_xc[space_index]
"""
NE = pm.lda.NE
# Currently only the finite LDAs can be used
if NE != 'heg':
p = LDA_parameters.dlda[NE]
D_xc = (p['a'] + n*(p['b'] + n*(p['c'] + n*(p['d'] + n*(p['e'] + n*p['f'])))))*(n**p['g'])
else:
raise IOError("Currently the HEG LDA is not implemented for the RPA preconditioner.")
return D_xc
[docs]def total_energy_eigv(pm, eigenvalues, orbitals=None, density=None, v_h=None, v_xc=None):
r"""Calculates the total energy from the Kohn-Sham eigenvalues.
.. math ::
E[n] = \sum_{j=1}^{N} \varepsilon_j + E_{xc}[n] - E_H[n] - \int n(x) V_{xc}(x)dx
parameters
----------
pm : object
Parameters object
eigenvalues : array_like
1D array of the Kohn-Sham eigenvalues, indexed as eigenvalues[orbital_number]
orbitals : array_like
2D array of the Kohn-Sham orbitals, index as orbitals[space_index,orbital_number]
density : array_like
1D array of the electron density, indexed as density[space_index]
v_h : array_like
1D array of the Hartree potential, indexed as v_h[space_index]
v_xc : array_like
1D array of the exchange-correlation potential, indexed as v_xc[space_index]
returns float
Total energy
"""
# Quantities needed to calculate the total energy
if density is None:
if orbitals is None:
raise ValueError("Need to specify either density or orbitals")
else:
density = electron_density(pm, orbitals)
if v_h is None:
v_h = hartree_potential(pm, density)
if v_xc is None:
v_xc = xc_potential(pm, density)
# Kohn-Sham eigenvalues
E = 0.0
for j in range(pm.sys.NE):
E += eigenvalues[j]
# Hartree Energy
E -= hartree_energy(pm, v_h, density)
# Exchange-correlation potential term
E -= np.dot(density, v_xc)*pm.space.delta
# Exchange-correlation energy
E += xc_energy(pm, density)
return E.real
[docs]def total_energy_eigf(pm, orbitals, density=None, v_h=None):
r"""Calculates the total energy from the Kohn-Sham orbitals.
.. math ::
E[n] = \sum_{j=1}^{N} \langle \phi_{j} | K | \phi_{j} \rangle + E_H[n] + E_{xc}[n]
+ \int n(x) V_{\mathrm{ext}}(x)dx
parameters
----------
pm : object
Parameters object
orbitals : array_like
2D array of the Kohn-Sham orbitals, index as orbitals[space_index,orbital_number]
density : array_like
1D array of the electron density, indexed as density[space_index]
v_h : array_like
1D array of the Hartree potential, indexed as v_h[space_index]
returns float
Total energy
"""
# Quantities needed to calculate the total energy
if density is None:
density = electron_density(pm, orbitals)
if v_h is None:
v_h = hartree_potential(pm, density)
# Kinetic energy
E = 0.0
E += kinetic_energy(pm, orbitals)
# Hartree energy
E += hartree_energy(pm, v_h, density)
# Exchange-correlation energy
E += xc_energy(pm, density)
# External potential term
E += np.dot(pm.space.v_ext, density)*pm.space.delta
return E.real
[docs]def kinetic_energy(pm, orbitals):
r"""Calculates the kinetic energy from the Kohn-Sham orbitals.
.. math ::
T_{s}[n] = \sum_{j=1}^{N} \langle \phi_{j} | K | \phi_{j} \rangle
parameters
----------
pm : object
Parameters object
orbitals : array_like
2D array of the Kohn-Sham orbitals, index as orbitals[space_index,orbital_number]
"""
# Kinetic energy matrix
sd = pm.space.second_derivative
sd_ind = pm.space.second_derivative_indices
K = -0.5*sps.diags(sd, sd_ind, shape=(pm.space.npt, pm.space.npt), dtype=np.float, format='csr')
# Kinetic energy of each occupied orbital
occ = orbitals[:,:pm.sys.NE]
eigenvalues = (occ.conj() * K.dot(occ)).sum(0)*pm.space.delta
return np.sum(eigenvalues)
[docs]def calculate_current_density(pm, density):
r"""Calculates the current density from the time-dependent electron density by solving the
continuity equation.
.. math::
\frac{\partial n}{\partial t} + \frac{\partial j}{\partial x} = 0
parameters
----------
pm : object
Parameters object
density : array_like
2D array of the time-dependent density, indexed as density[time_index,space_index]
returns array_like
2D array of the current density, indexed as current_density[time_index,space_index]
"""
pm.sprint('', 1)
string = 'LDA: calculating current density'
pm.sprint(string, 1)
current_density = np.zeros((pm.sys.imax,pm.space.npt), dtype=np.float)
for i in range(1, pm.sys.imax):
string = 'LDA: t = {:.5f}'.format(i*pm.sys.deltat)
pm.sprint(string, 1, newline=False)
J = np.zeros(pm.space.npt, dtype=np.float)
J = RE_cython.continuity_eqn(pm, density[i,:], density[i-1,:])
current_density[i,:] = J[:]
pm.sprint('', 1)
return current_density
[docs]def crank_nicolson_step(pm, orbitals, H_full):
r"""Solves Crank Nicolson Equation
.. math::
\left(I + i\frac{dt}{2} H\right) \Psi(x,t+dt) = \left(I - i \frac{dt}{2} H\right) \Psi(x,t)
parameters
----------
pm : object
Parameters object
orbitals : array_like
2D array of the Kohn-Sham orbitals, index as orbitals[space_index,orbital_number]
H_full : array_like
2D array of the Hamiltonian matrix in full form, indexed as H_full[space_index,space_index]
returns
"""
# Construct matrices
dH = 0.5j*pm.sys.deltat*H_full
identity = np.identity(pm.space.npt, dtype=np.cfloat)
A = identity + dH
Abar = identity - dH
# Solve for all single-particle states at once
RHS = np.dot(Abar, orbitals[:, :pm.sys.NE])
orbitals_new = spla.solve(A, RHS)
return orbitals_new
[docs]def main(parameters):
r"""Performs LDA calculation
parameters
----------
parameters : object
Parameters object
returns object
Results object
"""
# Array initialisations
pm = parameters
string = 'LDA: constructing arrays'
pm.sprint(string, 1)
pm.setup_space()
# Take external potential as the initial guess to the Kohn-Sham potential
H = hamiltonian(pm, v_ks=pm.space.v_ext)
n_inp, orbitals, eigenvalues = groundstate(pm, H)
E = total_energy_eigv(pm, eigenvalues=eigenvalues, density=n_inp)
# Need n_inp and n_out to start mixing
H = hamiltonian(pm, v_ks=ks_potential(pm, n_inp))
n_out, orbitals_out, eigenvalues_out = groundstate(pm, H)
# Mixing scheme
if pm.lda.scf_type == 'pulay':
mixer = mix.PulayMixer(pm, order=pm.lda.pulay_order, preconditioner=pm.lda.pulay_preconditioner)
elif pm.lda.scf_type == 'cg':
minimizer = minimize.CGMinimizer(pm, total_energy_eigf)
elif pm.lda.scf_type == 'mixh':
minimizer = minimize.DiagMinimizer(pm, total_energy_eigf)
H_mix = copy.copy(H)
# Find the self-consistent solution
iteration = 1
converged = False
while (not converged) and iteration <= pm.lda.max_iter:
E_old = E
# Conjugate-gradient minimization starts with orbitals, H[orbitals]
if pm.lda.scf_type == 'cg':
orbitals = minimizer.step(orbitals, banded_to_full(pm, H))
n_inp = electron_density(pm, orbitals)
# Calculate total energy at n_inp
E = total_energy_eigf(pm, orbitals=orbitals, density=n_inp)
# Minimization that mixes Hamiltonian directly starts with n_inp, H[n_inp]
elif pm.lda.scf_type == 'mixh':
n_tmp, orbitals_tmp, eigenvalues_tmp = groundstate(pm,H_mix)
H_tmp = hamiltonian(pm, v_ks=ks_potential(pm, n_tmp))
H_mix = minimizer.h_step(H_mix, H_tmp)
n_inp, orbitals_inp, eigenvalues_inp = groundstate(pm,H_mix)
# Calculate total energy at n_inp
E = total_energy_eigv(pm, eigenvalues=eigenvalues_inp, density=n_inp)
# Mixing schemes starting with n_inp, n_out (Pulay, linear or none)
else:
# Calculate new n_inp
if pm.lda.scf_type == 'pulay':
n_inp = mixer.mix(n_inp, n_out, eigenvalues_out, orbitals_out.T)
elif pm.lda.scf_type == 'linear':
n_inp = (1-pm.lda.mix)*n_inp + pm.lda.mix*n_out
else:
n_inp = n_out
# Calculate total energy at n_inp
E = total_energy_eigv(pm, eigenvalues=eigenvalues_out, density=n_inp)
# Calculate new Kohn-Sham potential and update the Hamiltonian
v_ks = ks_potential(pm, n_inp)
H = hamiltonian(pm, v_ks=v_ks)
# Calculate new n_out
n_out, orbitals_out, eigenvalues_out = groundstate(pm,H)
# Calculate the Kohn-Sham gap
gap = eigenvalues_out[pm.sys.NE]- eigenvalues_out[pm.sys.NE-1]
if gap < 1e-3:
string = "\nLDA: Warning: small KS gap {:.3e} Ha. Convergence may be slow.".format(gap)
pm.sprint(string, 1)
# Calculate the self-consistent density and energy error
dn = np.sum(np.abs(n_inp-n_out))*pm.space.delta
dE = E - E_old
# Check if converged
converged = dn < pm.lda.tol and np.abs(dE) < pm.lda.etol
string = 'LDA: E = {:.8f} Ha, de = {:+.3e}, dn = {:.3e}, iter = {}'.format(E, dE, dn, iteration)
pm.sprint(string, 1, newline=False)
# Iterate
iteration += 1
iteration -= 1
pm.sprint('')
# Print to screen
if not converged:
string = 'LDA: Warning: convergence not reached in {} iterations. Terminating.'.format(iteration)
pm.sprint(string, 1)
else:
pm.sprint('LDA: reached convergence in {} iterations.'.format(iteration),0)
# Self-consistent solution
density = n_out
orbitals = orbitals_out
eigenvalues = eigenvalues_out
# Calculate potentials and energies
if pm.lda.NE == 'heg':
E_xc, E_x, E_c = xc_energy(pm, density, separate=True)
v_xc, v_x, v_c = xc_potential(pm, density, separate=True)
else:
E_xc = xc_energy(pm, density)
v_xc = xc_potential(pm, density)
v_h = hartree_potential(pm, density)
v_ks = pm.space.v_ext + v_h + v_xc
E = total_energy_eigf(pm, orbitals=orbitals, density=density)
E_h = hartree_energy(pm, v_h, density)
E_hxc = E_h + E_xc
# Print to screen
pm.sprint('LDA: ground-state energy: {}'.format(E),1)
pm.sprint('LDA: ground-state Hartree exchange-correlation energy: {}'.format(E_hxc),1)
pm.sprint('LDA: ground-state Hartree energy: {}'.format(E_h),1)
pm.sprint('LDA: ground-state exchange-correlation energy: {}'.format(E_xc),1)
if pm.lda.NE == 'heg':
pm.sprint('LDA: ground-state exchange energy: {}'.format(E_x),1)
pm.sprint('LDA: ground-state correlation energy: {}'.format(E_c),1)
# Save the quantities to file
results = rs.Results()
results.add(density, 'gs_lda{}_den'.format(pm.lda.NE))
results.add(v_h, 'gs_lda{}_vh'.format(pm.lda.NE))
results.add(v_xc, 'gs_lda{}_vxc'.format(pm.lda.NE))
results.add(v_ks, 'gs_lda{}_vks'.format(pm.lda.NE))
results.add(E, 'gs_lda{}_E'.format(pm.lda.NE))
results.add(E_xc, 'gs_lda{}_Exc'.format(pm.lda.NE))
results.add(E_h, 'gs_lda{}_Eh'.format(pm.lda.NE))
results.add(E_hxc, 'gs_lda{}_Ehxc'.format(pm.lda.NE))
if pm.lda.NE == 'heg' :
results.add(E_x, 'gs_lda{}_Ex'.format(pm.lda.NE))
results.add(E_c, 'gs_lda{}_Ec'.format(pm.lda.NE))
results.add(v_x, 'gs_lda{}_vx'.format(pm.lda.NE))
results.add(v_c, 'gs_lda{}_vc'.format(pm.lda.NE))
results.add(orbitals.T,'gs_lda{}_eigf'.format(pm.lda.NE))
results.add(eigenvalues,'gs_lda{}_eigv'.format(pm.lda.NE))
if pm.run.save:
results.save(pm)
# Propagate through real time
if pm.run.time_dependence:
# Construct arrays
v_ks_td = np.zeros((pm.sys.imax,pm.space.npt), dtype=np.float)
v_xc_td = np.zeros((pm.sys.imax,pm.space.npt), dtype=np.float)
v_h_td = np.zeros((pm.sys.imax,pm.space.npt), dtype=np.float)
current = np.zeros((pm.sys.imax,pm.space.npt), dtype=np.float)
density_td = np.zeros((pm.sys.imax,pm.space.npt), dtype=np.float)
orbitals = orbitals.astype(np.cfloat)
# Save the ground-state
v_ks_td[0,:] = v_ks[:]
v_h_td[0,:] = v_h[:]
v_xc_td[0,:] = v_xc[:]
density_td[0,:] = density[:]
# Perform real time iterations
for i in range(1, pm.sys.imax):
# Print to screen
string = 'LDA: evolving through real time: t = {}'.format(i*pm.sys.deltat)
pm.sprint(string, 1, newline=False)
# Construct the Hamiltonian
H = hamiltonian(pm, orbitals=orbitals, perturbation=True)
H_full = banded_to_full(pm, H)
# Propagate through time-step using the Crank-Nicolson method
orbitals[:, :pm.sys.NE] = crank_nicolson_step(pm, orbitals, H_full)
density_td[i,:] = electron_density(pm, orbitals)
v_ks_td[i,:] = pm.space.v_ext[:] + pm.space.v_pert[:] + hartree_potential(pm, density_td[i,:]) + xc_potential(pm, density_td[i,:])
# Hartree and exchange-correlation potential
v_h_td[i,:] = hartree_potential(pm, density_td[i,:])
v_xc_td[i,:] = xc_potential(pm, density_td[i,:])
# Calculate the current density
current_density = calculate_current_density(pm, density_td)
# Save the quantities to file
results.add(v_ks_td, 'td_lda{}_vks'.format(pm.lda.NE))
results.add(v_h_td, 'td_lda{}_vh'.format(pm.lda.NE))
results.add(v_xc_td, 'td_lda{}_vxc'.format(pm.lda.NE))
results.add(density_td, 'td_lda{}_den'.format(pm.lda.NE))
results.add(current_density, 'td_lda{}_cur'.format(pm.lda.NE))
if pm.run.save:
results.save(pm)
pm.sprint('',1)
return results