コード例 #1
0
ファイル: add_labels.py プロジェクト: tvt173/gdsfactory
def add_labels(
    component: Component,
    get_label_function: Callable = get_input_label_electrical,
    layer_label: Layer = gf.LAYER.LABEL,
    gc: Optional[Component] = None,
    **kwargs,
) -> Component:
    """Returns component with labels a particular type of ports

    Args:
        component: to add labels to
        get_label_function: function to get label
        layer_label: layer_label
        gc: Optional grating coupler
        **port_settings to select

    Returns:
        original component with labels

    """
    ports = component.get_ports_list(**kwargs)

    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
コード例 #2
0
def crossing_arm(
    width: float = 0.5,
    r1: float = 3.0,
    r2: float = 1.1,
    w: float = 1.2,
    L: float = 3.4,
) -> Component:
    """arm of a crossing"""
    c = Component()
    _ellipse = ellipse(radii=(r1, r2), layer=LAYER.SLAB150).ref()
    c.add(_ellipse)
    c.absorb(_ellipse)

    a = np.round(L + w / 2, 3)
    h = width / 2

    taper_pts = [
        (-a, h),
        (-w / 2, w / 2),
        (w / 2, w / 2),
        (a, h),
        (a, -h),
        (w / 2, -w / 2),
        (-w / 2, -w / 2),
        (-a, -h),
    ]

    c.add_polygon(taper_pts, layer=LAYER.WG)
    c.add_port(
        name="o1", midpoint=(-a, 0), orientation=180, width=width, layer=LAYER.WG
    )

    c.add_port(name="o2", midpoint=(a, 0), orientation=0, width=width, layer=LAYER.WG)

    return c
コード例 #3
0
ファイル: invert.py プロジェクト: simbilod/gdsfactory
def invert(
        elements,
        border: float = 10.0,
        precision: float = 1e-4,
        num_divisions: Union[int, Int2] = (1, 1),
        max_points: int = 4000,
        layer: Layer = (1, 0),
):
    """Creates an inverted version of the input shapes with an additional
    border around the edges. adapted from phidl.geometry.invert

    Args:
        elements : Component(/Reference), list of Component(/Reference), or Polygon
            A Component containing the polygons to invert.
        border : int or float
            Size of the border around the inverted shape (border value is the
            distance from the edges of the boundary box defining the inverted
            shape to the border, and is applied to all 4 sides of the shape).
        precision : float
            Desired precision for rounding vertex coordinates.
        num_divisions : array-like[2] of int
            The number of divisions with which the geometry is divided into
            multiple rectangular regions. This allows for each region to be
            processed sequentially, which is more computationally efficient.
        max_points : int
            The maximum number of vertices within the resulting polygon.
        layer : int, array-like[2], or set
            Specific layer(s) to put polygon geometry on.

    Returns
        D: A Component containing the inverted version of the input shape(s) and the
        corresponding border(s).
    """
    Temp = Component()
    if type(elements) is not list:
        elements = [elements]
    for e in elements:
        if isinstance(e, Component):
            Temp.add_ref(e)
        else:
            Temp.add(e)
    gds_layer, gds_datatype = pg._parse_layer(layer)

    # Build the rectangle around the Component D
    R = gf.c.rectangle(size=(Temp.xsize + 2 * border, Temp.ysize + 2 * border),
                       centered=True)
    R.center = Temp.center
    D = boolean(
        A=R,
        B=Temp,
        operation="A-B",
        precision=precision,
        num_divisions=num_divisions,
        max_points=max_points,
        layer=layer,
    )
    return D
コード例 #4
0
ファイル: manhattan.py プロジェクト: simbilod/gdsfactory
def test_manhattan_pass() -> Component:
    waypoints = [
        [10.0, 0.0],
        [20.0, 0.0],
        [20.0, 12.0],
        [120.0, 12.0],
        [120.0, 80.0],
        [110.0, 80.0],
    ]
    route = round_corners(waypoints, radius=5)
    c = Component()
    c.add(route.references)
    return c
コード例 #5
0
ファイル: manhattan.py プロジェクト: simbilod/gdsfactory
def _demo_manhattan_fail() -> Component:
    waypoints = [
        [10.0, 0.0],
        [20.0, 0.0],
        [20.0, 12.0],
        [120.0, 12.0],
        [120.0, 80.0],
        [110.0, 80.0],
    ]
    route = round_corners(waypoints, radius=10.0, with_point_markers=False)
    c = Component()
    c.add(route.references)
    return c
コード例 #6
0
def fanout_component(
    component: ComponentOrFactory,
    port_names: Tuple[str, ...],
    pitch: Tuple[float, float] = (0.0, 20.0),
    dx: float = 20.0,
    sort_ports: bool = True,
    **kwargs,
) -> Component:
    """Returns component with Sbend fanout routes.

    Args:
        component: to fanout ports
        port_names: list of port names
        pitch: target port spacing for new component
        dx: how far the fanout in x direction
        kwargs: for get_route_sbend
    """

    c = Component()
    comp = component() if callable(component) else component
    ref = c.add_ref(comp)
    ref.movey(-comp.y)

    for port_name in port_names:
        if port_name not in ref.ports:
            raise ValueError(f"{port_name} not in {list(ref.ports.keys())}")

    ports1 = [p for p in ref.ports.values() if p.name in port_names]
    port = ports1[0]
    port_extended_x = port.get_extended_midpoint(dx)[0]
    port_settings = port.settings.copy()

    port_settings.pop("name")
    port_settings.update(midpoint=(port_extended_x, 0))
    port_settings.update(orientation=(port.angle + 180) % 360)

    ports2 = port_array(n=len(ports1), pitch=pitch, **port_settings)

    if sort_ports:
        ports1, ports2 = sort_ports_function(ports1, ports2)

    for i, (p1, p2) in enumerate(zip(ports1, ports2)):
        route = get_route_sbend(port1=p1, port2=p2, **kwargs)
        c.add(route.references)
        c.add_port(f"new_{i}", port=flip(p2))

    for port in ref.ports.values():
        if port.name not in port_names:
            c.add_port(port.name, port=port)

    return c
コード例 #7
0
def _sample_route_sides() -> Component:
    c = Component()
    _dummy_t = _sample_route_side()
    sides = ["north", "south", "east", "west"]
    positions = [(0, 0), (400, 0), (400, 400), (0, 400)]
    for pos, side in zip(positions, sides):
        dummy_ref = _dummy_t.ref(position=pos)
        c.add(dummy_ref)
        routes, ports = route_ports_to_side(dummy_ref, side, layer=(2, 0))
        for route in routes:
            c.add(route.references)
        for i, p in enumerate(ports):
            c.add_port(name=f"{side[0]}{i}", port=p)
    return c
