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, TM_1550_500, 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, eskid_TE_1550, SiN_TE_1550_750
Basic Technology ports:
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]:
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]:
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]:
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]:
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]:
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}
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"])
Starting...
Loading cached simulation from .tidy3d/pf_cache/5LZ/ms_info-XY62H77R4HCJMYV22LW35BRWMXLMCTNJE6I632XRY5LEB7JR7IRQ.json.
Progress: 100%
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...
11:22: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 3 WARNING messages.
Loading cached simulation from .tidy3d/pf_cache/ECN/fdtd_info-JMQV25XI5UVYFP52SAZEREB2OCQL4SZ2LYPMBVIV7VLDTOCSEHHQ.json.
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 3 WARNING messages.
Progress: 100%