Ejemplo n.º 1
0
def staircase(
    bend90: ComponentOrFactory = bend_euler,
    length_v: float = 5.0,
    length_h: float = 5.0,
    rows: int = 4,
    straight: ComponentFactory = straight_function,
) -> Component:
    bend90 = gf.call_if_func(bend90)

    wgh = straight(length=length_h, width=bend90.ports["o1"].width)
    wgv = straight(length=length_v, width=bend90.ports["o1"].width)

    # Define a map between symbols and (component, input port, output port)
    symbol_to_component = {
        "A": (bend90, "o1", "o2"),
        "B": (bend90, "o2", "o1"),
        "-": (wgh, "o1", "o2"),
        "|": (wgv, "o1", "o2"),
    }

    # Generate the sequence of staircases
    s = "-A|B" * rows + "-"

    c = component_sequence(sequence=s,
                           symbol_to_component=symbol_to_component,
                           start_orientation=0)
    c.info.n_bends = 2 * rows
    return c
Ejemplo n.º 2
0
def coupler_full(length: float = 40.0,
                 gap: float = 0.5,
                 dw: float = 0.1,
                 angle: float = np.pi / 6,
                 parity: int = 1,
                 port: Tuple[int, int] = (0, 0),
                 direction: str = "EAST",
                 waveguide_template: ComponentFactory = strip,
                 **kwargs) -> Component:
    """Adiabatic Full Coupler.
    Design based on asymmetric adiabatic full coupler designs, such as the one reported
    in 'Integrated Optic Adiabatic Devices on Silicon' by Y. Shani, et al
    (IEEE Journal of Quantum Electronics, Vol. 27, No. 3 March 1991).

    Region I is the first half of the input S-bend straight where the
    input straights widths taper by +dw and -dw,
    Region II is the second half of the S-bend straight with constant,
    unbalanced widths,
    Region III is the coupling region where the straights from unbalanced widths to
    balanced widths to reverse polarity unbalanced widths,
    Region IV is the fixed width straight that curves away from the coupling region,
    Region V is the final curve where the straights taper back to the regular width
    specified in the straight template.

    Args:
       length: Length of the coupling region.
       gap: Distance between the two straights.
       dw: Change in straight width. Top arm tapers to width - dw, bottom to width - dw.
       angle: Angle in radians at which the straight bends towards the coupling region.
       parity (integer -1 or 1): If -1, mirror-flips the structure so that input port
        is actually the *bottom* port.
       port: Cartesian coordinate for input port (AT TOP if parity=1, AT BOTTOM if parity=-1).
       direction: Direction that the component points *towards*,
        can be of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians).
       waveguide_template: function that returns Picwriter WaveguideTemplate object

    Keyword Args:
       wg_width: 0.5
       wg_layer: gf.LAYER.WG[0]
       wg_datatype: gf.LAYER.WG[1]
       clad_layer: gf.LAYER.WGCLAD[0]
       clad_datatype: gf.LAYER.WGCLAD[1]
       bend_radius: 10
       cladding_offset: 3

    """

    c = pc.FullCoupler(
        gf.call_if_func(waveguide_template, **kwargs),
        length=length,
        gap=gap,
        dw=dw,
        angle=angle,
        parity=parity,
        port=port,
        direction=direction,
    )

    return gf.read.from_picwriter(c)
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
def cutback_bend(bend90: ComponentOrFactory = bend_euler,
                 straight_length: float = 5.0,
                 rows: int = 6,
                 columns: int = 5,
                 straight: ComponentFactory = straight_function,
                 **kwargs):
    """Deprecated! use cutback_bend90 instead,
    which has smaller footprint

    Args:
        bend90:
        straight_length:
        rows:
        columns:
        straight: function for straight

    keyword args:
        cross_section:


    .. code::

        this is a column
            _
          _|
        _|

        _ this is a row

    """

    bend90 = gf.call_if_func(bend90)
    straightx = straight(length=straight_length,
                         width=bend90.ports["o1"].width,
                         **kwargs)

    # Define a map between symbols and (component, input port, output port)
    symbol_to_component = {
        "A": (bend90, "o1", "o2"),
        "B": (bend90, "o2", "o1"),
        "S": (straightx, "o1", "o2"),
    }

    # Generate the sequence of staircases
    s = ""
    for i in range(columns):
        s += "ASBS" * rows
        s += "ASAS" if i % 2 == 0 else "BSBS"
    s = s[:-4]

    c = component_sequence(sequence=s,
                           symbol_to_component=symbol_to_component,
                           start_orientation=90)
    c.info.n_bends = rows * columns * 2 + columns * 2 - 2
    return c
Ejemplo n.º 5
0
def splitter_chain(
    splitter: ComponentFactory = mmi1x2,
    columns: int = 3,
    bend: ComponentFactory = bend_s,
) -> Component:
    """Chain of splitters

    Args:
        splitter: splitter to chain
        columns: number of splitters to chain
        bend: bend to connect splitters

    .. code::

                 __o5
              __|
           __|  |__o4
      o1 _|  |__o3
          |__o2


           __o2
      o1 _|
          |__o3

    """
    c = gf.Component()
    splitter_component = gf.call_if_func(splitter)
    cref = c.add_ref(splitter_component)

    splitter_ports_east = cref.get_ports_list(port_type="optical", orientation=0)
    e1_port_name = splitter_ports_east[0].name
    e0_port_name = splitter_ports_east[1].name

    bend = bend() if callable(bend) else bend
    c.add_port(name="o1", port=cref.ports["o1"])
    c.add_port(name="o2", port=cref.ports[e0_port_name])

    for i in range(1, columns):
        bref = c.add_ref(bend)
        bref.connect(port="o1", destination=cref.ports[e1_port_name])

        cref = c.add_ref(splitter_component)

        cref.connect(port="o1", destination=bref.ports["o2"])
        c.add_port(name=f"o{i+2}", port=cref.ports[e0_port_name])

    c.add_port(name=f"o{i+3}", port=cref.ports[e1_port_name])
    c.copy_child_info(splitter_component)
    return c
Ejemplo n.º 6
0
def cutback_bend180(
    bend180: ComponentOrFactory = bend_euler180,
    straight_length: float = 5.0,
    rows: int = 6,
    columns: int = 6,
    spacing: int = 3,
    straight: ComponentFactory = straight_function,
) -> Component:
    """

    .. code::

          _
        _| |_  this is a row

        _ this is a column

    """
    bend180 = gf.call_if_func(bend180)

    straightx = straight(length=straight_length,
                         width=bend180.ports["o1"].width)
    wg_vertical = straight(
        length=2 * bend180.size_info.width + straight_length + spacing,
        width=bend180.ports["o1"].width,
    )

    # Define a map between symbols and (component, input port, output port)
    symbol_to_component = {
        "D": (bend180, "o1", "o2"),
        "C": (bend180, "o2", "o1"),
        "-": (straightx, "o1", "o2"),
        "|": (wg_vertical, "o1", "o2"),
    }

    # Generate the sequence of staircases
    s = ""
    for i in range(columns):
        if i % 2 == 0:  # even row
            s += "D-C-" * rows + "|"
        else:
            s += "C-D-" * rows + "|"
    s = s[:-1]

    # Create the component from the sequence
    c = component_sequence(sequence=s,
                           symbol_to_component=symbol_to_component,
                           start_orientation=0)
    c.info.n_bends = rows * columns * 2 + columns * 2 - 2
    return c