コード例 #8
0
def awg(
    arms: int = 10,
    outputs: int = 3,
    free_propagation_region_input_function=free_propagation_region_input,
    free_propagation_region_output_function=free_propagation_region_output,
    fpr_spacing: float = 50.0,
) -> Component:
    """Returns a basic Arrayed Waveguide grating.

    Args:
        arms: number of arms
        outputs: number of outputs
        free_propagation_region_input_function: for input
        free_propagation_region_output_function: for output
        fpr_spacing: x separation between input/output FPR

    """
    c = Component()
    fpr_in = free_propagation_region_input_function(
        inputs=1,
        outputs=arms,
    )
    fpr_out = free_propagation_region_output_function(
        inputs=outputs,
        outputs=arms,
    )

    fpr_in_ref = c.add_ref(fpr_in)
    fpr_out_ref = c.add_ref(fpr_out)

    fpr_in_ref.rotate(90)
    fpr_out_ref.rotate(90)

    fpr_out_ref.x += fpr_spacing
    routes = gf.routing.get_bundle(
        fpr_in_ref.get_ports_list(prefix="E"), fpr_out_ref.get_ports_list(prefix="E")
    )

    c.lengths = []
    for route in routes:
        c.add(route.references)
        c.lengths.append(route.length)

    c.add_port("o1", port=fpr_in_ref.ports["o1"])

    for i, port in enumerate(fpr_out_ref.get_ports_list(prefix="W")):
        c.add_port(f"E{i}", port=port)

    c.delta_length = np.mean(np.diff(c.lengths))
    return c
コード例 #9
0
ファイル: manhattan.py プロジェクト: simbilod/gdsfactory
def test_manhattan_fail() -> Component:
    waypoints = [
        [10.0, 0.0],
        [20.0, 0.0],
        [20.0, 12.0],
        [120.0, 12.0],
        [120.0, 80.0],
        [110.0, 80.0],
    ]
    with pytest.warns(RouteWarning):
        route = round_corners(waypoints, radius=10.0, with_point_markers=False)
    c = Component()
    c.add(route.references)
    return c
コード例 #10
0
def add_grating_couplers(
    component: Component,
    grating_coupler: ComponentFactory = grating_coupler_te,
    layer_label: Tuple[int, int] = (200, 0),
    gc_port_name: str = "o1",
    get_input_labels_function: Callable[..., List[Label]] = get_input_labels,
    select_ports: Callable = select_ports_optical,
    component_name: Optional[str] = None,
) -> Component:
    """Returns new component with grating couplers and labels.

    Args:
        component: to add grating_couplers
        grating_coupler: grating_coupler function
        layer_label: for label
        gc_port_name: where to add label
        get_input_labels_function: function to get label
        select_ports: for selecting optical_ports

    """

    c = Component()
    c.component = component
    component_name = component_name or component.info_child.name
    c.add_ref(component)
    grating_coupler = (
        grating_coupler() if callable(grating_coupler) else grating_coupler
    )

    io_gratings = []
    optical_ports = select_ports(component.ports)
    optical_ports = list(optical_ports.values())
    for port in optical_ports:
        gc_ref = grating_coupler.ref()
        gc_port = gc_ref.ports[gc_port_name]
        gc_ref.connect(gc_port, port)
        io_gratings.append(gc_ref)
        c.add(gc_ref)

    labels = get_input_labels_function(
        io_gratings,
        list(component.ports.values()),
        component_name=component_name,
        layer_label=layer_label,
        gc_port_name=gc_port_name,
    )
    c.add(labels)
    c.copy_child_info(component)
    return c
コード例 #11
0
def crossing_from_taper(taper=lambda: taper(width2=2.5, length=3.0)):
    """
    Crossing based on a taper. The default is a dummy taper
    """
    taper = taper() if callable(taper) else taper

    c = Component()
    for i, a in enumerate([0, 90, 180, 270]):
        _taper = taper.ref(position=(0, 0), port_id="o2", rotation=a)
        c.add(_taper)
        c.add_port(name=i, port=_taper.ports["o1"])
        c.absorb(_taper)

    c.auto_rename_ports()
    return c
コード例 #12
0
ファイル: copy.py プロジェクト: simbilod/gdsfactory
def copy(D: Component,
         prefix: str = "",
         suffix: str = "_copy",
         cache: bool = True) -> Component:
    """returns a deep copy of a Component.
    based on phidl.geometry with CellArray support
    """
    D_copy = Component(name=f"{prefix}{D.name}{suffix}")
    D_copy.info = python_copy.deepcopy(D.info)
    for ref in D.references:
        if isinstance(ref, DeviceReference):
            new_ref = ComponentReference(
                ref.parent,
                origin=ref.origin,
                rotation=ref.rotation,
                magnification=ref.magnification,
                x_reflection=ref.x_reflection,
            )
            new_ref.owner = D_copy
        elif isinstance(ref, gdspy.CellArray):
            new_ref = CellArray(
                device=ref.parent,
                columns=ref.columns,
                rows=ref.rows,
                spacing=ref.spacing,
                origin=ref.origin,
                rotation=ref.rotation,
                magnification=ref.magnification,
                x_reflection=ref.x_reflection,
            )
        D_copy.add(new_ref)
        for alias_name, alias_ref in D.aliases.items():
            if alias_ref == ref:
                D_copy.aliases[alias_name] = new_ref

    for port in D.ports.values():
        D_copy.add_port(port=port)
    for poly in D.polygons:
        D_copy.add_polygon(poly)
    for label in D.labels:
        D_copy.add_label(
            text=label.text,
            position=label.position,
            layer=(label.layer, label.texttype),
        )
    if cache:
        D_copy = avoid_duplicated_cells(D_copy)
    return D_copy
コード例 #13
0
def crossing(arm: ComponentFactory = crossing_arm) -> Component:
    """Waveguide crossing"""
    cx = Component()
    arm = arm() if callable(arm) else arm
    arm_h = arm.ref()
    arm_v = arm.ref(rotation=90)

    port_id = 0
    for c in [arm_h, arm_v]:
        cx.add(c)
        cx.absorb(c)
        for p in c.ports.values():
            cx.add_port(name=port_id, port=p)
            port_id += 1
    cx.auto_rename_ports()
    return cx
コード例 #14
0
def add_electrical_pads_top_dc(
    component: Component,
    spacing: Float2 = (0.0, 100.0),
    pad_array: ComponentFactory = pad_array_function,
    select_ports: Callable = select_ports_electrical,
    **kwargs,
) -> Component:
    """connects component electrical ports with pad array at the top

    Args:
        component:
        spacing: component to pad spacing
        pad_array:
        select_ports: function to select_ports
        **kwargs: route settings
    """
    c = Component()

    cref = c << component
    ports = select_ports(cref.ports)
    ports_component = list(ports.values())
    ports_component = [port.copy() for port in ports_component]

    for port in ports_component:
        port.orientation = 90

    pads = c << pad_array(columns=len(ports))
    pads.x = cref.x + spacing[0]
    pads.ymin = cref.ymax + spacing[1]

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

    routes = get_bundle(ports_component,
                        ports_pads,
                        bend=wire_corner,
                        **kwargs)
    for route in routes:
        c.add(route.references)

    c.add_ports(cref.ports)
    for port in ports_component:
        c.ports.pop(port.name)

    c.copy_child_info(component)
    return c
