def pack_manual( self, cell: Cell, x: float, y: float, origin: Tuple[int, int] = ap.SOUTH_WEST, tboxes: None = None, ) -> None: """ Pack a cell at a manually selected position """ box = cell.bbox() # Do an offset if origin is given w = box.width() h = box.height() oy, ox = origin x -= {ap.WEST: 0, ap.EAST: w, ap.MIDDLE: w / 2}[ox] y -= {ap.SOUTH: 0, ap.NORTH: w, ap.MIDDLE: h / 2}[oy] # Insert into the quadtree if tboxes is None: tbox = (x, y, x + box.width(), y + box.height()) tboxes = [tbox] for tbox in tboxes: self.quadtree.insert(tbox, tbox) new_cell = self.import_cell(cell) # Make an instance transform = pya.Trans(int(x - box.left), int(y - box.bottom)) new_instance = pya.CellInstArray(new_cell.cell_index(), transform) self.cell(self.name).insert(new_instance)
def placer_grid_cell_refs( cells, cols: int = 1, rows: int = 1, dx: float = 10.0, dy: float = 10.0, x0: float = 0, y0: float = 0, um_to_grid: float = UM_TO_GRID, **settings, ): """cells: list of cells - order matters for placing""" indices = [(i, j) for j in range(cols) for i in range(rows)] if rows * cols < len(cells): raise ValueError( "Shape ({}, {}): Not enough emplacements ({}) for all these components" " ({}).".format(rows, cols, len(indices), len(cells))) components = [] for cell, (i, j) in zip(cells, indices): _x = int((x0 + j * dx) * um_to_grid) _y = int((y0 + i * dy) * um_to_grid) transform = pya.Trans(_x, _y) c_ref = pya.CellInstArray(cell.cell_index(), transform) components += [c_ref] return components
def port_to_pin_helper(ports_list, cell, layerPinRec): """ Draws port shapes for visual help in KLayout. """ # Create the pins, as short paths: # from siepic_tools.config import PIN_LENGTH PIN_LENGTH = 100 dbu = cell.layout().dbu for port in ports_list: if port.name.startswith("el"): pin_length = port.width else: pin_length = PIN_LENGTH * dbu port_position_i = port.position.to_itype(dbu) cell.shapes(layerPinRec).insert( kdb.DPath( [ port.position - 0.5 * pin_length * port.direction, port.position + 0.5 * pin_length * port.direction, ], port.width, ).to_itype(dbu)) cell.shapes(layerPinRec).insert( kdb.Text( port.name, kdb.Trans(kdb.Trans.R0, port_position_i.x, port_position_i.y))).text_size = (2 / dbu)
def assemble_subdies( mask_name, dict_subdies, subdies_directory, mask_directory=None, um_to_grid=UM_TO_GRID, ): """ Args: dict_subdies: {subdie_name: (x, y, rotation) in (um, um, deg)} subdies_directory: directory where the subdies should be looked for """ top_level_layout = pya.Layout() top_level = top_level_layout.create_cell(mask_name) if mask_directory is None: mask_directory = subdies_directory for subdie_name, (x_um, y_um, R) in dict_subdies.items(): gdspath = os.path.join(subdies_directory, subdie_name + ".gds") subdie = load_gds(gdspath).top_cell() _subdie = import_cell(top_level_layout, subdie) t = pya.Trans(R / 2, 0, int(x_um * um_to_grid), int(y_um * um_to_grid)) # t = pya.Trans(0, 0) subdie_instance = pya.CellInstArray(_subdie.cell_index(), t) top_level.insert(subdie_instance) top_level.write(os.path.join(mask_directory, mask_name + ".gds")) return top_level
def placer_fixed_coords( cells, x, y, x0: float = 0, y0: float = 0, do_permutation: bool = False, um_to_grid=UM_TO_GRID, **kwargs, ): """place cells using a list of coordinates""" # List all coordinates if do_permutation: coords = [(_x, _y) for _x in x for _y in y] else: coords = [(_x, _y) for _x, _y in zip(x, y)] # Update origin coords = [(c[0] + x0, c[1] + y0) for c in coords] # Generate cell list if len(cells) == 1: cells = cells * len(coords) # update coordinates from um to grid coords = [(int(c[0] * um_to_grid), int(c[1] * um_to_grid)) for c in coords] # Generate transforms transforms = [pya.Trans(*c) for c in coords] return [ pya.CellInstArray(c.cell_index(), t) for c, t in zip(cells, transforms) ]
def _demo(): import pp c = pp.c.waveguide() gdspath = pp.write_component(c) layout1 = load_gds(gdspath) cell1 = layout1.top_cell() cell1_instance1 = pya.CellInstArray(cell1.cell_index(), pya.Trans(10, 0)) layout2 = pya.Layout() cell2 = layout2.create_cell("TOP_LEVEL") layout2.cell("TOP_LEVEL").insert(cell1_instance1) layout2.write("test.gds")
def _demo(): import gdsfactory as gf c = gf.components.straight() gdspath = c.write_gds_with_metadata() layout1 = load_gds(gdspath) cell1 = layout1.top_cell() cell1_instance1 = pya.CellInstArray(cell1.cell_index(), pya.Trans(10, 0)) layout2 = pya.Layout() layout2.create_cell("TOP_LEVEL") layout2.cell("TOP_LEVEL").insert(cell1_instance1) layout2.write("test.gds")
def make_dicing_lanes(self): """Make the dicing lanes""" container = self.create_cell("DicingLanes") instance = pya.CellInstArray(container.cell_index(), pya.Trans(0, 0)) self.cell(self.name).insert(instance) lw = self.lane_width / 2 for dicing_layer in ap.DICING_LAYERS: layer = self.layer(dicing_layer[0], dicing_layer[1]) for row in range(1, self.rows): y = row * (self.chip_height + self.spacing) - self.spacing / 2 box = pya.Box(0, y - lw, self.max_width, y + lw) container.shapes(layer).insert(box) for col in range(1, self.cols): x = col * (self.chip_width + self.spacing) - self.spacing / 2 for row in range(self.rows): y1 = row * (self.chip_height + self.spacing) y2 = (row + 1) * (self.chip_height + self.spacing) - self.spacing box = pya.Box(x - lw, y1, x + lw, y2) container.shapes(layer).insert(box) # on the corners, line has half the width lw = self.lane_width / 4 for row in [0, self.rows]: y = row * (self.chip_height + self.spacing) - self.spacing / 2 box = pya.Box(0, y - lw, self.max_width, y + lw) container.shapes(layer).insert(box) for col in [0, self.cols]: x = col * (self.chip_width + self.spacing) - self.spacing / 2 for row in range(self.rows): y1 = row * (self.chip_height + self.spacing) y2 = (row + 1) * (self.chip_height + self.spacing) - self.spacing box = pya.Box(x - lw, y1, x + lw, y2) container.shapes(layer).insert(box)
def place_from_yaml( filepath_yaml: Path, root_does: Path = CONFIG["cache_doe_directory"], precision: float = 1e-9, fontpath: Path = text.FONT_PATH, default_align_x: NSEW = "W", default_align_y: NSEW = "S", default_margin: int = 10, default_x0: NSEW = "E", default_y0: NSEW = "S", ) -> Cell: """Returns a gds cell composed of DOEs/components given in a yaml file allows for each DOE to have its own x and y spacing (more flexible than method1) Args: filepath_yaml: root_does: used for cache, requires content.txt """ transform_identity = pya.Trans(0, 0) dicts, mask_settings = load_yaml(filepath_yaml) does, templates = separate_does_from_templates(dicts) placed_doe = None placed_does = {} top_level_name = mask_settings.get("name", "TOP_LEVEL") layer_doe_label = mask_settings["layer_doe_label"] top_level_layout = pya.Layout() # Set database units according to precision top_level_layout.dbu = precision / 1e-6 dbu = top_level_layout.dbu um_to_grid = int(1 / dbu) top_level = top_level_layout.create_cell(top_level_name) global CELLS CELLS[top_level_name] = top_level_layout default_doe_settings = { "add_doe_label": False, "add_doe_visual_label": False, "dx_visual_label": 0, "dy_visual_label": 0, } for doe_name, doe in does.items(): # If a template is specified, apply it if "template" in doe: doe_templates = doe["template"] if type(doe_templates) != list: doe_templates = [doe_templates] for doe_template in doe_templates: try: doe = update_dicts_recurse(doe, templates[doe_template]) except BaseException: print(doe_template, "does not exist") raise doe = update_dicts_recurse(doe, default_doe_settings) # Get all the components components = load_doe(doe_name, root_does) # Check that the high level components are all unique # For now this is mostly to circumvent a bug # But the design manual also specifies that DOE components should have # unique names. So one instance per cell if components: if len(components) != len( set([_c.top_cell().name for _c in components])): __dict_component_debug = {} for _c in components: _name = _c.top_cell().name if _name not in __dict_component_debug: __dict_component_debug[_name] = 0 __dict_component_debug[_name] += 1 duplicates_components = [ _name for _name, _count in __dict_component_debug.items() if _count > 1 ] print( "Please remove duplicate components at DOE entry level: ") print(duplicates_components) components = [ import_cell(top_level_layout, _c.top_cell()) for _c in components ] default_placer_settings = { "align_x": default_align_x, "align_y": default_align_y, "margin": default_margin, "x0": default_x0, "y0": default_y0, } settings = default_placer_settings.copy() placer = doe.get("placer") if placer: placer_type = placer.pop("type", "pack_col") settings.update(doe["placer"]) else: placer_type = "pack_col" if placer_type not in PLACER_NAME2FUNC: raise ValueError( f"{placer_type} is not an available placer, Choose:" f" {list(PLACER_NAME2FUNC.keys())}") _placer = PLACER_NAME2FUNC[placer_type] # All other attributes are assumed to be settings for the placer # Check if the cell should be attached to a specific parent cell if "parent" in settings: parent_name = settings.pop("parent") if parent_name not in CELLS: # Create parent cell in layout and insert it under top level parent_cell = top_level_layout.create_cell(parent_name) CELLS[parent_name] = parent_cell parent_cell_instance = pya.CellInstArray( parent_cell.cell_index(), transform_identity) top_level.insert(parent_cell_instance) doe_parent_cell = CELLS[parent_name] else: # If no parent specified, insert the DOE at top level doe_parent_cell = top_level # Check if we should create a DOE cell which regroups the DOEs if "with_doe_cell" in settings: with_doe_cell = settings.pop("with_doe_cell") else: with_doe_cell = True # x0, y0 can either be float or string x0 = settings.pop("x0") y0 = settings.pop("y0") # Check whether we are doing relative or absolute placement # if (x0 in ["E", "W"] or y0 in ["N", "S"]) and not placed_doe: # raise ValueError( # "At least one DOE must be placed to use relative placement" # ) # For relative placement (to previous DOE) if "margin_x" not in settings: settings["margin_x"] = settings["margin"] if "margin_y" not in settings: settings["margin_y"] = settings["margin"] if "inter_margin_x" not in settings: inter_margin_x = settings["margin_x"] else: inter_margin_x = settings.pop("inter_margin_x") if "inter_margin_y" not in settings: inter_margin_y = settings["margin_y"] else: inter_margin_y = settings.pop("inter_margin_y") align_x = settings["align_x"] align_y = settings["align_y"] # Make sure that the alignment is sensible depending on how we stack # If we specify a DOE to place next to, use it if "next_to" in settings: placed_doe = placed_does[settings.pop("next_to")] # print(placed_doe) # print(placed_does) # Otherwise, use previously placed DOE as starting point doe_si = (SizeInfo(placed_doe, top_level_layout, um_to_grid=um_to_grid) if placed_doe is not None else None) if x0 == "E": x0 = doe_si.east if align_x == "W": x0 += inter_margin_x if x0 == "W": x0 = doe_si.west if align_x == "E": x0 -= inter_margin_x if y0 == "N": y0 = doe_si.north if align_y == "S": y0 += inter_margin_y if y0 == "S": y0 = doe_si.south if align_y == "N": y0 -= inter_margin_y # Add x0, y0 in settings as float settings["x0"] = x0 settings["y0"] = y0 settings["um_to_grid"] = um_to_grid placed_components = _placer(components, **settings) # Place components within a cell having the DOE name if with_doe_cell or len(placed_components) > 1: doe_cell = top_level_layout.create_cell(doe_name) CELLS[doe_name] = doe_cell for instance in placed_components: doe_cell.insert(instance) placed_does[doe_name] = doe_cell placed_doe = doe_cell doe_instance = pya.CellInstArray(doe_cell.cell_index(), transform_identity) else: # If only single cell and we want to skip the sweep cell doe_instance = placed_components[0] placed_does[doe_name] = doe_instance placed_doe = doe_instance add_doe_label = doe["add_doe_label"] add_doe_visual_label = doe["add_doe_visual_label"] if add_doe_label: layer_label_index, layer_label_datatype = layer_doe_label layer_index = top_level.layout().insert_layer( pya.LayerInfo(layer_label_index, layer_label_datatype)) # Add the name of the DOE at the center of the cell _p = doe_instance.bbox(top_level_layout).center() _text = pya.Text(doe_name, _p.x, _p.y) top_level.shapes(layer_index).insert(_text) if add_doe_visual_label: _bbox = doe_instance.bbox(top_level_layout) idbu = 1 / top_level.layout().dbu x_text = _bbox.center().x + doe["dx_visual_label"] * idbu y_text = _bbox.bottom + (15.0 + doe["dy_visual_label"]) * idbu _text = text.add_text(top_level, doe_name, position=(x_text, y_text), fontpath=fontpath) # _transform = pya.DTrans(x_text, y_text) # top_level.insert(pya.CellInstArray(_text.cell_index(), _transform)) doe_parent_cell.insert(doe_instance) return top_level
def pack_col( cells: List[Cell], col_ids: None = None, nb_rows: Optional[int] = None, x0: float = 0, y0: float = 0, align_x: NSEW = "W", align_y: NSEW = "S", margin: int = 20, margin_x: Optional[int] = None, margin_y: Optional[int] = None, um_to_grid: int = UM_TO_GRID, period_x: Optional[float] = None, period_y: Optional[float] = None, rotation: int = 0, ) -> List[CellInstArray]: """ Args: cells: a list of cells (size n) col_ids: a list of column ids (size n) where each id represents the row where the cell should be placed None by default => all cells are packed in the same column Returns: list of cell references """ widths = [SizeInfo(c, um_to_grid=um_to_grid).width for c in cells] margin_y = margin_y if margin_y is not None else margin margin_x = margin_x if margin_x is not None else margin if col_ids is None: col_ids = [] nb_cells = len(cells) if nb_rows is None: nb_rows = len(cells) nb_full_cols = nb_cells // nb_rows nb_rows_last_col = nb_cells % nb_rows for col_id in range(nb_full_cols): col_ids += [col_id] * nb_rows last_col_index = col_id + 1 col_ids += [last_col_index] * nb_rows_last_col if len(cells) != len(col_ids): raise ValueError("Each cell should be assigned a row id. " f"Got {len(cells)} cells for {len(col_ids)} col ids") # Find the width of each column to fit the cells # Also group the cells by column unique_col_ids = list(set(col_ids)) unique_col_ids.sort() _col_to_widths = {r: [] for r in set(col_ids)} col_to_cells = {r: [] for r in unique_col_ids} for col, w, cell in zip(col_ids, widths, cells): _col_to_widths[col] += [w] col_to_cells[col] += [cell] col_to_width = {k: max(v) for k, v in _col_to_widths.items()} components = [] # Do the packing per column x = x0 for col in unique_col_ids: cells = col_to_cells[col] y = y0 for c in cells: si = SizeInfo(c, um_to_grid=um_to_grid) if align_x == "W" and align_y == "S": component_origin = si.sw elif align_x == "E" and align_y == "S": component_origin = si.se elif align_x == "E" and align_y == "N": component_origin = si.ne elif align_x == "W" and align_y == "N": component_origin = si.nw _x = to_grid(x - component_origin[0], um_to_grid=um_to_grid) _y = to_grid(y - component_origin[1], um_to_grid=um_to_grid) try: transform = pya.Trans(rotation / 2, 0, _x, _y) # transform = pya.Trans(_x, _y) c_ref = pya.CellInstArray(c.cell_index(), transform) components += [c_ref] except BaseException: print(x, component_origin[0], um_to_grid) print(y, component_origin[1], um_to_grid) print("ISSUE PLACING AT", _x, _y) print("ISSUE PLACING at", _x, _y) dy = si.height + margin_y if period_y is None else period_y if align_y == "S": y += dy else: y -= dy dx = col_to_width[col] + margin_x if period_x is None else period_x if align_x == "W": x += dx else: x -= dx return components
def pack_row( cells: List[Cell], row_ids: Optional[List[int]] = None, nb_cols: Optional[int] = None, x0: Union[float, int] = 0, y0: Union[float, int] = 0, align_x: NSEW = "W", align_y: NSEW = "S", margin: Union[float, int] = 20, margin_x: Optional[Union[float, int]] = None, margin_y: Optional[Union[float, int]] = None, um_to_grid: int = UM_TO_GRID, period_x: Optional[float] = None, period_y: Optional[float] = None, rotation: int = 0, ) -> List[CellInstArray]: """Pack row. Args: cells: a list of cells (size n) row_ids: a list of row ids (size n) where each id represents the row where the cell should be placed None by default => all cells in the same row nb_cols: number of columns period_x, period_y: not used by default, if set, use this period instead of computing the component spacing from the margin and the component dimension Returns: list of cell references """ si_list = [SizeInfo(c, um_to_grid=um_to_grid) for c in cells] heights = [si.height for si in si_list] margin_y = margin_y if margin_y is not None else margin margin_x = margin_x if margin_x is not None else margin if row_ids is None: row_ids = [] nb_cells = len(cells) if nb_cols is None: nb_cols = len(cells) nb_full_rows = nb_cells // nb_cols nb_cols_last_row = nb_cells % nb_cols for row_id in range(nb_full_rows): row_ids += [row_id] * nb_cols last_row_index = row_id + 1 row_ids += [last_row_index] * nb_cols_last_row if len(cells) != len(row_ids): raise ValueError("Each cell should be assigned a row id. " f"Got {len(cells)} cells for {len(row_ids)} row ids") # Find the height of each row to fit the cells # Also group the cells by row unique_row_ids = list(set(row_ids)) unique_row_ids.sort() _row_to_heights = {r: [] for r in set(row_ids)} row_to_cells = {r: [] for r in unique_row_ids} for row, h, cell in zip(row_ids, heights, cells): _row_to_heights[row] += [h] row_to_cells[row] += [cell] row_to_height = {k: max(v) for k, v in _row_to_heights.items()} components = [] # Do the packing per row y = y0 for row in unique_row_ids: cells = row_to_cells[row] x = x0 for c in cells: si = SizeInfo(c, um_to_grid=um_to_grid) if align_x == "W" and align_y == "S": component_origin = si.sw elif align_x == "E" and align_y == "S": component_origin = si.se elif align_x == "E" and align_y == "N": component_origin = si.ne elif align_x == "W" and align_y == "N": component_origin = si.nw try: _x = to_grid(x - component_origin[0], um_to_grid) _y = to_grid(y - component_origin[1], um_to_grid) transform = pya.Trans(rotation / 2, 0, _x, _y) c_ref = pya.CellInstArray(c.cell_index(), transform) components += [c_ref] except BaseException: logger.error(x, component_origin[0], um_to_grid) logger.error("ISSUE PLACING AT", _x, _y) if align_x not in ["W", "E"]: logger.error("align_x should be `W`, `E` or a float") if align_y not in ["N", "S"]: logger.error("align_y should be `N`, `S` or a float") # raise dx = si.width + margin_x if period_x is None else period_x if align_x == "W": x += dx else: x -= dx dy = row_to_height[row] + margin_y if period_y is None else period_y if align_y == "S": y += dy else: y -= dy return components
def transformation_from_shape_impl(self): return pya.Trans(self.shape.bbox().center())