Ejemplo n.º 7
0
def loop_mirror_with_delay(loop_mirror=loop_mirror, spiral=spiral_external_io):
    """
    delay = 13e-12
    # delay = length/speed
    # length=delay*speed
    13e-12*3e8/4.2*1e6

    """
    c = Component()
    lm = c << gf.call_if_func(loop_mirror)
    s = c << spiral_external_io()

    lm.connect("o1", s.ports["o1"])
    return c
Ejemplo n.º 8
0
def cutback_bend90(
    bend90: ComponentOrFactory = bend_euler,
    straight_length: float = 5.0,
    rows: int = 6,
    columns: int = 6,
    spacing: int = 5,
    straight: ComponentFactory = straight_function,
) -> Component:
    """

    .. code::

           _
        |_| |

    """
    bend90 = gf.call_if_func(bend90)
    straightx = straight(length=straight_length,
                         width=bend90.ports["o1"].width)

    straight_length = 2 * _get_bend_size(bend90) + spacing + straight_length
    straighty = straight(
        length=straight_length,
        width=bend90.ports["o1"].width,
    )
    # Define a map between symbols and (component, input port, output port)
    symbol_to_component = {
        "A": (bend90, "o1", "o2"),
        "B": (bend90, "o2", "o1"),
        "-": (straightx, "o1", "o2"),
        "|": (straighty, "o1", "o2"),
    }

    # Generate the sequence of staircases
    s = ""
    for i in range(columns):
        if i % 2 == 0:  # even row
            s += "A-A-B-B-" * rows + "|"
        else:
            s += "B-B-A-A-" * rows + "|"
    s = s[:-1]

    # Create the component from the sequence
    c = component_sequence(sequence=s,
                           symbol_to_component=symbol_to_component,
                           start_orientation=0)
    c.info.n_bends = rows * columns * 4
    return c
Ejemplo n.º 9
0
def spiral(port_spacing: float = 500.0,
           length: float = 10e3,
           spacing: Optional[float] = None,
           parity: int = 1,
           port: Tuple[int, int] = (0, 0),
           direction: str = "WEST",
           waveguide_template: ComponentFactory = strip,
           layer: Layer = gf.LAYER.WG,
           layer_cladding: Layer = gf.LAYER.WGCLAD,
           cladding_offset: float = 3.0,
           wg_width: float = 0.5,
           radius: float = 10.0,
           **kwargs) -> Component:
    """Picwriter Spiral

    Args:
        port_spacing: distance between input/output ports
        length: spiral length (um)
        spacing: distance between parallel straights
        parity: If 1 spiral on right side, if -1 spiral on left side (mirror flip)
        port: Cartesian coordinate of the input port
        direction: NORTH, WEST, SOUTH, EAST  or angle in radians
        waveguide_template (WaveguideTemplate): Picwriter WaveguideTemplate function
        layer: core layer
        layer_cladding: cladding layer
        cladding_offset: distance from core to cladding
        wg_width: 0.5
        radius: 10
        kwargs: cross_section settings

    """
    c = pc.Spiral(
        gf.call_if_func(waveguide_template,
                        wg_width=wg_width,
                        radius=radius,
                        layer=layer,
                        layer_cladding=layer_cladding,
                        cladding_offset=cladding_offset,
                        **kwargs),
        width=port_spacing,
        length=length,
        spacing=spacing,
        parity=parity,
        port=port,
        direction=direction,
    )
    return gf.read.from_picwriter(c, port_layer=layer)
Ejemplo n.º 10
0
def splitter_chain(
    splitter: ComponentFactory = mmi1x2,
    n_devices: int = 3,
    bend: ComponentFactory = bend_s,
    **kwargs,
) -> Component:
    """Chain of splitters

    .. code::

                __5
             __|
          __|  |__4
      1 _|  |__3
         |__2


          __E1
      1 _|
         |__E0

    """
    c = gf.Component()
    splitter_component = gf.call_if_func(splitter, **kwargs)
    cref = c.add_ref(splitter_component)

    splitter_ports_east = cref.get_ports_list(port_type="optical",
                                              orientation=0)
    e1_port_name = splitter_ports_east[0].name
    e0_port_name = splitter_ports_east[1].name

    bend = bend() if callable(bend) else bend
    c.add_port(name="o1", port=cref.ports["o1"])
    c.add_port(name="o2", port=cref.ports[e0_port_name])

    for i in range(1, n_devices):
        bref = c.add_ref(bend)
        bref.connect(port="o1", destination=cref.ports[e1_port_name])

        cref = c.add_ref(splitter_component)

        cref.connect(port="o1", destination=bref.ports["o2"])
        c.add_port(name=f"o{i+2}", port=cref.ports[e0_port_name])

    c.add_port(name=f"o{i+3}", port=cref.ports[e1_port_name])
    c.copy_child_info(splitter_component)
    return c
Ejemplo n.º 11
0
def disk(radius: float = 10.0,
         gap: float = 0.2,
         wrap_angle_deg: float = 180.0,
         parity: int = 1,
         port: Tuple[int, int] = (0, 0),
         direction: str = "EAST",
         waveguide_template: ComponentFactory = strip,
         **kwargs) -> Component:
    """Disk Resonator

    Args:
       radius: disk resonator radius
       gap: Distance between the bus straight and resonator
       wrap_angle : Angle in degrees between 0 and 180
        determines how much the bus straight wraps along the resonator.
        0 corresponds to a straight bus straight,
        180 corresponds to a bus straight wrapped around half of the resonator.
       parity (1 or -1): 1, resonator left from bus straight, -1 resonator to the right
       port (tuple): Cartesian coordinate of the input port (x1, y1)
       direction: Direction that the component will point *towards*, can be of type
        'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians)
       waveguide_template (WaveguideTemplate): Picwriter WaveguideTemplate object


    Other Parameters:
       wg_width: 0.5
       wg_layer: gf.LAYER.WG[0]
       wg_datatype: gf.LAYER.WG[1]
       clad_layer: gf.LAYER.WGCLAD[0]
       clad_datatype: gf.LAYER.WGCLAD[1]
       bend_radius: 10
       cladding_offset: 3

    """

    c = pc.Disk(
        gf.call_if_func(strip, **kwargs),
        radius=radius,
        coupling_gap=gap,
        wrap_angle=wrap_angle_deg * np.pi / 180,
        parity=parity,
        port=port,
        direction=direction,
    )

    return gf.read.from_picwriter(c)
Ejemplo n.º 12
0
def get_ports_and_tapers(
    component: Component,
    taper: ComponentFactory = taper_function,
    select_ports: Optional[Callable] = select_ports_optical,
) -> Tuple[List[Port], List[ComponentReference]]:
    """returns ports and taper elements for a component"""
    elements = []

    taper = gf.call_if_func(taper)
    ports = select_ports(component.ports) if select_ports else component.ports

    for port in component.ports.copy().values():
        if port.name in ports.key():
            taper_ref = taper.ref()
            taper_ref.connect(taper_ref.ports["o2"].name, port)
            elements.append(taper_ref)
            ports.append(taper_ref.ports["o1"])
    return ports, elements
Ejemplo n.º 13
0
def route_fiber_array(
    component: Component,
    fiber_spacing: float = TECH.fiber_array_spacing,
    grating_coupler: ComponentOrFactory = grating_coupler_te,
    bend: ComponentFactory = bend_euler,
    straight: ComponentFactory = straight,
    taper_factory: ComponentFactory = taper,
    fanout_length: Optional[float] = None,
    max_y0_optical: None = None,
    with_loopback: bool = True,
    nlabels_loopback: int = 2,
    straight_separation: float = 6.0,
    straight_to_grating_spacing: float = 5.0,
    optical_routing_type: Optional[int] = None,
    connected_port_names: None = None,
    nb_optical_ports_lines: int = 1,
    force_manhattan: bool = False,
    excluded_ports: List[Any] = None,
    grating_indices: None = None,
    route_filter: Callable = get_route_from_waypoints,
    gc_port_name: str = "o1",
    gc_rotation: int = -90,
    layer_label: Optional[Tuple[int, int]] = (66, 0),
    layer_label_loopback: Optional[Tuple[int, int]] = None,
    component_name: Optional[str] = None,
    x_grating_offset: int = 0,
    optical_port_labels: Optional[Tuple[str, ...]] = None,
    get_input_label_text_loopback_function:
    Callable = get_input_label_text_loopback,
    get_input_label_text_function: Callable = get_input_label_text,
    get_input_labels_function: Optional[Callable] = get_input_labels,
    select_ports: Callable = select_ports_optical,
    cross_section: CrossSectionFactory = strip,
    **kwargs,
) -> Tuple[List[Union[ComponentReference, Label]],
           List[List[ComponentReference]], List[Port]]:
    """Returns component I/O elements for adding grating couplers with a fiber array
    Many components are fine with the defaults.

    Args:
        component: The component to connect.
        fiber_spacing: the wanted spacing between the optical I/O
        grating_coupler: grating coupler instance, function or list of functions
        bend:  for bends
        straight: straight
        fanout_length: target distance between gratings and the southest component port.
            If None, automatically calculated.
        max_y0_optical: Maximum y coordinate at which the intermediate optical ports can be set.
            Usually fine to leave at None.
        with_loopback: If True, add compact loopback alignment ports
        nlabels_loopback: number of labels of align ports (0: no labels, 1: first port, 2: both ports2)
        straight_separation: min separation between routing straights
        straight_to_grating_spacing: from align ports
        optical_routing_type: There are three options for optical routing
           * ``0`` is very basic but can be more compact.
            Can also be used in combination with ``connected_port_names``
            or to route some components which otherwise fail with type ``1``.
           * ``1`` is the standard routing.
           * ``2`` uses the optical ports as a guideline for the component's physical size
            (instead of using the actual component size).
            Useful where the component is large due to metal tracks
           * ``None: leads to an automatic decision based on size and number
           of I/O of the component.
        connected_port_names: only for type 0 optical routing.
            Can specify which ports goes to which grating assuming the gratings are ordered from left to right.
            e.g ['N0', 'W1','W0','E0','E1', 'N1' ] or [4,1,7,3]
        nb_optical_ports_lines: number of lines with I/O grating couplers. One line by default.
            WARNING: Only works properly if:
            - nb_optical_ports_lines divides the total number of ports
            - the components have an equal number of inputs and outputs
        force_manhattan: sometimes port linker defaults to an S-bend due to lack of space to do manhattan.
            Force manhattan offsets all the ports to replace the s-bend by a straight link.
            This fails if multiple ports have the same issue
        excluded_ports: ports excluded from routing
        grating_indices: allows to fine skip some grating slots
            e.g [0,1,4,5] will put two gratings separated by the pitch.
            Then there will be two empty grating slots, and after that an additional two gratings.
        route_filter: straight and bend factories
        gc_port_name: grating_coupler port name, where to route straights
        gc_rotation: grating_coupler rotation (deg)
        layer_label: for TM labels
        component_name: name of component
        x_grating_offset: x offset
        optical_port_labels: port labels to route_to_fiber_array
        select_ports: function to select ports for which to add grating couplers
        get_input_label_text_loopback_function: function to get input labels for grating couplers
        get_input_label_text_function

    Returns:
        elements, io_grating_lines, list of ports
    """

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

    assert isinstance(
        radius,
        (int,
         float)), f"radius = {radius} {type (radius)} needs to be int or float"

    component_name = component_name or component.name
    excluded_ports = excluded_ports or []
    if optical_port_labels is None:
        optical_ports = list(select_ports(component.ports).values())
    else:
        optical_ports = [component.ports[lbl] for lbl in optical_port_labels]

    optical_ports = [p for p in optical_ports if p.name not in excluded_ports]
    N = len(optical_ports)
    # optical_ports_labels = [p.name for p in optical_ports]
    # print(optical_ports_labels)
    if N == 0:
        return [], [], 0

    elements = []

    # grating_coupler can either be a component/function
    # or a list of components/functions

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

    assert (gc_port_name in grating_coupler.ports
            ), f"{gc_port_name} not in {list(grating_coupler.ports.keys())}"

    if gc_port_name not in grating_coupler.ports:
        raise ValueError(
            f"{gc_port_name} not in {list(grating_coupler.ports.keys())}")

    # Now:
    # - grating_coupler is a single grating coupler
    # - grating_couplers is a list of grating couplers
    # Define the route filter to apply to connection methods

    bend90 = bend(cross_section=cross_section, **
                  kwargs) if callable(bend) else bend

    dy = abs(bend90.info.dy)

    # `delta_gr_min` Used to avoid crossing between straights in special cases
    # This could happen when abs(x_port - x_grating) <= 2 * radius

    dy = bend90.info.dy
    delta_gr_min = 2 * dy + 1

    offset = (N - 1) * fiber_spacing / 2.0

    # Get the center along x axis
    x_c = round(sum([p.x for p in optical_ports]) / N, 1)
    y_min = component.ymin  # min([p.y for p in optical_ports])

    # Sort the list of optical ports:
    direction_ports = direction_ports_from_list_ports(optical_ports)
    sep = straight_separation

    K = len(optical_ports)
    K = K + 1 if K % 2 else K

    # Set routing type if not specified
    pxs = [p.x for p in optical_ports]
    is_big_component = ((K > 2)
                        or (max(pxs) - min(pxs) > fiber_spacing - delta_gr_min)
                        or (component.xsize > fiber_spacing))
    if optical_routing_type is None:
        if not is_big_component:
            optical_routing_type = 0
        else:
            optical_routing_type = 1

    # choose the default length if the default fanout distance is not set
    def has_p(side):
        return len(direction_ports[side]) > 0

    list_ew_ports_on_sides = [has_p(side) for side in ["E", "W"]]
    list_ns_ports_on_sides = [has_p(side) for side in ["N", "S"]]

    has_ew_ports = any(list_ew_ports_on_sides)
    has_ns_ports = any(list_ns_ports_on_sides)

    is_one_sided_horizontal = False
    for side1, side2 in [("E", "W"), ("W", "E")]:
        if len(direction_ports[side1]) >= 2:
            if all([
                    len(direction_ports[side]) == 0
                    for side in ["N", "S", side2]
            ]):
                is_one_sided_horizontal = True

    # Compute fanout length if not specified
    if fanout_length is None:
        fanout_length = dy + 1.0
        # We need 3 bends in that case to connect the most bottom port to the
        # grating couplers
        if has_ew_ports and is_big_component:
            # print('big')
            fanout_length = max(fanout_length, 3 * dy + 1.0)

        if has_ns_ports or is_one_sided_horizontal:
            # print('one sided')
            fanout_length = max(fanout_length, 2 * dy + 1.0)

        if has_ew_ports and not is_big_component:
            # print('ew_ports')
            fanout_length = max(fanout_length, dy + 1.0)

    fanout_length += dy

    # use x for grating coupler since we rotate it
    y0_optical = y_min - fanout_length - grating_coupler.ports[gc_port_name].x
    y0_optical += -K / 2 * sep
    y0_optical = round(y0_optical, 1)

    if max_y0_optical is not None:
        y0_optical = round(min(max_y0_optical, y0_optical), 1)
    """
     - First connect half of the north ports going from middle of list
    down to first elements
     - then connect west ports (top to bottom)
     - then connect south ports (left to right)
     - then east ports (bottom to top)
     - then second half of the north ports (right to left)

    """
    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

    nb_ports_per_line = N // nb_optical_ports_lines
    grating_coupler_si = grating_coupler.size_info
    y_gr_gap = (K / (nb_optical_ports_lines) + 1) * sep
    gr_coupler_y_sep = grating_coupler_si.height + y_gr_gap + dy

    offset = (nb_ports_per_line - 1) * fiber_spacing / 2 - x_grating_offset
    io_gratings_lines = [
    ]  # [[gr11, gr12, gr13...], [gr21, gr22, gr23...] ...]

    if grating_indices is None:
        grating_indices = list(range(nb_ports_per_line))
    else:
        assert len(grating_indices) == nb_ports_per_line

    for j in range(nb_optical_ports_lines):
        io_gratings = [
            gc.ref(
                position=(
                    x_c - offset + i * fiber_spacing,
                    y0_optical - j * gr_coupler_y_sep,
                ),
                rotation=gc_rotation,
                port_id=gc_port_name,
            ) for i, gc in zip(grating_indices, grating_couplers)
        ]

        io_gratings_lines += [io_gratings[:]]
        ports += [grating.ports[gc_port_name] for grating in io_gratings]

    if optical_routing_type == 0:
        """
        Basic optical routing, typically fine for small components
        No heuristic to avoid collisions between connectors.

        If specified ports to connect in a specific order
        (i.e if connected_port_names is not None and not empty)
        then grab these ports
        """
        if connected_port_names:
            ordered_ports = [component.ports[i] for i in connected_port_names]

        for io_gratings in io_gratings_lines:
            for i in range(N):
                p0 = io_gratings[i].ports[gc_port_name]
                p1 = ordered_ports[i]
                waypoints = generate_manhattan_waypoints(
                    input_port=p0,
                    output_port=p1,
                    bend=bend90,
                    straight=straight,
                    cross_section=cross_section,
                    **kwargs,
                )
                route = route_filter(
                    waypoints=waypoints,
                    bend=bend90,
                    straight=straight,
                    cross_section=cross_section,
                    **kwargs,
                )
                elements.extend(route.references)

    elif optical_routing_type in [1, 2]:
        route = route_south(
            component=component,
            optical_routing_type=optical_routing_type,
            excluded_ports=excluded_ports,
            straight_separation=straight_separation,
            io_gratings_lines=io_gratings_lines,
            gc_port_name=gc_port_name,
            bend=bend,
            straight=straight,
            taper_factory=taper_factory,
            select_ports=select_ports,
            cross_section=cross_section,
            **kwargs,
        )
        elems = route.references
        to_route = route.ports
        elements.extend(elems)

        if force_manhattan:
            """
            1) find the min x_distance between each grating port and
            each component port.
            2) If abs(min distance) < 2* bend radius
                then offset io_gratings by -min_distance
            """
            min_dist = 2 * dy + 10.0
            min_dist_threshold = 2 * dy + 1.0
            for io_gratings in io_gratings_lines:
                for gr in io_gratings:
                    for p in to_route:
                        dist = gr.x - p.x
                        if abs(dist) < abs(min_dist):
                            min_dist = dist
                if abs(min_dist) < min_dist_threshold:
                    for gr in io_gratings:
                        gr.movex(-min_dist)

        # If the array of gratings is too close, adjust its location
        gc_ports_tmp = []
        for io_gratings in io_gratings_lines:
            gc_ports_tmp += [gc.ports[gc_port_name] for gc in io_gratings]
        min_y = get_min_spacing(to_route, gc_ports_tmp, sep=sep, radius=dy)
        delta_y = abs(to_route[0].y - gc_ports_tmp[0].y)

        if min_y > delta_y:
            for io_gratings in io_gratings_lines:
                for gr in io_gratings:
                    gr.translate(0, delta_y - min_y)

        # If we add align ports, we need enough space for the bends
        end_straight_offset = (straight_separation + 5 if with_loopback else
                               x.info.get("min_length", 0.1))
        if len(io_gratings_lines) == 1:
            io_gratings = io_gratings_lines[0]
            gc_ports = [gc.ports[gc_port_name] for gc in io_gratings]
            routes = get_bundle(
                ports1=to_route,
                ports2=gc_ports,
                separation=sep,
                end_straight_offset=end_straight_offset,
                straight=straight,
                bend=bend90,
                cross_section=cross_section,
                **kwargs,
            )
            elements.extend([route.references for route in routes])

        else:
            for io_gratings in io_gratings_lines:
                gc_ports = [gc.ports[gc_port_name] for gc in io_gratings]
                nb_gc_ports = len(io_gratings)
                nb_ports_to_route = len(to_route)
                n0 = nb_ports_to_route / 2
                dn = nb_gc_ports / 2
                routes = get_bundle(
                    ports1=to_route[n0 - dn:n0 + dn],
                    ports2=gc_ports,
                    separation=sep,
                    end_straight_offset=end_straight_offset,
                    bend=bend90,
                    straight=straight,
                    radius=radius,
                    cross_section=cross_section,
                    **kwargs,
                )
                elements.extend([route.references for route in routes])
                del to_route[n0 - dn:n0 + dn]

    if with_loopback:
        gca1, gca2 = [
            grating_coupler.ref(
                position=(
                    x_c - offset + ii * fiber_spacing,
                    io_gratings_lines[-1][0].ports[gc_port_name].y,
                ),
                rotation=gc_rotation,
                port_id=gc_port_name,
            ) for ii in [grating_indices[0] - 1, grating_indices[-1] + 1]
        ]
        port0 = gca1.ports[gc_port_name]
        port1 = gca2.ports[gc_port_name]
        ports.append(port0)
        ports.append(port1)

        p0 = port0.position
        p1 = port1.position

        dy = bend90.info.dy
        dx = max(2 * dy, fiber_spacing / 2)

        gc_east = max([gci.size_info.east for gci in grating_couplers])
        y_bot_align_route = gc_east + straight_to_grating_spacing

        points = [
            p0,
            p0 + (0, dy),
            p0 + (dx, dy),
            p0 + (dx, -y_bot_align_route),
            p1 + (-dx, -y_bot_align_route),
            p1 + (-dx, dy),
            p1 + (0, dy),
            p1,
        ]
        elements.extend([gca1, gca2])

        route = round_corners(
            points=points,
            straight=straight,
            bend=bend90,
            cross_section=cross_section,
            **kwargs,
        )
        elements.extend(route.references)
        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]
        elif nlabels_loopback > 2:
            raise ValueError(
                f"Invalid nlabels_loopback = {nlabels_loopback}, "
                "valid (0: no labels, 1: first port, 2: both ports2)")
        if nlabels_loopback > 0 and get_input_labels_function:
            elements.extend(
                get_input_labels_function(
                    io_gratings=io_gratings_loopback,
                    ordered_ports=ordered_ports_loopback,
                    component_name=component_name,
                    layer_label=layer_label_loopback or layer_label,
                    gc_port_name=gc_port_name,
                    get_input_label_text_function=
                    get_input_label_text_loopback_function,
                ))

    if get_input_labels_function:
        elements.extend(
            get_input_labels_function(
                io_gratings=io_gratings,
                ordered_ports=ordered_ports,
                component_name=component_name,
                layer_label=layer_label,
                gc_port_name=gc_port_name,
                get_input_label_text_function=get_input_label_text_function,
            ))

    return elements, io_gratings_lines, ports
