Floquet Engineering: A Chern Insulator From Circularly Polarized Light#

T. Oka and H. Aoki (2009) predicted that graphene illuminated by circularly polarized light becomes a Hall insulator; Kitagawa, Oka, Brataas, Fu and Demler (2011) showed it is a Floquet realization of Haldane’s model. A time-periodic drive \(H(t+T) = H(t)\) acts, stroboscopically, as the static Floquet Hamiltonian \(H_F\), \(e^{-iH_FT} = \mathcal{T}e^{-i\int_0^TH(t)dt}\). Light enters the hoppings through the Peierls phase of its vector potential, \(t_{ij} \to t_{ij}e^{i\mathbf{A}(t)\cdot\mathbf{d}_{ij}}\) with \(\mathbf{A}(t) = A_0(\cos\omega t, \sin\omega t)\). At high frequency,

\[H_F \approx H_0 + \frac{1}{\omega}\sum_{m\ge1}\frac{[H_{-m}, H_m]}{m}\, ,\]

and the commutator is a complex second-neighbour hopping – Haldane’s – which opens gaps of the same sign at both Dirac points: a Chern insulator made by light, observed in graphene by McIver et al. (2020).

FloquetKSpace makes the driven Bloch model; its bands and Chern numbers are those of \(H_F\).

import numpy as np
import matplotlib.pyplot as plt

import tbkit.lattices as lattices
from tbkit.bridges import finite_system
from tbkit.floquet import FloquetKSpace, harmonics, quasienergies, sambe_hamiltonian
from tbkit.kspace import KSpace, reciprocal_vectors
from tbkit.plot import Plot

graphene = KSpace(lattices.honeycomb())
graphene.set_hopping([{'i': 0, 'j': 1, 'R': R, 't': 1.} for R in [(0, 0), (-1, 0), (0, -1)]])
omega, a0 = 12., 0.6
period = 2 * np.pi / omega


def light(helicity):
    return lambda t: (a0 * np.cos(omega * t), helicity * a0 * np.sin(omega * t))


driven = FloquetKSpace(graphene, light(+1), period, n_steps=60)
b1, b2 = (np.array(v) for v in reciprocal_vectors(graphene.lat.prim_vec))
K = (b1 - b2) / 3

The lattice#

Graphene’s honeycomb lattice on a patch of \(5\times4\) cells (finite_system()): two sites per unit cell (sublattices ‘a’ and ‘b’, in two colours), each bonded to its three nearest neighbours on the other sublattice.

fig_lat = Plot(finite_system(graphene, (5, 4))).lattice(plt_hop=True, ms=12, figsize=(5.5, 4.5))
plot floquet chern insulator

Two routes to the quasienergies#

The time-ordered evolution over a period, and the static Sambe-space problem of the drive’s harmonics, agree.

ham_t = lambda t: graphene.get_ham_peierls(K, light(+1)(t))
eps = quasienergies(ham_t, period, 400)
sambe = np.linalg.eigvalsh(sambe_hamiltonian(harmonics(ham_t, period, 4), omega, 8))
assert np.allclose(eps, np.sort(sambe[np.abs(sambe) < omega / 2]), atol=1e-5)

Light opens the Dirac points: a Chern insulator#

gap_k = np.diff(np.linalg.eigvalsh(driven.get_ham(K)))[0]
gap_kp = np.diff(np.linalg.eigvalsh(driven.get_ham(-K)))[0]
chern = driven.chern_number(0, nk=16)
chern_reversed = FloquetKSpace(graphene, light(-1), period, n_steps=60).chern_number(0, nk=16)
print("Gaps at K and K': {:.4f}, {:.4f}; Chern number {:.3f}, {:.3f} for the opposite helicity."
          .format(gap_k, gap_kp, chern, chern_reversed))
assert gap_k > 0.05 and np.isclose(gap_k, gap_kp)
assert np.isclose(abs(chern), 1., atol=1e-6) and np.isclose(chern_reversed, -chern, atol=1e-6)
# linear light keeps time reversal: no Chern number, Dirac points only shifted
linear = FloquetKSpace(graphene, lambda t: (a0 * np.cos(omega * t), 0.), period, n_steps=60)
assert np.min(np.diff(linear.mesh_bands(30), axis=1)) < 0.2
Gaps at K and K': 0.1211, 0.1211; Chern number -1.000, 1.000 for the opposite helicity.

The gap grows as the intensity, and falls as 1/omega#

amps = np.array([0.2, 0.4, 0.6])
gaps = [np.diff(np.linalg.eigvalsh(FloquetKSpace(graphene, lambda t, a=a: (a*np.cos(omega*t), a*np.sin(omega*t)),
                                                                                  period, 60).get_ham(K)))[0] for a in amps]
ratios = np.array(gaps) / amps**2
print('gap / A0^2:', np.round(ratios, 4))
assert np.allclose(ratios, ratios[0], rtol=0.1)


def gap_at(w, a=0.4):
    drive = FloquetKSpace(graphene, lambda t: (a*np.cos(w*t), a*np.sin(w*t)), 2*np.pi/w, 60)
    return np.diff(np.linalg.eigvalsh(drive.get_ham(K)))[0]


ratio = gap_at(12.) / gap_at(24.)
print('gap(omega = 12) / gap(omega = 24) = {:.3f}'.format(ratio))
assert abs(ratio - 2.) < 0.2

driven.k_path([np.zeros(2), K, b1 / 2, np.zeros(2)], nk=60)
fig = driven.plot_bands(node_labels=[r'$\Gamma$', 'K', 'M', r'$\Gamma$'])
fig.axes[0].set_title('Graphene in circularly polarized light: Floquet bands')
Graphene in circularly polarized light: Floquet bands
gap / A0^2: [0.3702 0.3572 0.3365]
gap(omega = 12) / gap(omega = 24) = 1.996

Text(0.5, 1.0, 'Graphene in circularly polarized light: Floquet bands')

Total running time of the script: (0 minutes 1.809 seconds)

Gallery generated by Sphinx-Gallery