Example #1
0
class PortLayout(spira.Cell):
    """  """

    port = spira.PortParameter()

    edge = Parameter(fdef_name='create_edge')
    arrow = Parameter(fdef_name='create_arrow')
    label = Parameter(fdef_name='create_label')

    def create_edge(self):
        dw = self.port.width
        dl = self.port.length / 10
        layer = PLayer(process=self.port.process, purpose=self.port.purpose)
        p = spira.Box(width=dw, height=dl, layer=layer)
        # T = transformation_from_vector(self.port) + spira.Rotation(-90)
        T = transformation_from_vector(self.port) + spira.Rotation(
            rotation=-90, rotation_center=self.port.midpoint)
        p.transform(T)
        return p

    def create_arrow(self):
        layer = PLayer(self.port.process, RDD.PURPOSE.PORT.DIRECTION)
        # w = self.port.length * 3
        w = 0.01
        # l = 2
        # l = self.port.length * 3
        l = 0.2
        arrow_shape = shapes.ArrowShape(width=w, length=l, head=l * 0.2)
        p = spira.Polygon(shape=arrow_shape, layer=layer, enable_edges=False)
        T = transformation_from_vector(self.port)
        p.transform(T)
        return p

    def create_label(self):
        # enabled_purposes = (RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED, RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED)
        # disabled_purposes = (RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED, RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED)
        # if self.port.purpose in enabled_purposes:
        #     layer = PLayer(self.port.process, RDD.PURPOSE.PORT.TEXT_ENABLED)
        # elif self.port.purpose is disabled_purposes:
        #     layer = PLayer(self.port.process, RDD.PURPOSE.PORT.TEXT_DISABLED)
        # else:
        #     layer = PLayer(self.port.process, RDD.PURPOSE.TEXT)
        purpose = RDD.PURPOSE.TEXT[self.port.purpose.symbol + 'T']
        layer = PLayer(self.port.process, purpose)
        return spira.Label(position=self.port.midpoint,
                           text=self.port.name,
                           orientation=self.port.orientation,
                           layer=layer)

    def create_elements(self, elems):
        elems += self.edge
        elems += self.label
        # if not isinstance(self.port, ContactPort):
        if self.port.purpose.name != 'ContactPort':
            elems += self.arrow
        return elems
Example #2
0
class Edge(__EdgeElement__):
    """ Edge elements are object that represents the edge
    of a polygonal shape.
    
    Example
    -------
    >>> edge Edge()
    """

    inward_extend = NumberParameter(
        default=1, doc='The distance the edge extends inwards to the shape.')
    inside = Parameter(fdef_name='create_inside')

    outward_extend = NumberParameter(
        default=1, doc='The distance the edge extends outwards to the shape.')
    outside = Parameter(fdef_name='create_outside')

    def create_inside(self):
        for e in self.elements:
            purposes = [
                RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED,
                RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED
            ]
            if e.layer.purpose in purposes:
                return e
        return None

    def create_outside(self):
        for e in self.elements:
            purposes = [
                RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED,
                RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED
            ]
            if e.layer.purpose in purposes:
                return e
        return None

    def create_elements(self, elems):

        layer = PLayer(process=self.process,
                       purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED)
        elems += Box(alias='InsideEdge',
                     width=self.width,
                     height=self.inward_extend,
                     center=Coord(0, self.inward_extend / 2),
                     layer=layer)

        layer = PLayer(process=self.process,
                       purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED)
        elems += Box(alias='OutsideEdge',
                     width=self.width,
                     height=self.outward_extend,
                     center=Coord(0, -self.outward_extend / 2),
                     layer=layer)

        return elems
Example #3
0
class RouteGeneral(Cell):

    layer = LayerParameter()

    route_shape = ShapeParameter(doc='Shape of the routing polygon.')

    port_input = Parameter(fdef_name='create_port_input')
    port_output = Parameter(fdef_name='create_port_output')

    gds_layer = Parameter(fdef_name='create_gds_layer')

    def create_gds_layer(self):
        ll = spira.Layer(
            number=self.layer.number,
            # datatype=RDD.PURPOSE.TERM.datatype
            datatype=22)
        return ll

    def create_port_input(self):
        term = spira.Port(
            name='P1',
            midpoint=self.route_shape.m1,
            width=self.route_shape.w1,
            orientation=self.route_shape.o1,
            # gds_layer=self.gds_layer
        )
        return term

    def create_port_gdsii_output(self):
        term = spira.Port(
            name='P2',
            midpoint=self.route_shape.m2,
            width=self.route_shape.w2,
            orientation=self.route_shape.o2,
            # gds_layer=self.gds_layer
        )
        return term

    def create_elements(self, elems):
        poly = spira.Polygon(shape=self.route_shape,
                             layer=self.layer,
                             enable_edges=False)
        elems += poly
        return elems

    def create_ports(self, ports):
        ports += self.port_input
        ports += self.port_output
        return ports
Example #4
0
class InputBasic(__InputBasic__):
    """  """

    prefix = StringParameter(default='')
    flatten = BoolParameter()
    layer_map = Parameter(default=RDD.GDSII.IMPORT_LAYER_MAP)

    def __init__(self, file_name, **kwargs):
        super().__init__(file_name=file_name, **kwargs)
        self.library = None

    def read(self):
        self.library = Library('Imported')
        for cell in self.parse.dependencies():
            self.library += cell
        return self.library

    def map_layer(self, layer):
        L = self.layer_map.get(layer, None)
        if isinstance(L, __Layer__):
            return L
        elif L is None:
            return L
        else:
            return Layer(L)
Example #5
0
class NetlistAspects(__Aspects__):
    """ Defines the nets from the defined elements. """

    netlist = Parameter(fdef_name='create_netlist')

    def create_netlist(self):
        net = Net()
        return net
