Skip to content
Snippets Groups Projects
Commit 06ee3d62 authored by Kostas Vilkelis's avatar Kostas Vilkelis :flamingo:
Browse files

adjust docstrings and ensure consistent naming schemes

parent 7e07f78a
No related branches found
No related tags found
1 merge request!6Documentation
Pipeline #179518 failed
This commit is part of merge request !6. Comments created here will be created in the context of that merge request.
import inspect import inspect
from copy import copy from copy import copy
from itertools import product from itertools import product
from typing import Callable
from pymf.tb.tb import tb_type
import kwant import kwant
import numpy as np import numpy as np
from scipy.sparse import coo_array from scipy.sparse import coo_array
def builder_to_tb(builder, params={}, return_data=False): def builder_to_tb(
"""Construct a tight-binding model dictionary from a `kwant.Builder`. builder: kwant.Builder, params: dict = {}, return_data: bool = False
) -> tb_type:
"""Construct a tight-binding dictionary from a `kwant.Builder` system.
Parameters Parameters
---------- ----------
builder : `kwant.Builder` builder :
Either builder for non-interacting system or interacting Hamiltonian. system to convert to tight-binding dictionary.
params : dict params :
Dictionary of parameters to evaluate the Hamiltonian. Dictionary of parameters to evaluate the builder on.
return_data : bool return_data :
Returns dictionary with sites and number of orbitals per site. Returns dictionary with sites and number of orbitals per site.
Returns Returns
------- -------
h_0 : dict h_0 :
Tight-binding model of non-interacting systems. Tight-binding dictionary that corresponds to the builder.
data : dict data :
Data with sites and number of orbitals. Only if `return_data=True`. Data with sites and number of orbitals. Only if `return_data=True`.
""" """
builder = copy(builder) builder = copy(builder)
...@@ -124,27 +128,35 @@ def builder_to_tb(builder, params={}, return_data=False): ...@@ -124,27 +128,35 @@ def builder_to_tb(builder, params={}, return_data=False):
return h_0 return h_0
def build_interacting_syst(builder, lattice, func_onsite, func_hop, max_neighbor=1): def build_interacting_syst(
"""Construct an auxiliary `kwant` system to build Hamiltonian matrix. builder: kwant.Builder,
lattice: kwant.lattice,
func_onsite: Callable,
func_hop: Callable,
max_neighbor: int = 1,
) -> kwant.Builder:
"""
Construct an auxiliary `kwant` system that encodes the interactions.
Parameters Parameters
---------- ----------
builder : `kwant.Builder` builder :
Non-interacting `kwant` system. Non-interacting `kwant` system.
func_onsite : function lattice :
Onsite function. Lattice of the system.
func_hop : function func_onsite :
Hopping function. Onsite interactions function.
max_neighbor : int func_hop :
Maximal nearest-neighbor order. Hopping/inter unit cell interactions function.
max_neighbor :
The maximal number of neighbouring unit cells (along a lattice vector)
connected by interaction. Interaction goes to zero after this distance.
Returns Returns
------- -------
int_builder : `kwant.Builder` int_builder :
Dummy `kwant.Builder` to compute interaction matrix. Auxiliary `kwant.Builder` that encodes the interactions of the system.
""" """
# lattice_info = list(builder.sites())[0][0]
# lattice = kwant.lattice.general(lattice_info.prim_vecs, norbs=lattice_info.norbs)
int_builder = kwant.Builder(kwant.TranslationalSymmetry(*builder.symmetry.periods)) int_builder = kwant.Builder(kwant.TranslationalSymmetry(*builder.symmetry.periods))
int_builder[builder.sites()] = func_onsite int_builder[builder.sites()] = func_onsite
for neighbors in range(max_neighbor): for neighbors in range(max_neighbor):
......
import numpy as np import numpy as np
from scipy.fftpack import ifftn from scipy.fftpack import ifftn
from pymf.tb.tb import add_tb from pymf.tb.tb import add_tb, tb_type
from pymf.tb.transforms import ifftn_to_tb, tb_to_khamvector from pymf.tb.transforms import ifftn_to_tb, tb_to_khamvector
def construct_density_matrix_kgrid(kham, filling): def construct_density_matrix_kgrid(
kham: np.ndarray, filling: float
) -> (np.ndarray, float):
"""Calculate density matrix on a k-space grid. """Calculate density matrix on a k-space grid.
Parameters Parameters
---------- ----------
kham : npndarray kham :
Hamiltonian in k-space of shape (len(dim), norbs, norbs) Hamiltonian from which to construct the density matrix.
filling : float The hamiltonian is sampled on a grid of k-points and has shape (nk, nk, ..., ndof, ndof),
where ndof is number of internal degrees of freedom.
filling :
Number of particles in a unit cell. Number of particles in a unit cell.
Used to determine the Fermi level.
Returns Returns
------- -------
np.ndarray, float :
Density matrix in k-space and Fermi energy. Density matrix on a k-space grid with shape (nk, nk, ..., ndof, ndof) and Fermi energy.
""" """
vals, vecs = np.linalg.eigh(kham) vals, vecs = np.linalg.eigh(kham)
fermi = fermi_on_grid(vals, filling) fermi = fermi_on_grid(vals, filling)
unocc_vals = vals > fermi unocc_vals = vals > fermi
occ_vecs = vecs occ_vecs = vecs
np.moveaxis(occ_vecs, -1, -2)[unocc_vals, :] = 0 np.moveaxis(occ_vecs, -1, -2)[unocc_vals, :] = 0
rho_krid = occ_vecs @ np.moveaxis(occ_vecs, -1, -2).conj() density_matrix_krid = occ_vecs @ np.moveaxis(occ_vecs, -1, -2).conj()
return rho_krid, fermi return density_matrix_krid, fermi
def construct_density_matrix(h, filling, nk): def construct_density_matrix(h: tb_type, filling: float, nk: int) -> (tb_type, float):
"""Compute the density matrix in real-space tight-binding format. """Compute the density matrix in real-space tight-binding format.
Parameters Parameters
---------- ----------
h : dict h :
Tight-binding model. Hamiltonian tight-binding dictionary from which to construct the density matrix.
filling : float filling :
Filling of the system. Number of particles in a unit cell.
nk : int Used to determine the Fermi level.
Number of k-points in the grid. nk :
Number of k-points in a grid to sample the Brillouin zone along each dimension.
If the system is 0-dimensional (finite), this parameter is ignored.
Returns Returns
------- -------
(dict, float) :
Density matrix in real-space tight-binding format and Fermi energy. Density matrix tight-binding dictionary and Fermi energy.
""" """
ndim = len(list(h)[0]) ndim = len(list(h)[0])
if ndim > 0: if ndim > 0:
kham = tb_to_khamvector(h, nk=nk) kham = tb_to_khamvector(h, nk=nk)
rho_grid, fermi = construct_density_matrix_kgrid(kham, filling) density_matrix_krid, fermi = construct_density_matrix_kgrid(kham, filling)
return ( return (
ifftn_to_tb(ifftn(rho_grid, axes=np.arange(ndim))), ifftn_to_tb(ifftn(density_matrix_krid, axes=np.arange(ndim))),
fermi, fermi,
) )
else: else:
rho, fermi = construct_density_matrix_kgrid(h[()], filling) density_matrix, fermi = construct_density_matrix_kgrid(h[()], filling)
return {(): rho}, fermi return {(): density_matrix}, fermi
def meanfield(density_matrix_tb, h_int): def meanfield(density_matrix: tb_type, h_int: tb_type) -> tb_type:
"""Compute the mean-field in k-space. """Compute the mean-field correction from the density matrix.
Parameters Parameters
---------- ----------
density_matrix : dict density_matrix :
Density matrix in real-space tight-binding format. Density matrix tight-binding dictionary.
h_int : dict h_int :
Interaction tb model. Interaction hermitian Hamiltonian tight-binding dictionary.
The interaction must be of density-density type, h_int[R][i, j] * c_i^dagger(R) c_j^dagger(0) c_j(0) c_i(R).
For example in 1D system with ndof internal degrees of freedom,
h_int[(2,)] = U * np.ones((ndof, ndof)) is a Coulomb repulsion interaction
with strength U between unit cells separated by 2 lattice vectors, where
the interaction is the same between all internal degrees of freedom.
Returns Returns
------- -------
dict :
Mean-field tb model. Mean-field correction tight-binding dictionary.
""" """
n = len(list(density_matrix_tb)[0]) n = len(list(density_matrix)[0])
local_key = tuple(np.zeros((n,), dtype=int)) local_key = tuple(np.zeros((n,), dtype=int))
direct = { direct = {
...@@ -82,7 +93,7 @@ def meanfield(density_matrix_tb, h_int): ...@@ -82,7 +93,7 @@ def meanfield(density_matrix_tb, h_int):
np.array( np.array(
[ [
np.diag( np.diag(
np.einsum("pp,pn->n", density_matrix_tb[local_key], h_int[vec]) np.einsum("pp,pn->n", density_matrix[local_key], h_int[vec])
) )
for vec in frozenset(h_int) for vec in frozenset(h_int)
] ]
...@@ -92,26 +103,26 @@ def meanfield(density_matrix_tb, h_int): ...@@ -92,26 +103,26 @@ def meanfield(density_matrix_tb, h_int):
} }
exchange = { exchange = {
vec: -1 * h_int.get(vec, 0) * density_matrix_tb[vec] # / (2 * np.pi)#**2 vec: -1 * h_int.get(vec, 0) * density_matrix[vec] for vec in frozenset(h_int)
for vec in frozenset(h_int)
} }
return add_tb(direct, exchange) return add_tb(direct, exchange)
def fermi_on_grid(vals, filling): def fermi_on_grid(vals: np.ndarray, filling: float) -> float:
"""Compute the Fermi energy on a grid of k-points. """Compute the Fermi energy on a grid of k-points.
Parameters Parameters
---------- ----------
vals : ndarray vals :
Eigenvalues of the hamiltonian in k-space of shape (len(dim), norbs, norbs) Eigenvalues of a hamiltonian sampled on a k-point grid with shape (nk, nk, ..., ndof, ndof),
filling : int where ndof is number of internal degrees of freedom.
Number of particles in a unit cell. filling :
Number of particles in a unit cell.
Used to determine the Fermi level.
Returns Returns
------- -------
fermi_energy : float :
Fermi energy Fermi energy
""" """
norbs = vals.shape[-1] norbs = vals.shape[-1]
vals_flat = np.sort(vals.flatten()) vals_flat = np.sort(vals.flatten())
......
...@@ -12,44 +12,46 @@ def _check_hermiticity(h): ...@@ -12,44 +12,46 @@ def _check_hermiticity(h):
op_vector = tuple(-1 * np.array(vector)) op_vector = tuple(-1 * np.array(vector))
op_vector = tuple(-1 * np.array(vector)) op_vector = tuple(-1 * np.array(vector))
if not np.allclose(h[vector], h[op_vector].conj().T): if not np.allclose(h[vector], h[op_vector].conj().T):
raise ValueError("Hamiltonian is not Hermitian.") raise ValueError("Tight-binding dictionary must be hermitian.")
def _tb_type_check(tb): def _tb_type_check(tb):
for count, key in enumerate(tb): for count, key in enumerate(tb):
if not isinstance(tb[key], np.ndarray): if not isinstance(tb[key], np.ndarray):
raise ValueError("Inputted dictionary values are not np.ndarray's") raise ValueError(
"Values of the tight-binding dictionary must be numpy arrays"
)
shape = tb[key].shape shape = tb[key].shape
if count == 0: if count == 0:
size = shape[0] size = shape[0]
if not len(shape) == 2: if not len(shape) == 2:
raise ValueError("Inputted dictionary values are not square matrices") raise ValueError(
"Values of the tight-binding dictionary must be square matrices"
)
if not size == shape[0]: if not size == shape[0]:
raise ValueError("Inputted dictionary elements shapes are not consistent") raise ValueError(
"Values of the tight-binding dictionary must have consistent shape"
)
class Model: class Model:
""" """
Data class which defines the mean-field tight-binding problem Data class which defines the interacting tight-binding problem.
and computes the mean-field Hamiltonian.
Parameters Parameters
---------- ----------
h_0 : h_0 :
Non-interacting Hamiltonian. Non-interacting hermitian Hamiltonian tight-binding dictionary.
h_int : h_int :
Interaction Hamiltonian. Interaction hermitian Hamiltonian tight-binding dictionary.
The interaction must be of density-density type, h_int[R][i, j] * c_i^dagger(R) c_j^dagger(0) c_j(0) c_i(R).
For example in 1D system with ndof internal degrees of freedom,
h_int[(2,)] = U * np.ones((ndof, ndof)) is a Coulomb repulsion interaction
with strength U between unit cells separated by 2 lattice vectors, where
the interaction is the same between all internal degrees of freedom.
filling : filling :
Filling of the system. Number of particles in a unit cell.
Used to determine the Fermi level.
Attributes
----------
h_0 :
Non-interacting Hamiltonian.
h_int :
Interaction Hamiltonian.
filling :
Filling of the system.
""" """
def __init__(self, h_0: tb_type, h_int: tb_type, filling: float) -> None: def __init__(self, h_0: tb_type, h_int: tb_type, filling: float) -> None:
...@@ -71,24 +73,24 @@ class Model: ...@@ -71,24 +73,24 @@ class Model:
_check_hermiticity(h_0) _check_hermiticity(h_0)
_check_hermiticity(h_int) _check_hermiticity(h_int)
def mfield(self, mf_tb: tb_type, nk: int = 200) -> tb_type: def mfield(self, mf: tb_type, nk: int = 200) -> tb_type:
"""Compute single mean field iteration. """Computes a new mean-field correction from a given one.
Parameters Parameters
---------- ----------
mf_tb : mf :
Mean-field tight-binding model. Initial mean-field correction tight-binding dictionary.
nk : nk :
Number of k-points in the grid along a single direction. Number of k-points in a grid to sample the Brillouin zone along each dimension.
If the system is 0-dimensional (finite), this parameter is ignored. If the system is 0-dimensional (finite), this parameter is ignored.
Returns Returns
------- -------
: :
New mean-field tight-binding model. new mean-field correction tight-binding dictionary.
""" """
rho, fermi_energy = construct_density_matrix( rho, fermi_energy = construct_density_matrix(
add_tb(self.h_0, mf_tb), self.filling, nk add_tb(self.h_0, mf), self.filling, nk
) )
return add_tb( return add_tb(
meanfield(rho, self.h_int), meanfield(rho, self.h_int),
......
import numpy as np import numpy as np
from pymf.tb.tb import tb_type from pymf.tb.tb import tb_type
...@@ -8,9 +9,9 @@ def expectation_value(density_matrix: tb_type, observable: tb_type) -> complex: ...@@ -8,9 +9,9 @@ def expectation_value(density_matrix: tb_type, observable: tb_type) -> complex:
Parameters Parameters
---------- ----------
density_matrix : density_matrix :
Density matrix in tight-binding format. Density matrix tight-binding dictionary.
observable : observable :
Observable in tight-binding format. Observable tight-binding dictionary.
Returns Returns
------- -------
......
import numpy as np import numpy as np
from pymf.tb.tb import tb_type from pymf.tb.tb import tb_type
def tb_to_flat(tb: tb_type) -> np.ndarray: def tb_to_flat(tb: tb_type) -> np.ndarray:
"""Convert a hermitian tight-binding dictionary to flat complex matrix. """Parametrise a hermitian tight-binding dictionary by a flat complex vector.
Parameters Parameters
---------- ----------
tb : tb :
Hermitian tigh-binding model Hermitian tigh-binding dictionary
Returns Returns
------- -------
flat : :
1D complex array that parametrises the tb model. 1D complex array that parametrises the tight-binding dictionary.
""" """
if len(list(tb)[0]) == 0: if len(list(tb)[0]) == 0:
matrix = np.array(list(tb.values())) matrix = np.array(list(tb.values()))
...@@ -25,7 +26,7 @@ def tb_to_flat(tb: tb_type) -> np.ndarray: ...@@ -25,7 +26,7 @@ def tb_to_flat(tb: tb_type) -> np.ndarray:
def flat_to_tb( def flat_to_tb(
flat: np.ndarray, tb_param_complex: np.ndarray,
shape: tuple[int, int], shape: tuple[int, int],
tb_keys: list[tuple[None] | tuple[int, ...]], tb_keys: list[tuple[None] | tuple[int, ...]],
) -> tb_type: ) -> tb_type:
...@@ -35,28 +36,28 @@ def flat_to_tb( ...@@ -35,28 +36,28 @@ def flat_to_tb(
Parameters Parameters
---------- ----------
flat : tb_param_complex :
1d complex array that parametrises the tb model. 1d complex array that parametrises the tb model.
shape : shape :
Tuple (n, n) where n is the number of internal degrees of freedom Tuple (n, n) where n is the number of internal degrees of freedom
(e.g. orbitals, spin, sublattice) within the tight-binding model. (e.g. orbitals, spin, sublattice) within the tight-binding model.
tb_keys : tb_keys :
A list of the keys within the tight-binding model (all the hoppings). List of keys of the tight-binding dictionary.
Returns Returns
------- -------
tb : tb :
tight-binding model tight-binding dictionary
""" """
if len(tb_keys[0]) == 0: if len(tb_keys[0]) == 0:
matrix = np.zeros((shape[-1], shape[-2]), dtype=complex) matrix = np.zeros((shape[-1], shape[-2]), dtype=complex)
matrix[np.triu_indices(shape[-1])] = flat matrix[np.triu_indices(shape[-1])] = tb_param_complex
matrix += matrix.conj().T matrix += matrix.conj().T
matrix[np.diag_indices(shape[-1])] /= 2 matrix[np.diag_indices(shape[-1])] /= 2
return {(): matrix} return {(): matrix}
matrix = np.zeros(shape, dtype=complex) matrix = np.zeros(shape, dtype=complex)
N = len(tb_keys) // 2 + 1 N = len(tb_keys) // 2 + 1
matrix[:N] = flat.reshape(N, *shape[1:]) matrix[:N] = tb_param_complex.reshape(N, *shape[1:])
matrix[N:] = np.moveaxis(matrix[-(N + 1) :: -1], -1, -2).conj() matrix[N:] = np.moveaxis(matrix[-(N + 1) :: -1], -1, -2).conj()
tb_keys = np.array(list(tb_keys)) tb_keys = np.array(list(tb_keys))
......
import numpy as np
from pymf.params.param_transforms import ( from pymf.params.param_transforms import (
complex_to_real, complex_to_real,
flat_to_tb, flat_to_tb,
real_to_complex, real_to_complex,
tb_to_flat, tb_to_flat,
) )
import numpy as np
from pymf.tb.tb import tb_type from pymf.tb.tb import tb_type
def tb_to_rparams(tb: tb_type) -> np.ndarray: def tb_to_rparams(tb: tb_type) -> np.ndarray:
"""Convert a mean-field tight-binding model to a set of real parameters. """Parametrise a hermitian tight-binding dictionary by a real vector.
Parameters Parameters
---------- ----------
tb : tb :
Mean-field tight-binding model. tight-binding dictionary.
Returns Returns
------- -------
: :
1D real vector that parametrises the tb model. 1D real vector that parametrises the tight-binding dictionary.
""" """
return complex_to_real(tb_to_flat(tb)) return complex_to_real(tb_to_flat(tb))
def rparams_to_tb( def rparams_to_tb(
r_params: np.ndarray, key_list: list[tuple[None] | tuple[int, ...]], size: int tb_params: np.ndarray, tb_keys: list[tuple[None] | tuple[int, ...]], ndof: int
) -> tb_type: ) -> tb_type:
"""Extract mean-field tight-binding model from a set of real parameters. """Extract a hermitian tight-binding dictionary from a real vector parametrisation.
Parameters Parameters
---------- ----------
r_params : tb_params :
Real parameters. 1D real array that parametrises the tight-binding dictionary.
key_list : tb_keys :
List of the keys within the tight-binding model (all the hoppings). List of keys of the tight-binding dictionary.
size : ndof :
Number of internal degrees of freedom (e.g. orbitals, spin, sublattice) within Number internal degrees of freedom within the unit cell.
the tight-binding model.
Returns Returns
------- -------
: :
Mean-field tight-binding model. Tight-biding dictionary.
""" """
flat_matrix = real_to_complex(r_params) flat_matrix = real_to_complex(tb_params)
return flat_to_tb(flat_matrix, (len(key_list), size, size), key_list) return flat_to_tb(flat_matrix, (len(tb_keys), ndof, ndof), tb_keys)
...@@ -9,52 +9,52 @@ from pymf.model import Model ...@@ -9,52 +9,52 @@ from pymf.model import Model
from pymf.tb.utils import calculate_fermi_energy from pymf.tb.utils import calculate_fermi_energy
def cost(mf_param: np.ndarray, model: Model, nk: Optional[int] = 100) -> np.ndarray: def cost(mf_param: np.ndarray, model: Model, nk: int = 100) -> np.ndarray:
"""Define the cost function for fixed point iteration. """Defines the cost function for root solver.
The cost function is the difference between the input mean-field real space The cost function is the difference between the computed and inputted mean-field.
parametrisation and a new mean-field.
Parameters Parameters
---------- ----------
mf_param : mf_param :
1D real array that parametrises the mean-field tight-binding correction. 1D real array that parametrises the mean-field correction.
Model : Model :
Object which defines the mean-field problem to solve. Interacting tight-binding problem definition.
nk : nk :
The number of k-points to use in the grid. Number of k-points in a grid to sample the Brillouin zone along each dimension.
If the system is 0-dimensional (finite), this parameter is ignored.
Returns Returns
------- -------
: :
1D real array which contains the difference between the input mean-field 1D real array that is the difference between the computed and inputted mean-field
and the new mean-field. parametrisations
""" """
shape = model._size shape = model._size
mf_tb = rparams_to_tb(mf_param, list(model.h_int), shape) mf = rparams_to_tb(mf_param, list(model.h_int), shape)
mf_tb_new = model.mfield(mf_tb, nk=nk) mf_new = model.mfield(mf, nk=nk)
mf_params_new = tb_to_rparams(mf_tb_new) mf_params_new = tb_to_rparams(mf_new)
return mf_params_new - mf_param return mf_params_new - mf_param
def solver( def solver(
model: Model, model: Model,
mf_guess: np.ndarray, mf_guess: np.ndarray,
nk: Optional[int] = 100, nk: int = 100,
optimizer: Optional[Callable] = scipy.optimize.anderson, optimizer: Optional[Callable] = scipy.optimize.anderson,
optimizer_kwargs: Optional[dict[str, str]] = {}, optimizer_kwargs: Optional[dict[str, str]] = {"M": 0},
) -> tb_type: ) -> tb_type:
"""Solve the mean-field self-consistent equation. """Solve for the mean-field correction through self-consistent root finding.
Parameters Parameters
---------- ----------
model : model :
The model object. Interacting tight-binding problem definition.
mf_guess : mf_guess :
The initial guess for the mean-field tight-binding model. The initial guess for the mean-field correction in the tight-binding dictionary format.
nk : nk :
The number of k-points to use in the grid. The default is 100. In the Number of k-points in a grid to sample the Brillouin zone along each dimension.
0-dimensional case, this parameter is ignored. If the system is 0-dimensional (finite), this parameter is ignored.
optimizer : optimizer :
The solver used to solve the fixed point iteration. The solver used to solve the fixed point iteration.
optimizer_kwargs : optimizer_kwargs :
...@@ -63,7 +63,7 @@ def solver( ...@@ -63,7 +63,7 @@ def solver(
Returns Returns
------- -------
: :
The mean-field tight-binding model. Mean-field correction solution in the tight-binding dictionary format.
""" """
shape = model._size shape = model._size
mf_params = tb_to_rparams(mf_guess) mf_params = tb_to_rparams(mf_guess)
......
...@@ -4,37 +4,37 @@ tb_type = dict[tuple[None] | tuple[int, ...], np.ndarray] ...@@ -4,37 +4,37 @@ tb_type = dict[tuple[None] | tuple[int, ...], np.ndarray]
def add_tb(tb1: tb_type, tb2: tb_type) -> tb_type: def add_tb(tb1: tb_type, tb2: tb_type) -> tb_type:
"""Add up two tight-binding models together. """Add up two tight-binding dictionaries together.
Parameters Parameters
---------- ----------
tb1 : tb1 :
Tight-binding model. Tight-binding dictionary.
tb2 : tb2 :
Tight-binding model. Tight-binding dictionary.
Returns Returns
------- -------
: :
Sum of the two tight-binding models. Sum of the two tight-binding dictionaries.
""" """
return {k: tb1.get(k, 0) + tb2.get(k, 0) for k in frozenset(tb1) | frozenset(tb2)} return {k: tb1.get(k, 0) + tb2.get(k, 0) for k in frozenset(tb1) | frozenset(tb2)}
def scale_tb(tb: tb_type, scale: float) -> tb_type: def scale_tb(tb: tb_type, scale: float) -> tb_type:
"""Scale a tight-binding model. """Scale a tight-binding dictionary by a constant.
Parameters Parameters
---------- ----------
tb : dict tb :
Tight-binding model. Tight-binding dictionary.
scale : float scale :
The scaling factor. Constant to scale the tight-binding dictionary by.
Returns Returns
------- -------
: :
Scaled tight-binding model. Scaled tight-binding dictionary.
""" """
return {k: tb.get(k, 0) * scale for k in frozenset(tb)} return {k: tb.get(k, 0) * scale for k in frozenset(tb)}
......
...@@ -7,21 +7,24 @@ ks_type = Optional[np.ndarray] ...@@ -7,21 +7,24 @@ ks_type = Optional[np.ndarray]
def tb_to_khamvector(tb: tb_type, nk: int, ks: ks_type = None) -> np.ndarray: def tb_to_khamvector(tb: tb_type, nk: int, ks: ks_type = None) -> np.ndarray:
"""Real-space tight-binding model to hamiltonian on k-space grid. """Evaluate a tight-binding dictionary on a k-space grid.
Parameters Parameters
---------- ----------
tb : tb :
A dictionary with real-space vectors as keys and complex np.arrays as values. Tight-binding dictionary to evaluate on a k-space grid.
nk : nk :
Number of k-points along each direction. Number of k-points in a grid to sample the Brillouin zone along each dimension.
If the system is 0-dimensional (finite), this parameter is ignored.
ks : ks :
Set of k-points. Repeated for all directions. Set of points along which to evalaute the k-point grid. Repeated for all dimensions.
If not provided, a linear grid is used based on nk.
Returns Returns
------- -------
: :
Hamiltonian evaluated on a k-point grid. Tight-binding dictionary evaluated on a k-space grid.
Has shape (nk, nk, ..., ndof, ndof), where ndof is number of internal degrees of freedom.
""" """
ndim = len(list(tb)[0]) ndim = len(list(tb)[0])
if ks is None: if ks is None:
...@@ -41,17 +44,18 @@ def tb_to_khamvector(tb: tb_type, nk: int, ks: ks_type = None) -> np.ndarray: ...@@ -41,17 +44,18 @@ def tb_to_khamvector(tb: tb_type, nk: int, ks: ks_type = None) -> np.ndarray:
def ifftn_to_tb(ifft_array: np.ndarray) -> tb_type: def ifftn_to_tb(ifft_array: np.ndarray) -> tb_type:
"""Convert an array from ifftn to a tight-binding model format. """
Convert the result of `scipy.fft.ifftn` to a tight-binding dictionary.
Parameters Parameters
---------- ----------
ifft_array : ifft_array :
An array obtained from ifftn. Result of `scipy.fft.ifftn` to convert to a tight-binding dictionary.
The input to `scipy.fft.ifftn` should be from `tb_to_khamvector`.
Returns Returns
------- -------
: :
A dictionary with real-space vectors as keys and complex np.arrays as values. Tight-binding dictionary.
""" """
size = ifft_array.shape[:-2] size = ifft_array.shape[:-2]
...@@ -62,21 +66,20 @@ def ifftn_to_tb(ifft_array: np.ndarray) -> tb_type: ...@@ -62,21 +66,20 @@ def ifftn_to_tb(ifft_array: np.ndarray) -> tb_type:
def kham_to_tb( def kham_to_tb(
kham: np.ndarray, kham: np.ndarray,
hopping_vecs: list[tuple[None] | tuple[int, ...]], tb_keys: list[tuple[None] | tuple[int, ...]],
ks: ks_type = None, ks: ks_type = None,
) -> tb_type: ) -> tb_type:
"""Extract hopping matrices from Bloch Hamiltonian. """Convert a Hamiltonian evaluated on a k-grid to a tight-binding dictionary.
Parameters Parameters
---------- ----------
kham : kham :
Bloch Hamiltonian matrix kham[k_x, ..., k_n, i, j] Hamiltonian sampled on a grid of k-points with shape (nk, nk, ..., ndof, ndof),
hopping_vecs : where ndof is number of internal degrees of freedom.
List of hopping vectors, will be the keys to the tb. tb_keys :
List of keys for which to compute the tight-binding dictionary.
ks : ks :
Set of k-points. Repeated for all directions. I have no clue why we need this, so this goes on the chopping board.
If system is finite, this option is ignored.
Returns Returns
------- -------
: :
...@@ -94,16 +97,16 @@ def kham_to_tb( ...@@ -94,16 +97,16 @@ def kham_to_tb(
hopps = ( hopps = (
np.einsum( np.einsum(
"ij,jkl->ikl", "ij,jkl->ikl",
np.exp(1j * np.einsum("ij,jk->ik", hopping_vecs, k_grid)), np.exp(1j * np.einsum("ij,jk->ik", tb_keys, k_grid)),
kham, kham,
) )
* (dk / (2 * np.pi)) ** ndim * (dk / (2 * np.pi)) ** ndim
) )
h_0 = {} h = {}
for i, vector in enumerate(hopping_vecs): for i, vector in enumerate(tb_keys):
h_0[tuple(vector)] = hopps[i] h[tuple(vector)] = hopps[i]
return h_0 return h
else: else:
return {(): kham} return {(): kham}
...@@ -7,25 +7,25 @@ from pymf.tb.transforms import tb_to_khamvector ...@@ -7,25 +7,25 @@ from pymf.tb.transforms import tb_to_khamvector
def generate_guess( def generate_guess(
vectors: list[tuple[None] | tuple[int, ...]], ndof: int, scale: float = 1 tb_keys: list[tuple[None] | tuple[int, ...]], ndof: int, scale: float = 1
) -> tb_type: ) -> tb_type:
"""Generate guess for a tight-binding model. """Generate guess tight-binding dictionary.
Parameters Parameters
---------- ----------
vectors : tb_keys :
List of hopping vectors. List of tight-binding dictionary keys the guess contains.
ndof : ndof :
Number internal degrees of freedom (e.g. orbitals, spin, sublattice), Number internal degrees of freedom within the unit cell.
scale : scale :
Scale of the random guess. Default is 1. Scale of the random guess.
Returns Returns
------- -------
: :
Guess in the form of a tight-binding model. Guess tight-binding dictionary.
""" """
guess = {} guess = {}
for vector in vectors: for vector in tb_keys:
if vector not in guess.keys(): if vector not in guess.keys():
amplitude = scale * np.random.rand(ndof, ndof) amplitude = scale * np.random.rand(ndof, ndof)
phase = 2 * np.pi * np.random.rand(ndof, ndof) phase = 2 * np.pi * np.random.rand(ndof, ndof)
...@@ -41,25 +41,43 @@ def generate_guess( ...@@ -41,25 +41,43 @@ def generate_guess(
return guess return guess
def generate_vectors(cutoff: int, dim: int) -> list[tuple[None] | tuple[int, ...]]: def generate_tb_keys(cutoff: int, dim: int) -> list[tuple[None] | tuple[int, ...]]:
"""Generate hopping vectors up to a cutoff. """Generate tight-binding dictionary keys up to a cutoff.
Parameters Parameters
---------- ----------
cutoff : cutoff :
Maximum distance along each direction. Maximum distance along each dimension to generate tight-bindign dictionary keys for.
dim : dim :
Dimension of the vectors. Dimension of the tight-binding dictionary.
Returns Returns
------- -------
List of hopping vectors. List of generated tight-binding dictionary keys up to a cutoff.
""" """
return [*product(*([[*range(-cutoff, cutoff + 1)]] * dim))] return [*product(*([[*range(-cutoff, cutoff + 1)]] * dim))]
def calculate_fermi_energy(tb: tb_type, filling: float, nk: int = 100): def calculate_fermi_energy(tb: tb_type, filling: float, nk: int = 100):
"""Calculate the Fermi energy for a given filling.""" """
Calculate the Fermi energy of a given tight-binding dictionary.
Parameters
----------
tb :
Tight-binding dictionary.
filling :
Number of particles in a unit cell.
Used to determine the Fermi level.
nk :
Number of k-points in a grid to sample the Brillouin zone along each dimension.
If the system is 0-dimensional (finite), this parameter is ignored.
Returns
-------
:
Fermi energy.
"""
kham = tb_to_khamvector(tb, nk, ks=None) kham = tb_to_khamvector(tb, nk, ks=None)
vals = np.linalg.eigvalsh(kham) vals = np.linalg.eigvalsh(kham)
return fermi_on_grid(vals, filling) return fermi_on_grid(vals, filling)
...@@ -21,7 +21,7 @@ def test_zero_hint(seed): ...@@ -21,7 +21,7 @@ def test_zero_hint(seed):
dim = np.random.randint(0, 3) dim = np.random.randint(0, 3)
ndof = np.random.randint(2, 10) ndof = np.random.randint(2, 10)
filling = np.random.randint(1, ndof) filling = np.random.randint(1, ndof)
random_hopping_vecs = utils.generate_vectors(cutoff, dim) random_hopping_vecs = utils.generate_tb_keys(cutoff, dim)
zero_key = tuple([0] * dim) zero_key = tuple([0] * dim)
h_0_random = utils.generate_guess(random_hopping_vecs, ndof, scale=1) h_0_random = utils.generate_guess(random_hopping_vecs, ndof, scale=1)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment