Example #1
0
def bend_s(size: Float2 = (10.0, 2.0),
           nb_points: int = 99,
           with_cladding_box: bool = True,
           cross_section: CrossSectionFactory = strip,
           **kwargs) -> Component:
    """S bend with bezier curve

    stores min_bend_radius property in self.info['min_bend_radius']
    min_bend_radius depends on height and length

    Args:
        size: in x and y direction
        nb_points: number of points
        with_cladding_box: square bounding box to avoid DRC errors
        cross_section: function
        kwargs: cross_section settings

    """
    dx, dy = size
    x = cross_section(**kwargs)
    width = x.info["width"]
    layer = x.info["layer"]

    c = Component()

    bend = bezier(
        width=width,
        control_points=[(0, 0), (dx / 2, 0), (dx / 2, dy), (dx, dy)],
        npoints=nb_points,
        layer=layer,
    )

    bend_ref = c << bend
    c.add_ports(bend_ref.ports)
    c.copy_child_info(bend)
    c.info.start_angle = bend.info.start_angle
    c.info.end_angle = bend.info.end_angle
    c.info.length = bend.info.length
    c.info.min_bend_radius = bend.info.min_bend_radius

    if with_cladding_box and x.info["layers_cladding"]:
        layers_cladding = x.info["layers_cladding"]
        cladding_offset = x.info["cladding_offset"]
        points = get_padding_points(
            component=c,
            default=0,
            bottom=cladding_offset,
            top=cladding_offset,
        )
        for layer in layers_cladding or []:
            c.add_polygon(points, layer=layer)

    auto_rename_ports(c)
    return c
Example #2
0
def fanout2x2(component: ComponentOrFactory,
              port_spacing: float = 20.0,
              bend_length: Optional[float] = None,
              npoints: int = 101,
              select_ports: Callable = select_ports_optical,
              cross_section: CrossSectionFactory = strip,
              with_cladding_box: bool = True,
              **kwargs) -> Component:
    """returns component with port_spacing.

    Args:
        component: to package
        port_spacing: for the returned component
        bend_length: length of the bend (defaults to port_spacing)
        npoints: for sbend
        select_ports: function to select  optical_ports ports
        cross_section:
        with_cladding_box: square bounding box to avoid DRC errors

    Keyword Args:
        cross_section settings

    """

    c = gf.Component()

    component = component() if callable(component) else component
    component.component = component
    ref = c << component
    ref.movey(-ref.y)

    if bend_length is None:
        bend_length = port_spacing
    dx = bend_length

    y = port_spacing / 2.0

    p_w0 = ref.ports["o1"].midpoint
    p_w1 = ref.ports["o2"].midpoint
    p_e1 = ref.ports["o3"].midpoint
    p_e0 = ref.ports["o4"].midpoint
    y0 = p_e1[1]

    dy = y - y0

    control_points = [(0, 0), (dx / 2, 0), (dx / 2, dy), (dx, dy)]

    x = cross_section(**kwargs)
    width = x.info["width"]
    layer = x.info["layer"]

    bend = bezier(
        control_points=control_points,
        npoints=npoints,
        start_angle=0,
        end_angle=0,
        width=width,
        layer=layer,
    )
    if with_cladding_box and x.info["layers_cladding"]:
        layers_cladding = x.info["layers_cladding"]
        cladding_offset = x.info["cladding_offset"]
        bend.unlock()
        points = get_padding_points(
            component=bend,
            default=0,
            bottom=cladding_offset,
            top=cladding_offset,
        )
        for layer in layers_cladding or []:
            bend.add_polygon(points, layer=layer)

    b_tr = bend.ref(port_id="o1", position=p_e1)
    b_br = bend.ref(port_id="o1", position=p_e0, v_mirror=True)
    b_tl = bend.ref(port_id="o1", position=p_w1, h_mirror=True)
    b_bl = bend.ref(port_id="o1", position=p_w0, rotation=180)

    c.add([b_tr, b_br, b_tl, b_bl])

    c.add_port("o1", port=b_bl.ports["o2"])
    c.add_port("o2", port=b_tl.ports["o2"])
    c.add_port("o3", port=b_tr.ports["o2"])
    c.add_port("o4", port=b_br.ports["o2"])

    c.min_bend_radius = bend.info["min_bend_radius"]

    optical_ports = select_ports(ref.ports)
    for port_name in ref.ports.keys():
        if port_name not in optical_ports:
            c.add_port(port_name, port=ref.ports[port_name])
    c.copy_child_info(component)
    return c
