Skip to content

Band structure

The unit of the array example, with lossy spheres of permittivity 4 + 0.1i, repeats along z with a period of 210 nm: the 10 nm slab, a 100 nm gap, the plane of sphere centers and another 100 nm gap. A Bloch mode of this stack repeats from one period to the next up to a factor exp(i kz a), where kz is its Bloch wavenumber and a = 210 nm the period. bands finds these wavenumbers from the S-matrix of one period. The table lists kz a / π at normal incidence for the modes with |Im| < 0.1, that is, for the modes that decay slowly along the stack. The modes come in pairs ±kz, one for each direction. Lengths are in nm; the frequency column is k0 / 2π, the inverse vacuum wavelength in 1/nm.

"""A periodic stack of sphere arrays on slabs: its Bloch modes along z."""

import numpy as np

import treams_rs as tr

# Enlarge for a full band diagram: treams uses 200 frequencies.
FREQUENCIES = 6

k0s = 2 * np.pi * np.linspace(1 / 10_000, 1 / 350, FREQUENCIES)
material_slab = 3
thickness = 10
material_sphere = tr.Material(4 + 0.1j, 1, 0.05)
lattice = tr.Lattice.square(500)
radius = 100
lmax = 3
# Period of the stack along z.
az = 210

# One array per frequency: Bloch wavenumbers kz in units of pi / az, kept
# where |Im| < 0.1, that is, for modes that decay slowly along the stack.
res = []
for k0 in k0s:
    kpar = [0, 0]
    sphere = tr.sphere_tmatrix(
        k0=k0, lmax=lmax, radius=radius, material=material_sphere
    )
    spheres = tr.solve_periodic(sphere, lattice=lattice, kpar=kpar)

    ports = tr.PlaneWavePorts.diffr_orders(kpar, lattice, 0.02)
    slab = tr.slab(k0=k0, basis=ports, thickness=thickness, material=material_slab)
    gap = tr.propagation(k0=k0, basis=ports, distance=radius)
    array = spheres.to_smatrix(ports)
    # One period: slab, gap, sphere centers, and the gap to the next slab.
    cell = tr.stack([slab, gap, array, gap])
    x = cell.bands(period=az).wavenumbers * az / np.pi
    # Eigenvalue solvers return the modes in no fixed order; sort them.
    res.append(np.sort_complex(x[np.abs(np.imag(x)) < 0.1]))

if __name__ == "__main__":
    print("Bloch wavenumbers kz az / pi with |Im| < 0.1")
    print(f"{'k0/(2 pi)':>13}{'Re':>13}{'Im':>13}")
    for k0, values in zip(k0s, res, strict=True):
        for value in values:
            print(f"{k0 / (2 * np.pi):>13.6g}{value.real:>13.6g}{value.imag:>13.6g}")
# Source: https://github.com/tfp-photonics/treams/blob/1f5d0d6ebb007288f28bc9e16f6d266e8b55dc39/docs/examples/band_structure.py
# Adapted: reduced sizes, plotting removed.
# MIT license, copyright Dominik Beutel; see LICENSE.treams.
import numpy as np
import treams

k0s = 2 * np.pi * np.linspace(1 / 10_000, 1 / 350, 6)
material_slab = 3
thickness = 10
material_sphere = treams.Material(4 + 0.1j, 1, 0.05)
lattice = treams.Lattice.square(500)
radius = 100
lmax = 3
az = 210

res = []
for k0 in k0s:
    kpar = [0, 0]
    spheres = treams.TMatrix.sphere(
        lmax, k0, radius, [material_sphere, 1]
    ).latticeinteraction.solve(lattice, kpar)

    pwb = treams.PlaneWaveBasisByComp.diffr_orders(kpar, lattice, 0.02)
    plw = treams.plane_wave(kpar, [1, 0, 0], k0=k0, basis=pwb, material=1)
    slab = treams.SMatrices.slab(thickness, pwb, k0, [1, material_slab, 1])
    dist = treams.SMatrices.propagation([0, 0, radius], pwb, k0, 1)
    array = treams.SMatrices.from_array(spheres, pwb)
    total = treams.SMatrices.stack([slab, dist, array, dist])
    x, _ = total.bands_kz(az)
    x = x * az / np.pi
    sel = x[np.abs(np.imag(x)) < 0.1]
    res.append(sel)