Example #6
0
class UnitGridContainer(ParameterInitializer):
    """  """

    grids_per_unit = Parameter(fdef_name='create_grids_per_unit')
    units_per_grid = Parameter(fdef_name='create_units_per_grid')
    unit = NumberParameter(default=RDD.GDSII.UNIT)
    grid = NumberParameter(default=RDD.GDSII.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 ValueError('The grid should be smaller than the unit.')
        return True
Example #7
0
class RoutePointShape(__RouteSimple__):

    width = NumberParameter(default=100)
    angles = Parameter(fdef_name='create_angles')

    def get_path(self):
        try:
            return self.__path__
        except:
            raise ValueError('Path not set for {}'.format(
                self.__class__.__name__))

    def set_path(self, value):
        self.__path__ = np.asarray(value)

    path = FunctionParameter(get_path, set_path)

    def create_midpoint1(self):
        return self.path[0]

    def create_midpoint2(self):
        return self.path[-1]

    def create_width1(self):
        return self.width

    def create_width2(self):
        return self.width

    def create_orientation1(self):
        # return self.angles[0]*180/pi+90
        return self.angles[0] * 180 / pi + 90

    def create_orientation2(self):
        # return self.angles[-1]*180/pi-90
        return self.angles[-1] * 180 / pi - 90

    def create_angles(self):
        dxdy = self.path[1:] - self.path[:-1]
        angles = (np.arctan2(dxdy[:, 1], dxdy[:, 0])).tolist()
        angles = np.array([angles[0]] + angles + [angles[-1]])
        return angles

    def create_points(self, points):
        diff_angles = (self.angles[1:] - self.angles[:-1])
        mean_angles = (self.angles[1:] + self.angles[:-1]) / 2
        dx = self.width / 2 * np.cos((mean_angles - pi / 2)) / np.cos(
            (diff_angles / 2))
        dy = self.width / 2 * np.sin((mean_angles - pi / 2)) / np.cos(
            (diff_angles / 2))
        left_points = self.path.T - np.array([dx, dy])
        right_points = self.path.T + np.array([dx, dy])
        points = np.concatenate([left_points.T, right_points.T[::-1]])
        return points
Example #8
0
class RouteShape(shapes.Shape):

    path = Parameter()

    def create_points(self, points):
        if isinstance(self.path, gdspy.Path):
            points = clipping.boolean(subj=self.path.polygons,
                                      clip_type='or')[0]
        elif isinstance(self.path, gdspy.FlexPath):
            points = self.path.get_polygons()[0]
        return points
Example #9
0
class PCell(Cell):
    """  """

    pcell = BoolParameter(default=True)
    routes = ElementListParameter(
        doc='List of `Route` elements connected to the cell.')
    structures = ElementListParameter(
        doc='List of cell structures that coalesces the top-level cell.')
    extract_netlist = Parameter(fdef_name='create_extract_netlist')

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
Example #10
0
class EdgeAspects(__Aspects__):

    edges = Parameter(fdef_name='create_edges')

    def create_edges(self):
        """ Generate default edges for this polygon.
        These edges can be transformed using adapters. """
        from spira.yevon.geometry.edges.edges import generate_edges
        return generate_edges(shape=self.shape,
                              layer=self.layer,
                              internal_pid=self.id_string(),
                              transformation=self.transformation)
Example #11
0
class __RouteSimple__(shapes.Shape):
    """ Interface class for shaping route patterns. """

    m1 = Parameter(fdef_name='create_midpoint1')
    m2 = Parameter(fdef_name='create_midpoint2')

    w1 = Parameter(fdef_name='create_width1')
    w2 = Parameter(fdef_name='create_width2')

    o1 = Parameter(fdef_name='create_orientation1')
    o2 = Parameter(fdef_name='create_orientation2')

    def create_midpoint1(self):
        pass

    def create_midpoint2(self):
        pass

    def create_width1(self):
        pass

    def create_width2(self):
        pass

    def create_orientation1(self):
        pass

    def create_orientation2(self):
        pass
Example #12
0
class Cell(__Cell__):
    """ A Cell encapsulates a set of elements that
    describes the layout being generated. """

    _next_uid = 0

    lcar = NumberParameter(default=100)
    name = Parameter(fdef_name='create_name', doc='Name of the cell instance.')

    def get_alias(self):
        if not hasattr(self, '__alias__'):
            self.__alias__ = self.name.split('__')[0]
        return self.__alias__

    def set_alias(self, value):
        self.__alias__ = value

    alias = FunctionParameter(
        get_alias,
        set_alias,
        doc='Functions to generate an alias for cell name.')

    def __init__(self,
                 name=None,
                 elements=None,
                 ports=None,
                 nets=None,
                 library=None,
                 **kwargs):
        __Cell__.__init__(self, **kwargs)

        if name is not None:
            s = '{}_{}'.format(name, Cell._next_uid)
            self.__dict__['__name__'] = s
            Cell.name.__set__(self, s)

        self.uid = Cell._next_uid
        Cell._next_uid += 1

        if library is not None:
            self.library = library
        if elements is not None:
            self.elements = ElementList(elements)
        if ports is not None:
            self.ports = PortList(ports)

    def __repr__(self):
        class_string = "[SPiRA: Cell(\'{}\')] (elements {}, ports {})"
        return class_string.format(self.name, self.elements.__len__(),
                                   self.ports.__len__())

    def __str__(self):
        return self.__repr__()

    def create_name(self):
        if not hasattr(self, '__name__'):
            self.__name__ = self.__name_generator__(self)
        return self.__name__

    def expand_transform(self):
        for S in self.elements.sref:
            S.expand_transform()
            S.reference.expand_transform()
        return self

    def expand_flat_copy(self, exclude_devices=False):
        from spira.yevon.gdsii.pcell import Device
        from spira.yevon.gdsii.polygon import Polygon
        from spira.yevon.gdsii.sref import SRef
        from spira.core.transforms.translation import Translation

        name = ''
        S = self.expand_transform()
        C = Cell(name=S.name + '_ExpandedCell')

        def _traverse_polygons(subj, cell, name):
            c_name = deepcopy(name)
            # print(cell)
            for e in cell.elements:
                if isinstance(e, SRef):
                    if e.alias is not None:
                        c_name += e.alias + ':'
                    else:
                        c_name += ':'
                    subj = _traverse_polygons(subj=subj,
                                              cell=e.reference,
                                              name=c_name)
                    # if exclude_devices is True:
                    #     if isinstance(e.reference, Device):
                    #         subj += e
                    #     else:
                    #         subj = _traverse_polygons(subj=subj, cell=e.reference, name=c_name)
                    # else:
                    #     subj = _traverse_polygons(subj=subj, cell=e.reference, name=c_name)
                elif isinstance(e, Polygon):
                    e.location_name = c_name
                    # e.transform(expand_transform)
                    # e.shape.move(expand_transform)
                    subj += e
                    # print(e.transformation)
                c_name = name
            # print('')
            return subj

        D = _traverse_polygons(C, S, name)

        return D

    def move(self, midpoint=(0, 0), destination=None, axis=None):
        """  """
        from spira.yevon.geometry.ports.base import __Port__

        if destination is None:
            destination = midpoint
            midpoint = [0, 0]

        if issubclass(type(midpoint), __Port__):
            o = midpoint.midpoint
        elif isinstance(midpoint, Coord):
            o = midpoint
        elif np.array(midpoint).size == 2:
            o = midpoint
        elif midpoint in obj.ports:
            o = obj.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 isinstance(destination, Coord):
            d = destination
        elif np.array(destination).size == 2:
            d = destination
        elif destination in obj.ports:
            d = obj.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])

        d = Coord(d[0], d[1])
        o = Coord(o[0], o[1])

        for e in self.elements:
            e.move(midpoint=o, destination=d)

        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 stretch_p2p(self, port, destination):
        """
        The element by moving the subject port, without
        distorting the entire element. Note: The opposite
        port position is used as the stretching center.
        """
        from spira.core.transforms import stretching
        from spira.yevon.geometry import bbox_info
        from spira.yevon.gdsii.polygon import Polygon
        opposite_port = bbox_info.bbox_info_opposite_boundary_port(self, port)
        T = stretching.stretch_element_by_port(self, opposite_port, port,
                                               destination)
        if port.bbox is True:
            self = T(self)
        else:
            for i, e in enumerate(self.elements):
                if isinstance(e, Polygon):
                    if e.id_string() == port.local_pid:
                        self.elements[i] = T(e)
        return self

    def nets(self, lcar):
        return self.elements.nets(lcar=lcar)

    def create_netlist(self):
        net = self.nets(lcar=self.lcar).disjoint()
        return net
Example #13
0
class GmshGeometry(__Geometry__):
    """ Generate a geometry using the Gmsh library. """

    _ID = 0

    _uid = 0

    lcar = NumberParameter(default=100, doc='Mesh characteristic length.')
    algorithm = IntegerParameter(default=1, doc='Mesh algorithm used by Gmsh.')
    scale_Factor = NumberParameter(default=1e-6,
                                   doc='Mesh coord dimention scaling.')
    coherence_mesh = BoolParameter(defualt=True, doc='Merge similar points.')

    process = ProcessParameter()
    process_polygons = ElementListParameter()

    mesh_data = Parameter(fdef_name='create_mesh_data')

    def get_filename(self):
        if not hasattr(self, '__alias__'):
            self.__alias__ = '{}_{}'.format(self.process.symbol,
                                            GmshGeometry._uid)
            GmshGeometry._uid += 1
        return self.__alias__

    def set_filename(self, value):
        if value is not None:
            self.__alias__ = value

    filename = FunctionParameter(
        get_filename,
        set_filename,
        doc='Functions to generate an alias for cell name.')

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

        self.geom = pygmsh.opencascade.Geometry(
            characteristic_length_min=self.lcar,
            characteristic_length_max=self.lcar)
        self.geom.add_raw_code('Mesh.Algorithm = {};'.format(self.algorithm))
        self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(
            self.scale_Factor))
        if self.coherence_mesh is True:
            self.geom.add_raw_code('Coherence Mesh;')

    def __physical_surfaces__(self):
        """ Creates physical surfaces that is compatible
        with the GMSH library for mesh generation. """
        import re

        surfaces = []
        for i, ply in enumerate(self.process_polygons):
            shape = ply.shape.transform_copy(ply.transformation)
            layer = RDD.GDSII.EXPORT_LAYER_MAP[ply.layer]
            pts = [[p[0], p[1], 0] for p in shape.points]
            surface_label = '{}_{}_{}_{}'.format(layer.number, layer.datatype,
                                                 GmshGeometry._ID, i)
            gp = self.geom.add_polygon(pts,
                                       lcar=self.lcar,
                                       make_surface=True,
                                       holes=None)

            for j, ll in enumerate(gp.lines):
                pid = ply.shape.segment_labels[j].split(' - hash ')
                if len(pid) > 1:
                    line_label = "{}*{}*{}".format(pid[0], pid[1], j)
                else:
                    line_label = "{}*{}*{}".format(pid[0], None, j)
                self.geom.add_physical(ll, label=line_label)
            self.geom.add_physical(gp.surface, label=surface_label)
            # surfaces.append([gp.surface, gp.line_loop])

            surfaces.append(gp)
            GmshGeometry._ID += 1
        return surfaces

    def create_mesh_data(self):
        """ Generates the mesh data from the created physical surfaces. """

        # if len(self.physical_surfaces) > 1:
        #     self.geom.boolean_union(self.physical_surfaces)

        self.__physical_surfaces__()

        directory = os.getcwd() + '/debug/gmsh/'

        mesh_file = '{}{}.msh'.format(directory, self.filename)
        geo_file = '{}{}.geo'.format(directory, self.filename)
        vtk_file = '{}{}.vtu'.format(directory, self.filename)

        if not os.path.exists(directory):
            os.makedirs(directory)

        mesh_data = pygmsh.generate_mesh(self.geom,
                                         verbose=False,
                                         dim=2,
                                         prune_vertices=False,
                                         remove_faces=False,
                                         geo_filename=geo_file)

        # meshio.write(mesh_file, mesh_data)
        # meshio.write(vtk_file, mesh_data)

        return mesh_data
Example #14
0
class YtronShape(spira.Shape):
    """ Class for generating a yTron shape. """

    rho = NumberParameter(default=0.2,
                          doc='Angle of concave bend between the arms.')
    arm_lengths = CoordParameter(
        default=(5, 3), doc='Length or the left and right arms, respectively.')
    source_length = NumberParameter(default=5, doc='Length of the source arm.')
    arm_widths = CoordParameter(
        default=(2, 2), doc='Width of the left and right arms, respectively.')
    theta = NumberParameter(default=3, doc='Angle of the left and right arms.')
    theta_resolution = NumberParameter(default=10,
                                       doc='Smoothness of the concave bend.')

    xc = Parameter(fdef_name='create_xc')
    yc = Parameter(fdef_name='create_yc')
    arm_x_left = Parameter(fdef_name='create_arm_x_left')
    arm_y_left = Parameter(fdef_name='create_arm_y_left')
    arm_x_right = Parameter(fdef_name='create_arm_x_right')
    arm_y_right = Parameter(fdef_name='create_arm_y_right')
    rad_theta = Parameter(fdef_name='create_rad_theta')
    ml = Parameter(fdef_name='create_midpoint_left')
    mr = Parameter(fdef_name='create_midpoint_right')
    ms = Parameter(fdef_name='create_midpoint_source')

    def create_rad_theta(self):
        return self.theta * np.pi / 180

    def create_xc(self):
        return self.rho * np.cos(self.rad_theta)

    def create_yc(self):
        return self.rho * np.sin(self.rad_theta)

    def create_arm_x_left(self):
        return self.arm_lengths[0] * np.sin(self.rad_theta)

    def create_arm_y_left(self):
        return self.arm_lengths[0] * np.cos(self.rad_theta)

    def create_arm_x_right(self):
        return self.arm_lengths[1] * np.sin(self.rad_theta)

    def create_arm_y_right(self):
        return self.arm_lengths[1] * np.cos(self.rad_theta)

    def create_midpoint_left(self):
        xc = -(self.xc + self.arm_x_left + self.arm_widths[0] / 2)
        yc = self.yc + self.arm_y_left
        return [xc, yc]

    def create_midpoint_right(self):
        xc = self.xc + self.arm_x_right + self.arm_widths[1] / 2
        yc = self.yc + self.arm_y_right
        return [xc, yc]

    def create_midpoint_source(self):
        xc = (self.arm_widths[1] - self.arm_widths[0]) / 2
        yc = -self.source_length + self.yc
        return [xc, yc]

    def create_points(self, points):

        theta = self.theta * np.pi / 180
        theta_resolution = self.theta_resolution * np.pi / 180
        theta_norm = int((np.pi - 2 * theta) / theta_resolution) + 2
        thetalist = np.linspace(-(np.pi - theta), -theta, theta_norm)
        semicircle_x = self.rho * np.cos(thetalist)
        semicircle_y = self.rho * np.sin(thetalist) + self.rho

        xpts = semicircle_x.tolist() + [
            self.xc + self.arm_x_right,
            self.xc + self.arm_x_right + self.arm_widths[1],
            self.xc + self.arm_widths[1], self.xc + self.arm_widths[1], 0,
            -(self.xc + self.arm_widths[0]), -(self.xc + self.arm_widths[0]),
            -(self.xc + self.arm_x_left + self.arm_widths[0]),
            -(self.xc + self.arm_x_left)
        ]

        ypts = semicircle_y.tolist() + [
            self.yc + self.arm_y_right, self.yc + self.arm_y_right, self.yc,
            self.yc - self.source_length, self.yc - self.source_length,
            self.yc - self.source_length, self.yc, self.yc + self.arm_y_left,
            self.yc + self.arm_y_left
        ]

        points = np.array(list(zip(xpts, ypts)))

        return points