Ejemplo n.º 14
0
def coupler_adiabatic(length1: float = 20.0,
                      length2: float = 50.0,
                      length3: float = 30.0,
                      wg_sep: float = 1.0,
                      input_wg_sep: float = 3.0,
                      output_wg_sep: float = 3.0,
                      dw: float = 0.1,
                      port: Tuple[int, int] = (0, 0),
                      direction: str = "EAST",
                      waveguide_template: ComponentFactory = strip,
                      **kwargs) -> Component:
    """Returns 50/50 adiabatic coupler.
    Design based on asymmetric adiabatic 3dB coupler designs, such as those

    - https://doi.org/10.1364/CLEO.2010.CThAA2,
    - https://doi.org/10.1364/CLEO_SI.2017.SF1I.5
    - https://doi.org/10.1364/CLEO_SI.2018.STh4B.4

    Has input Bezier curves, with poles set to half of the x-length of the S-bend.

    I is the first half of input S-bend where input widths taper by +dw and -dw
    II is the second half of the S-bend straight with constant, unbalanced widths
    III is the region where the two asymmetric straights gradually come together
    IV  straights taper back to the original width at a fixed distance from one another
    IV is the output S-bend straight.

    Args:
        length1: region that gradually brings the two assymetric straights together.
            In this region the straight widths gradually change to be different by `dw`.
        length2: coupling region, where asymmetric straights gradually become the same width.
        length3: output region where the two straights separate.
        wg_sep: Distance between center-to-center in the coupling region (Region 2).
        input_wg_sep: Separation of the two straights at the input, center-to-center.
        output_wg_sep: Separation of the two straights at the output, center-to-center.
        dw: Change in straight width.
            In Region 1, top arm tapers to width+dw/2.0, bottom taper to width-dw/2.0.
        port: coordinate of the input port (top left).
        direction: for component NORTH, WEST, SOUTH, EAST,or angle in radians
        waveguide_template: object or function

    Other Parameters:
       wg_width: 0.5
       wg_layer: gf.LAYER.WG[0]
       wg_datatype: gf.LAYER.WG[1]
       clad_layer: gf.LAYER.WGCLAD[0]
       clad_datatype: gf.LAYER.WGCLAD[1]
       bend_radius: 10
       cladding_offset: 3

    """

    c = pc.AdiabaticCoupler(
        gf.call_if_func(waveguide_template, **kwargs),
        length1=length1,
        length2=length2,
        length3=length3,
        wg_sep=wg_sep,
        input_wg_sep=input_wg_sep,
        output_wg_sep=output_wg_sep,
        dw=dw,
        port=port,
        direction=direction,
    )

    return gf.read.from_picwriter(c)
