Esempio n. 1
0
def pad(
    width: int = 100, height: int = 100, layer: Tuple[int, int] = LAYER.M3
) -> Component:
    """rectangular pad with 4 ports (N, S, E, W)

    Args:
        width: pad width
        height: pad height
        layer: pad layer


    .. plot::
      :include-source:

      import pp

      c = pp.c.pad(width=100, height=100, layer=pp.LAYER.M3)
      pp.plotgds(c)

    """
    c = Component()
    _c = compass(size=(width, height), layer=layer).ref()
    c.add(_c)
    c.absorb(_c)
    c.ports = _c.ports
    return c
Esempio n. 2
0
def add_trenches(
    c: Component,
    sstw: float = 2.0,
    trench_width: float = 0.5,
    trench_keep_out: float = 2.0,
    trenches: List[Dict[str, int]] = [
        {
            "nb_segments": 2,
            "lane": 1,
            "x_start_offset": 0
        },
        {
            "nb_segments": 2,
            "lane": -1,
            "x_start_offset": 0
        },
    ],
    layer_trench: Tuple[int, int] = LAYER.DEEPTRENCH,
) -> Component:
    """
    Add trenches to a waveguide-heater-like component
    """

    heater_width = c.settings["heater_width"]
    heater_spacing = c.settings["heater_spacing"]
    width = c.settings["width"]
    length = c.settings["length"]

    a = heater_spacing + (width + heater_width) / 2

    # Add trenches
    if trench_width and trench_width > 0:
        tko = trench_keep_out

        for trench in trenches:
            lane = trench["lane"]
            td = tko + a + (trench_width + heater_width) / 2
            y = np.sign(lane) * (td + (abs(lane) - 1) * (trench_width + tko))
            x_start_offset = trench["x_start_offset"]

            if "segments" not in trench:
                nb_segments = trench["nb_segments"]
                trench_length = (length -
                                 (nb_segments - 1) * sstw) / nb_segments
                segments = [trench_length] * nb_segments
            else:
                segments = trench["segments"]
            x = x_start_offset
            for i, trench_length in enumerate(segments):
                trench = hline(length=trench_length,
                               width=trench_width,
                               layer=layer_trench)
                _trench = trench.ref(port_id="W0",
                                     position=c.ports["W0"].position + (x, y))
                c.add(_trench)
                c.absorb(_trench)
                x += trench_length + sstw

    return c
Esempio n. 3
0
def wg_heater_connected(waveguide_heater: Callable = waveguide_heater,
                        wg_heater_connector: Callable = wg_heater_connector,
                        tlm_layers: List[Tuple[int, int]] = [
                            LAYER.VIA1,
                            LAYER.M1,
                            LAYER.VIA2,
                            LAYER.M2,
                            LAYER.VIA3,
                            LAYER.M3,
                        ],
                        **kwargs) -> Component:
    """
    .. plot::
      :include-source:

      import pp

      c = pp.c.wg_heater_connected()
      pp.plotgds(c)

    """
    wg_heater = waveguide_heater(**kwargs)
    # print(wg_heater.ports.keys())
    conn1 = wg_heater_connector(
        heater_ports=[wg_heater.ports["HBE0"], wg_heater.ports["HTE0"]],
        tlm_layers=tlm_layers,
    )

    conn2 = wg_heater_connector(
        heater_ports=[wg_heater.ports["HBW0"], wg_heater.ports["HTW0"]],
        tlm_layers=tlm_layers,
    )

    cmp = Component()
    for c in [wg_heater, conn1, conn2]:
        _c = cmp.add_ref(c)
        cmp.absorb(_c)

    for port_name, p in wg_heater.ports.items():
        cmp.add_port(name=port_name, port=p)

    cmp.add_port(name=1, port=conn1.ports["0"])
    cmp.add_port(name=2, port=conn2.ports["0"])
    cmp.ports[1].orientation = 90
    cmp.ports[2].orientation = 90

    return cmp