Example #15
0
class __Manhattan__(Cell):

    port1 = PortParameter(default=None)
    port2 = PortParameter(default=None)

    length = NumberParameter(default=20)
    layer = LayerParameter(number=13)
    # gds_layer = LayerParameter(number=13)
    # layer = PhysicalLayerParameter(default=RDD.DEF.PDEFAULT)
    # layer = PhysicalLayerParameter()
    # bend_type = StringParameter(default='rectangle')
    bend_type = StringParameter(default='circular')

    b1 = Parameter(fdef_name='create_arc_bend_1')
    b2 = Parameter(fdef_name='create_arc_bend_2')

    p1 = Parameter(fdef_name='create_port1_position')
    p2 = Parameter(fdef_name='create_port2_position')

    quadrant_one = Parameter(fdef_name='create_quadrant_one')
    quadrant_two = Parameter(fdef_name='create_quadrant_two')
    quadrant_three = Parameter(fdef_name='create_quadrant_three')
    quadrant_four = Parameter(fdef_name='create_quadrant_four')

    def get_radius(self):
        if self.port1 and self.port2:
            if hasattr(self, '__radius__'):
                return self.__radius__
            else:
                dx = abs(self.p2[0] - self.p1[0])
                dy = abs(self.p2[1] - self.p1[1])
                if dx <= dy:
                    self.__radius__ = dx * 0.2
                elif dy <= dx:
                    self.__radius__ = dy * 0.2
                # if dx <= dy:
                #     self.__radius__ = dx
                # elif dy <= dx:
                #     self.__radius__ = dy
                return self.__radius__

    def set_radius(self, value):
        self.__radius__ = value

    radius = FunctionParameter(get_radius, set_radius)

    def route_straight(self, p1, p2):
        route_shape = RouteSimple(port1=p1,
                                  port2=p2,
                                  path_type='straight',
                                  width_type='straight')
        # route_shape.apply_merge
        R1 = RouteGeneral(route_shape=route_shape, connect_layer=self.layer)
        S = spira.SRef(R1)
        S.connect(port=S.ports['P1'], destination=p1)
        # S.connect(port=p1, destination=p2)

        # T = spira.Rotation(rotation=p2.orientation, center=p1.midpoint)
        # S.transform(T)
        # r1.rotate(angle=p2.orientation+90, center=R1.port_input.midpoint)
        # r1._rotate(rotation=p2.orientation-90, center=R1.port_input.midpoint)
        # S.move(midpoint=(0,0), destination=p1.midpoint)
        return S

    def create_port1_position(self):
        angle = np.mod(self.port1.orientation, 360)
        if angle == 90:
            p1 = [self.port1.midpoint[0], self.port1.midpoint[1]]
        if angle == 180:
            p1 = [self.port1.midpoint[1], -self.port1.midpoint[0]]
        if angle == 270:
            p1 = [-self.port1.midpoint[0], -self.port1.midpoint[1]]
        if angle == 0:
            p1 = [-self.port1.midpoint[1], self.port1.midpoint[0]]
        return p1

    def create_port2_position(self):
        angle = np.mod(self.port1.orientation, 360)
        if angle == 90:
            p2 = [self.port2.midpoint[0], self.port2.midpoint[1]]
        if angle == 180:
            p2 = [self.port2.midpoint[1], -self.port2.midpoint[0]]
        if angle == 270:
            p2 = [-self.port2.midpoint[0], -self.port2.midpoint[1]]
        if angle == 0:
            p2 = [-self.port2.midpoint[1], self.port2.midpoint[0]]
        return p2

    def create_arc_bend_1(self):
        if self.bend_type == 'circular':
            rs = RouteArcShape(radius=self.radius,
                               width=self.port1.width,
                               start_angle=0,
                               theta=90)
        if self.bend_type == 'rectangle':
            rs = RouteSquareShape(width=self.port1.width,
                                  size=(self.radius, self.radius))
        B1 = RouteGeneral(route_shape=rs, connect_layer=self.layer)
        return spira.SRef(B1)

    def create_arc_bend_2(self):
        if self.bend_type == 'circular':
            rs = RouteArcShape(radius=self.radius,
                               width=self.port1.width,
                               start_angle=0,
                               theta=-90)
        if self.bend_type == 'rectangle':
            rs = RouteSquareShape(width=self.port1.width,
                                  size=(self.radius, self.radius))
        B1 = RouteGeneral(route_shape=rs, connect_layer=self.layer)
        return spira.SRef(B1)
Example #16
0
class RouteArcShape(__RouteSimple__):

    radius = NumberParameter(default=5)
    width = NumberParameter(default=1)
    theta = NumberParameter(default=45)
    start_angle = NumberParameter(default=0)
    angle_resolution = NumberParameter(default=15)
    angle1 = Parameter(fdef_name='create_angle1')
    angle2 = Parameter(fdef_name='create_angle2')

    def create_midpoint1(self):
        x = np.cos(self.angle1)
        y = np.sin(self.angle1)
        midpoint = [self.radius * x, self.radius * y]
        return midpoint

    def create_midpoint2(self):
        x = np.cos(self.angle2)
        y = np.sin(self.angle2)
        midpoint = [self.radius * x, self.radius * y]
        return midpoint

    def create_width1(self):
        return self.width

    def create_width2(self):
        return self.width

    def create_orientation1(self):
        # return self.start_angle - 180 + 180*(self.theta<0)
        return self.start_angle - 90 + 180 * (self.theta < 0)

    def create_orientation2(self):
        # return self.start_angle + self.theta + 0 - 180*(self.theta<0)
        return self.start_angle + self.theta + 90 - 180 * (self.theta < 0)

    def create_angle1(self):
        angle1 = (self.start_angle + 0) * constants.DEG2RAD
        return angle1

    def create_angle2(self):
        angle2 = (self.start_angle + self.theta + 0) * constants.DEG2RAD
        return angle2

    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
Example #17
0
class Polygon(__Polygon__):
    """
    Element that connects shapes to the GDSII file format.
    Polygon are objects that represents the shapes in a layout.

    Examples
    --------
    >>> layer = spira.Layer(number=99)
    >>> rect_shape = spira.RectangleShape(p1=[0,0], p2=[1,1])
    >>> ply = spira.Polygon(shape=rect_shape, layer=layer)
    """

    _next_uid = 0

    edges = Parameter(fdef_name='create_edges')

    def get_alias(self):
        if not hasattr(self, '__alias__'):
            self.__alias__ = self.process
        return self.__alias__

    def set_alias(self, value):
        if value is not None:
            self.__alias__ = value

    alias = FunctionParameter(
        get_alias,
        set_alias,
        doc='Functions to generate an alias for cell name.')

    def __init__(self, shape, layer, transformation=None, **kwargs):
        super().__init__(shape=shape,
                         layer=layer,
                         transformation=transformation,
                         **kwargs)

        self.uid = Polygon._next_uid
        Polygon._next_uid += 1

    def __repr__(self):
        if self is None:
            return 'Polygon is None!'
        layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer]
        class_string = "[SPiRA: Polygon \'{}\'] (center {}, vertices {}, process {}, purpose {})"
        return class_string.format(self.alias, self.center, self.count,
                                   self.process, self.purpose)

    def __str__(self):
        return self.__repr__()

    def __hash__(self):
        return hash(self.__repr__())

    # NOTE: We are not copying the ports, so they
    # can be re-calculated for the transformed shape.
    def __deepcopy__(self, memo):
        return self.__class__(shape=deepcopy(self.shape),
                              layer=deepcopy(self.layer),
                              transformation=deepcopy(self.transformation))

    def id_string(self):
        sid = '{} - hash {}'.format(self.__repr__(), self.shape.hash_string)
        return sid

    def create_edges(self):
        """ Generate default edges for this polygon.
        These edges can be transformed using adapters. """
        from spira.yevon.geometry.edges.edges import generate_edges
        return generate_edges(shape=self.shape, layer=self.layer)

    def flat_copy(self, level=-1):
        """ Flatten a copy of the polygon. """
        S = Polygon(shape=self.shape,
                    layer=self.layer,
                    transformation=self.transformation)
        S.expand_transform()
        return S

    def flatten(self, level=-1):
        """ Flatten the polygon without creating a copy. """
        return self.expand_transform()

    def nets(self, lcar):
        from spira.yevon.geometry.nets.net import Net
        from spira.yevon.vmodel.geometry import GmshGeometry
        from spira.yevon.geometry.ports.port import ContactPort
        from spira.yevon import filters

        if self.purpose == 'METAL':

            if RDD.ENGINE.GEOMETRY == 'GMSH_ENGINE':
                geometry = GmshGeometry(lcar=lcar,
                                        process=self.layer.process,
                                        process_polygons=[deepcopy(self)])

            cc = []
            for p in self.ports:
                if isinstance(p, ContactPort):
                    cc.append(p)

            F = filters.ToggledCompoundFilter()
            F += filters.NetProcessLabelFilter(
                process_polygons=[deepcopy(self)])
            F += filters.NetDeviceLabelFilter(device_ports=cc)
            F += filters.NetEdgeFilter(process_polygons=[deepcopy(self)])

            net = Net(name=self.process, geometry=geometry)

            return F(net)

            # # from spira.yevon.utils.netlist import combine_net_nodes
            # # net.g = combine_net_nodes(g=net.g, algorithm='d2d')
            # # net.g = combine_net_nodes(g=net.g, algorithm='s2s')

            # from spira.yevon.geometry.nets.net import CellNet
            # cn = CellNet()
            # cn.g = net.g
            # # cn.generate_branches()
            # # cn.detect_dummy_nodes()
            # return cn

        return []
