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 Density(__DoubleLayerDesignRule__): minimum = param.IntegerField() error = param.IntegerField(default=RDD.PURPOSE.ERROR.DENSITY.datatype) # TODO: Detect holes in die polygon def __repr__(self): return 'Rule density: min={}'.format(self.minimum) def get_layer_area(self, elems): area = 0.0 for e in elems: area += e.ply_area return area def apply(self, elems): pos_elems = spira.ElementList() neg_elems = spira.ElementList() for C in elems.dependencies(): if C.layer.number == self.layer1.number: pos_elems = C.elementals elif C.layer.number == self.layer2.number: neg_elems = C.elementals fails = False Ap = self.get_layer_area(pos_elems) An = self.get_layer_area(neg_elems) if (Ap > 0) and (An > 0): presentage = 100 - (An / Ap) * 100 if presentage < self.minimum: fails = True print('\n ------ Design Rules ------') print(self.layer1) message = '[DRC: Density ({})]: (layer1 {}, layer2 {}, extracted_value {}%, rule_value {}%)'.format( 'fail', self.layer1.number, self.layer2.number, int(round(presentage)), self.min) raise ValueError(message) else: fails = False print('\n ------ Design Rules ------') print(self.layer1) print('Density ({}): {}%'.format('pass', int(round(presentage)))) return fails
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 __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 YtronShape(shapes.Shape): """ """ rho = param.IntegerField(default=1) arm_lengths = param.PointField(default=(500, 300)) source_length = param.IntegerField(default=500) arm_widths = param.PointField(default=(200, 200)) theta = param.FloatField(default=2.5) theta_resolution = param.FloatField(default=10) def create_points(self, points): theta = self.theta * np.pi / 180 theta_resolution = self.theta_resolution * np.pi / 180 thetalist = np.linspace( -(np.pi - theta), -theta, int((np.pi - 2 * theta) / theta_resolution) + 2) semicircle_x = self.rho * np.cos(thetalist) semicircle_y = self.rho * np.sin(thetalist) + self.rho xc = self.rho * np.cos(theta) yc = self.rho * np.sin(theta) arm_x_left = self.arm_lengths[0] * np.sin(theta) arm_y_left = self.arm_lengths[0] * np.cos(theta) arm_x_right = self.arm_lengths[1] * np.sin(theta) arm_y_right = self.arm_lengths[1] * np.cos(theta) xpts = semicircle_x.tolist() + [ xc + arm_x_right, xc + arm_x_right + self.arm_widths[1], xc + self.arm_widths[1], xc + self.arm_widths[1], 0, -(xc + self.arm_widths[0]), -(xc + self.arm_widths[0]), -(xc + arm_x_left + self.arm_widths[0]), -(xc + arm_x_left) ] ypts = semicircle_y.tolist() + [ yc + arm_y_right, yc + arm_y_right, yc, yc - self.source_length, yc - self.source_length, yc - self.source_length, yc, yc + arm_y_left, yc + arm_y_left ] points = np.array([list(zip(xpts, ypts))]) return points
class PurposeLayer(__Layer__): doc = param.StringField() name = param.StringField() datatype = param.IntegerField() symbol = param.StringField() def __init__(self, **kwargs): ElementalInitializer.__init__(self, **kwargs) # def __repr__(self): # string = '[SPiRA: PurposeLayer] (\'{}\', datatype {}, symbol \'{}\')' # return string.format(self.name, self.datatype, self.symbol) def __eq__(self, other): if isinstance(other, PurposeLayer): return self.key == other.key else: raise ValueError('Not Implemented!') def __ne__(self, other): if isinstance(other, PurposeLayer): return self.key != other.key else: raise ValueError('Not Implemented!') def __add__(self, other): if isinstance(other, PurposeLayer): 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, PurposeLayer): self.datatype += other.datatype elif isinstance(other, int): self.datatype += other else: raise ValueError('Not Implemented') return self def __deepcopy__(self, memo): return PurposeLayer( name=self.name, datatype=self.datatype, symbol=self.symbol ) @property def key(self): return (self.datatype, self.symbol)
class Width(__SingleLayerDesignRule__): minimum = param.FloatField() maximum = param.FloatField() error = param.IntegerField(default=RDD.PURPOSE.ERROR.MIN_WIDTH.datatype) def __repr__(self): return 'Rule width: min={} max={}'.format(self.minimum, self.maximum) def apply(self, elems): fails = False if self.violate: fails = True return fails
class ConvexPolygon(Shape): radius = param.FloatField(default=1.0) num_sides = param.IntegerField(default=6) def create_points(self, pts): if self.radius == 0.0: pts.append(self.center) return pts angle_step = 2 * math.pi / self.num_sides for i in range(0, self.num_sides): x0 = self.radius * np.cos((i + 0.5) * angle_step + math.pi / 2) y0 = self.radius * np.sin((i + 0.5) * angle_step + math.pi / 2) pts.append((self.center[0] + x0, self.center[1] + y0)) points = np.array([pts]) return points
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 __Path__(gdspy.Path, Shape): width = param.FloatField(default=1) initial_point = param.PointField() number_of_paths = param.IntegerField(default=1) distance = param.FloatField(default=0) def __init__(self, **kwargs): Shape.__init__(self, **kwargs) gdspy.Path.__init__(self, width=self.width, initial_point=self.initial_point, number_of_paths=self.number_of_paths, distance=self.distance ) def __repr__(self): if self is None: return 'Path is None!' return ("[SPiRA: Path] (width {}, distance {})").format(self.width, self.distance) def __str__(self): return self.__repr__()
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 Surround(__DoubleLayerDesignRule__): minimum = param.FloatField() error = param.IntegerField(default=RDD.PURPOSE.ERROR.SPACING.datatype) def __repr__(self): return 'Rule surround: min={}'.format(self.minimum) def apply(self, elems): pos_elems = spira.ElementList() neg_elems = spira.ElementList() # print(elems) for C in elems.dependencies(): for S in C.elementals.sref: if S.ref.layer.number == self.layer1.number: pos_elems = S.ref.elementals C1 = S.ref for C in elems.dependencies(): for S in C.elementals.sref: if S.ref.layer.number == self.layer2.number: neg_elems = S.ref.elementals C2 = S.ref fails = False if pos_elems and neg_elems: P = pos_elems[0] M = neg_elems[0] space = self.minimum * 1.0e+6 x1 = abs(P.xmax - P.center[0]) sx = (x1 + space) / x1 p_copy = deepcopy(P) p_scale = p_copy.scale(scalex=sx, scaley=sx, center=P.center) p_overlap = p_scale | M # print(M) if p_overlap: a1 = round(p_scale.ply_area * 10e-9) a2 = round(p_overlap.ply_area * 10e-9) if abs(a1 - a2) > 1e-9: fails = True P_error = ELayer( points=P.polygons, number=C1.layer.number, error_type=self.error) C1 += SRef(P_error) M_error = ELayer( points=M.polygons, number=C2.layer.number, error_type=self.error) C2 += SRef(M_error) print( '\n ------ Surround Rules ------' ) print(self.layer1) print('Surround ({}): {}'.format( 'fail', self.minimum)) else: fails = False print( '\n ------ Surround Rules ------' ) print(self.layer1) print('Surround ({}): {}'.format( 'pass', self.minimum)) return fails
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 RouteShape(shapes.Shape): port1 = param.DataField() port2 = param.DataField() num_path_pts = param.IntegerField(default=99) path_type = param.StringField(default='sine') width_type = param.StringField(default='straight') width1 = param.FloatField(default=None) width2 = param.FloatField(default=None) x_dist = param.FloatField() y_dist = param.FloatField() def create_points(self, points): point_a = np.array(self.port1.midpoint) if self.width1 is None: self.width1 = self.port1.width point_b = np.array(self.port2.midpoint) if self.width2 is None: self.width2 = self.port2.width if round( abs(mod(self.port1.orientation - self.port2.orientation, 360)), 3) != 180: raise ValueError('Ports do not face eachother.') orientation = self.port1.orientation - 90 separation = point_b - point_a distance = norm(separation) rotation = np.arctan2(separation[1], separation[0]) * 180 / pi angle = rotation - orientation forward_distance = distance * cos(angle * pi / 180) lateral_distance = distance * sin(angle * pi / 180) xf = forward_distance yf = lateral_distance self.x_dist = xf self.y_dist = yf if self.path_type == 'straight': curve_fun = lambda t: [xf * t, yf * t] curve_deriv_fun = lambda t: [xf + t * 0, 0 + t * 0] if self.path_type == 'sine': curve_fun = lambda t: [xf * t, yf * (1 - cos(t * pi)) / 2] curve_deriv_fun = lambda t: [ xf + t * 0, yf * (sin(t * pi) * pi) / 2 ] if self.width_type == 'straight': width_fun = lambda t: (self.width2 - self.width1) * t + self.width1 if self.width_type == 'sine': width_fun = lambda t: (self.width2 - self.width1) * (1 - cos( t * pi)) / 2 + self.width1 route_path = gdspy.Path(width=self.width1, initial_point=(0, 0)) route_path.parametric(curve_fun, curve_deriv_fun, number_of_evaluations=self.num_path_pts, max_points=199, final_width=width_fun, final_distance=None) points = route_path.polygons return points
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 __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 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 __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 __Generator__(__CellContainer__): level = param.IntegerField(default=1) lcar = param.IntegerField(default=0.01) algorithm = param.IntegerField(default=6) generate_devices = param.DataField(fdef_name='create_devices') def create_graph(self, elems): prim_elems = ElementList() for S in elems.sref: if isinstance(S.ref, (NLayer, TLayer, DLayer)): prim_elems += S for layer in RDD.METALS.layers: L = Cell(name='{}'.format(layer)) # ply_elems = D.get_mlayers(layer=layer) ply_elems = ElementList() for S in elems.sref: if isinstance(S.ref, CMLayers): # print(S.ref.layer) if S.ref.layer.number == layer: # print(S) for p in S.ref.elementals: # print(p.ref.player) # FIXME!!! # if isinstance(p, ELayers): # raise Errors if isinstance(p.ref.player, Polygons): ply_elems += p.ref.player if ply_elems: geom = Geometry(name='{}'.format(layer), lcar=self.lcar, algorithm=self.algorithm, layer=layer, polygons=ply_elems) mesh_data = geom.create_mesh params = { 'name': '{}'.format(layer), 'layer': Layer(number=layer), 'point_data': [mesh_data[2]], 'cell_data': [mesh_data[3]], 'field_data': [mesh_data[4]] } mesh = Mesh(polygons=ply_elems, primitives=prim_elems, points=mesh_data[0], cells=mesh_data[1], **params) L += mesh elems += SRef(L) sg = {} subgraphs = elems.subgraphs for name, g in subgraphs.items(): graph = Graph(subgraphs={name: g}) sg[name] = graph.g ng = Graph(subgraphs=sg) ng.write_graph(graphname='{}'.format(layer)) elems += ng def wrap_references(self, c, c2dmap): from spira.gdsii.utils import scale_coord_down as scd for e in c.elementals: if isinstance(e, SRef): if e.ref in c2dmap: e.ref = c2dmap[e.ref] def create_devices(self): deps = self.cell.dependencies() c2dmap = {} # for DeviceTCell in self.library.pcells: # print(RDD.DEVICES.JJ.PCELL) # for DeviceTCell in RDD.DEVICES.JJ.PCELL: # print(type(DeviceTCell)) # for C in deps: # plane_elems = ElementList() # plane_elems += self.cell.get_purpose_layers(purpose_symbol='GROUND') # # plane_elems += self.cell.elementals[(RDD.GDSII.GPLAYER, 0)] # D = Device(cell=C, cell_elems=C.elementals, plane_elems=plane_elems) # for PrimTCell in DeviceTCell.elementals.sref: # PrimTCell.ref.create_elementals(D.elementals) # c2dmap.update({C: D}) for key in RDD.DEVICES.keys: DeviceTCell = RDD.DEVICES[key].PCELL for C in deps: plane_elems = ElementList() # from spira.gdsii import utils # players = RDD.PLAYER.get_physical_layers(purposes='GROUND') # plane_elems += utils.get_purpose_layers(self.cell, players) # # plane_elems += self.cell.elementals[(RDD.GDSII.GPLAYER, 0)] D = Device(cell=C, cell_elems=C.elementals, plane_elems=plane_elems) for PrimTCell in DeviceTCell.elementals.sref: PrimTCell.ref.create_elementals(D.elementals) c2dmap.update({C: D}) for c in self.cell.dependencies(): self.wrap_references(c, c2dmap) return SRef(self.cell)