Esempio n. 4
0
def coupler90(
    bend_radius: float = 10.0,
    width: float = 0.5,
    gap: float = 0.2,
    waveguide_factory: Callable = waveguide,
    bend90_factory: Callable = bend_circular,
) -> Component:
    """ Waveguide coupled to a bend with gap

    Args:
        bend_radius: um
        width: waveguide width (um)
        gap: um

    .. plot::
      :include-source:

      import pp
      c = pp.c.coupler90()
      pp.plotgds(c)

    """
    # pp.drc.assert_on_1nm_grid((width + gap) / 2)
    y = pp.drc.snap_to_1nm_grid((width + gap) / 2)

    c = Component()
    wg = c << waveguide_factory(
        length=bend_radius,
        width=width,
    )
    bend = c << bend90_factory(radius=bend_radius, width=width)

    pbw = bend.ports["W0"]
    bend.movey(pbw.midpoint[1] + gap + width)

    # This component is a leaf cell => using absorb
    c.absorb(wg)
    c.absorb(bend)

    port_width = 2 * width + gap
    c.add_port(port=wg.ports["E0"], name="E0")
    c.add_port(port=bend.ports["N0"], name="N0")
    c.add_port(name="W0", midpoint=[0, y], width=port_width, orientation=180)
    return c
Esempio n. 5
0
def _arbitrary_straight_waveguide(length, windows):
    """
    Args:
        length: length
        windows: [(y_start, y_stop, layer), ...]
    """
    md5 = hashlib.md5()
    for e in windows:
        md5.update(str(e).encode())

    component = Component()
    component.name = "ARB_SW_L{}_HASH{}".format(length, md5.hexdigest())
    y_min, y_max, layer0 = windows[0]
    y_min, y_max = min(y_min, y_max), max(y_min, y_max)

    # Add one port on each side centered at y=0
    for y_start, y_stop, layer in windows:
        w = abs(y_stop - y_start)
        y = (y_stop + y_start) / 2
        _wg = hline(length=length, width=w, layer=layer).ref()
        _wg.movey(y)
        component.add(_wg)
        component.absorb(_wg)
        y_min = min(y_stop, y_start, y_min)
        y_max = max(y_stop, y_start, y_max)
    width = y_max - y_min

    component.add_port(name="W0",
                       midpoint=[0, 0],
                       width=width,
                       orientation=180,
                       layer=layer0)
    component.add_port(name="E0",
                       midpoint=[length, 0],
                       width=width,
                       orientation=0,
                       layer=layer0)

    return component
Esempio n. 6
0
def waveguide_pin(
    length: float = 10.0,
    width: float = 0.5,
    width_i: float = 0.0,
    width_p: float = 1.0,
    width_n: float = 1.0,
    width_pp: float = 1.0,
    width_np: float = 1.0,
    width_ppp: float = 1.0,
    width_npp: float = 1.0,
    layer_p: Tuple[int, int] = LAYER.P,
    layer_n: Tuple[int, int] = LAYER.N,
    layer_pp: Tuple[int, int] = LAYER.Pp,
    layer_np: Tuple[int, int] = LAYER.Np,
    layer_ppp: Tuple[int, int] = LAYER.Ppp,
    layer_npp: Tuple[int, int] = LAYER.Npp,
    waveguide_factory: Callable = waveguide,
) -> Component:
    """PN doped waveguide

        .. code::


                               |<------width------>|
                                ____________________
                               |     |       |     |
            ___________________|     |       |     |__________________________|
                                     |       |                                |
                P++     P+     P     |   I   |     N        N+         N++    |
            __________________________________________________________________|
                                                                              |
                                     |width_i| width_n | width_np | width_npp |
                                        0    oi        on        onp         onpp

    .. plot::
      :include-source:

      import pp

      c = pp.c.waveguide_pin(length=10)
      pp.plotgds(c)

    """
    c = Component()
    w = c << waveguide_factory(length=length, width=width)
    c.absorb(w)

    oi = width_i / 2
    on = oi + width_n
    onp = oi + width_n + width_np
    onpp = oi + width_n + width_np + width_npp

    # N doping
    c.add_polygon([(0, oi), (length, oi), (length, onpp), (0, onpp)],
                  layer=layer_n)

    if layer_np:
        c.add_polygon([(0, on), (length, on), (length, onpp), (0, onpp)],
                      layer=layer_np)
    if layer_npp:
        c.add_polygon([(0, onp), (length, onp), (length, onpp), (0, onpp)],
                      layer=layer_npp)

    oi = -width_i / 2
    op = oi - width_p
    opp = oi - width_p - width_pp
    oppp = oi - width_p - width_pp - width_ppp

    # P doping
    c.add_polygon([(0, oi), (length, oi), (length, oppp), (0, oppp)],
                  layer=layer_p)
    if layer_pp:
        c.add_polygon([(0, op), (length, op), (length, oppp), (0, oppp)],
                      layer=layer_pp)
    if layer_ppp:
        c.add_polygon([(0, opp), (length, opp), (length, oppp), (0, oppp)],
                      layer=layer_ppp)

    return c
