def test_get_bundle_sort_ports(data_regression: DataRegressionFixture, check: bool = True) -> Component: lengths = {} c = gf.Component("test_get_bundle_sort_ports") ys_right = [0, 10, 20, 40, 50, 80] pitch = 127.0 N = len(ys_right) ys_left = [(i - N / 2) * pitch for i in range(N)] right_ports = [ gf.Port(f"R_{i}", (0, ys_right[i]), 0.5, 180) for i in range(N) ] left_ports = [ gf.Port(f"L_{i}", (-400, ys_left[i]), 0.5, 0) for i in range(N) ] left_ports.reverse() routes = gf.routing.get_bundle(right_ports, left_ports) for i, route in enumerate(routes): c.add(route.references) lengths[i] = route.length if check: data_regression.check(lengths) return c
def get_route_error( points, cross_section: Optional[CrossSection] = None, layer_path: Layer = LAYER.ERROR_PATH, layer_label: Layer = LAYER.TEXT, layer_marker: Layer = LAYER.ERROR_MARKER, references: Optional[List[ComponentReference]] = None, ) -> Route: width = cross_section.info["width"] if cross_section else 10 warnings.warn( f"Route error for points {points}", RouteWarning, ) c = Component(f"route_{uuid.uuid4()}"[:16]) path = gdspy.FlexPath( points, width=width, gdsii_path=True, layer=layer_path[0], datatype=layer_path[1], ) c.add(path) ref = ComponentReference(c) port1 = gf.Port(name="p1", midpoint=points[0], width=width) port2 = gf.Port(name="p2", midpoint=points[1], width=width) point_marker = gf.c.rectangle(size=(width * 2, width * 2), centered=True, layer=layer_marker) point_markers = [point_marker.ref(position=point) for point in points] + [ref] labels = [ gf.Label(text=str(i), position=point, layer=layer_label[0], texttype=layer_label[1]) for i, point in enumerate(points) ] references = references or [] references += point_markers return Route(references=references, ports=[port1, port2], length=-1, labels=labels)
def test_path_length_matching() -> Component: c = gf.Component("path_length_match_sample") dy = 2000.0 xs1 = [-500, -300, -100, -90, -80, -55, -35, 200, 210, 240, 500, 650] pitch = 100.0 N = len(xs1) xs2 = [-20 + i * pitch for i in range(N)] a1 = 90 a2 = a1 + 180 ports1 = [gf.Port(f"top_{i}", (xs1[i], 0), 0.5, a1) for i in range(N)] ports2 = [gf.Port(f"bottom_{i}", (xs2[i], dy), 0.5, a2) for i in range(N)] routes = gf.routing.get_bundle_path_length_match(ports1, ports2) lengths = [2659.822] for route, length in zip(routes, lengths): c.add(route.references) assert np.isclose(route.length, length), route.length return c
def mmi2x2(width: float = 0.5, width_taper: float = 1.0, length_taper: float = 10.0, length_mmi: float = 5.5, width_mmi: float = 2.5, gap_mmi: float = 0.25, taper: ComponentFactory = taper_function, cross_section: CrossSectionFactory = strip, **kwargs) -> Component: r"""Mmi 2x2. Args: width: input and output straight width width_taper: interface between input straights and mmi region length_taper: into the mmi region length_mmi: in x direction width_mmi: in y direction gap_mmi: (width_taper + gap between tapered wg)/2 taper: taper function cross_section: **kwargs: cross_section settings .. code:: length_mmi <------> ________ | | __/ \__ W1 __ __ E1 \ /_ _ _ _ | | _ _ _ _| gap_mmi __/ \__ W0 __ __ E0 \ / |________| <-> length_taper """ gf.snap.assert_on_2nm_grid(gap_mmi) x = cross_section(**kwargs) cladding_offset = x.info["cladding_offset"] layers_cladding = x.info["layers_cladding"] layer = x.info["layer"] component = gf.Component() w_mmi = width_mmi w_taper = width_taper taper = taper(length=length_taper, width1=width, width2=w_taper, cross_section=cross_section, **kwargs) a = gap_mmi / 2 + width_taper / 2 mmi = gf.components.rectangle( size=(length_mmi, w_mmi), layer=layer, centered=True, ) ports = [ gf.Port("o1", orientation=180, midpoint=(-length_mmi / 2, -a), width=w_taper), gf.Port("o2", orientation=180, midpoint=(-length_mmi / 2, +a), width=w_taper), gf.Port("o3", orientation=0, midpoint=(+length_mmi / 2, +a), width=w_taper), gf.Port("o4", orientation=0, midpoint=(+length_mmi / 2, -a), width=w_taper), ] mmi_section = component.add_ref(mmi) for port in ports: taper_ref = component << taper taper_ref.connect(port="o2", destination=port) component.add_port(name=port.name, port=taper_ref.ports["o1"]) component.absorb(taper_ref) component.absorb(mmi_section) layers_cladding = layers_cladding or [] if layers_cladding: add_padding( component, default=cladding_offset, right=0, left=0, top=cladding_offset, bottom=cladding_offset, layers=layers_cladding, ) component.auto_rename_ports() return component
xsize = port2.midpoint[0] - port1.midpoint[0] size = (xsize, ysize) bend = bend_s(size=size, **kwargs) bend_ref = bend.ref() bend_ref.connect("o1", port1) return Route( references=[bend_ref], length=bend.info_child["length"], ports=(port1, port2) ) if __name__ == "__main__": import gdsfactory as gf c = gf.Component("test_get_route_sbend") pitch = 2.0 ys_left = [0, 10, 20] N = len(ys_left) ys_right = [(i - N / 2) * pitch for i in range(N)] right_ports = [gf.Port(f"R_{i}", (0, ys_right[i]), 0.5, 180) for i in range(N)] left_ports = [gf.Port(f"L_{i}", (-50, ys_left[i]), 0.5, 0) for i in range(N)] left_ports.reverse() right_ports, left_ports = sort_ports(right_ports, left_ports) for p1, p2 in zip(right_ports, left_ports): route = get_route_sbend(p1, p2, layer=(2, 0)) c.add(route.references) c.show()
def straight_heater_meander( length: float = 300.0, spacing: float = 2.0, cross_section: gf.types.CrossSectionFactory = gf.cross_section.strip, heater_width: float = 2.5, extension_length: float = 15.0, layer_heater: Optional[Layer] = LAYER.HEATER, radius: float = 5.0, contact: Optional[ComponentFactory] = contact_heater_m3, port_orientation1: int = 180, port_orientation2: int = 0, heater_taper_length: Optional[float] = 10.0, straight_width: float = 0.9, taper_length: float = 10, ): """Returns a meander based heater based on SungWon Chung, Makoto Nakai, and Hossein Hashemi, Low-power thermo-optic silicon modulator for large-scale photonic integrated systems Opt. Express 27, 13430-13459 (2019) https://www.osapublishing.org/oe/abstract.cfm?URI=oe-27-9-13430 FIXME: only works for 3 rows. Args: length: total length of the optical path spacing: waveguide spacing (center to center) cross_section: for waveguide heater_width: for heater extension_length: of input and output optical ports layer_heater: for top heater, if None, it does not add a heater radius: for the meander bends contact: for the heater to contact metal port_orientation1: port_orientation2: heater_taper_length: minimizes current concentrations from heater to contact straight_width: width of the straight section taper_length: from the cross_section """ rows = 3 c = gf.Component() p1 = gf.Port(midpoint=(0, 0), orientation=0) p2 = gf.Port(midpoint=(0, spacing), orientation=0) route = gf.routing.get_route(p1, p2, radius=radius) cross_section1 = gf.partial(cross_section, width=straight_width) cross_section2 = cross_section straight_length = gf.snap.snap_to_grid( (length - (rows - 1) * route.length) / rows) straight = gf.c.straight(length=straight_length - 2 * taper_length, cross_section=cross_section1) taper = gf.partial( gf.c.taper_cross_section_linear, cross_section1=cross_section1, cross_section2=cross_section2, length=taper_length, ) straight_with_tapers = gf.c.extend_ports(straight, extension_factory=taper) straight_array = c << gf.c.array( straight_with_tapers, spacing=(0, spacing), columns=1, rows=rows) for row in range(1, rows, 2): route = gf.routing.get_route( straight_array.ports[f"o2_{row+1}_1"], straight_array.ports[f"o2_{row}_1"], radius=radius, cross_section=cross_section, ) c.add(route.references) route = gf.routing.get_route( straight_array.ports[f"o1_{row+1}_1"], straight_array.ports[f"o1_{row+2}_1"], radius=radius, cross_section=cross_section, ) c.add(route.references) straight1 = c << gf.c.straight(length=extension_length, cross_section=cross_section) straight2 = c << gf.c.straight(length=extension_length, cross_section=cross_section) straight1.connect("o2", straight_array.ports["o1_1_1"]) straight2.connect("o1", straight_array.ports[f"o2_{rows}_1"]) c.add_port("o1", port=straight1.ports["o1"]) c.add_port("o2", port=straight2.ports["o2"]) if layer_heater: heater_cross_section = gf.partial(gf.cross_section.cross_section, width=heater_width, layer=layer_heater) heater = c << gf.c.straight( length=straight_length, cross_section=heater_cross_section, ) heater.movey(spacing * (rows // 2)) if layer_heater and contact: contactw = contact() contacte = contact() dx = contactw.get_ports_xsize() / 2 + heater_taper_length or 0 contact_west_midpoint = heater.size_info.cw - (dx, 0) contact_east_midpoint = heater.size_info.ce + (dx, 0) contact_west = c << contactw contact_east = c << contacte contact_west.move(contact_west_midpoint) contact_east.move(contact_east_midpoint) c.add_port( "e1", port=contact_west.get_ports_list(orientation=port_orientation1)[0]) c.add_port( "e2", port=contact_east.get_ports_list(orientation=port_orientation2)[0]) if heater_taper_length: taper = gf.c.taper( cross_section=heater_cross_section, width1=contactw.ports["e1"].width, width2=heater_width, length=heater_taper_length, ) taper1 = c << taper taper2 = c << taper taper1.connect("o2", heater.ports["o1"]) taper2.connect("o2", heater.ports["o2"]) contact_west.connect("e3", taper1.ports["o1"]) contact_east.connect("e1", taper2.ports["o1"]) return c
def spiral_inner_io(N: int = 6, x_straight_inner_right: float = 150.0, x_straight_inner_left: float = 50.0, y_straight_inner_top: float = 50.0, y_straight_inner_bottom: float = 10.0, grating_spacing: float = 127.0, waveguide_spacing: float = 3.0, bend90_function: ComponentFactory = bend_euler, bend180_function: ComponentFactory = bend_euler180, straight: ComponentFactory = straight_function, length: Optional[float] = None, cross_section: CrossSectionFactory = strip, cross_section_bend: Optional[CrossSectionFactory] = None, **kwargs) -> Component: """Returns Spiral with ports inside the spiral loop. You can add grating couplers inside . Args: N: number of loops x_straight_inner_right: x_straight_inner_left: y_straight_inner_top: y_straight_inner_bottom: grating_spacing: defaults to 127 for fiber array waveguide_spacing: center to center spacing bend90_function bend180_function straight: straight function length: spiral target length (um), overrides x_straight_inner_left to match the length by a simple 1D interpolation cross_section: cross_section_bend: for the bends kwargs: cross_section settings """ dx = dy = waveguide_spacing x = cross_section(**kwargs) width = x.info.get("width") layer = x.info.get("layer") cross_section_bend = cross_section_bend or cross_section if length: x_straight_inner_left = get_straight_length( length=length, spiral_function=spiral_inner_io, N=N, x_straight_inner_right=x_straight_inner_right, x_straight_inner_left=x_straight_inner_left, y_straight_inner_top=y_straight_inner_top, y_straight_inner_bottom=y_straight_inner_bottom, grating_spacing=grating_spacing, waveguide_spacing=waveguide_spacing, ) _bend180 = gf.call_if_func(bend180_function, cross_section=cross_section_bend, **kwargs) _bend90 = gf.call_if_func(bend90_function, cross_section=cross_section_bend, **kwargs) rx, ry = get_bend_port_distances(_bend90) _, rx180 = get_bend_port_distances( _bend180) # rx180, second arg since we rotate component = Component() p1 = gf.Port( name="o1", midpoint=(0, y_straight_inner_top), orientation=270, width=width, layer=layer, cross_section=cross_section_bend, ) p2 = gf.Port( name="o2", midpoint=(grating_spacing, y_straight_inner_top), orientation=270, width=width, layer=layer, cross_section=cross_section_bend, ) component.add_port(name="o1", port=p1) component.add_port(name="o2", port=p2) # Create manhattan path going from west grating to westest port of bend 180 _pt = np.array(p1.position) pts_w = [_pt] for i in range(N): y1 = y_straight_inner_top + ry + (2 * i + 1) * dy x2 = grating_spacing + 2 * rx + x_straight_inner_right + (2 * i + 1) * dx y3 = -y_straight_inner_bottom - ry - (2 * i + 3) * dy x4 = -x_straight_inner_left - (2 * i + 1) * dx if i == N - 1: x4 = x4 - rx180 + dx _pt1 = np.array([_pt[0], y1]) _pt2 = np.array([x2, _pt1[1]]) _pt3 = np.array([_pt2[0], y3]) _pt4 = np.array([x4, _pt3[1]]) _pt5 = np.array([_pt4[0], 0]) _pt = _pt5 pts_w += [_pt1, _pt2, _pt3, _pt4, _pt5] route_west = round_corners(pts_w, bend=_bend90, straight=straight, cross_section=cross_section, **kwargs) component.add(route_west.references) # Add loop back bend180_ref = _bend180.ref(port_id="o2", position=route_west.ports[1], rotation=90) component.add(bend180_ref) # Create manhattan path going from east grating to eastest port of bend 180 _pt = np.array(p2.position) pts_e = [_pt] for i in range(N): y1 = y_straight_inner_top + ry + (2 * i) * dy x2 = grating_spacing + 2 * rx + x_straight_inner_right + 2 * i * dx y3 = -y_straight_inner_bottom - ry - (2 * i + 2) * dy x4 = -x_straight_inner_left - (2 * i) * dx _pt1 = np.array([_pt[0], y1]) _pt2 = np.array([x2, _pt1[1]]) _pt3 = np.array([_pt2[0], y3]) _pt4 = np.array([x4, _pt3[1]]) _pt5 = np.array([_pt4[0], 0]) _pt = _pt5 pts_e += [_pt1, _pt2, _pt3, _pt4, _pt5] route_east = round_corners(pts_e, bend=_bend90, straight=straight, cross_section=cross_section, **kwargs) component.add(route_east.references) length = route_east.length + route_west.length + _bend180.info.length component.info.length = snap_to_grid(length + 2 * y_straight_inner_top) return component
def mmi1x2(width: float = 0.5, width_taper: float = 1.0, length_taper: float = 10.0, length_mmi: float = 5.5, width_mmi: float = 2.5, gap_mmi: float = 0.25, taper: ComponentFactory = taper_function, with_cladding_box: bool = True, cross_section: CrossSectionFactory = strip, **kwargs) -> Component: r"""Mmi 1x2. Args: width: input and output straight width width_taper: interface between input straights and mmi region length_taper: into the mmi region length_mmi: in x direction width_mmi: in y direction gap_mmi: gap between tapered wg taper: taper function with_cladding_box: to avoid DRC acute angle errors in cladding cross_section: **kwargs: cross_section settings .. code:: length_mmi <------> ________ | | | \__ | __ E1 __/ /_ _ _ _ W0 __ | _ _ _ _| gap_mmi \ \__ | __ E0 | / |________| <-> length_taper """ gf.snap.assert_on_2nm_grid(gap_mmi) x = cross_section(**kwargs) cladding_offset = x.info["cladding_offset"] layers_cladding = x.info["layers_cladding"] layer = x.info["layer"] c = Component() w_mmi = width_mmi w_taper = width_taper taper = taper(length=length_taper, width1=width, width2=w_taper, cross_section=cross_section, **kwargs) a = gap_mmi / 2 + width_taper / 2 mmi = c << gf.components.rectangle( size=(length_mmi, w_mmi), layer=layer, centered=True, ) ports = [ gf.Port("o1", orientation=180, midpoint=(-length_mmi / 2, 0), width=w_taper), gf.Port("o2", orientation=0, midpoint=(+length_mmi / 2, +a), width=w_taper), gf.Port("o3", orientation=0, midpoint=(+length_mmi / 2, -a), width=w_taper), ] for port in ports: taper_ref = c << taper taper_ref.connect(port="o2", destination=port) c.add_port(name=port.name, port=taper_ref.ports["o1"]) c.absorb(taper_ref) c.absorb(mmi) layers_cladding = layers_cladding or [] if layers_cladding and with_cladding_box: add_padding( c, default=cladding_offset, right=0, left=0, top=cladding_offset, bottom=cladding_offset, layers=layers_cladding, ) return c