<commit_before>Shape and symmetry
------------------

The last two sections showed how to model shape and symmetry individually, but we can be more
creative and combine the two.


Example
*******

Note the zero-energy mode in the band structure. For wave vector :math:`k = 0`, states on the
outer edge of the ring have the highest LDOS intensity, but for :math:`k = \pi / a` the inner
edge states dominate.


:download:`Source code </tutorial/shape_symmetry_example.py>`

.. plot:: tutorial/shape_symmetry_example.py
    :include-source:


Nanoribbons
***********

To create a graphene nanoribbon, we'll need a shape to give the finite width of the ribbon
while the infinite length is achieved with translational symmetry.

.. plot::
    :context: reset

    from pybinding.repository import graphene

    model = pb.Model(
        graphene.lattice.monolayer(),
        pb.rectangle(1.2),  # nm
        pb.translational_symmetry(a1=True, a2=False)
    )
    model.system.plot()
    model.lattice.plot_vectors(position=[-0.6, 0.3])  # nm

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.

The :func:`.translational_symmetry` is applied only in the :math:`a_1` lattice vector direction
which gives the ribbon its infinite length, but the symmetry is disabled in the :math:`a_2`
direction so that the finite size of the shape is preserved. The builtin :func:`.rectangle` shape
gives the nanoribbon its 1.2 nm width.

The band structure calculations work just as before.

.. plot::
    :context: close-figs

    from math import pi, sqrt

    solver = pb.solver.lapack(model)
    a = graphene.a_cc * sqrt(3)  # ribbon unit cell length
    bands = solver.calc_bands(-pi/a, pi/a)
    bands.plot()


.. todo::
    Translations symmetry with `float` arguments

.. todo::
    4 atom unit cell


Complex structures
******************

The combination of shape and symmetry can be more complex as shown here with a nanoribbon ring
structure.

.. plot::
    :context: close-figs

    def ring(inner_radius, outer_radius):
        def contains(x, y, z):
            r = np.sqrt(x**2 + y**2)
            return np.logical_and(inner_radius < r, r < outer_radius)

        return pb.FreeformShape(contains, width=[2 * outer_radius, 2 * outer_radius])


    model = pb.Model(
        graphene.lattice.monolayer_4atom(),
        ring(inner_radius=1.4, outer_radius=2),
        pb.translational_symmetry(a1=3.8, a2=False)
    )

    plt.figure(figsize=pb.pltutils.cm2inch(20, 7))
    model.system.plot()

The period length of the translation in the :math:`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.


.. plot::
    :context: close-figs

    solver = pb.solver.arpack(model, num_eigenvalues=10)
    a = 3.8  # [nm] unit cell length
    bands = solver.calc_bands(-pi/a, pi/a)
    bands.plot(point_labels=['$-\pi / a$', '$\pi / a$'])


.. todo::
    2D periodic structure


Further reading
***************

.. todo::
    For more symmetry tricks check out ...
<commit_msg>Expand tutorial section on shape and symmetry<commit_after>Shape and symmetry
------------------

The last two sections showed how to model shape and symmetry individually, but we can be more
creative and combine the two.


Example
*******

Note the zero-energy mode in the band structure. For wave vector :math:`k = 0`, states on the
outer edge of the ring have the highest LDOS intensity, but for :math:`k = \pi / a` the inner
edge states dominate.


:download:`Source code </tutorial/shape_symmetry_example.py>`

.. plot:: tutorial/shape_symmetry_example.py
    :include-source:


Nanoribbons
***********

To create a graphene nanoribbon, we'll need a shape to give the finite width of the ribbon
while the infinite length is achieved with translational symmetry.

.. plot::
    :context: reset

    from pybinding.repository import graphene

    model = pb.Model(
        graphene.lattice.monolayer(),
        pb.rectangle(1.2),  # nm
        pb.translational_symmetry(a1=True, a2=False)
    )
    model.system.plot()
    model.lattice.plot_vectors(position=[-0.6, 0.3])  # nm

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.

The :func:`.translational_symmetry` is applied only in the :math:`a_1` lattice vector direction
which gives the ribbon its infinite length, but the symmetry is disabled in the :math:`a_2`
direction so that the finite size of the shape is preserved. The builtin :func:`.rectangle` shape
gives the nanoribbon its 1.2 nm width.

The band structure calculations work just as before.

.. plot::
    :context: close-figs

    from math import pi, sqrt

    solver = pb.solver.lapack(model)
    a = graphene.a_cc * sqrt(3)  # ribbon unit cell length
    bands = solver.calc_bands(-pi/a, pi/a)
    bands.plot()


This is the characteristic band structure for zigzag nanoribbons with zero-energy edge states.
If we change the direction of the translational symmetry to :math:`a_2`, the orientation will
change, but we will still have a zigzag nanoribbon.

.. plot::
    :context: close-figs

    model = pb.Model(
        graphene.lattice.monolayer(),
        pb.rectangle(1.2),  # nm
        pb.translational_symmetry(a1=False, a2=True)
    )
    model.system.plot()
    model.lattice.plot_vectors(position=[0.6, -0.25])  # nm

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.

.. plot::
    :context: close-figs

    model = pb.Model(graphene.lattice.monolayer_4atom())
    model.system.plot()
    model.lattice.plot_vectors(position=[-0.13, -0.13])

Note that the lattice vectors :math:`a_1` and :math:`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 create armchair edges.

