def pad_array( pad: ComponentOrFactory = pad, spacing: Tuple[float, float] = (150.0, 150.0), columns: int = 6, rows: int = 1, orientation: int = 270, ) -> Component: """Returns 2D array of pads Args: pad: pad element spacing: x, y pitch columns: rows: orientation: port orientation in deg """ c = Component() pad = pad() if callable(pad) else pad size = pad.info.full.size c.info.size = size c.add_array(pad, columns=columns, rows=rows, spacing=spacing) width = size[0] if orientation in [90, 270] else size[1] for col in range(columns): for row in range(rows): c.add_port( name=f"e{row+1}{col+1}", midpoint=(col * spacing[0], row * spacing[1]), width=width, orientation=orientation, port_type="electrical", layer=pad.info["layer"], ) return c
def array( component: gf.types.ComponentOrFactory = straight, spacing: Tuple[float, float] = (150.0, 150.0), columns: int = 6, rows: int = 1, ) -> Component: """Returns an array of components. Args: component: to replicate spacing: x, y spacing columns: in x rows: in y Raises: ValueError: If columns > 1 and spacing[0] = 0 ValueError: If rows > 1 and spacing[1] = 0 .. code:: 2 rows x 4 columns ___ ___ ___ ___ | | | | | | | | |___| |___| |___| |___| ___ ___ ___ ___ | | | | | | | | |___| |___| |___| |___| """ if rows > 1 and spacing[1] == 0: raise ValueError(f"rows = {rows} > 1 require spacing[1] > 0") if columns > 1 and spacing[0] == 0: raise ValueError(f"columns = {columns} > 1 require spacing[0] > 0") c = Component() component = component() if callable(component) else component c.add_array(component, columns=columns, rows=rows, spacing=spacing) for col in range(columns): for row in range(rows): for port in component.ports.values(): name = f"{port.name}_{row+1}_{col+1}" c.add_port(name, port=port) c.ports[name].move((col * spacing[0], row * spacing[1])) return c
def dbr( w1: float = w1, w2: float = w2, l1: float = period / 2, l2: float = period / 2, n: int = 10, straight: ComponentFactory = straight_function, ) -> Component: """Distributed Bragg Reflector Args: w1: thin width l1: thin length w2: thick width l2: thick length n: number of periods straight: function .. code:: l1 l2 <-----><--------> _________ _______| w1 w2 ... n times _______ |_________ """ c = Component() l1 = gf.snap.snap_to_grid(l1) l2 = gf.snap.snap_to_grid(l2) cell = dbr_cell( w1=w1, w2=w2, l1=l1, l2=l2, straight=straight, ) c.add_array(cell, columns=n, rows=1, spacing=(l1 + l2, 100)) c.add_port("o1", port=cell.ports["o1"]) p1 = c.add_port("o2", port=cell.ports["o2"]) p1.midpoint = [(l1 + l2) * n, 0] return c
def contact( size: Tuple[float, float] = (11.0, 11.0), layers: Tuple[Layer, ...] = (LAYER.M1, LAYER.M2, LAYER.M3), vias: Optional[Tuple[Optional[ComponentOrFactory], ...]] = (via1, via2), layer_port: Optional[Layer] = None, ) -> Component: """Rectangular contact Args: size: of the layers layers: layers on which to draw rectangles vias: vias to use to fill the rectangles layer_port: if None asumes port is on the last layer """ width, height = size a = width / 2 b = height / 2 layer_port = layer_port or layers[-1] c = Component() c.height = height c.info.size = (float(size[0]), float(size[1])) c.info.layer = layer_port for layer in layers: ref = c << compass(size=(width, height), layer=layer) if layer == layer_port: c.add_ports(ref.ports) vias = vias or [] for via in vias: if via is not None: via = via() if callable(via) else via w, h = via.info["size"] g = via.info["enclosure"] pitch_x, pitch_y = via.info["spacing"] nb_vias_x = (width - w - 2 * g) / pitch_x + 1 nb_vias_y = (height - h - 2 * g) / pitch_y + 1 nb_vias_x = int(floor(nb_vias_x)) or 1 nb_vias_y = int(floor(nb_vias_y)) or 1 ref = c.add_array( via, columns=nb_vias_x, rows=nb_vias_y, spacing=(pitch_x, pitch_y) ) cw = (width - (nb_vias_x - 1) * pitch_x - w) / 2 ch = (height - (nb_vias_y - 1) * pitch_y - h) / 2 x0 = -a + cw + w / 2 y0 = -b + ch + h / 2 ref.move((x0, y0)) return c
def contact_with_offset( layers: Layers = (LAYER.PPP, LAYER.M1), sizes: Tuple[Tuple[float, float], ...] = ((10, 10), (10, 10)), vias: Tuple[Optional[ComponentOrFactory], ...] = (None, viac), offsets: Optional[Tuple[float, ...]] = None, port_orientation: int = 180, ) -> Component: """Rectangular layer transition with offset between layers Args: layers: vias: factory for via or None for no via sizes: offsets: for next layer port_orientation: 180: W0, 0: E0, 90: N0, 270: S0 """ c = Component() y0 = y1 = 0 offsets = offsets or [0] * len(layers) for layer, via, size, offset in zip(layers, vias, sizes, offsets): width, height = size x0 = -width / 2 x1 = +width / 2 y1 = y0 + height rect_pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)] c.add_polygon(rect_pts, layer=layer) if via: via = via() if callable(via) else via w, h = via.info["size"] enclosure = via.info["enclosure"] pitch_x, pitch_y = via.info["spacing"] nb_vias_x = (width - w - 2 * enclosure) / pitch_x + 1 nb_vias_y = (height - h - 2 * enclosure) / pitch_y + 1 nb_vias_x = int(floor(nb_vias_x)) or 1 nb_vias_y = int(floor(nb_vias_y)) or 1 cw = (width - (nb_vias_x - 1) * pitch_x - w) / 2 ch = (height - (nb_vias_y - 1) * pitch_y - h) / 2 x00 = x0 + cw + w / 2 y00 = y0 + ch + h / 2 + offset ref = c.add_array(via, columns=nb_vias_x, rows=nb_vias_y, spacing=(pitch_x, pitch_y)) ref.move((x00, y00)) y0 += offset y1 = y0 + height rect_pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)] c.add_polygon(rect_pts, layer=layer) port_width = height if port_orientation in [0, 180] else width if port_orientation not in [0, 90, 270, 180]: raise ValueError( f"Invalid port_orientation = {port_orientation} not in [0, 90, 180, 270]" ) c.add_port( name="e1", width=port_width, orientation=port_orientation, midpoint=(0, y1), port_type="electrical", ) return c
def contact_slot( size: Float2 = (11.0, 11.0), layers: Layers = (LAYER.M1, LAYER.M2), layer_offsets: Optional[Floats] = (0, 1.0), layer_offsetsx: Optional[Floats] = None, layer_offsetsy: Optional[Floats] = None, layer_port: Optional[Layer] = None, via: ComponentOrFactory = via1, enclosure: float = 1.0, ysize: float = 0.5, yspacing: float = 2.0, ) -> Component: """Rectangular contact with slotted via in X direction Args: size: of the layers layers: layers on which to draw rectangles layer_offsets: cladding_offset for each layer layer_offsetsx: optional xoffset for layers, defaults to layer_offsets layer_offsetsx: optional yoffset for layers, defaults to layer_offsets layer_port: if None asumes port is on the last layer via: via to use to fill the rectangles enclosure: of the via by rectangle ysize: via height in y yspacing: via spacing pitch in y .. code:: __________________________________________ | | | | | layer_offsetsy[1] | | ______________|______________________ | | |<---> |<>| | |enclosure | layer_offsetsx[1] | | ______________________ | | | | | | | | | | | via | ysize| | | | |______________________| | | | | | | | | | | yspacing size[1]| | | | | | | | | | ______________________ | | | | | | | | | | | | | via | ysize| | | | | |______________________| | | | | | | | | | | | |___________________________________| | | size[0] | | | |_________________________________________| """ if size[0] - 2 * enclosure < 0: raise ValueError( f"Contact length (size[0] = {size[0]}) < 2*enclosure ({2*enclosure}). " ) if size[1] - 2 * enclosure < 0: raise ValueError( f"Contact width (size[1] = {size[1]}) < 2*enclosure ({2*enclosure}). " ) layer_port = layer_port or layers[-1] c = Component() c.info.size = (float(size[0]), float(size[1])) layer_offsetsx = layer_offsetsx or layer_offsets layer_offsetsy = layer_offsetsy or layer_offsets layer_offsetsx = list(layer_offsetsx) + [0] * len(layers) layer_offsetsy = list(layer_offsetsy) + [0] * len(layers) for layer, offsetx, offsety in zip(layers, layer_offsetsx, layer_offsetsy): ref = c << compass(size=(size[0] + 2 * offsetx, size[1] + 2 * offsety), layer=layer) if layer == layer_port: c.add_ports(ref.ports) via = via(size=(size[0] - 2 * enclosure, ysize)) if callable(via) else via nb_vias_y = (size[1] - 2 * enclosure) / yspacing nb_vias_y = int(floor(nb_vias_y)) or 1 ref = c.add_array(via, columns=1, rows=nb_vias_y, spacing=(0, yspacing)) dy = (size[1] - (nb_vias_y - 1) * yspacing - size[1]) / 2 ref.move((0, dy)) return c
def contact( size: Tuple[float, float] = (11.0, 11.0), layers: Tuple[Layer, ...] = (LAYER.M1, LAYER.M2, LAYER.M3), vias: Optional[Tuple[Optional[ComponentOrFactory], ...]] = (via1, via2), layer_port: Optional[Layer] = None, ) -> Component: """Rectangular via array stack You can use it to connect different metal layers or metals to silicon. You can use the naming convention contact_layerSource_layerDestination Via array / stack name is more common for contacting metal while contact is used for contacting silicon http://www.vlsi-expert.com/2017/12/vias.html Args: size: of the layers layers: layers on which to draw rectangles vias: vias to use to fill the rectangles layer_port: if None asumes port is on the last layer """ width, height = size a = width / 2 b = height / 2 layer_port = layer_port or layers[-1] c = Component() c.height = height c.info.size = (float(size[0]), float(size[1])) c.info.layer = layer_port for layer in layers: ref = c << compass(size=(width, height), layer=layer) if layer == layer_port: c.add_ports(ref.ports) vias = vias or [] for via in vias: if via is not None: via = via() if callable(via) else via w, h = via.info["size"] g = via.info["enclosure"] pitch_x, pitch_y = via.info["spacing"] nb_vias_x = (width - w - 2 * g) / pitch_x + 1 nb_vias_y = (height - h - 2 * g) / pitch_y + 1 nb_vias_x = int(floor(nb_vias_x)) or 1 nb_vias_y = int(floor(nb_vias_y)) or 1 ref = c.add_array(via, columns=nb_vias_x, rows=nb_vias_y, spacing=(pitch_x, pitch_y)) cw = (width - (nb_vias_x - 1) * pitch_x - w) / 2 ch = (height - (nb_vias_y - 1) * pitch_y - h) / 2 x0 = -a + cw + w / 2 y0 = -b + ch + h / 2 ref.move((x0, y0)) return c
def contact_slot( size: Tuple[float, float] = (11.0, 11.0), layers: Tuple[Layer, ...] = (LAYER.M1, LAYER.M2), layer_offsets: Tuple[float, ...] = (0, 1.0), layer_port: Optional[Layer] = None, via: ComponentOrFactory = via1, enclosure: float = 1.0, ysize: float = 0.5, yspacing: float = 2.0, ) -> Component: """Rectangular contact with slotted via in X direction Args: size: of the layers layers: layers on which to draw rectangles layer_offsets: cladding_offset for each layer layer_port: if None asumes port is on the last layer via: via to use to fill the rectangles enclosure: of the via by rectangle ysize: via height in y yspacing: via spacing pitch in y .. code:: enclosure _____________________________________ |<---> | | ______________________ | | | | | | | | ysize| | |______________________| | | | | | | yspacing | | | | | | ______________________ | | | | | | | | | | ysize| | | |______________________| | | | |___________________________________| size[0] """ layer_port = layer_port or layers[-1] c = Component() for layer, offset in zip(layers, list(layer_offsets) + [0] * len(layers)): ref = c << compass(size=(size[0] + 2 * offset, size[1] + 2 * offset), layer=layer) if layer == layer_port: c.add_ports(ref.ports) via = via(size=(size[0] - 2 * enclosure, ysize)) if callable(via) else via nb_vias_y = (size[1] - 2 * enclosure) / yspacing nb_vias_y = int(floor(nb_vias_y)) or 1 ref = c.add_array(via, columns=1, rows=nb_vias_y, spacing=(0, yspacing)) dy = (size[1] - (nb_vias_y - 1) * yspacing - size[1]) / 2 ref.move((0, dy)) return c