Ejemplo n.º 1
0
def add_electrical_pads_top(component: Component, **kwargs) -> Component:
    """connects component electrical ports with pad array at the top

    Args:
        component:
        pad: pad element
        spacing: pad array (x, y) spacing
        width: pad width
        height: pad height
        layer: pad layer
    """
    c = Component(f"{component.name}_e")
    ports = component.get_ports_list(port_type="dc")
    c << component
    pads = c << pad_array(n=len(ports), port_list=["S"], **kwargs)
    pads.x = component.x
    pads.y = component.ymax + 100
    ports_pads = list(pads.ports.values())
    for p1, p2 in zip(ports_pads, ports):
        c.add(connect_electrical_shortest_path(p1, p2))

    c.ports = component.ports
    for port in ports:
        c.ports.pop(port.name)
    return c
Ejemplo n.º 2
0
def add_labels(
    component: Component,
    port_type: str = "dc",
    get_label_function: Callable = get_input_label_electrical,
    layer_label: Layer = pp.LAYER.LABEL,
    gc: Optional[Component] = None,
) -> Component:
    """Add labels a particular type of ports

    Args:
        component: to add labels to
        port_type: type of port ('dc', 'optical', 'electrical')
        get_label_function: function to get label
        layer_label: layer_label

    Returns:
        original component with labels

    """
    ports = component.get_ports_list(port_type=port_type)

    for i, port in enumerate(ports):
        label = get_label_function(
            port=port,
            gc=gc,
            gc_index=i,
            component_name=component.name,
            layer_label=layer_label,
        )
        component.add(label)

    return component
def add_electrical_pads_top(
    component: Component,
    component_top_to_pad_bottom_distance: float = 100.0,
    route_filter=connect_elec_waypoints,
    **kwargs,
) -> Component:
    """connects component electrical ports with pad array at the top

    Args:
        component:
        pad: pad element
        spacing: pad array (x, y) spacing
        width: pad width
        height: pad height
        layer: pad layer
    """
    c = Component(f"{component.name}_e")
    ports = component.get_ports_list(port_type="dc")
    # for port in ports:
    #     print(port.name)
    # print(len(ports))
    c << component
    pads = c << pad_array(n=len(ports), port_list=["S"], **kwargs)
    pads.x = component.x
    pads.ymin = component.ymax + component_top_to_pad_bottom_distance

    ports_pads = list(pads.ports.values())

    ports_pads.sort(key=lambda p: p.x)
    ports.sort(key=lambda p: p.x)

    for p1, p2 in zip(ports_pads, ports):
        c.add(connect_electrical_shortest_path(p1, p2))

    c.ports = component.ports.copy()
    for port in ports:
        c.ports.pop(port.name)
    return c