Example #3
0
def compensation_path(crossing45: ComponentOrFactory = crossing45,
                      direction: str = "top") -> Component:
    r"""Returns Component Path with same path length as the crossing

    with input and output ports having same y coordinates

    Args:
        crossing45: component that we want to match in path length
            needs to have .info["components"] with bends and crossing
        direction: the direction in which the bend should go "top" / "bottom"


    .. code::

          ----       ----
              \     /
               \   /
                \ /
                 X
                / \
               /   \
              /     \
          ----       ----

    Compensation path:

    .. code::

             --+--
           _/     \_
        --/         \--


    """
    # Get total path length taken by the bends
    crossing45 = crossing45() if callable(crossing45) else crossing45
    bezier_length = crossing45.info.bezier_length
    length = 2 * bezier_length

    # Find a bezier S-bend with half this length, but with a fixed length
    # governed by the crossing45 X-distance (west to east ports) and
    # the crossing x_distance

    target_bend_length = length / 2

    def get_x_span(cmp):
        return cmp.ports["o3"].x - cmp.ports["o1"].x

    x_span_crossing45 = get_x_span(crossing45)
    x_span_crossing = get_x_span(crossing45.crossing)

    # x span allowed for the bend
    x0 = (x_span_crossing45 - x_span_crossing) / 2

    def get_control_pts(x, y):
        return [(0, 0), (x0 / 2, 0), (x0 / 2, y), (x0, y)]

    def f(y):
        control_points = get_control_pts(x0, y)
        t = np.linspace(0, 1, 51)
        path_points = bezier_curve(t, control_points)
        return path_length(path_points) - target_bend_length

    # the path length of the s-bend between two ports p0 and p1 is :
    # - larger than the euclidian distance L2(p0, p1)
    # - smaller than the manhattan distance DL(p0, p1)
    #
    # This gives the bounds for the brentq root finding

    ya = target_bend_length - x0
    yb = np.sqrt(target_bend_length**2 - x0**2)

    solution = so.root_scalar(f, bracket=[ya, yb], method="brentq")

    y_bend = solution.root
    y_bend = snap_to_grid(y_bend)

    if direction == "top":
        v_mirror = False
    else:
        v_mirror = True

    sbend = bezier(control_points=get_control_pts(x0, y_bend))

    component = Component()
    crossing0 = crossing45.crossing.ref()
    component.add(crossing0)

    sbend_left = sbend.ref(position=crossing0.ports["o1"],
                           port_id="o2",
                           v_mirror=v_mirror)
    sbend_right = sbend.ref(position=crossing0.ports["o3"],
                            port_id="o2",
                            h_mirror=True,
                            v_mirror=v_mirror)

    component.add(sbend_left)
    component.add(sbend_right)

    component.add_port("o1", port=sbend_left.ports["o1"])
    component.add_port("o2", port=sbend_right.ports["o1"])

    component.info["min_bend_radius"] = sbend.info["min_bend_radius"]
    component.info.sbend = sbend.info
    return component
