Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Project: topocm
Views: 387
1
"""
2
Copyright (c) 2015 and later, Jayson Paulose. All rights reserved.
3
4
This script computes properties of the Kagome lattice model introduced
5
in the paper
6
7
"Topological boundary modes in isostatic lattices"
8
C.L. Kane and T. Lubensky
9
Nature Physics 10, 39 (2014)
10
arXiv:1308.0554
11
12
Redistribution and use in source and binary forms, with or without
13
modification, are permitted provided that the following conditions are
14
met:
15
16
1. Redistributions of source code must retain the above copyright
17
notice, this list of conditions and the following disclaimer.
18
19
2. Redistributions in binary form must reproduce the above copyright
20
notice, this list of conditions and the following disclaimer in the
21
documentation and/or other materials provided with the distribution.
22
23
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
"""
35
36
import numpy as np
37
from random import randint
38
import matplotlib.pyplot as plt
39
from numpy.linalg import norm
40
import scipy
41
from scipy import linalg as la
42
43
44
hex2dbasis = (np.array([1.,0]), np.array([0.5,np.sqrt(3.)/2.]))
45
hex2dbonds = [((0,0),(1,0)),((0,0),(0,1)),((0,0),(-1,1))]
46
klbasisbonds = [((0,1),(0,0)),
47
((1,2),(0,0)),
48
((2,0),(0,0)),
49
((1,2),(1,0)),
50
((2,0),(-1,1)),
51
((0,1),(0,-1))]
52
53
def not_same(bond1,bond2):
54
if bond1.p1 == bond2.p1 and bond1.p2 == bond2.p2: return False
55
if bond1.p2 == bond2.p1 and bond1.p1 == bond2.p2: return False
56
return True
57
58
class Bond:
59
def __init__(self, p1, p2, color='r',k=1.,l0=1.):
60
self.p1 = p1
61
self.p2 = p2
62
self.color = color
63
self.k = k
64
self.l0 = l0
65
66
def __str__(self):
67
return '{%d, %d, %1.4f, %1.4f, %s}' % (self.p1, self.p2, self.k, self.l0, self.color)
68
69
def __repr__(self):
70
return self.__str__()
71
72
class Mesh:
73
def __init__(self,dim=2,l=None):
74
self.Points = []
75
self.Bonds = []
76
self.N = 0
77
self.nbrinfo = []
78
self.l = l
79
self.dim=dim
80
self.dislocations = []
81
if self.l is None:
82
self.l = np.zeros(dim)
83
if len(self.l) != dim:
84
print("error: box lengths must be of correct dimension")
85
86
def points(self):
87
return np.array(self.Points)
88
89
def bonds(self):
90
return np.array([[bd.p1,bd.p2] for bd in self.Bonds])
91
92
def add_point(self,point):
93
self.Points.append(point)
94
self.nbrinfo.append(dict([]))
95
self.N += 1
96
97
98
def append_bond(self,bond):
99
self.Bonds.append(bond)
100
101
def add_bond(self, p1,p2=None,color='r',k=1.,l0=1.):
102
if p2 is None:
103
self.append_bond(p1,k=k,l0=l0)
104
else:
105
self.append_bond(Bond(p1,p2,color,k=k,l0=l0))
106
107
if len(color) == 2:
108
self.nbrinfo[p1][color[1]] = p2
109
self.nbrinfo[p2][color[0]] = p1
110
111
def dr(self,p1,p2=None):
112
if p2 is None:
113
p2 = p1.p2
114
p1 = p1.p1
115
dx = self.Points[p2] - self.Points[p1]
116
for i in range(self.dim):
117
dx[i] = dx[i] if abs(dx[i])<0.5*self.l[i] else dx[i]-abs(dx[i]+0.5*self.l[i])+abs(dx[i]-0.5*self.l[i])
118
return dx
119
120
121
122
def klbasis(x1,x2,x3,z=0):
123
"""Calculates the *s* vectors in the Kane-Lubensky parametrization (*Nat Phys 2014*) of the
124
deformed kagome lattices.
125
126
:param x1,x2,x3,z: The four parameters used by Kane and Lubensky in their parametrization.
127
:returns: List of the three vectors *s1,s2,s3* as defined in Fig. 2a of the paper.
128
"""
129
a1,a2,a3=[np.array([np.cos(2*np.pi*p/3.),np.sin(2*np.pi*p/3.)]) for p in range(3)]
130
y1 = z/3.+x3-x2
131
y2 = z/3.+x1-x3
132
s1 = x1*(a3-a2)+y1*a1
133
s2 = x2*(a1-a3)+y2*a2
134
return s2,np.array([0.,0.]),-s1 # return xvec30, xvec41, xvec52
135
136
137
def klbasispoints(x1,x2,x3,z=0):
138
"""Calculate the positions of the three points in the unit cell of the deformed kagome
139
lattice under the Kane-Lubensky parametrization.
140
141
:param x1,x2,x3,z: Four parameters used in the Kane-Lubensky parametrization.
142
:returns: List of three position vectors *d3,d1,d2* of the basis points in the unit cell as
143
defined in Fig. 2a of the paper.
144
"""
145
xvec30,xvec41,xvec52 = klbasis(x1,x2,x3,z)
146
x1, x2, x3 = np.vstack((np.cos(np.pi/3*np.arange(3)),np.sin(np.pi/3*np.arange(3)))).T
147
p1 = x1/2.+xvec30
148
p2 = (x1+x2)/2.+xvec52
149
p3 = x2/2.+xvec41
150
return [p1,p2,p3]
151
152
153
def vis2d(mesh,
154
draw_points=False,
155
eigenfunction=None,
156
ecolors='r',
157
offset=np.array([0.,0.]),
158
colormap='jet',
159
lw=1,
160
quiverwidth=None,
161
bondcolor=(.3,.3,.3,1.),
162
pad=1,
163
**kwargs):
164
"""Function for visualizing a mesh.
165
166
:param mesh: Input mesh, a :class:`Mesh` instance.
167
:param draw_points: If :const:`True`, draw all points with 'o' markers.
168
:param draw_edges: If:const:`True`, draw bonds as lines of thickness ``lw``.
169
:param eigenfunction: Pass an eigenmode for visualization. This is a 1D numpy array
170
whose length is either ``2*mesh.N`` or ``2*mesh.N+Nb``, where ``N`` and ``Nb`` are the
171
number of points and bonds in ``mesh``. If former, draws arrows corresponding to displacements
172
at each point (two consecutive numbers per point to give first ``2*mesh.N`` entries).
173
If latter, also colours bonds according to tensions which are the last ``Nb`` entries.
174
:param eigenfunctions: List of ``eigenfunction`` arrays to plot simultaneously.
175
:param ecolors: Single color or list of colors with which to draw arrows for ``eigenfunction(s)`` visualization.
176
Accepts any color specifications recognized by :mod:`matplotlib`.
177
:param figname: String that, if passed, becomes filename in which visualization is saved. Should be a valid
178
format for the :meth:`savefig()` command of :mod:`matplotlib.pyplot`.
179
:param noshow: If :const:`True`, does not call :func:`plt.show()` at end of plotting.
180
:param figidx,ax: Specify a figure index (integer) or axis (:obj:`matplotlib.pyplot.Axes` object) within
181
which to plot the mesh. Useful if you want to predefine a figure and axes to plot in. If :const:`None`,
182
a blank figure and axes for the plot are created from scratch.
183
:param clear: If :const:`True`, clear pre-existing stuff on ``figidx`` or ``ax``.
184
:param cutoff: Length of the longest bonds that are actually drawn. If :const:`None`, assigned to be
185
the smaller of ``mesh.lx/2`` or ``mesh.ly/2``, so that bonds that wrap around the system due to
186
periodic boundary conditions are not drawn. Set to some very large number to make sure all bonds are drawn.
187
:param offset: A shift to every point. Can be either a single vector (1D array with two entries) to add
188
to every point, or a ``Nx2`` array giving a unique shift to each of the ``N`` points.
189
:param colormap: Colormap to use for eigenfunction tension visualization as bond colors. String (name) or ColorMap
190
instance as recognized by :mod:`matplotlib`.
191
:param colorbar: If :const:`True`, show a colorbar of the colormap values.
192
:param lw: Line width of bond lines.
193
:param quiverwidth: Width of quiver arrows used for eigenmode visualization.
194
:param bondcolor: Color of bonds (if eigenfunction tensions are not defined). Any olor recognized by
195
:mod:`matplotlib` will work.
196
:param linecollection: If :const:`True`, uses a slightly slower method to draw the edges which behaves
197
much better under zooming in an interactive session.
198
:param ptidx: List of specific points to be drawn if ``draw_points`` is :const:`True`.
199
If :const:`None`, all points are drawn.
200
:param bondforces: A 1D array of length ``Nb`` of scalar values used to color each bond. All parameters that
201
affect eigenfunction tension plotting also affect this.
202
:param pad: Padding around mesh extremities to make sure all points are visible.
203
:param colorbarsymm: if :const:`True`, color bar is symmetric abound zero.
204
205
"""
206
207
fig = plt.figure(figsize=(8,4))
208
ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[])
209
210
211
fig.figurePatch.set_edgecolor('white')
212
fig.figurePatch.set_facecolor('white')
213
ax.set_aspect('equal')
214
ax.set_axis_off()
215
216
pts = mesh.points()+offset
217
lenmode = 2*len(pts)
218
219
zorder=0
220
cutoff = mesh.lx/2 if mesh.lx < mesh.ly else mesh.ly/2
221
222
xpairs = [[pts[bd.p1][0], pts[bd.p2][0]] for bd in mesh.Bonds
223
if norm(pts[bd.p1] - pts[bd.p2]) < cutoff]
224
ypairs = [[pts[bd.p1][1], pts[bd.p2][1]] for bd in mesh.Bonds
225
if norm(pts[bd.p1] - pts[bd.p2]) < cutoff]
226
xlist = []
227
ylist = []
228
for xends, yends in zip(xpairs,ypairs):
229
xlist.extend(xends)
230
xlist.append(None)
231
ylist.extend(yends)
232
ylist.append(None)
233
ax.plot(xlist,ylist,'-',color=bondcolor,lw=lw,zorder=zorder)
234
zorder += 1
235
ax.quiver(pts[:,0], pts[:,1],
236
eigenfunction[:lenmode:2], eigenfunction[1:lenmode:2],
237
color=ecolors, zorder=zorder, width=quiverwidth, **kwargs)
238
return fig
239
240
241
def periodicize(mesh,l,eps =.101231):
242
mesh += np.ones_like(l)*eps
243
l = np.array(l)
244
for pt in mesh:
245
wrap = np.round((pt-l/2)/l)
246
pt -= wrap*l
247
mesh -= np.ones_like(l)*eps
248
249
250
251
def makeLattice(a,basis,bondlist,n,rectangle=True,periodic=True,boundaryshift=None):
252
'''
253
Tile a unit cell to get a lattice. General dimensions
254
a: list of primitive lattice vectors (length gives dimension)
255
basis: set of point positions for a lattice
256
bondlist: adjacency list connecting points across respective boundaries
257
n: repeating units in each dimension
258
'''
259
dim = len(a)
260
261
if basis is None: basis = [np.zeros(dim,dtype=float)]
262
263
if boundaryshift is not None: boundaryshift=np.array(boundaryshift)
264
265
def idx(latticept,basisidx):
266
internalidx = sum([latticept[i]*np.prod(n[i+1:]) for i in range(dim)])
267
return int(internalidx + basisidx*np.prod(n))
268
269
totalsize = np.prod(n)*len(basis)
270
mesh = Mesh(dim,l=10000*np.ones(dim))
271
272
slices = tuple([slice(0,ni) for ni in n])
273
grid = np.mgrid[slices]
274
lattice = np.sum([a[i]*grid[i].ravel()[:,None] for i in range(dim)], axis=0)
275
#print lattice.shape
276
if rectangle:
277
dx = np.max(np.abs(np.array(a)),axis=0)
278
l = dx*np.array(n)
279
periodicize(lattice,l)
280
mesh.l=l
281
allpts = np.vstack([lattice + b for b in basis])
282
for p in allpts: mesh.add_point(p)
283
284
aidx = np.eye(dim,dtype=int)
285
latticeidx = np.sum([aidx[i]*grid[i].ravel()[:,None] for i in range(dim)], axis=0)
286
for lpt in latticeidx:
287
for bond, delta in bondlist:
288
secondpt = lpt+delta
289
if not periodic and not rectangle:
290
if np.all((0 <= secondpt) & (secondpt < n)):
291
mesh.add_bond(idx(lpt,bond[0]),idx(secondpt,bond[1]))
292
else:
293
secondptwrap = np.mod(secondpt,n)
294
whichborder = (secondptwrap != secondpt)
295
if np.any(whichborder) and boundaryshift is not None:
296
bsd = boundaryshift*np.sign(delta)[:,None]
297
secondptwrap = secondptwrap + np.sum(bsd[whichborder],axis=0)
298
idx1 = idx(lpt,bond[0])
299
idx2 = idx(np.mod(secondptwrap,n),bond[1])
300
if periodic:
301
mesh.add_bond(idx1,idx2)
302
else:
303
dx = np.abs(mesh.Points[idx1]-mesh.Points[idx2])
304
if np.all(dx < mesh.l/2):
305
mesh.add_bond(idx1,idx2)
306
307
return mesh
308
309
def kagome2d(lx,ly,x,rect=True,periodic=True):
310
boundaryshift=None
311
if rect: boundaryshift = [[0,0],[ly/2,0]]
312
lattice = makeLattice(hex2dbasis,klbasispoints(*x),
313
klbasisbonds,(lx,ly),rectangle=rect,boundaryshift=boundaryshift,periodic=periodic)
314
return to2dlattice(lattice)
315
316
def to2dlattice(mesh):
317
mesh.lx = mesh.l[0]
318
mesh.ly = mesh.l[1]
319
return mesh
320
321
322
def replacepoints(mesh1,mesh2,x1frac=.33,x2frac=.66):
323
'''
324
replace points in mesh1 with points from mesh2 for x between x1 and x2
325
'''
326
for i,pt in enumerate(mesh1.Points):
327
if x1frac*mesh1.l[0] < pt[0] < x2frac*mesh2.l[0]:
328
mesh1.Points[i] = mesh2.Points[i]
329
return mesh1
330
331
def dwallslab(x1,x2,lx=16,ly=6,dwall1=.25,dwall2=.75):
332
mesh1 = kagome2d(lx,ly,x1,rect=True)
333
mesh2 = kagome2d(lx,ly,x2,rect=True)
334
335
replacepoints(mesh1,mesh2,dwall1,dwall2)
336
return mesh1
337
338
339
##################################
340
# Rigidity matrix and eigenmodes #
341
##################################
342
343
344
def rigiditymatrix(mesh):
345
r = []
346
pts = mesh.points()
347
bds = mesh.Bonds
348
dim = len(pts[0])
349
for bd in bds:
350
row = np.zeros(dim*mesh.N)
351
dp = mesh.dr(bd.p2,bd.p1)
352
dp = dp/la.norm(dp)
353
for i in range(dim):
354
row[dim*bd.p1+i] = dp[i]
355
row[dim*bd.p2+i] = -dp[i]
356
r.append(row)
357
return np.matrix(r)
358
359
360
def dynamicalmatrix(mesh):
361
r = rigiditymatrix(mesh)
362
return np.dot(r.T,r)
363
364
def modes(mesh):
365
eigval,eigvec = la.eigh(dynamicalmatrix(mesh))
366
eigvec = np.array(eigvec).T
367
368
sortedargs = np.argsort(np.real(eigval))
369
return np.real(eigval)[sortedargs], np.real(eigvec)[sortedargs]
370
371
372
def showlocalizedmode(mesh,modenumber=2):
373
ee,ev = modes(mesh)
374
return vis2d(mesh,eigenfunction=ev[modenumber],scale=2)
375
376
377