Substrates

Benchmark substrate potentials: different wells and symmetries

The clusters move on a rigid substrate potential. This is defined in terms of a lattice or in terms of a superposition of plane waves (so it can also be a quasi-crystal).

For plane waves, the number and realtive phase of waves controls the symmetry and a parameter \(\epsilon\) the amplitude.

In case of a lattice, the repeated unit, or \textit{well}, can be an arbitrary function. We do not put any restriction on it, but one would usually assume the substrate is a continous function. Hence to avoid discontinuities, the well function should go to zero at the cell border.

[1]:
import numpy as np
from numpy import sqrt
import matplotlib.pyplot as plt

# Matrices defining substrate periodicity
from tool_create_substrate import calc_matrices_triangle, calc_matrices_square, calc_matrices_bvect
# Substrate types (particles-wise functions)
from tool_create_substrate import particle_en_gaussian, particle_en_sin, particle_en_tanh
# Misc
from tool_create_substrate import gaussian, get_ks
from misc import get_brillouin_zone_2d, plot_BZ2d, plot_UC

Lattice substrate

Define the susbstrate as a lattice of repeated potential wells.

Substrate symmetry

The substrate lattice is defined giving the two primitive vectors, just like a cluster.

[2]:
# Define the substrate lattice using vectors
### Ortho
#b1, b2, R = np.array([1,0]), np.array([0,2]), None # Ortho
#(u, u_inv), sym = calc_matrices_bvect(b1, b2), 'ortho'
### Triangle
b1, b2, R = np.array([1,0]), np.array([1/2, -sqrt(3)/2]), None # Triangle
(u, u_inv), sym = calc_matrices_bvect(b1, b2), 'triangle'

# Shortcut for common lattice (R is lattice spacing)
# R = 1
# Triangle (shortcut)
# (u, u_inv), sym = calc_matrices_triangle(R), ' triangle'

# Square (shortcut)
# (u, u_inv), sym = calc_matrices_square(R), ' square'

Basis

The Bravais lattice of the substrate can be decorated with a basis

[3]:
basis = [np.array([0,0])] # Simple lattice

# Decorated lattice examples
# basis = [[0,0], [0.6, 1]]
# basis = [[0,0], [0.5, 0.75]]
# basis = [[0,0], [0.5, 1.5]]

Well shape

This is the functino repeated at each lattice point

[4]:
# Select according to below: Gauss, Tanh, LJ // Sin to swi
sub_type = 'Sin'

Gauss

See Xin Cao, Andrea Silva, Emanuele Panizon, Andrea Vanossi, Nicola Manini, Erio Tosatti, and Clemens Bechinger Phys. Rev. X 12, 021059 (2022)

[5]:
if sub_type == 'Gauss':
    print('Gaussian substrate')
    title = 'Gauss well - Sym: %s' % sym
    R, epsilon, sigma, a, b = 1, 1, 0.1, 0.2, 0.45
    en_inputs = [basis, a, b, sigma, epsilon, u, u_inv]
    en_func = particle_en_gaussian

Tanh

See Xin Cao, Emanuele Panizon, Andrea Vanossi, Nicola Manini, Erio Tosatti, and Clemens Bechinger Phys. Rev. E 103, 012606 (2021)

[6]:
if sub_type == 'Tanh':
    print('Tanh substrate')
    title = 'Tanh well - Sym: %s' % sym
    epsilon, ww, a, b = 1, 0.25, 0.1, 0.45
    #epsilon, ww, a, b = 1, 0.1, 0.1, 0.45
    en_inputs = [basis, a, b, ww, epsilon, u, u_inv]
    en_func = particle_en_tanh

Plane wave substrate

See Vanossi, Manini, Tosatti, PNAS 9 109 16429–16433 (2012)

[7]:
if sub_type == 'Sin':
    print('Sin substrate')
    # Substrate params
    R, epsilon = 1, 1
    # Define symmetry of substrate (number of PW and phase)
    #n, c_n, alpha_n = 2, 1, 0 # Lines
    n, c_n, alpha_n = 3, 4/3, 0 # Tri
    #n, c_n, alpha_n = 4, np.sqrt(2), pi/4 # Square
    #n, c_n, alpha_n = 5, 2, 0 # Qausi-cristal
    #n, c_n, alpha_n = 6, 4/np.sqrt(3), -pi/6
    ks = get_ks(R, n, c_n, alpha_n) # Get the reciprocal vectors defined by the coefficients above

    title = 'Sin - sym n=%i' % n

    en_inputs = [basis, ks, epsilon]
    en_func = particle_en_sin
