Skip to content

Commit 863e8c1

Browse files
committed
Added a strip-slot-MMI coupler class
1 parent 6bc7d17 commit 863e8c1

File tree

9 files changed

+641
-10
lines changed

9 files changed

+641
-10
lines changed

docs/source/components/couplers.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ Below is a standard taper class that can be used to generate inverse-tapered wav
1717
Strip-to-slot couplers
1818
============================
1919

20-
Provided below is a standard Y-junction strip-to-slot waveguide coupler. For more information on this, please see the original paper at https://doi.org/10.1364/OL.34.001498.
20+
.. automodule:: picwriter.components
21+
:members: StripSlotMMICoupler
22+
23+
.. image:: imgs/stripslotmmicoupler.png
2124

2225
.. automodule:: picwriter.components
2326
:members: StripSlotYCoupler
15.2 KB
Loading

docs/source/components/imgs/stripslotmmicoupler.svg

Lines changed: 493 additions & 0 deletions
Loading

picwriter/components/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@
1616
from .directionalcoupler import DirectionalCoupler
1717
from .contradc import ContraDirectionalCoupler
1818
from .swgcontradc import SWGContraDirectionalCoupler
19-
from .stripslotcoupler import StripSlotYCoupler
19+
from .stripslotycoupler import StripSlotYCoupler
20+
from .stripslotmmicoupler import StripSlotMMICoupler
2021
from .broadbanddc import BroadbandDirectionalCoupler

