{
"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": [
"# Lattice\n",
"\n",
"A [`Lattice`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice) object describes the unit cell of a crystal lattice. This includes the primitive vectors, positions of sublattice sites and hopping parameters which connect those sites. All of this structural information is used to build up a larger system by translation.\n",
"\n",
"[Download this page as a Jupyter notebook](http://docs.pybinding.site/page/_downloads/lattice1.ipynb)\n",
"\n",
"## Square lattice\n",
"\n",
"Starting from the basics, we'll create a simple square lattice."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pybinding as pb\n",
"\n",
"d = 0.2 # [nm] unit cell length\n",
"t = 1 # [eV] hopping energy\n",
"\n",
"# create a simple 2D lattice with vectors a1 and a2\n",
"lattice = pb.Lattice(a1=[d, 0], a2=[0, d])\n",
"lattice.add_sublattices(\n",
" ('A', [0, 0]) # add an atom called 'A' at position [0, 0]\n",
")\n",
"lattice.add_hoppings(\n",
" # (relative_index, from_sublattice, to_sublattice, energy)\n",
" ([0, 1], 'A', 'A', t),\n",
" ([1, 0], 'A', 'A', t)\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It may not be immediately obvious what this code does. Fortunately, [`Lattice`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice) objects have a convenient [`Lattice.plot()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice.plot) method to easily visualize the constructed lattice."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"lattice.plot() # plot the lattice that was just constructed\n",
"plt.show() # standard matplotlib show() function"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the figure we see lattice vectors $a_1$ and $a_2$ which were used to initialize [`Lattice`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice). These vectors describe a Bravais lattice with an infinite set of positions,\n",
"\n",
"$$\n",
"\\vec{R} = n_1 \\vec{a}_1 + n_2 \\vec{a}_2,\n",
"$$\n",
"\n",
"where $n_1$ and $n_2$ are integers. The blue circle labeled A represents the atom which was created with the [`Lattice.add_sublattices()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice.add_sublattices) method. The slightly faded out circles represent translations of the lattice in the primitive vector directions, i.e. using the integer index $[n_1, n_2]$.\n",
"\n",
"The hoppings are specified using the [`Lattice.add_hoppings()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice.add_hoppings) method and each one consists of `(relative_index, from_sublattice, to_sublattice, energy)`:\n",
"\n",
"* The main cell always has the index $[n_1, n_2]$ = [0, 0]. The `relative_index` represents the number of integer steps needed to reach another cell starting from the main one. Each cell is labeled with its `relative_index`, as seen in the figure.\n",
" \n",
"* A hopping is created between the main cell and a neighboring cell specified by `relative_index`. Two hoppings are added in the definition: [0, 1] and [1, 0]. The opposite hoppings [0, -1] and [-1, 0] are added automatically to maintain hermiticity.\n",
" \n",
"* This lattice consists of only one sublattice so the `from` and `to` sublattice fields are trivial. Generally, `from_sublattice` indicates the sublattice in the [0, 0] cell and `to_sublattice` in the neighboring cell. This will be explained further in the next example.\n",
" \n",
"* The last parameter is simply the value of the hopping energy.\n",
" \n",
"It's good practice to build the lattice inside a function to make it easily reusable. Here we define the same lattice as before, but note that the unit cell length and hopping energy are function arguments, which makes the lattice easily configurable."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def square_lattice(d, t):\n",
" lat = pb.Lattice(a1=[d, 0], a2=[0, d])\n",
" lat.add_sublattices(('A', [0, 0]))\n",
" lat.add_hoppings(([0, 1], 'A', 'A', t),\n",
" ([1, 0], 'A', 'A', t))\n",
" return lat\n",
"\n",
"# we can quickly set a shorter unit length `d`\n",
"lattice = square_lattice(d=0.1, t=1)\n",
"lattice.plot()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Graphene\n",
"\n",
"The next example shows a slightly more complicated two-atom lattice of graphene."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from math import sqrt\n",
"\n",
"def monolayer_graphene():\n",
" a = 0.24595 # [nm] unit cell length\n",
" a_cc = 0.142 # [nm] carbon-carbon distance\n",
" t = -2.8 # [eV] nearest neighbour hopping\n",
"\n",
" lat = pb.Lattice(a1=[a, 0],\n",
" a2=[a/2, a/2 * sqrt(3)])\n",
" lat.add_sublattices(('A', [0, -a_cc/2]),\n",
" ('B', [0, a_cc/2]))\n",
" lat.add_hoppings(\n",
" # inside the main cell\n",
" ([0, 0], 'A', 'B', t),\n",
" # between neighboring cells\n",
" ([1, -1], 'A', 'B', t),\n",
" ([0, -1], 'A', 'B', t)\n",
" )\n",
" return lat\n",
"\n",
"lattice = monolayer_graphene()\n",
"lattice.plot()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The [`Lattice.add_sublattices()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice.add_sublattices) method creates atoms A and B (blue and orange) at different offsets: $[0, -a_{cc}/2]$ and $[0, a_{cc}/2]$. Once again, the translated cells are given at positions $\\vec{R} = n_1 \\vec{a}_1 + n_2 \\vec{a}_2$, however, this time the lattice vectors are not perpendicular which makes the integer indices $[n_1, n_2]$ slightly more complicate (see the labels in the figure).\n",
"\n",
"The hoppings are defined as follows:\n",
"\n",
"* `([0, 0], 'A', 'B', t)` specifies the hopping inside the main cell, from atom A to B. The main [0,0] cell is never labeled in the figure, but it is always the central cell where the lattice vectors originate.\n",
" \n",
"* `([1, -1], 'A', 'B', t)` specifies the hopping between [0, 0] and [1, -1], from A to B. The opposite hopping is added automatically: [-1, 1], from B to A. In the tight-binding matrix representation, the opposite hopping is the Hermitian conjugate of the first one. The lattice specification always requires explicitly mentioning only one half of the hoppings while the other half is automatically added to guarantee hermiticity.\n",
" \n",
"* `([0, -1], 'A', 'B', t)` is handled in the very same way.\n",
" \n",
"The [`Lattice.plot()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice.plot) method will always faithfully draw any lattice that has been specified. It serves as a handy visual inspection tool.\n",
"\n",
"## Brillouin zone\n",
"\n",
"The method [`Lattice.plot_brillouin_zone()`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice.plot_brillouin_zone) is another handy tool that does just as its name implies."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"lattice = monolayer_graphene()\n",
"lattice.plot_brillouin_zone()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The reciprocal lattice vectors $b_1$ and $b_2$ are calculated automatically based on the real space vectors. There is no need to specify them manually. The first Brillouin zone is determined as the Wignerâ€“Seitz cell in reciprocal space. By default, the plot method labels the vertices of the Brillouin zone.\n",
"\n",
"## Material repository\n",
"\n",
"A few common lattices are included in pybinding's [Material Repository](http://docs.pybinding.site/page/tutorial/../materials/index.html). You can get started quickly by importing one of them. For example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pybinding.repository import graphene\n",
"lattice = graphene.bilayer()\n",
"lattice.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Further reading\n",
"\n",
"Additional features of the [`Lattice`](http://docs.pybinding.site/page/tutorial/../_api/pybinding.Lattice.html#pybinding.Lattice) class are explained in the [Advanced Topics](http://docs.pybinding.site/page/tutorial/../advanced/lattice.html) section. For more lattice specifications check out the [examples section](http://docs.pybinding.site/page/tutorial/../examples/lattice/index.html).\n",
"\n",
"## Example\n",
"\n",
"This is a full example file which you can download and run with `python3 lattice_example.py`.\n",
"\n",
"[Download source code](http://docs.pybinding.site/page/_downloads/lattice_example.py)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\"\"\"Create and plot a monolayer graphene lattice and it's Brillouin zone\"\"\"\n",
"import pybinding as pb\n",
"import matplotlib.pyplot as plt\n",
"from math import sqrt\n",
"\n",
"pb.pltutils.use_style()\n",
"\n",
"\n",
"def monolayer_graphene():\n",
" \"\"\"Return the lattice specification for monolayer graphene\"\"\"\n",
" a = 0.24595 # [nm] unit cell length\n",
" a_cc = 0.142 # [nm] carbon-carbon distance\n",
" t = -2.8 # [eV] nearest neighbour hopping\n",
"\n",
" # create a lattice with 2 primitive vectors\n",
" lat = pb.Lattice(\n",
" a1=[a, 0],\n",
" a2=[a/2, a/2 * sqrt(3)]\n",
" )\n",
"\n",
" lat.add_sublattices(\n",
" # name and position\n",
" ('A', [0, -a_cc/2]),\n",
" ('B', [0, a_cc/2])\n",
" )\n",
"\n",
" lat.add_hoppings(\n",
" # inside the main cell\n",
" ([0, 0], 'A', 'B', t),\n",
" # between neighboring cells\n",
" ([1, -1], 'A', 'B', t),\n",
" ([0, -1], 'A', 'B', t)\n",
" )\n",
"\n",
" return lat\n",
"\n",
"\n",
"lattice = monolayer_graphene()\n",
"lattice.plot()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"lattice.plot_brillouin_zone()\n",
"plt.show()"
]
}
],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 2
}