Esempio n. 1
0
class ComposeMLayers(__CellContainer__):
    """
    Decorates all elementals with purpose metal with
    LCells and add them as elementals to the new class.
    """

    cell_elems = param.ElementListField()

    mlayers = param.DataField(fdef_name='create_mlayers')

    def _merge_layers(self, flat_metals):
        points = []
        elems = spira.ElementList()
        for p in flat_metals:
            for pp in p.polygons:
                points.append(pp)
        if points:
            from spira.gdsii.utils import scale_polygon_down as spd
            points = spd(points)
            shape = shapes.Shape(points=points)
            shape.apply_merge
            for pts in shape.points:
                pts = spd([pts])
                elems += spira.Polygons(shape=pts)
        return elems

    def create_mlayers(self):
        elems = spira.ElementList()
        # players = RDD.PLAYER.get_physical_layers(purpose_symbol=['METAL', 'GROUND', 'MOAT'])
        flat_elems = self.cell_elems.flat_copy()
        for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'):

            metal_elems = flat_elems.get_polygons(layer=pl.layer)

            if metal_elems:
                c_mlayer = CMLayers(layer=pl.layer)
                for i, ply in enumerate(self._merge_layers(metal_elems)):
                    ml = MLayer(name='MLayer_{}_{}_{}_{}'.format(
                        pl.layer.number, self.cell.name, self.cell.id, i),
                                points=ply.polygons,
                                number=pl.layer.number)
                    c_mlayer += spira.SRef(ml)
                elems += spira.SRef(c_mlayer)
        return elems

    def create_elementals(self, elems):

        # TODO: Apply DRC checking between metals, before being placed.

        for lcell in self.mlayers:
            elems += lcell

        # FIXME: Allow this operation.
        # elems += self.mlayers

        return elems
Esempio n. 2
0
class DLayer(__DeviceLayer__):

    blayer = param.PolygonField()
    device_elems = param.ElementListField()
    box = param.DataField(fdef_name='create_box_layer')
    terms = param.DataField(fdef_name='create_labels')

    color = param.ColorField(default='#e54e7f')

    def create_labels(self):
        elems = ElementList()
        for p in self.device_elems.polygons:
            layer = p.gdslayer.number
            players = RDD.PLAYER.get_physical_layers(purposes='METAL')
            if layer in players:
                l2 = Layer(name='BoundingBox', number=layer, datatype=8)
                # FIXME: Ports with the same name overrides eachother.
                elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2)
        return elems

    def create_box_layer(self):
        elems = ElementList()
        setter = {}

        for p in self.device_elems.polygons:
            layer = p.gdslayer.number
            setter[layer] = 'not_set'

        for p in self.device_elems.polygons:
            layer = p.gdslayer.number
            players = RDD.PLAYER.get_physical_layers(purposes=['METAL'])
            if layer in players and setter[layer] == 'not_set':
                l1 = Layer(name='BoundingBox', number=layer, datatype=8)
                elems += Polygons(polygons=self.blayer.polygons, gdslayer=l1)
                setter[layer] = 'already_set'
        return elems

    def create_elementals(self, elems):

        elems += self.box
        elems += self.terms

        elems = elems.flatten()

        return elems
Esempio n. 3
0
class ConnectDesignRules(ComposeGLayer):

    metal_elems = param.ElementListField()

    def create_elementals(self, elems):

        super().create_elementals(elems)

        incorrect_elems = ElementList()
        correct_elems = ElementList()

        for rule in RDD.RULES.elementals:
            if not rule.apply(elems):
                for composed_lcell in elems:
                    for lcell in composed_lcell.ref.elementals.sref:
                        if lcell.ref.layer.number == rule.layer1.number:
                            correct_elems += lcell
        return elems
Esempio n. 4
0
class Route(__Path__):

    ports = param.ElementListField(fdef_name='create_ports')
    # ports = param.PortListField(fdef_name='create_ports')

    input_term = param.DataField(fdef_name='create_port_input')
    output_term = param.DataField(fdef_name='create_port_output')

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def create_port_input(self):
        return None

    def create_port_output(self):
        return None

    def create_ports(self, ports):
        return ports
Esempio n. 5
0
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
Esempio n. 6
0
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
Esempio n. 7
0
class GLayer(__ProcessLayer__):
    """ Ground Plane layer. """

    blayer = param.PolygonField()
    device_elems = param.ElementListField()
    box = param.DataField(fdef_name='create_box_layer')
    terms = param.DataField(fdef_name='create_labels')

    def create_labels(self):
        elems = ElementList()
        for p in self.device_elems.polygons:
            layer = p.gdslayer.number
            # if layer in RDD.GROUND.layers:
            if layer == RDD.GDSII.GPLAYER:
                l2 = Layer(name='BoundingBox', number=layer, datatype=5)
                elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2)
        return elems

    def create_box_layer(self):
        elems = ElementList()
        for p in self.device_elems.polygons:
            layer = p.gdslayer.number
            # if layer in RDD.GROUND.layers:
            if layer == RDD.GDSII.GPLAYER:
                l1 = Layer(name='BoundingBox', number=layer, datatype=5)
                elems += Polygons(polygons=self.blayer.polygons, gdslayer=l1)
        return elems

    def create_elementals(self, elems):

        super().create_elementals(elems)

        # elems += self.box
        # elems += self.terms
        #
        # elems = elems.flatten()

        return elems
