Getting Started

SPLayout aims to expedite the layout design process in Silicon Photonics. While some flexibility is sacrificed compared to its dependency gdspy, most operations are simplified. The fundamental principle behind SPLayout is to facilitate swift connections between components based on the center points of their ports. All structures are designed to be functional for both layout generation and simulation verification.

First GDSII

Let’s create a gdsii file with a basic waveguide.

from splayout import *

# define cell 
cell = Cell("waveguide")
# define layer
wg_layer = Layer(1,0)

# start point and end point for the waveguide
wg_start_point = Point(0,0)
wg_end_point = wg_start_point + (10,0)
# make a waveguide
wg = Waveguide(wg_start_point,wg_end_point,width=0.5)
# draw the waveguide on the layout
wg.draw(cell,wg_layer)

# create file and save
make_gdsii_file("waveguide.gds")

Firstly, we create a cell named “waveguide”. We define a waveguide from Point(0,0) to Point(10,0) with 0.5μm width and draw it on the “1/0” layer. Finally, we generate the file “waveguide.gds” in the same folder with the python file.

We can check the gdsii file “waveguide.gds” with some gdsii editors such as KLayout.

First FDTD Simulation

Create a simulation for a waveguide.

from splayout import *

# initialize the simulation frame
fdtd = FDTDSimulation(fdtd_path="C:\\Program Files\\Lumerical\\v202\\api\\python")

# draw waveguides on Lumerical
waveguide = Waveguide(start_point=Point(-3,0), end_point=Point(3,0), width=1, z_start=-0.11, z_end=0.11, material=Si)
waveguide.draw_on_lumerical_CAD(fdtd)

# add simulation region, source, and monitor
fdtd.add_fdtd_region(bottom_left_corner_point=Point(-2, -1.5), top_right_corner_point=Point(2, 1.5), background_index=1.444,dimension=3, height=0.8)

fdtd.add_mode_source(position=Point(-1.5,0), width=1.5, height=0.8, wavelength_start=1.54, wavelength_end=1.57, mode_number=1)

fdtd.add_mode_expansion(position=Point(1.5, 0), width=1.5, height=0.8, points=101, mode_list=[1])

# run simulation
fdtd.run("./temp")

# get result return: (number of modes, 2, frequency points)
# where (number of modes, 0, frequency points) are wavelengths,
#       (number of modes, 1, frequency points) are transmissions
transmission = fdtd.get_mode_transmission(expansion_name="expansion")

# plot figure
plt.figure()
plt.plot(transmission[0, 0, :]*1e9, transmission[0, 1, :])
plt.xlabel("Wavelength(nm)")
plt.ylabel("Transmission")
plt.show()

Components

These can be found in example/basic.py .

Waveguide

# start point and end point for the waveguide
wg_start_point = Point(0,0)
wg_end_point = wg_start_point + (10,0)
# make a waveguide
wg = Waveguide(wg_start_point,wg_end_point,width=0.5)
# draw the waveguide on the layout
wg.draw(cell,wg_layer)

_images/waveguide.png

Taper

# start point and end point for the taper
tp_start_point = Point(30,0)
tp_end_point = tp_start_point + (0,5)
# make a taper
tp = Taper(tp_start_point,tp_end_point,start_width=0.5,end_width=1)
# draw the taper on the layout
tp.draw(cell,wg_layer)

_images/taper.png

Bend

# center point and angle for the bend
center_point = Point(60,0)
start_angle = math.pi*0
end_angle = math.pi*3/5
width = 0.5
radius = 5
# make a bend
first_bend = Bend(center_point, start_angle, end_angle, width , radius)
# draw the bend on the layout
first_bend.draw(cell,wg_layer)

_images/bend.png

QuarBend

# start point and end point for the quarbend
start_point = Point(90,0)
end_point = start_point + (-7,20)
# make a quarbend
first_QuarBend = QuarBend(start_point,end_point,width=0.5)
# draw the quarbend on the layout
first_QuarBend.draw(cell,wg_layer)

_images/quarbend.png

AQuarBend

