Silicon nanopillars on glass at 1550 nm. Sweep pillar radius to build a transmission phase library, then lay out a hyperbolic metalens from it
Setup¶
import os
import numpy as np
import matplotlib.pyplot as plt
import gdsfactory as gf
from gdsfactory.technology import LogicalLayer, LayerLevel, LayerStack
import luminescent as lumi
# simulation folder
root = "runs"
# dims [um]
# length units are arbitrary so long as they're consistent
P = 0.7 # periodicity, subwavelength in substrate (P < wavelength / n_sub) so no diffraction
h = 1.0 # pillar height
radii = np.linspace(0.08, 0.3, 6) # pillar radii to sweep
# refractive indices
n_si = 3.48
n_sub = 1.45
# wavelengths [um]
# spectrum computed from single FDTD run. more wavelengths slightly increases runtime
wavelength = 1.55
bandwidth = 0.1
nwls = 11
wavelengths = np.linspace(wavelength - bandwidth / 2, wavelength + bandwidth / 2, nwls)
# quarter circle polygon
def quarter_circle(r, n=32):
t = np.linspace(0, np.pi / 2, n)
return [(0, 0)] + list(zip(r * np.cos(t), r * np.sin(t)))
def setup(path, wavelength, wavelengths, P, h, r, plot=False):
# simulation parameters
nres = 8
stop_decay = 1e-3
PML_decay = 0.01
subpixel_smoothing = 0
courant=.6
shot_every = 10 # movie [periods]
views = [lumi.view("Ex", y=0, mirrors=["x-"])]
# simulate 1/4 unit cell. x- y- are symmetry planes, x+ y+ are periodic cell edges (also symmetry planes)
# Ex polarized normal incidence: PEC on x walls, PMC on y walls
boundaries = ["PEC", "PMC", "PML"]
# margins
# gap keeps evanescent near field away from monitors
gap = 1.0
source_offset = 0.5
# vertical dims. illuminated from substrate side, transmits into air
z_o1 = -gap
z_o2 = h + gap
zmin = z_o1 - source_offset
zmax = z_o2
# materials
materials = {
"air": lumi.material(1),
"SiO2": lumi.material(n_sub),
"Si": lumi.material(n_si),
}
background = "air"
# layers
PILLAR = 1, 0
SUB = 2, 0
layer_stack = LayerStack(
layers={
"pillar": LayerLevel(
layer=LogicalLayer(layer=PILLAR),
thickness=h,
zmin=0,
material="Si",
mesh_order=1,
),
"sub": LayerLevel(
layer=LogicalLayer(layer=SUB),
thickness=-zmin,
zmin=zmin,
material="SiO2",
mesh_order=2,
),
}
)
# make component in gdsfactory
# 1/4 of pillar unit cell
c = gf.Component()
c.add_polygon(quarter_circle(r), layer=PILLAR)
c.add_polygon([(0, 0), (0, P / 2), (P / 2, P / 2), (P / 2, 0)], layer=SUB)
if plot:
c.plot()
bbox = c.bbox_np().tolist()
bbox[0].append(zmin)
bbox[1].append(zmax)
# sources, monitors
# normally incident plane wave over 1/4 cell: Ex, Hy uniform. Hy = n Ex in medium of index n
dx = wavelength / nres / n_si
nx = ny = round(P / 2 / dx)
a = np.ones((nx, ny))
frames = [
lumi.plane_frame(
"sub_wave",
start=[0, 0],
stop=[P / 2, P / 2],
boundaries=["PEC", "PMC"],
modes=[[{"Ex": a, "Hy": n_sub * a}]],
),
lumi.plane_frame(
"air_wave",
start=[0, 0],
stop=[P / 2, P / 2],
boundaries=["PEC", "PMC"],
modes=[[{"Ex": a, "Hy": a}]],
),
]
# basis rows: local x, local y, outward normal
basis_top = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
basis_bottom = [[-1, 0, 0], [0, 1, 0], [0, 0, -1]]
sources = [
lumi.source(
"o1", [0, 0, z_o1 - source_offset], basis_bottom,
frame="sub_wave",
wl=wavelength,
bandwidth=max(max(wavelengths) - min(wavelengths), wavelength / 4),
)
]
monitors = [
lumi.monitor("o1", [0, 0, z_o1], basis_bottom, frame="sub_wave"),
lumi.monitor("o2", [0, 0, z_o2], basis_top, frame="air_wave"),
]
# writes .obj mesh files from gdsfactory component
lumi.mesh(path, c, layer_stack, materials, zmin, zmax)
# writes setup file
lumi.setup(
path=path,
wl=wavelength,
wls=wavelengths,
materials=materials,
background=background,
boundaries=boundaries,
bbox=bbox,
#
frames=frames,
sources=sources,
monitors=monitors,
#
nres=nres,
PML_decay=PML_decay,
subpixel_smoothing=subpixel_smoothing,
courant=courant,
#
views=views,
shot_every=shot_every,
stop_decay=stop_decay,
device="gpu",
)
paths = [os.path.join(root, f"r{r:.3f}") for r in radii]
for i, (path, r) in enumerate(zip(paths, radii)):
setup(path, wavelength, wavelengths, P, h, r, plot=i == len(radii) - 1)using simulation folder runs/r0.080
using simulation folder runs/r0.100
using simulation folder runs/r0.120
using simulation folder runs/r0.140
using simulation folder runs/r0.160
using simulation folder runs/r0.180
using simulation folder runs/r0.200
using simulation folder runs/r0.220
using simulation folder runs/r0.240
using simulation folder runs/r0.260
using simulation folder runs/r0.280
using simulation folder runs/r0.300

