def draw(self, cell): layout = cell.layout() cp = self.get_cell_params() lay = self.get_layers(layout) origin, ex, ey = self.origin_ex_ey() w_mh = cp.mh_width length = cp.mh_length w_contacts = cp.w_contacts input_port_position = origin - length / 2 * ex output_port_postion = origin + length / 2 * ex x_off = length / 2 - w_contacts / 2 y_off = w_mh / 2 + w_contacts / 2 input_contact_position = origin - x_off * ex + y_off * ey output_contact_position = origin + x_off * ex + y_off * ey # draw metal heater layout_waveguide(cell, lay.M_Heater, [input_port_position, output_port_postion], w_mh) # draw metal contacts # Left contacts contact_points = [ origin - x_off * ex + (y_off - w_contacts / 2) * ey, origin - x_off * ex + (y_off + w_contacts / 2) * ey, ] layout_waveguide(cell, lay.M_Heater, contact_points, w_contacts) layout_waveguide(cell, lay.ML, contact_points, w_contacts) # Right contacts contact_points = [ origin + x_off * ex + (y_off - w_contacts / 2) * ey, origin + x_off * ex + (y_off + w_contacts / 2) * ey, ] layout_waveguide(cell, lay.M_Heater, contact_points, w_contacts) layout_waveguide(cell, lay.ML, contact_points, w_contacts) ports = [ Port("el_contact_1", input_contact_position, ey, w_contacts), Port("el_contact_2", output_contact_position, ey, w_contacts), ] return cell, {port.name: port for port in ports}
def layout_mzi_curve(P0, P1): curve = bezier_optimal(P0, P1, cp.angle_ex, cp.angle_ex) return layout_waveguide(cell, lay.Si, curve, cp.wg_width, smooth=True)
def layout_connect_ports(cell, layer, port_from, port_to, smooth=True): """ Places an "optimal" bezier curve from port_from to port_to. """ if port_from.name.startswith("el"): assert port_to.name.startswith("el") P0 = port_from.position + port_from.direction * port_from.width / 2 P3 = port_to.position + port_to.direction * port_to.width / 2 smooth = smooth and True else: dbu = cell.layout().dbu P0 = port_from.position - dbu * port_from.direction P3 = port_to.position - dbu * port_to.direction smooth = smooth or True angle_from = np.arctan2(port_from.direction.y, port_from.direction.x) * 180 / pi angle_to = np.arctan2(-port_to.direction.y, -port_to.direction.x) * 180 / pi curve = bezier_optimal(P0, P3, angle_from, angle_to) if debug: for point in curve: print(point) print(f"bezier_optimal({P0}, {P3}, {angle_from}, {angle_to})") return layout_waveguide(cell, layer, curve, [port_from.width, port_to.width], smooth=smooth)
def main(): layout = pya.Layout() TOP = layout.create_cell("TOP") layer = pya.LayerInfo(1, 0) # First layer origin = pya.DPoint(0, 0) ex = pya.DVector(1, 0) ey = pya.DVector(0, 1) angles = np.linspace(-170, 170, 13) for i, angle_0 in enumerate(angles): for j, angle_3 in enumerate(angles): curve = bezier_curve(origin + ey * i * 150 + ex * j * 150, angle_0, angle_3, ex, ey) layout_waveguide(TOP, layer, curve, width=0.5) layout.write("bezier_waveguides.gds")
def connect_ports_L(cell, cplayer, ports_from, ports_to, ex): """ Connects ports ports_from to ports_to, always leaving vertically""" ey = rotate90(ex) for port_from, port_to in zip(ports_from, ports_to): assert port_from.direction == ey or port_from.direction == -ey o_y = ey if port_to.position * ey > port_from.position * ey else -ey o_x = ex if port_to.position * ex > port_from.position * ex else -ex middle_point = manhattan_intersection(port_from.position, port_to.position, ex) layout_waveguide( cell, ensure_layer(cell.layout(), cplayer), [port_from.position, middle_point + port_to.width * 0.5 * o_y], port_from.width, ) layout_waveguide( cell, ensure_layer(cell.layout(), cplayer), [middle_point - port_from.width * 0.5 * o_x, port_to.position], port_to.width, )
def layout_waveguide_from_points( cell, layer, points, width, radius, taper_width=None, taper_length=None ): assert radius > width / 2, "Please use a radius larger than the half-width" points = unique_points(points) if len(points) < 2: # Nothing to do return cell # First, get the list of lines and arcs try: rounded_path = compute_rounded_path(points, radius) except Exception as e: print("ERROR:", e) print("Continuing...") layout_waveguide(cell, layer, points, 0.1) return cell # Taper path if necessary if taper_width is not None and taper_length is not None: waveguide_path = compute_tapered_path( rounded_path, width, taper_width, taper_length ) else: waveguide_path = compute_untapered_path(rounded_path, width) # creating a single path _draw_points = [] _draw_widths = [] for element in waveguide_path: points, width = element.points, element.widths n_points = len(points) try: if len(width) == n_points: _draw_points.extend(points) _draw_widths.extend(width) elif len(width) == 2: _draw_widths.extend(np.linspace(width[0], width[1], n_points)) _draw_points.extend(points) else: raise RuntimeError("Internal error detected. Debug please.") except TypeError: _draw_points.extend(points) _draw_widths.extend(np.ones(n_points) * width) # deleting repeated points _cur_point = None _draw_points2 = [] _draw_widths2 = [] for p, w in zip(_draw_points, _draw_widths): if _cur_point and p == _cur_point: continue _draw_points2.append(p) _draw_widths2.append(w) _cur_point = p layout_waveguide(cell, layer, _draw_points2, _draw_widths2, smooth=False) return cell
def layout(self, cell, layer): layout_waveguide(cell, layer, self.points, self.widths, smooth=False)
def draw(self, cell): layout = cell.layout() cp = self.get_cell_params() lay = self.get_layers(layout) origin, ex, ey = self.origin_ex_ey() YBranch_te1550_Cell_0, YBranch_te1550_Ports_0 = YBranch_te1550( "Broadband_DC", params={ "angle_ex": cp.angle_ex }).new_cell(layout) YBranch_te1550_Ports_0 = place_cell(cell, YBranch_te1550_Cell_0, YBranch_te1550_Ports_0, origin) Broadband_DC_Cell, Broadband_DC_Ports = Broadband_DC_te1550( "Broadband_DC", cp).new_cell(layout) Broadband_DC_Ports = place_cell( cell, Broadband_DC_Cell, Broadband_DC_Ports, origin + (cp.waveguide_length_MZI + 100) * ex, ) top_arm_left = (YBranch_te1550_Ports_0["opt2"].position + 25 * ex + cp.MZI_height / 2 * ey) top_arm_right = top_arm_left + cp.waveguide_length_MZI * ex layout_waveguide(cell, lay.Si, [top_arm_left, top_arm_right], 0.5) Heater_1_MZI_Cell, Heater_1_MZI_Ports = Waveguide_heater( "waveguide heater", params={ "wg_length": cp.waveguide_length_MZI, "angle_ex": cp.angle_ex }, ).new_cell(layout) Heater_1_MZI_Ports = place_cell( cell, Heater_1_MZI_Cell, Heater_1_MZI_Ports, YBranch_te1550_Ports_0["opt2"].position + 25 * ex + cp.MZI_height / 2 * ey + cp.waveguide_length_MZI / 2 * ex, ) bottom_arm_left = (YBranch_te1550_Ports_0["opt3"].position + 25 * ex - cp.MZI_height / 2 * ey) bottom_arm_right = bottom_arm_left + cp.waveguide_length_MZI * ex layout_waveguide(cell, lay.Si, [bottom_arm_left, bottom_arm_right], 0.5) from zeropdk.layout.geometry import bezier_optimal def layout_mzi_curve(P0, P1): curve = bezier_optimal(P0, P1, cp.angle_ex, cp.angle_ex) return layout_waveguide(cell, lay.Si, curve, cp.wg_width, smooth=True) layout_mzi_curve(YBranch_te1550_Ports_0["opt2"].position, top_arm_left) layout_mzi_curve(YBranch_te1550_Ports_0["opt3"].position, bottom_arm_left) layout_mzi_curve(top_arm_right, Broadband_DC_Ports["opt1"].position) layout_mzi_curve(bottom_arm_right, Broadband_DC_Ports["opt2"].position) ports = [] ports.append(YBranch_te1550_Ports_0["opt1"].rename("opt_in")) ports.append(Broadband_DC_Ports["opt3"].rename("opt_out_1")) ports.append(Broadband_DC_Ports["opt4"].rename("opt_out_2")) ports.extend(Heater_1_MZI_Ports.values()) if cp.layout_ports: for port in ports: port.draw(cell, lay.PinRec) insert_shape(cell, lay.DevRec, cell.bbox()) return cell, {port.name: port for port in ports}
def common_layout_manhattan_traces(cell, layer1, layer2, layervia, via_cell_placer, path, ex, initiate_with_via=False): """Lays out a manhattan trace, potentially with vias Args: layer1 and layer2 are given to layout.LayerInfo(layer), generally layer2 is on top via_cell_placer: returns a cell when called with via_cell_placer(parent_cell, pya.DPoint origin, width, layer1, layer2, layervia, ex) path: list of tuples containing necessary info ((x, y) or pya.DPoint, layer, width) Returns: path Algorithm places a via when there is a change of layers. To terminate with a via, have the last layer be different than the penultimate one. """ assert isinstance(ex, (pya.DPoint, pya.DVector)) ey = rotate90(ex) first_point, _, first_width = path[0] if initiate_with_via: via_cell_placer(cell, first_point, first_width, layer1, layer2, layervia, ex) points_list = list() widths_list = list() _, previous_layer, _ = path[0] layout = cell.layout() for point, layer, width in path: if isinstance(point, tuple): # point are (x, y) coordinates x, y = point point = x * ex + y * ey else: assert isinstance(point, (pya.DPoint, pya.DVector)) if isinstance(point, pya.DVector): point = pya.DPoint(point) if layer == previous_layer: points_list.append(point) # store points widths_list.append(width) else: # time to place a via and layout points_list.append(point) widths_list.append(width) layout_waveguide( cell, ensure_layer(layout, previous_layer), points_list, widths_list, smooth=True, ) via_cell_placer(cell, point, width, layer1, layer2, layervia, ex) # delete all but the last point del points_list[:-1] del widths_list[:-1] previous_layer = layer # layout last trace if len(points_list) >= 2: layout_waveguide( cell, ensure_layer(layout, previous_layer), points_list, widths_list, smooth=True, ) return path