예제 #1
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
예제 #2
0
def coupler90bend(
    radius: float = 10.0,
    gap: float = 0.2,
    bend: ComponentFactory = bend_euler,
    cross_section_inner: CrossSectionFactory = strip,
    cross_section_outer: CrossSectionFactory = strip,
) -> Component:
    r"""Returns 2 coupled bends.

    Args:
        radius: um
        gap: um
        bend: for bend
        cross_section_inner:
        cross_section_outer:


    .. code::

            r   3 4
            |   | |
            |  / /
            | / /
        2____/ /
        1_____/

    """

    c = Component()

    xi = cross_section_inner()
    xo = cross_section_outer()

    width = xo.info["width"] / 2 + xi.info["width"] / 2
    spacing = gap + width

    bend90_inner = bend(radius=radius, cross_section=cross_section_inner)
    bend90_outer = bend(radius=radius + spacing,
                        cross_section=cross_section_outer)
    bend_inner_ref = c << bend90_inner
    bend_outer_ref = c << bend90_outer

    pbw = bend_inner_ref.ports["o1"]
    bend_inner_ref.movey(pbw.midpoint[1] + spacing)

    # This component is a leaf cell => using absorb
    c.absorb(bend_outer_ref)
    c.absorb(bend_inner_ref)

    c.add_port("o1", port=bend_outer_ref.ports["o1"])
    c.add_port("o2", port=bend_inner_ref.ports["o1"])
    c.add_port("o3", port=bend_inner_ref.ports["o2"])
    c.add_port("o4", port=bend_outer_ref.ports["o2"])
    return c
