import pya import operator # Note : this program works with simple transformations. # It should be modified in order to take into account complex transformations. class CellPlacement: """Placement of an instance relative to an ancestor.""" parent = None # ancestor trans = None # Transformation of the Cell in the ancestor def __init__(self, parent, trans): self.parent = parent self.trans = trans def __repr__(self): return "({})@'{}'".format(self.trans, self.parent) class CellInstances: """Instances of 'cell' located in the 'top' cell. The instances are leaves of the graph branch emerging from 'top'. """ layout = None # Current Layout cell = None # Target Cell (leaves) cell_idx = None # Target Cell index top = None # Top Cell (origin of the branch) top_idx = None # Top Cell index nodes = None # List of the nodes of the graph [indexes]. node_names = None links = None # Dictionary of elementary links... # For each node, it contains a list of the parent nodes # and the transformations [CellPlacement]. placement = None # List of transformations of the 'cell' leaves # seen from cell 'top' [pya.Trans]. def __init__(self, cell, top=None): """Localization of 'cell' leaves in the branch emerging from 'top'.""" # Initialization self.layout = config.layout if isinstance(cell, str) or isinstance(cell, int): _cell = self.layout.cell(cell) if _cell is None: raise ValueError("No cell '{}' was found.".format(cell)) cell = _cell self.cell = cell self.cell_idx = cell.cell_index() if isinstance(top, str) or isinstance(top, int): top = self.layout.cell(top) if top is None: top = config.top_cell self.top = top self.top_idx = top.cell_index() # Build the graph and find the instances self.nodes = self.get_nodes() self.node_names = self.get_node_names() self.links = self.get_links() self.placement = self.get_placement() def get_nodes(self): """Find the nodes of the graph [cell indexes].""" # List of all the cells calling 'cell' [indexes] caller_list = self.cell.caller_cells() # Check for consistency if self.top_idx not in caller_list: raise ValueError("Top cell '{}' ".format(self.top.name) + "is not a parent of cell '{}'.".format(self.cell.name)) # Initialization nodes = [self.cell_idx] # Find the nodes of the graph [indexes] for idx in caller_list: if self.top_idx in self.layout.cell(idx).caller_cells(): nodes.append(idx) # Add the 'top' node (not selected by the previous lines) nodes.append(self.top_idx) return nodes def get_node_names(self): """Returns the node names.""" return [self.layout.cell(idx).name for idx in self.nodes] def get_links(self): """Find the elementary links of the graph.""" links = {} links[self.top_idx] = [] # Origin of the branch child_nodes = self.nodes[:-1] parent_nodes = self.nodes[1:] for child_idx in child_nodes: # Cell indexes links[child_idx] = [] for parent in self.layout.cell(child_idx).each_parent_inst(): parent_idx = parent.parent_cell_index() if parent_idx in parent_nodes: child_inst = parent.child_inst() # Instance in the parent links[child_idx] += [CellPlacement(parent_idx, trans) for trans in child_inst.cell_inst.each_trans()] return links def get_placement(self, cell=None): """Returns all the positions of 'cell' in the 'top' cell.""" # Initialization of the variables if cell is None: cell_idx = self.cell_idx else: cell_idx = cell.cell_index() if cell_idx not in self.nodes[:-1]: raise ValueError("Cell is not a child node of the branch.") # Construction of the branch graph, with twigs of increasing length. twigs = self.links[cell_idx] final = [] while twigs: _twigs = [] # Empty container created for the longer twigs for inst in twigs: parent_links = self.links[inst.parent] if parent_links: for link in parent_links: parent = link.parent trans = link.trans * inst.trans _twigs.append(CellPlacement(parent, trans)) else: # This twig is attached to the 'top' final.append(inst.trans) twigs = _twigs # Longer twigs, longer by one link return final def get_positions(self): """Get the positions of 'cell' in 'top' (integer).""" return [get_xy(trans.disp) for trans in self.placement] def get_dpositions(self): """Get the positions of 'cell' in 'top' (µm).""" return [get_xy(trans.to_dtype(self.layout.dbu).disp) for trans in self.placement] # Utilities... (extracted from my Klayout environment and pasted here). get_xy = operator.attrgetter("x", "y") class Config: """Configuration.""" layout_view = None cell_view = None layout = None current_cell = None top_cell = None # A reference to the top Cell of the current Layout def setup(self, *args, **kwargs): """Update the configuration for the current layout.""" self.layout_view = pya.LayoutView.current() # LayoutView self.cell_view = self.layout_view.active_cellview() # CellView self.layout = self.cell_view.layout() # Layout self.top_cell = self.layout.top_cells()[0] # Cell (first top cell) self.current_cell = self.cell_view.cell # Cell config = Config() if __name__ == '__main__': print("Positions of the instances of a cell...") config.setup() instances = CellInstances(config.current_cell) pos = instances.get_dpositions() print("Cell '{}' ".format(instances.cell.name) + "has {} instances ".format(len(pos)) + "in cell '{}'.".format(instances.top.name)) print("Positions (x,y): ") for (x,y) in pos: print("{:>10.3f}, {:>10.3f}".format(x,y))