In [1]:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import brentq as find_root
plt.rc("figure", figsize=(3,2))
%config InlineBackend.figure_formats = ["svg"]
Define Helper Functions¶
In [2]:
adj = lambda u: np.conj(u.T)
In [3]:
def fermi(z):
if(beta*z<-30):
return 1
elif(beta*z>30):
return 0
else:
return 1/(1+np.exp(beta*z))
System Parameters¶
In [4]:
t1 = 1 #first hopping
t2 = 1 #second hopping
m = 0.5 #sublattice mass
U = 1 #Hubbard U
hz = 0.2 #zeeman splitting term
beta = 10**2 #cold limit
n_target = 1 #3/4 filling
delta = 0.1 #step size
eps = 1e-5 #initial asymmetry to allow possible magnetic ordering
Nk = 101
ks = np.linspace(-np.pi,np.pi,Nk,endpoint=False)
iterations = 100
Effective Hamiltonian and Step¶
In [5]:
def h_eff(k,ns):
"""
k = momentum point in [-pi,pi)
ns = dictionary of current orbital & spin resolved densities, spin-flip susceptibilities
"""
#hopping term
t = t1 + t2*np.exp(-1j*k)
#up-up
E_A_up = m + U*ns["n_A_dn"] #(the other spin densities because these electrons feel the effect of the _other_ spin species)
E_B_up = -m + U*ns["n_B_dn"]
h_uu = np.array([
[E_A_up+hz,t],
[adj(t),E_B_up+hz]
],dtype=complex)
#dn-dns
E_A_dn = m + U*ns["n_A_up"] #(the other spin densities because these electrons feel the effect of the _other_ spin species)
E_B_dn = -m + U*ns["n_B_up"]
h_dd = np.array([
[E_A_dn-hz,t],
[adj(t),E_B_dn-hz]
],dtype=complex)
#up-dn
S_A = -U*ns["s_A"] #(the other spin densities because these electrons feel the effect of the _other_ spin species)
S_B = -U*ns["s_B"]
h_ud = np.array([
[S_A,0],
[0,S_B]
],dtype=complex)
#full hamiltonian
h = np.block([
[h_uu,h_ud],
[adj(h_ud),h_dd]
])
return h
In [6]:
def new_density(ns,mu_guess):
n_A_up_new = 0
n_B_up_new = 0
n_A_dn_new = 0
n_B_dn_new = 0
s_A_new = 0
s_B_new = 0
for k_idx in range(Nk):
k = ks[k_idx]
#calculate eigenstates
Es,us = np.linalg.eigh(h_eff(k,ns))
#calculate densities
for i in range(4): #number of bands is 2*2 spins
n_A_up_new += fermi(Es[i]-mu_guess) * adj(us[0,i])*us[0,i] #0th element is sublattice A, spin up
n_B_up_new += fermi(Es[i]-mu_guess) * adj(us[1,i])*us[1,i] #1st element is sublattice B, spin up
n_A_dn_new += fermi(Es[i]-mu_guess) * adj(us[2,i])*us[2,i] #2nd element is sublattice A, spin dn
n_B_dn_new += fermi(Es[i]-mu_guess) * adj(us[3,i])*us[3,i] #3rd element is sublattice B, spin dn
s_A_new += fermi(Es[i]-mu_guess) * adj(us[2,i])*us[0,i] #s_A flips a down spin on A to an up spin on A
s_B_new += fermi(Es[i]-mu_guess) * adj(us[3,i])*us[1,i] #s_B flips a down spin on B to an up spin on B
#normalize by number of k points
n_A_up_new = np.real(n_A_up_new/Nk)
n_B_up_new = np.real(n_B_up_new/Nk)
n_A_dn_new = np.real(n_A_dn_new/Nk)
n_B_dn_new = np.real(n_B_dn_new/Nk)
s_A_new = s_A_new/Nk #this can be complex
s_B_new = s_B_new/Nk #this can be complex
return [n_A_up_new,n_B_up_new,n_A_dn_new,n_B_dn_new,s_A_new,s_B_new]
def update_density(ns,mu_guess):
n_A_up_new,n_B_up_new,n_A_dn_new,n_B_dn_new,s_A_new,s_B_new = new_density(ns,mu_guess)
#update densities
ns["n_A_up"] = (1-delta)*ns["n_A_up"] + delta*n_A_up_new
ns["n_B_up"] = (1-delta)*ns["n_B_up"] + delta*n_B_up_new
ns["n_A_dn"] = (1-delta)*ns["n_A_dn"] + delta*n_A_dn_new
ns["n_B_dn"] = (1-delta)*ns["n_B_dn"] + delta*n_B_dn_new
ns["s_A"] = (1-delta)*ns["s_A"] + delta*s_A_new
ns["s_B"] = (1-delta)*ns["s_B"] + delta*s_B_new
In [7]:
def total_density(ns,mu_guess):
n_A_up,n_B_up,n_A_dn,n_B_dn,_,_ = new_density(ns,mu_guess)
return n_A_up + n_B_up + n_A_dn + n_B_dn
def diff(mu_guess):
return total_density(ns,mu_guess) - n_target
Iteration¶
In [8]:
#initial guess at occupations
ns = {
"n_A_up" : n_target/4-eps,
"n_B_up" : n_target/4-eps,
"n_A_dn" : n_target/4+eps,
"n_B_dn" : n_target/4+eps,
"s_A" : eps,
"s_B" : eps
}
mu_guess = 0.0
#iterate
densities = np.zeros((iterations,4))
spin_flips = np.zeros((iterations,2),dtype=complex)
for s in range(iterations):
#find mu
if(diff(-10)*diff(10)<0):
pass
else:
raise(ValueError("mu not found"))
mu = find_root(diff,-10,10)
#compute
update_density(ns,mu)
densities[s] = [ns["n_A_up"],ns["n_B_up"],ns["n_A_dn"],ns["n_B_dn"]]
spin_flips[s] = [ns["s_A"],ns["s_B"]]
Evolution of Densities¶
In [9]:
plt.plot(densities[:,0],label=r"$n_{A,\!\!\!\uparrow}$")
plt.plot(densities[:,1],label=r"$n_{B,\!\!\!\uparrow}$")
plt.plot(densities[:,2],ls="dashed",label=r"$n_{A,\!\!\!\downarrow}$")
plt.plot(densities[:,3],ls="dashed",label=r"$n_{B,\!\!\!\downarrow}$")
plt.xlabel("iteration")
plt.ylabel("occupation")
plt.legend(fontsize=9)
plt.ylim(0.1,0.405)
Out[9]:
(0.1, 0.405)
In [10]:
plt.plot(np.real(spin_flips[:,0]),label=r"Re $s_A$")
plt.plot(np.real(spin_flips[:,1]),ls="dashed",label=r"Re $s_B$")
plt.xlabel("iteration")
plt.ylabel("spin flip coherence")
plt.legend()
Out[10]:
<matplotlib.legend.Legend at 0x169291c70>
Free and HF Energy Bands¶
In [11]:
eigs_free = np.zeros((Nk,4))
eigs_hf = np.zeros((Nk,4))
for k_idx in range(Nk):
eigs_free[k_idx] = np.linalg.eigvalsh(h_eff(ks[k_idx],{"n_A_up":0,"n_B_up":0,"n_A_dn":0,"n_B_dn":0,"s_A":0,"s_B":0}))
eigs_hf[k_idx] = np.linalg.eigvalsh(h_eff(ks[k_idx],ns))
In [12]:
plt.plot(ks,eigs_free[:,0],c="r",label=r"$\epsilon_{0}$")
for n in range(1,4): #2 bands * 2 spins
plt.plot(ks,eigs_free[:,n],c="r")
plt.plot(ks,eigs_hf[:,0]-1/4*U,ls="dashed",c="b",label=r"$\epsilon_{HF}$")
for n in range(1,4): #2 bands * 2 spins
plt.plot(ks,eigs_hf[:,n]-1/4*U,ls="dashed",c="b")
plt.xticks([-np.pi,0,np.pi],[r"$-\pi$",r"$0$",r"$\pi$"])
plt.xlabel(r"$k$")
plt.ylabel(r"Energy")
plt.legend(loc=1)
Out[12]:
<matplotlib.legend.Legend at 0x16935d400>
In [ ]:
In [ ]: