Ring filter#

Calculations#

For a ring resonator we need to define:

Optical parameters:

  • coupling coefficient: will define resonance extinction ratio for a particular ring loss.

  • Free spectral range.

Electrical parameters:

  • VpiL

  • Resistance

import gdsfactory as gf
import numpy as np


def ring(
    wl: np.ndarray,
    wl0: float,
    neff: float,
    ng: float,
    ring_length: float,
    coupling: float,
    loss: float,
) -> np.ndarray:
    """Returns Frequency Domain Response of an all pass filter.

    Args:
        wl: wavelength in  um.
        wl0: center wavelength at which neff and ng are defined.
        neff: effective index.
        ng: group index.
        ring_length: in um.
        coupling: coupling coefficient.
        loss: dB/um.
    """
    transmission = 1 - coupling
    neff_wl = (
        neff + (wl0 - wl) * (ng - neff) / wl0
    )  # we expect a linear behavior with respect to wavelength
    out = np.sqrt(transmission) - 10 ** (-loss * ring_length / 20.0) * np.exp(
        2j * np.pi * neff_wl * ring_length / wl
    )
    out /= 1 - np.sqrt(transmission) * 10 ** (-loss * ring_length / 20.0) * np.exp(
        2j * np.pi * neff_wl * ring_length / wl
    )
    return abs(out) ** 2


if __name__ == "__main__":
    import matplotlib.pyplot as plt

    loss = 0.03  # [dB/μm] (alpha) waveguide loss
    neff = 2.46  # Effective index of the waveguides
    wl0 = 1.55  # [μm] the wavelength at which neff and ng are defined
    radius = 5
    ring_length = 2 * np.pi * radius  # [μm] Length of the ring
    coupling = 0.5  # [] coupling of the coupler
    wl = np.linspace(1.5, 1.6, 1000)  # [μm] Wavelengths to sweep over
    wl = np.linspace(1.55, 1.60, 1000)  # [μm] Wavelengths to sweep over
    ngs = [4.182551, 4.169563, 4.172917]
    thicknesses = [210, 220, 230]

    # widths = np.array([0.4, 0.45, 0.5, 0.55, 0.6])
    # ngs = np.array([4.38215238, 4.27254985, 4.16956338, 4.13283219, 4.05791982])

    widths = np.array([0.495, 0.5, 0.505])
    neffs = np.array([2.40197253, 2.46586378, 2.46731758])
    ng = 4.2  # Group index of the waveguides

    for width, neff in zip(widths, neffs):
        p = ring(
            wl=wl,
            wl0=wl0,
            neff=neff,
            ng=ng,
            ring_length=ring_length,
            coupling=coupling,
            loss=loss,
        )
        plt.plot(wl, p, label=f"{int(width*1e3)}nm")

    plt.title("ring resonator vs waveguide width")
    plt.xlabel("wavelength (um)")
    plt.ylabel("Power Transmission")
    plt.grid()
    plt.legend()
    plt.show()
../_images/75764563d97a5034cbb8dd5d7f89e4db26739d341d9ff08e289f92602ba17c79.png

Layout#

gdsfactory easily enables you to layout Component with as many levels of hierarchy as you need.

A Component is a canvas where we can add polygons, references to other components or ports.

Lets add two references in a component.

c = gf.components.ring_single_heater(gap=0.2, radius=10, length_x=4)
c.plot()
../_images/def3724b7ad6e93e5c793007770d6a4cc6e62b30b727ce31b3c8fa4a90482d16.png
scene = c.to_3d()
scene.show()

Lets define a ring function that also accepts other component specs for the subcomponents (straight, coupler, bend)

ring = gf.components.ring_single_heater(
    gap=0.2, radius=10, length_x=4, via_stack_offset=(2, 0)
)
ring_with_grating_couplers = gf.routing.add_fiber_array(ring)
ring_with_grating_couplers.plot()
../_images/5c560b7c95c2d1967b78e3ef8b6d054b4d16631793aa39863b1e2010450a4f2e.png
port_names = ["l_e1", "r_e3"]
port_names = ["l_e4", "r_e4"]
c = gf.routing.add_pads_top(
    ring,
    port_names=port_names,
)
c = gf.routing.add_fiber_array(c)
c
../_images/1677caee8d05bc7b46e857d9f0960c5d43f4841c510270d877e429cba697daa5.png

Top reticle assembly#

Once you have your components and circuits defined, you can add them into a top reticle Component for fabrication.

You need to consider:

  • what design variations do you want to include in the mask? You need to define your Design Of Experiment or DOE

  • obey DRC (Design rule checking) foundry rules for manufacturability. Foundry usually provides those rules for each layer (min width, min space, min density, max density)

  • make sure you will be able to test te devices after fabrication. Obey DFT (design for testing) rules. For example, if your test setup works only for fiber array, what is the fiber array spacing (127 or 250um?)

  • if you plan to package your device, make sure you follow your packaging guidelines from your packaging house (min pad size, min pad pitch, max number of rows for wire bonding …)