def add_fiber_single(
    component: Component,
    grating_coupler: Callable = grating_coupler_te,
    layer_label: Tuple[int, int] = LAYER.LABEL,
    optical_io_spacing: int = 50,
    bend_factory: Callable = bend_circular,
    straight_factory: Callable = waveguide,
    taper_factory: Callable = taper,
    taper_length: float = 10.0,
    route_filter: Callable = connect_strip_way_points,
    min_input2output_spacing: int = 127,
    optical_routing_type: int = 2,
    with_align_ports: bool = True,
    component_name: Optional[str] = None,
    gc_port_name: str = "W0",
    **kwargs,
) -> Component:
    r"""returns component with grating ports and labels on each port
    can add align_ports reference structure

    Args:
        component: to connect
        grating_coupler: grating coupler instance, function or list of functions
        layer_label: LAYER.LABEL
        optical_io_spacing: SPACING_GC
        bend_factory: bend_circular
        straight_factory: waveguide
        fanout_length: None  # if None, automatic calculation of fanout length
        max_y0_optical: None
        with_align_ports: True, adds loopback structures
        waveguide_separation: 4.0
        bend_radius: BEND_RADIUS
        list_port_labels: None, adds TM labels to port indices in this list
        connected_port_list_ids: None # only for type 0 optical routing
        nb_optical_ports_lines: 1
        force_manhattan: False
        excluded_ports:
        grating_indices: None
        routing_method: connect_strip
        gc_port_name: W0
        optical_routing_type: None: autoselection, 0: no extension
        gc_rotation: -90
        component_name: name of component
        taper_factory: taper

    .. code::

              fiber
             ______
            /| | |
           / | | |
        W0|  | | |
           \ | | |
          | \|_|_|_

          |
         xmin = 0

    .. plot::
      :include-source:

       import pp
       from pp.routing import add_fiber_array

       c = pp.c.crossing()
       cc = add_fiber_array(c)
       pp.plotgds(cc)

    """
    component = component() if callable(component) else component
    gc = grating_coupler = (grating_coupler()
                            if callable(grating_coupler) else grating_coupler)
    gc_port_to_edge = abs(gc.xmax - gc.ports[gc_port_name].midpoint[0])
    port_width_gc = grating_coupler.ports[gc_port_name].width
    optical_ports = component.get_ports_list(port_type="optical")
    port_width_component = optical_ports[0].width

    if port_width_component != port_width_gc:
        component = add_tapers(
            component,
            taper_factory(length=taper_length,
                          width1=port_width_gc,
                          width2=port_width_component),
        )

    component_name = component_name or component.name
    name = f"{component_name}_{grating_coupler.name}"

    elements, grating_couplers = route_fiber_single(
        component,
        component_name=component_name,
        optical_io_spacing=optical_io_spacing,
        bend_factory=bend_factory,
        straight_factory=straight_factory,
        route_filter=route_filter,
        grating_coupler=grating_coupler,
        layer_label=layer_label,
        optical_routing_type=optical_routing_type,
        min_input2output_spacing=min_input2output_spacing,
        gc_port_name=gc_port_name,
        **kwargs,
    )

    c = Component(name=name)
    cr = c << component
    cr.rotate(90)

    for e in elements:
        c.add(e)
    for gc in grating_couplers:
        c.add(gc)

    for pname, p in component.ports.items():
        if p.port_type != "optical":
            c.add_port(pname, port=p)

    if isinstance(grating_coupler, list):
        grating_couplers = [call_if_func(g) for g in grating_coupler]
        grating_coupler = grating_couplers[0]
    else:
        grating_coupler = call_if_func(grating_coupler)
        grating_couplers = [grating_coupler]

    if with_align_ports:
        length = c.ysize - 2 * gc_port_to_edge
        wg = c << straight_factory(length=length)
        wg.rotate(90)
        wg.xmax = (c.xmin - optical_io_spacing
                   if abs(c.xmin) > abs(optical_io_spacing) else c.xmin -
                   optical_io_spacing)
        wg.ymin = c.ymin + gc_port_to_edge

        gci = c << grating_coupler
        gco = c << grating_coupler
        gci.connect(gc_port_name, wg.ports["W0"])
        gco.connect(gc_port_name, wg.ports["E0"])

        gds_layer_label, gds_datatype_label = pd._parse_layer(layer_label)

        port = wg.ports["E0"]
        text = get_optical_text(port,
                                grating_coupler,
                                0,
                                component_name=f"loopback_{component.name}")

        label = pd.Label(
            text=text,
            position=port.midpoint,
            anchor="o",
            layer=gds_layer_label,
            texttype=gds_datatype_label,
        )
        c.add(label)

        port = wg.ports["W0"]
        text = get_optical_text(port,
                                grating_coupler,
                                1,
                                component_name=f"loopback_{component.name}")
        label = pd.Label(
            text=text,
            position=port.midpoint,
            anchor="o",
            layer=gds_layer_label,
            texttype=gds_datatype_label,
        )
        c.add(label)

    return c
