class BasicSpline(shapes.Shape): """ """ radius = param.FloatField(default=5) angle = param.FloatField(default=10) angle_step = param.FloatField(default=2) def create_points(self, pts): DEG2RAD = np.pi / 180.0 alpha = self.angle * DEG2RAD c = math.sin(alpha) if self.angle == 45.0: t = 0.5 else: c2 = c**2 t = math.sin(math.atan(((1.0 - c2) / c2)**0.125))**2 L = self.radius * 2 * t * (1 - t) / (3 * (t**4 + (1 - t)**4)**1.5) q0_0 = np.array([-L, 0]) q0_1 = np.array([0, 0]) q0_2 = np.array([0, 0]) q0_3 = np.array([0, L]) q1_0 = t * q0_0 + (1 - t) * q0_1 q2_0 = t**2 * q0_0 + 2 * t * (1 - t) * q0_1 + (1 - t)**2 * q0_2 q3_0 = t**3 * q0_0 + 3 * t**2 * (1 - t) * q0_1 + 3 * t * ( 1 - t)**2 * q0_2 + (1 - t)**3 * q0_3 S = shapes.Shape(points=[[q0_0, q1_0, q2_0, q3_0]]) steps = int(math.ceil(2.0 * self.angle / self.angle_step)) return BezierCurve(midpointal_shape=S, steps=steps).points
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 TerminalExample(spira.Cell): width = param.FloatField(default=10) height = param.FloatField(default=1) def create_ports(self, ports): ports += spira.Term(name='P1', midpoint=(10, 0), width=self.height, orientation=180) return ports
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 BasicTriangle(Shape): a = param.FloatField(default=2) b = param.FloatField(default=0.5) c = param.FloatField(default=1) def create_points(self, points): p1 = [0, 0] p2 = [p1[0] + self.b, p1[1]] p3 = [p1[0], p1[1] + self.a] pts = np.array([p1, p2, p3]) points = [pts] return points
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 JunctionSquid(spira.Cell): width = param.FloatField() height = param.FloatField() midpoint = param.FloatField() w = param.FloatField() h = param.FloatField() top_routing = param.DataField(fdef_name='create_top_routing') bot_routing = param.DataField(fdef_name='create_bot_routing') def create_top_routing(self): p1 = [self.midpoint, self.h / 2] p2 = [self.midpoint, self.h / 2 + self.height] p3 = [self.width, self.h / 2 + self.height] p4 = [self.width, self.h / 2] points = [p1, p2, p3, p4] return spira.Path(points, width=1, gdslayer=RDD.M5, distance=3) def create_bot_routing(self): p1 = [self.midpoint, -self.h / 2] p2 = [self.midpoint, -self.height] p3 = [self.width, -self.height] p4 = [self.width, -self.h / 2] points = [p1, p2, p3, p4] return spira.Path(points, width=1, gdslayer=RDD.M6, distance=3) def create_elementals(self, elems): jj = Junction() # FIXME: Automate this movement. jj.move(origin=jj.center, destination=(0, 0)) # FIXME: Rotation applies to parent cell. j1 = spira.SRef(jj, origin=(-1, 0), rotation=90) # j1.move(origin=j1.ref.center, destination=(0,0)) j2 = spira.SRef(jj, origin=(10.5, 0), rotation=180) elems += j1 elems += j2 elems += self.top_routing elems += self.bot_routing return elems
class BoxShape(Shape): width = param.FloatField(default=1) height = param.FloatField(default=1) def create_points(self, points): cx = self.center[0] cy = self.center[1] dx = 0.5 * self.width dy = 0.5 * self.height pts = [(cx + dx, cy + dy), (cx - dx, cy + dy), (cx - dx, cy - dy), (cx + dx, cy - dy)] points = np.array([pts]) return points
class CircleShape(Shape): box_size = param.PointField(default=(1.0, 1.0)) start_angle = param.FloatField(default=0.0) end_angle = param.FloatField(default=360.0) angle_step = param.FloatField(default=3) def create_points(self, points): sa = self.start_angle * DEG2RAD ea = self.end_angle * DEG2RAD h_radius = self.box_size[0] / 2.0 v_radius = self.box_size[1] / 2.0 n_s = float(self.end_angle - self.start_angle) / self.angle_step n_steps = int(math.ceil(abs(n_s))) * np.sign(n_s) if n_steps == 0: if sa == ea: pts = np.array([[ math.cos(sa) * h_radius + self.center[0], math.sin(sa) * v_radius + self.center[1] ]]) else: pts = np.array([[ math.cos(sa) * h_radius + self.center[0], math.sin(sa) * v_radius + self.center[1] ], [ math.cos(ea) * h_radius + self.center[0], math.sin(ea) * v_radius + self.center[1] ]]) return pts angle_step = float(ea - sa) / n_steps if self.clockwise: angle_step = -angle_step sign = -1 else: sign = +1 while sign * sa > sign * ea: ea += sign * 2 * math.pi angles = np.arange(sa, ea + 0.5 * angle_step, angle_step) pts = np.column_stack((np.cos(angles), np.sin(angles))) \ * np.array([(h_radius, v_radius)]) \ + np.array([(self.center[0], self.center[1])]) points = np.array([pts]) return points
class LibraryAbstract(__Library__): grid = param.FloatField(default=RDD.GDSII.GRID) grids_per_unit = param.DataField(fdef_name='create_grids_per_unit') units_per_grid = param.DataField(fdef_name='create_units_per_grid') def create_grids_per_unit(self): return self.unit / self.grid def create_units_per_grid(self): return self.grid / self.unit def validate_parameters(self): if self.grid > self.unit: raise Exception('The grid should be smaller than the unit.') return True def referenced_structures(self): referred_to_list = list() for s in self.cells: referred_to_list.append(s.dependencies()) return referred_to_list def get_cell(self, cell_name): for C in self.cells: if C.name == cell_name: return C return None def is_empty(self): return len(self.cells) == 0 def clear(self): self.cells.clear()
class Squid(spira.Cell): m1 = param.MidPointField(default=(0, 0)) m2 = param.MidPointField(default=(0, 0)) rotation = param.FloatField(default=0) def create_elementals(self, elems): jj = Junction() jj.center = (0, 0) s1 = spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) s2 = spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) r1 = RouteManhattan(port1=s1.ports['Output'], port2=s2.ports['Output'], radius=1, length=1) r2 = RouteManhattan(port1=s1.ports['Input'], port2=s2.ports['Input'], radius=1, length=1) s3 = spira.SRef(r1) elems += s3 s4 = spira.SRef(r2) elems += s4 elems += [s1, s2] return elems
class Triangle(shapes.Shape): """ Right triangle """ a = param.FloatField(default=1) b = param.FloatField(default=1) c = param.FloatField(default=1) def create_points(self, points): p1 = [0, 0] p2 = [p1[0]+self.b, p1[1]] p3 = [p1[0], p1[1]+self.a] points = [[p1, p2, p3]] return points
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 Box(Base): w = param.FloatField(default=1) h = param.FloatField(default=1) center = param.PointField() def validate_parameters(self): if self.w < self.player.data.WIDTH: return False if self.h < self.player.data.WIDTH: return False return True def create_polygon(self): shape = shapes.BoxShape(center=self.center, width=self.w, height=self.h) ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) return ply
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 __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 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 Port(PortAbstract): """ Ports are objects that connect different polygons or references in a layout. Ports represent vertical connection such as vias or junctions. Examples -------- >>> port = spira.Port() """ edge_width = param.FloatField(default=0.25) def __init__(self, port=None, polygon=None, **kwargs): super().__init__(port=port, polygon=polygon, **kwargs) if polygon is None: from spira import shapes shape = shapes.CircleShape( center=self.midpoint, box_size=[self.edge_width, self.edge_width]) pp = spira.Polygons(shape=shape, gdslayer=self.gdslayer) pp.move(midpoint=pp.center, destination=self.midpoint) self.polygon = pp else: self.polygon = polygon def __repr__(self): return ("[SPiRA: Port] (name {}, number {}, midpoint {}, " + "radius {}, orientation {})").format(self.name, self.gdslayer.number, self.midpoint, self.edge_width, self.orientation) def _copy(self): new_port = Port(parent=self.parent, polygon=deepcopy(self.polygon), name=self.name, midpoint=deepcopy(self.midpoint), edge_width=self.edge_width, gdslayer=deepcopy(self.gdslayer), poly_layer=deepcopy(self.poly_layer), text_layer=deepcopy(self.text_layer), orientation=deepcopy(self.orientation)) return new_port
class BezierCurve(__ShapeContainer__): """ polynomial bezier curve based on a shape with control points """ steps = param.FloatField(default=100) def __init__(self, midpointal_shape, **kwargs): super().__init__(midpointal_shape=midpointal_shape, **kwargs) def create_points(self, pts): step = 1.0 / self.steps t = np.arange(0.0, 1.0 + 0.5 * step, step) P = np.array(self.midpointal_shape.points[0]) Px = np.outer(P[:, 0], np.ones(np.size(t))) Py = np.outer(P[:, 1], np.ones(np.size(t))) for j in range(len(self.midpointal_shape.points[0]) - 1, 0, -1): Px = Px[0:j, :] + np.diff(Px, 1, 0) * t Py = Py[0:j, :] + np.diff(Py, 1, 0) * t pts = np.transpose(np.row_stack((Px, Py))) points = np.array([pts]) return points
class SRefAbstract(__SRef__): midpoint = param.MidPointField() rotation = param.FloatField(default=0) reflection = param.BoolField(default=False) magnification = param.FloatField(default=1) def dependencies(self): """ """ from spira.gdsii.lists.cell_list import CellList d = CellList() d.add(self.ref) d.add(self.ref.dependencies()) return d def _copy(self, level=0): S = SRef(structure=self.ref, midpoint=self.midpoint, rotation=self.rotation, magnification=self.magnification, reflection=self.reflection) return S def flat_copy(self, level=-1, commit_to_gdspy=False): """ """ if level == 0: el = spira.ElementList() el += self return el transform = { 'midpoint': self.midpoint, 'rotation': self.rotation, 'magnification': self.magnification, 'reflection': self.reflection } el = self.ref.elementals.flat_copy(level - 1) el.transform(transform) return el 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 flatten(self): return self.ref.flatten() @property def ports(self): """ This property allows you to access my_device_reference.ports, and receive a copy of the ports dict which is correctly rotated and translated """ for port in self._parent_ports: tf = { 'midpoint': self.midpoint, 'rotation': self.rotation, 'magnification': self.magnification, 'reflection': self.reflection } # print(tf['rotation']) new_port = port._copy() self._local_ports[port.name] = new_port.transform(tf) return self._local_ports def move(self, midpoint=(0, 0), destination=None, axis=None): """ Moves the DeviceReference 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_ref """ 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]) dxdy = np.array(d) - np.array(o) self.midpoint = np.array(self.midpoint) + dxdy return self def translate(self, dx=0, dy=0): """ Translate port by dx and dy. """ super().translate(dx=dx, dy=dy) self.midpoint = self.midpoint return self def rotate(self, angle=45, center=(0, 0)): """ """ if angle == 0: return self if issubclass(type(center), __Port__): center = center.midpoint self.rotation += angle self.midpoint = self.__rotate__(self.midpoint, angle, center) return self def reflect(self, p1=(0, 1), p2=(0, 0)): """ """ if issubclass(type(p1), __Port__): p1 = p1.midpoint if issubclass(type(p2), __Port__): p2 = p2.midpoint p1 = np.array(p1) p2 = np.array(p2) # Translate so reflection axis passes through midpoint self.midpoint = self.midpoint - p1 # Rotate so reflection axis aligns with x-axis angle = np.arctan2((p2[1] - p1[1]), (p2[0] - p1[0])) * 180 / np.pi self.midpoint = self.__rotate__(self.midpoint, angle=-angle, center=[0, 0]) self.rotation -= angle # Reflect across x-axis self.reflection = not self.reflection self.midpoint = [self.midpoint[0], -self.midpoint[1]] self.rotation = -self.rotation # Un-rotate and un-translate self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=[0, 0]) self.rotation += angle self.midpoint = self.midpoint + p1 return self def connect(self, port, destination, overlap=0): """ """ if port in self.ports.keys(): p = self.ports[port] elif issubclass(type(port), __Port__): p = port else: raise ValueError( "[SPiRA] connect() did not receive a Port or " + "valid port name - received ({}), ports available " + "are ({})").format(port, self.ports.keys()) angle = 180 + destination.orientation - p.orientation self.rotate(angle=angle, center=p.midpoint) self.move(midpoint=p, destination=destination) return self def stretch(self, port, center=[0, 0], vector=[1, 1]): """ """ from spira.lgm.shape.stretch import Stretch self.stretching[port] = Stretch(center=center, vector=vector) 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 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 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 CellA(spira.Cell): layer = param.LayerField(number=18, datatype=1) boolean = param.BoolField(default=False) fvalue = param.FloatField(default=0.0)
class Jtl(spira.Cell): m1 = param.MidPointField(default=(0, 0)) m2 = param.MidPointField(default=(0, 0)) rotation = param.FloatField(default=0) jj1 = param.DataField(fdef_name='create_junction_one') jj2 = param.DataField(fdef_name='create_junction_two') quadrant = param.DataField(fdef_name='create_quadrant') def create_quadrant(self): quadrant = None if (self.m2[1] > self.m1[1]) and (self.m2[0] > self.m1[0]): quadrant = 'Q1' if (self.m2[1] > self.m1[1]) and (self.m2[0] < self.m1[0]): quadrant = 'Q2' if (self.m2[1] < self.m1[1]) and (self.m2[0] < self.m1[0]): quadrant = 'Q3' if (self.m2[1] < self.m1[1]) and (self.m2[0] > self.m1[0]): quadrant = 'Q4' return quadrant def create_junction_one(self): jj = Junction() jj.center = (0, 0) return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) def create_junction_two(self): jj = Junction() jj.center = (0, 0) return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) def create_elementals(self, elems): s1 = self.jj1 s2 = self.jj2 if self.quadrant in ['Q1', 'Q4']: route = RouteManhattan(port1=s1.ports['Output'], port2=s2.ports['Input'], radius=3, length=1, gdslayer=RDD.COU.LAYER) if self.quadrant in ['Q2', 'Q3']: route = RouteManhattan(port1=s2.ports['Output'], port2=s1.ports['Input'], radius=3, length=1, gdslayer=RDD.COU.LAYER) s3 = spira.SRef(route) s3.move(midpoint=s3.ports['T1'], destination=route.port1) r1 = Route(port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.COU) elems += spira.SRef(r1) r2 = Route(port1=self.term_ports['T2'], port2=s2.ports['Output'], player=RDD.PLAYER.COU) elems += spira.SRef(r2) elems += [s1, s2, s3] return elems def create_ports(self, ports): if self.quadrant in ['Q1', 'Q4']: ports += spira.Term(name='T1', midpoint=self.jj1.ports['Input'] + [-10, 0], orientation=-90) ports += spira.Term(name='T2', midpoint=self.jj2.ports['Output'] + [10, 0], orientation=90) if self.quadrant in ['Q2', 'Q3']: ports += spira.Term(name='T1', midpoint=self.jj1.ports['Input'] + [10, 0], orientation=-90) ports += spira.Term(name='T2', midpoint=self.jj2.ports['Output'] + [-10, 0], orientation=90) return ports
class Term(PortAbstract): """ Terminals are horizontal ports that connect SRef instances in the horizontal plane. They typically represents the i/o ports of a components. Examples -------- >>> term = spira.Term() """ width = param.FloatField(default=2) length = param.FloatField(default=0.1) def __init__(self, port=None, polygon=None, **kwargs): super().__init__(port=port, polygon=polygon, **kwargs) from spira import shapes if polygon is None: rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[self.width, self.length]) pp = spira.Polygons(shape=rect_shape, gdslayer=spira.Layer(number=65)) pp.rotate(angle=self.orientation, center=self.midpoint) # pp.rotate(angle=90-self.orientation, center=self.midpoint) pp.move(midpoint=pp.center, destination=self.midpoint) self.polygon = pp else: self.polygon = polygon arrow_shape = shapes.ArrowShape(a=self.width / 10, b=self.width / 20, c=self.width / 5) arrow_shape.apply_merge # arrow_shape.rotate(angle=self.orientation) self.arrow = spira.Polygons(shape=arrow_shape, gdslayer=spira.Layer(number=77)) self.arrow.rotate(angle=self.orientation) # self.arrow.rotate(angle=90-self.orientation) def __repr__(self): return ("[SPiRA: Term] (name {}, number {}, midpoint {}, " + "width {}, orientation {})").format(self.name, self.gdslayer.number, self.midpoint, self.width, self.orientation) def _copy(self): new_port = Term(parent=self.parent, name=self.name, midpoint=self.midpoint, width=self.width, length=self.length, gdslayer=deepcopy(self.gdslayer), poly_layer=deepcopy(self.poly_layer), text_layer=deepcopy(self.text_layer), orientation=self.orientation) return new_port
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 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