import gdsfactory as gf
import pandas as pd


@gf.cell
def spiral_gc(**kwargs):
    """Returns spiral with Grating Couplers."""
    c = gf.components.spiral(**kwargs)
    c = gf.routing.add_fiber_array(c)
    c.info["doe"] = "spirals_sc"  # strip Cband spirals
    c.info["measurement"] = "optical_spectrum"
    c.info["measurement_parameters"] = ""
    c.info["analysis"] = "[power_envelope]"
    c.info["analysis_parameters"] = "[]"
    c.info["ports_optical"] = 4
    c.info["ports_electrical"] = 0
    c.info.update(kwargs)
    return c


@gf.cell
def mzm_gc(length_x=10, **kwargs) -> gf.Component:
    """Returns a MZI with Grating Couplers.

    Args:
        length_x: length of the MZI.
        kwargs: additional settings.
    """
    c = gf.components.mzi2x2_2x2_phase_shifter(
        length_x=length_x, auto_rename_ports=False, **kwargs
    )
    c = gf.routing.add_pads_top(c, port_names=["top_l_e1", "top_r_e3"])
    c = gf.routing.add_fiber_array(c)
    c.info["doe"] = "mzm"
    c.info["measurement"] = "optical_spectrum"
    c.info["analysis"] = "[fsr]"
    c.info["analysis_parameters"] = "[]"
    c.info["measurement_parameters"] = ""
    c.info["ports_electrical"] = 2
    c.info["ports_optical"] = 6
    c.info["length_x"] = length_x
    c.info.update(kwargs)
    return c


def sample_reticle(grid: bool = False) -> gf.Component:
    """Returns MZI with TE grating couplers."""
    from gdsfactory.generic_tech.cells import (
        add_fiber_array_optical_south_electrical_north,
    )

    mzis = [mzm_gc(length_x=lengths) for lengths in [100, 200, 300]]
    spirals = [spiral_gc(length=length) for length in [0, 100, 200]]
    rings = []
    for length_x in [10, 20, 30]:
        ring = gf.components.ring_single_heater(length_x=length_x)
        c = add_fiber_array_optical_south_electrical_north(
            component=ring,
            electrical_port_names=["l_e2", "r_e2"],
        )
        c.name = f"ring_{length_x}"
        c.info["doe"] = "ring_length_x"
        c.info["measurement"] = "optical_spectrum"
        c.info["measurement_parameters"] = ""
        c.info["ports_electrical"] = 2
        c.info["ports_optical"] = 4
        c.info["analysis"] = "[fsr]"
        c.info["analysis_parameters"] = "[]"
        c.info["length_x"] = length_x
        rings.append(c)

    copies = 3  # number of copies of each component
    components = mzis * copies + rings * copies + spirals * copies
    if grid:
        return gf.grid(components)
    c = gf.pack(components)
    if len(c) > 1:
        c = gf.pack(c)[0]
    return c[0]