Ejemplo n.º 5
0
def route_fiber_single(
    component: Component,
    optical_io_spacing: int = 50,
    grating_coupler: Component = grating_coupler_te,
    min_input2output_spacing: int = 230,
    optical_routing_type: int = 1,
    optical_port_labels: Optional[List[str]] = None,
    excluded_ports: Optional[List[str]] = None,
    **kwargs
) -> Tuple[List[Union[ComponentReference, Label]], List[ComponentReference]]:
    """Returns routes with grating couplers for single fiber input/output.

    Args:
        component: to add grating couplers
        optical_io_spacing: between grating couplers
        grating_coupler:
        min_input2output_spacing: so opposite fibers do not touch
        optical_routing_type: 0, 1, 2
        optical_port_labels: port labels that need connection
        excluded_ports: ports excluded from routing

    Returns:
        elements: list of routes ComponentReference
        grating_couplers: list of grating_couplers ComponentReferences

    """
    component = component.copy()
    component_copy = component.copy()

    if optical_port_labels is None:
        optical_ports = component.get_ports_list(port_type="optical")
    else:
        optical_ports = [component.ports[lbl] for lbl in optical_port_labels]

    excluded_ports = excluded_ports or []
    optical_ports = [p for p in optical_ports if p.name not in excluded_ports]
    N = len(optical_ports)

    if isinstance(grating_coupler, list):
        grating_couplers = [pp.call_if_func(g) for g in grating_coupler]
        grating_coupler = grating_couplers[0]
    else:
        grating_coupler = pp.call_if_func(grating_coupler)
        grating_couplers = [grating_coupler] * N

    gc_port2center = getattr(grating_coupler, "port2center",
                             grating_coupler.xsize / 2)
    if component.xsize + 2 * gc_port2center < min_input2output_spacing:
        fanout_length = (pp.drc.snap_to_grid(
            min_input2output_spacing - component.xsize - 2 * gc_port2center,
            10) / 2)
    else:
        fanout_length = None
    """
         _________
        |         |_E1
     W0_|         |
        |         |_E0
        |_________|

    rotate +90 deg and route West ports to South

          E1  E0
         _|___|_
        |       |
        |       |
        |       |
        |       |
        |       |
        |       |
        |_______|
            |
            W0

    """
    # route west ports to south
    component = component.rotate(90)
    west_ports = component.get_ports_dict(prefix="W")
    north_ports = {
        p.name: p
        for p in component.ports.values() if not p.name.startswith("W")
    }
    component.ports = west_ports

    elements_south, gratings_south, _ = route_fiber_array(
        component=component,
        with_align_ports=False,
        optical_io_spacing=optical_io_spacing,
        fanout_length=fanout_length,
        grating_coupler=grating_couplers[0],
        optical_routing_type=optical_routing_type,
        **kwargs)

    # route north ports
    component = component_copy.rotate(-90)
    north_ports = {
        p.name: p
        for p in component.ports.values() if not p.name.startswith("W")
    }
    component.ports = north_ports

    elements_north, gratings_north, _ = route_fiber_array(
        component=component,
        with_align_ports=False,
        optical_io_spacing=optical_io_spacing,
        fanout_length=fanout_length,
        grating_coupler=grating_couplers[1:],
        **kwargs)
    for e in elements_north:
        elements_south.append(e.rotate(180))

    if len(gratings_north) > 0:
        for io in gratings_north[0]:
            gratings_south.append(io.rotate(180))

    return elements_south, gratings_south