コード例 #15
0
ファイル: loop_mirror.py プロジェクト: tvt173/gdsfactory
def loop_mirror(component: ComponentFactory = mmi1x2,
                bend90: ComponentFactory = bend_euler) -> Component:
    """Returns Sagnac loop_mirror."""
    c = Component()
    component = gf.call_if_func(component)
    bend90 = gf.call_if_func(bend90)
    cref = c.add_ref(component)
    routes = route_manhattan(
        cref.ports["o3"],
        cref.ports["o2"],
        straight=gf.components.straight,
        bend=bend90,
    )
    c.add(routes.references)
    c.add_port(name="o1", port=cref.ports["o1"])
    c.absorb(cref)
    return c
コード例 #16
0
ファイル: manhattan.py プロジェクト: simbilod/gdsfactory
def get_route_error(
    points,
    cross_section: Optional[CrossSection] = None,
    layer_path: Layer = LAYER.ERROR_PATH,
    layer_label: Layer = LAYER.TEXT,
    layer_marker: Layer = LAYER.ERROR_MARKER,
    references: Optional[List[ComponentReference]] = None,
) -> Route:
    width = cross_section.info["width"] if cross_section else 10
    warnings.warn(
        f"Route error for points {points}",
        RouteWarning,
    )

    c = Component(f"route_{uuid.uuid4()}"[:16])
    path = gdspy.FlexPath(
        points,
        width=width,
        gdsii_path=True,
        layer=layer_path[0],
        datatype=layer_path[1],
    )
    c.add(path)
    ref = ComponentReference(c)
    port1 = gf.Port(name="p1", midpoint=points[0], width=width)
    port2 = gf.Port(name="p2", midpoint=points[1], width=width)

    point_marker = gf.c.rectangle(size=(width * 2, width * 2),
                                  centered=True,
                                  layer=layer_marker)
    point_markers = [point_marker.ref(position=point)
                     for point in points] + [ref]
    labels = [
        gf.Label(text=str(i),
                 position=point,
                 layer=layer_label[0],
                 texttype=layer_label[1]) for i, point in enumerate(points)
    ]

    references = references or []
    references += point_markers
    return Route(references=references,
                 ports=[port1, port2],
                 length=-1,
                 labels=labels)
コード例 #17
0
def add_electrical_pads_shortest(
        component: Component,
        pad: ComponentOrFactory = pad_function,
        pad_port_spacing: float = 50.0,
        select_ports=select_ports_electrical,
        port_orientation: int = 90,
        layer: gf.types.Layer = (31, 0),
        **kwargs,
) -> Component:
    """Add pad to each closest electrical port.

    Args:
        component:
        pad: pad element or function
        pad_port_spacing: between pad and port
        select_ports: function
        port_orientation
        layer: for the routing
        **kwargs: pad_settings

    """
    c = Component()
    c.component = component
    ref = c << component
    ports = select_ports(ref.ports)
    ports = list(ports.values())

    pad = pad(**kwargs) if callable(pad) else pad
    pad_port_spacing += pad.info_child.full["size"][0] / 2

    for port in ports:
        p = c << pad
        if port_orientation == 0:
            p.x = port.x + pad_port_spacing
            p.y = port.y
            c.add(route_quad(port, p.ports["e1"], layer=layer))
        elif port_orientation == 180:
            p.x = port.x - pad_port_spacing
            p.y = port.y
            c.add(route_quad(port, p.ports["e3"], layer=layer))
        elif port_orientation == 90:
            p.y = port.y + pad_port_spacing
            p.x = port.x
            c.add(route_quad(port, p.ports["e4"], layer=layer))
        elif port_orientation == 270:
            p.y = port.y - pad_port_spacing
            p.x = port.x
            c.add(route_quad(port, p.ports["e2"], layer=layer))

    c.add_ports(ref.ports)
    for port in ports:
        c.ports.pop(port.name)
    c.copy_child_info(component)
    return c
コード例 #18
0
ファイル: manhattan.py プロジェクト: simbilod/gdsfactory
def test_manhattan() -> Component:
    top_cell = Component()

    inputs = [
        Port("in1", (10, 5), 0.5, 90),
        # Port("in2", (-10, 20), 0.5, 0),
        # Port("in3", (10, 30), 0.5, 0),
        # Port("in4", (-10, -5), 0.5, 90),
        # Port("in5", (0, 0), 0.5, 0),
        # Port("in6", (0, 0), 0.5, 0),
    ]

    outputs = [
        Port("in1", (290, -60), 0.5, 180),
        # Port("in2", (-100, 20), 0.5, 0),
        # Port("in3", (100, -25), 0.5, 0),
        # Port("in4", (-150, -65), 0.5, 270),
        # Port("in5", (25, 3), 0.5, 180),
        # Port("in6", (0, 10), 0.5, 0),
    ]

    lengths = [349.974]

    for input_port, output_port, length in zip(inputs, outputs, lengths):

        # input_port = Port("input_port", (10,5), 0.5, 90)
        # output_port = Port("output_port", (90,-60), 0.5, 180)
        # bend = bend_circular(radius=5.0)

        route = route_manhattan(
            input_port=input_port,
            output_port=output_port,
            straight=straight_function,
            radius=5.0,
            auto_widen=True,
            width_wide=2,
            # layer=(2, 0),
            # width=0.2,
        )

        top_cell.add(route.references)
        assert np.isclose(route.length, length), route.length
    return top_cell
コード例 #19
0
def add_labels(
    component: Component,
    get_label_function: Callable = get_input_label_electrical,
    layer_label: Layer = gf.LAYER.LABEL,
    gc: Optional[Component] = None,
    **kwargs,
) -> Component:
    """Returns component with labels on some ports

    Args:
        component: to add labels to
        get_label_function: function to get label
        layer_label: layer_label
        gc: Optional grating coupler

    keyword Args:
        layer: port GDS layer
        prefix: with in port name
        orientation: in degrees
        width:
        layers_excluded: List of layers to exclude
        port_type: optical, electrical, ...
        clockwise: if True, sort ports clockwise, False: counter-clockwise

    Returns:
        original component with labels

    """
    ports = component.get_ports_list(**kwargs)

    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