c = sample_reticle()
c.show()
c
../_images/8b5c05f89052c74bcd294ce6b2a5418831a0d3d30cfd8b7a1574cbef76509158.png
gf.labels.write_test_manifest(c, csvpath="sample_reticle.csv")
df = pd.read_csv("sample_reticle.csv")
df
cell x y info ports settings doe analysis analysis_parameters measurement measurement_parameters ports_optical ports_electrical
0 mzm_gc_LX100 250.020 153.225 {"length_x": 100} {"in_o1": {"name": "in_o1", "center": [145.02,... {"length_x":100} mzm [fsr] [] optical_spectrum NaN 6 2
1 mzm_gc_LX200 200.020 419.775 {"length_x": 200} {"in_o1": {"name": "in_o1", "center": [145.02,... {"length_x":200} mzm [fsr] [] optical_spectrum NaN 6 2
2 mzm_gc_LX300 150.020 686.325 {"length_x": 300} {"in_o1": {"name": "in_o1", "center": [145.02,... {"length_x":300} mzm [fsr] [] optical_spectrum NaN 6 2
3 mzm_gc_LX100 250.020 952.876 {"length_x": 100} {"in_o1": {"name": "in_o1", "center": [145.02,... {"length_x":100} mzm [fsr] [] optical_spectrum NaN 6 2
4 mzm_gc_LX200 200.020 1219.426 {"length_x": 200} {"in_o1": {"name": "in_o1", "center": [145.02,... {"length_x":200} mzm [fsr] [] optical_spectrum NaN 6 2
5 mzm_gc_LX300 150.020 1485.976 {"length_x": 300} {"in_o1": {"name": "in_o1", "center": [145.02,... {"length_x":300} mzm [fsr] [] optical_spectrum NaN 6 2
6 mzm_gc_LX100 250.020 1752.526 {"length_x": 100} {"in_o1": {"name": "in_o1", "center": [145.02,... {"length_x":100} mzm [fsr] [] optical_spectrum NaN 6 2
7 mzm_gc_LX200 200.020 2019.076 {"length_x": 200} {"in_o1": {"name": "in_o1", "center": [145.02,... {"length_x":200} mzm [fsr] [] optical_spectrum NaN 6 2
8 mzm_gc_LX300 820.960 153.225 {"length_x": 300} {"in_o1": {"name": "in_o1", "center": [815.96,... {"length_x":300} mzm [fsr] [] optical_spectrum NaN 6 2
9 ring_10 884.460 367.151 {"length_x": 10} {"o1": {"name": "o1", "center": [815.96, 300.6... {} ring_length_x [fsr] [] optical_spectrum NaN 4 2
10 ring_20 889.460 707.151 {"length_x": 20} {"o1": {"name": "o1", "center": [815.96, 640.6... {} ring_length_x [fsr] [] optical_spectrum NaN 4 2
11 ring_30 894.460 1047.151 {"length_x": 30} {"o1": {"name": "o1", "center": [815.96, 980.6... {} ring_length_x [fsr] [] optical_spectrum NaN 4 2
12 ring_10 884.460 1387.151 {"length_x": 10} {"o1": {"name": "o1", "center": [815.96, 1320.... {} ring_length_x [fsr] [] optical_spectrum NaN 4 2
13 ring_20 889.460 1727.151 {"length_x": 20} {"o1": {"name": "o1", "center": [815.96, 1660.... {} ring_length_x [fsr] [] optical_spectrum NaN 4 2
14 ring_30 1311.400 367.151 {"length_x": 30} {"o1": {"name": "o1", "center": [1232.9, 300.6... {} ring_length_x [fsr] [] optical_spectrum NaN 4 2
15 ring_10 1301.400 707.151 {"length_x": 10} {"o1": {"name": "o1", "center": [1232.9, 640.6... {} ring_length_x [fsr] [] optical_spectrum NaN 4 2
16 ring_20 1306.400 1047.151 {"length_x": 20} {"o1": {"name": "o1", "center": [1232.9, 980.6... {} ring_length_x [fsr] [] optical_spectrum NaN 4 2
17 ring_30 1311.400 1387.151 {"length_x": 30} {"o1": {"name": "o1", "center": [1232.9, 1320.... {} ring_length_x [fsr] [] optical_spectrum NaN 4 2
18 spiral_gc_L200 1395.179 140.351 {"length": 200} {"o1": {"name": "o1", "center": [1551.179, 34.... {"length":200} spirals_sc [power_envelope] [] optical_spectrum NaN 4 0
19 spiral_gc_L200 1141.179 1766.901 {"length": 200} {"o1": {"name": "o1", "center": [1297.179, 166... {"length":200} spirals_sc [power_envelope] [] optical_spectrum NaN 4 0
20 spiral_gc_L200 1141.179 1950.451 {"length": 200} {"o1": {"name": "o1", "center": [1297.179, 184... {"length":200} spirals_sc [power_envelope] [] optical_spectrum NaN 4 0
21 spiral_gc_L100 759.960 2106.901 {"length": 100} {"o1": {"name": "o1", "center": [815.96, 2000.... {"length":100} spirals_sc [power_envelope] [] optical_spectrum NaN 4 0
22 spiral_gc_L100 1593.840 323.901 {"length": 100} {"o1": {"name": "o1", "center": [1649.84000000... {"length":100} spirals_sc [power_envelope] [] optical_spectrum NaN 4 0
23 spiral_gc_L100 1593.840 507.451 {"length": 100} {"o1": {"name": "o1", "center": [1649.84000000... {"length":100} spirals_sc [power_envelope] [] optical_spectrum NaN 4 0
24 spiral_gc_L0 1693.840 680.751 {"length": 0} {"o1": {"name": "o1", "center": [1649.84000000... {"length":0} spirals_sc [power_envelope] [] optical_spectrum NaN 4 0
25 spiral_gc_L0 1693.840 854.051 {"length": 0} {"o1": {"name": "o1", "center": [1649.84000000... {"length":0} spirals_sc [power_envelope] [] optical_spectrum NaN 4 0
26 spiral_gc_L0 1693.840 1027.351 {"length": 0} {"o1": {"name": "o1", "center": [1649.84000000... {"length":0} spirals_sc [power_envelope] [] optical_spectrum NaN 4 0