Skip to content

Commit

Permalink
Modify waveguide routing so Euler bends can be chosen
Browse files Browse the repository at this point in the history
  • Loading branch information
DerekK44 committed Jul 21, 2019
1 parent 8a3fdb3 commit 28d26e4
Showing 1 changed file with 96 additions and 43 deletions.
139 changes: 96 additions & 43 deletions picwriter/components/waveguide.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import numpy as np
import gdspy
import picwriter.toolkit as tk
from picwriter.components.ebend import EBend

class WaveguideTemplate:
""" Template for waveguides that contains standard information about the geometry and fabrication. Supported waveguide types are **strip** (also known as 'channel' waveguides), **slot**, and **SWG** ('sub-wavelength grating', or 1D photonic crystal waveguides).
Expand All @@ -13,6 +14,7 @@ class WaveguideTemplate:
* **bend_radius** (float): Radius of curvature for waveguide bends (circular). Defaults to 50.
* **waveguide_stack** (list): List of layers and path widths to be drawn when waveguides are routed & placed. Format is '[[width1, (layer1, datatype1)], [width2, (layer2, datatype2)], ...]'. The first element defines the main waveguide width & layer for slot and subwavelength gratings. If using waveguide_stack, the following keyword arguments are ignored: wg_width, clad_width, wg_layer, wg_datatype, clad_layer, clad_datatype. Defaults to [[2.0, (1,0)], [10.0, (2,0)]].
* **wg_width** (float): Width of the waveguide as shown on the mask. Defaults to 2.
* **euler_bend** (boolean): If `True`, uses Euler bends to route waveguides. Defaults to `False`. Currently only works with slot and strip waveguides. The given `bend_radius` value determines the **smallest** bend radius along the entire Euler curve.
* **slot** (float): Size of the waveguide slot region. This is only used if `wg_type`=`'slot'`. Defaults to 0.1.
* **period** (float): Period of the SWG. This is only used if `wg_type`=`'swg'`. Defaults to 0.1.
* **duty_cycle** (float): Duty cycle of the SWG. This is only used if `wg_type`=`'swg'`. Defaults to 0.5.
Expand All @@ -30,7 +32,8 @@ def __init__(self, wg_type='strip', bend_radius=50.0,
waveguide_stack = None,
wg_width=2.0, clad_width=10.0, grid=0.001,
resist='+', fab='ETCH', slot=0.1, period=0.1, duty_cycle=0.5,
wg_layer=1, wg_datatype=0, clad_layer=2, clad_datatype=0):
wg_layer=1, wg_datatype=0, clad_layer=2, clad_datatype=0,
euler_bend=False):
self.name = tk.getCellName("WaveguideTemplate") #Each WaveguideTemplate is given a unique name

if waveguide_stack==None:
Expand Down Expand Up @@ -74,6 +77,7 @@ def __init__(self, wg_type='strip', bend_radius=50.0,
self.resist = '+' if resist=='-' else '-'

self.grid = grid
self.euler = euler_bend

if self.wg_type =='swg':
self.straight_period_cell = gdspy.Cell("swg_seg_"+str(self.wg_width)+"_"+str(self.period)+"_"+str(self.duty_cycle)+"_"+str(self.wg_layer)+"_"+str(self.clad_layer))
Expand Down Expand Up @@ -318,78 +322,127 @@ def __build_cell(self):
first_path.arc(br, curr_angle+angle_change, curr_angle, **self.wg_spec)
self.add(first_path)
remaining_period = remaining_period - br*abs(angle_change)

# add cladding
for i in range(len(self.wgt.waveguide_stack)-1):
cur_width = self.wgt.waveguide_stack[i+1][0]
cur_spec = {'layer': self.wgt.waveguide_stack[i+1][1][0], 'datatype': self.wgt.waveguide_stack[i+1][1][1]}
if len(self.trace)==2:
path2 = gdspy.Path(cur_width, self.trace[0])
path2.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **cur_spec)
else:
path2 = gdspy.Path(cur_width, self.trace[0])
prev_dl = 0.0
for i in range(len(self.trace)-2):
start_angle = tk.get_exact_angle(self.trace[i], self.trace[i+1])
next_angle = tk.get_exact_angle(self.trace[i+1], self.trace[i+2])

#dl is the amount of distance that is taken *off* the waveguide from the curved section
dl = abs(br*np.tan((next_angle-start_angle)/2.0))
if (dl+prev_dl) > tk.dist(self.trace[i], self.trace[i+1])+1E-6:
raise ValueError("Warning! The waypoints "+str(self.trace[i])+" and "+str(self.trace[i+1])+" are too close to accommodate "
" the necessary bend-radius of "+str(br)+", the points were closer than "+str(dl+prev_dl))

path2.segment(tk.dist(self.trace[i], self.trace[i+1])-dl-prev_dl,
direction=start_angle, **cur_spec)

turnby = tk.normalize_angle(next_angle - start_angle)

path2.turn(br, turnby, number_of_points=self.wgt.get_num_points_wg(turnby), **cur_spec)
prev_dl = dl

path2.segment(tk.dist(self.trace[-2], self.trace[-1])-prev_dl,
direction=next_angle, **cur_spec)
self.add(path2)

else:
# Strip and slot waveguide generation below
""" Strip and slot waveguide generation below
"""
if len(self.trace)==2:
if self.wgt.wg_type=='strip':
path = gdspy.Path(self.wgt.wg_width, self.trace[0])
path.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **self.wg_spec)
elif self.wgt.wg_type=='slot':
path = gdspy.Path(self.wgt.rail, self.trace[0], number_of_paths=2, distance=self.wgt.rail_dist)
path.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **self.wg_spec)

clad_path_list = []
for c in range(len(self.wgt.waveguide_stack)-1):
cur_width = self.wgt.waveguide_stack[c+1][0]
cur_spec = {'layer': self.wgt.waveguide_stack[c+1][1][0], 'datatype': self.wgt.waveguide_stack[c+1][1][1]}
cp = gdspy.Path(cur_width, self.trace[0])
cp.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **cur_spec)
clad_path_list.append(cp)

else:
if self.wgt.wg_type=='strip':
path = gdspy.Path(self.wgt.wg_width, self.trace[0])
elif self.wgt.wg_type=='slot':
path = gdspy.Path(self.wgt.rail, self.trace[0], number_of_paths=2, distance=self.wgt.rail_dist)

clad_path_list = []
for c in range(len(self.wgt.waveguide_stack)-1):
clad_path_list.append(gdspy.Path(self.wgt.waveguide_stack[c+1][0], self.trace[0]))

prev_dl = 0.0
for i in range(len(self.trace)-2):
start_angle = tk.get_exact_angle(self.trace[i], self.trace[i+1])
next_angle = tk.get_exact_angle(self.trace[i+1], self.trace[i+2])
# The following makes sure the turn-by angle is *always* between -pi and +pi
turnby = tk.normalize_angle(next_angle - start_angle)

#dl is the amount of distance that is taken *off* the waveguide from the curved section
dl = abs(br*np.tan((next_angle-start_angle)/2.0))
if self.wgt.euler == False:
dl = abs(br*np.tan((next_angle-start_angle)/2.0))
else:
# Generate next Euler bend ahead of time
ebend = EBend(self.wgt, turnby, direction=start_angle, vertex=self.trace[i+1])
dl = ebend.dist_to_vertex
self.add(ebend)

if (dl+prev_dl) > tk.dist(self.trace[i], self.trace[i+1])+1E-6:
raise ValueError("Warning! The waypoints "+str(self.trace[i])+" and "+str(self.trace[i+1])+" are too close to accommodate "
" the necessary bend-radius of "+str(br)+", the points were closer than "+str(dl+prev_dl))

path.segment(tk.dist(self.trace[i], self.trace[i+1])-dl-prev_dl,
direction=start_angle, **self.wg_spec)

# The following makes sure the turn-by angle is *always* between -pi and +pi
turnby = tk.normalize_angle(next_angle - start_angle)

path.turn(br, turnby, number_of_points=self.wgt.get_num_points_wg(turnby), **self.wg_spec)

for c in range(len(self.wgt.waveguide_stack)-1):
cur_spec = {'layer': self.wgt.waveguide_stack[c+1][1][0], 'datatype': self.wgt.waveguide_stack[c+1][1][1]}
clad_path_list[c].segment(tk.dist(self.trace[i], self.trace[i+1])-dl-prev_dl,
direction=start_angle, **cur_spec)

if self.wgt.euler==False:
path.turn(br, turnby, number_of_points=self.wgt.get_num_points_wg(turnby), **self.wg_spec)
for c in range(len(self.wgt.waveguide_stack)-1):
cur_spec = {'layer': self.wgt.waveguide_stack[c+1][1][0], 'datatype': self.wgt.waveguide_stack[c+1][1][1]}
clad_path_list[c].turn(br, turnby, number_of_points=self.wgt.get_num_points_wg(turnby), **cur_spec)
else:
self.add(path)
for cpath in clad_path_list:
self.add(cpath)

if self.wgt.wg_type=='strip':
path = gdspy.Path(self.wgt.wg_width, ebend.portlist["output"]["port"])
elif self.wgt.wg_type=='slot':
path = gdspy.Path(self.wgt.rail, ebend.portlist["output"]["port"], number_of_paths=2, distance=self.wgt.rail_dist)

for c in range(len(self.wgt.waveguide_stack)-1):
clad_path_list[c] = gdspy.Path(self.wgt.waveguide_stack[c+1][0], ebend.portlist["output"]["port"])

prev_dl = dl

# Add on the final segment
path.segment(tk.dist(self.trace[-2], self.trace[-1])-prev_dl,
direction=next_angle, **self.wg_spec)
for c in range(len(self.wgt.waveguide_stack)-1):
cur_spec = {'layer': self.wgt.waveguide_stack[c+1][1][0], 'datatype': self.wgt.waveguide_stack[c+1][1][1]}
clad_path_list[c].segment(tk.dist(self.trace[-2], self.trace[-1])-prev_dl,
direction=next_angle, **cur_spec)

self.add(path)

# add cladding
for i in range(len(self.wgt.waveguide_stack)-1):
cur_width = self.wgt.waveguide_stack[i+1][0]
cur_spec = {'layer': self.wgt.waveguide_stack[i+1][1][0], 'datatype': self.wgt.waveguide_stack[i+1][1][1]}
if len(self.trace)==2:
path2 = gdspy.Path(cur_width, self.trace[0])
path2.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **cur_spec)
else:
path2 = gdspy.Path(cur_width, self.trace[0])
prev_dl = 0.0
for i in range(len(self.trace)-2):
start_angle = tk.get_exact_angle(self.trace[i], self.trace[i+1])
next_angle = tk.get_exact_angle(self.trace[i+1], self.trace[i+2])

#dl is the amount of distance that is taken *off* the waveguide from the curved section
dl = abs(br*np.tan((next_angle-start_angle)/2.0))
if (dl+prev_dl) > tk.dist(self.trace[i], self.trace[i+1])+1E-6:
raise ValueError("Warning! The waypoints "+str(self.trace[i])+" and "+str(self.trace[i+1])+" are too close to accommodate "
" the necessary bend-radius of "+str(br)+", the points were closer than "+str(dl+prev_dl))

path2.segment(tk.dist(self.trace[i], self.trace[i+1])-dl-prev_dl,
direction=start_angle, **cur_spec)

turnby = tk.normalize_angle(next_angle - start_angle)

path2.turn(br, turnby, number_of_points=self.wgt.get_num_points_wg(turnby), **cur_spec)
prev_dl = dl

path2.segment(tk.dist(self.trace[-2], self.trace[-1])-prev_dl,
direction=next_angle, **cur_spec)
self.add(path2)
for cpath in clad_path_list:
self.add(cpath)


def __build_ports(self):
Expand All @@ -403,14 +456,14 @@ def __build_ports(self):
if __name__ == "__main__":
gdspy.current_library = gdspy.GdsLibrary()
top = gdspy.Cell("top")
wgt1= WaveguideTemplate(wg_type='strip', wg_width=1.0, bend_radius=25, resist='+', fab="ETCH")
# wgt1= WaveguideTemplate(wg_type='strip', wg_width=1.0, bend_radius=25, resist='+', euler_bend=True)
wgt2= WaveguideTemplate(wg_type='slot', wg_width=1.0, bend_radius=25, slot=0.3, resist='+', fab="ETCH")
wgt3= WaveguideTemplate(wg_type='swg', wg_width=1.0, bend_radius=25, duty_cycle=0.50, period=1.0, resist='+', fab="ETCH")
wg_stack = [[0.5, (1,0)], [2.0, (2,0)], [10, (4,0)]]
wgt1= WaveguideTemplate(wg_type='strip', bend_radius=25, waveguide_stack=wg_stack, resist='+', fab="ETCH")
wgt1= WaveguideTemplate(wg_type='slot', wg_width=1.0, bend_radius=25, slot=0.3, waveguide_stack=wg_stack, resist='+', euler_bend=True)

space = 10.0
wg1=Waveguide([(0, 0), (140.0-space, 0), (160.0-space, 50.0), (300.0, 50.0)], wgt1)
wg1=Waveguide([(0, 0), (140.0-space, 0), (160.0-space, 100.0), (300.0, 100.0), (400, 150.0), (200, -300), (-500, 100), (-500, -200)], wgt1)
tk.add(top, wg1)
wg2=Waveguide([(0, -space), (140.0, -space), (160.0, 50.0-space), (300.0, 50.0-space)], wgt2)
tk.add(top, wg2)
Expand Down

0 comments on commit 28d26e4

Please sign in to comment.