Example #18
0
class YtronShape(Shape):
    """ Shape for generating a yTron device. """

    rho = NumberParameter(default=0.2)
    arm_lengths = CoordParameter(default=(5, 3))
    source_length = NumberParameter(default=5)
    arm_widths = CoordParameter(default=(2, 2))
    theta = NumberParameter(default=2.5)
    theta_resolution = NumberParameter(default=10.0)

    xc = Parameter(fdef_name='create_xc')
    yc = Parameter(fdef_name='create_yc')
    arm_x_left = Parameter(fdef_name='create_arm_x_left')
    arm_y_left = Parameter(fdef_name='create_arm_y_left')
    arm_x_right = Parameter(fdef_name='create_arm_x_right')
    arm_y_right = Parameter(fdef_name='create_arm_y_right')
    rad_theta = Parameter(fdef_name='create_rad_theta')

    def create_rad_theta(self):
        return self.theta * np.pi / 180

    def create_xc(self):
        return self.rho * np.cos(self.rad_theta)

    def create_yc(self):
        return self.rho * np.sin(self.rad_theta)

    def create_arm_x_left(self):
        return self.arm_lengths[0] * np.sin(self.rad_theta)

    def create_arm_y_left(self):
        return self.arm_lengths[0] * np.cos(self.rad_theta)

    def create_arm_x_right(self):
        return self.arm_lengths[1] * np.sin(self.rad_theta)

    def create_arm_y_right(self):
        return self.arm_lengths[1] * np.cos(self.rad_theta)

    def create_points(self, points):

        theta = self.theta * np.pi / 180
        theta_resolution = self.theta_resolution * np.pi / 180
        theta_norm = int((np.pi - 2 * theta) / theta_resolution) + 2
        thetalist = np.linspace(-(np.pi - theta), -theta, theta_norm)
        semicircle_x = self.rho * np.cos(thetalist)
        semicircle_y = self.rho * np.sin(thetalist) + self.rho

        xpts = semicircle_x.tolist() + [
            self.xc + self.arm_x_right,
            self.xc + self.arm_x_right + self.arm_widths[1],
            self.xc + self.arm_widths[1], self.xc + self.arm_widths[1], 0,
            -(self.xc + self.arm_widths[0]), -(self.xc + self.arm_widths[0]),
            -(self.xc + self.arm_x_left + self.arm_widths[0]),
            -(self.xc + self.arm_x_left)
        ]

        ypts = semicircle_y.tolist() + [
            self.yc + self.arm_y_right, self.yc + self.arm_y_right, self.yc,
            self.yc - self.source_length, self.yc - self.source_length,
            self.yc - self.source_length, self.yc, self.yc + self.arm_y_left,
            self.yc + self.arm_y_left
        ]

        points = np.array(list(zip(xpts, ypts)))

        return points