예제 #3
0
def bend_circular(angle: int = 90,
                  npoints: int = 720,
                  with_cladding_box: bool = True,
                  cross_section: CrossSectionOrFactory = strip,
                  **kwargs) -> Component:
    """Returns a radial arc.

    Args:
        angle: angle of arc (degrees)
        npoints: number of points
        with_cladding_box: square in layers_cladding to remove DRC
        cross_section:
        kwargs: cross_section settings


    .. code::

                  o2
                  |
                 /
                /
               /
       o1_____/

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

    p = arc(radius=radius, angle=angle, npoints=npoints)
    c = Component()
    path = extrude(p, x)
    ref = c << path
    c.add_ports(ref.ports)

    c.info.length = snap_to_grid(p.length())
    c.info.dy = float(abs(p.points[0][0] - p.points[-1][0]))
    c.info.radius = float(radius)

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

    c.absorb(ref)
    return c
예제 #4
0
def coupler90(gap: float = 0.2,
              radius: float = 10.0,
              bend: ComponentFactory = bend_euler,
              cross_section: CrossSectionFactory = strip,
              **kwargs) -> Component:
    r"""straight coupled to a bend.

    Args:
        gap: um
        radius: um
        straight: for straight
        bend: for bend
        cross_section:
        kwargs: cross_section settings

    .. code::

             3
             |
            /
           /
        2_/
        1____4

    """
    c = Component()
    x = cross_section(radius=radius, **kwargs)

    bend90 = (bend(cross_section=cross_section, radius=radius, **kwargs)
              if callable(bend) else bend)
    bend_ref = c << bend90
    straight_component = (straight(
        cross_section=cross_section,
        length=bend90.ports["o2"].midpoint[0] - bend90.ports["o1"].midpoint[0],
        **kwargs) if callable(straight) else straight)

    wg_ref = c << straight_component
    width = x.info["width"]

    pbw = bend_ref.ports["o1"]
    bend_ref.movey(pbw.midpoint[1] + gap + width)

    c.absorb(wg_ref)
    c.absorb(bend_ref)

    c.add_port("o1", port=wg_ref.ports["o1"])
    c.add_port("o4", port=wg_ref.ports["o2"])
    c.add_port("o2", port=bend_ref.ports["o1"])
    c.add_port("o3", port=bend_ref.ports["o2"])
    return c
예제 #5
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
예제 #6
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
예제 #7
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
예제 #8
0
def cdsem_straight_density(
    width: float = 0.372,
    trench_width: float = 0.304,
    x: float = LINE_LENGTH,
    y: float = 50.0,
    margin: float = 2.0,
    label: str = "",
    straight: ComponentFactory = straight,
    layer: Tuple[int, int] = LAYER.WG,
    layers_cladding: Optional[Tuple[Layer, ...]] = None,
    cross_section: CrossSectionFactory = strip,
    pixel_size: float = 1.0,
) -> Component:
    """Horizontal grating etch lines

    Args:
        width: width
        trench_width: trench_width
    """
    c = Component()
    period = width + trench_width
    n_o_lines = int((y - 2 * margin) / period)
    length = x - 2 * margin

    cross_section = partial(cross_section, width=width, layer=layer)
    tooth = straight(length=length, cross_section=cross_section)

    for i in range(n_o_lines):
        tooth_ref = c.add_ref(tooth)
        tooth_ref.movey((-n_o_lines / 2 + 0.5 + i) * period)
        c.absorb(tooth_ref)

    marker_label = manhattan_text(
        text=f"{label}",
        size=pixel_size,
        layer=layer,
    )
    _marker_label = c.add_ref(marker_label)
    _marker_label.move((length + 3, 10.0))
    c.absorb(_marker_label)
    return c
예제 #9
0
def add_frame(
    component: Component = rectangle,
    width: float = 10.0,
    spacing: float = 10.0,
    layer: Layer = (1, 0),
) -> Component:
    """Returns component with a frame around it.

    Args:
        component: Component to frame
        width: of the frame
        spacing: of component to frame

    """
    c = Component()
    component = component() if callable(component) else component
    cref = c.add_ref(component)
    cref.move(-c.size_info.center)
    y = (
        max([component.size_info.height, component.size_info.width]) / 2
        + spacing
        + width / 2
    )
    x = y
    w = width

    rh = rectangle(size=(2 * y + w, w), layer=layer, centered=True)
    rtop = c.add_ref(rh)
    rbot = c.add_ref(rh)
    rtop.movey(+y)
    rbot.movey(-y)

    rv = rectangle(size=(w, 2 * y), layer=layer, centered=True)
    rl = c.add_ref(rv)
    rr = c.add_ref(rv)
    rl.movex(-x)
    rr.movex(+x)
    c.absorb(cref)
    return c
예제 #10
0
def straight(length: float = 10.0,
             npoints: int = 2,
             with_cladding_box: bool = True,
             cross_section: CrossSectionOrFactory = strip,
             **kwargs) -> Component:
    """Returns a Straight waveguide.

    Args:
        length: straight length
        npoints: number of points
        with_cladding_box: box in layers_cladding to avoid DRC sharp edges
        cross_section:
        **kwargs: cross_section settings
    """
    p = gf.path.straight(length=length, npoints=npoints)
    x = cross_section(**kwargs) if callable(cross_section) else cross_section

    c = Component()
    path = gf.path.extrude(p, x)
    ref = c << path
    c.add_ports(ref.ports)
    c.info.length = gf.snap.snap_to_grid(length)
    c.info.width = float(x.info["width"])
    if length > 0 and with_cladding_box and x.info["layers_cladding"]:
        layers_cladding = x.info["layers_cladding"]
        cladding_offset = x.info["cladding_offset"]
        points = get_padding_points(
            component=c,
            default=0,
            bottom=cladding_offset,
            top=cladding_offset,
        )
        for layer in layers_cladding or []:
            c.add_polygon(points, layer=layer)
    c.absorb(ref)
    return c
예제 #11
0
def rectangle_with_slits(
    size: Tuple[float, float] = (100.0, 200.0),
    layer: Layer = (1, 0),
    layer_slit: Optional[Layer] = (2, 0),
    centered: bool = False,
    port_type: Optional[str] = None,
    slit_size: Tuple[float, float] = (1.0, 1.0),
    slit_spacing: Float2 = (20, 20),
    slit_enclosure: float = 10,
) -> Component:
    """Returns a rectangle with slits. Metal slits reduce stress.

    Args:
        size: (tuple) Width and height of rectangle.
        layer: Specific layer to put polygon geometry on.
        layer_slit: does a boolan NOT when None.
        centered: True sets center to (0, 0), False sets south-west to (0, 0)
        port_type: for the rectangle
        slit_size: x, y slit size
        slit_spacing: pitch_x, pitch_y for slits
        slit_enclosure: from slit to rectangle edge


    .. code::

        slit_enclosure
        _____________________________________
        |<--->                              |
        |                                   |
        |      ______________________       |
        |     |                      |      |
        |     |                      | slit_size[1]
        |     |______________________|      |
        |  |                                |
        |  | slit_spacing                   |
        |  |                                |  size[1]
        |  |   ______________________       |
        |  |  |                      |      |
        |  |  |                      |      |
        |  |  |______________________|      |
        |     <--------------------->       |
        |            slit_size[0]           |
        |___________________________________|
                        size[0]


    """
    c = Component()
    r = rectangle(size=size,
                  layer=layer,
                  port_type=port_type,
                  centered=centered)
    c.add_ports(r.ports)
    slit = rectangle(size=slit_size, port_type=None, layer=layer_slit or layer)

    columns = np.floor((size[0] - 2 * slit_enclosure) / slit_spacing[0])
    rows = np.floor((size[1] - 2 * slit_enclosure) / slit_spacing[1])
    slits = array(slit, columns=columns, rows=rows, spacing=slit_spacing).ref()
    slits.xmin = slit_enclosure
    slits.ymin = slit_enclosure

    if layer_slit:
        c << r
        c.add(slits)
    else:
        r_with_slits = c << gf.geometry.boolean(
            r, slits, operation="not", layer=layer)
        c.absorb(r_with_slits)
    return c
예제 #12
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
예제 #13
0
def resistance_meander(
    num_squares: int = 1000,
    width: float = 1.0,
    layer: Tuple[int, int] = LAYER.M3,
    pad: ComponentFactory = pad80,
) -> Component:
    """meander to test resistance
    from phidl.geometry

    FIXME, add pad_pitch

    Args:
        pad_size: Size of the two matched impedance pads (microns)
        num_squares: Number of squares comprising the resonator wire
        width: The width of the squares (microns)
        layer:
        pad: function for pad

    """
    pad = pad()

    pad_size = pad.info.size
    x = pad_size[0]
    z = pad_size[1]

    # Checking validity of input
    if x <= 0 or z <= 0:
        raise ValueError("Pad must have positive, real dimensions")
    elif width > z:
        raise ValueError("Width of cell cannot be greater than height of pad")
    elif num_squares <= 0:
        raise ValueError("Number of squares must be a positive real number")
    elif width <= 0:
        raise ValueError("Width of cell must be a positive real number")

    # Performing preliminary calculations
    num_rows = int(np.floor(z / (2 * width)))
    if num_rows % 2 == 0:
        num_rows -= 1
    num_columns = num_rows - 1
    squares_in_row = (num_squares - num_columns - 2) / num_rows

    # Compensating for weird edge cases
    if squares_in_row < 1:
        num_rows = round(num_rows / 2) - 2
        squares_in_row = 1
    if width * 2 > z:
        num_rows = 1
        squares_in_row = num_squares - 2

    length_row = squares_in_row * width

    # Creating row/column corner combination structure
    T = Component()
    Row = pc.rectangle(size=(length_row, width), layer=layer, port_type=None)
    Col = pc.rectangle(size=(width, width), layer=layer, port_type=None)

    T.add_ref(Row)
    col = T.add_ref(Col)
    col.move([length_row - width, -width])

    # Creating entire straight net
    N = Component("net")
    n = 1
    for i in range(num_rows):
        if i != num_rows - 1:
            d = N.add_ref(T)
        else:
            d = N.add_ref(Row)
        if n % 2 == 0:
            d.reflect(p1=(d.x, d.ymax), p2=(d.x, d.ymin))
        d.movey(-(n - 1) * T.ysize)
        n += 1
    d = N.add_ref(Col).movex(-width)
    d = N.add_ref(Col).move([length_row, -(n - 2) * T.ysize])

    # Creating pads
    P = Component()
    pad1 = P.add_ref(pad)
    pad1.movex(-x - width)
    pad2 = P.add_ref(pad)
    pad2.movex(length_row + width)

    pad1.xmax = 0
    pad2.xmin = z

    net = P.add_ref(N)
    net.y = pad1.y
    P.absorb(net)
    P.absorb(pad1)
    P.absorb(pad2)
    return P
예제 #14
0
def coupler_ring(
        gap: float = 0.2,
        radius: float = 5.0,
        length_x: float = 4.0,
        coupler90: ComponentFactory = coupler90function,
        bend: Optional[ComponentFactory] = None,
        coupler_straight: ComponentFactory = coupler_straight_function,
        cross_section: CrossSectionFactory = strip,
        **kwargs) -> Component:
    r"""Coupler for ring.

    Args:
        gap: spacing between parallel coupled straight waveguides.
        radius: of the bends.
        length_x: length of the parallel coupled straight waveguides.
        coupler90: straight coupled to a 90deg bend.
        straight: library for straight waveguides.
        bend: library for bend
        coupler_straight: two parallel coupled straight waveguides.
        cross_section:
        **kwargs: cross_section settings

    .. code::

           2             3
           |             |
            \           /
             \         /
           ---=========---
         1    length_x    4


    """
    bend = bend or bend_euler

    c = Component()
    assert_on_2nm_grid(gap)

    # define subcells
    coupler90_component = (coupler90(gap=gap,
                                     radius=radius,
                                     bend=bend,
                                     cross_section=cross_section,
                                     **kwargs)
                           if callable(coupler90) else coupler90)
    coupler_straight_component = (coupler_straight(
        gap=gap, length=length_x, cross_section=cross_section, **
        kwargs) if callable(coupler_straight) else coupler_straight)

    # add references to subcells
    cbl = c << coupler90_component
    cbr = c << coupler90_component
    cs = c << coupler_straight_component

    # connect references
    y = coupler90_component.y
    cs.connect(port="o4", destination=cbr.ports["o1"])
    cbl.reflect(p1=(0, y), p2=(1, y))
    cbl.connect(port="o2", destination=cs.ports["o2"])

    c.absorb(cbl)
    c.absorb(cbr)
    c.absorb(cs)

    c.add_port("o1", port=cbl.ports["o3"])
    c.add_port("o2", port=cbl.ports["o4"])
    c.add_port("o3", port=cbr.ports["o3"])
    c.add_port("o4", port=cbr.ports["o4"])
    c.auto_rename_ports()
    return c
예제 #15
0
def coupler_symmetric(
    bend: ComponentFactory = bend_s,
    gap: float = 0.234,
    dy: float = 5.0,
    dx: float = 10.0,
    cross_section: CrossSectionFactory = strip,
    **kwargs,
) -> Component:
    r"""Two coupled straights with bends.

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

    .. code::

                       dx
                    |-----|
                       ___ E1
                      /       |
                _____/        |
           gap  _____         |  dy
                     \        |
                      \___    |
                           E0

    """

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

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

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

    c.add(top_bend)
    c.add(bottom_bend)

    c.absorb(top_bend)
    c.absorb(bottom_bend)

    c.add_port("o1", port=bottom_bend.ports["o1"])
    c.add_port("o2", port=top_bend.ports["o1"])

    c.add_port("o3", port=top_bend.ports["o2"])
    c.add_port("o4", port=bottom_bend.ports["o2"])
    c.info.length = bend_component.info.length
    c.info.min_bend_radius = bend_component.info.min_bend_radius
    return c
예제 #16
0
def mmi1x2(width: float = 0.5,
           width_taper: float = 1.0,
           length_taper: float = 10.0,
           length_mmi: float = 5.5,
           width_mmi: float = 2.5,
           gap_mmi: float = 0.25,
           taper: ComponentFactory = taper_function,
           with_cladding_box: bool = True,
           cross_section: CrossSectionFactory = strip,
           **kwargs) -> Component:
    r"""Mmi 1x2.

    Args:
        width: input and output straight width
        width_taper: interface between input straights and mmi region
        length_taper: into the mmi region
        length_mmi: in x direction
        width_mmi: in y direction
        gap_mmi:  gap between tapered wg
        taper: taper function
        with_cladding_box: to avoid DRC acute angle errors in cladding
        cross_section:
        **kwargs: cross_section settings


    .. code::

               length_mmi
                <------>
                ________
               |        |
               |         \__
               |          __  E1
            __/          /_ _ _ _
         W0 __          | _ _ _ _| gap_mmi
              \          \__
               |          __  E0
               |         /
               |________|

             <->
        length_taper

    """
    gf.snap.assert_on_2nm_grid(gap_mmi)
    x = cross_section(**kwargs)
    cladding_offset = x.info["cladding_offset"]
    layers_cladding = x.info["layers_cladding"]
    layer = x.info["layer"]

    c = Component()
    w_mmi = width_mmi
    w_taper = width_taper

    taper = taper(length=length_taper,
                  width1=width,
                  width2=w_taper,
                  cross_section=cross_section,
                  **kwargs)

    a = gap_mmi / 2 + width_taper / 2
    mmi = c << gf.components.rectangle(
        size=(length_mmi, w_mmi),
        layer=layer,
        centered=True,
    )

    ports = [
        gf.Port("o1",
                orientation=180,
                midpoint=(-length_mmi / 2, 0),
                width=w_taper),
        gf.Port("o2",
                orientation=0,
                midpoint=(+length_mmi / 2, +a),
                width=w_taper),
        gf.Port("o3",
                orientation=0,
                midpoint=(+length_mmi / 2, -a),
                width=w_taper),
    ]

    for port in ports:
        taper_ref = c << taper
        taper_ref.connect(port="o2", destination=port)
        c.add_port(name=port.name, port=taper_ref.ports["o1"])
        c.absorb(taper_ref)

    c.absorb(mmi)

    layers_cladding = layers_cladding or []
    if layers_cladding and with_cladding_box:
        add_padding(
            c,
            default=cladding_offset,
            right=0,
            left=0,
            top=cladding_offset,
            bottom=cladding_offset,
            layers=layers_cladding,
        )
    return c
예제 #17
0
def crossing45(
    crossing: ComponentFactory = crossing,
    port_spacing: float = 40.0,
    dx: Optional[float] = None,
    alpha: float = 0.08,
    npoints: int = 101,
) -> Component:
    r"""Returns 45deg crossing with bends.

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


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

    .. code::

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

    """

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

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

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

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

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

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

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

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

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

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

    c.bezier = bend
    c.crossing = crossing

    c.add_port("o1", port=b_br.ports["o2"])
    c.add_port("o2", port=b_tr.ports["o2"])
    c.add_port("o3", port=b_bl.ports["o2"])
    c.add_port("o4", port=b_tl.ports["o2"])
    c.snap_ports_to_grid()
    c.auto_rename_ports()
    return c
예제 #18
0
def crossing_etched(
    width: float = 0.5,
    r1: float = 3.0,
    r2: float = 1.1,
    w: float = 1.2,
    L: float = 3.4,
    layer_wg: Layer = LAYER.WG,
    layer_slab: Layer = LAYER.SLAB150,
):
    """
    Waveguide crossing:
    - The full crossing has to be on WG layer (to start with a 220nm slab)
    - Then we etch the ellipses down to 150nm slabs and we keep linear taper at 220nm.
    What we write is what we etch on this step
    """

    # Draw the ellipses
    c = Component()
    _ellipse1 = c << ellipse(radii=(r1, r2), layer=layer_wg)
    _ellipse2 = c << ellipse(radii=(r2, r1), layer=layer_wg)
    c.absorb(_ellipse1)
    c.absorb(_ellipse2)

    a = L + w / 2
    h = width / 2

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

    c.add_polygon(taper_cross_pts, layer=layer_wg)

    # tapers_poly = c.add_polygon(taper_cross_pts, layer=layer_wg)
    # b = a - 0.1  # To make sure we get 4 distinct polygons when doing bool ops
    # tmp_polygon = [(-b, b), (b, b), (b, -b), (-b, -b)]
    # polys_etch = gdspy.fast_boolean([tmp_polygon], tapers_poly, "not", layer=layer_slab)
    # c.add(polys_etch)

    positions = [(a, 0), (0, a), (-a, 0), (0, -a)]
    angles = [0, 90, 180, 270]

    i = 0
    for p, angle in zip(positions, angles):
        c.add_port(
            name=str(i),
            midpoint=p,
            orientation=angle,
            width=width,
            layer=layer_wg,
        )
        i += 1

    c.auto_rename_ports()
    return c
예제 #19
0
def coupler(gap: float = 0.236,
            length: float = 20.0,
            coupler_symmetric: ComponentFactory = coupler_symmetric_function,
            coupler_straight: ComponentFactory = coupler_straight_function,
            dy: float = 5.0,
            dx: float = 10.0,
            cross_section: CrossSectionFactory = strip,
            **kwargs) -> Component:
    r"""Symmetric coupler.

    Args:
        gap: between straights
        length: of coupling region
        coupler_symmetric
        coupler_straight
        dy: port to port vertical spacing
        dx: length of bend in x direction
        cross_section: factory
        kwargs: cross_section settings

    .. code::

               dx                                 dx
            |------|                           |------|
         o2 ________                           ______o3
                    \                         /           |
                     \        length         /            |
                      ======================= gap         | dy
                     /                       \            |
            ________/                         \_______    |
         o1                                          o4

                        coupler_straight  coupler_symmetric


    """
    length = snap_to_grid(length)
    assert_on_2nm_grid(gap)
    c = Component()

    sbend = coupler_symmetric(gap=gap,
                              dy=dy,
                              dx=dx,
                              cross_section=cross_section,
                              **kwargs)

    sr = c << sbend
    sl = c << sbend
    cs = c << coupler_straight(
        length=length, gap=gap, cross_section=cross_section, **kwargs)
    sl.connect("o2", destination=cs.ports["o1"])
    sr.connect("o1", destination=cs.ports["o4"])

    c.add_port("o1", port=sl.ports["o3"])
    c.add_port("o2", port=sl.ports["o4"])
    c.add_port("o3", port=sr.ports["o3"])
    c.add_port("o4", port=sr.ports["o4"])

    c.absorb(sl)
    c.absorb(sr)
    c.absorb(cs)
    c.info.length = sbend.info.length
    c.info.min_bend_radius = sbend.info.min_bend_radius
    c.auto_rename_ports()
    return c
예제 #20
0
def bend_euler(angle: int = 90,
               p: float = 0.5,
               with_arc_floorplan: bool = True,
               npoints: int = 720,
               direction: str = "ccw",
               with_cladding_box: bool = True,
               cross_section: CrossSectionOrFactory = strip,
               **kwargs) -> Component:
    """Returns an euler bend that adiabatically transitions from straight to curved.
    By default, `radius` corresponds to the minimum radius of curvature of the bend.
    However, if `with_arc_floorplan` is True, `radius` corresponds to the effective
    radius of curvature (making the curve a drop-in replacement for an arc). If
    p < 1.0, will create a "partial euler" curve as described in Vogelbacher et.
    al. https://dx.doi.org/10.1364/oe.27.031394

    default p = 0.5 based on this paper
    https://www.osapublishing.org/oe/fulltext.cfm?uri=oe-25-8-9150&id=362937

    Args:
        angle: total angle of the curve
        p: Proportion of the curve that is an Euler curve
        with_arc_floorplan: If False: `radius` is the minimum radius of curvature
          If True: The curve scales such that the endpoints match a bend_circular
          with parameters `radius` and `angle`
        npoints: Number of points used per 360 degrees
        direction: cw (clock-wise) or ccw (counter clock-wise)
        with_cladding_box: to avoid DRC acute angle errors in cladding
        cross_section:
        kwargs: cross_section settings


    .. code::

                  o2
                  |
                 /
                /
               /
       o1_____/


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

    c = Component()
    p = euler(radius=radius,
              angle=angle,
              p=p,
              use_eff=with_arc_floorplan,
              npoints=npoints)
    ref = c << extrude(p, x)
    c.add_ports(ref.ports)
    c.info.length = snap_to_grid(p.length())
    c.info.dy = abs(float(p.points[0][0] - p.points[-1][0]))
    c.info.radius_min = float(snap_to_grid(p.info["Rmin"]))
    c.info.radius = float(radius)

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

    if direction == "cw":
        ref.mirror(p1=[0, 0], p2=[1, 0])

    c.absorb(ref)
    return c