Solve¶
One simulation per radius, run one after another
for path in paths:
lumi.solve(path)========================================
backend using simulation folder runs/r0.080
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.234156 seconds (3.73 M allocations: 207.338 MiB, 1.35% gc time, 170.30% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.080
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (6, 7, 39)
padded size: (6, 7, 55)
cell count: 2,310
step size: 106.0 steps/period
source duration: 3.93 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.049, 1.13 seconds/period
period 20, decay: 0.024, 0.026 seconds/period
period 30, decay: 0.012, 0.026 seconds/period
period 40, decay: 0.007, 0.027 seconds/period
period 50, decay: 0.005, 0.026 seconds/period
period 60, decay: 0.004, 0.025 seconds/period
period 70, decay: 0.002, 0.026 seconds/period
period 80, decay: 0.001, 0.026 seconds/period
period 80.91 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.100
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.204151 seconds (3.73 M allocations: 206.870 MiB, 166.41% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.100
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (5, 6, 39)
padded size: (5, 6, 55)
cell count: 1,650
step size: 88.0 steps/period
source duration: 3.93 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.049, 1.1 seconds/period
period 20, decay: 0.026, 0.017 seconds/period
period 30, decay: 0.018, 0.016 seconds/period
period 40, decay: 0.01, 0.016 seconds/period
period 50, decay: 0.005, 0.016 seconds/period
period 60, decay: 0.003, 0.016 seconds/period
period 70, decay: 0.003, 0.016 seconds/period
period 80, decay: 0.001, 0.016 seconds/period
period 84.36 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.120
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.193320 seconds (3.91 M allocations: 212.004 MiB, 0.81% gc time, 181.59% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.120
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (7, 7, 39)
padded size: (7, 7, 55)
cell count: 2,695
step size: 103.0 steps/period
source duration: 3.94 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.031, 1.12 seconds/period
period 20, decay: 0.017, 0.03 seconds/period
period 30, decay: 0.012, 0.028 seconds/period
period 40, decay: 0.008, 0.029 seconds/period
period 50, decay: 0.003, 0.028 seconds/period
period 60, decay: 0.003, 0.029 seconds/period
period 70, decay: 0.002, 0.028 seconds/period
period 80, decay: 0.001, 0.029 seconds/period
period 84.5 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.140
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.235662 seconds (3.75 M allocations: 208.371 MiB, 1.32% gc time, 169.81% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.140
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (6, 7, 39)
padded size: (6, 7, 55)
cell count: 2,310
step size: 95.0 steps/period
source duration: 3.94 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.112, 1.13 seconds/period
period 20, decay: 0.042, 0.024 seconds/period
period 30.0, decay: 0.016, 0.023 seconds/period
period 40, decay: 0.009, 0.023 seconds/period
period 50.0, decay: 0.005, 0.023 seconds/period
period 60.0, decay: 0.002, 0.023 seconds/period
period 70, decay: 0.002, 0.022 seconds/period
period 80, decay: 0.001, 0.023 seconds/period
period 82.19 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.160
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.244506 seconds (3.81 M allocations: 208.934 MiB, 1.38% gc time, 169.51% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.160
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (5, 6, 39)
padded size: (5, 6, 55)
cell count: 1,650
step size: 83.0 steps/period
source duration: 3.94 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.219, 1.11 seconds/period
period 20, decay: 0.064, 0.016 seconds/period
period 30.0, decay: 0.026, 0.015 seconds/period
period 40, decay: 0.013, 0.016 seconds/period
period 50, decay: 0.006, 0.015 seconds/period
period 60.0, decay: 0.004, 0.015 seconds/period
period 70, decay: 0.002, 0.016 seconds/period
period 80, decay: 0.001, 0.015 seconds/period
period 87.9 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.180
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.214721 seconds (4.00 M allocations: 214.467 MiB, 0.74% gc time, 171.73% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.180
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (7, 7, 39)
padded size: (7, 7, 55)
cell count: 2,695
step size: 94.0 steps/period
source duration: 3.94 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.172, 1.11 seconds/period
period 20, decay: 0.07, 0.028 seconds/period
period 30, decay: 0.031, 0.026 seconds/period
period 40, decay: 0.015, 0.027 seconds/period
period 50, decay: 0.008, 0.026 seconds/period
period 60, decay: 0.004, 0.026 seconds/period
period 70, decay: 0.002, 0.026 seconds/period
period 78.98 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.200
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.266750 seconds (3.96 M allocations: 213.232 MiB, 1.24% gc time, 175.19% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.200
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (6, 7, 39)
padded size: (6, 7, 55)
cell count: 2,310
step size: 89.0 steps/period
source duration: 3.93 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.293, 1.11 seconds/period
period 20, decay: 0.087, 0.022 seconds/period
period 30, decay: 0.034, 0.021 seconds/period
period 40, decay: 0.014, 0.022 seconds/period
period 50, decay: 0.006, 0.022 seconds/period
period 60, decay: 0.003, 0.022 seconds/period
period 70, decay: 0.002, 0.022 seconds/period
period 79.1 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.220
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.232769 seconds (3.93 M allocations: 212.366 MiB, 1.28% gc time, 176.27% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.220
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (6, 6, 39)
padded size: (6, 6, 55)
cell count: 1,980
step size: 82.0 steps/period
source duration: 3.94 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.305, 1.1 seconds/period
period 20, decay: 0.097, 0.018 seconds/period
period 30.0, decay: 0.029, 0.017 seconds/period
period 40, decay: 0.012, 0.018 seconds/period
period 50, decay: 0.004, 0.017 seconds/period
period 60.0, decay: 0.002, 0.017 seconds/period
period 68.68 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.240
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.245139 seconds (4.14 M allocations: 218.571 MiB, 1.28% gc time, 168.42% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.240
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (7, 8, 39)
padded size: (7, 8, 55)
cell count: 3,080
step size: 100.0 steps/period
source duration: 3.94 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.367, 1.12 seconds/period
period 20, decay: 0.106, 0.032 seconds/period
period 30, decay: 0.033, 0.032 seconds/period
period 40, decay: 0.013, 0.031 seconds/period
period 50, decay: 0.009, 0.031 seconds/period
period 60, decay: 0.007, 0.035 seconds/period
period 70, decay: 0.006, 0.031 seconds/period
period 80, decay: 0.005, 0.031 seconds/period
period 90, decay: 0.005, 0.032 seconds/period
period 100, decay: 0.004, 0.031 seconds/period
period 110, decay: 0.005, 0.031 seconds/period
period 120, decay: 0.004, 0.031 seconds/period
period 130, decay: 0.004, 0.031 seconds/period
period 140, decay: 0.004, 0.031 seconds/period
period 150, decay: 0.003, 0.031 seconds/period
period 160, decay: 0.003, 0.031 seconds/period
period 170, decay: 0.003, 0.031 seconds/period
period 180, decay: 0.003, 0.031 seconds/period
period 190, decay: 0.003, 0.031 seconds/period
period 200, decay: 0.002, 0.031 seconds/period
period 210, decay: 0.002, 0.031 seconds/period
period 220, decay: 0.002, 0.031 seconds/period
period 230, decay: 0.002, 0.031 seconds/period
period 240, decay: 0.002, 0.031 seconds/period
period 250, decay: 0.002, 0.031 seconds/period
period 260, decay: 0.002, 0.031 seconds/period
period 270, decay: 0.002, 0.031 seconds/period
period 280, decay: 0.002, 0.031 seconds/period
period 290, decay: 0.001, 0.031 seconds/period
period 300, decay: 0.001, 0.031 seconds/period
period 310, decay: 0.001, 0.031 seconds/period
period 320, decay: 0.001, 0.031 seconds/period
period 330, decay: 0.001, 0.031 seconds/period
period 340, decay: 0.001, 0.031 seconds/period
period 344.32 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.260
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.204989 seconds (4.07 M allocations: 216.314 MiB, 0.74% gc time, 169.34% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.260
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (6, 7, 39)
padded size: (6, 7, 55)
cell count: 2,310
step size: 85.0 steps/period
source duration: 3.94 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.45, 1.1 seconds/period
period 20, decay: 0.17, 0.021 seconds/period
period 30, decay: 0.086, 0.021 seconds/period
period 40, decay: 0.034, 0.02 seconds/period
period 50, decay: 0.018, 0.02 seconds/period
period 60, decay: 0.016, 0.021 seconds/period
period 70, decay: 0.007, 0.02 seconds/period
period 80, decay: 0.005, 0.02 seconds/period
period 90, decay: 0.004, 0.021 seconds/period
period 100, decay: 0.002, 0.02 seconds/period
period 110, decay: 0.002, 0.021 seconds/period
period 120, decay: 0.001, 0.02 seconds/period
period 129.51 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.280
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.232770 seconds (4.27 M allocations: 222.166 MiB, 1.22% gc time, 172.69% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.280
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (7, 8, 39)
padded size: (7, 8, 55)
cell count: 3,080
step size: 96.0 steps/period
source duration: 3.94 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.433, 1.13 seconds/period
period 20, decay: 0.147, 0.032 seconds/period
period 30, decay: 0.085, 0.03 seconds/period
period 40, decay: 0.043, 0.031 seconds/period
period 50, decay: 0.03, 0.03 seconds/period
period 60, decay: 0.02, 0.03 seconds/period
period 70, decay: 0.014, 0.03 seconds/period
period 80, decay: 0.01, 0.03 seconds/period
period 90, decay: 0.008, 0.03 seconds/period
period 100, decay: 0.006, 0.03 seconds/period
period 110, decay: 0.005, 0.03 seconds/period
period 120, decay: 0.004, 0.03 seconds/period
period 130, decay: 0.003, 0.03 seconds/period
period 140, decay: 0.003, 0.03 seconds/period
period 150, decay: 0.002, 0.03 seconds/period
period 160, decay: 0.002, 0.03 seconds/period
period 170, decay: 0.002, 0.03 seconds/period
period 180, decay: 0.002, 0.03 seconds/period
period 190, decay: 0.001, 0.03 seconds/period
period 200, decay: 0.001, 0.03 seconds/period
period 210, decay: 0.001, 0.03 seconds/period
period 216 stop on energy decay
simulation completed.
========================================
backend using simulation folder runs/r0.300
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 1.252845 seconds (4.26 M allocations: 221.848 MiB, 1.15% gc time, 173.33% compilation time)
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/r0.300
processor: CPU
cpu threads: 16
nres: 8.0
courant: 0.6
subpixel_smoothing: 0
boundaries: [:PEC :PEC; :PMC :PMC; :PML :PML]
sources
o1: 0, 0, -1.5
monitors
o1: 0, 0, -1
o2: 0, 0, 2
original size: (7, 8, 39)
padded size: (7, 8, 55)
cell count: 3,080
step size: 111.0 steps/period
source duration: 3.94 periods
max time: unbounded
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.399, 1.14 seconds/period
period 20, decay: 0.12, 0.035 seconds/period
period 30, decay: 0.066, 0.035 seconds/period
period 40, decay: 0.05, 0.034 seconds/period
period 50, decay: 0.037, 0.035 seconds/period
period 60, decay: 0.027, 0.034 seconds/period
period 70, decay: 0.022, 0.034 seconds/period
period 80, decay: 0.018, 0.034 seconds/period
period 90, decay: 0.014, 0.039 seconds/period
period 100, decay: 0.013, 0.034 seconds/period
period 110, decay: 0.01, 0.035 seconds/period
period 120, decay: 0.009, 0.034 seconds/period
period 130, decay: 0.007, 0.034 seconds/period
period 140, decay: 0.007, 0.034 seconds/period
period 150, decay: 0.006, 0.035 seconds/period
period 160, decay: 0.005, 0.034 seconds/period
period 170, decay: 0.004, 0.035 seconds/period
period 180, decay: 0.004, 0.034 seconds/period
period 190, decay: 0.004, 0.034 seconds/period
period 200, decay: 0.003, 0.034 seconds/period
period 210, decay: 0.003, 0.034 seconds/period
period 220, decay: 0.003, 0.034 seconds/period
period 230, decay: 0.003, 0.035 seconds/period
period 240, decay: 0.003, 0.034 seconds/period
period 250, decay: 0.002, 0.034 seconds/period
period 260, decay: 0.002, 0.034 seconds/period
period 270, decay: 0.002, 0.034 seconds/period
period 280, decay: 0.002, 0.034 seconds/period
period 290, decay: 0.002, 0.034 seconds/period
period 300, decay: 0.002, 0.034 seconds/period
period 310, decay: 0.002, 0.034 seconds/period
period 320, decay: 0.002, 0.034 seconds/period
period 330, decay: 0.002, 0.034 seconds/period
period 340, decay: 0.002, 0.034 seconds/period
period 350, decay: 0.002, 0.034 seconds/period
period 360, decay: 0.001, 0.034 seconds/period
period 370, decay: 0.001, 0.034 seconds/period
period 380, decay: 0.001, 0.034 seconds/period
period 390, decay: 0.001, 0.034 seconds/period
period 400, decay: 0.001, 0.034 seconds/period
period 410, decay: 0.001, 0.034 seconds/period
period 420, decay: 0.001, 0.034 seconds/period
period 430, decay: 0.001, 0.034 seconds/period
period 440, decay: 0.001, 0.034 seconds/period
period 450, decay: 0.001, 0.034 seconds/period
period 460, decay: 0.001, 0.034 seconds/period
period 470, decay: 0.001, 0.034 seconds/period
period 480, decay: 0.001, 0.034 seconds/period
period 490, decay: 0.001, 0.034 seconds/period
period 500, decay: 0.001, 0.034 seconds/period
period 510, decay: 0.001, 0.034 seconds/period
period 520, decay: 0.001, 0.034 seconds/period
period 530, decay: 0.001, 0.034 seconds/period
period 540, decay: 0.001, 0.034 seconds/period
period 548.9 stop on energy decay
simulation completed.
Movie¶
Largest pillar
lumi.movie(paths[0])Loading...
Results: phase library¶
# S21 at center wavelength for each radius
mid = nwls // 2
S21 = []
for path in paths:
sol = lumi.load(path)
o1 = sol["monitors"]["o1"]
o2 = sol["monitors"]["o2"]
# + denotes outgoing mode (wrt port monitor outward normal), - incoming
S21.append((o2["modes"][0]["+"] / o1["modes"][0]["-"])[mid])
S21 = np.array(S21)
# transmission relative to smallest pillar, oriented so larger pillars mean more phase delay
t = S21 / S21[0]
if np.unwrap(np.angle(t))[-1] < 0:
t = np.conj(t)
delay = np.unwrap(np.angle(t))
print(f"phase coverage: {delay.max() - delay.min():.2f} rad ({(delay.max() - delay.min()) / (2 * np.pi):.2f} x 2pi)")
fig, ax = plt.subplots()
ax.plot(radii, np.abs(S21) ** 2, "o-", label="transmission")
ax.set_xlabel("pillar radius [um]")
ax.set_ylabel("transmission")
ax2 = ax.twinx()
ax2.plot(radii, delay / (2 * np.pi), "s-", color="C1", label="phase delay")
ax2.set_ylabel("phase delay [2pi]")
fig.legend()
plt.show()loading solution from runs/r0.080
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.100
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.120
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.140
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.160
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.180
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.200
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.220
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.240
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.260
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.280
may take a minute if simulation folder is remotely mounted
loading solution from runs/r0.300
may take a minute if simulation folder is remotely mounted
phase coverage: 8.03 rad (1.28 x 2pi)

Metalens layout¶
Hyperbolic phase profile focuses normally incident light at focal length f. At each lattice site, pick the pillar whose complex transmission best matches the target
D = 20 # lens diameter [um]
f = 20 # focal length [um]
# library transmission normalized to max amplitude
lib = t * np.abs(S21) / np.abs(S21).max()
PILLAR = 1, 0
lens = gf.Component()
xs = np.arange(-D / 2, D / 2 + P / 2, P)
for x in xs:
for y in xs:
rho = np.hypot(x, y)
if rho > D / 2:
continue
# center delayed most: edge rays travel farther to focus
target = np.exp(-1j * 2 * np.pi / wavelength * (np.hypot(rho, f) - f))
r = radii[np.argmin(np.abs(lib - target))]
tt = np.linspace(0, 2 * np.pi, 64, endpoint=False)
lens.add_polygon(list(zip(x + r * np.cos(tt), y + r * np.sin(tt))), layer=PILLAR)
lens.plot()
lens.write_gds(os.path.join(root, "metalens.gds"))PosixPath('runs/metalens.gds')