From 4b9dcc65424075c871cef52bf8233f5c810e07d6 Mon Sep 17 00:00:00 2001 From: antoniolrm <am@antoniomanesco.org> Date: Tue, 24 Oct 2023 17:00:04 +0200 Subject: [PATCH] introduce dictionary input --- codes/hf.py | 20 ++++++- codes/utils.py | 145 +++++++++++++++++++++++++++++-------------------- 2 files changed, 104 insertions(+), 61 deletions(-) diff --git a/codes/hf.py b/codes/hf.py index b418f2c..ee836b9 100644 --- a/codes/hf.py +++ b/codes/hf.py @@ -136,7 +136,16 @@ def scf_loop(mf, H_int, filling, hamiltonians_0, tol): def find_groundstate_ham( - H_int, filling, hamiltonians_0, tol, guess, mixing=0.5, order=1, verbose=False + tb_model, + int_model, + filling, + nk=10, + tol=1e-6, + guess=None, + mixing=0.1, + order=3, + verbose=False, + return_ks=False ): """ Self-consistent loop to find groundstate Hamiltonian. @@ -165,8 +174,13 @@ def find_groundstate_ham( hamiltonian : nd-array Groundstate Hamiltonian with same format as `H_int`. """ + hamiltonians_0, ks = utils.kgrid_hamiltonian(nk, tb_model, return_ks=True) fun = partial( - scf_loop, H_int=H_int, filling=filling, hamiltonians_0=hamiltonians_0, tol=tol + scf_loop, + hamiltonians_0=hamiltonians_0, + H_int=utils.kgrid_hamiltonian(nk, int_model), + filling=filling, + tol=tol, ) mf = anderson(fun, guess, f_tol=tol, w0=mixing, M=order, verbose=verbose) - return hamiltonians_0 + mf + return utils.hk2tb_model(hamiltonians_0 + mf, tb_model, int_model, ks) diff --git a/codes/utils.py b/codes/utils.py index 603537c..ac68cda 100644 --- a/codes/utils.py +++ b/codes/utils.py @@ -2,6 +2,7 @@ import numpy as np import kwant from itertools import product + def get_fermi_energy(vals, filling): """ Compute Fermi energy for a given filling factor. @@ -24,7 +25,7 @@ def get_fermi_energy(vals, filling): return fermi -def syst2hamiltonian(ks, syst, params={}, coordinate_names="xyz"): +def kwant2hk(syst, params={}, coordinate_names="xyz"): """ Obtain Hamiltonian on k-point grid for a given `kwant` system. @@ -50,26 +51,87 @@ def syst2hamiltonian(ks, syst, params={}, coordinate_names="xyz"): for i in range(len(syst._wrapped_symmetry.periods)) ] - def h_k(k): + def bloch_ham(k): _k_dict = {} for i, k_n in enumerate(momenta): _k_dict[k_n] = k[i] return syst.hamiltonian_submatrix(params={**params, **_k_dict}) - k_pts = np.tile(ks, len(momenta)).reshape(len(momenta), len(ks)) + return bloch_ham + + +def dict2hk(tb_dict): + """ + Build Bloch Hamiltonian. + + + Returns: + -------- + ham : function + Evaluates the Hamiltonian at a give k-point + """ + + def bloch_ham(k): + return sum( + tb_dict[vector] * np.exp(1j * np.dot(k, np.array(vector))) + for vector in tb_dict.keys() + ) + + return bloch_ham + + +def kgrid_hamiltonian(nk, syst, params={}, return_ks=False): + """ + Evaluates Hamiltonian on a k-point grid. + + Paramters: + ---------- + nk : int + Number of k-points along each direction. + tb_dict : dictionary + Must have the following structure: + - Keys are tuples for each hopping vector (in units of lattice vectors). + - Values are hopping matrices. + + Returns: + -------- + ham : nd.array + Hamiltonian evaluated on a k-point grid from k-points + along each direction evaluated from zero to 2*pi. + The indices are ordered as [k_1, ... , k_n, i, j], where + `k_m` corresponding to the k-point element along each + direction and `i` and `j` are the internal degrees of freedom. + """ + if type(syst) == kwant.builder.FiniteSystem: + try: + dim = len(syst._wrapped_symmetry.periods) + hk = kwant2hk(syst, params) + except: + return syst.hamiltonian_submatrix(params=params) + elif type(syst) == dict: + dim = len(next(iter(syst))) + if dim == 0: + return syst[next(iter(syst))] + else: + hk = dict2hk(syst) + + ks = 2 * np.pi * np.linspace(0, 1, nk, endpoint=False) + + k_pts = np.tile(ks, dim).reshape(dim, nk) ham = [] for k in product(*k_pts): - ham.append(h_k(k)) + ham.append(hk(k)) ham = np.array(ham) - shape = (*np.repeat(k_pts.shape[1], k_pts.shape[0]), ham.shape[-1], ham.shape[-1]) + shape = (*[nk] * dim, ham.shape[-1], ham.shape[-1]) - return ham.reshape(*shape) + if return_ks: + return ham.reshape(*shape), ks + else: + return ham.reshape(*shape) -def build_interacting_syst( - syst, lattice, func_onsite, func_hop, max_neighbor=1 -): +def build_interacting_syst(syst, lattice, func_onsite, func_hop, max_neighbor=1): """ Construct an auxiliary `kwant` system to build Hamiltonian matrix. @@ -102,42 +164,7 @@ def build_interacting_syst( return syst_V -def assign_kdependence( - ks, hopping_vecs, hopping_matrices -): - """ - Computes Bloch matrix. - - ks : 1D-array - Set of k-points. Repeated for all directions. - hopping_vecs : 2D-array - Hopping vectors. - hopping_matrices : 3D-array - Hopping matrices. - - Returns: - -------- - bloch_matrix : nd-array - Bloch matrix on a k-point grid. - """ - ndof = hopping_matrices[0].shape[0] - dim = len(hopping_vecs[0]) - nks = [len(ks) for i in range(dim)] - bloch_matrix = np.zeros((nks + [ndof, ndof]), dtype=complex) - kgrid = ( - np.asarray(np.meshgrid(*[ks for i in range(dim)])) - .reshape(dim, -1) - .T - ) - - for vec, matrix in zip(hopping_vecs, hopping_matrices): - bloch_phase = np.exp(1j * np.dot(kgrid, vec)).reshape(nks + [1, 1]) - bloch_matrix += matrix.reshape([1 for i in range(dim)] + [ndof, ndof]) * bloch_phase - - return bloch_matrix - - -def generate_guess(nk, hopping_vecs, ndof, scale=0.1): +def generate_guess(nk, syst_V, scale=0.1): """ nk : int number of k points @@ -155,19 +182,17 @@ def generate_guess(nk, hopping_vecs, ndof, scale=0.1): Assumes that the desired max nearest neighbour distance is included in the hopping_vecs information. Creates a square grid by definition, might still want to change that """ - dim = len(hopping_vecs[0]) - all_rand_hermitians = [] - for n in hopping_vecs: + ndof = syst_V[next(iter(syst_V))].shape[-1] + guess = {} + for vector in syst_V.keys(): amplitude = np.random.rand(ndof, ndof) phase = 2 * np.pi * np.random.rand(ndof, ndof) rand_hermitian = amplitude * np.exp(1j * phase) rand_hermitian += rand_hermitian.T.conj() rand_hermitian /= 2 - all_rand_hermitians.append(rand_hermitian) - all_rand_hermitians = np.asarray(all_rand_hermitians) + guess[vector] = rand_hermitian - guess = assign_kdependence(nk, hopping_vecs, all_rand_hermitians) - return guess * scale + return kgrid_hamiltonian(nk, guess) * scale def extract_hopping_vectors(builder): @@ -195,7 +220,7 @@ def extract_hopping_vectors(builder): return np.asarray(hopping_vecs) -def hk2hop(hk, hopping_vecs, ks): +def hk2tb_model(hk, tb_model, int_model, ks): """ Extract hopping matrices from Bloch Hamiltonian. @@ -213,13 +238,13 @@ def hk2hop(hk, hopping_vecs, ks): hopps : 3d-array Hopping matrices. """ + hopping_vecs = np.unique(np.array([*tb_model.keys(), *int_model.keys()]), axis=0) ndim = len(hk.shape) - 2 dk = np.diff(ks)[0] nk = len(ks) k_pts = np.tile(ks, ndim).reshape(ndim, nk) k_grid = np.array(np.meshgrid(*k_pts)) k_grid = k_grid.reshape(k_grid.shape[0], np.prod(k_grid.shape[1:])) - # Can probably flatten this object to make einsum simpler hk = hk.reshape(np.prod(hk.shape[:ndim]), *hk.shape[-2:]) hopps = ( @@ -231,10 +256,14 @@ def hk2hop(hk, hopping_vecs, ks): * (dk / (2 * np.pi)) ** ndim ) - return hopps + tb_model = {} + for i, vector in enumerate(hopping_vecs): + tb_model[tuple(vector)] = hopps[i] + + return tb_model -def hk_densegrid(hk, ks, ks_dense, hopping_vecs): +def hk_densegrid(hk, ks, nk_dense): """ Recomputes Hamiltonian on a denser k-point grid. @@ -254,8 +283,8 @@ def hk_densegrid(hk, ks, ks_dense, hopping_vecs): hk_dense : nd-array Bloch Hamiltonian computed on a dense grid. """ - hops = hk2hop(hk, hopping_vecs, ks) - return assign_kdependence(ks_dense, hopping_vecs, hops) + tb_model = hk2hop(hk, ks) + return kgrid_hamiltonian(nk_dense, tb_model) def calc_gap(vals, E_F): -- GitLab