Ejemplo n.º 15
0
def component_lattice(
    lattice: str = """
        C-X
        CXX
        CXX
        C-X
        """,
    components: Dict[str, Component] = None,
    grid_per_unit: int = 1000,
    name: str = "lattice",
) -> Component:
    """
    Returns a lattice Component of N inputs and outputs with components at given locations
    Columns must have components with the same x spacing between ports input/output ports
    Lines must have components with the same y spacing between input and output ports

    Lattice example:

    .. code::

        X-X
        XCX
        XCX
        X-X

    .. plot::
      :include-source:

      import gdsfactory as gf
      from gdsfactory.components.crossing_waveguide import crossing45
      from gdsfactory.components.crossing_waveguide import compensation_path

      components =  {
            "C": gf.routing.fanout2x2(component=gf.components.coupler(), port_spacing=40.0),
            "X": crossing45(port_spacing=40.0),
            "-": compensation_path(crossing45=crossing45(port_spacing=40.0)),
      }
      c = gf.components.component_lattice(components=components)
      c.plot()

    """
    components = components or {
        "C": gf.routing.fanout2x2(component=coupler(), port_spacing=40.0),
        "X": crossing45(port_spacing=40.0),
        "-": compensation_path(crossing45=crossing45(port_spacing=40.0)),
    }

    # Find y spacing and check that all components have same y spacing

    y_spacing = None
    for component in components.values():
        component = gf.call_if_func(component)
        component.auto_rename_ports_orientation()

        for direction in ["W", "E"]:
            ports_dir = get_ports_facing(component.ports, direction)
            ports_dir.sort(key=lambda p: p.y)
            nb_ports = len(ports_dir)
            if nb_ports > 1:
                _y_spacing = (ports_dir[-1].y - ports_dir[0].y) / (nb_ports -
                                                                   1)
                if y_spacing is None:
                    y_spacing = _y_spacing
                else:
                    assert abs(y_spacing - _y_spacing) < 0.1 / grid_per_unit, (
                        "All component must have the same y port spacing. Got"
                        f" {y_spacing}, {_y_spacing} for {component.name}")

    a = y_spacing
    columns, columns_to_length = parse_lattice(lattice, components)
    keys = sorted(columns.keys())

    components_to_nb_input_ports = {}
    for c in components.keys():
        components_to_nb_input_ports[c] = len(
            get_ports_facing(components[c], "W"))

    component = gf.Component(name)
    x = 0
    for i in keys:
        col = columns[i]
        j = 0
        L = columns_to_length[i]
        skip = 0  # number of lines to skip depending on the number of ports
        for c in col:
            y = -j * a
            if skip == 1:
                j += skip
                skip = 0
                continue

            if c in components.keys():
                # Compute the number of ports to skip: They will already be
                # connected since they belong to this component

                nb_inputs = components_to_nb_input_ports[c]
                skip = nb_inputs - 1
                _cmp = components[c].ref((x, y), port_id="oW{}".format(skip))
                component.add(_cmp)

                if i == 0:
                    _ports = get_ports_facing(_cmp, "W")
                    for _p in _ports:
                        component.add_port(gen_tmp_port_name(), port=_p)

                if i == keys[-1]:
                    _ports = get_ports_facing(_cmp, "E")
                    for _p in _ports:
                        component.add_port(gen_tmp_port_name(), port=_p)

            else:
                raise ValueError(
                    f"symbol {c} not in components dict {components.keys()}")

            j += 1
        x += L

    return component
Ejemplo n.º 16
0
def dbr2(length: float = 10.0,
         period: float = 0.85,
         dc: float = 0.5,
         w1: float = 0.4,
         w2: float = 1.0,
         taper_length: float = 20.0,
         fins: bool = False,
         fin_size: Tuple[float, float] = (0.2, 0.05),
         port: Tuple[int, int] = (0, 0),
         direction: str = "EAST",
         waveguide_template: ComponentFactory = strip,
         waveguide_template_dbr: Optional[ComponentFactory] = None,
         **kwargs) -> Component:
    """Distributed Bragg Reflector Cell class.
    Tapers the input straight to a periodic straight structure with varying width
    (1-D photonic crystal).

    Args:
       length: Length of the DBR region.
       period: Period of the repeated unit.
       dc: Duty cycle of the repeated unit (must be a float between 0 and 1.0).
       w1: Width of the thin section of the straight.  w1 = 0 corresponds to disconnected periodic blocks.
       w2: Width of the wide section of the straight
       taper_length: Length of the taper between the input/output straight and the DBR region.
       fins (boolean): If `True`, adds fins to the input/output straights.
        In this case a different template for the component must be specified.
       fin_size ((x,y) Tuple): Specifies the x- and y-size of the `fins`. Defaults to 200 nm x 50 nm
       waveguide_template_dbr: If `fins` above is True, a WaveguideTemplate (dbr_wgt) must be specified.
        This defines the layertype / datatype of the DBR (which will be separate from the input/output straights)
       port (tuple): Cartesian coordinate of the input port.  Defaults to (0,0).
       direction (string): Direction that the component will point *towards*,
       can be of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians)
       waveguide_template:  WaveguideTemplate object
       waveguide_template_dbr: Picwriter WaveguideTemplate object
       wg_width: 0.5
       wg_layer: gf.LAYER.WG[0]
       wg_datatype: gf.LAYER.WG[1]
       clad_layer: gf.LAYER.WGCLAD[0]
       clad_datatype: gf.LAYER.WGCLAD[1]
       bend_radius: 10
       cladding_offset: 3

    .. code::

                 period
        <-----><-------->
                _________
        _______|

          w1       w2       ...  n times
        _______
               |_________



    .. plot::
      :include-source:

      import gdsfactory as gf

      c = gf.components.dbr2(length=10, period=0.85, dc=0.5, w2=1, w1=0.4)
      c.plot()

    """

    waveguide_template_dbr = waveguide_template_dbr or waveguide_template(
        wg_width=w2)

    c = pc.DBR(
        wgt=gf.call_if_func(waveguide_template, wg_width=w2, **kwargs),
        length=length,
        period=period,
        dc=dc,
        w_phc=w1,
        taper_length=taper_length,
        fins=fins,
        fin_size=fin_size,
        dbr_wgt=waveguide_template_dbr,
        port=port,
        direction=direction,
    )

    return gf.read.from_picwriter(c)
