Source code for strawberrypy.utils
import numpy as np
[docs]
def fermidirac(evals, temperature : float, mu : float):
r"""
The Fermi-Dirac distribution :math:`f(\epsilon, T, \mu)=\big[ 1 + e^{\frac{\epsilon-\mu}{T}} \big]^{-1}`.
Parameters
----------
evals :
List of eigenvalues of the Hamiltonian.
temperature :
Temperature of the system.
mu :
The chemical potential of the system.
Returns
-------
occupations : :python:`np.array | float`
The occupation(s) of the state corresponding to the given energy(ies)
"""
if temperature < 1e-6:
if evals.shape == ():
return 1 if evals <+ mu else 0
else:
return np.array([ 1 if e <= mu else 0 for e in evals ])
else:
return 1 / (1 + np.exp( (evals - mu) / temperature ))
[docs]
def chemical_potential(evals, temperature : float, occupied_states : int):
r"""
Calculate the chemical potential of a given model knowing the eigenvalue distribution and the number of electrons (occupied states) in the system.
Parameters
----------
evals :
List of eigenvalues of the Hamiltonian.
temperature :
Temperature (real or fictitious, as in the case of smearing) of the system, appearing in the Fermi-Dirac distribution.
occupied_states :
Number of occupied states of the system.
Returns
-------
mu : :python:`float`
The chemical potential of the system.
.. warning::
The chemical potential is calculated via bisection method, if convergence is not achieved in 200 iterations, an error is returned.
"""
mu_min = np.min(evals)
mu_max = np.max(evals)
mu = 0
niter = 0
maxiter = 200
while True:
mu = 0.5 * (mu_min + mu_max)
n_exp = np.sum(fermidirac(evals, temperature, mu))
if n_exp < occupied_states:
mu_min = mu
else:
mu_max = mu
if np.abs(n_exp - occupied_states) < 1e-6:
break
niter += 1
if niter > maxiter:
raise RuntimeError("Chemical potential cannot be found: bisection method failed (200 iterations)")
return mu
[docs]
def smearing(vecs, gamma_hevecs, evals, temperature : float, mu : float):
r"""
Smearing introduced to improve the convergence of the formula: it measures how much the projector built from :python:`vecs` is similar to the one built from :python:`gamma_hevecs`, the eigenstates of the Hamiltonian at the :math:`\Gamma`-point. Naming :math:`|\phi_n\rangle` the vectors in :python:`vecs` and :math:`|u_n\rangle` the ones in :python:`gamma_hevecs`, the smearing factor is computed as :math:`c_n=\sum_m f(\epsilon_m, T_s, \mu)|\langle \phi_n|u_m\rangle|^2`, where :math:`\epsilon_m` is the eigenvalue corresponding to the eigenstate :math:`|u_m\rangle`, :math:`T_s` is the smearing temperature, :math:`\mu` is the chemical potential.
Parameters
----------
vecs :
List of states that need to be weighted according to some smearing.
gamma_hevecs :
Eigenstates of th Hamiltonian at the :math:`\Gamma`-point.
evals :
Eigenvales of the Hamiltonian at the :math:`\Gamma`-point.
temperature :
Temperature introduced to smoothen the occupation of the states (smearing temperature).
mu :
Chemical potential of the system.
Returns
-------
smearing_coeffs : :python:`np.array`
A list of smearing coefficients that weights the states :python:`vecs`.
"""
if temperature < 1e-6:
return np.array([1 for _ in range(vecs.shape[1])])
else:
return np.array([np.sum([fermidirac(evals[m], temperature, mu) * np.abs(np.vdot(vecs[:, n], gamma_hevecs[:, m])) ** 2 for m in range(gamma_hevecs.shape[1])]) for n in range(vecs.shape[1])])
[docs]
def unique_vacancies(num : int, Lx : int, Ly : int, basis : int, seed : int = None):
r"""
Returns a list of unique random lattice sites to be removed in the model using the method :python:`add_vacancies`.
Parameters
----------
num :
Number of lattice positions to generate.
Lx :
Number of unit cells of the model along the :math:`\mathbf{a}_1` direction.
Ly :
Number of unit cells of the model along the :math:`\mathbf{a}_2` direction.
basis :
Number of atoms per unit cell.
seed :
Seed for the random number generation. Default is :python:`None`.
Returns
-------
unique_list : :python:`list`
List of unique random lattice site.
"""
indexes = []
unique_list = []
# Set random seed
if seed is not None:
np.random.seed(seed)
# Generate num unique entries
while len(unique_list) < num:
# Trial entry
trial = [np.random.randint(Lx), np.random.randint(Ly), np.random.randint(basis)]
# Generate internal index
trial_index = Ly * basis * trial[0] + basis * trial[1] + trial[2]
# If this is a new entry store it
if not trial_index in indexes:
unique_list.append(trial)
return unique_list