Sin substrate

Plot

[8]:
# Matrix describing the lattice: S[0] first vector, S[1] second
if sub_type == 'Sin':
    print("Sin potential: no matrix S, u, u_inv")
    S = np.array([[0,0], [R,0]]) # Migtht not be well defined, q-crystals
else:
    print("Gauss/Tanh/LJ potential: use S, u, u_inv")
    S = u_inv.T
Sin potential: no matrix S, u, u_inv

Grid and compute energetics

Let’s look at a portion of this infinite substrate. Define a grid in real space, and

[9]:
x0, x1, nx = -2, 2, 150
y0, y1, ny = -2, 2, 150
xx, yy = np.meshgrid(np.linspace(x0, x1, nx), np.linspace(y0, y1, ny))
# I am a physics not a computer scientist, so I don't like this meshgrid format.
# I want N points in two dimension, so N x 2
pp = np.stack([xx, yy], axis=2)
p = np.reshape(pp, (pp.shape[0]*pp.shape[1], 2))

en, F, tau = en_func(p, [0,0], *en_inputs)

# x line
xyline = np.stack([np.linspace(0, x1/2, 2*nx), np.zeros(2*nx)], axis=1)
# skwed line
#xyline = np.stack([np.linspace(0, x1, nx), 0.5*np.linspace(0, y1, ny)], axis=1)
tline = np.linspace(0, 1, len(xyline))
enl, Fl, taul = en_func(xyline, [0,0], *en_inputs)
[10]:
def plot_UC(ax, u, params={'ls': '--', 'color': 'tab:gray', 'lw': 1}):
    """Shortcut to plot the Brillouin zone of the lattice"""
    BZ_corner = np.array([(n*u[0]+m*u[1])
                          for n,m in [[0,0], [1,0], [1,1], [0,1], [0,0]]
                         ])
    for i in range(4):
        ax.plot([BZ_corner[i+1,0], BZ_corner[i,0]],
                [BZ_corner[i+1,1], BZ_corner[i,1]], **params)
    return ax

2D maps

First plot the energy density, the two components of the force density field.

We can also plot the torque density field with repsect to the origin. Just remember that for an infinite layer like this it makes not so much sense.

[11]:
fig, ((axE, axFx), (axFy, axtau)) = plt.subplots(2,2, dpi=200, sharex=True, sharey=True, figsize=(8,6))
fig.suptitle(title)
ws_params = {'ls': '--', 'color': 'tab:gray', 'lw': 1, 'fill': False}
s0 = 1

# Energy
sc = axE.scatter(p[:,0], p[:,1], c=en, s=s0)
plt.colorbar(sc, label=r'$E(x,y)$', ax=axE)
axE.plot(xyline[:,0], xyline[:,1], '--', color='black', label='$t$ line')
if sub_type != 'Sin': plot_BZ2d(axE, get_brillouin_zone_2d(S), ws_params)
axE.quiver(0, 0, *S[0], angles='xy', scale_units='xy', scale=1, zorder=5, color='red', label='b1')
axE.quiver(0, 0, *S[1], angles='xy', scale_units='xy', scale=1, zorder=5, color='orange', label='b2')
axE.legend()
axE.set_xlim([x0, x1])
axE.set_ylim([y0, y1])
axE.set_ylabel('y')
axE.set_aspect('equal')
#-------------------------

# Force along x
sc = axFx.scatter(p[:,0], p[:,1], c=F[:,0], s=s0, cmap='PiYG')
plt.colorbar(sc, label=r'$F_x(x,y)$', ax=axFx)
axFx.plot(xyline[:,0], xyline[:,1], '--', color='black')
if sub_type != 'Sin': plot_BZ2d(axFx, get_brillouin_zone_2d(S), ws_params)
axFx.set_xlim([x0, x1])
axFx.set_ylim([y0, y1])
axFx.set_aspect('equal')
#-------------------------

# Force along y
sc = axFy.scatter(p[:,0], p[:,1], c=F[:,1], s=s0, cmap='PiYG')
plt.colorbar(sc, label=r'$F_y(x,y)$', ax=axFy)
axFy.plot(xyline[:,0], xyline[:,1], '--', color='black')
if sub_type != 'Sin': plot_BZ2d(axFy, get_brillouin_zone_2d(S), ws_params)
axFy.set_xlim([x0, x1])
axFy.set_ylim([y0, y1])
axFy.set_xlabel('x')
axFy.set_ylabel('y')
axFy.set_aspect('equal')
#-------------------------

