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 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 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 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 __DesignRule__(ElementalInitializer): violate = param.BoolField() doc = param.StringField() name = param.StringField()
class __DeviceLayer__(Cell): doc = param.StringField() name = param.StringField()
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 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 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 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 CellAbstract(__Cell__): name = param.StringField() ports = param.ElementListField(fdef_name='create_ports') elementals = param.ElementListField(fdef_name='create_elementals') def create_elementals(self, elems): result = ElementList() return result def create_ports(self, ports): return ports def flatten(self): self.elementals = self.elementals.flatten() return self.elementals def flat_copy(self, level=-1, commit_to_gdspy=False): self.elementals = self.elementals.flat_copy(level, commit_to_gdspy) return self.elementals def dependencies(self): deps = self.elementals.dependencies() deps += self return deps def commit_to_gdspy(self): cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: if issubclass(type(e), Cell): for elem in e.elementals: elem.commit_to_gdspy(cell=cell) for port in e.ports: port.commit_to_gdspy(cell=cell) elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): e.commit_to_gdspy(cell=cell) return cell def move(self, midpoint=(0, 0), destination=None, axis=None): """ Moves elements of the Device from the midpoint point to the destination. Both midpoint and destination can be 1x2 array-like, Port, or a key corresponding to one of the Ports in this device """ if destination is None: destination = midpoint midpoint = [0, 0] if issubclass(type(midpoint), __Port__): o = midpoint.midpoint elif np.array(midpoint).size == 2: o = midpoint elif midpoint in self.ports: o = self.ports[midpoint].midpoint else: raise ValueError('[DeviceReference.move()] ``midpoint`` ' + \ 'not array-like, a port, or port name') if issubclass(type(destination), __Port__): d = destination.midpoint elif np.array(destination).size == 2: d = destination elif destination in self.ports: d = self.ports[destination].midpoint else: raise ValueError('[DeviceReference.move()] ``destination`` ' + \ 'not array-like, a port, or port name') if axis == 'x': d = (d[0], o[1]) if axis == 'y': d = (o[0], d[1]) dx, dy = np.array(d) - o for e in self.elementals: if issubclass(type(e), (LabelAbstract, PolygonAbstract)): e.translate(dx, dy) if isinstance(e, (Cell, SRef)): e.move(destination=d, midpoint=o) for p in self.ports: mc = np.array(p.midpoint) + np.array(d) - np.array(o) p.move(midpoint=p.midpoint, destination=mc) return self def reflect(self, p1=(0, 1), p2=(0, 0)): """ Reflects the cell around the line [p1, p2]. """ for e in self.elementals: if not issubclass(type(e), (LabelAbstract, __Port__)): e.reflect(p1, p2) for p in self.ports: p.midpoint = self.__reflect__(p.midpoint, p1, p2) phi = np.arctan2(p2[1] - p1[1], p2[0] - p1[0]) * 180 / np.pi p.orientation = 2 * phi - p.orientation return self def rotate(self, angle=45, center=(0, 0)): """ Rotates the cell with angle around a center. """ if angle == 0: return self for e in self.elementals: if issubclass(type(e), PolygonAbstract): e.rotate(angle=angle, center=center) elif isinstance(e, SRef): e.rotate(angle, center) ports = self.ports self.ports = ElementList() for p in ports: if issubclass(type(p), __Port__): p.midpoint = self.__rotate__(p.midpoint, angle, center) p.orientation = np.mod(p.orientation + angle, 360) self.ports += p return self def get_ports(self, level=None): """ Returns copies of all the ports of the Device """ port_list = [p._copy() for p in self.ports] if level is None or level > 0: for r in self.elementals.sref: if level is None: new_level = None else: new_level = level - 1 ref_ports = r.ref.get_ports(level=new_level) tf = { 'midpoint': r.midpoint, 'rotation': r.rotation, 'magnification': r.magnification, 'reflection': r.reflection } ref_ports_transformed = [] for rp in ref_ports: new_port = rp._copy() new_port = new_port.transform(tf) ref_ports_transformed.append(new_port) port_list += ref_ports_transformed return port_list