Real vs. Reciprocal Space in 2D¶
Introduction¶
In this short notebook, we will introduce the lattice with periodic(Born- von Karman) boundary condition. Following the boundary condition, there is a transformation from real lattice to the reciprocal lattice.
In the code below, we define a1 and a2 through cartesian vectors. Hence, the reciprocal vectors will be the original rotated by 90 degrees multiplied by 2pi and divided by the area of the unit cell (|a1xa2|)
In [1]:
import numpy as np
import matplotlib.pyplot as plt
# from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
%matplotlib widget
In [2]:
def get_reciprocal_vectors(a1,a2):
'''
:param a1,a2: Real Space 2D lattice vectors
:return: b1,b2 : Reciprocal Space basis vector
'''
# Define the 90 degree rotation matrix
R =[[0, -1],[1,0]]
# Create to vectors b1,b2 such that ai*bj = 2\pi \delta_{i,j}
# we first ensure the orthogonality of b1(2) wrt a2,(1) and then normalize the vector
b1 = 2*np.pi* np.matmul(R,a2)/np.matmul(np.transpose(a1),np.matmul(R,a2))
b2 = 2*np.pi* np.matmul(R,a1)/np.matmul(np.transpose(a2),np.matmul(R,a1))
return b1,b2
In [3]:
def plot_lattice(v1,v2,nb_x,nb_y,add_brillouin=False):
'''
:param v1,v2: lattice vectors
:parma nb_x,nb_y: (int) Control the number of points by setting the number of times v1 and v2 are repeated
:param add_brillouin: (bool) if True, show the 1st Brillouin zone
:return: matplotlib figure
'''
coordinates = []
# Set up the coordinates
for ii in np.arange(-nb_x+1,nb_x):
for jj in np.arange(-nb_y+1,nb_y):
coordinates.append(ii*v1 + jj*v2)
#Plot the unit cell
if not(add_brillouin):
edges = ([(0,0),(v1[0,0],v1[1,0]),(v1[0,0]+v2[0,0],v1[1,0]+v2[1,0]),(v2[0,0],v2[1,0])])
x,y = zip(*edges)
plt.fill(x, y, color='#6699cc', alpha=0.7,linewidth=3, zorder=2)
# Plot the first Brillouin zone
if add_brillouin:
# Note, we decided to keep v1 horizontal, so the extreme left and right xcoordinates are simply given by :
x_left = -v1[0,0]/2
x_right = v1[0,0]/2
x_interval = np.linspace(x_left,x_right,100)
# we need to find the equations of the 3 perpendicular bissector
#slope is given by -1/m
m1 = -((v2[1,0])/(v2[0,0]-v1[0,0]))**(-1)
m2 = -(v2[1,0]/v2[0,0])**(-1)
m3 = -(v2[1,0]/(v2[0,0]+v1[0,0]))**(-1)
# Middle of the segment
M1 = 0.5*(v2-v1)
M2=0.5*v2
M3 = 0.5*(v2+v1)
# Origin
b1 = M1[1,0]-m1*M1[0,0]
b2 = M2[1,0]-m2*M2[0,0]
b3 = M3[1,0]-m3*M3[0,0]
y_up = lambda x : np.minimum (x*m1+b1,np.minimum(m2*x+b2,x*m3+b3 ))
# Now we can do the same for the bottom (not the best way, but let's keep it simple)
m1_d = -((-v2[1,0])/(-v2[0,0]-v1[0,0]))**(-1)
m2_d = -(-v2[1,0]/-v2[0,0])**(-1)
m3_d = -(-v2[1,0]/(-v2[0,0]+v1[0,0]))**(-1)
# Middle of the segment
M1_d = 0.5*(-v2-v1)
M2_d=0.5*-v2
M3_d = 0.5*(-v2+v1)
# Origin
b1_d = M1_d[1,0]-m1_d*M1_d[0,0]
b2_d = M2_d[1,0]-m2_d*M2_d[0,0]
b3_d = M3_d[1,0]-m3_d*M3_d[0,0]
y_down = lambda x : np.maximum (x*m1_d+b1_d,np.maximum(m2_d*x+b2_d,x*m3_d+b3_d ))
plt.fill_between(x_interval,y_up(x_interval),y_down(x_interval))
# Separate the x and y coordinates (for the scatter function)
x,y=zip(*coordinates)
plt.scatter(x,y)
# Plot the lattice vectors
plt.arrow(0,0,v1[0,0],v1[1,0],color="red")
plt.arrow(0,0,v2[0,0],v2[1,0],color="red")
plt.xlim([-5, 10])
plt.ylim([-5, 10])
return plt.gcf
In [4]:
def interactive_lattice_plot(a1,a2,theta):
plt.clf()
'''
:param a1,a2: norm of the lattice vectors
:param theta: angle between the lattice vectors
'''
theta = theta/360*2*np.pi
# prepare the vectors
v2 = np.matrix([[0.00],[1.00]])
v1 = np.matmul(np.matrix([[np.cos(-theta),-np.sin(-theta)],[np.sin(-theta),np.cos(-theta)]]),v2)
v1*=a1
v2*=a2
plt.subplot(1,2,1)
plt.axis([-6,6,-6,6])
plt.axis('scaled')
plt.axis('off')
plot_lattice (v1,v2,24,24)
plt.subplot(1,2,2)
plt.axis([-6,6,-6,6])
plt.axis('scaled')
plt.axis('off')
b1,b2=get_reciprocal_vectors(v1,v2)
plot_lattice (b1*2/np.pi,b2*2/np.pi,24,24)
In [5]:
interact(interactive_lattice_plot, a1=widgets.FloatSlider(min=1.,max=4.,step=0.1, value=1, description = 'a1'), a2=widgets.FloatSlider(min=1.,max=4.,step=0.1, value=1, description = 'a2'),theta=widgets.FloatSlider(min=30,max=120,step=1, value=90, description = 'theta'))
interactive(children=(FloatSlider(value=1.0, description='a1', max=4.0, min=1.0), FloatSlider(value=1.0, descr…
Out[5]:
<function __main__.interactive_lattice_plot(a1, a2, theta)>
In [ ]: