Parametric Component Library

Components are the building blocks for all devices in PhotonForge. Besides those provided by PDKs, PhotonForge offers a growing number of parametric components that can be used to build photonic circuits. They are built to be used with any technology, so that we can freely mix them with PDK-provided components to create larger systems.

We can use both the included basic technology and the SiEPIC OpenEBL PDK through the siepic_forge module to show the parametric components working on different technologies.

[1]:
import photonforge as pf
import siepic_forge as siepic

# Create 2 technologies for comparison
siepic_tech = siepic.ebeam()
basic_tech = pf.basic_technology()

# Use the SiEPIC PDK as project default
pf.config.default_technology = siepic_tech

One parameter that most parametric components require is a port specification to define their ports and waveguides. We can recall the port specifications available in each technology:

[2]:
print("SiEPIC ports:\n" + ", ".join(siepic_tech.ports.keys()))
print("\nBasic Technology ports:\n" + ", ".join(basic_tech.ports.keys()))
SiEPIC ports:
SiN_TE_1310_750, TE_1550_500, TE_1310_410, SiN_TE_895_450, SiN_TM_1310_750, TE_1310_350, SiN_TE_1310_800, TM_1310_350, TM_1550_500, SiN_TE-TM_1550_1000, SiN_TM_1550_1000, SiN_TE_1550_1000, SiN_TE_1550_800, Rib_TE_1550_500, TE-TM_1550_450, Rib_TE_1310_350, MM_TE_1550_2000, MM_TE_1550_3000, MM_SiN_TE_1550_3000, Slot_TE_1550_500, SiN_TE_1550_750, eskid_TE_1550

Basic Technology ports:
CPW, Rib, Strip

Creating the actual component is straightforward. We’ll use the “TE_1550_500” waveguide from SiEPIC to create a 10 μm straight waveguide section:

[3]:
straight = pf.parametric.straight(port_spec="TE_1550_500", length=10)
straight
[3]:
../_images/guides_Parametric_Component_Library_5_0.svg

Creating an S bend for the same port specification is also simple:

[4]:
s_bend = pf.parametric.s_bend(port_spec="TE_1550_500", length=8, offset=2)
s_bend
[4]:
../_images/guides_Parametric_Component_Library_7_0.svg

For comparison, here’s the same S bend created for the basic technology we generated earlier. Note that, because we’re not using the default technology, we have to specify the technology we want to use.

[5]:
s_bend_basic = pf.parametric.s_bend(
    port_spec="Strip", length=8, offset=2, technology=basic_tech
)
s_bend_basic
[5]:
../_images/guides_Parametric_Component_Library_9_0.svg

Caching

Parametric components are cached by default, which means that when called multiple times with the same arguments, the same component will be returned, unless that component has been modified. For example, if we recreate the S bend, we’ll get the same component as before.

[6]:
s_bend_cached = pf.parametric.s_bend(
    port_spec="Strip", length=8, offset=2, technology=basic_tech
)
s_bend_cached is s_bend_basic
[6]:
True

The cache can be skipped with use_parametric_cache=False:

[7]:
s_bend_non_cached = pf.parametric.s_bend(
    port_spec="Strip",
    length=8,
    offset=2,
    technology=basic_tech,
    use_parametric_cache=False,
)
s_bend_non_cached is s_bend_basic
[7]:
False

Component Updates

Parametric components can be updated after creation by setting new keyword argument parameters to the update function:

[8]:
s_bend.update(port_spec="Slot_TE_1550_500", length=6)
[8]:
../_images/guides_Parametric_Component_Library_15_0.svg

Attention: component updates reset any component changes added after creation because it re-instantiates the component contents from its original function. For example, if we add a new model to the component, it will be lost after an update:

[9]:
s_bend.add_model(pf.WaveguideModel(n_complex=2.0 + 0.1j), "BadModel")
assert "BadModel" in s_bend.models

s_bend.update(port_spec="Rib_TE_1550_500", offset=1)
assert "BadModel" not in s_bend.models

s_bend
[9]:
../_images/guides_Parametric_Component_Library_17_0.svg

The parametric information stored in the component can be inspected with the parametric_function and parametric_kwargs attributes:

[10]:
print("Parametric function name:", s_bend.parametric_function)
print("Parametric kwargs:", s_bend.parametric_kwargs)
Parametric function name: photonforge.parametric.s_bend
Parametric kwargs: {'port_spec': 'Rib_TE_1550_500', 'length': 6, 'offset': 1, 'euler_fraction': None, 'active_model': None, 'technology': None, 'name': None, 'tidy3d_model_kwargs': None, 'waveguide_model_kwargs': None}

Simulation

Parametric components include the models that are meaningful for them. Most include a Tidy3D model and, when applicable, an analytical one. Therefore, simulating them can be as simple as:

[11]:
import numpy as np

s_matrix = s_bend.s_matrix(pf.C_0 / np.linspace(1.45, 1.65, 11))
_ = pf.plot_s_matrix(s_matrix, input_ports=["P0"])
Loading cached simulation from .tidy3d/pf_cache/S4R/ms_info-JNSZRDJUA7E4EGUDBJZCZOISGWV3FUH45ZOJM54CIRDQ62QXFHAA.json.
Progress: 100%
../_images/guides_Parametric_Component_Library_21_1.png

The S bend uses a semi-analytical waveguide model by default, which assumes the bend has no significant radiation losses. We can use Tidy3D to get more realistic S parameters through FDTD. The warnings can be ignored in this case: they come from the slabs touching the simulation boundaries, as they are meant to.

[12]:
s_bend.activate_model("Tidy3D")

s_matrix = s_bend.s_matrix(pf.C_0 / np.linspace(1.45, 1.65, 11))
_ = pf.plot_s_matrix(s_matrix, input_ports=["P0"])
Starting…
20:59:26 -03 WARNING: Structure at 'structures[0]' has bounds that extend       
             exactly to simulation edges. This can cause unexpected behavior. If
             intending to extend the structure to infinity along one dimension, 
             use td.inf as a size variable instead to make this explicit.       
             WARNING: Suppressed 1 WARNING message.                             
Loading cached simulation from .tidy3d/pf_cache/VMO/fdtd_info-P5NP6AOESJDB63ZKXSS7L7T2GRKGDZ4BUZ627UMCEVXCF5PQTO7A.json.
20:59:27 -03 WARNING: Structure at 'structures[0]' has bounds that extend       
             exactly to simulation edges. This can cause unexpected behavior. If
             intending to extend the structure to infinity along one dimension, 
             use td.inf as a size variable instead to make this explicit.       
             WARNING: Suppressed 1 WARNING message.                             
Progress: 100%
../_images/guides_Parametric_Component_Library_23_7.png

Default Arguments

In many cases, the same arguments will be user over and over when creating parametric components. To avoid repetition and help with automation, we can preset keyword arguments for the parametric components using config.default_kwargs. For example, if we define a default port specification, we don’t need to pass it when creating the component:

[13]:
pf.config.default_kwargs["port_spec"] = "TE_1550_500"

pf.parametric.straight(length=10)
[13]:
../_images/guides_Parametric_Component_Library_25_0.svg

In some cases, it is desirable to set a default only for a specific component. For example, both bend and s_bend have a euler_fraction argument. If we want to set different defaults for each (or maybe only for one of them), we can do so using a sub-dictionary with the function name:

[14]:
pf.config.default_kwargs["bend"] = {"euler_fraction": 0}
pf.config.default_kwargs["s_bend"] = {"euler_fraction": 1}

# References used only to show both components simultaneously
pf.Component().add(
    pf.Reference(pf.parametric.bend(radius=4)),
    pf.Reference(pf.parametric.s_bend(length=6, offset=3), (5, 0))
)
[14]:
../_images/guides_Parametric_Component_Library_27_0.svg

Finally, defaults can still be overridden at the function call:

[15]:
pf.parametric.bend(radius=4, euler_fraction=1)
[15]:
../_images/guides_Parametric_Component_Library_29_0.svg