Ejemplo n.º 6
0
def route_south(
    component: Component,
    bend_radius: float = conf.tech.bend_radius,
    optical_routing_type: int = 1,
    excluded_ports: List[str] = None,
    waveguide_separation: float = 4.0,
    io_gratings_lines: Optional[List[List[ComponentReference]]] = None,
    route_filter: Callable = connect_strip_way_points,
    gc_port_name: str = "E0",
) -> Union[Tuple[List[Any], List[Port]], Tuple[List[ComponentReference], List[Port]]]:
    """
    Args:
        component: component to route
        bend_radius
        optical_routing_type: routing heuristic `1` or `2`
            `1` uses the component size info to estimate the box size.
            `2` only looks at the optical port positions to estimate the size
        excluded_ports=[]: list of port names to NOT route
        waveguide_separation
        io_gratings_lines: list of ports to which the ports produced by this
            function will be connected. Supplying this information helps
            avoiding waveguide collisions

        routing_method: routing method to connect the waveguides
        gc_port_name: grating port name

    Returns:
        list of elements, list of ports


    Works well if the component looks rougly like a rectangular box with
        north ports on the north of the box
        south ports on the south of the box
        east ports on the east of the box
        west ports on the west of the box
    """
    excluded_ports = excluded_ports or []
    assert optical_routing_type in [
        1,
        2,
    ], f"optical_routing_type = {optical_routing_type}, not supported "

    optical_ports = component.get_ports_list(port_type="optical")
    optical_ports = [p for p in optical_ports if p.name not in excluded_ports]
    csi = component.size_info
    elements = []

    # Handle empty list gracefully
    if not optical_ports:
        return [], []

    conn_params = {"bend_radius": bend_radius}

    route_filter_params = {
        "bend_radius": bend_radius,
        "wg_width": optical_ports[0].width,
    }

    def routing_method(p1, p2, **kwargs):
        way_points = get_waypoints_connect_strip(p1, p2, **kwargs)
        return route_filter(way_points, **route_filter_params)

    # Used to avoid crossing between waveguides in special cases
    # This could happen when abs(x_port - x_grating) <= 2 * bend_radius
    delta_gr_min = 2 * bend_radius + 1

    sep = waveguide_separation

    # Get lists of optical ports by orientation
    direction_ports = direction_ports_from_list_ports(optical_ports)

    north_ports = direction_ports["N"]
    north_start = north_ports[0 : len(north_ports) // 2]
    north_finish = north_ports[len(north_ports) // 2 :]

    west_ports = direction_ports["W"]
    west_ports.reverse()
    east_ports = direction_ports["E"]
    south_ports = direction_ports["S"]
    north_finish.reverse()  # Sort right to left
    north_start.reverse()  # Sort right to left
    ordered_ports = north_start + west_ports + south_ports + east_ports + north_finish

    def get_index_port_closest_to_x(x, list_ports):
        return np.array([abs(x - p.ports[gc_port_name].x) for p in list_ports]).argmin()

    def gen_port_from_port(x, y, p):
        new_p = pd.Port(name=p.name, midpoint=(x, y), orientation=90.0, width=p.width)

        return new_p

    R = bend_radius
    west_ports.reverse()

    y0 = min([p.y for p in ordered_ports]) - R - 0.5

    ports_to_route = []

    i = 0
    optical_xs_tmp = [p.x for p in ordered_ports]
    x_optical_min = min(optical_xs_tmp)
    x_optical_max = max(optical_xs_tmp)

    """
    ``x`` is the x-coord of the waypoint where the current component port is connected.
    x starts as close as possible to the component.
    For each new port, the distance is increased by the separation.
    The starting x depends on the heuristic chosen : ``1`` or ``2``
    """

    # Set starting ``x`` on the west side
    if optical_routing_type == 1:
        # use component size to know how far to route
        x = csi.west - R - 1
    elif optical_routing_type == 2:
        # use optical port to know how far to route
        x = x_optical_min - R - 1
    else:
        raise ValueError("Invalid optical routing type")

    # First route the ports facing west
    for p in west_ports:
        """
        In case we have to connect these ports to a line of grating,
        Ensure that the port is aligned with the grating port or
        has enough space for manhattan routing (at least two bend radius)
        """
        if io_gratings_lines:
            i_grating = get_index_port_closest_to_x(x, io_gratings_lines[-1])
            x_gr = io_gratings_lines[-1][i_grating].ports[gc_port_name].x
            if abs(x - x_gr) < delta_gr_min:
                if x > x_gr:
                    x = x_gr
                elif x < x_gr:
                    x = x_gr - delta_gr_min

        tmp_port = gen_port_from_port(x, y0, p)
        ports_to_route.append(tmp_port)
        elements += [routing_method(p, tmp_port, **conn_params)]
        x -= sep

        i += 1
    start_straight = 0.5

    # First-half of north ports
    # This ensures that north ports are routed above the top west one
    north_start.reverse()  # We need them from left to right
    if len(north_start) > 0:
        y_max = max([p.y for p in west_ports + north_start])
        for p in north_start:
            tmp_port = gen_port_from_port(x, y0, p)

            elements += [
                routing_method(
                    p,
                    tmp_port,
                    start_straight=start_straight + y_max - p.y,
                    **conn_params,
                )
            ]

            ports_to_route.append(tmp_port)
            x -= sep
            start_straight += sep

    # Set starting ``x`` on the east side
    if optical_routing_type == 1:
        #  use component size to know how far to route
        x = csi.east + R + 1
    elif optical_routing_type == 2:
        # use optical port to know how far to route
        x = x_optical_max + R + 1
    else:
        raise ValueError(
            f"Invalid optical routing type. Got {optical_routing_type}, only (1, 2 supported) "
        )
    i = 0

    # Route the east ports
    start_straight = 0.5
    for p in east_ports:
        """
        In case we have to connect these ports to a line of grating,
        Ensure that the port is aligned with the grating port or
        has enough space for manhattan routing (at least two bend radius)
        """
        if io_gratings_lines:
            i_grating = get_index_port_closest_to_x(x, io_gratings_lines[-1])
            x_gr = io_gratings_lines[-1][i_grating].ports[gc_port_name].x
            if abs(x - x_gr) < delta_gr_min:
                if x < x_gr:
                    x = x_gr
                elif x > x_gr:
                    x = x_gr + delta_gr_min

        tmp_port = gen_port_from_port(x, y0, p)

        elements += [
            routing_method(p, tmp_port, start_straight=start_straight, **conn_params)
        ]

        ports_to_route.append(tmp_port)
        x += sep
        i += 1

    # Route the remaining north ports
    start_straight = 0.5
    if len(north_finish) > 0:
        y_max = max([p.y for p in east_ports + north_finish])
        for p in north_finish:
            tmp_port = gen_port_from_port(x, y0, p)
            ports_to_route.append(tmp_port)
            elements += [
                routing_method(
                    p,
                    tmp_port,
                    start_straight=start_straight + y_max - p.y,
                    **conn_params,
                )
            ]
            x += sep
            start_straight += sep

    # Add south ports
    ports = [flip(p) for p in ports_to_route] + south_ports

    return elements, ports