コード例 #20
0
def add_electrical_pads_top(
    component: Component,
    spacing: Float2 = (0.0, 100.0),
    pad_array: ComponentFactory = pad_array_function,
    select_ports=select_ports_electrical,
    **kwargs,
) -> Component:
    """Returns new component with electrical ports connected to top pad array

    Args:
        component:
        spacing: component to pad spacing
        select_ports: function to select electrical ports
        kwargs: pad settings
            pad: pad element
            pitch: x spacing
            n: number of pads
            **port_settings
    """
    c = Component()
    c.component = component
    ref = c << component
    ports = select_ports(ref.ports)
    ports = list(ports.values())
    pads = c << pad_array_function(
        columns=len(ports), orientation=270, **kwargs)
    pads.x = ref.x + spacing[0]
    pads.ymin = ref.ymax + spacing[1]
    ports_pads = list(pads.ports.values())

    ports_pads = gf.routing.sort_ports.sort_ports_x(ports_pads)
    ports_component = gf.routing.sort_ports.sort_ports_x(ports)

    for p1, p2 in zip(ports_component, ports_pads):
        c.add(get_route_electrical_shortest_path(p1, p2))

    c.add_ports(ref.ports)
    for port in ports:
        c.ports.pop(port.name)
    return c
コード例 #21
0
def add_electrical_pads_top(
        component: Component,
        spacing: Float2 = (0.0, 100.0),
        pad_array: ComponentFactory = pad_array_function,
        select_ports=select_ports_electrical,
        layer: gf.types.Layer = (31, 0),
) -> Component:
    """Returns new component with electrical ports connected to top pad array

    Args:
        component:
        spacing: component to pad spacing
        pad_array: function for pad_array
        select_ports: function to select electrical ports
        layer: for the routes
    """
    c = Component()
    c.component = component
    ref = c << component
    ports = select_ports(ref.ports)
    ports = list(ports.values())
    pads = c << pad_array(columns=len(ports), orientation=270)
    pads.x = ref.x + spacing[0]
    pads.ymin = ref.ymax + spacing[1]
    ports_pads = list(pads.ports.values())

    ports_pads = gf.routing.sort_ports.sort_ports_x(ports_pads)
    ports_component = gf.routing.sort_ports.sort_ports_x(ports)

    for p1, p2 in zip(ports_component, ports_pads):
        c.add(route_quad(p1, p2, layer=layer))

    c.add_ports(ref.ports)
    for port in ports:
        c.ports.pop(port.name)
    c.copy_child_info(component)
    return c
コード例 #22
0
def _demo():
    """plot curvature of bends"""
    from matplotlib import pyplot as plt

    c = crossing45(port_spacing=20.0, dx=15)
    c2 = compensation_path(crossing45=c)
    print(c.info["min_bend_radius"])
    print(c2.info["min_bend_radius"])

    component = Component(name="top_lvl")
    component.add(c.ref(port_id="o1"))
    component.add(c2.ref(port_id="o1", position=(0, 10)))

    bend_info1 = c.info["components"]["bezier_bend"].info
    bend_info2 = c2.info["components"]["sbend"].info

    DL = bend_info1["length"]
    L2 = bend_info1["length"]
    plt.plot(bend_info1["t"][1:-1] * DL, abs(bend_info1["curvature"]))
    plt.plot(bend_info2["t"][1:-1] * L2, abs(bend_info2["curvature"]))
    plt.xlabel("bend length (um)")
    plt.ylabel("curvature (um^-1)")
    component.show()
    plt.show()
コード例 #23
0
def array_with_via(
    component: ComponentOrFactory = pad,
    columns: int = 3,
    spacing: float = 150.0,
    via_spacing: float = 10.0,
    straight_length: float = 60.0,
    cross_section: Optional[CrossSectionFactory] = metal2,
    contact: ComponentFactory = contact_factory,
    contact_dy: float = 0,
    port_orientation: int = 180,
    port_offset: Optional[Float2] = None,
    **kwargs,
) -> Component:
    """Returns an array of components in X axis
    with fanout waveguides facing west

    Args:
        component: to replicate in the array
        columns: number of components
        spacing: for the array
        via_spacing: for fanout
        straight_length: lenght of the straight at the end
        waveguide: waveguide definition
        cross_section:
        contact:
        contact_dy: contact offset
        port_orientation: 180: facing west
        port_offset: Optional port movement
        kwargs: cross_section settings
    """

    c = Component()
    component = component() if callable(component) else component
    contact = contact()

    for col in range(columns):
        ref = component.ref()
        ref.x = col * spacing
        c.add(ref)

        if port_orientation == 180:
            xlength = col * spacing + straight_length
        elif port_orientation == 0:
            xlength = columns * spacing - (col * spacing) + straight_length
        elif port_orientation == 270:
            xlength = col * via_spacing + straight_length

        elif port_orientation == 90:
            xlength = columns * via_spacing - (col *
                                               via_spacing) + straight_length

        else:
            raise ValueError(
                f"Invalid port_orientation = {port_orientation}",
                "180: west, 0: east, 90: north, 270: south",
            )

        contact_ref = c << contact
        contact_ref.x = col * spacing
        contact_ref.y = col * via_spacing + contact_dy

        if cross_section:
            port_name = f"e{col}"
            straightx_ref = c << straight(
                length=xlength, cross_section=cross_section, **kwargs)
            straightx_ref.connect(
                "e2",
                contact_ref.get_ports_list(orientation=port_orientation)[0])
            c.add_port(port_name, port=straightx_ref.ports["e1"])
            if port_offset:
                c.ports[port_name].move(np.array(port_offset) * col)
    return c
