diff --git a/codes/hf.py b/codes/hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..173f68ce1cbed369b07f06c9945d95d12d138417
--- /dev/null
+++ b/codes/hf.py
@@ -0,0 +1,40 @@
+from scipy.signal import convolve2d
+import numpy as np
+import utils
+
+def mean_field_F(vals, vecs, E_F):
+    N_ks = vecs.shape[0]
+    unocc_vals = vals > E_F
+
+    def mf_generator(i, j):
+        occ_vecs = vecs[i, j]
+        occ_vecs[:, unocc_vals[i, j]] = 0
+        F_ij = occ_vecs @ occ_vecs.conj().T
+        return F_ij
+
+    F = np.array([[mf_generator(i, j) for i in range(N_ks)] for j in range(N_ks)])
+    return F
+
+def convolution(M1, M2):
+    cell_size = M2.shape[-1]
+    V_output = np.array(
+        [
+            [
+                convolve2d(M1[:, :, i, j], M2[:, :, i, j], boundary="wrap", mode="same")
+                for i in range(cell_size)
+            ]
+            for j in range(cell_size)
+        ]
+    )
+    V_output = np.transpose(V_output, axes=(2, 3, 0, 1))
+    return V_output
+
+def compute_mf(vals, vecs, filling, nk, H_int):
+    H0_int = H_int[0,0]
+    E_F = utils.get_fermi_energy(vals, filling)
+    F = mean_field_F(vals, vecs, E_F=E_F)
+    rho = np.diag(np.average(F, axis=(0, 1)))
+    exchange_mf = convolution(F, H_int) * nk ** (-2)
+    direct_mf = np.diag(np.einsum("i,ij->j", rho, H0_int))
+
+    return direct_mf - exchange_mf
\ No newline at end of file
diff --git a/codes/utils.py b/codes/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..a2fbf84cb30d473c81198a9eb36c52e22a7ec8bb
--- /dev/null
+++ b/codes/utils.py
@@ -0,0 +1,82 @@
+import numpy as np
+import kwant
+
+def get_fermi_energy(vals, filling):
+    norbs = vals.shape[-1]
+    vals_flat = np.sort(vals.flatten())
+    ne = len(vals_flat)
+    ifermi = int(round(ne * filling / norbs))
+    if ifermi >= ne:
+        ifermi = ne - 1
+    sorte = np.sort(vals_flat)  # sorted eigenvalues
+    if ifermi == 0:
+        return sorte[0]
+    fermi = (sorte[ifermi - 1] + sorte[ifermi]) / 2.0  # fermi energy
+    return fermi
+
+def syst2hamiltonian(kxs, kys, syst, params={}):
+    def h_k(kx, ky):
+        return syst.hamiltonian_submatrix(params={**params, **dict(k_x=kx, k_y=ky)})
+    return np.array(
+        [[h_k(kx, ky) for kx in kxs] for ky in kys]
+    )
+
+def potential2hamiltonian(
+    syst, lattice, func_onsite, func_hop, ks, params={}, max_neighbor=1
+):
+    V = kwant.Builder(kwant.TranslationalSymmetry(*lattice.prim_vecs))
+    V[syst.sites()] = func_onsite
+    for neighbors in range(max_neighbor):
+        V[lattice.neighbors(neighbors + 1)] = func_hop
+    wrapped_V = kwant.wraparound.wraparound(V).finalized()
+    return syst2hamiltonian(kxs=ks, kys=ks, syst=wrapped_V, params=params)
+
+def generate_guess(max_neighbor, norbs, lattice):
+    n_sub = len(lattice.sublattices)
+    guess = np.zeros((n_sub + max_neighbor, 2, norbs, norbs))
+    for i in range(n_sub):
+        # Real part
+        guess[i, 0] = np.random.rand(norbs, norbs)
+        guess[i, 0] += guess[i, 0].T
+        # Imag part
+        guess[i, 1] = np.random.rand(norbs, norbs)
+        guess[i, 1] -= guess[i, 1].T
+    for neighbor in range(max_neighbor):
+        # Real part
+        guess[n_sub + neighbor, 0] = np.random.rand(norbs, norbs)
+        # Imag part
+        guess[n_sub + neighbor, 1] = np.random.rand(norbs, norbs)
+    return guess
+
+def extract_hopping_vectors(builder):
+    keep=None
+    deltas=[]
+    for hop, val in builder.hopping_value_pairs():
+        a, b=hop
+        b_dom = builder.symmetry.which(b)
+        # Throw away part that is in the remaining translation direction, so we get
+        # an element of 'sym' which is being wrapped
+        b_dom = np.array([t for i, t in enumerate(b_dom) if i != keep])
+        deltas.append(b_dom)
+    return np.asarray(deltas)
+
+def generate_scf_syst(max_neighbor, syst, lattice):
+    subs = np.array(lattice.sublattices)
+    def scf_onsite(site, mat):
+        idx = np.where(subs == site.family)[0][0]
+        return mat[idx, 0] + 1j * mat[idx, 1]
+
+    scf = kwant.Builder(kwant.TranslationalSymmetry(*lattice.prim_vecs))
+    scf[syst.sites()] = scf_onsite
+    for neighbor in range(max_neighbor):
+
+        def scf_hopping(site1, site2, mat):
+            return (
+                mat[len(lattice.sublattices) + neighbor, 0]
+                + 1j * mat[len(lattice.sublattices) + neighbor, 1]
+            )
+
+        scf[lattice.neighbors(neighbor + 1)] = scf_hopping
+    deltas = extract_hopping_vectors(scf)
+    wrapped_scf = kwant.wraparound.wraparound(scf).finalized()
+    return wrapped_scf, deltas
\ No newline at end of file