Ejemplo n.º 17
0
def route_fiber_single(
    component: Component,
    fiber_spacing: float = 50.0,
    grating_coupler: Callable = grating_coupler_te,
    min_input_to_output_spacing: float = 200.0,
    optical_routing_type: int = 1,
    optical_port_labels: Optional[Tuple[str, ...]] = None,
    excluded_ports: Optional[Tuple[str, ...]] = None,
    auto_widen: bool = False,
    component_name: Optional[str] = None,
    select_ports: Callable = select_ports_optical,
    cross_section: CrossSectionFactory = strip,
    **kwargs,
) -> Tuple[List[Union[ComponentReference, Label]], List[ComponentReference]]:
    """Returns route Tuple(references, grating couplers) for single fiber input/output.

    Args:
        component: to add grating couplers
        fiber_spacing: between grating couplers
        grating_coupler:
        min_input_to_output_spacing: so opposite fibers do not touch
        optical_routing_type: 0 (basic), 1 (standard), 2 (looks at ports)
        optical_port_labels: port labels that need connection
        excluded_ports: ports excluded from routing
        auto_widen: for long routes
        component_name:
        select_ports:
        cross_section:
        **kwargs: cross_section settings

    Returns:
        elements: list of ComponentReferences for routes and labels
        grating_couplers: list of grating_couplers references


    .. code::
              _________
             |         |_E1
          W0_|         |
             |         |_E0
             |_________|


         rotates +90 deg and routes West ports to South

         the rest of the original ports (East, North, South) will route south

         it calls route_fiber_array twice

         route_fiber_array is designed to route ports south

               E1  E0
              _|___|_
             |       |
             |       |
             |       |
             |       |
             |       |
             |       |
             |_______|
                 |
                 W0     1st part routes West ports south

        then rotates 180 and routes the rest of the ports North

    """
    if not select_ports(component.ports):
        raise ValueError(f"No ports for {component.name}")

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

    if optical_port_labels is None:
        optical_ports = select_ports(component.ports)
    else:
        optical_ports = [component.ports[lbl] for lbl in optical_port_labels]

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

    if isinstance(grating_coupler, list):
        grating_couplers = [gf.call_if_func(g) for g in grating_coupler]
        grating_coupler = grating_couplers[0]
    else:
        grating_coupler = gf.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_input_to_output_spacing:
        fanout_length = (gf.snap.snap_to_grid(
            min_input_to_output_spacing - component.xsize - 2 * gc_port2center,
            10) / 2)
    else:
        fanout_length = None

    # route WEST ports to south
    component_west_ports = Component()
    ref = component_west_ports << component
    ref.rotate(90)
    south_ports = ref.get_ports_dict(orientation=270)
    component_west_ports.ports = south_ports

    if len(south_ports):
        elements_south, gratings_south, _ = route_fiber_array(
            component=component_west_ports,
            with_loopback=False,
            fiber_spacing=fiber_spacing,
            fanout_length=fanout_length,
            grating_coupler=grating_couplers[0],
            optical_routing_type=optical_routing_type,
            auto_widen=auto_widen,
            component_name=component_name,
            cross_section=cross_section,
            select_ports=select_ports,
            **kwargs,
        )

    # route non WEST ports north
    component = gf.Component()
    component_ref = component << component_copy
    component_ref.rotate(-90)
    component.add_ports(component_ref.ports)
    for port_already_routed in south_ports.values():
        component.ports.pop(port_already_routed.name)

    component.ports = select_ports(component.ports)

    elements_north, gratings_north, _ = route_fiber_array(
        component=component,
        with_loopback=False,
        fiber_spacing=fiber_spacing,
        fanout_length=fanout_length,
        grating_coupler=grating_couplers[1:],
        optical_routing_type=optical_routing_type,
        auto_widen=auto_widen,
        component_name=component_name,
        cross_section=cross_section,
        select_ports=select_ports,
        **kwargs,
    )
    for e in elements_north:
        if isinstance(e, list):
            for ei in e:
                elements_south.append(ei.rotate(180))
        else:
            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.º 18
0
def grating_coupler_elliptical2(
    taper_angle: float = 30.0,
    taper_length: float = 10.0,
    length: float = 30.0,
    period: float = 1.0,
    dutycycle: float = 0.7,
    port: Coordinate = (0.0, 0.0),
    layer_ridge: Optional[Layer] = None,
    layer_core: Layer = gf.LAYER.WG,
    layer_cladding: Layer = gf.LAYER.WGCLAD,
    teeth_list: Optional[Coordinates] = None,
    direction: str = "EAST",
    polarization: str = "te",
    wavelength: float = 1.55,
    fiber_marker_width: float = 11.0,
    fiber_marker_layer: Layer = gf.LAYER.TE,
    wgt: ComponentFactory = strip,
    wg_width: float = 0.5,
    cladding_offset: float = 2.0,
) -> Component:
    r"""Returns Grating coupler from Picwriter

    Args:
        taper_angle: taper flare angle in degrees
        taper_length: Length of the taper before the grating coupler.
        length: total grating coupler length.
        period: Grating period.
        dutycycle: (period-gap)/period.
        port: Cartesian coordinate of the input port
        layer_ridge: for partial etched gratings
        layer_core: Tuple specifying the layer/datatype of the ridge region.
        layer_cladding: for the straight.
        teeth_list: (gap, width) tuples to be used as the gap and teeth widths
          for irregularly spaced gratings.
          For example, [(0.6, 0.2), (0.7, 0.3), ...] would be a gap of 0.6,
          then a tooth of width 0.2, then gap of 0.7 and tooth of 0.3, and so on.
          Overrides *period*, *dutycycle*, and *length*.  Defaults to None.
        direction: Direction that the component will point *towards*,
          can be of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`,
          OR an angle (float, in radians)
        polarization: te or tm
        wavelength: wavelength um
        fiber_marker_width:
        wgt: waveguide_template object or function
        wg_width
        cladding_offset:


    .. code::

                      fiber

                   /  /  /  /
                  /  /  /  /
                _|-|_|-|_|-|___
        WG  o1  ______________|

    """
    ridge = True if layer_ridge else False

    c = pc.GratingCoupler(
        gf.call_if_func(
            wgt,
            cladding_offset=cladding_offset,
            wg_width=wg_width,
            layer=layer_core,
            layer_cladding=layer_cladding,
        ),
        theta=np.deg2rad(taper_angle),
        length=length,
        taper_length=taper_length,
        period=period,
        dutycycle=1 - dutycycle,
        ridge=ridge,
        ridge_layers=layer_ridge,
        teeth_list=teeth_list,
        port=port,
        direction=direction,
    )

    c = gf.read.from_picwriter(c)
    c.info.polarization = polarization
    c.info.wavelength = wavelength

    x = c.center[0] + taper_length / 2
    circle = gf.components.circle(radius=fiber_marker_width / 2,
                                  layer=fiber_marker_layer)
    circle_ref = c.add_ref(circle)
    circle_ref.movex(x)

    c.add_port(
        name=f"vertical_{polarization.lower()}",
        midpoint=[x, 0],
        width=fiber_marker_width,
        orientation=0,
        layer=fiber_marker_layer,
    )

    c.auto_rename_ports()
    return c