# start point and end point for the anti-clockwise quarbend
start_point = Point(0,-30)
end_point = start_point + (-7,20)
# make the anti-clockwise quarbend
first_AQuarBend = AQuarBend(start_point,end_point,width=0.5)
# draw the anti-clockwise quarbend on the layout
first_AQuarBend.draw(cell,wg_layer)

_images/aqarben.png

SBend (Without Length Specified)

# start point and end point for the clockwise SBend
start_point = Point(150,0)
end_point = start_point + (5,1)
# make the clockwise SBend
first_SBend = SBend(start_point,end_point,width=0.5)
# draw the clockwise SBend on the layout
first_SBend.draw(cell,wg_layer)

_images/sbend1.png

ASBend (Without Length Specified)

# start point and end point for the anti-clockwise SBend
start_point = Point(180,0)
end_point = start_point + (5,1)
# make the anti-clockwise SBend
first_ASBend = ASBend(start_point,end_point,width=0.5)
# draw the anti-clockwise SBend on the layout
first_ASBend.draw(cell,wg_layer)

_images/asbend1.png

SBend (With Length Specified)

# start point and end point and length for the clockwise SBend
start_point = Point(210,0)
end_point = start_point + (5,1)
length = 10
# make the clockwise SBend with length specified
second_SBend = SBend(start_point,end_point,width=0.5,length=length)
# draw the clockwise SBend on the layout
second_SBend.draw(cell,wg_layer)

_images/sbend2.png

ASBend (With Length Specified)

# start point and end point and length for the anti-clockwise SBend
start_point = Point(0,-30)
end_point = start_point + (5,1)
length = 10
# make the anti-clockwise SBend with length specified
second_ASBend = ASBend(start_point,end_point,width=0.5, length=length)
# draw the anti-clockwise SBend on the layout
second_ASBend.draw(cell,wg_layer)

_images/asbend2.png

Polygon

# points for the polygon
pointlist = [Point(30,-30) ,Point(30,-25),Point(37,-20),Point(33,-27),Point(32,-28),Point(31,-29)] ## or [(30,-30),(30,-25),(37,-20),(33,-27),(32,-28),(31,-29)]
# make the polygon
polygon = Polygon(pointlist)
# draw the polygon on the layout
polygon.draw(cell,wg_layer)

_images/polygon.png

DoubleBendConnector

# start point and end point for the doubleconnector
double_connect_start_point = Point(60,-30)
double_connect_end_point = double_connect_start_point + (20,10)
# make the doubleconnector
connector = DoubleBendConnector(double_connect_start_point, double_connect_end_point, width=0.5)
# draw the doubleconnector on the layout
connector.draw(cell,wg_layer)

_images/doubleconnector.png

AddDropMicroring

# start point(input point) for the microring, and radius, gap, waveguide width, coupling length
start_point = Point(50,40)
radius = 5.1973
gap = 0.18
wg_width = 0.45
coupling_length = 5.5
# make the add drop microring
first_ring = AddDropMicroring(start_point,radius,gap,wg_width,coupling_length)
# drawe the microring on the layout
first_ring.draw(cell,wg_layer)
# add heater for the microring
first_ring.add_heater(cell, heater_layer, contact=1, contact_layer=contact_layer)

_images/adddropmicroring.png

AddDropMicroringFlat

start_point = Point(50,-300)
radius = 5.1973
gap = 0.18
wg_width = 0.45
coupling_length = 5.5
# make the add drop microring
second_ring = AddDropMicroringFlat(start_point,radius,gap,wg_width,coupling_length)
# draw the microring on the layout
second_ring.draw(cell,wg_layer)
# add heater for the microring
second_ring.add_heater(cell, heater_layer, contact=1, contact_layer=contact_layer)

_images/adddropmicroringflat.png

Text

# start point for the text
text_start_point = Point(0,-60)
# make the text
text = Text(text_start_point,"OTIP2021")
# draw the text on the layout
text.draw(cell,wg_layer)

_images/text.png

Circle

# center point and radius for the circle
center_point = Point(30,-90)
radius = 5
# make the circle
circle = Circle(center_point,radius = radius)
# draw the circle on the layout
circle.draw(cell,wg_layer)

_images/circle.png

Rectangle

