Add-Drop Filter Layout

e7c2b568c6884e11923bc65f8f11cb74

This notebook demonstrates how to create a basic add-drop filter layout using PhotonForge. The goal is to focus on layout generation without involving simulation or technology setup.

PhotonForge is a powerful tool for creating photonic layouts. This example will guide you through the process of creating an add-drop filter with one input grating and three output gratings (through, drop, and isolation ports).

Import Libraries

We start by importing the necessary PhotonForge library. We also use basic technology, although it is not necessary to use a technology when you aim to only create a layout.

It is also good (but not necessary) to start a LiveViewer instance at the top of the notebook and use it whenever we create a structure or change a component.

[1]:
import photonforge as pf
from photonforge.live_viewer import LiveViewer

pf.config.default_technology = pf.basic_technology()
viewer = LiveViewer()
LiveViewer started at http://localhost:34485

Define Design Parameters

Before building the layout, we define the key design parameters that control the geometry and spacing of the components:

  • Grating Distance – Distance between the starting points of the input and output gratings.

  • Output Grating Spacing – Vertical distance between the output gratings.

  • Waveguide Width – Width of the optical waveguide.

  • Ring Radius – Radius of the ring resonator.

  • Gap – Distance between the bus waveguide and the ring resonator.

  • Grating Taper Length – Length of the taper section of the grating.

These parameters will allow us to control the size and alignment of the layout.

[2]:
# design parameters
grating_distance = 200
output_grating_spacing = 50
waveguide_width = 1.0
ring_radius = 10
gap = 1.0
grating_taper_length = 30

Create Main Component

We create a main component that will serve as the container for the entire layout. All individual elements (gratings, waveguides, and the ring) will be added to this main component.

The Component object in PhotonForge allows us to group multiple elements into a single design.

[3]:
main = pf.Component("ADD_DROP_FILTER")

Create Grating Structure

Next, we create a grating structure that will be used for both the input and output ports.

The stencil.grating function generates a grating structure with a defined period, number of periods, and taper. Tapering is important to reduce optical losses when coupling light into the waveguides. The output is a list of sructures that we need to add them to the grating component. All structures are placed on layer (1, 0), which corresponds to the GDS layer and datatype. Assigning structures to a specific layer is essential for exporting the design to a GDS file.

[4]:
grating = pf.Component("GRATING")
grating_structure = pf.stencil.grating(
    period=0.6,
    num_periods=20,
    width=10,
    taper_length=grating_taper_length,
    taper_width=waveguide_width,
)

# add grating structure to grating component
grating.add((1, 0), *grating_structure)
viewer(grating)
[4]:
../_images/examples_Add-Drop_Filter_Layout_7_0.svg

Place Input and Output Gratings

With the grating structure prepared, we now position the gratings at specified locations within the main component:

  • The input grating is placed at the origin and rotated by 180 degrees.

  • The through-port grating is horizontally aligned with the input grating.

  • The drop-port and isolation-port gratings are placed vertically above the through-port at defined distances.

This configuration ensures proper alignment of optical paths for efficient coupling to fiber arrays.

We utilize the Reference class to create a Reference to the previously defined grating Component. This approach allows efficient reuse of the same component at multiple locations within the layout.

[5]:
input_grating = pf.Reference(grating, (0, 0), rotation=180)
output_grating_through = pf.Reference(
    grating, (grating_distance - 2 * grating_taper_length, 0)
)
output_grating_isolation = pf.Reference(
    grating, (grating_distance - 2 * grating_taper_length, output_grating_spacing)
)
output_grating_drop = pf.Reference(
    grating, (grating_distance - 2 * grating_taper_length, 2 * output_grating_spacing)
)

main.add(
    (1, 0),
    input_grating,
    output_grating_through,
    output_grating_isolation,
    output_grating_drop,
)
[5]:
../_images/examples_Add-Drop_Filter_Layout_9_0.svg

Create Bus Waveguide

The bus waveguide connects the input grating to the through port.

  • We extract the bounds of the input and through port gratings to define the start and end points of the waveguide.

  • The Path.segment function creates a straight waveguide between two points.

This forms the main optical path for light propagation:

[6]:
(min_x_in, min_y_in), (max_x_in, max_y_in) = input_grating.bounds()
(min_x_out1, min_y_out1), (max_x_out1, max_y_out1) = output_grating_through.bounds()

bus_waveguide = pf.Path(
    origin=(max_x_in, (max_y_in + min_y_in) / 2), width=waveguide_width
).segment((min_x_out1, (max_y_out1 + min_y_out1) / 2))

viewer(bus_waveguide)
[6]:
../_images/examples_Add-Drop_Filter_Layout_11_0.svg

Create Ring Resonator

We now create the ring resonator:

  • The ring is centered along the bus waveguide.

  • The Circle function defines the ring with appropriately identifying radius and inner_radius.

[7]:
x_ring = (max_x_in + min_x_out1) / 2
y_ring = (max_y_in + min_y_in) / 2 + gap + waveguide_width

ring_resonator = pf.Circle(
    radius=ring_radius + waveguide_width / 2,
    inner_radius=ring_radius - waveguide_width / 2,
    center=(x_ring, y_ring + ring_radius),
)

viewer(ring_resonator)
[7]:
../_images/examples_Add-Drop_Filter_Layout_13_0.svg

Create Isolation-Drop Path

We now define the path that couples light from the ring resonator to the isolation and drop ports:

  • A series of straight segments and bends route the waveguide through the layout.

  • The s_bend function creates smooth transitions to minimize optical loss.

[8]:
(min_x_out3, min_y_out3), (max_x_out3, max_y_out3) = output_grating_drop.bounds()
(min_x_out2, min_y_out2), (max_x_out2, max_y_out2) = output_grating_isolation.bounds()

isolation_drop_path = (
    pf.Path(origin=(min_x_out3, (max_y_out3 + min_y_out3) / 2), width=waveguide_width)
    .segment((max_x_in, (max_y_out3 + min_y_out3) / 2))
    .arc(90, 270, output_grating_spacing / 2)
    .s_bend((x_ring - ring_radius, y_ring + 2 * ring_radius + gap + waveguide_width))
    .segment((x_ring + ring_radius, y_ring + 2 * ring_radius + gap + waveguide_width))
    .s_bend((min_x_out2, (max_y_out2 + min_y_out2) / 2))
)

viewer(isolation_drop_path)
[8]:
../_images/examples_Add-Drop_Filter_Layout_15_0.svg

Export final GDS file

Add the bus waveguide, ring resonator, and isolation-drop path to the main component and export the layout to a GDS file.

[9]:
main.add((1, 0), bus_waveguide, ring_resonator, isolation_drop_path)
main.write_gds("add_drop_filter.gds")
[9]:
../_images/examples_Add-Drop_Filter_Layout_17_0.svg