Ejemplo n.º 19
0
def add_fiber_array(
        component: Component,
        grating_coupler: Component = grating_coupler_te,
        straight: ComponentFactory = straight,
        bend: ComponentFactory = bend_euler,
        gc_port_name: str = "o1",
        gc_port_labels: Optional[Tuple[str, ...]] = None,
        component_name: Optional[str] = None,
        select_ports: Callable = select_ports_optical,
        cross_section: CrossSectionFactory = strip,
        get_input_labels_function: Optional[Callable] = get_input_labels,
        layer_label: Optional[Tuple[int, int]] = (66, 0),
        **kwargs,
) -> Component:
    """Returns component with optical IO (tapers, south routes and grating_couplers).

    Args:
        component: to connect
        grating_coupler: grating coupler instance, function or list of functions
        bend: bend_circular
        gc_port_name: grating coupler input port name 'W0'
        component_name: for the label
        taper: taper function name or dict
        get_input_labels_function: function to get input labels for grating couplers
        get_input_label_text_loopback_function: function to get input label test
        get_input_label_text_function
        straight: straight
        fanout_length: None  # if None, automatic calculation of fanout length
        max_y0_optical: None
        with_loopback: True, adds loopback structures
        straight_separation: 4.0
        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_straight: None
        routing_method: get_route
        optical_routing_type: None: auto, 0: no extension, 1: standard, 2: check
        gc_rotation: -90
        layer_label: LAYER.LABEL
        input_port_indexes: [0]

    .. plot::
        :include-source:


        import gdsfactory as gf
        gf.config.set_plot_options(show_subports=False)

        c = gf.components.crossing()
        cc = gf.routing.add_fiber_array(
            component=c,
            optical_routing_type=2,
            grating_coupler=gf.components.grating_coupler_elliptical_te,
            with_loopback=False
        )
        cc.plot()

    """
    get_input_labels_function = None if gc_port_labels else get_input_labels_function
    component = gf.call_if_func(component)
    grating_coupler = (grating_coupler()
                       if callable(grating_coupler) else grating_coupler)
    if not component.ports:
        return component

    if isinstance(grating_coupler, list):
        gc = grating_coupler[0]
    else:
        gc = grating_coupler
    gc = gf.call_if_func(gc)

    if gc_port_name not in gc.ports:
        raise ValueError(
            f"gc_port_name={gc_port_name} not in {gc.ports.keys()}")

    component_name = component_name or component.get_parent_name()
    component_new = Component()
    component_new.component = component

    optical_ports = select_ports(component.ports)
    optical_ports_names = list(optical_ports.keys())
    if not optical_ports:
        return component

    elements, io_gratings_lines, ports = route_fiber_array(
        component=component,
        grating_coupler=grating_coupler,
        bend=bend,
        straight=straight,
        gc_port_name=gc_port_name,
        component_name=component_name,
        cross_section=cross_section,
        select_ports=select_ports,
        get_input_labels_function=get_input_labels_function,
        layer_label=layer_label,
        **kwargs,
    )
    if len(elements) == 0:
        return component

    for e in elements:
        component_new.add(e)
    for io_gratings in io_gratings_lines:
        component_new.add(io_gratings)

    component_new.add_ref(component)

    for pname, p in component.ports.items():
        if p.name not in optical_ports_names:
            component_new.add_port(pname, port=p)

    ports = sort_ports_x(ports)

    if gc_port_labels:
        for gc_port_label, port in zip(gc_port_labels, ports):
            component_new.add_label(text=gc_port_label,
                                    layer=layer_label,
                                    position=port.midpoint)

    for i, io_row in enumerate(io_gratings_lines):
        for j, io in enumerate(io_row):
            ports = io.get_ports_list(prefix="vertical")
            if ports:
                port = ports[0]
                component_new.add_port(f"{port.name}_{i}{j}", port=port)

    component_new.copy_child_info(component)
    return component_new