# center point and width and height for the rectangle
center_point = Point(60,-90)
width = 5
height = 7
# make the rectangle
rectangle = Rectangle(center_point,width = width, height = height)
# draw the rectangle on the layout
rectangle.draw(cell,wg_layer)

_images/rectangle.png

Functions for Self-define Components

AEMD Grating

We can get a “Class” from the function “MAKE_AEMD_GRATING” that can be used to define an AEMD grating. More details can be found in “API Reference”.

# get a AEMD grating definition
AEMDgrating = MAKE_AEMD_GRATING(port_width=0.5)
# start point for the AEMD grating
grating_point = Point(90,-30)
# make the AEMD grating
right_grating = AEMDgrating(grating_point,RIGHT)
# draw the AEMD grating on the layout
right_grating.draw(cell)

_images/aemdgrating.png

Customize Components

We can get a “Class” from the function “MAKE_COMPONENT” that can be used to define our own sub cell from another gdsii file. More details can be found in “API Reference”.

# take the "selfdefine.gds" as an example
SelfDefineComponent = MAKE_COMPONENT("selfdefine.gds")
# start point for the component
start_point = Point(0,-90)
# make the component
component = SelfDefineComponent(start_point,RIGHT)
# draw the component on the layout
component.draw(cell)

_images/selfdefine.png

Interconnect Example

All the components have functions for returning their port points to simplify the interconnecting operations.

# first, a waveguide
waveguide = Waveguide(Point(0,-350),Point(10,-350),width=0.5)
waveguide.draw(cell,wg_layer)
# second, a double connector
doubleconnector = DoubleBendConnector(waveguide.get_end_point(),waveguide.get_end_point()+(10,-10),width=0.5)
doubleconnector.draw(cell,wg_layer)
# third, add grating at the end of the double connector
rightgrating = AEMDgrating(doubleconnector.get_end_point(),RIGHT)
rightgrating.draw(cell)
# fourth, add grating at the start of the waveguide
leftgrating = AEMDgrating(waveguide.get_start_point(), LEFT)
leftgrating.draw(cell)

_images/interconnect.png

Make File and Generate Specifical Layer

make_gdsii_file

Make gdsii file based on all the drawn component before the function is called. It also can generate some specifical layers (like: cover layer, inverse layer).

# create file and save
make_gdsii_file("basic.gds")
basic
# create file and save the layout with an inverse layer
make_gdsii_file("basic_inverse.gds",inv_source_layer=wg_layer,inv_target_layer=inv_layer)
basic_inverse
# create file and save the layout with inverse layer and cover layer
make_gdsii_file("basic_inverse_and_cover.gds",inv_source_layer=wg_layer,inv_target_layer=inv_layer,cover_source_layer=wg_layer,cover_target_layer=cover_layer)
basic_inverse_and_cover

Boolean operations

Boolean operations can be made for layers.

from splayout import *

# prepare the initial pattern
cell = Cell("Boolean")
layer1 = Layer(1, 0)
layer2 = Layer(2, 0)
result_layer = Layer(31,0)

circle1 = Circle(Point(-1, 0), radius=2)
circle1.draw(cell, layer1)
circle2 = Circle(Point(1, 0), radius=2)
circle2.draw(cell, layer2)

make_gdsii_file("boolean_before.gds")

_images/boolean_before.png

# cut operation
layer1.cut(layer2, output_layer=result_layer)
make_gdsii_file("boolean_cut.gds")

_images/boolean_cut.png

# add operation
layer1.add(layer2, output_layer=result_layer)
make_gdsii_file("boolean_add.gds")

_images/boolean_add.png

# common operation
layer1.common(layer2, output_layer=result_layer)
make_gdsii_file("boolean_common.gds")

_images/boolean_common.png

# dilation operation
layer1.dilation(distance=2, output_layer=result_layer)
make_gdsii_file("boolean_dilation.gds")

_images/boolean_dilation.png

# inversion operation
layer1.inversion(distance=2, output_layer=result_layer)
make_gdsii_file("boolean_inversion.gds")

_images/boolean_inversion.png

Examples

There are two examples for a quick start.

AEMD design

CUMEC design