Esempio n. 7
0
def _bend_circular_heater(
    radius: float = 10,
    wg_width: float = 0.5,
    theta: int = -90,
    start_angle: int = 0,
    angle_resolution: float = 2.5,
    heater_to_wg_distance: float = 1.2,
    heater_width: float = 0.5,
) -> Component:
    """ Creates an arc of arclength ``theta`` starting at angle ``start_angle``

    Args:
        radius
        width: of the waveguide
        theta: arc length
        start_angle:
        angle_resolution
    """
    component = Component()

    wg_bend = bend_circular(
        radius=radius,
        width=wg_width,
        theta=theta,
        start_angle=start_angle,
        angle_resolution=angle_resolution,
        layer=LAYER.WG,
    ).ref((0, 0))

    a = heater_to_wg_distance + wg_width / 2 + heater_width / 2

    heater_outer = bend_circular(
        radius=radius + a,
        width=heater_width,
        theta=theta,
        start_angle=start_angle,
        angle_resolution=angle_resolution,
        layer=LAYER.HEATER,
    ).ref((0, -a))

    heater_inner = bend_circular(
        radius=radius - a,
        width=heater_width,
        theta=theta,
        start_angle=start_angle,
        angle_resolution=angle_resolution,
        layer=LAYER.HEATER,
    ).ref((0, a))

    component.add(wg_bend)
    component.add(heater_outer)
    component.add(heater_inner)

    component.absorb(wg_bend)
    component.absorb(heater_outer)
    component.absorb(heater_inner)

    i = 0

    for device in [wg_bend, heater_outer, heater_inner]:
        for port in device.ports.values():
            component.ports["{}".format(i)] = port
            i += 1

    component.info["length"] = wg_bend.info["length"]
    component.radius = radius
    component.width = wg_width
    return component
Esempio n. 8
0
def waveguide_heater(
    length: float = 10.0,
    width: float = 0.5,
    heater_width: float = 0.5,
    heater_spacing: float = 1.2,
    metal_connection: bool = True,
    sstw: float = 2.0,
    trench_width: float = 0.5,
    trench_keep_out: float = 2.0,
    trenches: List[Dict[str, int]] = [
        {
            "nb_segments": 2,
            "lane": 1,
            "x_start_offset": 0
        },
        {
            "nb_segments": 2,
            "lane": -1,
            "x_start_offset": 0
        },
    ],
    layers_heater: List[Tuple[int, int]] = [LAYER.HEATER],
    waveguide_factory: Callable = waveguide,
    layer_trench: Tuple[int, int] = LAYER.DEEPTRENCH,
) -> Component:
    """ waveguide with heater

    .. code::

        TTTTTTTTTTTTT    TTTTTTTTTTTTT <-- trench

        HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH <-- heater

        ------------------------------ <-- waveguide

        HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH <-- heater

        TTTTTTTTTTTTT    TTTTTTTTTTTTT <-- trench

    .. plot::
      :include-source:

      import pp

      c = pp.c.waveguide_heater()
      pp.plotgds(c)

    """
    c = Component()

    _heater = heater(length=length,
                     width=heater_width,
                     layers_heater=layers_heater)

    y_heater = heater_spacing + (width + heater_width) / 2
    heater_top = c << _heater
    heater_bot = c << _heater

    heater_top.movey(+y_heater)
    heater_bot.movey(-y_heater)

    wg = c << waveguide_factory(length=length, width=width)

    for i in [heater_top, heater_bot, wg]:
        c.absorb(i)

    # Add wg ports
    for p in wg.ports.values():
        c.add_port(name=p.name, port=p)

    # Add heater ports
    for p in heater_top.ports.values():
        c.add_port(name="HT" + p.name, port=p)

    for p in heater_bot.ports.values():
        c.add_port(name="HB" + p.name, port=p)

    c.settings["width"] = width
    c.settings["heater_width"] = heater_width
    c.settings["heater_spacing"] = heater_spacing
    c.settings["length"] = length
    add_trenches(c,
                 sstw,
                 trench_width,
                 trench_keep_out,
                 trenches,
                 layer_trench=layer_trench)

    return c
