class ViaTemplate(__TempatePrimitive__): layer1 = param.LayerField(number=3) layer2 = param.LayerField(number=8) via_layer = param.LayerField(number=9) def create_elementals(self, elems): M1 = spira.ElementList() M2 = spira.ElementList() contacts = spira.ElementList() for e in elems: if e.player.purpose == RDD.PURPOSE.METAL: if e.player.layer == self.layer1: M1 += e elif e.player.layer == self.layer2: M2 += e if e.player.purpose == RDD.PURPOSE.PRIM.VIA: if e.player.layer == self.via_layer: contacts += e for D in contacts: for M in M1: if D.polygon | M.polygon: pp = D.polygon | M.polygon # TODO: Apply DRC enclosure rule here. D.ports[0]._update(name=D.name, layer=M.player.layer) for M in M2: if D.polygon | M.polygon: pp = D.polygon | M.polygon # TODO: Apply DRC enclosure rule here. D.ports[1]._update(name=D.name, layer=M.player.layer) return elems
class __ConnectLayer__(__ProcessLayer__): midpoint = param.MidPointField() layer1 = param.LayerField() layer2 = param.LayerField() port1 = param.DataField(fdef_name='create_port1') port2 = param.DataField(fdef_name='create_port2') def create_port1(self): port = Port(name='P1', midpoint=self.midpoint, gdslayer=self.layer1) return port def create_port2(self): port = Port(name='P2', midpoint=self.midpoint, gdslayer=self.layer2) return port def create_ports(self, ports): ports += self.port1 ports += self.port2 return ports def create_elementals(self, elems): super().create_elementals(elems) return elems
class ArcRoute(spira.Route): gdslayer = param.LayerField(name='ArcLayer', number=91) radius = param.FloatField(default=5) width = param.FloatField(default=1) theta = param.FloatField(default=45) start_angle = param.FloatField(default=0) angle_resolution = param.FloatField(default=1) angle1 = param.DataField(fdef_name='create_angle1') angle2 = param.DataField(fdef_name='create_angle2') def create_angle1(self): angle1 = (self.start_angle + 0) * np.pi / 180 return angle1 def create_angle2(self): angle2 = (self.start_angle + self.theta + 0) * np.pi / 180 return angle2 def create_port_input(self): midpoint = self.radius * np.cos(self.angle1), self.radius * np.sin( self.angle1) orientation = self.start_angle - 0 + 180 * (self.theta < 0) port = spira.Term(name='P1', midpoint=midpoint, width=self.width, length=0.2, orientation=orientation + 180) return port def create_port_output(self): midpoint = self.radius * np.cos(self.angle2), self.radius * np.sin( self.angle2) orientation = self.start_angle + self.theta + 180 - 180 * (self.theta < 0) port = spira.Term(name='P2', midpoint=midpoint, width=self.width, length=0.2, orientation=orientation + 180) return port def create_points(self, points): inner_radius = self.radius - self.width / 2.0 outer_radius = self.radius + self.width / 2.0 z = int(np.ceil(abs(self.theta) / self.angle_resolution)) t = np.linspace(self.angle1, self.angle2, z) inner_points_x = (inner_radius * np.cos(t)).tolist() inner_points_y = (inner_radius * np.sin(t)).tolist() outer_points_x = (outer_radius * np.cos(t)).tolist() outer_points_y = (outer_radius * np.sin(t)).tolist() xpts = np.array(inner_points_x + outer_points_x[::-1]) ypts = np.array(inner_points_y + outer_points_y[::-1]) points = [[list(p) for p in list(zip(xpts, ypts))]] return points
class PhysicalLayer(spira.Cell): points = param.ListField() layer = param.LayerField() def create_elementals(self, elems): for pp in self.points: elems += Rectangle(point1=pp[0], point2=pp[1], layer=self.layer) return elems
class CMLayers(Cell): layer = param.LayerField() def create_elementals(self, elems): return elems def set_net(self): pass def get_net(self): pass
class ArcSeries(spira.Cell): gdslayer = param.LayerField(number=91) radius = param.FloatField(default=20) # radius = param.FloatField(default=20 * 1e6) width = param.FloatField(default=1.0) # width = param.FloatField(default=1.0 * 1e6) angular_coverage = param.FloatField(default=30) num_steps = param.IntegerField(default=1) angle_resolution = param.FloatField(default=0.1) start_angle = param.IntegerField(default=0) direction = param.StringField(default='ccw') port1 = param.DataField() port2 = param.DataField() subarc = SubArcSeries def get_subarc_routes(self): D = SubArcSeries(gdslayer=self.gdslayer, radius=self.radius, width=self.width, angular_coverage=self.angular_coverage, num_steps=self.num_steps, angle_resolution=self.angle_resolution, start_angle=self.start_angle) s1 = spira.SRef(D) s2 = spira.SRef(D) s2.reflect(p1=[0, 0], p2=[1, 1]) s2.connect(port='P2', destination=s1.ports['P2']) return s1, s2 def create_elementals(self, elems): s1, s2 = self.get_subarc_routes() elems += s1 elems += s2 return elems def create_ports(self, ports): s1, s2 = self.get_subarc_routes() # ports += s1.ports['P1'].modified_copy(name='Port_1') # ports += s2.ports['P1'].modified_copy(name='Port_2') return ports
class RouteBasic(spira.Cell): route = param.ShapeField() connect_layer = param.LayerField( doc='GDSII layer to which the route connects.') port1 = param.DataField(fdef_name='create_port1') port2 = param.DataField(fdef_name='create_port2') llayer = param.DataField(fdef_name='create_layer') def create_layer(self): ll = spira.Layer(number=self.connect_layer.number, datatype=RDD.PURPOSE.TERM.datatype) return ll def create_elementals(self, elems): ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) ply.rotate(angle=-90) elems += ply return elems def create_port1(self): term = spira.Term(name='TERM1', midpoint=(0, 0), width=self.route.width1, length=0.2, orientation=180, gdslayer=self.llayer) term.rotate(angle=-90) return term def create_port2(self): term = spira.Term(name='TERM2', midpoint=[self.route.x_dist, self.route.y_dist], width=self.route.width2, length=0.2, orientation=0, gdslayer=self.llayer) term.rotate(angle=-90) return term def create_ports(self, ports): ports += self.port1 ports += self.port2 return ports
class GradualFractal(spira.Cell): """ Creates a 90-degree bent waveguide the bending radius is gradually increased until it reaches the minimum value of the radius at the "angular coverage" angle. It essentially creates a smooth transition to a bent waveguide mode. User can control number of steps provided. Direction determined by start angle and cw or ccw switch with the default 10 "num_steps" and 15 degree coverage, effective radius is about 1.5*radius. """ gdslayer = param.LayerField(number=91) radius = param.FloatField(default=20) # radius = param.FloatField(default=20 * 1e6) width = param.FloatField(default=1.0) # width = param.FloatField(default=1.0 * 1e6) angular_coverage = param.FloatField(default=20) num_steps = param.IntegerField(default=5) angle_resolution = param.FloatField(default=0.01) start_angle = param.IntegerField(default=0) direction = param.StringField(default='ccw') def create_elementals(self, elems): D = ArcSeries(gdslayer=self.gdslayer, radius=self.radius, width=self.width, angular_coverage=self.angular_coverage, num_steps=self.num_steps, angle_resolution=self.angle_resolution, start_angle=self.start_angle) # D.xmin, D.ymin = 0, 0 # Orient to default settings... # D.reflect(p1=[0,0], p2=[1,1]) # D.reflect(p1=[0,0], p2=[1,0]) # D.rotate(angle=self.start_angle, center=D.center) # D.center = [0, 0] s1 = spira.SRef(D) elems += s1 return elems
class LabelAbstract(__Label__): gdslayer = param.LayerField() text = param.StringField() str_anchor = param.StringField(default='o') rotation = param.FloatField(default=0) magnification = param.FloatField(default=1) reflection = param.BoolField(default=False) texttype = param.IntegerField() gdspy_commit = param.BoolField() def __init__(self, position, **kwargs): super().__init__(position, **kwargs) def commit_to_gdspy(self, cell): if self.__repr__() not in list(LabelAbstract.__committed__.keys()): L = gdspy.Label(self.text, deepcopy(self.position), anchor='o', rotation=self.rotation, magnification=self.magnification, x_reflection=self.reflection, layer=self.gdslayer.number, texttype=self.texttype ) cell.add(L) LabelAbstract.__committed__.update({self.__repr__():L}) else: cell.add(LabelAbstract.__committed__[self.__repr__()]) def reflect(self, p1=(0,1), p2=(0,0)): self.position = [self.position[0], -self.position[1]] self.rotation = self.rotation * (-1) self.rotation = np.mod(self.rotation, 360) return self def rotate(self, angle=45, center=(0,0)): self.position = self.__rotate__(self.position, angle=angle, center=[0, 0]) self.rotation += angle self.rotation = np.mod(self.rotation, 360) return self def point_inside(self, polygon): return pyclipper.PointInPolygon(self.position, polygon) != 0 def transform(self, transform): if transform['reflection']: self.reflect(p1=[0,0], p2=[1,0]) if transform['rotation']: self.rotate(angle=transform['rotation']) if transform['midpoint']: self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) return self def flat_copy(self, level=-1, commit_to_gdspy=False): c_label = self.modified_copy(position=self.position) if commit_to_gdspy: self.gdspy_commit = True return c_label def move(self, midpoint=(0,0), destination=None, axis=None): from spira.gdsii.elemental.port import __Port__ 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("[PHIDL] [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("[PHIDL] [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 super().translate(dx, dy) return self
class SubArcSeries(spira.Cell): gdslayer = param.LayerField(number=99) radius = param.FloatField(default=20) # radius = param.FloatField(default=20 * 1e6) width = param.FloatField(default=1.0) # width = param.FloatField(default=1.0 * 1e6) angular_coverage = param.FloatField(default=30) num_steps = param.IntegerField(default=1) angle_resolution = param.FloatField(default=0.1) start_angle = param.IntegerField(default=0) port1 = param.DataField() port2 = param.DataField() def _regular_bend(self, prev_port): """ Now connect a regular bend for the normal curved portion. """ B = Arc(shape=ArcRoute(radius=self.radius, width=self.width, theta=45 - np.rad2deg(self.angular_coverage), start_angle=self.angular_coverage, angle_resolution=self.angle_resolution, gdslayer=spira.Layer(number=88))) b = spira.SRef(B) b.connect(port='P1', destination=prev_port) p0 = b.ports['P2'] self.port2 = spira.Term( name='P2', midpoint=p0.midpoint, # midpoint=scu(p0.midpoint), width=p0.width, orientation=p0.orientation) return b def create_elementals(self, elems): self.angular_coverage = np.deg2rad(self.angular_coverage) inc_rad = (self.radius**-1) / self.num_steps angle_step = self.angular_coverage / self.num_steps print('inc_rad: {}'.format(inc_rad)) print('angle_step: {}'.format(angle_step)) arcs = [] for x in range(self.num_steps): A = Arc(shape=ArcRoute(radius=1 / ((x + 1) * inc_rad), width=self.width, theta=np.rad2deg(angle_step), start_angle=x * np.rad2deg(angle_step), angle_resolution=self.angle_resolution, gdslayer=self.gdslayer)) a = spira.SRef(A) elems += a arcs.append(a) if x > 0: a.connect(port='P1', destination=prevPort) prevPort = a.ports['P2'] self.port1 = arcs[0].ports['P1'] elems += self._regular_bend(prevPort) return elems def create_ports(self, ports): ports += self.port1 ports += self.port2 return ports
class __Shape__(FieldInitializer): center = param.PointField() gdslayer = param.LayerField() clockwise = param.BoolField(default=False) points = param.PointArrayField(fdef_name='create_points') apply_merge = param.DataField(fdef_name='create_merged_points') simplify = param.DataField(fdef_name='create_simplified_points') edges = param.DataField(fdef_name='create_edge_lines') def __init__(self, **kwargs): super().__init__(**kwargs) def create_points(self, points): return points def create_merged_points(self): """ """ from spira.gdsii.utils import scale_polygon_up as spu from spira.gdsii.utils import scale_polygon_down as spd polygons = spu(self.points) self.points = [] for poly in polygons: if pyclipper.Orientation(poly) is False: reverse_poly = pyclipper.ReversePath(poly) solution = pyclipper.SimplifyPolygon(reverse_poly) else: solution = pyclipper.SimplifyPolygon(poly) for sol in solution: self.points.append(sol) self.points = bool_operation(subj=self.points, method='union') self.points = spd(self.points) return self def create_simplified_points(self): """ """ from shapely.geometry import Polygon as ShapelyPolygon value = 1 polygons = self.points self.points = [] for points in polygons: factor = (len(points) / 100) * 1e5 * value sp = ShapelyPolygon(points).simplify(factor) pp = [[p[0], p[1]] for p in sp.exterior.coords] self.points.append(pp) return self def reflect(self, p1=(0, 1), p2=(0, 0)): """ Reflect across a line. """ points = np.array(self.points[0]) p1 = np.array(p1) p2 = np.array(p2) if np.asarray(points).ndim == 1: t = np.dot((p2 - p1), (points - p1)) / norm(p2 - p1)**2 pts = 2 * (p1 + (p2 - p1) * t) - points if np.asarray(points).ndim == 2: pts = np.array([0, 0]) for p in points: t = np.dot((p2 - p1), (p - p1)) / norm(p2 - p1)**2 r = np.array(2 * (p1 + (p2 - p1) * t) - p) pts = np.vstack((pts, r)) self.points = [pts] return self def rotate(self, angle=45, center=(0, 0)): """ Rotate points with an angle around a center. """ points = np.array(self.points[0]) angle = angle * np.pi / 180 ca = np.cos(angle) sa = np.sin(angle) sa = np.array((-sa, sa)) c0 = np.array(center) if np.asarray(points).ndim == 2: pts = (points - c0) * ca + (points - c0)[:, ::-1] * sa + c0 pts = np.round(pts, 6) if np.asarray(points).ndim == 1: pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 pts = np.round(pts, 6) self.points = [pts] return self @property def orientation(self): """ Returns the orientation of the shape: +1(counterclock) or -1(clock) """ # FIXME: Error with multiple shapes: [[[s1], [s2]]] pts = self.points[0] T = np.roll(np.roll(pts, 1, 1), 1, 0) return -np.sign(sum(np.diff(pts * T, 1, 1))) @property def area(self): """ Returns the area of the shape. """ pts = self.points[0] T = np.roll(np.roll(pts, 1, 1), 1, 0) return sum(abs(np.diff(pts * T, 1, 1))) * 0.5 @property def count(self): """ number of points in the shape """ return self.__len__() @property def reverse(self): pass def move(self, pos): p = np.array([pos[0], pos[1]]) self.points += p return self def transform(self): pass def point_inside(self): pass def index(self, item): pass
class CellA(spira.Cell): layer = param.LayerField(number=18, datatype=1) boolean = param.BoolField(default=False) fvalue = param.FloatField(default=0.0)
class PhysicalLayer(__Layer__): """ """ doc = param.StringField() layer = param.LayerField() purpose = PurposeLayerField() data = param.DataField(default=ProcessTree()) def __init__(self, **kwargs): ElementalInitializer.__init__(self, **kwargs) def __repr__(self): string = '[SPiRA: PhysicalLayer] (layer \'{}\', symbol \'{}\')' return string.format(self.layer.name, self.purpose.symbol) def __str__(self): return self.__repr__() def __eq__(self, other): if isinstance(other, PhysicalLayer): return other.key == self.key # elif isinstance(other, Layer): # return other.number == self.layer.number elif isinstance(other, int): return other == self.layer.number else: raise ValueError('Not Implemented!') def __neq__(self, other): if isinstance(other, PhysicalLayer): return other.key != self.key # elif isinstance(other, Layer): # return other.number != self.layer.number elif isinstance(other, int): return other != self.layer.number else: raise ValueError('Not Implemented!') # def __add__(self, other): # if isinstance(other, PhysicalLayer): # d = self.datatype + other.datatype # elif isinstance(other, int): # d = self.datatype + other # else: # raise ValueError('Not Implemented') # return PurposeLayer(datatype=d) # def __iadd__(self, other): # if isinstance(other, PhysicalLayer): # self.datatype += other.datatype # elif isinstance(other, int): # self.datatype += other # else: # raise ValueError('Not Implemented') # return self @property def key(self): return (self.layer.number, self.purpose.symbol)
class __DoubleLayerDesignRule__(__DesignRule__): """ Rule applying to a specific layer """ layer1 = param.LayerField() layer2 = param.LayerField()
class CGLayers(Cell): layer = param.LayerField() def create_elementals(self, elems): return elems
class PortAbstract(__Port__): name = param.StringField() midpoint = param.MidPointField() orientation = param.IntegerField() parent = param.DataField() gdslayer = param.LayerField(name='PortLayer', number=64) poly_layer = param.LayerField(name='PortLayer', number=64) text_layer = param.LayerField(name='PortLayer', number=63) def __init__(self, port=None, polygon=None, **kwargs): super().__init__(**kwargs) self.orientation = np.mod(self.orientation, 360) L = spira.Label(position=self.midpoint, text=self.name, gdslayer=self.gdslayer, texttype=self.text_layer.number) self.label = L self.arrow = None @property def endpoints(self): dx = self.width / 2 * np.cos((self.orientation - 90) * np.pi / 180) dy = self.width / 2 * np.sin((self.orientation - 90) * np.pi / 180) left_point = self.midpoint - np.array([dx, dy]) right_point = self.midpoint + np.array([dx, dy]) return np.array([left_point, right_point]) @endpoints.setter def endpoints(self, points): p1, p2 = np.array(points[0]), np.array(points[1]) self.midpoint = (p1 + p2) / 2 dx, dy = p2 - p1 self.orientation = np.arctan2(dx, dy) * 180 / np.pi self.width = np.sqrt(dx**2 + dy**2) @property def normal(self): dx = np.cos((self.orientation) * np.pi / 180) dy = np.sin((self.orientation) * np.pi / 180) return np.array([self.midpoint, self.midpoint + np.array([dx, dy])]) def point_inside(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 def flat_copy(self, level=-1, commit_to_gdspy=False): c_port = self.modified_copy(midpoint=self.midpoint) if commit_to_gdspy: self.gdspy_write = True return c_port def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): # self.polygon.rotate(angle=self.orientation) # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) self.polygon.commit_to_gdspy(cell) self.label.commit_to_gdspy(cell) if self.arrow: # print(self.orientation) # self.arrow.rotate(angle=45) # self.arrow.rotate(angle=90) # self.arrow.rotate(angle=90-self.orientation) self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) self.arrow.commit_to_gdspy(cell) __Port__.__committed__.update({self.__repr__(): self}) def reflect(self): """ Reflect around the x-axis. """ self.midpoint = [self.midpoint[0], -self.midpoint[1]] self.orientation = -self.orientation self.orientation = np.mod(self.orientation, 360) self.polygon.reflect() if self.arrow: self.arrow.reflect() return self def rotate(self, angle=45, center=(0, 0)): """ Rotate port around the center with angle. """ self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle self.orientation = np.mod(self.orientation, 360) self.polygon.rotate(angle=self.orientation) if self.arrow: # self.arrow.rotate(angle=angle) self.arrow.rotate(angle=np.mod(angle, 90)) return self def translate(self, dx, dy): """ Translate port by dx and dy. """ self.midpoint = self.midpoint + np.array([dx, dy]) return self def move(self, midpoint=(0, 0), destination=None, axis=None): from spira.gdsii.elemental.port import __Port__ 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("[PHIDL] [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( "[PHIDL] [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 self.translate(dx, dy) self.label.move(midpoint=self.label.position, destination=self.midpoint) self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) if self.arrow: self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) return self def stretch(self, stretch_class): """ Stretch port by with the given stretch class. """ p = stretch_class.apply(self.midpoint) self.midpoint = p return self def transform(self, T): """ Transform port with the given transform class. """ if T['reflection']: self.reflect() self.label.reflect() self.polygon.reflect() if self.arrow: self.arrow.reflect() if T['rotation']: self.rotate(angle=T['rotation'], center=(0, 0)) self.label.rotate(angle=T['rotation']) self.polygon.rotate(angle=T['rotation']) if self.arrow: self.arrow.rotate(angle=T['rotation']) if T['midpoint']: self.translate(dx=T['midpoint'][0], dy=T['midpoint'][1]) self.label.move(midpoint=self.label.position, destination=self.midpoint) self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) if self.arrow: self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) return self def _update(self, name, layer): ll = deepcopy(layer) ll.datatype = 65 self.polygon.gdslayer = ll self.label.gdslayer = ll
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 __Manhattan__(spira.Cell): port1 = param.DataField() port2 = param.DataField() length = param.FloatField(default=20) gdslayer = param.LayerField(number=13) radius = param.IntegerField(default=1) bend_type = param.StringField(default='circular') b1 = param.DataField(fdef_name='create_arc_bend_1') b2 = param.DataField(fdef_name='create_arc_bend_2') p1 = param.DataField(fdef_name='create_port1_position') p2 = param.DataField(fdef_name='create_port2_position') quadrant_one = param.DataField(fdef_name='create_quadrant_one') quadrant_two = param.DataField(fdef_name='create_quadrant_two') quadrant_three = param.DataField(fdef_name='create_quadrant_three') quadrant_four = param.DataField(fdef_name='create_quadrant_four') def _generate_route(self, p1, p2): route = RouteShape(port1=p1, port2=p2, path_type='straight', width_type='straight') R1 = RouteBasic(route=route, connect_layer=self.gdslayer) r1 = spira.SRef(R1) r1.rotate(angle=p2.orientation - 180, center=R1.port1.midpoint) r1.move(midpoint=(0, 0), destination=p1.midpoint) return r1 def create_port1_position(self): p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] if self.port1.orientation == 90: p1 = [self.port1.midpoint[1], -self.port1.midpoint[0]] if self.port1.orientation == 180: p1 = [-self.port1.midpoint[0], -self.port1.midpoint[1]] if self.port1.orientation == 270: p1 = [-self.port1.midpoint[1], self.port1.midpoint[0]] return p1 def create_port2_position(self): p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] if self.port1.orientation == 90: p2 = [self.port2.midpoint[1], -self.port2.midpoint[0]] if self.port1.orientation == 180: p2 = [-self.port2.midpoint[0], -self.port2.midpoint[1]] if self.port1.orientation == 270: p2 = [-self.port2.midpoint[1], self.port2.midpoint[0]] return p2 def create_arc_bend_1(self): if self.bend_type == 'circular': B1 = Arc(shape=ArcRoute( radius=self.radius, width=self.port1.width, gdslayer=self.gdslayer, # gdslayer=spira.Layer(number=18), start_angle=0, theta=90)) return spira.SRef(B1) def create_arc_bend_2(self): if self.bend_type == 'circular': B2 = Arc(shape=ArcRoute( radius=self.radius, width=self.port1.width, gdslayer=self.gdslayer, # gdslayer=spira.Layer(number=18), start_angle=0, theta=-90)) return spira.SRef(B2)
class PolygonAbstract(__Polygon__): gdslayer = param.LayerField() gdspy_commit = param.BoolField() clockwise = param.BoolField(default=True) nodes = param.DataField(fdef_name='create_nodes') edges = param.DataField(fdef_name='create_edges') def __init__(self, shape, **kwargs): from spira.lgm.shapes.shape import __Shape__ from spira.lgm.shapes.shape import Shape if issubclass(type(shape), __Shape__): self.shape = shape elif isinstance(shape, (list, set, np.ndarray)): self.shape = Shape(points=shape) else: raise ValueError('Shape type not supported!') ElementalInitializer.__init__(self, **kwargs) gdspy.PolygonSet.__init__(self, self.shape.points, layer=self.gdslayer.number, datatype=self.gdslayer.datatype, verbose=False) def create_nodes(self): """ Created nodes of each point in the polygon array. Converting a point to a node allows us to bind other objects to that specific node or point. """ pass def create_edges(self): """ A list of tuples containing two nodes. """ pass def move_edge(self): pass def commit_to_gdspy(self, cell): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): ply = deepcopy(self.shape.points) P = gdspy.PolygonSet(ply, self.gdslayer.number, self.gdslayer.datatype) cell.add(P) PolygonAbstract.__committed__.update({self.__repr__(): P}) else: cell.add(PolygonAbstract.__committed__[self.__repr__()]) def flat_copy(self, level=-1, commit_to_gdspy=False): elems = [] for points in self.shape.points: c_poly = self.modified_copy(shape=deepcopy([points]), gdspy_commit=self.gdspy_commit) elems.append(c_poly) if commit_to_gdspy: self.gdspy_commit = True return elems def merge(self, other): if isinstance(other, (list, set)): pass elif isinstance(other, Polygons): pass else: raise ValueError('Type is not supported for Polygon merging.') def transform(self, transform): if transform['reflection']: self.reflect(p1=[0, 0], p2=[1, 0]) if transform['rotation']: self.rotate(angle=transform['rotation']) if transform['midpoint']: self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) self.shape.points = self.polygons return self def reflect(self, p1=(0, 1), p2=(0, 0)): for n, points in enumerate(self.shape.points): self.shape.points[n] = self.__reflect__(points, p1, p2) self.shape.points = self.polygons return self def rotate(self, angle=45, center=(0, 0)): super().rotate(angle=angle * np.pi / 180, center=center) self.shape.points = self.polygons return self def translate(self, dx, dy): super().translate(dx=dx, dy=dy) self.shape.points = self.polygons return self def stretch(self, stretch_class): p = stretch_class.apply_to_polygon(self.points[0]) self.shape.points = [np.array(p)] return self def move(self, midpoint=(0, 0), destination=None, axis=None): from spira.gdsii.elemental.port import __Port__ """ 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("[PHIDL] [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( "[PHIDL] [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 self.translate(dx, dy) return self def fast_boolean(self, other, operation): mm = gdspy.fast_boolean(self.shape.points, other.shape.points, operation=operation) return Polygons(shape=mm.points, gdslayer=self.gdslayer)