def pads_shorted( pad: ComponentFactory = pad_function, columns: int = 8, pad_spacing: float = 150.0, layer_metal: Tuple[int, int] = LAYER.M3, metal_width: float = 10, ) -> Component: """Returns a 1D array of shorted_pads Args: pad: pad function columns: number of columns pad_spacing: layer_metal: for the short metal_width: for the short """ c = Component(name="shorted_pads") pad = pad() for i in range(columns): pad_ref = c.add_ref(pad) pad_ref.movex(i * pad_spacing - columns / 2 * pad_spacing + pad_spacing / 2) short = rectangle( size=(pad_spacing * (columns - 1), metal_width), layer=layer_metal, centered=True, ) c.add_ref(short) return c
def pixel_array( pixels: str = character_a, pixel_size: float = 10.0, layer: Tuple[int, int] = LAYER.M1, ) -> Component: """Returns a pixel component from a string representing the pixels. Args: pixels: string representing the pixels pixel_size: widht/height for each pixel layer: layer for each pixel """ component = Component() lines = [line for line in pixels.split("\n") if len(line) > 0] lines.reverse() j = 0 i = 0 i_max = 0 a = pixel_size for line in lines: i = 0 for c in line: if c in ["X", "1"]: p0 = np.array([i * a, j * a]) pixel = [p0 + p for p in [(0, 0), (a, 0), (a, a), (0, a)]] component.add_polygon(pixel, layer=layer) i += 1 i_max = max(i_max, i) j += 1 return component
def add_labels( component: Component, get_label_function: Callable = get_input_label_electrical, layer_label: Layer = gf.LAYER.LABEL, gc: Optional[Component] = None, **kwargs, ) -> Component: """Returns component with labels a particular type of ports Args: component: to add labels to get_label_function: function to get label layer_label: layer_label gc: Optional grating coupler **port_settings to select Returns: original component with labels """ ports = component.get_ports_list(**kwargs) for i, port in enumerate(ports): label = get_label_function( port=port, gc=gc, gc_index=i, component_name=component.name, layer_label=layer_label, ) component.add(label) return component
def grating_coupler_array( grating_coupler: ComponentOrFactory = grating_coupler_circular, pitch: float = 127.0, n: int = 6, port_name: str = "o1", rotation: int = 0, ) -> Component: """Array of rectangular pads. Args: grating_coupler: ComponentOrFactory spacing: x spacing n: number of pads port_name: port name rotation: rotation angle for each reference """ c = Component() grating_coupler = (grating_coupler() if callable(grating_coupler) else grating_coupler) for i in range(n): gc = c << grating_coupler gc.rotate(rotation) gc.x = i * pitch port_name_new = f"o{i}" c.add_port(port=gc.ports[port_name], name=port_name_new) return c
def spiral_inner_io_fiber_single( cross_section: CrossSectionFactory = strip, cross_section_bend: Optional[CrossSectionFactory] = None, x_straight_inner_right: float = 40.0, x_straight_inner_left: float = 75.0, y_straight_inner_top: float = 10.0, y_straight_inner_bottom: float = 0.0, grating_spacing: float = 200.0, **kwargs): """Spiral with 0 and 270 degree ports.""" c = Component() ref = c << spiral_inner_io(cross_section=cross_section, cross_section_bend=cross_section_bend, 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, **kwargs) ref.rotate(90) bend = bend_euler(cross_section=cross_section_bend or cross_section) btop = c << bend bbot = c << bend bbot.connect("o2", ref.ports["o1"]) btop.connect("o1", ref.ports["o2"]) c.add_port("o2", port=btop.ports["o2"]) c.add_port("o1", port=bbot.ports["o1"]) return c
def add_keepout( component: Component, target_layers: Layers, keepout_layers: Layers, margin: float = 2.0, ) -> Component: """Adds keepout after Looking up all polygons in a cell. You can also use add_padding Args: component target_layers: list of layers to read keepout_layers: list of layers to add keepout margin: offset from tareget to keepout_layers """ c = Component(f"{component.name}_ko") c << component for layer in target_layers: polygons = component.get_polygons(by_spec=layer) if polygons: for ko_layer in keepout_layers: ko_layer = _parse_layer(ko_layer) polygon_keepout = [ polygon_grow(polygon, margin) for polygon in polygons ] c.add_polygon(polygon_keepout, ko_layer) return c
def add_padding( component: Component, layers: Tuple[Layer, ...] = (LAYER.PADDING, ), **kwargs, ) -> Component: """Adds padding layers to a component inside a container. Returns the same ports as the component. Args: component layers: list of layers new_component: returns a new component if True keyword Args: default: default padding top: north padding bottom: south padding right: east padding left: west padding """ points = get_padding_points(component, **kwargs) for layer in layers: component.add_polygon(points, layer=layer) return component
def grating_coupler_array( grating_coupler: ComponentOrFactory = grating_coupler_elliptical2, pitch: float = 127.0, n: int = 6, port_name: str = "o1", ) -> Component: """Array of rectangular pads. Args: grating_coupler: ComponentOrFactory spacing: x spacing n: number of pads port_list: list of port orientations (N, S, W, E) per pad """ c = Component() grating_coupler = (grating_coupler() if callable(grating_coupler) else grating_coupler) for i in range(n): gc = c << grating_coupler gc.x = i * pitch port_name_new = f"o{i}" c.add_port(port=gc.ports[port_name], name=port_name_new) return c
def add_padding_to_size( component: Component, layers: Tuple[Layer, ...] = (LAYER.PADDING, ), xsize: Optional[float] = None, ysize: Optional[float] = None, left: float = 0, bottom: float = 0, ) -> Component: """Returns component with padding layers on each side. New size is multiple of grid size """ c = component top = abs(ysize - component.ysize) if ysize else 0 right = abs(xsize - component.xsize) if xsize else 0 points = [ [c.xmin - left, c.ymin - bottom], [c.xmax + right, c.ymin - bottom], [c.xmax + right, c.ymax + top], [c.xmin - left, c.ymax + top], ] for layer in layers: component.add_polygon(points, layer=layer) return component
def triangle( x: float = 10, xtop: float = 0, y: float = 20, ybot: float = 0, layer: Layer = (1, 0), ) -> Component: r""" Args: x: base xsize xtop: top xsize y: ysize ybot: bottom ysize layer: .. code:: xtop _ | \ | \ | \ y| \ | \ | \ |______|ybot x """ c = Component() points = [[0, 0], [x, 0], [x, ybot], [xtop, y], [0, y]] c.add_polygon(points, layer=layer) return c
def add_ports_from_labels( component: Component, port_width: float, port_layer: Layer, xcenter: Optional[float] = None, port_name_prefix: str = "o", port_type: str = "optical", ) -> Component: """Add ports from labels. Assumes that all ports have a label at the port center. """ xc = xcenter or component.x yc = component.y for i, label in enumerate(component.labels): x, y = label.position port_name = f"{port_name_prefix}{i+1}" if port_name_prefix else i if x > xc: # east orientation = 0 elif x < xc: # west orientation = 180 elif y > yc: # north orientation = 90 elif y < yc: # south orientation = 270 component.add_port( name=port_name, midpoint=(x, y), width=port_width, orientation=orientation, port_type=port_type, layer=port_layer, ) return component
def _add_pin_square( component: Component, port: Port, pin_length: float = 0.1, layer: Tuple[int, int] = LAYER.PORT, label_layer: Optional[Tuple[int, int]] = LAYER.PORT, port_margin: float = 0.0, ) -> None: """Add half out pin to a component. Args: component: port: Port pin_length: length of the pin marker for the port layer: for the pin marker label_layer: for the label port_margin: margin to port edge .. code:: _______________ | | | | | | ||| | ||| | | | | __ | |_______________| __ """ p = port a = p.orientation ca = np.cos(a * np.pi / 180) sa = np.sin(a * np.pi / 180) rot_mat = np.array([[ca, -sa], [sa, ca]]) d = p.width / 2 + port_margin dbot = np.array([pin_length / 2, -d]) dtop = np.array([pin_length / 2, d]) dbotin = np.array([-pin_length / 2, -d]) dtopin = np.array([-pin_length / 2, +d]) p0 = p.position + _rotate(dbot, rot_mat) p1 = p.position + _rotate(dtop, rot_mat) ptopin = p.position + _rotate(dtopin, rot_mat) pbotin = p.position + _rotate(dbotin, rot_mat) polygon = [p0, p1, ptopin, pbotin] component.add_polygon(polygon, layer=layer) if label_layer: component.add_label( text=str(p.name), position=p.midpoint, layer=label_layer, )
def add_pins_container( component: Component, add_pins_function=add_pins_triangle, ) -> Component: c = Component() ref = c << component add_pins_function(component=c, reference=ref) c.ref = ref return c
def add_pin_square_inside( component: Component, port: Port, pin_length: float = 0.1, layer: Tuple[int, int] = LAYER.PORT, layer_label: Optional[Tuple[int, int]] = LAYER.TEXT, ) -> None: """Add square pin towards the inside of the port Args: component: port: Port pin_length: length of the pin marker for the port layer: for the pin marker layer_label: for the label .. code:: _______________ | | | | | | || | || | | | | __ | |_______________| """ p = port a = p.orientation ca = np.cos(a * np.pi / 180) sa = np.sin(a * np.pi / 180) rot_mat = np.array([[ca, -sa], [sa, ca]]) d = p.width / 2 dbot = np.array([0, -d]) dtop = np.array([0, d]) dbotin = np.array([-pin_length, -d]) dtopin = np.array([-pin_length, +d]) p0 = p.position + _rotate(dbot, rot_mat) p1 = p.position + _rotate(dtop, rot_mat) ptopin = p.position + _rotate(dtopin, rot_mat) pbotin = p.position + _rotate(dbotin, rot_mat) polygon = [p0, p1, ptopin, pbotin] component.add_polygon(polygon, layer=layer) if layer_label: component.add_label( text=str(p.name), position=p.midpoint, layer=layer_label, )
def bend_euler_s(**kwargs) -> Component: """Sbend made of euler bends.""" c = Component() b = bend_euler(**kwargs) b1 = c.add_ref(b) b2 = c.add_ref(b) b2.mirror() b2.connect("o1", b1.ports["o2"]) c.add_port("o1", port=b1.ports["o1"]) c.add_port("o2", port=b2.ports["o2"]) return c
def cdsem_uturn( width: float = 0.5, radius: float = 10.0, wg_length: float = LINE_LENGTH, straight: ComponentFactory = straight, bend90: ComponentFactory = bend_circular, layer: Tuple[int, int] = LAYER.WG, layers_cladding: List[Tuple[int, int]] = None, cross_section: CrossSectionFactory = strip, pixel_size: float = 1.0, ) -> Component: """ Args: width: of the line cladding_offset: radius: bend radius wg_length """ c = Component() r = radius cross_section = partial(cross_section, width=width, layer=layer) if wg_length is None: wg_length = 2 * r bend90 = bend90(cross_section=cross_section, radius=r) wg = straight( cross_section=cross_section, length=wg_length, ) # Add the U-turn on straight layer b1 = c.add_ref(bend90) b2 = c.add_ref(bend90) b2.connect("o2", b1.ports["o1"]) wg1 = c.add_ref(wg) wg1.connect("o1", b1.ports["o2"]) wg2 = c.add_ref(wg) wg2.connect("o1", b2.ports["o1"]) label = c << manhattan_text( text=str(int(width * 1e3)), size=pixel_size, layer=layer) label.ymax = b2.ymin - 5 label.x = 0 b1.rotate(90) b2.rotate(90) wg1.rotate(90) wg2.rotate(90) label.rotate(90) return c
def demo(length: int = 3, wg_width: float = 0.5) -> Component: """Demo Dummy cell""" c = Component() w = length h = wg_width points = [ [-w / 2.0, -h / 2.0], [-w / 2.0, h / 2], [w / 2, h / 2], [w / 2, -h / 2.0], ] c.add_polygon(points) return c
def _demo_manhattan_fail() -> Component: waypoints = [ [10.0, 0.0], [20.0, 0.0], [20.0, 12.0], [120.0, 12.0], [120.0, 80.0], [110.0, 80.0], ] route = round_corners(waypoints, radius=10.0, with_point_markers=False) c = Component() c.add(route.references) return c
def test_manhattan_pass() -> Component: waypoints = [ [10.0, 0.0], [20.0, 0.0], [20.0, 12.0], [120.0, 12.0], [120.0, 80.0], [110.0, 80.0], ] route = round_corners(waypoints, radius=5) c = Component() c.add(route.references) return c
def cdsem_bend180( width: float = 0.5, radius: float = 10.0, wg_length: float = LINE_LENGTH, straight: ComponentFactory = straight_function, bend90: ComponentFactory = bend_circular, cross_section: CrossSectionFactory = strip, text: ComponentFactory = text_rectangular_mini, ) -> Component: """ Args: width: of the line cladding_offset: radius: bend radius wg_length """ c = Component() r = radius cross_section = partial(cross_section, width=width) if wg_length is None: wg_length = 2 * r bend90 = bend90(cross_section=cross_section, radius=r) wg = straight( cross_section=cross_section, length=wg_length, ) # Add the U-turn on straight layer b1 = c.add_ref(bend90) b2 = c.add_ref(bend90) b2.connect("o2", b1.ports["o1"]) wg1 = c.add_ref(wg) wg1.connect("o1", b1.ports["o2"]) wg2 = c.add_ref(wg) wg2.connect("o1", b2.ports["o1"]) label = c << text(text=str(int(width * 1e3))) label.ymax = b2.ymin - 5 label.x = 0 b1.rotate(90) b2.rotate(90) wg1.rotate(90) wg2.rotate(90) label.rotate(90) return c
def test_manhattan_fail() -> Component: waypoints = [ [10.0, 0.0], [20.0, 0.0], [20.0, 12.0], [120.0, 12.0], [120.0, 80.0], [110.0, 80.0], ] with pytest.warns(RouteWarning): route = round_corners(waypoints, radius=10.0, with_point_markers=False) c = Component() c.add(route.references) return c
def add_padding_container( component: Component, layers: Tuple[Layer, ...] = (LAYER.PADDING, ), **kwargs, ) -> Component: """Returns new component with padding added. Args: component layers: list of layers default: default padding top: north padding bottom: south padding right: east padding left: west padding """ c = Component() c.component = component cref = c << component points = get_padding_points(component, **kwargs) for layer in layers: c.add_polygon(points, layer=layer) c.ports = cref.ports c.copy_child_info(component) return c
def L( width: Union[int, float] = 1, size: Tuple[int, int] = (10, 20), layer: Tuple[int, int] = LAYER.M3, port_type: str = "electrical", ) -> Component: """Generates an 'L' geometry with ports on both ends. Based on phidl. Args: width: of the line size: length and height of the base layer: """ D = Component() w = width / 2 s1, s2 = size points = [(-w, -w), (s1, -w), (s1, w), (w, w), (w, s2), (-w, s2), (-w, -w)] D.add_polygon(points, layer=layer) D.add_port(name="e1", midpoint=(0, s2), width=width, orientation=90, port_type=port_type) D.add_port(name="e2", midpoint=(s1, 0), width=width, orientation=0, port_type=port_type) return D
def C( width: float = 1.0, size: Tuple[float, float] = (10.0, 20.0), layer: Tuple[int, int] = LAYER.M3, ) -> Component: """Generates a 'C' geometry with ports on both ends. Adapted from phidl Args: width: of the line size: length and height of the base layer: """ D = Component() w = width / 2 s1, s2 = size points = [ (-w, -w), (s1, -w), (s1, w), (w, w), (w, s2 - w), (s1, s2 - w), (s1, s2 + w), (-w, s2 + w), (-w, -w), ] D.add_polygon(points, layer=layer) D.add_port(name="o1", midpoint=(s1, s2), width=width, orientation=0) D.add_port(name="o2", midpoint=(s1, 0), width=width, orientation=0) return D
def add_text( component: ComponentOrFactory, text: str = "", text_offset: Float2 = (0, 0), text_anchor: Anchor = "cc", text_factory: ComponentFactory = text_rectangular_multi_layer, ) -> Component: """Returns component inside a new component with text geometry. Args: component: text: text string. text_offset: relative to component anchor. Defaults to center (cc). text_anchor: relative to component (ce cw nc ne nw sc se sw center cc). text_factory: function to add text labels. """ component = component() if callable(component) else component component_new = Component() component_new.component = component ref = component_new.add_ref(component) t = component_new << text_factory(text) t.move((np.array(text_offset) + getattr(ref.size_info, text_anchor))) component_new.add_ports(ref.ports) component_new.copy_child_info(component) return component_new
def extend_port(port: Port, length: float, layer: Optional[Layer] = None) -> Component: """Returns a straight extension component out of a port. Args: port: port to extend length: extension length layer: for the straight section """ c = Component() layer = layer or port.layer # Generate a port extension p_start = port.midpoint angle = port.angle p_end = move_polar_rad_copy(p_start, angle * DEG2RAD, length) w = port.width _line = line(p_start, p_end, w) c.add_polygon(_line, layer=layer) c.add_port(name="original", port=port) port_settings = port.settings.copy() port_settings.update(midpoint=p_end) c.add_port(**port_settings) return c
def ramp( length: float = 10.0, width1: float = 5.0, width2: Optional[float] = 8.0, layer: Layer = (1, 0), ) -> Component: """Return a ramp component. Based on phidl. Args: length: Length of the ramp section. width1: Width of the start of the ramp section. width2: Width of the end of the ramp section (defaults to width1). layer: Specific layer to put polygon geometry on. """ if width2 is None: width2 = width1 xpts = [0, length, length, 0] ypts = [width1, width2, 0, 0] c = Component() c.add_polygon([xpts, ypts], layer=layer) c.add_port(name="o1", midpoint=[0, width1 / 2], width=width1, orientation=180) c.add_port(name="o2", midpoint=[length, width2 / 2], width=width2, orientation=0) return c
def coupler90(gap: float = 0.2, radius: float = 10.0, bend: ComponentFactory = bend_euler, cross_section: CrossSectionFactory = strip, **kwargs) -> Component: r"""straight coupled to a bend. Args: gap: um radius: um straight: for straight bend: for bend cross_section: kwargs: cross_section settings .. code:: 3 | / / 2_/ 1____4 """ c = Component() x = cross_section(radius=radius, **kwargs) bend90 = (bend(cross_section=cross_section, radius=radius, **kwargs) if callable(bend) else bend) bend_ref = c << bend90 straight_component = (straight( cross_section=cross_section, length=bend90.ports["o2"].midpoint[0] - bend90.ports["o1"].midpoint[0], **kwargs) if callable(straight) else straight) wg_ref = c << straight_component width = x.info["width"] pbw = bend_ref.ports["o1"] bend_ref.movey(pbw.midpoint[1] + gap + width) c.absorb(wg_ref) c.absorb(bend_ref) c.add_port("o1", port=wg_ref.ports["o1"]) c.add_port("o4", port=wg_ref.ports["o2"]) c.add_port("o2", port=bend_ref.ports["o1"]) c.add_port("o3", port=bend_ref.ports["o2"]) return c
def from_np( ndarray: np.ndarray, nm_per_pixel: int = 20, layer: Tuple[int, int] = (1, 0), threshold: float = 0.99, ) -> Component: """Returns Component from a np.ndarray. Extracts contours skimage.measure.find_contours using `threshold`. Args: ndarray: 2D ndarray representing the device layout nm_per_pixel: scale_factor layer: layer tuple to output gds threshold: value along which to find contours in the array """ c = Component() d = Component() ndarray = np.pad(ndarray, 2) contours = measure.find_contours(ndarray, threshold) assert len(contours) > 0, ( f"no contours found for threshold = {threshold}, maybe you can reduce the" " threshold") for contour in contours: area = compute_area_signed(contour) points = contour * 1e-3 * nm_per_pixel if area < 0: c.add_polygon(points, layer=layer) else: d.add_polygon(points, layer=layer) c = boolean(c, d, operation="not", layer=layer) return c
def coupler_straight(length: float = 10.0, gap: float = 0.27, straight: ComponentFactory = straight_function, **kwargs) -> Component: """Coupler_straight with two parallel straights. Args: length: of straight gap: between straights straight: straight waveguide function kwargs: cross_section settings """ component = Component() straight_component = (straight(length=length, **kwargs) if callable(straight) else straight) top = component << straight_component bot = component << straight_component # bot.ymax = 0 # top.ymin = gap top.movey(straight_component.info.width + gap) component.add_port("o1", port=bot.ports["o1"]) component.add_port("o2", port=top.ports["o1"]) component.add_port("o3", port=bot.ports["o2"]) component.add_port("o4", port=top.ports["o2"]) component.auto_rename_ports() return component