FDTD Luminescent Simulation and Inverse Design#
v0.2.12-beta
2024/12/06
Paul Shen pxshen@alumni.stanford.edu
Synopsis#
Luminescent AI empowers photonic and RF engineers to simulate or inverse design complex electromagnetic components in just a few lines of code! We created an automatic differentiation (AD) and GPU compatible FDTD engine and geometry generator for photonic integrated circuits (PIC) and metasurfaces, as well (in the future) RF microstrip circuits and patch antennas. Experimental release đ„Œ. Expect bugs đđ
Follow us for updates! Star us GitHub if you like our work. We respond to issues within a day
%%HTML
<iframe width="560" height="315" src="https://www.youtube.com/embed/n_O1r6wggLU?si=x2tKzw4hzrIz6h7C" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
Features and conventions#
We do simulation and generative inverse design in 3D or 2.5D, with multiple wavelengths and modes, and on CPU or GPU. However, GPU acceleration is guaranteed only for simulation and not inverse design because the later requires significant VRAM.
Technical features of FDTD backend#
Automatic differentiation (AD) compatible for adjoint optimization
Optional GPU acceleration
Length scale controlled geometry optimizer
Nonlinear and anisotropic materials
Tensor subpixel smoothing for accuracy
Adaptive graded grid and Float16 support for speed
PML, periodic, PEC, PMC boundaries
Modal sources, plane waves, Gaussian beams
Modal monitors, DFT fields
Geometry layout#
We layout using Python gdsfactory which is integrated with KLayout. Can also import .gds into gdsfactory . Internally, gdsfactory component and layer stack let generate a 3D mesh which is clipped vertically some depths above and below core_layer
. By default,
we apply a SOI 220nm node (Si, SiO, SiN and Ge layers) implemented by gdsfactory.generic_tech
layer stack. luminescent.MATERIALS
maps the material tag of a layer in the layer stack to its property eg MATERIALS["si"]["epsilon"]
. Can create your own LAYER_STACK and MATERIALS for your process node.
Margins and ports#
We automatically extend waveguides and adds margins during simulation but port locations remain fixed thus not affecting sparams. Port numbers map to gdsfactory component ports. Specify port pairs as eg 2,1
which is equivalent to o2@0,o1@0
meaning optical_port_number@mode_number.
Reciprocity and symmetry#
Usually only a subset of sparams is needed because of symmetry or reciprocity, specified as keys
in sparams study or inferred automatically from inverse design study. We do a run for each port excitation. If keys
is omitted in sparams study, all sparams will be calculated requiring more runs.
File workflow#
Each simulation or design optimization run is saved to a folder named after name
arg or (if unnamed) timestamp string. These run folders are inside working directory wd="~/luminescent_runs"
which can be modified by passing to write_sparams
or gcell_problem
. Can access saved runs via load_solution()
, load_solution()
, finetune()
which act on latest modified run folder inside wd
if name
not specified.
Installation#
Backend CPU binaries at releases . GPU binaries can be requested
Windows#
Frontend. Install Anaconda distribution of Python . Inside Anaconda Prompt do
pip install -U luminescent
Backend. Unzip
LuminescentAI.zip
binaries. Add where_you_unzipped\LuminescentAI\bin
to Path (using âedit environmental variablesâ)
Linux#
Frontend.
pip install -U luminescent
Backend. TBD
Tutorials: PIC S-parameters simulation#
waveguide bend#
5um inner radius#
Single wavelength 3D#
We characterize insertion loss (IL) of waveguide bends wrt radius. We set R=5.0um, the often cited minimum radius in silicon photonics. First, we use gdsfactory
to make the bend (note gdsfactory
uses inner radius). Next, write_sparams
simulates and saves results, which are retrieved and visualized by load_solution
.
nres
is the maximum resolution - the number of points per wavelength in free space. The grid is adaptive so border regions actually use a lower resolution for speed. Remember FDTD scales as O(n^4)! nres
has a huge effect on simulation time! Here, nres=40
is accurate but can take half hour on CPU. nres=20
is 16x faster and offers a reasonable estimate. Finally, Because of reciprocity we only need S or T param keys of â2,1â.
import gdsfactory as gf
import luminescent as lumi
import numpy as np
radius = 5
c = gf.components.bend_circular(radius=radius)
# c.plot()
name = f"bend_R{radius}"
N = 3 # 3D or 2D
wavelengths = 1.55
keys = ["2,1"] # same as keys=["o2@0,o1@0"]
nres = 40
lumi.write_sparams(c, name=name, wavelengths=wavelengths, nres=nres, keys=keys, N=N)
sol = lumi.load_solution(name=name)
Loss is <-0.01dB, which is lower than reported by Tidy3D example
Wavelength range 3D#
We can repeat the simulation with multiple wavelengths in a range. Closely spaced wavelengths may require longer integration periods to resolve.
name = f"bend_R{radius}_multi"
wavelengths = np.linspace(1.5, 1.6, 5) # number or list or array
lumi.write_sparams(c, name=name, wavelengths=wavelengths, nres=nres, keys=keys, N=N)
sol = lumi.load_solution(name=name)
Quickie 2.5D approximation#
Set N=2
to do a 2.5D simulation using (Ex, Ey, Hz) on a 2D plane with an âeffectiveâ refractive index integrated from a vertical modal profile. This is significantly faster but not accurate. Nevertheless itâs a good way of exploring various geometries.
1.5um inner radius#
Letâs try a tighter bend
radius = 1.5
name = f"bend_R{radius}"
c = gf.components.bend_circular(radius=radius, allow_min_radius_violation=True)
lumi.write_sparams(c, name=name, wavelengths=wavelengths, nres=nres, keys=keys, N=N)
sol = lumi.load_solution(name=name)
Loss increased to 0.11dB
customizing materials, layers, and modes#
Defaults#
import gdsfactory as gf
import luminescent as lumi
from gdsfactory.generic_tech import LAYER, LAYER_STACK
from luminescent import MATERIALS
core_layer = LAYER.WG
bbox_layer = LAYER.WAFER
layer_stack = LAYER_STACK
materials = MATERIALS
N = 3
dtype = "float32"
gpu = None
TE1#
c = gf.components.straight(length=1.0, width=0.5, layer=core_layer)
wavelengths = 1.55
nres = 30
name = "TE1"
keys = ["o2@1,o1@1"]
lumi.write_sparams(
c,
name=name,
wavelengths=wavelengths,
keys=keys,
nres=nres,
N=N,
dtype=dtype,
gpu=gpu,
)
lumi.load_solution(name=name)
Si rib waveguide#
only need to modify bbox_layer to include 90nm bottom silicon slab
name = "rib"
keys = ["2,1"]
bbox_layer = [LAYER.WAFER, LAYER.SLAB90]
lumi.write_sparams(
c,
name=name,
wavelengths=wavelengths,
keys=keys,
nres=nres,
N=N,
dtype=dtype,
gpu=gpu,
bbox_layer=bbox_layer,
)
sol = lumi.load_solution(name=name)
SiN strip waveguide#
name = "SiN"
core_layer = LAYER.WGN
c = gf.components.straight(length=2.0, width=1.0, layer=core_layer)
lumi.write_sparams(
c,
name=name,
wavelengths=wavelengths,
keys=keys,
nres=nres,
N=N,
dtype=dtype,
gpu=gpu,
core_layer=core_layer,
)
sol = lumi.load_solution(name=name)
Tutorials: PIC generative inverse design#
We introduce GCells (generative cells), a natural evolution of PCells (parametric cells) in semiconductor design . Given a set of inverse design objectives, a GCell will generate optimal geometry using adjoint optimization while ensuring manufacturability by enforcing minimum feature lengths.
In examples below, gcells.mimo
(multi in multi out) is just a gdsfactory component with configurable waveguide ports, simple slab as pre-optimization geometry, and overlying rectangular design regions. Dimensions l
along x and w
along y. Ports are numbered incrementally: west (SW->NW) -> east (SE->NE) -> south (SW->SE) -> north (NW->NE). By default, theyâre spaced equally on a side. Example: west=1, east=2
places port 1 on west, ports 2 & 3 on east. But can also individually specify their locations and widths. Example : west=[1.0, 2.5], wwg_west=[0.5, 0.4]
.
Optimization targets
is a dictionary organized wrt target type & wavelength. Types include T-params (tparams
, most common), phase difference (phasediff
), S-params (sparams
). Multiple types & wavelengths are possible & often necessary . Loss for each type is scaled automatically to vary from 0 to 1 usually . For example , tparams
loss of 0.5 roughly means 50% of power going to wrong places.
lvoid
is minimum length scale for voids. No void features smaller than it (currently this isnât exact - contact us for a more precise algorithm). Similarly for lsolid
.
N=2
optimizes in 2.5D which serves as a fast initialization for 3D optimization. Examples done at low resolution and lax convergence in 2.5D on CPU. For accuracy, the result must be finetuned in 3D at finer resolution, a feature that can be requested from Luminescent AI .
Generative cells PDK for passive devices#
mode converter#
We target âo2@1,o1@0â, converting optical port 1 mode TE0 input to optical port 2 mode TE1 output at 1.55um. Mode converters are notoriously hard to design, but generative AI does it in a pinch!
# recommended RAM: >16G
import luminescent as lumi
name = "mode_converter" # can be any string
c = lumi.gcells.mimo(west=1, east=1, l=5.0, w=2.4, wwg=0.5, taper=0.05)
targets = {"tparams": {1.55: {"o2@1,o1@0": 1.0}}}
prob = lumi.gcell_problem(
c,
targets,
name=name,
N=2,
nres=15,
lvoid=0.15,
lsolid=0.15,
iters=100,
stoploss=0.05,
)
lumi.solve(prob)
Can optionally optimize more using finetune
# import luminescent as lumi
# lumi.finetune(iters=40,name="mode_converter")
Lets see simulation fields and optimized geometry. Note the lax convergence criteria (stoploss=.05
) means the solution isnât perfect.
name = "mode_converter"
sol = lumi.load_solution(name=name)
1x2 splitter MMI#
1.55um wavelength 1x2 splitter. We set symmetry about y so only need to specify T21=.5 as optimization target. Data saved to name
folder inside working directory. We start iteratiions of adjoint optimization.
import luminescent as lumi
name = "splitter"
c = lumi.gcells.mimo(
west=1,
east=2,
l=4.0,
w=2.0,
wwg=0.5,
taper=0.05,
)
targets = {
"tparams": {1.55: {"2,1": 0.5}},
}
prob = lumi.gcell_problem(
c,
targets,
name=name,
N=2,
nres=15,
symmetries=[1],
lvoid=0.15,
lsolid=0.15,
iters=50,
stoploss=0.05,
)
lumi.solve(prob, run=False)
name = "splitter"
sol = lumi.load_solution(name=name)
1x4 splitter MMI#
1x4 splitter at 1.55um with same phase on outputs. Because of symmetry, only need 0.0 phase difference between first 2 outputs.
# finetune(iters=10,name=name)
sol = lumi.load_solution(name)
wavelength domain demultiplexer#
# RAM: 32G
import luminescent as lumi
name = "demux"
c = lumi.gcells.mimo(west=1, east=2, l=4.0, w=4.0, wwg=0.5)
targets = {
"tparams": {
1.55: {"2,1": 1.0},
1.20: {"3,1": 1.0},
}
}
prob = lumi.gcell_problem(
c, targets, name=name, lvoid=0.15, lsolid=0.15, nres=15, N=2, iters=50
)
lumi.solve(prob)
name = "demux"
sol = lumi.load_solution(name)
symmetric crossing#
import luminescent as lumi
name = "crossing"
c = lumi.gcells.mimo(west=1, east=1, south=1, north=1, l=4.0, w=4.0, wwg=0.5)
targets = {"tparams": {1.55: {"2,1": 1.0}}}
prob = lumi.gcell_problem(
c,
targets,
name=name,
lvoid=0.15,
lsolid=0.15,
nres=15,
symmetries=[0, 1],
N=2,
iters=40,
)
lumi.solve(prob)
sol = lumi.load_solution(name)
GCells PDK for active devices#
Thermo-optic phase shifters#
Please request
GCells PDK for nonlinear devices#
Please request
Collaboration and research opportunities#
We can publish or keep it proprietary, depending on your needs.
Algorithms
Reduced basis geometry generators for length scale controlled topology optimization
Automatic differentiation and GPU compatible FDTD for inverse design in photonics and RF
Algorithms for reduced memory usage in time domain adjoint optimization
Neural surrogates for FDTD
Photonic applications
Energy efficient and compact photonic phase shifters and resonators
Energy efficient and compact photonic modulators and MZIs
Inverse design of nonlinear photonic devices
Inverse design of âŠ
Links#
GitHub: Star us :) We respond to issues within a day
LinkedIn: Follow us for new features and bug fixes
Company: Consulting, collaboration, publication opportunities available
Email: pxshen@alumni.stanford.edu info@luminescentai.com
WhatsApp: 650-776-7724
WeChat: pxshen1230