コード例 #24
0
def from_yaml(
    yaml_str: Union[str, pathlib.Path, IO[Any]],
    component_factory: ComponentFactoryDict = factory,
    routing_strategy: Dict[str, Callable] = routing_strategy_factories,
    cross_section_factory: Dict[str,
                                CrossSectionFactory] = cross_section_factory,
    label_instance_function: Callable = add_instance_label,
    **kwargs,
) -> Component:
    """Returns a Component defined in YAML file or string.

    Args:
        yaml: YAML IO describing Component file or string (with newlines)
          (instances, placements, routes, ports, connections, names)
        component_factory: dict of functions {factory_name: factory_function}
        routing_strategy: for links
        label_instance_function: to label each instance
        kwargs: cache, prefix, autoname ... to pass to all factories

    Returns:
        Component

    .. code::

        valid properties:
        name: Optional Component name
        vars: Optional variables
        info: Optional component info
            description: just a demo
            polarization: TE
            ...
        instances:
            name:
                component: (ComponentFactory)
                settings (Optional)
                    length: 10
                    ...
        placements:
            x: Optional[float, str]  str can be instanceName,portName
            y: Optional[float, str]
            rotation: Optional[float]
            mirror: Optional[bool, float] float is x mirror axis
            port: Optional[str] port anchor
        connections (Optional): between instances
        ports (Optional): ports to expose
        routes (Optional): bundles of routes
            routeName:
            library: optical
            links:
                instance1,port1: instance2,port2


    .. code::

        vars:
            lenght: 3

        instances:
            mmi_bot:
              component: mmi1x2
              settings:
                width_mmi: 4.5
                length_mmi: 10
            mmi_top:
              component: mmi1x2
              settings:
                width_mmi: 4.5
                length_mmi: 5

        placements:
            mmi_top:
                port: o1
                x: 0
                y: 0
            mmi_bot:
                port: o1
                x: mmi_top,o2
                y: mmi_top,o2
                dx: 30
                dy: -30
        routes:
            optical:
                library: optical
                links:
                    mmi_top,o3: mmi_bot,o1

    """
    yaml_str = (io.StringIO(yaml_str) if isinstance(yaml_str, str)
                and "\n" in yaml_str else yaml_str)

    conf = OmegaConf.load(
        yaml_str)  # nicer loader than conf = yaml.safe_load(yaml_str)
    for key in conf.keys():
        assert key in valid_top_level_keys, f"{key} not in {list(valid_top_level_keys)}"

    instances = {}
    routes = {}
    name = conf.get(
        "name",
        f"Unnamed_{hashlib.md5(json.dumps(OmegaConf.to_container(conf)).encode()).hexdigest()[:8]}",
    )
    if name in CACHE:
        return CACHE[name]
    else:
        c = Component(name)
        CACHE[name] = c
    placements_conf = conf.get("placements")
    routes_conf = conf.get("routes")
    ports_conf = conf.get("ports")
    connections_conf = conf.get("connections")
    instances_dict = conf["instances"]
    c.info = conf.get("info", omegaconf.DictConfig({}))

    for instance_name in instances_dict:
        instance_conf = instances_dict[instance_name]
        component_type = instance_conf["component"]
        assert (component_type in component_factory
                ), f"{component_type} not in {list(component_factory.keys())}"

        settings = instance_conf.get("settings", {})
        settings = OmegaConf.to_container(settings,
                                          resolve=True) if settings else {}
        settings.update(**kwargs)

        if "cross_section" in settings:
            name_or_dict = settings["cross_section"]
            if isinstance(name_or_dict, str):
                cross_section = cross_section_factory[name_or_dict]
            elif isinstance(name_or_dict, dict):
                name = name_or_dict.pop("function")
                cross_section = functools.partial(cross_section_factory[name],
                                                  **name_or_dict)
            else:
                raise ValueError(
                    f"invalid type for cross_section={name_or_dict}")
            settings["cross_section"] = cross_section

        ci = component_factory[component_type](**settings)
        ref = c << ci
        instances[instance_name] = ref

    placements_conf = dict() if placements_conf is None else placements_conf

    connections_by_transformed_inst = transform_connections_dict(
        connections_conf)
    components_to_place = set(placements_conf.keys())
    components_with_placement_conflicts = components_to_place.intersection(
        connections_by_transformed_inst.keys())
    for instance_name in components_with_placement_conflicts:
        placement_settings = placements_conf[instance_name]
        if "x" in placement_settings or "y" in placement_settings:
            warnings.warn(
                f"YAML defined: ({', '.join(components_with_placement_conflicts)}) "
                +
                "with both connection and placement. Please use one or the other.",
            )

    all_remaining_insts = list(
        set(placements_conf.keys()).union(
            set(connections_by_transformed_inst.keys())))

    while all_remaining_insts:
        place(
            placements_conf=placements_conf,
            connections_by_transformed_inst=connections_by_transformed_inst,
            instances=instances,
            encountered_insts=list(),
            all_remaining_insts=all_remaining_insts,
        )

    for instance_name in instances_dict:
        label_instance_function(component=c,
                                instance_name=instance_name,
                                reference=instances[instance_name])

    if routes_conf:
        for route_alias in routes_conf:
            route_names = []
            ports1 = []
            ports2 = []
            routes_dict = routes_conf[route_alias]
            for key in routes_dict.keys():
                if key not in valid_route_keys:
                    raise ValueError(
                        f"`{route_alias}` key=`{key}` not in {valid_route_keys}"
                    )

            settings = routes_dict.pop("settings", {})
            settings = (OmegaConf.to_container(settings, resolve=True)
                        if settings else {})
            if "cross_section" in settings:
                name_or_dict = settings["cross_section"]
                if isinstance(name_or_dict, str):
                    cross_section = cross_section_factory[name_or_dict]
                elif isinstance(name_or_dict, dict):
                    name = name_or_dict.pop("function")
                    cross_section = functools.partial(
                        cross_section_factory[name], **name_or_dict)
                else:
                    raise ValueError(
                        f"invalid type for cross_section={name_or_dict}")
                settings["cross_section"] = cross_section
            routing_strategy_name = routes_dict.pop("routing_strategy",
                                                    "get_bundle")
            if routing_strategy_name not in routing_strategy:
                raise ValueError(
                    f"function `{routing_strategy_name}` not in routing_strategy {list(routing_strategy.keys())}"
                )

            if "links" not in routes_dict:
                raise ValueError(
                    f"You need to define links for the `{route_alias}` route")
            links_dict = routes_dict["links"]

            for port_src_string, port_dst_string in links_dict.items():

                if ":" in port_src_string:
                    src, src0, src1 = [
                        s.strip() for s in port_src_string.split(":")
                    ]
                    dst, dst0, dst1 = [
                        s.strip() for s in port_dst_string.split(":")
                    ]
                    instance_src_name, port_src_name = [
                        s.strip() for s in src.split(",")
                    ]
                    instance_dst_name, port_dst_name = [
                        s.strip() for s in dst.split(",")
                    ]

                    src0 = int(src0)
                    src1 = int(src1)
                    dst0 = int(dst0)
                    dst1 = int(dst1)

                    if src1 > src0:
                        ports1names = [
                            f"{port_src_name}{i}"
                            for i in range(src0, src1 + 1, 1)
                        ]
                    else:
                        ports1names = [
                            f"{port_src_name}{i}"
                            for i in range(src0, src1 - 1, -1)
                        ]

                    if dst1 > dst0:
                        ports2names = [
                            f"{port_dst_name}{i}"
                            for i in range(dst0, dst1 + 1, 1)
                        ]
                    else:
                        ports2names = [
                            f"{port_dst_name}{i}"
                            for i in range(dst0, dst1 - 1, -1)
                        ]

                    assert len(ports1names) == len(ports2names)
                    route_names += [
                        f"{instance_src_name},{i}:{instance_dst_name},{j}"
                        for i, j in zip(ports1names, ports2names)
                    ]

                    instance_src = instances[instance_src_name]
                    instance_dst = instances[instance_dst_name]

                    for port_src_name in ports1names:
                        assert port_src_name in instance_src.ports, (
                            f"{port_src_name} not in {list(instance_src.ports.keys())}"
                            f"for {instance_src_name} ")
                        ports1.append(instance_src.ports[port_src_name])

                    for port_dst_name in ports2names:
                        assert port_dst_name in instance_dst.ports, (
                            f"{port_dst_name} not in {list(instance_dst.ports.keys())}"
                            f"for {instance_dst_name}")
                        ports2.append(instance_dst.ports[port_dst_name])

                    # print(ports1)
                    # print(ports2)
                    # print(route_names)

                else:
                    instance_src_name, port_src_name = port_src_string.split(
                        ",")
                    instance_dst_name, port_dst_name = port_dst_string.split(
                        ",")

                    instance_src_name = instance_src_name.strip()
                    instance_dst_name = instance_dst_name.strip()
                    port_src_name = port_src_name.strip()
                    port_dst_name = port_dst_name.strip()

                    assert (
                        instance_src_name in instances
                    ), f"{instance_src_name} not in {list(instances.keys())}"
                    assert (
                        instance_dst_name in instances
                    ), f"{instance_dst_name} not in {list(instances.keys())}"

                    instance_src = instances[instance_src_name]
                    instance_dst = instances[instance_dst_name]

                    assert port_src_name in instance_src.ports, (
                        f"{port_src_name} not in {list(instance_src.ports.keys())} for"
                        f" {instance_src_name} ")
                    assert port_dst_name in instance_dst.ports, (
                        f"{port_dst_name} not in {list(instance_dst.ports.keys())} for"
                        f" {instance_dst_name}")

                    ports1.append(instance_src.ports[port_src_name])
                    ports2.append(instance_dst.ports[port_dst_name])
                    route_name = f"{port_src_string}:{port_dst_string}"
                    route_names.append(route_name)

            routing_function = routing_strategy[routing_strategy_name]
            route_or_route_list = routing_function(
                ports1=ports1,
                ports2=ports2,
                **settings,
            )

            # FIXME, be more consistent
            if isinstance(route_or_route_list, list):
                for route_name, route_dict in zip(route_names,
                                                  route_or_route_list):
                    c.add(route_dict.references)
                    routes[route_name] = route_dict.length
            elif isinstance(route_or_route_list, Route):
                c.add(route_or_route_list.references)
                routes[route_name] = route_or_route_list.length
            else:
                raise ValueError(
                    f"{route_or_route_list} needs to be a Route or a list")

    if ports_conf:
        assert hasattr(ports_conf, "items"), f"{ports_conf} needs to be a dict"
        for port_name, instance_comma_port in ports_conf.items():
            instance_name, instance_port_name = instance_comma_port.split(",")
            instance_name = instance_name.strip()
            instance_port_name = instance_port_name.strip()
            assert (instance_name in instances
                    ), f"{instance_name} not in {list(instances.keys())}"
            instance = instances[instance_name]
            assert instance_port_name in instance.ports, (
                f"{instance_port_name} not in {list(instance.ports.keys())} for"
                f" {instance_name} ")
            c.add_port(port_name, port=instance.ports[instance_port_name])
    c.routes = routes
    c.instances = instances
    return c
コード例 #25
0
def _sample_route_sides() -> Component:
    c = Component()
    _dummy_t = _sample_route_side()
    sides = ["north", "south", "east", "west"]
    positions = [(0, 0), (400, 0), (400, 400), (0, 400)]
    for pos, side in zip(positions, sides):
        dummy_ref = _dummy_t.ref(position=pos)
        c.add(dummy_ref)
        routes, ports = route_ports_to_side(dummy_ref, side, layer=(2, 0))
        for route in routes:
            c.add(route.references)
        for i, p in enumerate(ports):
            c.add_port(name=f"{side[0]}{i}", port=p)
    return c


if __name__ == "__main__":
    c = Component()
    _dummy_t = _sample_route_side()
    sides = ["north", "south", "east", "west"]
    positions = [(0, 0), (400, 0), (400, 400), (0, 400)]
    for pos, side in zip(positions, sides):
        dummy_ref = _dummy_t.ref(position=pos)
        c.add(dummy_ref)
        routes, ports = route_ports_to_side(dummy_ref, side, layer=(2, 0))
        for route in routes:
            c.add(route.references)
        for i, p in enumerate(ports):
            c.add_port(name=f"{side[0]}{i}", port=p)
    c.show()
コード例 #26
0
def array_with_fanout(
    component: ComponentOrFactory = pad,
    columns: int = 3,
    pitch: float = 150.0,
    waveguide_pitch: float = 10.0,
    start_straight_length: float = 5.0,
    end_straight_length: float = 40.0,
    radius: float = 5.0,
    component_port_name: str = "e4",
    bend: ComponentFactory = bend_euler,
    bend_port_name1: Optional[str] = None,
    bend_port_name2: Optional[str] = None,
    cross_section: CrossSectionFactory = strip,
    **kwargs,
) -> Component:
    """Returns an array of components in X axis
    with fanout waveguides facing west

    Args:
        component: to replicate.
        columns: number of components.
        pitch: for waveguides.
        waveguide_pitch: for output waveguides.
        start_straight_length: length of the start of the straight
        end_straight_length: lenght of the straight at the end
        radius: bend radius
        component_port_name:
        bend:
        bend_port_name1:
        bend_port_name2:
        cross_section: cross_section definition
        kwargs: cross_section settings
    """
    c = Component()
    component = component() if callable(component) else component
    bend = bend(radius=radius, cross_section=cross_section, **kwargs)

    bend_ports = bend.get_ports_list()
    bend_ports = sort_ports_x(bend_ports)
    bend_ports.reverse()
    bend_port_name1 = bend_port_name1 or bend_ports[0].name
    bend_port_name2 = bend_port_name2 or bend_ports[1].name

    for col in range(columns):
        ref = component.ref()
        ref.x = col * pitch
        c.add(ref)
        ylength = col * waveguide_pitch + start_straight_length
        xlength = col * pitch + end_straight_length
        straight_ref = c << straight(
            length=ylength, cross_section=cross_section, **kwargs)
        port_s1, port_s2 = straight_ref.get_ports_list()

        straight_ref.connect(port_s2.name, ref.ports[component_port_name])

        bend_ref = c.add_ref(bend)
        bend_ref.connect(bend_port_name1, straight_ref.ports[port_s1.name])
        straightx_ref = c << straight(
            length=xlength, cross_section=cross_section, **kwargs)
        straightx_ref.connect(port_s2.name, bend_ref.ports[bend_port_name2])
        c.add_port(f"W_{col}", port=straightx_ref.ports[port_s1.name])
    auto_rename_ports(c)
    return c
コード例 #27
0
def coupler_asymmetric(
    bend: ComponentFactory = bend_s,
    straight: ComponentFactory = straight_function,
    gap: float = 0.234,
    dy: float = 5.0,
    dx: float = 10.0,
    cross_section: CrossSectionFactory = strip,
    **kwargs
) -> Component:
    """bend coupled to straight waveguide

    Args:
        bend:
        straight: straight library
        gap: um
        dy: port to port vertical spacing
        dx: bend length in x direction
        cross_section:
        **kwargs: cross_section settings

    .. code::

                        dx
                     |-----|
                      _____ o2
                     /         |
               _____/          |
         gap o1____________    |  dy
                            o3

    """
    x = cross_section(**kwargs)
    width = x.info["width"]
    bend_component = (
        bend(size=(dx, dy - gap - width), cross_section=cross_section, **kwargs)
        if callable(bend)
        else bend
    )
    wg = (
        straight(cross_section=cross_section, **kwargs)
        if callable(straight)
        else straight
    )

    w = bend_component.ports["o1"].width
    y = (w + gap) / 2

    c = Component()
    wg = wg.ref(position=(0, y), port_id="o1")
    bottom_bend = bend_component.ref(position=(0, -y), port_id="o1", v_mirror=True)

    c.add(wg)
    c.add(bottom_bend)

    # Using absorb here to have a flat cell and avoid
    # to have deeper hierarchy than needed
    c.absorb(wg)
    c.absorb(bottom_bend)

    port_width = 2 * w + gap
    c.add_port(name="o1", midpoint=[0, 0], width=port_width, orientation=180)
    c.add_port(port=bottom_bend.ports["o2"], name="o3")
    c.add_port(port=wg.ports["o2"], name="o2")

    return c
コード例 #28
0
def add_grating_couplers_with_loopback_fiber_single(
    component: Component,
    grating_coupler: ComponentFactory = grating_coupler_te,
    layer_label: Tuple[int, int] = (200, 0),
    gc_port_name: str = "o1",
    get_input_labels_function: Callable[..., List[Label]] = get_input_labels,
    get_input_label_text_loopback_function: Callable = get_input_label_text_loopback,
    select_ports: Callable = select_ports_optical,
    with_loopback: bool = True,
    cross_section: CrossSectionFactory = strip,
    component_name: Optional[str] = None,
    fiber_spacing: float = 50.0,
    loopback_xspacing: float = 5.0,
    straight: ComponentFactory = straight_function,
    rotation: int = 90,
) -> Component:
    """
    Returns component with all ports terminated with grating couplers

    Args:
        component:
        grating_coupler:
        layer_label:
        gc_port_name:
        get_input_label_text_loopback_function:
        with_loopback: adds a reference loopback
        rotation: 90 for North South devices, 0 for East-West

    """

    c = Component()
    c.component = component
    c.add_ref(component)
    grating_coupler = (
        grating_coupler() if callable(grating_coupler) else grating_coupler
    )

    component_name = component_name or component.info_child.name

    io_gratings = []
    optical_ports = select_ports(component.ports)
    optical_ports = list(optical_ports.values())
    for port in optical_ports:
        gc_ref = grating_coupler.ref()
        gc_port = gc_ref.ports[gc_port_name]
        gc_ref.connect(gc_port, port)
        io_gratings.append(gc_ref)
        c.add(gc_ref)

    labels = get_input_labels_function(
        io_gratings,
        list(component.ports.values()),
        component_name=component_name,
        layer_label=layer_label,
        gc_port_name=gc_port_name,
    )
    c.add(labels)

    p2 = optical_ports[0]
    p1 = optical_ports[-1]

    if with_loopback:

        if rotation in [0, 180]:
            length = abs(p2.x - p1.x)
            wg = c << straight(length=length, cross_section=cross_section)
            wg.rotate(rotation)
            wg.xmin = p2.x
            wg.ymin = c.ymax + grating_coupler.ysize / 2 + loopback_xspacing
        else:
            length = abs(p2.y - p1.y)
            wg = c << straight(length=length, cross_section=cross_section)
            wg.rotate(rotation)
            wg.ymin = p1.y
            wg.xmin = c.xmax + grating_coupler.ysize / 2 + loopback_xspacing

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

        port = wg.ports["o2"]
        text = get_input_label_text_loopback_function(
            port=port, gc=grating_coupler, gc_index=0, component_name=component_name
        )

        c.add_label(
            text=text,
            position=port.midpoint,
            anchor="o",
            layer=layer_label,
        )

        port = wg.ports["o1"]
        text = get_input_label_text_loopback_function(
            port=port, gc=grating_coupler, gc_index=1, component_name=component_name
        )
        c.add_label(
            text=text,
            position=port.midpoint,
            anchor="o",
            layer=layer_label,
        )

    c.copy_child_info(component)
    return c
コード例 #29
0
def test_connect_corner(config: str,
                        data_regression: DataRegressionFixture,
                        check: bool = True,
                        N=6) -> Component:
    d = 10.0
    sep = 5.0
    c = Component(name=f"test_connect_corner_{config}")

    if config in ["A", "B"]:
        a = 100.0
        ports_A_TR = [
            Port("A_TR_{}".format(i), (d, a / 2 + i * sep), 0.5, 0)
            for i in range(N)
        ]
        ports_A_TL = [
            Port("A_TL_{}".format(i), (-d, a / 2 + i * sep), 0.5, 180)
            for i in range(N)
        ]
        ports_A_BR = [
            Port("A_BR_{}".format(i), (d, -a / 2 - i * sep), 0.5, 0)
            for i in range(N)
        ]
        ports_A_BL = [
            Port("A_BL_{}".format(i), (-d, -a / 2 - i * sep), 0.5, 180)
            for i in range(N)
        ]

        ports_A = [ports_A_TR, ports_A_TL, ports_A_BR, ports_A_BL]

        ports_B_TR = [
            Port("B_TR_{}".format(i), (a / 2 + i * sep, d), 0.5, 90)
            for i in range(N)
        ]
        ports_B_TL = [
            Port("B_TL_{}".format(i), (-a / 2 - i * sep, d), 0.5, 90)
            for i in range(N)
        ]
        ports_B_BR = [
            Port("B_BR_{}".format(i), (a / 2 + i * sep, -d), 0.5, 270)
            for i in range(N)
        ]
        ports_B_BL = [
            Port("B_BL_{}".format(i), (-a / 2 - i * sep, -d), 0.5, 270)
            for i in range(N)
        ]

        ports_B = [ports_B_TR, ports_B_TL, ports_B_BR, ports_B_BL]

    elif config in ["C", "D"]:
        a = N * sep + 2 * d
        ports_A_TR = [
            Port("A_TR_{}".format(i), (a, d + i * sep), 0.5, 0)
            for i in range(N)
        ]
        ports_A_TL = [
            Port("A_TL_{}".format(i), (-a, d + i * sep), 0.5, 180)
            for i in range(N)
        ]
        ports_A_BR = [
            Port("A_BR_{}".format(i), (a, -d - i * sep), 0.5, 0)
            for i in range(N)
        ]
        ports_A_BL = [
            Port("A_BL_{}".format(i), (-a, -d - i * sep), 0.5, 180)
            for i in range(N)
        ]

        ports_A = [ports_A_TR, ports_A_TL, ports_A_BR, ports_A_BL]

        ports_B_TR = [
            Port("B_TR_{}".format(i), (d + i * sep, a), 0.5, 90)
            for i in range(N)
        ]
        ports_B_TL = [
            Port("B_TL_{}".format(i), (-d - i * sep, a), 0.5, 90)
            for i in range(N)
        ]
        ports_B_BR = [
            Port("B_BR_{}".format(i), (d + i * sep, -a), 0.5, 270)
            for i in range(N)
        ]
        ports_B_BL = [
            Port("B_BL_{}".format(i), (-d - i * sep, -a), 0.5, 270)
            for i in range(N)
        ]

        ports_B = [ports_B_TR, ports_B_TL, ports_B_BR, ports_B_BL]

    lengths = {}
    i = 0
    if config in ["A", "C"]:
        for ports1, ports2 in zip(ports_A, ports_B):
            routes = get_bundle(ports1, ports2)
            for route in routes:
                c.add(route.references)
                lengths[i] = route.length
                i += 1

    elif config in ["B", "D"]:
        for ports1, ports2 in zip(ports_A, ports_B):
            routes = get_bundle(ports2, ports1)
            for route in routes:
                c.add(route.references)
                lengths[i] = route.length
                i += 1

    if check:
        data_regression.check(lengths)
        difftest(c)
    return c
