FDTD Lumerical#

gdsfactory provides you with a Lumerical FDTD interface to calculate Sparameters automatically (without you having to click around the Lumerical GUI)

The function gdsfactory.simulation.lumerical.write_sparameters_lumerical brings up a GUI, runs simulation and then writes the Sparameters both in .CSV and .DAT (Lumerical interconnect / simphony) file formats, as well as the simulation settings in YAML format.

In the CSV format each Sparameter will have 2 columns, s12m where m stands for magnitude and s12a where a stands for angle in radians.

For the simulation to wor well, your components need to have ports, that will be extended automatically to go over the PML.

lum GUI

The script calls internally the lumerical python API lumapi so you will need to make sure that you can run this from python.

import lumapi

session = lumapi.FDTD()

In linux that may require you to export the PYTHONPATH variable in your shell environment.

You can add one line into your .bashrc in your Linux machine.

[ -d "/opt/lumerical" ] && export PATH=$PATH:/opt/lumerical/$(ls /opt/lumerical)/bin && export PYTHONPATH=/opt/lumerical/$(ls /opt/lumerical)/api/python

Finally, You can chain the Sparameters to calculate solve of larger circuits using a circuit solver such as:

[ ]:
import gdsfactory as gf
import gdsfactory.simulation.lumerical as sim

gf.tech.SIMULATION_SETTINGS_LUMERICAL_FDTD
[ ]:
sim.write_sparameters_lumerical?
[ ]:
# NBVAL_SKIP
import gdsfactory as gf
import gdsfactory.simulation.lumerical as sim
import lumapi

s = lumapi.FDTD()
[ ]:
gf.components.cells.keys()
[ ]:
components = [
    "bend_euler",
    "bend_s",
    "coupler",
    "coupler_ring",
    "crossing",
    "mmi1x2",
    "mmi2x2",
    "taper",
    "straight",
]
need_review = []

for component_name in components:
    component = gf.components.cells[component_name]()
    sim.write_sparameters_lumerical(component, run=False, session=s)
    response = input(f"does the simulation for {component_name} look good? (y/n)")
    if response.upper()[0] == "N":
        need_review.append(componnent_name)

Modify layer stack#

All layers information is passed to the Lumerical simulator through the layer_stack

Layer thickness#

You can modify the thickness of any specific layer of the stack. For example lets increase the core thickness to 230 nm

[ ]:
from gdsfactory.tech import LAYER_STACK
[ ]:
LAYER_STACK
[ ]:
layer_stack2 = LAYER_STACK
[ ]:
nm = 1e-3
layer_stack2["core"].thickness = 230 * nm
[ ]:
layer_stack2["core"].thickness
[ ]:
sim.write_sparameters_lumerical(
    gf.components.mmi1x2(), layer_stack=layer_stack2, run=False, session=s
)

You will be able to see the layer thickness increase in the lumerical GUI

thickness

Layer material or index#

You can also modify the material refractive index or material name from the Lumerical Material database

material: material spec, can be

  • a string from lumerical database materials.

  • a complex for n, k materials.

  • a float or int, representing refractive index.

[ ]:
LAYER_STACK
[ ]:
material_name_to_lumerical = dict(si=3.6)

sim.write_sparameters_lumerical(
    gf.components.mmi1x2(),
    layer_stack=layer_stack2,
    run=False,
    session=s,
    material_name_to_lumerical=material_name_to_lumerical,
)

stack

[ ]:
component = gf.components.mmi1x2()
material_name_to_lumerical = dict(si=(3.45, 2))  # or dict(si=3.45+2j)

r = sim.write_sparameters_lumerical(
    component=component,
    material_name_to_lumerical=material_name_to_lumerical,
    run=False,
    session=s,
)

complex index

[ ]:
material_name_to_lumerical = dict(si="InP - Palik")

sim.write_sparameters_lumerical(
    gf.components.mmi1x2(),
    layer_stack=layer_stack2,
    run=False,
    session=s,
    material_name_to_lumerical=material_name_to_lumerical,
)

extra

gdsfactory can also compute the Sparameters of a component that have not been simulated before.

[ ]:
sim.write_sparameters_lumerical(gf.components.mmi1x2())
[ ]:
sim.plot.plot_sparameters(gf.components.mmi1x2(), keys=["S23m", "S13m"], logscale=True)

As well as a group of components

[ ]:
# NBVAL_SKIP

components = [
    gf.components.coupler_ring(gap=gap, radius=radius)
    for gap in [0.15, 0.2, 0.3]
    for radius in [5, 10]
]

for c in components:
    c.show(show_ports=True)
    print(c)
    sim.write_sparameters_lumerical(c)

To debug a simulation you can create a Lumerical session outside the simulator, pass it to the simulator, and use run=False flag

[ ]:
# NBVAL_SKIP
import lumapi
import gdsfactory as gf

s = lumapi.FDTD()
c = gf.components.straight()
sim.write_sparameters_lumerical(c, run=False, session=s)

By default gdsfactory uses the generic LayerStack for 0.22um height silicon layer.

You can also define any LayerStack

[ ]:
import gdsfactory as gf


def get_layer_stack():
    return gf.tech.LayerStack(
        wg=gf.tech.LayerLevel(layer=(1, 0), thickness=400e-3, zmin=0.0, material="sin")
    )


layer_stack = get_layer_stack()
[ ]:
# NBVAL_SKIP
import gdsfactory as gf
import gdsfactory.simulation.lumerical as sim

c = gf.components.straight()
s = sim.write_sparameters_lumerical(c, layer_stack=layer_stack, run=False, session=s)
[ ]:
dict(si=(2, 3))
[ ]:
si = (2, 3)
[ ]:
isinstance(si, (list, tuple))
[ ]: