|
| 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) |
0 commit comments