Ejemplo n.º 20
0
def spiral_inner_io(N: int = 6,
                    x_straight_inner_right: float = 150.0,
                    x_straight_inner_left: float = 50.0,
                    y_straight_inner_top: float = 50.0,
                    y_straight_inner_bottom: float = 10.0,
                    grating_spacing: float = 127.0,
                    waveguide_spacing: float = 3.0,
                    bend90_function: ComponentFactory = bend_euler,
                    bend180_function: ComponentFactory = bend_euler180,
                    straight: ComponentFactory = straight_function,
                    length: Optional[float] = None,
                    cross_section: CrossSectionFactory = strip,
                    cross_section_bend: Optional[CrossSectionFactory] = None,
                    **kwargs) -> Component:
    """Returns Spiral with ports inside the spiral loop.
    You can add grating couplers inside .

    Args:
        N: number of loops
        x_straight_inner_right:
        x_straight_inner_left:
        y_straight_inner_top:
        y_straight_inner_bottom:
        grating_spacing: defaults to 127 for fiber array
        waveguide_spacing: center to center spacing
        bend90_function
        bend180_function
        straight: straight function
        length: spiral target length (um), overrides x_straight_inner_left
            to match the length by a simple 1D interpolation
        cross_section:
        cross_section_bend: for the bends
        kwargs: cross_section settings

    """
    dx = dy = waveguide_spacing
    x = cross_section(**kwargs)
    width = x.info.get("width")
    layer = x.info.get("layer")
    cross_section_bend = cross_section_bend or cross_section

    if length:
        x_straight_inner_left = get_straight_length(
            length=length,
            spiral_function=spiral_inner_io,
            N=N,
            x_straight_inner_right=x_straight_inner_right,
            x_straight_inner_left=x_straight_inner_left,
            y_straight_inner_top=y_straight_inner_top,
            y_straight_inner_bottom=y_straight_inner_bottom,
            grating_spacing=grating_spacing,
            waveguide_spacing=waveguide_spacing,
        )

    _bend180 = gf.call_if_func(bend180_function,
                               cross_section=cross_section_bend,
                               **kwargs)
    _bend90 = gf.call_if_func(bend90_function,
                              cross_section=cross_section_bend,
                              **kwargs)

    rx, ry = get_bend_port_distances(_bend90)
    _, rx180 = get_bend_port_distances(
        _bend180)  # rx180, second arg since we rotate

    component = Component()

    p1 = gf.Port(
        name="o1",
        midpoint=(0, y_straight_inner_top),
        orientation=270,
        width=width,
        layer=layer,
        cross_section=cross_section_bend,
    )
    p2 = gf.Port(
        name="o2",
        midpoint=(grating_spacing, y_straight_inner_top),
        orientation=270,
        width=width,
        layer=layer,
        cross_section=cross_section_bend,
    )

    component.add_port(name="o1", port=p1)
    component.add_port(name="o2", port=p2)

    # Create manhattan path going from west grating to westest port of bend 180
    _pt = np.array(p1.position)
    pts_w = [_pt]

    for i in range(N):
        y1 = y_straight_inner_top + ry + (2 * i + 1) * dy
        x2 = grating_spacing + 2 * rx + x_straight_inner_right + (2 * i +
                                                                  1) * dx
        y3 = -y_straight_inner_bottom - ry - (2 * i + 3) * dy
        x4 = -x_straight_inner_left - (2 * i + 1) * dx
        if i == N - 1:
            x4 = x4 - rx180 + dx

        _pt1 = np.array([_pt[0], y1])
        _pt2 = np.array([x2, _pt1[1]])
        _pt3 = np.array([_pt2[0], y3])
        _pt4 = np.array([x4, _pt3[1]])
        _pt5 = np.array([_pt4[0], 0])
        _pt = _pt5

        pts_w += [_pt1, _pt2, _pt3, _pt4, _pt5]

    route_west = round_corners(pts_w,
                               bend=_bend90,
                               straight=straight,
                               cross_section=cross_section,
                               **kwargs)
    component.add(route_west.references)

    # Add loop back
    bend180_ref = _bend180.ref(port_id="o2",
                               position=route_west.ports[1],
                               rotation=90)
    component.add(bend180_ref)

    # Create manhattan path going from east grating to eastest port of bend 180
    _pt = np.array(p2.position)
    pts_e = [_pt]

    for i in range(N):
        y1 = y_straight_inner_top + ry + (2 * i) * dy
        x2 = grating_spacing + 2 * rx + x_straight_inner_right + 2 * i * dx
        y3 = -y_straight_inner_bottom - ry - (2 * i + 2) * dy
        x4 = -x_straight_inner_left - (2 * i) * dx

        _pt1 = np.array([_pt[0], y1])
        _pt2 = np.array([x2, _pt1[1]])
        _pt3 = np.array([_pt2[0], y3])
        _pt4 = np.array([x4, _pt3[1]])
        _pt5 = np.array([_pt4[0], 0])
        _pt = _pt5

        pts_e += [_pt1, _pt2, _pt3, _pt4, _pt5]

    route_east = round_corners(pts_e,
                               bend=_bend90,
                               straight=straight,
                               cross_section=cross_section,
                               **kwargs)
    component.add(route_east.references)

    length = route_east.length + route_west.length + _bend180.info.length
    component.info.length = snap_to_grid(length + 2 * y_straight_inner_top)
    return component
Ejemplo n.º 21
0
def cdc(length: float = 30.0,
        gap: float = 0.5,
        period: float = 0.220,
        dc: float = 0.5,
        angle: float = np.pi / 6.0,
        width_top: float = 2.0,
        width_bot: float = 0.75,
        input_bot: bool = False,
        dw_top: Optional[float] = None,
        dw_bot: Optional[float] = None,
        fins: bool = False,
        fin_size: Tuple[float, float] = (0.2, 0.05),
        contradc_wgt: None = None,
        port_midpoint: Tuple[int, int] = (0, 0),
        direction: str = "EAST",
        waveguide_template: ComponentFactory = strip,
        **kwargs) -> Component:
    """Grating-Assisted Contra-Directional Coupler

    Args:
       length : Length of the coupling region.
       gap: Distance between the two straights.
       period: Period of the grating.
       dc: Duty cycle of the grating. Must be between 0 and 1.
       angle: in radians at which the straight bends towards the coupling region.
       width_top: Width of the top straight in the coupling region.
       width_bot: Width of the bottom straight in the coupling region.
       dw_top: Amplitude of the width variation on the top.  Default=gap/2.0.
       dw_bot: Amplitude of the width variation on the bottom.  Default=gap/2.0.
       input_bot: True makes the default input the bottom straight (rather than top)
       fins: If `True`, adds fins to the input/output straights.
        In this case a different template for the component must be specified.
        This feature is useful when performing electron-beam lithography
        and using different beam currents
        for fine features (helps to reduce stitching errors).
       fin_size: Specifies the x- and y-size of the `fins`. Defaults to 200 nm x 50 nm
       contradc_wgt:
       port_midpoint: Cartesian coordinate of the input port
        (AT TOP if input_bot=False, AT BOTTOM if input_bot=True).
       direction: Direction that the component will point *towards*,
        can be of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`,
        OR an angle (float, in radians).
       waveguide_template: Picwriter WaveguideTemplate function


    """

    c = pc.ContraDirectionalCoupler(
        gf.call_if_func(strip, **kwargs),
        length=length,
        gap=gap,
        period=period,
        dc=dc,
        angle=angle,
        width_top=width_top,
        width_bot=width_bot,
        dw_top=dw_top,
        dw_bot=dw_bot,
        input_bot=input_bot,
        fins=fins,
        fin_size=fin_size,
        contradc_wgt=contradc_wgt,
        port=port_midpoint,
        direction=direction,
    )

    return gf.read.from_picwriter(c)
Ejemplo n.º 22
0
def dbr_tapered(length: float = 10.0,
                period: float = 0.85,
                dc: float = 0.5,
                w1: float = 0.4,
                w2: float = 1.0,
                taper_length: float = 20.0,
                fins: bool = False,
                fin_size: Tuple[float, float] = (0.2, 0.05),
                port: Tuple[int, int] = (0, 0),
                direction: str = "EAST",
                waveguide_template: ComponentFactory = strip,
                waveguide_template_dbr: Optional[ComponentFactory] = None,
                **kwargs) -> Component:
    """Distributed Bragg Reflector Cell class.
    Tapers the input straight to a periodic straight structure with varying width
    (1-D photonic crystal).

    Args:
       length: Length of the DBR region.
       period: Period of the repeated unit.
       dc: Duty cycle of the repeated unit (must be a float between 0 and 1.0).
       w1: thin section width. w1 = 0 corresponds to disconnected periodic blocks.
       w2: wide section width
       taper_length: between the input/output straight and the DBR region.
       fins: If `True`, adds fins to the input/output straights.
       fin_size: Specifies the x- and y-size of the `fins`. Defaults to 200 nm x 50 nm
       waveguide_template_dbr: If `fins` is True, a WaveguideTemplate must be specified.
       port: Cartesian coordinate of the input port.  Defaults to (0,0).
       direction: Direction that the component points *towards*,
        `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians)
       waveguide_template: WaveguideTemplate object

    Keyword Args:
        wg_width: 0.5
        wg_layer: gf.LAYER.WG[0]
        wg_datatype: gf.LAYER.WG[1]
        clad_layer: gf.LAYER.WGCLAD[0]
        clad_datatype: gf.LAYER.WGCLAD[1]
        bend_radius: 10
        cladding_offset: 3

    .. code::

                 period
        <-----><-------->
                _________
        _______|

          w1       w2       ...  n times
        _______
               |_________


    """

    waveguide_template_dbr = waveguide_template_dbr or waveguide_template(
        wg_width=w2)

    c = pc.DBR(
        wgt=gf.call_if_func(waveguide_template, wg_width=w2, **kwargs),
        length=length,
        period=period,
        dc=dc,
        w_phc=w1,
        taper_length=taper_length,
        fins=fins,
        fin_size=fin_size,
        dbr_wgt=waveguide_template_dbr,
        port=port,
        direction=direction,
    )

    return gf.read.from_picwriter(c)