def import_oas(filename, cellname = None, flatten = False): if filename.lower().endswith('.gds'): # you are looking for import_gds retval = pg.import_gds(filename, cellname = cellname, flatten = flatten) return retval try: import klayout.db as pya except ImportError as err: err.args = ('[PHIDL] klayout package needed to import OASIS. pip install klayout\n' + err.args[0], ) + err.args[1:] raise if not filename.lower().endswith('.oas'): filename += '.oas' fileroot = os.path.splitext(filename)[0] tempfilename = fileroot + '-tmp.gds' layout = pya.Layout() layout.read(filename) # We want to end up with one Device. If the imported layout has multiple top cells, # a new toplevel is created, and they go into the second level if len(layout.top_cells()) > 1: topcell = layout.create_cell('toplevel') rot_DTrans = pya.DTrans.R0 origin = pya.DPoint(0, 0) for childcell in layout.top_cells(): if childcell == topcell: continue topcell.insert(pya.DCellInstArray(childcell.cell_index(), pya.DTrans(rot_DTrans, origin))) else: topcell = layout.top_cell() topcell.write(tempfilename) retval = pg.import_gds(tempfilename, cellname = cellname, flatten = flatten) os.remove(tempfilename) return retval
def add_text( cell: Cell, text: str, position: Tuple[int, int] = (0, 0), align_x: str = "center", align_y: str = "top", fontpath: Path = FONT_PATH, ) -> Cell: """add text label""" text = text.upper() alphabet = load_alphabet(filepath=fontpath) idbu = 1 / cell.layout().dbu si = alphabet["A"].dbbox() x, y = position w = si.width() h = si.height() c = cell.layout().create_cell("TEXT_{}".format(text)) n = len(text) if align_x == "center": dx = -(n + 0.5) * w / 2 elif align_x == "right": dx = -n * w else: dx = 0 if align_y == "top": dy = -h elif align_y == "center": dy = -h / 2 else: dy = 0 for i, char in enumerate(text): _l = import_cell(cell.layout(), alphabet[char]) _transform = pya.DTrans((i * w + dx) * idbu, dy * idbu) label = pya.CellInstArray(_l.cell_index(), _transform) c.insert(label) cell.insert(pya.CellInstArray(c.cell_index(), pya.DTrans(x, y))) return c
def draw(self, cell, layer): """ Draws this port on cell's layer using klayout.db""" if self.name.startswith("el"): pin_length = self.width else: # port is optical pin_length = max(2, self.width / 10) ex = self.direction # Place a Path around the port pointing towards its exit port_path = kdb.DPath( [ self.position - 0.5 * pin_length * ex, self.position + 0.5 * pin_length * ex, ], self.width, ) cell.shapes(layer).insert(port_path) # Place a small arrow around the tip of the port from zeropdk.layout.geometry import rotate90 ey = rotate90(ex) port_tip = kdb.DSimplePolygon( [ self.position + 0.5 * pin_length * ex, self.position + 0.4 * pin_length * ex + 0.1 * pin_length * ey, self.position + 0.4 * pin_length * ex - 0.1 * pin_length * ey, ] ) cell.shapes(layer).insert(port_tip) # pin_rectangle = rectangle(self.position, self.width, # pin_length, ex, ey) # cell.shapes(layer).insert(pin_rectangle) # Place a text object annotating the name of the port cell.shapes(layer).insert( kdb.DText( self.name, kdb.DTrans(kdb.DTrans.R0, self.position.x, self.position.y), min(pin_length, 20), 0, ) ) return self
def place_cell( parent_cell, pcell, ports_dict, placement_origin, relative_to=None, transform_into=False, ): """ Places an pya cell and return ports with updated positions Args: parent_cell: cell to place into pcell, ports_dict: result of KLayoutPCell.pcell call placement_origin: pya.Point object to be used as origin relative_to: port name the cell is placed so that the port is located at placement_origin transform_into: if used with relative_into, transform the cell's coordinate system so that its origin is in the given port. Returns: ports(dict): key:port.name, value: geometry.Port with positions relative to parent_cell's origin """ ports = ports_dict cell = pcell port_offset = placement_origin if relative_to is not None: offset = ports[relative_to].position port_offset = placement_origin - offset if transform_into: # print(type(pcell)) offset_transform = kdb.DTrans(kdb.DTrans.R0, -offset) for instance in cell.each_inst(): instance.transform(offset_transform) cell.transform_into(offset_transform) else: placement_origin = placement_origin - offset parent_cell.insert_cell(cell, placement_origin, 0) new_ports = deepcopy(ports) for port in new_ports.values(): port.position += port_offset return new_ports
def rodGetObj(i): print "rodGetObject: " + str(i) if i in rodsByName: return rodsByName[i] s = i.split("/") if len(s) > 1: tx = [] print "rod subobject query: " + str(s) cmap = rodsByName inst = rodsByName[s[0]] dbox = None got_shape = False for e in s: e = cmap[e] cell = e['_shapes'][0] if isinstance(cell, db.Instance): cmap = rod_map[cell.cell.basic_name()] tx.append(cell.dtrans) else: got_shape = True dbox = e['_shapes'][0].dbbox() #accumulate the transforms if not got_shape: print dbox dbox.p1 = db.DPoint.new( 0, 0) #If only have a cell, then all stuff relative to origin dbox.p2 = dbox.p1 total = db.DTrans() for t in tx: total = total * t if not dbox: write() exit() dbox = total.trans(dbox) obj = createObj(dbox=dbox) obj['_slaves'].append(inst) inst['_masters'].append(obj) obj['_transform'] = total rodsByName[i] = obj return obj write() assert (False) exit(0) print "****************getObject failed" return None
def wrapper_draw(self, cell): layout = cell.layout() try: layer_map_dict[layout] except KeyError: layer_map_dict[layout] = pya.LayerMap() # Adding the dbu of the layout in the hash (bit us in the butt last time) short_hash_pcell = produce_hash(self, extra=(layout.dbu, extra_hash)) # cache paths cache_fname = f"cache_{self.__class__.__qualname__}_{short_hash_pcell}" cache_fname_gds = cache_fname + ".gds" cache_fname_pkl = cache_fname + ".klayout.pkl" os.makedirs(cache_dir, mode=0o775, exist_ok=True) cache_fpath_gds = os.path.join(cache_dir, cache_fname_gds) cache_fpath_pkl = os.path.join(cache_dir, cache_fname_pkl) if os.path.isfile(cache_fpath_gds) and os.path.isfile( cache_fpath_pkl): with open(cache_fpath_pkl, "rb") as file: ports, read_short_hash_pcell, cellname = pickle.load(file) # pylint: disable=unused-variable logger.debug( f"Reading from cache: {cache_fname}: {cellname}, {ports}") print("r", end="", flush=True) if not layout.has_cell(cache_fname): read_layout(layout, cache_fpath_gds, disambiguation_name=cellname) retrieved_cell = layout.cell(cache_fname) cell.insert( pya.DCellInstArray( retrieved_cell.cell_index(), pya.DTrans(pya.DTrans.R0, pya.DPoint(0, 0)), )) # cell.move_tree(retrieved_cell) else: if layout.has_cell(cache_fname): logger.warning( f"WARNING: {cache_fname_gds} does not exist but {cache_fname} is in layout." ) # populating .gds and .pkl empty_layout = pya.Layout() empty_layout.dbu = layout.dbu empty_cell = empty_layout.create_cell(cell.name) filled_cell, ports = draw(self, empty_cell) logger.debug( f"Writing to cache: {cache_fname}: {filled_cell.name}, {ports}" ) print("w", end="", flush=True) cellname, filled_cell.name = filled_cell.name, cache_fname # There can be duplicate cell names in subcells here. # We are saving a list of them inside a property named CACHE_PROP_ID # So we need to allow the properties to be saved inside the gds file (incompatible with the GDS2 standard) save_options = pya.SaveLayoutOptions() save_options.gds2_write_file_properties = True empty_layout.write(cache_fpath_gds, save_options) with open(cache_fpath_pkl, "wb") as file: pickle.dump((ports, short_hash_pcell, cellname), file) # Make sure we delete the empty_layout to not grow # helps debug layer_map_dict.pop(empty_layout, None) del empty_layout assert not layout.has_cell(cache_fname) read_layout(layout, cache_fpath_gds, disambiguation_name=cellname) retrieved_cell = layout.cell(cache_fname) cell.insert( pya.DCellInstArray( retrieved_cell.cell_index(), pya.DTrans(pya.DTrans.R0, pya.DPoint(0, 0)), )) return cell, ports
def wrapper_draw(self, cell): global layer_map_dict layout = cell.layout() try: layer_map_dict[layout] except KeyError: layer_map_dict[layout] = pya.LayerMap() # Adding the dbu of the layout in the hash (bit us in the butt last time) short_hash_pcell = produce_hash(self, extra=(layout.dbu, extra_hash)) # cache paths cache_fname = f"cache_{self.__class__.__qualname__}_{short_hash_pcell}" cache_fname_gds = cache_fname + ".gds" cache_fname_pkl = cache_fname + ".klayout.pkl" os.makedirs(cache_dir, mode=0o775, exist_ok=True) cache_fpath_gds = os.path.join(cache_dir, cache_fname_gds) cache_fpath_pkl = os.path.join(cache_dir, cache_fname_pkl) if os.path.isfile(cache_fpath_gds) and os.path.isfile(cache_fpath_pkl): with open(cache_fpath_pkl, "rb") as file: ports, read_short_hash_pcell, cellname = pickle.load(file) if debug: print(f"Reading from cache: {cache_fname}: {cellname}, {ports}") else: print("r", end="", flush=True) if not layout.has_cell(cache_fname): read_layout(layout, cache_fpath_gds) retrieved_cell = layout.cell(cache_fname) cell.insert( pya.DCellInstArray( retrieved_cell.cell_index(), pya.DTrans(pya.DTrans.R0, pya.DPoint(0, 0)), ) ) # cell.move_tree(retrieved_cell) else: if layout.has_cell(cache_fname): print( f"WARNING: {cache_fname_gds} does not exist but {cache_fname} is in layout." ) # populating .gds and .pkl empty_layout = pya.Layout() empty_layout.dbu = layout.dbu empty_cell = empty_layout.create_cell(cell.name) filled_cell, ports = draw(self, empty_cell) if debug: print( f"Writing to cache: {cache_fname}: {filled_cell.name}, {ports}" ) else: print("w", end="", flush=True) cellname, filled_cell.name = filled_cell.name, cache_fname filled_cell.write(cache_fpath_gds) with open(cache_fpath_pkl, "wb") as file: pickle.dump((ports, short_hash_pcell, cellname), file) # Make sure we delete the empty_layout to not grow # helps debug layer_map_dict.pop(empty_layout, None) del empty_layout assert not layout.has_cell(cache_fname) read_layout(layout, cache_fpath_gds) retrieved_cell = layout.cell(cache_fname) cell.insert( pya.DCellInstArray( retrieved_cell.cell_index(), pya.DTrans(pya.DTrans.R0, pya.DPoint(0, 0)), ) ) return cell, ports