class ComposeMLayers(__CellContainer__): """ Decorates all elementals with purpose metal with LCells and add them as elementals to the new class. """ cell_elems = param.ElementListField() mlayers = param.DataField(fdef_name='create_mlayers') def _merge_layers(self, flat_metals): points = [] elems = spira.ElementList() for p in flat_metals: for pp in p.polygons: points.append(pp) if points: from spira.gdsii.utils import scale_polygon_down as spd points = spd(points) shape = shapes.Shape(points=points) shape.apply_merge for pts in shape.points: pts = spd([pts]) elems += spira.Polygons(shape=pts) return elems def create_mlayers(self): elems = spira.ElementList() # players = RDD.PLAYER.get_physical_layers(purpose_symbol=['METAL', 'GROUND', 'MOAT']) flat_elems = self.cell_elems.flat_copy() for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): metal_elems = flat_elems.get_polygons(layer=pl.layer) if metal_elems: c_mlayer = CMLayers(layer=pl.layer) for i, ply in enumerate(self._merge_layers(metal_elems)): ml = MLayer(name='MLayer_{}_{}_{}_{}'.format( pl.layer.number, self.cell.name, self.cell.id, i), points=ply.polygons, number=pl.layer.number) c_mlayer += spira.SRef(ml) elems += spira.SRef(c_mlayer) return elems def create_elementals(self, elems): # TODO: Apply DRC checking between metals, before being placed. for lcell in self.mlayers: elems += lcell # FIXME: Allow this operation. # elems += self.mlayers return elems
class DLayer(__DeviceLayer__): blayer = param.PolygonField() device_elems = param.ElementListField() box = param.DataField(fdef_name='create_box_layer') terms = param.DataField(fdef_name='create_labels') color = param.ColorField(default='#e54e7f') def create_labels(self): elems = ElementList() for p in self.device_elems.polygons: layer = p.gdslayer.number players = RDD.PLAYER.get_physical_layers(purposes='METAL') if layer in players: l2 = Layer(name='BoundingBox', number=layer, datatype=8) # FIXME: Ports with the same name overrides eachother. elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2) return elems def create_box_layer(self): elems = ElementList() setter = {} for p in self.device_elems.polygons: layer = p.gdslayer.number setter[layer] = 'not_set' for p in self.device_elems.polygons: layer = p.gdslayer.number players = RDD.PLAYER.get_physical_layers(purposes=['METAL']) if layer in players and setter[layer] == 'not_set': l1 = Layer(name='BoundingBox', number=layer, datatype=8) elems += Polygons(polygons=self.blayer.polygons, gdslayer=l1) setter[layer] = 'already_set' return elems def create_elementals(self, elems): elems += self.box elems += self.terms elems = elems.flatten() return elems
class ConnectDesignRules(ComposeGLayer): metal_elems = param.ElementListField() def create_elementals(self, elems): super().create_elementals(elems) incorrect_elems = ElementList() correct_elems = ElementList() for rule in RDD.RULES.elementals: if not rule.apply(elems): for composed_lcell in elems: for lcell in composed_lcell.ref.elementals.sref: if lcell.ref.layer.number == rule.layer1.number: correct_elems += lcell return elems
class Route(__Path__): ports = param.ElementListField(fdef_name='create_ports') # ports = param.PortListField(fdef_name='create_ports') input_term = param.DataField(fdef_name='create_port_input') output_term = param.DataField(fdef_name='create_port_output') def __init__(self, **kwargs): super().__init__(**kwargs) def create_port_input(self): return None def create_port_output(self): return None def create_ports(self, ports): return ports
class __ProcessLayer__(Cell): doc = param.StringField() points = param.ElementListField() # points = param.PointArrayField() number = param.IntegerField() error_type = param.IntegerField() layer = param.DataField(fdef_name='create_layer') player = param.DataField(fdef_name='create_polygon_layer') def create_polygon_layer(self): return Polygons(shape=self.points, gdslayer=self.layer) def create_layer(self): return Layer(name=self.name, number=self.number, datatype=self.error_type) def create_elementals(self, elems): elems += self.player return elems
class ComposeNLayer(ComposeMLayers): """ Decorates all elementas with purpose via with LCells and add them as elementals to the new class. """ cell_elems = param.ElementListField() level = param.IntegerField(default=1) nlayers = param.DataField(fdef_name='create_nlayers') def create_nlayers(self): elems = ElementList() flat_elems = self.cell_elems.flat_copy() for pl in RDD.PLAYER.get_physical_layers(purposes='VIA'): via_elems = flat_elems.get_polygons(layer=pl.layer) if via_elems: c_nlayer = CNLayers(layer=pl.layer) for i, ply in enumerate(via_elems): ml = NLayer(name='Via_NLayer_{}_{}_{}'.format( pl.layer.number, self.cell.name, i), points=ply.polygons, midpoint=ply.center, number=pl.layer.number) c_nlayer += spira.SRef(ml) elems += SRef(c_nlayer) return elems def create_elementals(self, elems): super().create_elementals(elems) # Only add it if its a Device. if self.level == 1: for lcell in self.nlayers: elems += lcell return elems
class GLayer(__ProcessLayer__): """ Ground Plane layer. """ blayer = param.PolygonField() device_elems = param.ElementListField() box = param.DataField(fdef_name='create_box_layer') terms = param.DataField(fdef_name='create_labels') def create_labels(self): elems = ElementList() for p in self.device_elems.polygons: layer = p.gdslayer.number # if layer in RDD.GROUND.layers: if layer == RDD.GDSII.GPLAYER: l2 = Layer(name='BoundingBox', number=layer, datatype=5) elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2) return elems def create_box_layer(self): elems = ElementList() for p in self.device_elems.polygons: layer = p.gdslayer.number # if layer in RDD.GROUND.layers: if layer == RDD.GDSII.GPLAYER: l1 = Layer(name='BoundingBox', number=layer, datatype=5) elems += Polygons(polygons=self.blayer.polygons, gdslayer=l1) return elems def create_elementals(self, elems): super().create_elementals(elems) # elems += self.box # elems += self.terms # # elems = elems.flatten() return elems
class ComposeGLayer(ComposeNLayer): plane_elems = param.ElementListField( ) # Elementals like skyplanes and groundplanes. ground_layer = param.DataField(fdef_name='create_merged_ground_layers') def create_merged_ground_layers(self): points = [] for p in self.plane_elems.flat_copy(): for pp in p.polygons: points.append(pp) if points: ll = Layer(number=RDD.GDSII.GPLAYER, datatype=6) merged_ply = UnionPolygons(polygons=points, gdslayer=ll) return merged_ply return None def create_elementals(self, elems): super().create_elementals(elems) if self.level == 1: if self.ground_layer: box = self.cell.bbox # box.move(midpoint=box.center, destination=(0,0)) gnd = self.ground_layer | box if gnd: c_glayer = CGLayers(layer=gnd.gdslayer) name = 'GLayer_{}_{}'.format(self.cell.name, gnd.gdslayer.number) gnd_layer = GLayer(name=name, layer=gnd.gdslayer, player=gnd) c_glayer += spira.SRef(gnd_layer) elems += spira.SRef(c_glayer) return elems
class GeometryAbstract(__Geometry__): _ID = 0 name = param.StringField() layer = param.IntegerField() dimension = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) polygons = param.ElementListField() # gmsh_elements = param.ElementListField() create_mesh = param.DataField(fdef_name='create_meshio') elements = param.DataField(fdef_name='create_pygmsh_elements') def __init__(self, lcar=0.01, **kwargs): super().__init__(lcar=lcar, **kwargs) def create_meshio(self): """ Generates a GMSH mesh, which is saved in the `debug` folder. Arguments --------- mesh : dict Dictionary containing all the necessary mesh information. """ if len(self.__surfaces__()) > 1: self.geom.boolean_union(self.__surfaces__()) directory = os.getcwd() + '/debug/gmsh/' mesh_file = '{}{}.msh'.format(directory, self.name) geo_file = '{}{}.geo'.format(directory, self.name) vtk_file = '{}{}.vtu'.format(directory, self.name) if not os.path.exists(directory): os.makedirs(directory) mesh_data = pygmsh.generate_mesh(self.geom, verbose=False, dim=self.dimension, prune_vertices=False, remove_faces=False, geo_filename=geo_file) mm = meshio.Mesh(*mesh_data) meshio.write(mesh_file, mm) meshio.write(vtk_file, mm) # params = { # 'name': self.name, # 'layer': spira.Layer(number=self.layer), # 'points': [mesh_data[0]], # 'cells': [mesh_data[1]], # 'point_data': [mesh_data[2]], # 'cell_data': [mesh_data[3]], # 'field_data': [mesh_data[4]] # } # return params return mesh_data def create_pygmsh_elements(self): print('number of polygons {}'.format(len(self.polygons))) height = 0.0 holes = None elems = ElementList() for ply in self.polygons: for i, points in enumerate(ply.polygons): pp = numpy_to_list(points, height, unit=10e-9) surface_label = '{}_{}_{}_{}'.format(ply.gdslayer.number, ply.gdslayer.datatype, GeometryAbstract._ID, i) gp = self.geom.add_polygon(pp, lcar=1.0, make_surface=True, holes=holes) self.geom.add_physical_surface(gp.surface, label=surface_label) elems += [gp.surface, gp.line_loop] GeometryAbstract._ID += 1 return elems def extrude_surfaces(self, geom, surfaces): """ This extrudes the surface to a 3d volume element. """ for i, surface in enumerate(surfaces): width = float(self.width) * scale ex = self.geom.extrude(surface, [0, 0, width]) unique_id = '{}_{}'.format(polygons._id, i) volume = self.geom.add_physical_volume(ex[1], unique_id) self.extrude.append(ex[1]) self.volume.append(volume) def geom_holes(self): """ Create a list of gmsh surfaces from the mask polygons generated by the gdsii package. Arguments --------- surfaces : list list of pygmsh surface objects. """ print('number of polygons {}'.format(len(self.e.polygons))) dim = 2 height = 0.0 material_stack = None for i, points in enumerate(self.e.polygons): if dim == 3: height = self.vertical_position(material_stack) pp = numpy_to_list(points, height, unit=self.e.unit) gp = geom.add_polygon(pp, lcar=1.0, make_surface=true) line_loops.append(gp.line_loop) def flat_copy(self, level=-1, commit_to_gdspy=False): return self def flatten(self): return [self] def commit_to_gdspy(self, cell): pass def transform(self, transform): return self
class MeshAbstract(__Mesh__): """ Class that connects a meshio generated mesh with a networkx generated graph of the set of polygons. """ name = param.StringField() layer = param.LayerField() point_data = param.ElementListField() cell_data = param.ElementListField() field_data = param.ElementListField() node_sets = param.ElementListField() gmsh_periodic = param.ElementListField() mesh_graph = param.DataField(fdef_name='create_mesh_graph') def __init__(self, polygons, points, cells, **kwargs): super().__init__(polygons, points, cells, **kwargs) def create_mesh_graph(self): """ Create a graph from the meshed geometry. """ ll = len(self.points) A = np.zeros((ll, ll), dtype=np.int64) for n, triangle in enumerate(self.__triangles__()): self.add_edges(n, triangle, A) for n, triangle in enumerate(self.__triangles__()): self.add_positions(n, triangle) def add_edges(self, n, tri, A): def update_adj(self, t1, adj_mat, v_pair): if (adj_mat[v_pair[0]][v_pair[1]] != 0): t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 self.g.add_edge(t1, t2, label=None) else: adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 v1 = [tri[0], tri[1], tri[2]] v2 = [tri[1], tri[2], tri[0]] for v_pair in list(zip(v1, v2)): update_adj(self, n, A, v_pair) def add_positions(self, n, tri): pp = self.points n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] sum_x = 1e+8 * (n1[0] + n2[0] + n3[0]) / 3.0 sum_y = 1e+8 * (n1[1] + n2[1] + n3[1]) / 3.0 self.g.node[n]['vertex'] = tri self.g.node[n]['pos'] = [sum_x, sum_y] def __triangles__(self): if 'triangle' not in self.cells: raise ValueError('Triangle not found in cells') return self.cells['triangle'] def __physical_triangles__(self): if 'triangle' not in self.cell_data[0]: raise ValueError('Triangle not in meshio cell_data') if 'gmsh:physical' not in self.cell_data[0]['triangle']: raise ValueError('Physical not found ing meshio triangle') return self.cell_data[0]['triangle']['gmsh:physical'].tolist() def __layer_triangles_dict__(self): """ Arguments --------- tri : list The surface_id of the triangle corresponding to the index value. key -> 5_0_1 (layer_datatype_polyid) value -> [1 2] (1=surface_id 2=triangle) """ triangles = {} for name, value in self.field_data[0].items(): for n in self.g.nodes(): surface_id = value[0] ptriangles = self.__physical_triangles__() if ptriangles[n] == surface_id: layer = int(name.split('_')[0]) datatype = int(name.split('_')[1]) key = (layer, datatype) if key in triangles: triangles[key].append(n) else: triangles[key] = [n] return triangles def __triangle_nodes__(self): """ Get triangle field_data in list form. """ nodes = [] for v in self.__layer_triangles_dict__().values(): nodes.extend(v) triangles = {} for n in nodes: for node, triangle in enumerate(self.__triangles__()): if n == node: triangles[n] = triangle return triangles def __point_data__(self): pass def flat_copy(self, level=-1, commit_to_gdspy=False): return self def flatten(self): return [self] def commit_to_gdspy(self, cell): pass def transform(self, transform): return self
class MeshLabeled(MeshAbstract): RDD = spira.get_rule_deck() primitives = param.ElementListField() surface_nodes = param.DataField(fdef_name='create_surface_nodes') pinlabel_nodes = param.DataField(fdef_name='create_pinlabel_nodes') def __init__(self, polygons, points, cells, **kwargs): print('\nPinLabels object') super().__init__(polygons, points, cells, **kwargs) self.points = points self.cells = cells self.surface_nodes self.pinlabel_nodes def create_surface_nodes(self): LOG.header('Adding surface labels') node_count = 0 triangles = self.__layer_triangles_dict__() for key, nodes in triangles.items(): for n in nodes: position = self.g.node[n]['pos'] pid = utils.labeled_polygon_id(position, self.polygons) if pid is not None: params = {} params['text'] = self.name params['gdslayer'] = self.layer params['color'] = RDD.METALS.get_key_by_layer( self.layer)['COLOR'] label = spira.Label(position=position, **params) label.id = '{}_{}'.format(key[0], pid) self.g.node[n]['surface'] = label node_count += 1 print('# surface nodes added: {}'.format(node_count)) def create_pinlabel_nodes(self): LOG.header('Adding pin labels') for node, triangle in self.__triangle_nodes__().items(): points = [utils.c2d(self.points[i]) for i in triangle] for S in self.primitives: if isinstance(S, spira.Port): self.add_port_label(node, S, points) else: self.add_device_label(node, S, points) def add_new_node(self, n, D, pos): params = {} params['text'] = 'new' l1 = spira.Layer(name='Label', number=104) params['gdslayer'] = l1 # params['color'] = RDD.METALS.get_key_by_layer(self.layer)['COLOR'] label = spira.Label(position=pos, **params) label.id = '{}_{}'.format(n, n) num = self.g.number_of_nodes() self.g.add_node(num + 1, pos=pos, pin=D, surface=label) self.g.add_edge(n, num + 1) def add_port_label(self, n, D, points): if D.point_inside(points): P = spira.PortNode(name=D.name, elementals=D) self.g.node[n]['pin'] = P def add_device_label(self, n, S, points): for name, p in S.ports.items(): if p.gdslayer.name == 'GROUND': pass # if lbl.layer == self.layer.number: # params = {} # params['text'] = 'GROUND' # l1 = spira.Layer(name='GND', number=104) # params['gdslayer'] = l1 # # label = spira.Label(position=lbl.position, **params) # label.id = '{}_{}'.format(n, n) # # ply = spira.Polygons(gdslayer=l1) # # D_gnd = BaseVia(name='BaseVIA_GND', # ply=ply, # m2=l1, m1=l1) # # num = self.g.number_of_nodes() # # self.g.add_node(num+1, pos=lbl.position, pin=D_gnd, surface=label) # self.g.add_edge(n, num+1) else: # print(S.flat_copy()) # print(p.gdslayer.number) # print(self.layer.number) # print('') if p.gdslayer.number == self.layer.number: if p.point_inside(points): self.g.node[n]['pin'] = S
class __StructureCell__(ConnectDesignRules): """ Add a GROUND bbox to Device for primitive and DRC detection, since GROUND is only in Mask Cell. """ level = param.IntegerField(default=1) device_elems = param.ElementListField() devices = param.DataField(fdef_name='create_device_layers') terminals = param.DataField(fdef_name='create_terminal_layers') def create_device_layers(self): box = self.cell.bbox box.move(midpoint=box.center, destination=(0, 0)) B = DLayer(blayer=box, device_elems=self.cell.elementals) Bs = SRef(B) Bs.move(midpoint=(0, 0), destination=self.cell.bbox.center) return Bs def create_terminal_layers(self): # flat_elems = self.cell_elems.flat_copy() # port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) # label_elems = flat_elems.labels # # elems = ElementList() # for port in port_elems: # for label in label_elems: # # lbls = label.text.split(' ') # s_p1, s_p2 = lbls[1], lbls[2] # p1, p2 = None, None # # if s_p1 in RDD.METALS.keys: # layer = RDD.METALS[s_p1].LAYER # p1 = spira.Layer(name=lbls[0], number=layer, datatype=RDD.GDSII.TEXT) # # if s_p2 in RDD.METALS.keys: # layer = RDD.METALS[s_p2].LAYER # p2 = spira.Layer(name=lbls[0], number=layer, datatype=RDD.GDSII.TEXT) # # if p1 and p2: # if label.point_inside(polygon=port.polygons[0]): # term = TLayer(points=port.polygons, # layer1=p1, # layer2=p2, # number=RDD.GDSII.TERM, # midpoint=label.position) # # term.ports[0].name = 'P1_{}'.format(label.text) # term.ports[1].name = 'P2_{}'.format(label.text) # # elems += SRef(term) elems = ElementList() for p in self.cell.ports: if isinstance(p, spira.Term): term = TLayer( points=p.polygon.polygons, # layer1=p1, # layer2=p2, number=RDD.PURPOSE.TERM.datatype, midpoint=p.label.position) term.ports[0].name = 'P1_{}'.format(1) term.ports[1].name = 'P2_{}'.format(2) elems += SRef(term) return elems def create_elementals(self, elems): super().create_elementals(elems) # elems += self.devices # for term in self.terminals: # elems += term return elems def create_ports(self, ports): # for t in self.cell.terms: # ports += t return ports
class __ElementalContainer__(Cell): received_elementals = param.ElementListField() def create_elementals(self, elems): return elems
class CellAbstract(__Cell__): name = param.StringField() ports = param.ElementListField(fdef_name='create_ports') elementals = param.ElementListField(fdef_name='create_elementals') def create_elementals(self, elems): result = ElementList() return result def create_ports(self, ports): return ports def flatten(self): self.elementals = self.elementals.flatten() return self.elementals def flat_copy(self, level=-1, commit_to_gdspy=False): self.elementals = self.elementals.flat_copy(level, commit_to_gdspy) return self.elementals def dependencies(self): deps = self.elementals.dependencies() deps += self return deps def commit_to_gdspy(self): cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: if issubclass(type(e), Cell): for elem in e.elementals: elem.commit_to_gdspy(cell=cell) for port in e.ports: port.commit_to_gdspy(cell=cell) elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): e.commit_to_gdspy(cell=cell) return cell def move(self, midpoint=(0, 0), destination=None, axis=None): """ Moves elements of the Device from the midpoint point to the destination. Both midpoint and destination can be 1x2 array-like, Port, or a key corresponding to one of the Ports in this device """ if destination is None: destination = midpoint midpoint = [0, 0] if issubclass(type(midpoint), __Port__): o = midpoint.midpoint elif np.array(midpoint).size == 2: o = midpoint elif midpoint in self.ports: o = self.ports[midpoint].midpoint else: raise ValueError('[DeviceReference.move()] ``midpoint`` ' + \ 'not array-like, a port, or port name') if issubclass(type(destination), __Port__): d = destination.midpoint elif np.array(destination).size == 2: d = destination elif destination in self.ports: d = self.ports[destination].midpoint else: raise ValueError('[DeviceReference.move()] ``destination`` ' + \ 'not array-like, a port, or port name') if axis == 'x': d = (d[0], o[1]) if axis == 'y': d = (o[0], d[1]) dx, dy = np.array(d) - o for e in self.elementals: if issubclass(type(e), (LabelAbstract, PolygonAbstract)): e.translate(dx, dy) if isinstance(e, (Cell, SRef)): e.move(destination=d, midpoint=o) for p in self.ports: mc = np.array(p.midpoint) + np.array(d) - np.array(o) p.move(midpoint=p.midpoint, destination=mc) return self def reflect(self, p1=(0, 1), p2=(0, 0)): """ Reflects the cell around the line [p1, p2]. """ for e in self.elementals: if not issubclass(type(e), (LabelAbstract, __Port__)): e.reflect(p1, p2) for p in self.ports: p.midpoint = self.__reflect__(p.midpoint, p1, p2) phi = np.arctan2(p2[1] - p1[1], p2[0] - p1[0]) * 180 / np.pi p.orientation = 2 * phi - p.orientation return self def rotate(self, angle=45, center=(0, 0)): """ Rotates the cell with angle around a center. """ if angle == 0: return self for e in self.elementals: if issubclass(type(e), PolygonAbstract): e.rotate(angle=angle, center=center) elif isinstance(e, SRef): e.rotate(angle, center) ports = self.ports self.ports = ElementList() for p in ports: if issubclass(type(p), __Port__): p.midpoint = self.__rotate__(p.midpoint, angle, center) p.orientation = np.mod(p.orientation + angle, 360) self.ports += p return self def get_ports(self, level=None): """ Returns copies of all the ports of the Device """ port_list = [p._copy() for p in self.ports] if level is None or level > 0: for r in self.elementals.sref: if level is None: new_level = None else: new_level = level - 1 ref_ports = r.ref.get_ports(level=new_level) tf = { 'midpoint': r.midpoint, 'rotation': r.rotation, 'magnification': r.magnification, 'reflection': r.reflection } ref_ports_transformed = [] for rp in ref_ports: new_port = rp._copy() new_port = new_port.transform(tf) ref_ports_transformed.append(new_port) port_list += ref_ports_transformed return port_list