# Torque
sc = axtau.scatter(p[:,0], p[:,1], c=tau, s=s0, cmap='RdBu')
plt.colorbar(sc, label=r'$\tau(x,y)$', ax=axtau)
axtau.plot(xyline[:,0], xyline[:,1], '--', color='black')
if sub_type != 'Sin': plot_BZ2d(axtau, get_brillouin_zone_2d(S), ws_params)
axtau.set_xlim([x0, x1])
axtau.set_ylim([y0, y1])
axtau.set_xlabel('x')
axtau.set_aspect('equal')
#-------------------------

plt.tight_layout()
plt.show()
_images/0-Substrate_types_20_0.png

1D lines

The same quantities above along the black dashed line

[12]:
fig, ((axE, axFx), (axFy, axtau)) = plt.subplots(2,2, dpi=200, sharex=True, sharey=False, figsize=(8,6))
fig.suptitle(title)

axE.plot(tline, enl)
axE.set_ylabel(r'$E(x(t), y(t))$')

axFx.plot(tline, Fl[:,0])
axFx.set_ylabel(r'$F_x(x(t), y(t))$')

axFy.plot(tline, Fl[:,1])
axFy.set_ylabel(r'$F_y(x(t), y(t))$')
axFy.set_xlabel('t')

axtau.plot(tline, taul)
axtau.set_ylabel(r'$\tau(x(t), y(t))$')
axtau.set_xlabel('t')

plt.tight_layout()
plt.show()
_images/0-Substrate_types_22_0.png

Init from param

You can define the substrate potential simply by putting the parameters in a dictionary and loading it with the substrate_from_params function. It returns the right per particle and total energy functions, along with the right parameters.

[13]:
from tool_create_substrate import substrate_from_params

# Define symmetry of substrate
#n, c_n, alpha_n = 2, 1, 0 # Lines
n, c_n, alpha_n = 3, 4/3, 0 # Tri
# n, c_n, alpha_n = 4, np.sqrt(2), pi/4 # Square
# n, c_n, alpha_n = 5, 2, 0 # Qausi-cristal
#n, c_n, alpha_n = 6, 4/np.sqrt(3), -pi/6
R = 1
ks = get_ks(R, n, c_n, alpha_n) # Get the reciprocal vectors defined  che coefficients above

params = {
    'sub_basis': [[0,0]],
    'b1': [1,0],
    'b2': [-1/2, sqrt(3)/2],
    #'b2': [0,1],
    'epsilon': 1,
    #------ Gaussian
    'well_shape': 'gaussian',
    'sigma': 0.1, 'a': 0.3, 'b': 0.45,
    #------ LJ
    #'well_shape': 'lj',
    #'sigma': 0.1, 'd0': 0.11224620483093731, 'a': 0.1, 'b': 0.45,
    #------ Tanh
    #'well_shape': 'tanh',
    'wd': 0.25, 'a': 0.1, 'b': 0.45,
    #------ Sin
    #'well_shape': 'sin',
    'ks': ks
}

pen_func, en_func, en_inputs = substrate_from_params(params)
side = 2
x0, x1, nx = -side, side, 150
y0, y1, ny = -side, side, 150
xx, yy = np.meshgrid(np.linspace(x0, x1, nx), np.linspace(y0, y1, ny))
pp = np.stack([xx, yy], axis=2)
p = np.reshape(pp, (pp.shape[0]*pp.shape[1], 2))

en, F, tau = pen_func(p, [0,0], *en_inputs)

fig, axE = plt.subplots(1,1, dpi=100, sharex=True, sharey=True, figsize=(6,4))
fig.suptitle('Well %s' % params['well_shape'])
s0 = 1

# Energy
sc = axE.scatter(p[:,0], p[:,1], c=en, s=s0)
plt.colorbar(sc, label=r'$E(x,y)$', ax=axE)
_, u_inv = calc_matrices_bvect(params['b1'], params['b2'])
S = u_inv.T
if params['well_shape'] != 'sin': plot_BZ2d(axE, get_brillouin_zone_2d(S), ws_params)
axE.quiver(0, 0, *S[0], angles='xy', scale_units='xy', scale=1, zorder=5, color='red', label='b1')
axE.quiver(0, 0, *S[1], angles='xy', scale_units='xy', scale=1, zorder=5, color='orange', label='b2')
axE.legend(loc='upper right')
axE.set_xlim([x0, x1])
axE.set_ylim([y0, y1])
axE.set_ylabel('y')
axE.set_aspect('equal')
#-------------------------
_images/0-Substrate_types_24_0.png