コード例 #30
0
def add_grating_couplers_with_loopback_fiber_array(
    component: Component,
    grating_coupler: ComponentFactory = grating_coupler_te,
    excluded_ports: None = None,
    grating_separation: float = 127.0,
    bend_radius_loopback: Optional[float] = None,
    gc_port_name: str = "o1",
    gc_rotation: int = -90,
    straight_separation: float = 5.0,
    bend: ComponentFactory = bend_euler,
    straight: ComponentFactory = straight_function,
    layer_label: Tuple[int, int] = (200, 0),
    layer_label_loopback: Optional[Tuple[int, int]] = None,
    component_name: Optional[str] = None,
    with_loopback: bool = True,
    nlabels_loopback: int = 2,
    get_input_labels_function: Callable = get_input_labels,
    cross_section: CrossSectionFactory = strip,
    select_ports: Callable = select_ports_optical,
    **kwargs,
) -> Component:
    """Returns a component with grating_couplers and loopback.

    Args:
        component:
        grating_coupler:
        excluded_ports:
        grating_separation:
        bend_radius_loopback:
        gc_port_name:
        gc_rotation:
        straight_separation:
        bend:
        straight:
        layer_label:
        component_name:
        with_loopback: If True, add compact loopback alignment ports
        nlabels_loopback: number of ports to label (0: no labels, 1: first port, 2: both ports)
        cross_section:
        **kwargs: cross_section settings
    """
    x = cross_section(**kwargs)
    bend_radius_loopback = bend_radius_loopback or x.info["radius"]
    excluded_ports = excluded_ports or []
    gc = grating_coupler() if callable(grating_coupler) else grating_coupler

    direction = "S"
    component_name = component_name or component.info_child.name
    c = Component()
    c.component = component

    c.add_ref(component)

    # Find grating port name if not specified
    if gc_port_name is None:
        gc_port_name = list(gc.ports.values())[0].name

    # List the optical ports to connect
    optical_ports = select_ports(component.ports)
    optical_ports = list(optical_ports.values())
    optical_ports = [p for p in optical_ports if p.name not in excluded_ports]
    optical_ports = direction_ports_from_list_ports(optical_ports)[direction]

    # Check if the ports are equally spaced
    grating_separation_extracted = check_ports_have_equal_spacing(optical_ports)
    if grating_separation_extracted != grating_separation:
        raise ValueError(
            "Grating separation must be {}. Got {}".format(
                grating_separation, grating_separation_extracted
            )
        )

    # Add grating references
    references = []
    for port in optical_ports:
        gc_ref = c.add_ref(gc)
        gc_ref.connect(gc.ports[gc_port_name].name, port)
        references += [gc_ref]

    labels = get_input_labels_function(
        io_gratings=references,
        ordered_ports=optical_ports,
        component_name=component_name,
        layer_label=layer_label,
        gc_port_name=gc_port_name,
    )
    c.add(labels)

    if with_loopback:
        y0 = references[0].ports[gc_port_name].y
        xs = [p.x for p in optical_ports]
        x0 = min(xs) - grating_separation
        x1 = max(xs) + grating_separation

        gca1, gca2 = [
            gc.ref(position=(x, y0), rotation=gc_rotation, port_id=gc_port_name)
            for x in [x0, x1]
        ]

        gsi = gc.size_info
        port0 = gca1.ports[gc_port_name]
        port1 = gca2.ports[gc_port_name]
        p0 = port0.position
        p1 = port1.position
        a = bend_radius_loopback + 0.5
        b = max(2 * a, grating_separation / 2)
        y_bot_align_route = -gsi.width - straight_separation

        points = np.array(
            [
                p0,
                p0 + (0, a),
                p0 + (b, a),
                p0 + (b, y_bot_align_route),
                p1 + (-b, y_bot_align_route),
                p1 + (-b, a),
                p1 + (0, a),
                p1,
            ]
        )
        bend90 = bend(
            radius=bend_radius_loopback, cross_section=cross_section, **kwargs
        )
        loopback_route = round_corners(
            points=points,
            bend=bend90,
            straight=straight,
            cross_section=cross_section,
            **kwargs,
        )
        c.add([gca1, gca2])
        c.add(loopback_route.references)

        component_name_loopback = f"loopback_{component_name}"
        if nlabels_loopback == 1:
            io_gratings_loopback = [gca1]
            ordered_ports_loopback = [port0]
        if nlabels_loopback == 2:
            io_gratings_loopback = [gca1, gca2]
            ordered_ports_loopback = [port0, port1]
        if nlabels_loopback == 0:
            pass
        elif 0 < nlabels_loopback <= 2:
            c.add(
                get_input_labels_function(
                    io_gratings=io_gratings_loopback,
                    ordered_ports=ordered_ports_loopback,
                    component_name=component_name_loopback,
                    layer_label=layer_label_loopback or layer_label,
                    gc_port_name=gc_port_name,
                )
            )
        else:
            raise ValueError(
                f"Invalid nlabels_loopback = {nlabels_loopback}, "
                "valid (0: no labels, 1: first port, 2: both ports2)"
            )
    c.copy_child_info(component)
    return c