Esempio n. 8
0
class ComposeGLayer(ComposeNLayer):

    plane_elems = param.ElementListField(
    )  # Elementals like skyplanes and groundplanes.
    ground_layer = param.DataField(fdef_name='create_merged_ground_layers')

    def create_merged_ground_layers(self):
        points = []
        for p in self.plane_elems.flat_copy():
            for pp in p.polygons:
                points.append(pp)
        if points:
            ll = Layer(number=RDD.GDSII.GPLAYER, datatype=6)
            merged_ply = UnionPolygons(polygons=points, gdslayer=ll)
            return merged_ply
        return None

    def create_elementals(self, elems):

        super().create_elementals(elems)

        if self.level == 1:
            if self.ground_layer:
                box = self.cell.bbox
                # box.move(midpoint=box.center, destination=(0,0))

                gnd = self.ground_layer | box
                if gnd:
                    c_glayer = CGLayers(layer=gnd.gdslayer)
                    name = 'GLayer_{}_{}'.format(self.cell.name,
                                                 gnd.gdslayer.number)
                    gnd_layer = GLayer(name=name,
                                       layer=gnd.gdslayer,
                                       player=gnd)
                    c_glayer += spira.SRef(gnd_layer)
                    elems += spira.SRef(c_glayer)

        return elems
Esempio n. 9
0
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
Esempio n. 10
0
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
Esempio n. 11
0
class MeshLabeled(MeshAbstract):

    RDD = spira.get_rule_deck()

    primitives = param.ElementListField()

    surface_nodes = param.DataField(fdef_name='create_surface_nodes')
    pinlabel_nodes = param.DataField(fdef_name='create_pinlabel_nodes')

    def __init__(self, polygons, points, cells, **kwargs):
        print('\nPinLabels object')

        super().__init__(polygons, points, cells, **kwargs)

        self.points = points
        self.cells = cells

        self.surface_nodes
        self.pinlabel_nodes

    def create_surface_nodes(self):

        LOG.header('Adding surface labels')

        node_count = 0

        triangles = self.__layer_triangles_dict__()
        for key, nodes in triangles.items():
            for n in nodes:
                position = self.g.node[n]['pos']

                pid = utils.labeled_polygon_id(position, self.polygons)

                if pid is not None:
                    params = {}
                    params['text'] = self.name
                    params['gdslayer'] = self.layer
                    params['color'] = RDD.METALS.get_key_by_layer(
                        self.layer)['COLOR']

                    label = spira.Label(position=position, **params)
                    label.id = '{}_{}'.format(key[0], pid)

                    self.g.node[n]['surface'] = label

            node_count += 1

        print('# surface nodes added: {}'.format(node_count))

    def create_pinlabel_nodes(self):

        LOG.header('Adding pin labels')

        for node, triangle in self.__triangle_nodes__().items():
            points = [utils.c2d(self.points[i]) for i in triangle]
            for S in self.primitives:
                if isinstance(S, spira.Port):
                    self.add_port_label(node, S, points)
                else:
                    self.add_device_label(node, S, points)

    def add_new_node(self, n, D, pos):
        params = {}
        params['text'] = 'new'
        l1 = spira.Layer(name='Label', number=104)
        params['gdslayer'] = l1
        # params['color'] = RDD.METALS.get_key_by_layer(self.layer)['COLOR']

        label = spira.Label(position=pos, **params)
        label.id = '{}_{}'.format(n, n)

        num = self.g.number_of_nodes()

        self.g.add_node(num + 1, pos=pos, pin=D, surface=label)
        self.g.add_edge(n, num + 1)

    def add_port_label(self, n, D, points):
        if D.point_inside(points):
            P = spira.PortNode(name=D.name, elementals=D)
            self.g.node[n]['pin'] = P

    def add_device_label(self, n, S, points):
        for name, p in S.ports.items():
            if p.gdslayer.name == 'GROUND':
                pass
                # if lbl.layer == self.layer.number:
                #     params = {}
                #     params['text'] = 'GROUND'
                #     l1 = spira.Layer(name='GND', number=104)
                #     params['gdslayer'] = l1
                #
                #     label = spira.Label(position=lbl.position, **params)
                #     label.id = '{}_{}'.format(n, n)
                #
                #     ply = spira.Polygons(gdslayer=l1)
                #
                #     D_gnd = BaseVia(name='BaseVIA_GND',
                #                     ply=ply,
                #                     m2=l1, m1=l1)
                #
                #     num = self.g.number_of_nodes()
                #
                #     self.g.add_node(num+1, pos=lbl.position, pin=D_gnd, surface=label)
                #     self.g.add_edge(n, num+1)
            else:
                # print(S.flat_copy())
                # print(p.gdslayer.number)
                # print(self.layer.number)
                # print('')
                if p.gdslayer.number == self.layer.number:
                    if p.point_inside(points):
                        self.g.node[n]['pin'] = S
Esempio n. 12
0
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
Esempio n. 13
0
class __ElementalContainer__(Cell):

    received_elementals = param.ElementListField()

    def create_elementals(self, elems):
        return elems
Esempio n. 14
0
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