Example #4
0
def crossing45(
    crossing: ComponentFactory = crossing,
    port_spacing: float = 40.0,
    dx: Optional[float] = None,
    alpha: float = 0.08,
    npoints: int = 101,
) -> Component:
    r"""Returns 45deg crossing with bends.

    Args:
        crossing: crossing function
        port_spacing: target I/O port spacing
        dx: target length
        alpha: optimization parameter. diminish it for tight bends,
          increase it if raises assertion angle errors
        npoints: number of points.


    Implementation note: The 45 Degree crossing CANNOT be kept as an SRef since
    we only allow for multiples of 90Deg rotations in SRef

    .. code::

        ----   ----
            \ /
             X
            / \
        ---    ----

    """

    crossing = crossing() if callable(crossing) else crossing

    c = Component()
    _crossing = crossing.ref(rotation=45)
    c.add(_crossing)

    # Add bends
    p_e = _crossing.ports["o3"].midpoint
    p_w = _crossing.ports["o1"].midpoint
    p_n = _crossing.ports["o2"].midpoint
    p_s = _crossing.ports["o4"].midpoint

    # Flatten the crossing - not an SRef anymore
    c.absorb(_crossing)
    dx = dx or port_spacing
    dy = port_spacing / 2

    start_angle = 45
    end_angle = 0
    cpts = find_min_curv_bezier_control_points(
        start_point=p_e,
        end_point=(dx, dy),
        start_angle=start_angle,
        end_angle=end_angle,
        npoints=npoints,
        alpha=alpha,
    )

    bend = bezier(
        control_points=cpts,
        start_angle=start_angle,
        end_angle=end_angle,
        npoints=npoints,
    )

    tol = 1e-2
    assert abs(bend.info["start_angle"] -
               start_angle) < tol, bend.info["start_angle"]
    assert abs(bend.info["end_angle"] -
               end_angle) < tol, bend.info["end_angle"]

    b_tr = bend.ref(position=p_e, port_id="o1")
    b_tl = bend.ref(position=p_n, port_id="o1", h_mirror=True)
    b_bl = bend.ref(position=p_w, port_id="o1", rotation=180)
    b_br = bend.ref(position=p_s, port_id="o1", v_mirror=True)

    for cmp_ref in [b_tr, b_br, b_tl, b_bl]:
        # cmp_ref = _cmp.ref()
        c.add(cmp_ref)
        c.absorb(cmp_ref)

    c.info.bezier_length = bend.info.length
    c.info.crossing = crossing.info
    c.info.min_bend_radius = b_br.info.min_bend_radius

    c.bezier = bend
    c.crossing = crossing

    c.add_port("o1", port=b_br.ports["o2"])
    c.add_port("o2", port=b_tr.ports["o2"])
    c.add_port("o3", port=b_bl.ports["o2"])
    c.add_port("o4", port=b_tl.ports["o2"])
    c.snap_ports_to_grid()
    c.auto_rename_ports()
    return c
Example #5
0
def fanout2x2(
    component: Component,
    port_spacing: float = 20.0,
    bend_length: Optional[float] = None,
    npoints: int = 101,
    select_ports: Callable = select_ports_optical,
) -> Component:
    """returns component with port_spacing.

    Args:
        component: to package
        port_spacing: for the returned component
        bend_length: length of the bend (defaults to port_spacing)
        npoints: for sbend
        select_ports: function to select  optical_ports ports

    """

    c = gf.Component()

    component = component() if callable(component) else component
    component.component = component
    ref = c << component
    ref.movey(-ref.y)

    if bend_length is None:
        bend_length = port_spacing
    dx = bend_length

    y = port_spacing / 2.0

    p_w0 = ref.ports["o1"].midpoint
    p_w1 = ref.ports["o2"].midpoint
    p_e1 = ref.ports["o3"].midpoint
    p_e0 = ref.ports["o4"].midpoint
    y0 = p_e1[1]

    dy = y - y0

    control_points = [(0, 0), (dx / 2, 0), (dx / 2, dy), (dx, dy)]

    bezier_bend_t = bezier(control_points=control_points,
                           npoints=npoints,
                           start_angle=0,
                           end_angle=0)

    b_tr = bezier_bend_t.ref(port_id="o1", position=p_e1)
    b_br = bezier_bend_t.ref(port_id="o1", position=p_e0, v_mirror=True)
    b_tl = bezier_bend_t.ref(port_id="o1", position=p_w1, h_mirror=True)
    b_bl = bezier_bend_t.ref(port_id="o1", position=p_w0, rotation=180)

    c.add([b_tr, b_br, b_tl, b_bl])

    c.add_port("o1", port=b_bl.ports["o2"])
    c.add_port("o2", port=b_tl.ports["o2"])
    c.add_port("o3", port=b_tr.ports["o2"])
    c.add_port("o4", port=b_br.ports["o2"])

    c.min_bend_radius = bezier_bend_t.info["min_bend_radius"]

    optical_ports = select_ports(ref.ports)
    for port_name in ref.ports.keys():
        if port_name not in optical_ports:
            c.add_port(port_name, port=ref.ports[port_name])
    c.copy_child_info(component)
    return c