Example #19
0
class Net(__Net__):
    """
    Constructs a graph from the physical geometry
    generated from the list of elements.
    """

    # g = GraphParameter()
    g = Parameter()

    mesh_data = Parameter(fdef_name='create_mesh_data')
    geometry = GeometryParameter(allow_none=True, default=None)

    branch_nodes = Parameter(fdef_name='create_branch_nodes')

    lines = Parameter(fdef_name='create_lines')
    triangles = Parameter(fdef_name='create_triangles')
    physical_triangles = Parameter(fdef_name='create_physical_triangles')
    physical_lines = Parameter(fdef_name='create_physical_lines')

    name = StringParameter(default='no_name')

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if 'g' in kwargs:
            self.g = kwargs['g']
        else:
            self.g = nx.Graph()
            self._generate_mesh_graph()

    def __repr__(self):
        if self.geometry is None:
            class_string = "[SPiRA: Net] (name \'{}\', nodes {})"
            return class_string.format(self.name, self.count)
        else:
            class_string = "[SPiRA: Net] (name \'{}\', nodes {}, geometry {})"
            return class_string.format(self.name, self.count,
                                       self.geometry.process.symbol)

    def __str__(self):
        return self.__repr__()

    def _generate_mesh_graph(self):
        """ Create a graph from the meshed geometry. """
        ll = len(self.mesh_data.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, triangle):
        from spira import settings
        pp = self.mesh_data.points
        grids_per_unit = settings.get_grids_per_unit()
        n1, n2, n3 = pp[triangle[0]], pp[triangle[1]], pp[triangle[2]]
        x = (n1[0] + n2[0] + n3[0]) / 3
        y = (n1[1] + n2[1] + n3[1]) / 3
        x = x * grids_per_unit
        y = y * grids_per_unit
        self.g.node[n]['vertex'] = triangle
        self.g.node[n]['position'] = Coord(x, y)
        self.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.METAL]

    def create_mesh_data(self):
        return self.geometry.mesh_data

    def add_new_node(self, n, D, polygon, position, display):
        num = self.g.number_of_nodes()
        self.g.add_node(num + 1,
                        position=position,
                        device_reference=D,
                        process_polygon=polygon,
                        display=display)
        self.g.add_edge(n, num + 1)

    def create_triangles(self):
        if 'triangle' not in self.mesh_data.cells:
            raise ValueError('Triangle not found in cells')
        return self.mesh_data.cells['triangle']

    def create_lines(self):
        if 'line' not in self.mesh_data.cells:
            raise ValueError('Line not found in cells')
        return self.mesh_data.cells['line']

    def create_physical_triangles(self):
        if 'triangle' not in self.mesh_data.cell_data:
            raise ValueError('Triangle not in meshio cell_data')
        if 'gmsh:physical' not in self.mesh_data.cell_data['triangle']:
            raise ValueError('Physical not found in meshio triangle')
        return self.mesh_data.cell_data['triangle']['gmsh:physical'].tolist()

    def create_physical_lines(self):
        if 'line' not in self.mesh_data.cell_data:
            raise ValueError('Line not in meshio cell_data')
        if 'gmsh:physical' not in self.mesh_data.cell_data['line']:
            raise ValueError('Physical not found in meshio triangle')
        return self.mesh_data.cell_data['line']['gmsh:physical'].tolist()

    def process_triangles(self):
        """
        Arguments
        ---------
        tri : list
            The surface_id of the triangle
            corresponding to the index value.
        name -> 5_0_1 (layer_datatype_polyid)
        value -> [1 2] (1=surface_id 2=triangle)
        """

        triangles = {}
        for name, value in self.mesh_data.field_data.items():
            for n in self.g.nodes():
                surface_id = value[0]
                if self.physical_triangles[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 process_lines(self):
        """
        Arguments
        ---------
        tri : list
            The surface_id of the triangle
            corresponding to the index value.
        name -> 5_0_1 (layer_datatype_polyid)
        value -> [1 2] (1=surface_id 2=triangle)
        """

        lines = {}
        for name, value in self.mesh_data.field_data.items():
            # print(name, value)
            # print(self.physical_lines)
            for n in self.physical_lines:
                line_id = value[0]
                if n == line_id:
                    # print(name)
                    # print(value)
                    # print('')
                    polygon_string = name.split('*')[0]
                    polygon_hash = name.split('*')[1]
                    polygon_uid = int(name.split('*')[2])
                    key = (polygon_string, polygon_hash, polygon_uid)
                    if key in lines:
                        lines[key].append(n)
                    else:
                        lines[key] = [n]
        return lines

    def get_triangles_connected_to_line(self):
        """
        Labeling of an edge line:
        polygon_uid_i [line elm_type]
        [SPiRA: Polygon 'M5']_17_0 [2 1]

        Labeling of triangle:
        layer datatype [triangle elm_type]
        50_1_0_0 [1 2]
        """

        # lines = []
        # for v in self.process_lines().values():
        #     lines.extend(v)
        # print(lines)
        # triangles = {}
        # for n in nodes:
        #     for node, triangle in enumerate(self.triangles):
        #         if n == node:
        #             triangles[n] = triangle
        # return triangles

    def triangle_nodes(self):
        """ Get triangle field_data in list form. """
        nodes = []
        for v in self.process_triangles().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 transform(self, transformation):
        for n in self.g.nodes():
            self.g.node[n]['position'] = transformation.apply_to_coord(
                self.g.node[n]['position'])
        return self

    def create_branch_nodes(self):
        """ Nodes that defines different conducting branches. """
        from spira.yevon.gdsii.sref import SRef
        from spira.yevon.geometry.ports import Port
        branch_nodes = list()
        for n in self.g.nodes():
            if 'device_reference' in self.g.node[n]:
                D = self.g.node[n]['device_reference']
                if isinstance(D, SRef):
                    branch_nodes.append(n)
                if isinstance(D, Port):
                    branch_nodes.append(n)
        return branch_nodes

    def st_nodes(self):
        """ Nodes that defines different conducting branches.
        All nodes are ports. Chek port purposes.
        """
        from spira.yevon.gdsii.sref import SRef
        from spira.yevon.geometry.ports import Port
        branch_nodes = list()
        for n in self.g.nodes():
            if 'device_reference' in self.g.node[n]:
                D = self.g.node[n]['device_reference']
                P = self.g.node[n]['process_polygon']
                # FIXME: Maybe implement node operators (__and__, etc)
                # if (D.purpose.symbol == 'B') and (P.layer.purpose.symbol == 'DEVICE_METAL'):
                #     branch_nodes.append(n)
                if D.purpose.symbol == 'C':
                    branch_nodes.append(n)
                elif D.purpose.symbol == 'D':
                    branch_nodes.append(n)
                # elif D.purpose.symbol == 'P':
                #     branch_nodes.append(n)
                elif D.purpose.symbol == 'T':
                    branch_nodes.append(n)
                # elif (D.purpose.symbol == 'P') and (D.name[1] != 'E'):
                #     branch_nodes.append(n)
        return branch_nodes

    def convert_to_branch_node(self, n, uid):
        pass

    def del_branch_attrs(self):
        """ Reset the branch attrs for new branch node creation. """
        for n in self.g.nodes():
            if 'branch_node' in self.g.node[n]:
                del self.g.node[n]['branch_node']
        return self

    def convert_pins(self):
        """ Remove pin node attrs with more than 1 edge connected to it. """
        for n in self.g.nodes():
            if 'device_reference' in self.g.node[n]:
                D = self.g.node[n]['device_reference']
                if D.purpose.symbol == 'P':
                    if len(self.g.edges(n)) > 0:
                        del self.g.node[n]['device_reference']
        return self

    def convert_device(self):
        """ Convert a device metal node to a dummy port.
        Has to be connected to atleast 1 PEdge node. """

        from spira.yevon.geometry.ports import Port

        for n in self.g.nodes():
            convert = False

            P = self.g.node[n]['process_polygon']

            if P.layer.purpose.symbol == 'DEVICE_METAL':
                for i in self.g.neighbors(n):
                    if 'device_reference' in self.g.node[i]:
                        D = self.g.node[i]['device_reference']
                        # print(D)
                        if D.purpose.symbol == 'P':
                            convert = True

            if convert is True:
                port = Port(
                    name='Djj{}'.format(n),
                    midpoint=P.center,
                    process=P.layer.process,
                )
                self.g.node[n]['device_reference'] = port
        return self

    def remove_nodes(self):
        """
        Nodes to be removed:
        1. Are not a branch node.
        2. Are not a device node.
        3. Branch nodes must equal the branch id.
        """
        from spira.yevon.gdsii.sref import SRef
        from spira.yevon.geometry.ports import Port

        locked_nodes = []
        remove_nodes = []
        for n in self.g.nodes():
            if 'branch_node' in self.g.node[n]:
                D = self.g.node[n]['branch_node']
                if isinstance(D, Port):
                    locked_nodes.append(n)
            elif 'device_reference' in self.g.node[n]:
                D = self.g.node[n]['device_reference']
                if isinstance(D, (Port, SRef)):
                    locked_nodes.append(n)
        for n in self.g.nodes():
            if n not in locked_nodes:
                remove_nodes.append(n)
        self.g.remove_nodes_from(remove_nodes)
Example #20
0
class ElectricalConnections(__Connection__):
    """

    """

    edges = Parameter(fdef_name='create_edges')

    def create_elements(self, elems):
        overlap_elems, edges = self.edges
        # for p in self.cell.elements:
        for i, p in enumerate(self.cell.elements):

            # shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid()
            # cs = ShapeConnected(original_shape=shape, edges=edges)
            # elems += Polygon(shape=cs, layer=p.layer)

            # cs = ShapeConnected(original_shape=shape, edges=edges)
            # # p.shape = cs
            # self.cell.elements[i].shape = cs

            if i == 2:
                shape = deepcopy(p.shape).transform(
                    p.transformation).snap_to_grid()
                # shape = p.shape
                # print(shape.points)
                cs = ShapeConnected(original_shape=shape, edges=edges)
                # print(cs.points)
                # self.cell.elements[i].shape = cs
                p.shape = cs
                # elems += Polygon(shape=cs, layer=p.layer)
                # elems += p

        return elems

    def create_edges(self):
        el = ElementList()
        for p1 in deepcopy(self.cell.elements):
            el += p1
            for edge in p1.edges:
                el += edge.outside.transform(edge.transformation)

        map1 = {
            RDD.PLAYER.M5.EDGE_CONNECTED: RDD.PLAYER.M5.INSIDE_EDGE_ENABLED
        }

        pg_overlap = self.cell.overlap_elements
        edges = get_derived_elements(el, mapping=map1, store_as_edge=True)

        for j, pg in enumerate(pg_overlap):
            for e in pg.elements:
                for i, edge in enumerate(edges):
                    if edge.overlaps(e):
                        # FIXME: Cannot use this, since Gmsh seems to crach due to the hashing string.
                        # edges[i].pid = p.id_string()
                        # edges[i].pid = '{}_{}'.format(p.__repr__(), p.uid)
                        edges[i].pid = '{}'.format(e.shape.hash_string)

        return pg_overlap, edges

    def gdsii_output_electrical_connection(self):

        elems = ElementList()
        overlap_elems, edges = self.edges
        for e in overlap_elems:
            elems += e
        for edge in edges:
            elems += edge.outside
        for e in self.cell.elements:
            elems += e

        D = Cell(name='_ELECTRICAL_CONNECT', elements=elems)
        D.gdsii_output()
Example #21
0
class RouteParallel(__Manhattan__):

    parallel = Parameter(fdef_name='create_parallel_route')
    quadrant_one_parallel = Parameter(fdef_name='create_quadrant_one_parallel')
    q1 = Parameter(fdef_name='create_q1_180')
    q2 = Parameter(fdef_name='create_q2_180')
    q3 = Parameter(fdef_name='create_q3_180')
    q4 = Parameter(fdef_name='create_q4_180')

    def create_parallel_route(self):

        p1, p2 = self.p1, self.p2
        b1, b2 = self.b2, self.b1

        dx = max(p1[0], p2[0])
        dy = max(p1[1], p2[1])

        if p2[0] > p1[0]:
            b1, b2 = self.b1, self.b2
        h = p2[1] + self.length
        d1 = [0, h]
        d2 = [self.ports['T2'].midpoint[0], h]

        b1.connect(port=b1.ports['P2'], destination=self.ports['T1'])
        b1.move(midpoint=b1.ports['P2'], destination=d1)

        b2.connect(port=b2.ports['P2'], destination=b1.ports['P1'])
        b2.move(midpoint=b2.ports['P1'], destination=d2)

        r1 = self.route_straight(b1.ports['P2'], self.ports['T1'])
        r2 = self.route_straight(b2.ports['P1'], self.ports['T2'])
        r3 = self.route_straight(b1.ports['P1'], b2.ports['P2'])

        D = spira.Cell(name='Parallel')
        D += [self.b1, self.b2, r1, r2, r3]

        t1 = self.ports['T1']
        t2 = self.ports['T2']

        t1.rotate(angle=self.port1.orientation)
        t2.rotate(angle=self.port1.orientation)

        D.rotate(angle=self.port1.orientation, center=self.p1)
        D.move(midpoint=self.ports['T1'], destination=self.port1)

        return spira.SRef(D)

    def create_quadrant_one_parallel(self):

        p1 = [self.port1.midpoint[0], self.port1.midpoint[1]]
        p2 = [self.port2.midpoint[0], self.port2.midpoint[1]]

        b1, b2 = self.b2, self.b1
        d1, d2 = [0, 0], [0, 0]

        if self.port1.orientation == 0:
            dy = max(p1[1], p2[1])
            if p2[0] > p1[0]:
                b1, b2 = self.b1, self.b2
            h = dy + self.length
            d1 = [0, h]
            d2 = [self.ports['T2'].midpoint[0], h]
        elif self.port1.orientation == 90:
            dx = max(p1[0], p2[0])
            if p2[1] > p1[1]:
                b1, b2 = self.b1, self.b2
            h = dx - self.length
            d1 = [h, 0]
            d2 = [h, self.ports['T2'].midpoint[1]]
        elif self.port1.orientation == -90:
            dx = min(p1[0], p2[0])
            if p1[1] > p2[1]:
                b1, b2 = self.b1, self.b2
            h = dx + self.length
            d1 = [h, 0]
            d2 = [h, self.ports['T2'].midpoint[1]]
        elif self.port1.orientation == 180:
            dy = min(p1[1], p2[1])
            if p1[0] > p2[0]:
                b1, b2 = self.b1, self.b2
            elif p2[0] > p1[0]:
                b1, b2 = self.b2, self.b1
            h = dy - self.length
            d1 = [0, h]
            d2 = [self.ports['T2'].midpoint[0], h]

        b1.connect(port=b1.ports['P2'], destination=self.ports['T1'])
        b1.move(midpoint=b1.ports['P2'], destination=d1)

        b2.connect(port=b2.ports['P1'], destination=b1.ports['P1'])
        b2.move(midpoint=b2.ports['P2'], destination=d2)

        r1 = self.route_straight(b1.ports['P2'], self.ports['T1'])
        r2 = self.route_straight(b2.ports['P2'], self.ports['T2'])
        r3 = self.route_straight(b1.ports['P1'], b2.ports['P1'])

        return [self.b1, self.b2, r1, r2, r3]

    def create_q1_180(self):

        b1 = self.b1
        b2 = self.b2

        b1.connect(port=b1.ports['P2'], destination=self.ports['T1'])
        h = self.p2[1] + self.radius + self.length
        b1.move(midpoint=b1.ports['P2'], destination=[0, h])

        b2.connect(port=b2.ports['P1'], destination=b1.ports['P1'])
        b2.move(midpoint=b2.ports['P2'],
                destination=[self.ports['T2'].midpoint[0], h])

        r1 = self.route_straight(b1.ports['P2'], self.ports['T1'])
        r2 = self.route_straight(b2.ports['P2'], self.ports['T2'])
        r3 = self.route_straight(b1.ports['P1'], b2.ports['P1'])

        D = spira.Cell(name='SameQ1')
        D += [self.b1, self.b2, r1, r2, r3]

        D += self.ports['T1']
        D += self.ports['T2']

        D.rotate(angle=self.port1.orientation, center=self.p1)
        D.move(midpoint=self.ports['T1'], destination=self.port1)

        return spira.SRef(D)

    def create_q2_180(self):

        b1 = self.b1
        b2 = self.b2

        b1.connect(port=b1.ports['P1'], destination=self.ports['T2'])
        h = self.p2[1] + self.radius + self.length
        b1.move(midpoint=b1.ports['P1'], destination=[0, h])

        b2.connect(port=b2.ports['P2'], destination=b1.ports['P2'])
        b2.move(midpoint=b2.ports['P1'],
                destination=[self.ports['T2'].midpoint[0], h])

        r1 = self.route_straight(b1.ports['P1'], self.ports['T1'])
        r2 = self.route_straight(b2.ports['P1'], self.ports['T2'])
        r3 = self.route_straight(b1.ports['P2'], b2.ports['P2'])

        D = spira.Cell(name='SameQ2')
        D += [self.b1, self.b2, r1, r2, r3]

        D += self.ports['T1']
        D += self.ports['T2']

        D.rotate(angle=self.port1.orientation, center=self.p1)
        D.move(midpoint=self.ports['T1'], destination=self.port1)

        return spira.SRef(D)

    def create_q3_180(self):

        b1 = self.b1
        b2 = self.b2

        b1.connect(port=b1.ports['P1'], destination=self.ports['T2'])
        h = self.p1[1] + self.radius + self.length
        b1.move(midpoint=b1.ports['P1'], destination=[0, h])

        b2.connect(port=b2.ports['P2'], destination=b1.ports['P2'])
        b2.move(midpoint=b2.ports['P1'],
                destination=[self.ports['T2'].midpoint[0], h])

        r1 = self.route_straight(b1.ports['P1'], self.ports['T1'])
        r2 = self.route_straight(b2.ports['P1'], self.ports['T2'])
        r3 = self.route_straight(b1.ports['P2'], b2.ports['P2'])

        D = spira.Cell(name='SameQ3')
        D += [self.b1, self.b2, r1, r2, r3]

        D += self.ports['T1']
        D += self.ports['T2']

        D.rotate(angle=self.port1.orientation, center=self.p1)
        D.move(midpoint=self.ports['T1'], destination=self.port1)

        return spira.SRef(D)

    def create_q4_180(self):

        b1 = self.b1
        b2 = self.b2

        b2.connect(port=b2.ports['P1'], destination=self.ports['T1'])
        h = self.p1[1] + self.radius + self.length
        b2.move(midpoint=b2.ports['P1'], destination=[0, h])

        b1.connect(port=b1.ports['P2'], destination=b2.ports['P2'])
        b1.move(midpoint=b1.ports['P1'],
                destination=[self.ports['T2'].midpoint[0], h])

        r1 = self.route_straight(b2.ports['P1'], self.ports['T1'])
        r2 = self.route_straight(b1.ports['P1'], self.ports['T2'])
        r3 = self.route_straight(b1.ports['P2'], b2.ports['P2'])

        D = spira.Cell(name='SameQ4')
        D += [self.b1, self.b2, r1, r2, r3]

        D += self.ports['T1']
        D += self.ports['T2']

        D.rotate(angle=self.port1.orientation, center=self.p1)
        D.move(midpoint=self.ports['T1'], destination=self.port1)

        return spira.SRef(D)
Example #22
0
class Net(__Net__):
    """
    Constructs a graph from the physical geometry
    generated from the list of elements.
    """

    # g = GraphParameter()
    g = Parameter()

    mesh_data = Parameter(fdef_name='create_mesh_data')
    geometry = GeometryParameter(allow_none=True, default=None)

    lines = Parameter(fdef_name='create_lines')
    triangles = Parameter(fdef_name='create_triangles')
    physical_triangles = Parameter(fdef_name='create_physical_triangles')
    physical_lines = Parameter(fdef_name='create_physical_lines')

    name = StringParameter(default='no_name')

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if 'g' in kwargs:
            self.g = kwargs['g']
        else:
            self.g = nx.Graph()
            self._generate_mesh_graph()

    def __repr__(self):
        if self.geometry is None:
            class_string = "[SPiRA: Net] (name \'{}\', nodes {})"
            return class_string.format(self.name, self.count)
        else:
            class_string = "[SPiRA: Net] (name \'{}\', nodes {}, geometry {})"
            return class_string.format(self.name, self.count,
                                       self.geometry.process.symbol)

    def __str__(self):
        return self.__repr__()

    def _generate_mesh_graph(self):
        """ Create a graph from the meshed geometry. """
        ll = len(self.mesh_data.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, triangle):
        from spira import settings
        pp = self.mesh_data.points
        grids_per_unit = settings.get_grids_per_unit()
        n1, n2, n3 = pp[triangle[0]], pp[triangle[1]], pp[triangle[2]]
        x = (n1[0] + n2[0] + n3[0]) / 3
        y = (n1[1] + n2[1] + n3[1]) / 3
        x = x * grids_per_unit
        y = y * grids_per_unit
        self.g.node[n]['vertex'] = triangle
        self.g.node[n]['position'] = Coord(x, y)
        self.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.METAL]

    def create_mesh_data(self):
        return self.geometry.mesh_data

    def add_new_node(self, n, D, polygon, position, display):
        num = self.g.number_of_nodes()
        self.g.add_node(num + 1,
                        position=position,
                        device_reference=D,
                        process_polygon=polygon,
                        display=display)
        self.g.add_edge(n, num + 1)

    def create_triangles(self):
        if 'triangle' not in self.mesh_data.cells:
            raise ValueError('Triangle not found in cells')
        return self.mesh_data.cells['triangle']

    def create_lines(self):
        if 'line' not in self.mesh_data.cells:
            raise ValueError('Line not found in cells')
        return self.mesh_data.cells['line']

    def create_physical_triangles(self):
        if 'triangle' not in self.mesh_data.cell_data:
            raise ValueError('Triangle not in meshio cell_data')
        if 'gmsh:physical' not in self.mesh_data.cell_data['triangle']:
            raise ValueError('Physical not found in meshio triangle')
        return self.mesh_data.cell_data['triangle']['gmsh:physical'].tolist()

    def create_physical_lines(self):
        if 'line' not in self.mesh_data.cell_data:
            raise ValueError('Line not in meshio cell_data')
        if 'gmsh:physical' not in self.mesh_data.cell_data['line']:
            raise ValueError('Physical not found in meshio triangle')
        return self.mesh_data.cell_data['line']['gmsh:physical'].tolist()

    def process_triangles(self):
        """
        Arguments
        ---------
        tri : list
            The surface_id of the triangle
            corresponding to the index value.
        name -> 5_0_1 (layer_datatype_polyid)
        value -> [1 2] (1=surface_id 2=triangle)
        """

        triangles = {}
        for name, value in self.mesh_data.field_data.items():
            for n in self.g.nodes():
                surface_id = value[0]
                if self.physical_triangles[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 process_lines(self):
        """
        Arguments
        ---------
        tri : list
            The surface_id of the triangle
            corresponding to the index value.
        name -> 5_0_1 (layer_datatype_polyid)
        value -> [1 2] (1=surface_id 2=triangle)
        """

        lines = {}
        for name, value in self.mesh_data.field_data.items():
            # print(name, value)
            # print(self.physical_lines)
            for n in self.physical_lines:
                line_id = value[0]
                if n == line_id:
                    # print(name)
                    # print(value)
                    # print('')
                    polygon_string = name.split('_')[0]
                    polygon_uid = int(name.split('_')[1])
                    key = (polygon_string, polygon_uid)
                    if key in lines:
                        lines[key].append(n)
                    else:
                        lines[key] = [n]
        return lines

    def get_triangles_connected_to_line(self):
        """
        Labeling of an edge line:
        polygon_uid_i [line elm_type]
        [SPiRA: Polygon 'M5']_17_0 [2 1]

        Labeling of triangle:
        layer datatype [triangle elm_type]
        50_1_0_0 [1 2]
        """

        # lines = []
        # for v in self.process_lines().values():
        #     lines.extend(v)
        # print(lines)
        # triangles = {}
        # for n in nodes:
        #     for node, triangle in enumerate(self.triangles):
        #         if n == node:
        #             triangles[n] = triangle
        # return triangles

    def triangle_nodes(self):
        """ Get triangle field_data in list form. """
        nodes = []
        for v in self.process_triangles().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 transform(self, transformation):
        for n in self.g.nodes():
            self.g.node[n]['position'] = transformation.apply_to_coord(
                self.g.node[n]['position'])
        return self
Example #23
0
class VModelProcessFlow(ParameterInitializer):
    """  """

    active_processes = Parameter(
        doc='Active process layers for virtual model creation.')