Output

Bloch wavenumbers kz az / pi with |Im| < 0.1
    k0/(2 pi)           Re           Im
       0.0001   -0.0463046 -3.44198e-05
       0.0001   -0.0461419 -3.72028e-05
       0.0001    0.0461419  3.72028e-05
       0.0001    0.0463046  3.44198e-05
  0.000651429    -0.302842 -0.000268014
  0.000651429      -0.3016 -0.000283914
  0.000651429       0.3016  0.000283914
  0.000651429     0.302842  0.000268014
   0.00120286     -0.56635 -0.000811572
   0.00120286    -0.562797 -0.000808936
   0.00120286     0.562797  0.000808936
   0.00120286      0.56635  0.000811572
   0.00175429    -0.855806  -0.00313047
   0.00175429    -0.843723  -0.00273337
   0.00175429    -0.348999   -0.0068234
   0.00175429   -0.0280163   -0.0941962
   0.00175429    0.0280163    0.0941962
   0.00175429     0.348999    0.0068234
   0.00175429     0.843723   0.00273337
   0.00175429     0.855806   0.00313047
   0.00230571     -0.86705  -0.00732377
   0.00230571    -0.838782  -0.00779511
   0.00230571    -0.817621   -0.0128551
   0.00230571    -0.801108    0.0120328
   0.00230571    -0.781632   -0.0110793
   0.00230571    -0.742894    0.0187004
   0.00230571    -0.629712  -0.00815674
   0.00230571    -0.627992  -0.00123334
   0.00230571    -0.612331  -0.00392768
   0.00230571    -0.563723  -0.00133275
   0.00230571     0.563723   0.00133275
   0.00230571     0.612331   0.00392768
   0.00230571     0.627992   0.00123334
   0.00230571     0.629712   0.00815674
   0.00230571     0.742894   -0.0187004
   0.00230571     0.781632    0.0110793
   0.00230571     0.801108   -0.0120328
   0.00230571     0.817621    0.0128551
   0.00230571     0.838782   0.00779511
   0.00230571      0.86705   0.00732377
   0.00285714    -0.998195    0.0528495
   0.00285714    -0.997536    0.0433105
   0.00285714    -0.995829   -0.0524251
   0.00285714     -0.99351   -0.0287446
   0.00285714     -0.86054   0.00796757
   0.00285714    -0.830748   0.00342023
   0.00285714    -0.817856   0.00309955
   0.00285714    -0.658499  -0.00536573
   0.00285714    -0.637574  -0.00510091
   0.00285714    -0.606323  -0.00513062
   0.00285714    -0.587249  -0.00357882
   0.00285714    -0.541919    -0.010382
   0.00285714    -0.500106   -0.0243015
   0.00285714    -0.464966   -0.0095632
   0.00285714    -0.433045    0.0399034
   0.00285714    -0.320497    0.0269757
   0.00285714     0.320497   -0.0269757
   0.00285714     0.433045   -0.0399034
   0.00285714     0.464966    0.0095632
   0.00285714     0.500106    0.0243015
   0.00285714     0.541919     0.010382
   0.00285714     0.587249   0.00357882
   0.00285714     0.606323   0.00513062
   0.00285714     0.637574   0.00510091
   0.00285714     0.658499   0.00536573
   0.00285714     0.817856  -0.00309955
   0.00285714     0.830748  -0.00342023
   0.00285714      0.86054  -0.00796757
   0.00285714      0.99351    0.0287446
   0.00285714     0.995829    0.0524251
   0.00285714     0.997536   -0.0433105
   0.00285714     0.998195   -0.0528495

Differences from the treams version

  • bands(period=az) returns the named fields wavenumbers and eigenvectors; treams' bands_kz(az) returns a pair. treams-rs also has bands_kz.
  • The script sorts the modes of each frequency, so the table has a fixed order. The two codes return the eigenvalues in different orders.
  • The treams version also builds a plane wave that it never uses; the treams-rs script leaves it out.