.. plot::
    :context: close-figs

    model = pb.Model(
        graphene.lattice.monolayer_4atom(),
        pb.primitive(a1=5),
        pb.translational_symmetry(a1=False, a2=True)
    )
    model.system.plot()
    model.lattice.plot_vectors(position=[-0.59, -0.6])

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 :math:`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]`).

.. plot::
    :context: close-figs

    solver = pb.solver.lapack(model)
    d = 3 * graphene.a_cc  # ribbon unit cell length
    bands = solver.calc_bands([0, -pi/d], [0, pi/d])
    bands.plot(point_labels=['$-\pi / 3 a_{cc}$', '$\pi / 3 a_{cc}$'])


Complex structures
******************

Up to now, we used :func:`.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.

.. plot::
    :context: close-figs

    model = pb.Model(
        graphene.lattice.monolayer_4atom(),
        pb.rectangle(x=2, y=2),
        pb.translational_symmetry(a1=1.2, a2=False)
    )
    model.system.plot()

The period length is given in nanometers. Note that our base shape is 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.

If you specify a periodic length which is bigger than the base shape, the periodic conditions
not be applied because the periodic boundary will not have anything to bind to.

.. plot::
    :context: close-figs

    model = pb.Model(
        graphene.lattice.monolayer_4atom(),
        pb.rectangle(x=1.5, y=1.5),  # don't combine a small shape
        pb.translational_symmetry(a1=1.7, a2=False)  # with large period length
    )
    model.system.plot()

As you can see, making the period bigger than the shape (1.7 nm vs. 1.5 nm), results in just the
finite sized part of the system. Don't do this.

The combination of shape and symmetry can be more complex as shown here with a nanoribbon ring
structure.

.. plot::
    :context: close-figs

    def ring(inner_radius, outer_radius):
        def contains(x, y, z):
            r = np.sqrt(x**2 + y**2)
            return np.logical_and(inner_radius < r, r < outer_radius)

        return pb.FreeformShape(contains, width=[2 * outer_radius, 2 * outer_radius])


    model = pb.Model(
        graphene.lattice.monolayer_4atom(),
        ring(inner_radius=1.4, outer_radius=2),
        pb.translational_symmetry(a1=3.8, a2=False)
    )

    plt.figure(figsize=pb.pltutils.cm2inch(20, 7))
    model.system.plot()

The period length of the translation in the :math:`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.


.. plot::
    :context: close-figs

    solver = pb.solver.arpack(model, num_eigenvalues=10)
    a = 3.8  # [nm] unit cell length
    bands = solver.calc_bands(-pi/a, pi/a)
    bands.plot(point_labels=['$-\pi / a$', '$\pi / a$'])


.. todo::
    2D periodic structure


Further reading
***************

.. todo::
    For more symmetry tricks check out ...

{'fraction_non_alphanumeric': 0.10780707132638463, 'fraction_numerical': 0.014612712037604742, 'mean_word_length': 3.2887817703768625, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6416306', 'n_tokens_mistral': 3133, 'n_tokens_neox': 2974, 'n_words': 1125}