예제 #21
0
def resistance_meander(
    pad_size: Tuple[float] = (50.0, 50.0),
    num_squares: int = 1000,
    width: float = 1.0,
    res_layer: Tuple[int, int] = LAYER.M3,
    pad_layer: Tuple[int, int] = LAYER.M3,
    gnd_layer: Tuple[int, int] = LAYER.M3,
) -> Component:
    """meander to test resistance
    from phidl.geometry

    Args:
        pad_size: Size of the two matched impedance pads (microns)
        num_squares: Number of squares comprising the resonator wire
        width: The width of the squares (microns)
        res_layer:
        pad_layer:
        gnd_layer:

    """

    x = pad_size[0]
    z = pad_size[1]

    # Checking validity of input
    if x <= 0 or z <= 0:
        raise ValueError("Pad must have positive, real dimensions")
    elif width > z:
        raise ValueError("Width of cell cannot be greater than height of pad")
    elif num_squares <= 0:
        raise ValueError("Number of squares must be a positive real number")
    elif width <= 0:
        raise ValueError("Width of cell must be a positive real number")

    # Performing preliminary calculations
    num_rows = int(np.floor(z / (2 * width)))
    if num_rows % 2 == 0:
        num_rows -= 1
    num_columns = num_rows - 1
    squares_in_row = (num_squares - num_columns - 2) / num_rows

    # Compensating for weird edge cases
    if squares_in_row < 1:
        num_rows = round(num_rows / 2) - 2
        squares_in_row = 1
    if width * 2 > z:
        num_rows = 1
        squares_in_row = num_squares - 2

    length_row = squares_in_row * width

    # Creating row/column corner combination structure
    T = Component()
    Row = pc.rectangle(size=(length_row, width), layer=res_layer)
    Col = pc.rectangle(size=(width, width), layer=res_layer)

    T.add_ref(Row)
    col = T.add_ref(Col)
    col.move([length_row - width, -width])

    # Creating entire straight net
    N = Component("net")
    n = 1
    for i in range(num_rows):
        if i != num_rows - 1:
            d = N.add_ref(T)
        else:
            d = N.add_ref(Row)
        if n % 2 == 0:
            d.reflect(p1=(d.x, d.ymax), p2=(d.x, d.ymin))
        d.movey(-(n - 1) * T.ysize)
        n += 1
    d = N.add_ref(Col).movex(-width)
    d = N.add_ref(Col).move([length_row, -(n - 2) * T.ysize])

    # Creating pads
    P = Component()
    pad1 = pc.rectangle(size=(x, z), layer=pad_layer)
    pad2 = pc.rectangle(size=(x + 5, z), layer=pad_layer)
    gnd1 = offset(pad1, distance=-5, layer=gnd_layer)
    gnd2 = offset(pad2, distance=-5, layer=gnd_layer)
    pad1_ref = P.add_ref(pad1)
    pad1_ref.movex(-x - width)
    pad2_ref = P.add_ref(pad1)
    pad2_ref.movex(length_row + width)
    gnd1_ref = P.add_ref(gnd1)
    gnd1_ref.center = pad1_ref.center
    gnd2_ref = P.add_ref(gnd2)
    net = P.add_ref(N)
    net.y = pad1_ref.y
    gnd2_ref.center = pad2_ref.center
    gnd2_ref.movex(2.5)
    P.absorb(net)
    P.absorb(gnd1_ref)
    P.absorb(gnd2_ref)
    P.absorb(pad1_ref)
    P.absorb(pad2_ref)
    return P