Esempio n. 9
0
def coupler(
    wg_width: float = 0.5,
    gap: float = 0.236,
    length: float = 20.007,
    coupler_symmetric_factory: Callable = coupler_symmetric,
    coupler_straight_factory: Callable = coupler_straight,
    layer: Tuple[int, int] = LAYER.WG,
    layers_cladding: List[Tuple[int, int]] = [LAYER.WGCLAD],
    cladding_offset: float = conf.tech.cladding_offset,
    dy: float = 5.0,
) -> Component:
    r"""symmetric coupler

    Args:
        gap
        length
        coupler_symmetric_factory
        coupler_straight_factory
        layer:
        layers_cladding: list of cladding layers
        cladding_offset: offset from waveguide to cladding edge
        dy: port to port vertical spacing

    .. code::

       W1 __                           __ E1
            \                         /       |
             \        length         /        |
              ======================= gap     | dy
             /                       \        |
           _/                         \_      |
        W0                             E0     |

            coupler_straight_factory  coupler_symmetric_factory

    .. plot::
      :include-source:

      import pp

      c = pp.c.coupler(gap=0.2, length=10)
      pp.plotgds(c)

    """
    assert_on_1nm_grid(length)
    assert_on_1nm_grid(gap)
    c = Component()

    sbend = coupler_symmetric_factory(
        gap=gap,
        wg_width=wg_width,
        layer=layer,
        layers_cladding=layers_cladding,
        cladding_offset=cladding_offset,
        dy=dy,
    )

    sr = c << sbend
    sl = c << sbend
    cs = c << coupler_straight_factory(
        length=length,
        gap=gap,
        width=wg_width,
        layer=layer,
        layers_cladding=layers_cladding,
        cladding_offset=cladding_offset,
    )
    sl.connect("W0", destination=cs.ports["W0"])
    sr.connect("W0", destination=cs.ports["E0"])

    c.add_port("W1", port=sl.ports["E0"])
    c.add_port("W0", port=sl.ports["E1"])
    c.add_port("E0", port=sr.ports["E0"])
    c.add_port("E1", port=sr.ports["E1"])

    c.absorb(sl)
    c.absorb(sr)
    c.absorb(cs)
    return c
Esempio n. 10
0
def coupler(
    wg_width: float = 0.5,
    gap: float = 0.236,
    length: float = 20.007,
    coupler_symmetric_factory: Callable = coupler_symmetric,
    coupler_straight: Callable = coupler_straight,
    layer: Tuple[int, int] = LAYER.WG,
    layers_cladding: List[Tuple[int, int]] = [LAYER.WGCLAD],
    cladding_offset: int = 3,
) -> Component:
    r""" symmetric coupler

    Args:
        gap
        length
        coupler_symmetric_factory
        coupler_straight

    .. code::

       W1 __                           __ E1
            \                         /
             \        length         /
              ======================= gap
             /                        \
           _/                          \_
        W0                              E0

            coupler_straight  coupler_symmetric_factory

    .. plot::
      :include-source:

      import pp

      c = pp.c.coupler(gap=0.2, length=10)
      pp.plotgds(c)

    """
    assert_on_1nm_grid(length)
    assert_on_1nm_grid(gap)
    c = Component()

    sbend = coupler_symmetric_factory(
        gap=gap,
        wg_width=wg_width,
        layer=layer,
        layers_cladding=layers_cladding,
        cladding_offset=cladding_offset,
    )

    sr = c << sbend
    sl = c << sbend
    cs = c << coupler_straight(
        length=length,
        gap=gap,
        width=wg_width,
        layer=layer,
        layers_cladding=layers_cladding,
        cladding_offset=cladding_offset,
    )
    sl.connect("W0", destination=cs.ports["W0"])
    sr.connect("W0", destination=cs.ports["E0"])

    c.add_port("W1", port=sl.ports["E0"])
    c.add_port("W0", port=sl.ports["E1"])
    c.add_port("E0", port=sr.ports["E0"])
    c.add_port("E1", port=sr.ports["E1"])

    c.absorb(sl)
    c.absorb(sr)
    c.absorb(cs)
    return c