{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pybinding as pb\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"pb.pltutils.use_style()\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Shape and symmetry\n",
"\n",
"The last two sections showed how to model shape and symmetry individually, but we can be more creative and combine the two.\n",
"\n",
"[Download this page as a Jupyter notebook](http://docs.pybinding.site/page/_downloads/90d0e3f3fc0de2b78a318d1843253dc7/shape_symmetry.ipynb)\n",
"\n",
"## Nanoribbons\n",
"\n",
"To create a graphene nanoribbon, we’ll need a shape to give the finite width of the ribbon while the infinite length is achieved by imposing translational symmetry."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pybinding.repository import graphene\n",
"\n",
"model = pb.Model(\n",
" graphene.monolayer(),\n",
" pb.rectangle(1.2), # nm\n",
" pb.translational_symmetry(a1=True, a2=False)\n",
")\n",
"model.plot()\n",
"model.lattice.plot_vectors(position=[-0.6, 0.3]) # nm"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As before, the central darker circles represent the main cell of the nanoribbon, the lighter colored circles are the translations due to symmetry and the red lines are boundary hoppings. The two arrows in the upper left corner show the primitive lattice vectors of graphene.\n",
"\n",
"The [`translational_symmetry()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.translational_symmetry.html#pybinding.translational_symmetry) is applied only in the $a_1$ lattice vector direction which gives the ribbon its infinite length, but the symmetry is disabled in the $a_2$ direction so that the finite size of the shape is preserved. The builtin [`rectangle()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.rectangle.html#pybinding.rectangle) shape gives the nanoribbon its 1.2 nm width.\n",
"\n",
"The band structure calculations work just as before."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from math import pi, sqrt\n",
"\n",
"solver = pb.solver.lapack(model)\n",
"a = graphene.a_cc * sqrt(3) # ribbon unit cell length\n",
"bands = solver.calc_bands(-pi/a, pi/a)\n",
"bands.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is the characteristic band structure for zigzag nanoribbons with zero-energy edge states. If we change the direction of the translational symmetry to $a_2$, the orientation will change, but we will still have a zigzag nanoribbon."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model = pb.Model(\n",
" graphene.monolayer(),\n",
" pb.rectangle(1.2), # nm\n",
" pb.translational_symmetry(a1=False, a2=True)\n",
")\n",
"model.plot()\n",
"model.lattice.plot_vectors(position=[0.6, -0.25]) # nm"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Because of the nature of graphene’s 2-atom unit cell and lattice vector, only zigzag edges can be created. In order to create armchair edges, we must introduce a different unit cell with 4 atoms."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model = pb.Model(graphene.monolayer_4atom())\n",
"model.plot()\n",
"model.lattice.plot_vectors(position=[-0.13, -0.13])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> To learn how to create this 4-atom unit cell, see [Constructing a supercell](http://docs.pybinding.site/page/tutorial/../advanced/lattice.html#construct-a-supercell).\n",
"\n",
"Notice that the lattice vectors $a_1$ and $a_2$ are at a right angle, unlike the sharp angle of the base 2-atom cell. The lattice properties are identical for the 2 and 4 atom cells, but the new geometry helps to create armchair edges."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model = pb.Model(\n",
" graphene.monolayer_4atom(),\n",
" pb.primitive(a1=5),\n",
" pb.translational_symmetry(a1=False, a2=True)\n",
")\n",
"model.plot()\n",
"model.lattice.plot_vectors(position=[-0.59, -0.6])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To calculate the band structure we must enter at least two points in k-space between which the energy will be calculated. Note that because the periodicity is in the direction of the second lattice vector $a_2$, the points in k-space are given as `[0, pi/d]` instead of just `pi/d` (which would be equivalent to `[pi/d, 0]`)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"solver = pb.solver.lapack(model)\n",
"d = 3 * graphene.a_cc # ribbon unit cell length\n",
"bands = solver.calc_bands([0, -pi/d], [0, pi/d])\n",
"bands.plot(point_labels=['$-\\pi / 3 a_{cc}$', '$\\pi / 3 a_{cc}$'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1D periodic supercell\n",
"\n",
"Up to now, we used [`translational_symmetry()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.translational_symmetry.html#pybinding.translational_symmetry) with `True` or `False` parameters to enable or disable periodicity in certain directions. We can also pass a number to indicate the desired period length."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model = pb.Model(\n",
" graphene.monolayer_4atom(),\n",
" pb.rectangle(x=2, y=2),\n",
" pb.translational_symmetry(a1=1.2, a2=False)\n",
")\n",
"model.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The period length is given in nanometers. Note that our base shape is a square with 2 nm sides. The base shape forms the supercell of the periodic structure, but because the period length (1.2 nm) is smaller than the shape (2 nm), the extra length is cut off by the periodic boundary.\n",
"\n",
"If you specify a periodic length which is larger than the base shape, the periodic conditions will not be applied because the periodic boundary will not have anything to bind to."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model = pb.Model(\n",
" graphene.monolayer_4atom(),\n",
" pb.rectangle(x=1.5, y=1.5), # don't combine a small shape\n",
" pb.translational_symmetry(a1=1.7, a2=False) # with large period length\n",
")\n",
"model.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As you can see, making the period larger than the shape (1.7 nm vs. 1.5 nm), results in just the finite-sized part of the system. Don’t do this.\n",
"\n",
"The combination of shape and symmetry can be more complex as shown here with a nanoribbon ring structure."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def ring(inner_radius, outer_radius):\n",
" \"\"\"Ring shape defined by an inner and outer radius\"\"\"\n",
" def contains(x, y, z):\n",
" r = np.sqrt(x**2 + y**2)\n",
" return np.logical_and(inner_radius < r, r < outer_radius)\n",
" return pb.FreeformShape(contains, width=[2*outer_radius, 2*outer_radius])\n",
"\n",
"model = pb.Model(\n",
" graphene.monolayer_4atom(),\n",
" ring(inner_radius=1.4, outer_radius=2),\n",
" pb.translational_symmetry(a1=3.8, a2=False)\n",
")\n",
"plt.figure(figsize=[8, 3])\n",
"model.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The period length of the translation in the $a_1$ direction is set to 3.8 nm. This ensures that the inner ring shape is preserved and the periodic boundaries are placed on the outer edges."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"solver = pb.solver.arpack(model, k=10) # only the 10 lowest states\n",
"a = 3.8 # [nm] unit cell length\n",
"bands = solver.calc_bands(-pi/a, pi/a)\n",
"bands.plot(point_labels=['$-\\pi / a$', '$\\pi / a$'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2D periodic supercell\n",
"\n",
"A 2D periodic system made up of just a primitive cell was already covered in the [Band structure](http://docs.pybinding.site/page/tutorial/bands.html) section. Here, we’ll create a system with a periodic unit cell which is larger than the primitive cell. Similar to the 1D case, this is accomplished by giving [`translational_symmetry()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.translational_symmetry.html#pybinding.translational_symmetry) specific lengths for the translation directions. As an example, we’ll take a look at a graphene antidot superlattice:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"width = 2.5\n",
"rectangle = pb.rectangle(x=width * 1.2, y=width * 1.2)\n",
"dot = pb.circle(radius=0.4)\n",
"\n",
"model = pb.Model(\n",
" graphene.monolayer_4atom(),\n",
" rectangle - dot,\n",
" pb.translational_symmetry(a1=width, a2=width)\n",
")\n",
"plt.figure(figsize=(5, 5))\n",
"model.plot()\n",
"model.lattice.plot_vectors(position=[2, -3.5], scale=3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The antidot unit cell is created using a [composite shape](http://docs.pybinding.site/page/tutorial/../advanced/shapes.html). Note that the width of the rectangle is made to be slightly larger than the period length. Just like the 1D case, this is necessary in order to give [`translational_symmetry()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.translational_symmetry.html#pybinding.translational_symmetry) some room to cut off the edges of the system and create periodic boundaries as needed. If the unit cell size is smaller then the period length, translational symmetry cannot be applied.\n",
"\n",
"In the figure above, notice that 6 translations of the unit cell are presented and it appears as if 2 are missing. This is only in appearance. By default, [`Model.plot()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Model.html#pybinding.Model.plot) shows just the first-nearest translations of the unit cell. It just so happens that the 2 which appear missing are second-nearest translations. To see this in the figure, we can set the `num_periods` argument to a higher value:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plt.figure(figsize=(5, 5))\n",
"model.plot(num_periods=2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example\n",
"\n",
"Note the zero-energy mode in the band structure. For wave vector $k = 0$, states on the outer edge of the ring have the highest LDOS intensity, but for $k = \\pi / a$ the inner edge states dominate.\n",
"\n",
"[Download source code](http://docs.pybinding.site/page/_downloads/2b4ffe7c63511eb385ba914ef141b162/shape_symmetry_example.py)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\"\"\"Model an infinite nanoribbon consisting of graphene rings\"\"\"\n",
"import pybinding as pb\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from pybinding.repository import graphene\n",
"from math import pi\n",
"\n",
"pb.pltutils.use_style()\n",
"\n",
"\n",
"def ring(inner_radius, outer_radius):\n",
" \"\"\"A simple ring shape\"\"\"\n",
" def contains(x, y, z):\n",
" r = np.sqrt(x**2 + y**2)\n",
" return np.logical_and(inner_radius < r, r < outer_radius)\n",
"\n",
" return pb.FreeformShape(contains, width=[2 * outer_radius, 2 * outer_radius])\n",
"\n",
"\n",
"model = pb.Model(\n",
" graphene.monolayer_4atom(),\n",
" ring(inner_radius=1.4, outer_radius=2), # length in nanometers\n",
" pb.translational_symmetry(a1=3.8, a2=False) # period in nanometers\n",
")\n",
"\n",
"plt.figure(figsize=pb.pltutils.cm2inch(20, 7))\n",
"model.plot()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# only solve for the 10 lowest energy eigenvalues\n",
"solver = pb.solver.arpack(model, k=10)\n",
"a = 3.8 # [nm] unit cell length\n",
"bands = solver.calc_bands(-pi/a, pi/a)\n",
"bands.plot(point_labels=[r'$-\\pi / a$', r'$\\pi / a$'])\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"solver.set_wave_vector(k=0)\n",
"ldos = solver.calc_spatial_ldos(energy=0, broadening=0.01) # LDOS around 0 eV\n",
"\n",
"plt.figure(figsize=pb.pltutils.cm2inch(20, 7))\n",
"ldos.plot(site_radius=(0.03, 0.12))\n",
"pb.pltutils.colorbar(label=\"LDOS\")\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"solver.set_wave_vector(k=pi/a)\n",
"ldos = solver.calc_spatial_ldos(energy=0, broadening=0.01) # LDOS around 0 eV\n",
"\n",
"plt.figure(figsize=pb.pltutils.cm2inch(20, 7))\n",
"ldos.plot(site_radius=(0.03, 0.12))\n",
"pb.pltutils.colorbar(label=\"LDOS\")\n",
"plt.show()"
]
}
],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 4
}