picwriter/components/ring.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ def __init__(self, wgt, radius, coupling_gap, wrap_angle=0, parity=1, draw_bus_w
3838
self.portlist = {}
3939

4040
self.port = port
41-
# self.trace=[port, tk.translate_point(port, 2*radius, direction)]
4241
self.direction = direction
4342

4443
self.radius = radius
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from __future__ import absolute_import, division, print_function, unicode_literals
4+
import numpy as np
5+
import gdspy
6+
import uuid
7+
import picwriter.toolkit as tk
8+
9+
class StripSlotMMICoupler(gdspy.Cell):
10+
""" Strip-to-Slot MMI Coupler Cell class (subclass of gdspy.Cell). For more information on this specific type of strip to slot mode converter, please see the original papers at https://doi.org/10.1364/OL.39.005665 and https://doi.org/10.1364/OE.24.007347.
11+
12+
Args:
13+
* **wgt_input** (WaveguideTemplate): WaveguideTemplate object for the input waveguide (should be either of type `strip` or `slot`).
14+
* **wgt_output** (WaveguideTemplate): WaveguideTemplate object for the output waveguide (should be either of type `strip` or `slot`, opposite of the input type).
15+
* **w_mmi** (float): Width of the MMI region.
16+
* **l_mmi** (float): Length of the MMI region.
17+
* **length** (float): Length of the entire mode converter (MMI region + tapered region on slot waveguide side).
18+
19+
Keyword Args:
20+
* **input_strip** (Boolean): If `True`, sets the input port to be the strip waveguide side. If `False`, slot waveguide is on the input. Defaults to `None`, in which case the input port waveguide template is used to choose.
21+
* **port** (tuple): Cartesian coordinate of the input port. Defaults to (0,0).
22+
* **direction** (string): Direction that the component will point *towards*, can be of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians)
23+
24+
Members:
25+
* **portlist** (dict): Dictionary with the relevant port information
26+
27+
Portlist format:
28+
* portlist['input'] = {'port': (x1,y1), 'direction': 'dir1'}
29+
* portlist['output'] = {'port': (x2, y2), 'direction': 'dir2'}
30+
31+
Where in the above (x1,y1) is the same as the 'port' input, (x2, y2) is the end of the taper, and 'dir1', 'dir2' are of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, *or* an angle in *radians*.
32+
'Direction' points *towards* the waveguide that will connect to it.
33+
34+
Note: The waveguide and cladding layer/datatype are taken from the `wgt_slot` by default.
35+
36+
"""
37+
def __init__(self, wgt_input, wgt_output, w_mmi, l_mmi, length, input_strip=None, port=(0,0), direction='EAST'):
38+
gdspy.Cell.__init__(self, "StripSlotMMICoupler--"+str(uuid.uuid4()))
39+
40+
self.portlist = {}
41+
42+
if (not isinstance(input_strip, bool)) and (input_strip != None):
43+
raise ValueError("Invalid input provided for `input_strip`. Please specify a boolean.")
44+
if input_strip == None:
45+
#Auto-detect based on wgt_input
46+
self.input_strip = (wgt_input.wg_type=='strip' or wgt_input.wg_type=='swg')
47+
else:
48+
#User-override
49+
self.input_strip = input_strip
50+
51+
if self.input_strip:
52+
self.wgt_strip = wgt_input
53+
self.wgt_slot = wgt_output
54+
else:
55+
self.wgt_strip = wgt_output
56+
self.wgt_slot = wgt_input
57+
self.wg_spec = {'layer': wgt_output.wg_layer, 'datatype': wgt_output.wg_datatype}
58+
self.clad_spec = {'layer': wgt_output.clad_layer, 'datatype': wgt_output.clad_datatype}
59+
60+
self.length = length
61+
self.w_mmi = w_mmi
62+
self.l_mmi = l_mmi
63+
64+
self.port = port
65+
self.trace=[port, tk.translate_point(port, length, direction)]
66+
self.direction = direction
67+
68+
self.build_cell()
69+
self.build_ports()
70+
71+
def build_cell(self):
72+
# Sequentially build all the geometric shapes using gdspy path functions
73+
# for waveguide, then add it to the Cell
74+
75+
angle = tk.get_exact_angle(self.trace[0], self.trace[1])
76+
77+
# Add MMI region
78+
path_mmi = gdspy.Path(self.w_mmi, self.trace[0])
79+
path_mmi.segment(self.l_mmi, direction=angle, **self.wg_spec)
80+
81+
print("path_mmi_coords = "+str((path_mmi.x, path_mmi.y)))
82+
83+
# Add slot tapered region
84+
path_taper = gdspy.Path((self.w_mmi - self.wgt_slot.slot)/2.0,
85+
initial_point=(path_mmi.x, path_mmi.y),
86+
number_of_paths=2,
87+
distance=(self.w_mmi + self.wgt_slot.slot)/2.0)
88+
path_taper.segment(self.length - self.l_mmi,
89+
final_width = self.wgt_slot.rail,
90+
final_distance = self.wgt_slot.rail_dist,
91+
direction=angle,
92+
**self.wg_spec)
93+
94+
# Cladding for waveguide taper
95+
path_clad = gdspy.Path(2*self.wgt_strip.clad_width+self.wgt_strip.wg_width, self.trace[0])
96+
path_clad.segment(self.length, final_width=2*self.wgt_slot.clad_width+self.wgt_slot.wg_width, direction=angle, **self.clad_spec)
97+
98+
if not self.input_strip:
99+
center_pt = ((self.trace[0][0]+self.trace[1][0])/2.0, (self.trace[0][1]+self.trace[1][1])/2.0)
100+
path_mmi.rotate(np.pi, center_pt)
101+
path_taper.rotate(np.pi, center_pt)
102+
path_clad.rotate(np.pi, center_pt)
103+
104+
self.add(path_mmi)
105+
self.add(path_taper)
106+
self.add(path_clad)
107+
108+
def build_ports(self):
109+
# Portlist format:
110+
# example: example: {'port':(x_position, y_position), 'direction': 'NORTH'}
111+
self.portlist["input"] = {'port':self.trace[0], 'direction':tk.flip_direction(self.direction)}
112+
self.portlist["output"] = {'port':self.trace[1], 'direction':self.direction}
113+
114+
if __name__ == "__main__":
115+
from . import *
116+
top = gdspy.Cell("top")
117+
wgt_strip = WaveguideTemplate(bend_radius=50, wg_type='strip', wg_width=0.7)
118+
wgt_slot = WaveguideTemplate(bend_radius=50, wg_type='slot', wg_width=0.7, slot=0.2)
119+
120+
wg1=Waveguide([(0,0), (100,0)], wgt_strip)
121+
tk.add(top, wg1)
122+
123+
ycoup = StripSlotMMICoupler(wgt_strip, wgt_slot, 2.5, 6.0, 20.0, **wg1.portlist["output"])
124+
tk.add(top, ycoup)
125+
126+
(x1,y1)=ycoup.portlist["output"]["port"]
127+
wg2=Waveguide([(x1, y1), (x1+100, y1)], wgt_slot)
128+
tk.add(top, wg2)
129+
130+
gdspy.LayoutViewer(cells=top)
131+
gdspy.write_gds('stripslotmmicoupler.gds', unit=1.0e-6, precision=1.0e-9)

picwriter/components/stripslotcoupler.py renamed to picwriter/components/stripslotycoupler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import picwriter.toolkit as tk
88

99
class StripSlotYCoupler(gdspy.Cell):
10-
""" Strip-to-Slot Y-Coupler Cell class (subclass of gdspy.Cell).
10+
""" Strip-to-Slot Y-Coupler Cell class (subclass of gdspy.Cell). For more information on this specific type of strip to slot mode converter, please see the original paper at https://doi.org/10.1364/OL.34.001498.
1111
1212
Args:
1313
* **wgt_input** (WaveguideTemplate): WaveguideTemplate object for the input waveguide (should be either of type `strip` or `slot`).

picwriter/tests/test_picwriter.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def test_contradc_creation(self):
204204
self.assertTrue(len(top.elements)==2)
205205
self.assertTrue(abs(top.area()-4222.12846627) <= 1e-6)
206206

207-
def test_stripslotycoupler_creation(self):
207+
def test_stripslotcoupler_creation(self):
208208
top = gdspy.Cell("t-stripslotycoupler")
209209
wgt_strip = WaveguideTemplate(bend_radius=50, wg_type='strip', wg_width=0.7)
210210
wgt_slot = WaveguideTemplate(bend_radius=50, wg_type='slot', wg_width=0.7, slot=0.2)
@@ -217,10 +217,14 @@ def test_stripslotycoupler_creation(self):
217217
(x1,y1)=ycoup.portlist["output"]["port"]
218218
wg2=Waveguide([(x1, y1), (x1+100, y1)], wgt_slot)
219219
tk.add(top, wg2)
220-
# print("StripSlotYCoupler area = "+str(top.area()))
220+
221+
ycoup2 = StripSlotMMICoupler(wgt_strip, wgt_slot, 2.5, 6.0, 20.0, **wg2.portlist["output"])
222+
tk.add(top, ycoup2)
223+
224+
# print("StripSlotCoupler area = "+str(top.area()))
221225
print(len(top.elements))
222-
self.assertTrue(len(top.elements)==3)
223-
self.assertTrue(abs(top.area()-4473.0) <= 1e-6)
226+
self.assertTrue(len(top.elements)==4)
227+
self.assertTrue(abs(top.area()-4921.6) <= 1e-6)
224228

225229
def test_broadbanddc_creation(self):
226230
top = gdspy.Cell("t-bdc")
@@ -234,4 +238,4 @@ def test_broadbanddc_creation(self):
234238
# print("BroadbandDirectoinalCoupler area = "+str(top.area()))
235239
print(len(top.elements))
236240
self.assertTrue(len(top.elements)==2)
237-
self.assertTrue(abs(top.area()-8386.54754147) <= 1e-6)
241+
self.assertTrue(abs(top.area()-8386.54754147) <= 1e-6)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def readme():
1111
return f.read()
1212

1313
setup(name='picwriter',
14-
version='0.3',
14+
version='0.4',
1515
description='Mask generation tool',
1616
long_description=readme(),
1717
url='http://github.com/DerekK88/picwriter',

0 commit comments

Comments
 (0)