コード例 #1
0
ファイル: pad.py プロジェクト: tvt173/gdsfactory
def pad(
    size: Tuple[float, float] = (100.0, 100.0),
    layer: Layer = LAYER.M3,
    layers_cladding: Optional[Tuple[Layer, ...]] = None,
    cladding_offsets: Optional[Tuple[float, ...]] = None,
) -> Component:
    """Rectangular pad with 4 ports (1, 2, 3, 4)

    Args:
        width: pad width
        height: pad height
        layer: pad layer
        layers_cladding:
        cladding_offsets:
    """
    c = Component()
    rect = compass(size=size, layer=layer)
    c_ref = c.add_ref(rect)
    c.add_ports(c_ref.ports)
    c.info.size = (float(size[0]), float(size[1]))
    c.info.layer = layer

    if layers_cladding and cladding_offsets:
        for layer, cladding_offset in zip(layers_cladding, cladding_offsets):
            c.add_ref(
                compass(
                    size=(size[0] + 2 * cladding_offset,
                          size[1] + 2 * cladding_offset),
                    layer=layer,
                ))

    return c
コード例 #2
0
def add_text(
    component: ComponentOrFactory,
    text: str = "",
    text_offset: Float2 = (0, 0),
    text_anchor: Anchor = "cc",
    text_factory: ComponentFactory = text_rectangular_multi_layer,
) -> Component:
    """Returns component inside a new component with text geometry.

    Args:
        component:
        text: text string.
        text_offset: relative to component anchor. Defaults to center (cc).
        text_anchor: relative to component (ce cw nc ne nw sc se sw center cc).
        text_factory: function to add text labels.
    """
    component = component() if callable(component) else component
    component_new = Component()
    component_new.component = component
    ref = component_new.add_ref(component)

    t = component_new << text_factory(text)
    t.move((np.array(text_offset) + getattr(ref.size_info, text_anchor)))

    component_new.add_ports(ref.ports)
    component_new.copy_child_info(component)
    return component_new
コード例 #3
0
ファイル: straight_array.py プロジェクト: simbilod/gdsfactory
def straight_array(n: int = 4,
                   spacing: float = 4.0,
                   straigth: ComponentOrFactory = straight_function,
                   **kwargs) -> Component:
    """Array of straights connected with grating couplers.

    useful to align the 4 corners of the chip

    Args:
        n: number of straights
        spacing: edge to edge straight spacing
        straigth: straigth straight Component or library
        **kwargs
    """

    c = Component()
    wg = straigth(**kwargs) if callable(straigth) else straigth

    for i in range(n):
        wref = c.add_ref(wg)
        wref.y += i * (spacing + wg.info.width)
        c.add_ports(wref.ports, prefix=str(i))

    c.auto_rename_ports()
    return c
コード例 #4
0
def contact(
    size: Tuple[float, float] = (11.0, 11.0),
    layers: Tuple[Layer, ...] = (LAYER.M1, LAYER.M2, LAYER.M3),
    vias: Optional[Tuple[Optional[ComponentOrFactory], ...]] = (via1, via2),
    layer_port: Optional[Layer] = None,
) -> Component:
    """Rectangular contact

    Args:
        size: of the layers
        layers: layers on which to draw rectangles
        vias: vias to use to fill the rectangles
        layer_port: if None asumes port is on the last layer
    """

    width, height = size
    a = width / 2
    b = height / 2
    layer_port = layer_port or layers[-1]

    c = Component()
    c.height = height
    c.info.size = (float(size[0]), float(size[1]))
    c.info.layer = layer_port

    for layer in layers:
        ref = c << compass(size=(width, height), layer=layer)

        if layer == layer_port:
            c.add_ports(ref.ports)

    vias = vias or []
    for via in vias:
        if via is not None:
            via = via() if callable(via) else via

            w, h = via.info["size"]
            g = via.info["enclosure"]
            pitch_x, pitch_y = via.info["spacing"]

            nb_vias_x = (width - w - 2 * g) / pitch_x + 1
            nb_vias_y = (height - h - 2 * g) / pitch_y + 1

            nb_vias_x = int(floor(nb_vias_x)) or 1
            nb_vias_y = int(floor(nb_vias_y)) or 1
            ref = c.add_array(
                via, columns=nb_vias_x, rows=nb_vias_y, spacing=(pitch_x, pitch_y)
            )

            cw = (width - (nb_vias_x - 1) * pitch_x - w) / 2
            ch = (height - (nb_vias_y - 1) * pitch_y - h) / 2
            x0 = -a + cw + w / 2
            y0 = -b + ch + h / 2
            ref.move((x0, y0))

    return c
コード例 #5
0
ファイル: bend_circular.py プロジェクト: simbilod/gdsfactory
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
コード例 #6
0
def bend_s(size: Float2 = (10.0, 2.0),
           nb_points: int = 99,
           with_cladding_box: bool = True,
           cross_section: CrossSectionFactory = strip,
           **kwargs) -> Component:
    """S bend with bezier curve

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

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

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

    c = Component()

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

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

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

    auto_rename_ports(c)
    return c
コード例 #7
0
def add_electrical_pads_shortest(
        component: Component,
        pad: ComponentOrFactory = pad_function,
        pad_port_spacing: float = 50.0,
        select_ports=select_ports_electrical,
        port_orientation: int = 90,
        layer: gf.types.Layer = (31, 0),
        **kwargs,
) -> Component:
    """Add pad to each closest electrical port.

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

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

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

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

    c.add_ports(ref.ports)
    for port in ports:
        c.ports.pop(port.name)
    c.copy_child_info(component)
    return c
コード例 #8
0
def mirror(component: Component, p1: Float2 = (0, 1), p2: Float2 = (0, 0)) -> Component:
    """Returns mirrored component inside a new component.

    Args:
        p1: first point to define mirror axis
        p2: second point to define mirror axis
    """
    component_new = Component()
    component_new.component = component
    ref = component_new.add_ref(component)
    ref.mirror(p1=p1, p2=p2)
    component_new.add_ports(ref.ports)
    component_new.copy_child_info(component)
    return component_new
コード例 #9
0
def move(
    component: Component,
    origin=(0, 0),
    destination=None,
    axis: Optional[str] = None,
) -> Component:
    """Return container that contains a reference to the original component."""
    component_new = Component()
    component_new.component = component
    ref = component_new.add_ref(component)
    ref.move(origin=origin, destination=destination, axis=axis)
    component_new.add_ports(ref.ports)
    component_new.copy_child_info(component)
    return component_new
コード例 #10
0
def add_electrical_pads_top_dc(
    component: Component,
    spacing: Float2 = (0.0, 100.0),
    pad_array: ComponentFactory = pad_array_function,
    select_ports: Callable = select_ports_electrical,
    **kwargs,
) -> Component:
    """connects component electrical ports with pad array at the top

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

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

    for port in ports_component:
        port.orientation = 90

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

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

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

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

    c.copy_child_info(component)
    return c
コード例 #11
0
def add_fidutials(component: ComponentFactory = pad_array,
                  gap: float = 50,
                  left: Optional[ComponentFactory] = cross,
                  right: Optional[ComponentFactory] = cross,
                  top: Optional[ComponentFactory] = None,
                  bottom: Optional[ComponentFactory] = None,
                  **kwargs) -> Component:
    """Return component with fidutials.

    Args:
        component: component to add to the new component.
        gap: from component to fidutial edge.
        left: optional left fidutial.
        right: optional right fidutial.
        top: optional top fidutial.
        bottom: optional bottom fidutial.

    """
    c = Component()
    component = component(**kwargs)
    r = c << component

    if left:
        x1 = c << left()
        x1.xmax = r.xmin - gap

    if right:
        x2 = c << right()
        x2.xmin = r.xmax + gap

    if top:
        y1 = c << top()
        y1.ymin = r.ymax + gap

    if bottom:
        y2 = c << bottom()
        y2.ymin = r.ymin - gap

    c.add_ports(r.ports)
    c.copy_child_info(component)
    return c
コード例 #12
0
def rectangle(
    size: Tuple[float, float] = (4.0, 2.0),
    layer: Layer = (1, 0),
    centered: bool = False,
    port_type: str = "electrical",
) -> Component:
    """rectangle

    Args:
        size: (tuple) Width and height of rectangle.
        layer: Specific layer to put polygon geometry on.
        centered: True sets center to (0, 0), False sets south-west to (0, 0)
        port_type:

    """
    c = Component()
    ref = c << compass(size=size, layer=layer, port_type=port_type)
    if not centered:
        ref.move((size[0] / 2, size[1] / 2))
    c.add_ports(ref.ports)
    return c
コード例 #13
0
def rotate(
    component: ComponentOrFactory,
    angle: int = 90,
) -> Component:
    """Returns rotated component inside a new component.

    Most times you just need to place a reference and rotate it.
    This rotate function just encapsulates the rotated reference into a new component.

    Args:
        component:
        angle: in degrees
    """
    component = component() if callable(component) else component
    component_new = Component()
    component_new.component = component
    ref = component_new.add_ref(component)
    ref.rotate(angle)
    component_new.add_ports(ref.ports)
    component_new.copy_child_info(component)
    return component_new
コード例 #14
0
def add_electrical_pads_top(
    component: Component,
    spacing: Float2 = (0.0, 100.0),
    pad_array: ComponentFactory = pad_array_function,
    select_ports=select_ports_electrical,
    **kwargs,
) -> Component:
    """Returns new component with electrical ports connected to top pad array

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

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

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

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

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

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

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

    c.add_ports(ref.ports)
    for port in ports:
        c.ports.pop(port.name)
    c.copy_child_info(component)
    return c
コード例 #16
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
コード例 #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
コード例 #18
0
def mzi_lattice(
    coupler_lengths: Tuple[float, ...] = (10.0, 20.0),
    coupler_gaps: Tuple[float, ...] = (0.2, 0.3),
    delta_lengths: Tuple[float, ...] = (10.0, ),
    mzi: ComponentFactory = mzi_coupler,
    splitter: ComponentFactory = coupler_function,
    **kwargs,
) -> Component:
    r"""Mzi lattice filter.

    Args:
        coupler_lengths: list of length for each coupler
        coupler_gaps: list of coupler gaps
        delta_lengths: list of length differences
        mzi: function for the mzi
        splitter: splitter function

    keyword Args:
        length_y: vertical length for both and top arms
        length_x: horizontal length
        bend: 90 degrees bend library
        straight: straight function
        straight_y: straight for length_y and delta_length
        straight_x_top: top straight for length_x
        straight_x_bot: bottom straight for length_x
        cross_section: for routing (sxtop/sxbot to combiner)

    .. code::

               ______             ______
              |      |           |      |
              |      |           |      |
         cp1==|      |===cp2=====|      |=== .... ===cp_last===
              |      |           |      |
              |      |           |      |
             DL1     |          DL2     |
              |      |           |      |
              |______|           |      |
                                 |______|

    """
    assert len(coupler_lengths) == len(coupler_gaps)
    assert len(coupler_lengths) == len(delta_lengths) + 1

    c = Component()

    splitter_settings = dict(gap=coupler_gaps[0], length=coupler_lengths[0])
    combiner_settings = dict(gap=coupler_gaps[1], length=coupler_lengths[1])

    splitter1 = partial(splitter, **splitter_settings)
    combiner1 = partial(splitter, **combiner_settings)

    cp1 = splitter1()

    sprevious = c << mzi(
        splitter=splitter1,
        combiner=combiner1,
        with_splitter=True,
        delta_length=delta_lengths[0],
        **kwargs,
    )
    c.add_ports(sprevious.get_ports_list(port_type="electrical"))

    stages = []

    for length, gap, delta_length in zip(coupler_lengths[2:], coupler_gaps[2:],
                                         delta_lengths[1:]):

        splitter_settings = dict(gap=coupler_gaps[1],
                                 length=coupler_lengths[1])
        combiner_settings = dict(length=length, gap=gap)
        splitter1 = partial(splitter, **splitter_settings)
        combiner1 = partial(splitter, **combiner_settings)

        stage = c << mzi(
            splitter=splitter1,
            combiner=combiner1,
            with_splitter=False,
            delta_length=delta_length,
            **kwargs,
        )
        splitter_settings = combiner_settings

        stages.append(stage)
        c.add_ports(stage.get_ports_list(port_type="electrical"))

    for stage in stages:
        stage.connect("o1", sprevious.ports["o4"])
        # stage.connect('o2', sprevious.ports['o1'])
        sprevious = stage

    for port in cp1.get_ports_list(orientation=180, port_type="optical"):
        c.add_port(port.name, port=port)

    for port in sprevious.get_ports_list(orientation=0, port_type="optical"):
        c.add_port(f"o_{port.name}", port=port)

    c.auto_rename_ports()
    return c
コード例 #19
0
ファイル: contact_slot.py プロジェクト: tvt173/gdsfactory
def contact_slot(
    size: Tuple[float, float] = (11.0, 11.0),
    layers: Tuple[Layer, ...] = (LAYER.M1, LAYER.M2),
    layer_offsets: Tuple[float, ...] = (0, 1.0),
    layer_port: Optional[Layer] = None,
    via: ComponentOrFactory = via1,
    enclosure: float = 1.0,
    ysize: float = 0.5,
    yspacing: float = 2.0,
) -> Component:
    """Rectangular contact with slotted via in X direction

    Args:
        size: of the layers
        layers: layers on which to draw rectangles
        layer_offsets: cladding_offset for each layer
        layer_port: if None asumes port is on the last layer
        via: via to use to fill the rectangles
        enclosure: of the via by rectangle
        ysize: via height in y
        yspacing: via spacing pitch in y

    .. code::

        enclosure
        _____________________________________
        |<--->                              |
        |      ______________________       |
        |     |                      |      |
        |     |                      | ysize|
        |     |______________________|      |
        |  |                                |
        |  | yspacing                       |
        |  |                                |
        |  |   ______________________       |
        |  |  |                      |      |
        |  |  |                      | ysize|
        |  |  |______________________|      |
        |                                   |
        |___________________________________|
                        size[0]


    """

    layer_port = layer_port or layers[-1]

    c = Component()

    for layer, offset in zip(layers, list(layer_offsets) + [0] * len(layers)):
        ref = c << compass(size=(size[0] + 2 * offset, size[1] + 2 * offset),
                           layer=layer)

        if layer == layer_port:
            c.add_ports(ref.ports)

    via = via(size=(size[0] - 2 * enclosure, ysize)) if callable(via) else via

    nb_vias_y = (size[1] - 2 * enclosure) / yspacing
    nb_vias_y = int(floor(nb_vias_y)) or 1
    ref = c.add_array(via, columns=1, rows=nb_vias_y, spacing=(0, yspacing))
    dy = (size[1] - (nb_vias_y - 1) * yspacing - size[1]) / 2
    ref.move((0, dy))
    return c
コード例 #20
0
def mzi(
    delta_length: float = 10.0,
    length_y: float = 2.0,
    length_x: Optional[float] = 0.1,
    bend: ComponentOrFactory = bend_euler,
    straight: ComponentFactory = straight_function,
    straight_y: Optional[ComponentFactory] = None,
    straight_x_top: Optional[ComponentFactory] = None,
    straight_x_bot: Optional[ComponentFactory] = None,
    splitter: ComponentOrFactory = mmi1x2,
    combiner: Optional[ComponentFactory] = None,
    with_splitter: bool = True,
    port_e1_splitter: str = "o2",
    port_e0_splitter: str = "o3",
    port_e1_combiner: str = "o2",
    port_e0_combiner: str = "o3",
    nbends: int = 2,
    cross_section: CrossSectionFactory = strip,
) -> Component:
    """Mzi.

    Args:
        delta_length: bottom arm vertical extra length
        length_y: vertical length for both and top arms
        length_x: horizontal length. None uses to the straight_x_bot/top defaults
        bend: 90 degrees bend library
        straight: straight function
        straight_y: straight for length_y and delta_length
        straight_x_top: top straight for length_x
        straight_x_bot: bottom straight for length_x
        splitter: splitter function
        combiner: combiner function
        with_splitter: if False removes splitter
        port_e1_combiner: east top combiner port
        port_e0_splitter: east bot splitter port
        port_e1_splitter: east top splitter port
        port_e0_combiner: east bot combiner port
        nbends: from straight top/bot to combiner (at least 2)
        cross_section: for routing (sxtop/sxbot to combiner)

    .. code::

                       b2______b3
                      |  sxtop  |
              straight_y        |
                      |         |
                      b1        b4
            splitter==|         |==combiner
                      b5        b8
                      |         |
              straight_y        |
                      |         |
        delta_length/2          |
                      |         |
                     b6__sxbot__b7
                          Lx
    """
    combiner = combiner or splitter
    straight = partial(straight, cross_section=cross_section)

    straight_x_top = straight_x_top or straight
    straight_x_bot = straight_x_bot or straight
    straight_y = straight_y or straight
    bend_factory = bend
    bend = bend_factory(cross_section=cross_section)

    c = Component()
    cp1 = splitter() if callable(splitter) else splitter
    cp2 = combiner() if combiner else cp1

    if with_splitter:
        cp1 = c << cp1

    cp2 = c << cp2
    b5 = c << bend
    b5.mirror()
    b5.connect("o1", cp1.ports[port_e0_splitter])

    syl = c << straight_y(length=delta_length / 2 + length_y, )
    syl.connect("o1", b5.ports["o2"])
    b6 = c << bend
    b6.connect("o1", syl.ports["o2"])

    straight_x_bot = straight_x_bot(
        length=length_x) if length_x else straight_x_bot()
    sxb = c << straight_x_bot
    sxb.connect("o1", b6.ports["o2"])

    b1 = c << bend
    b1.connect("o1", cp1.ports[port_e1_splitter])

    sy = c << straight_y(length=length_y)
    sy.connect("o1", b1.ports["o2"])

    b2 = c << bend
    b2.connect("o2", sy.ports["o2"])
    straight_x_top = straight_x_top(
        length=length_x) if length_x else straight_x_top()
    sxt = c << straight_x_top
    sxt.connect("o1", b2.ports["o1"])

    cp2.mirror()
    cp2.xmin = sxt.ports["o2"].x + bend.info["radius"] * nbends + 0.1

    route = get_route(
        sxt.ports["o2"],
        cp2.ports[port_e1_combiner],
        straight=straight,
        bend=bend_factory,
        cross_section=cross_section,
    )
    c.add(route.references)
    route = get_route(
        sxb.ports["o2"],
        cp2.ports[port_e0_combiner],
        straight=straight,
        bend=bend_factory,
        cross_section=cross_section,
    )
    c.add(route.references)

    if with_splitter:
        c.add_ports(cp1.get_ports_list(orientation=180), prefix="in")
    else:
        c.add_port("o1", port=b1.ports["o1"])
        c.add_port("o2", port=b5.ports["o1"])
    c.add_ports(cp2.get_ports_list(orientation=0), prefix="out")
    c.add_ports(sxt.get_ports_list(port_type="electrical"), prefix="top")
    c.add_ports(sxb.get_ports_list(port_type="electrical"), prefix="bot")
    c.auto_rename_ports()
    return c
コード例 #21
0
def straight_heater_metal_undercut(
    length: float = 320.0,
    length_undercut_spacing: float = 6.0,
    length_undercut: float = 30.0,
    length_straight_input: float = 15.0,
    heater_width: float = 2.5,
    cross_section_heater: CrossSectionFactory = strip_heater_metal,
    cross_section_heater_undercut: CrossSectionFactory = strip_heater_metal_undercut,
    with_undercut: bool = True,
    contact: Optional[ComponentFactory] = contact_heater,
    port_orientation1: int = 180,
    port_orientation2: int = 0,
    taper_length: Optional[float] = 5.0,
    **kwargs,
) -> Component:
    """Returns a thermal phase shifter.
    dimensions from https://doi.org/10.1364/OE.27.010456

    Args:
        length: of the waveguide
        length_undercut_spacing: from undercut regions
        length_undercut: length of each undercut section
        length_straight_input: from input port to where trenches start
        cross_section_heater: for heated sections
        cross_section_heater_undercut: for heated sections with undercut
        with_undercut: isolation trenches for higher efficiency
        contact: via stack
        port_orientation1: left via stack port orientation
        port_orientation2: right via stack port orientation
        kwargs: cross_section common settings
    """
    period = length_undercut + length_undercut_spacing
    n = int((length - 2 * length_straight_input) // period)

    length_straight_input = (length - n * period) / 2

    s_si = gf.c.straight(
        cross_section=cross_section_heater,
        length=length_straight_input,
        heater_width=heater_width,
        **kwargs,
    )
    cross_section_undercut = (
        cross_section_heater_undercut if with_undercut else cross_section_heater
    )
    s_uc = gf.c.straight(
        cross_section=cross_section_undercut,
        length=length_undercut,
        heater_width=heater_width,
        **kwargs,
    )
    s_spacing = gf.c.straight(
        cross_section=cross_section_heater,
        length=length_undercut_spacing,
        heater_width=heater_width,
        **kwargs,
    )
    symbol_to_component = {
        "-": (s_si, "o1", "o2"),
        "U": (s_uc, "o1", "o2"),
        "H": (s_spacing, "o1", "o2"),
    }

    # Each character in the sequence represents a component
    sequence = "-" + n * "UH" + "-"

    c = Component()
    sequence = gf.components.component_sequence(
        sequence=sequence, symbol_to_component=symbol_to_component
    )
    c.add_ref(sequence)
    c.add_ports(sequence.ports)

    if contact:
        contactw = contact()
        contacte = contact()
        contact_west_midpoint = sequence.aliases["-1"].size_info.cw
        contact_east_midpoint = sequence.aliases["-2"].size_info.ce
        dx = contactw.get_ports_xsize() / 2 + taper_length or 0

        contact_west = c << contactw
        contact_east = c << contacte
        contact_west.move(contact_west_midpoint - (dx, 0))
        contact_east.move(contact_east_midpoint + (dx, 0))
        c.add_port(
            "e1", port=contact_west.get_ports_list(orientation=port_orientation1)[0]
        )
        c.add_port(
            "e2", port=contact_east.get_ports_list(orientation=port_orientation2)[0]
        )
        if taper_length:
            x = cross_section_heater()
            taper = gf.c.taper(
                width1=contactw.ports["e1"].width,
                width2=heater_width,
                length=taper_length,
                layer=x.info["layer_heater"],
            )
            taper1 = c << taper
            taper2 = c << taper
            taper1.connect("o1", contact_west.ports["e3"])
            taper2.connect("o1", contact_east.ports["e1"])
    return c
コード例 #22
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
コード例 #23
0
ファイル: mzi_arms.py プロジェクト: simbilod/gdsfactory
def mzi_arms(
    delta_length: float = 10.0,
    length_y: float = 0.8,
    length_x: float = 0.1,
    bend: ComponentOrFactory = bend_euler,
    straight: ComponentFactory = straight_function,
    straight_y: Optional[ComponentFactory] = None,
    straight_x_top: Optional[ComponentFactory] = None,
    straight_x_bot: Optional[ComponentFactory] = None,
    splitter: ComponentOrFactory = mmi1x2,
    combiner: Optional[ComponentFactory] = None,
    with_splitter: bool = True,
    delta_yright: float = 0,
    **kwargs,
) -> Component:
    """Mzi made with arms.

    This MZI code is slightly deprecated
    You can find a more robust mzi in gf.components.mzi

    Args:
        delta_length: bottom arm vertical extra length
        length_y: vertical length for both and top arms
        length_x: horizontal length
        bend: 90 degrees bend library
        straight: straight function
        straight_y: straight for length_y and delta_length
        straight_x_top: top straight for length_x
        straight_x_bot: bottom straight for length_x
        splitter: splitter function
        combiner: combiner function
        with_splitter: if False removes splitter
        delta_yright: extra length for right y-oriented waveguide
        kwargs: cross_section settings

    .. code::

                   __Lx__
                  |      |
                  Ly     Lyr (not a parameter)
                  |      |
        splitter==|      |==combiner
                  |      |
                  Ly     Lyr (not a parameter)
                  |      |
                  | delta_length/2
                  |      |
                  |__Lx__|

            ____________           __________
            |          |          |
            |          |       ___|
        ____|          |____
            | splitter   d1     d2  combiner
        ____|           ____
            |          |       ____
            |          |          |
            |__________|          |__________
    """
    combiner = combiner or splitter

    straight_x_top = straight_x_top or straight
    straight_x_bot = straight_x_bot or straight
    straight_y = straight_y or straight

    c = Component()
    cp1 = splitter() if callable(splitter) else splitter
    cp2 = combiner() if combiner else cp1

    if with_splitter:
        cin = c << cp1
    cout = c << cp2

    ports_cp1 = cp1.get_ports_list(clockwise=False)
    ports_cp2 = cp2.get_ports_list(clockwise=False)

    port_e1_cp1 = ports_cp1[1]
    port_e0_cp1 = ports_cp1[0]

    port_e1_cp2 = ports_cp2[1]
    port_e0_cp2 = ports_cp2[0]

    y1t = port_e1_cp1.y
    y1b = port_e0_cp1.y

    y2t = port_e1_cp2.y
    y2b = port_e0_cp2.y

    d1 = abs(y1t - y1b)  # splitter ports distance
    d2 = abs(y2t - y2b)  # combiner ports distance

    delta_symm_half = -delta_yright / 2

    if d2 > d1:
        length_y_left = length_y + (d2 - d1) / 2
        length_y_right = length_y
    else:
        length_y_right = length_y + (d1 - d2) / 2
        length_y_left = length_y

    _top_arm = mzi_arm(
        straight_x=straight_x_top,
        straight_y=straight_y,
        length_x=length_x,
        length_y_left=length_y_left + delta_symm_half,
        length_y_right=length_y_right + delta_symm_half + delta_yright,
        bend=bend,
        **kwargs,
    )

    top_arm = c << _top_arm

    bot_arm = c << mzi_arm(
        straight_x=straight_x_bot,
        straight_y=straight_y,
        length_x=length_x,
        length_y_left=length_y_left + delta_length / 2,
        length_y_right=length_y_right + delta_length / 2,
        bend=bend,
        **kwargs,
    )

    bot_arm.mirror()
    top_arm.connect("o1", port_e1_cp1)
    bot_arm.connect("o1", port_e0_cp1)
    cout.connect(port_e1_cp2.name, bot_arm.ports["o2"])
    if with_splitter:
        c.add_ports(cin.get_ports_list(orientation=180), prefix="in")
    else:
        c.add_port("o1", port=bot_arm.ports["o1"])
        c.add_port("o2", port=top_arm.ports["o1"])

    c.add_ports(cout.get_ports_list(orientation=0), prefix="out")
    c.add_ports(top_arm.get_ports_list(port_type="electrical"), prefix="top")
    c.add_ports(bot_arm.get_ports_list(port_type="electrical"), prefix="bot")
    c.auto_rename_ports()
    return c
コード例 #24
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
コード例 #25
0
ファイル: contact.py プロジェクト: simbilod/gdsfactory
def contact(
    size: Tuple[float, float] = (11.0, 11.0),
    layers: Tuple[Layer, ...] = (LAYER.M1, LAYER.M2, LAYER.M3),
    vias: Optional[Tuple[Optional[ComponentOrFactory], ...]] = (via1, via2),
    layer_port: Optional[Layer] = None,
) -> Component:
    """Rectangular via array stack

    You can use it to connect different metal layers or metals to silicon.
    You can use the naming convention contact_layerSource_layerDestination

    Via array / stack name is more common for contacting metal while
    contact is used for contacting silicon

    http://www.vlsi-expert.com/2017/12/vias.html

    Args:
        size: of the layers
        layers: layers on which to draw rectangles
        vias: vias to use to fill the rectangles
        layer_port: if None asumes port is on the last layer
    """

    width, height = size
    a = width / 2
    b = height / 2
    layer_port = layer_port or layers[-1]

    c = Component()
    c.height = height
    c.info.size = (float(size[0]), float(size[1]))
    c.info.layer = layer_port

    for layer in layers:
        ref = c << compass(size=(width, height), layer=layer)

        if layer == layer_port:
            c.add_ports(ref.ports)

    vias = vias or []
    for via in vias:
        if via is not None:
            via = via() if callable(via) else via

            w, h = via.info["size"]
            g = via.info["enclosure"]
            pitch_x, pitch_y = via.info["spacing"]

            nb_vias_x = (width - w - 2 * g) / pitch_x + 1
            nb_vias_y = (height - h - 2 * g) / pitch_y + 1

            nb_vias_x = int(floor(nb_vias_x)) or 1
            nb_vias_y = int(floor(nb_vias_y)) or 1
            ref = c.add_array(via,
                              columns=nb_vias_x,
                              rows=nb_vias_y,
                              spacing=(pitch_x, pitch_y))

            cw = (width - (nb_vias_x - 1) * pitch_x - w) / 2
            ch = (height - (nb_vias_y - 1) * pitch_y - h) / 2
            x0 = -a + cw + w / 2
            y0 = -b + ch + h / 2
            ref.move((x0, y0))

    return c
コード例 #26
0
def straight_pin_slot(
    length: float = 500.0,
    cross_section: CrossSectionFactory = pin,
    contact: ComponentFactory = contact_metal,
    contact_slot: ComponentFactory = contact_slot_slab,
    contact_width: float = 10.0,
    contact_spacing: float = 2,
    port_orientation_top: int = 0,
    port_orientation_bot: int = 180,
    taper: Optional[ComponentFactory] = taper_strip_to_ridge,
    **kwargs,
) -> Component:
    """Returns PIN doped waveguide with contacts with slotted via

    https://doi.org/10.1364/OE.26.029983

    500um length from
    https://ieeexplore.ieee.org/document/8268112

    Args:
        length: of the waveguide
        cross_section: for the waveguide
        contact: for the contacts
        contact_size:
        contact_spacing: spacing between contacts
        port_orientation_top: for top contact
        port_orientation_bot: for bottom contact
        taper: optional taper
        kwargs: cross_section settings

    """
    c = Component()
    if taper:
        taper = taper() if callable(taper) else taper
        length -= 2 * taper.get_ports_xsize()

    wg = c << gf.c.straight(
        cross_section=cross_section,
        length=length,
        **kwargs,
    )

    if taper:
        t1 = c << taper
        t2 = c << taper
        t1.connect("o2", wg.ports["o1"])
        t2.connect("o2", wg.ports["o2"])
        c.add_port("o1", port=t1.ports["o1"])
        c.add_port("o2", port=t2.ports["o1"])

    else:
        c.add_ports(wg.get_ports_list())

    contact_length = length
    contact_top = c << contact(
        size=(contact_length, contact_width),
    )
    contact_bot = c << contact(
        size=(contact_length, contact_width),
    )

    contact_bot.xmin = wg.xmin
    contact_top.xmin = wg.xmin

    contact_top.ymin = +contact_spacing / 2
    contact_bot.ymax = -contact_spacing / 2

    slot_top = c << contact_slot(
        size=(contact_length, contact_width),
    )
    slot_bot = c << contact_slot(
        size=(contact_length, contact_width),
    )

    slot_bot.xmin = wg.xmin
    slot_top.xmin = wg.xmin
    slot_top.ymin = +contact_spacing / 2
    slot_bot.ymax = -contact_spacing / 2

    c.add_port(
        "e1", port=contact_top.get_ports_list(orientation=port_orientation_top)[0]
    )
    c.add_port(
        "e2", port=contact_bot.get_ports_list(orientation=port_orientation_bot)[0]
    )
    return c
コード例 #27
0
def straight_heater_doped_rib(
    length: float = 320.0,
    nsections: int = 3,
    cross_section: CrossSectionFactory = strip_rib_tip,
    cross_section_heater: CrossSectionFactory = rib_heater_doped,
    contact: Optional[ComponentFactory] = contact_slab_npp_m3,
    contact_metal: Optional[ComponentFactory] = contact_metal_function,
    contact_metal_size: Tuple[float, float] = (10.0, 10.0),
    contact_size: Tuple[float, float] = (10.0, 10.0),
    taper: Optional[ComponentOrFactory] = taper_cross_section,
    with_taper1: bool = True,
    with_taper2: bool = True,
    heater_width: float = 2.0,
    heater_gap: float = 0.8,
    contact_gap: float = 0.0,
    width: float = 0.5,
    with_top_contact: bool = True,
    with_bot_contact: bool = True,
    **kwargs
) -> Component:
    r"""Returns a doped thermal phase shifter.
    dimensions from https://doi.org/10.1364/OE.27.010456

    Args:
        length: of the waveguide
        nsections: between contacts
        cross_section: for the input/output ports
        cross_section_heater: for the heater
        contact: function to connect the heated strip
        contact_metal: function to connect the metal area
        contact_metal_size:
        contact_size:
        taper: optional taper function
        heater_width:
        heater_gap:
        contact_gap: from edge of contact to waveguide
        width: waveguide width on the ridge
        kwargs: cross_section settings

    .. code::

                              length
        |<--------------------------------------------->|
        |              length_section                   |
        |    <--------------------------->              |
        |  length_contact                               |
        |    <------->                             taper|
        |    _________                    _________     |
        |   |        |                    |        |    |
        |   | contact|____________________|        |    |
        |   |  size  |    heater width    |        |    |
        |  /|________|____________________|________|\   |
        | / |             heater_gap               | \  |
        |/  |______________________________________|  \ |
         \  |_______________width__________________|  /
          \ |                                      | /
           \|_____________heater_gap______________ |/
            |        |                    |        |
            |        |____heater_width____|        |
            |        |                    |        |
            |________|                    |________|

        taper         cross_section_heater



                                   |<------width------>|
                                    ____________________ heater_gap             slab_gap
             top_contact           |                   |<---------->| bot_contact   <-->
         ___ ______________________|                   |___________________________|___
        |   |            |               undoped Si                 |              |   |
        |   |layer_heater|               intrinsic region           |layer_heater  |   |
        |___|____________|__________________________________________|______________|___|
                                                                     <------------>
                                                                      heater_width
        <------------------------------------------------------------------------------>
                                       slab_width

    """
    c = Component()
    cross_section_heater = gf.partial(
        cross_section_heater,
        heater_width=heater_width,
        heater_gap=heater_gap,
        width=width,
        **kwargs
    )

    if taper:
        taper = (
            taper(cross_section1=cross_section, cross_section2=cross_section_heater)
            if callable(taper)
            else taper
        )
        length -= taper.get_ports_xsize() * 2

    wg = c << gf.c.straight(
        cross_section=cross_section_heater,
        length=snap_to_grid(length),
    )

    if taper:
        if with_taper1:
            taper1 = c << taper
            taper1.connect("o2", wg.ports["o1"])
            c.add_port("o1", port=taper1.ports["o1"])
        else:
            c.add_port("o1", port=wg.ports["o1"])

        if with_taper2:
            taper2 = c << taper
            taper2.mirror()
            taper2.connect("o2", wg.ports["o2"])
            c.add_port("o2", port=taper2.ports["o1"])

        else:
            c.add_port("o2", port=wg.ports["o2"])
    else:
        c.add_port("o2", port=wg.ports["o2"])
        c.add_port("o1", port=wg.ports["o1"])

    if contact_metal:
        contact_section = contact_metal(size=contact_metal_size)
    contacts = []
    length_contact = snap_to_grid(contact_size[1])
    length_section = snap_to_grid((length - length_contact) / nsections)
    x0 = contact_size[0] / 2
    for i in range(0, nsections + 1):
        xi = x0 + length_section * i

        if contact_metal and contact:
            contact_center = c.add_ref(contact_section)
            contact_center.x = xi
            contact_ref = c << contact_section
            contact_ref.x = xi
            contact_ref.y = (
                +contact_metal_size[1] if i % 2 == 0 else -contact_metal_size[1]
            )
            contacts.append(contact_ref)

        if contact:
            if with_top_contact:
                contact_top = c << contact(size=contact_size)
                contact_top.x = xi
                contact_top.ymin = +(heater_gap + width / 2 + contact_gap)

            if with_bot_contact:
                contact_bot = c << contact(size=contact_size)
                contact_bot.x = xi
                contact_bot.ymax = -(heater_gap + width / 2 + contact_gap)

    if contact_metal and contact:
        contact_length = length + contact_metal_size[0]
        contact_top = c << contact_metal(
            size=(contact_length, contact_metal_size[0]),
        )
        contact_bot = c << contact_metal(
            size=(contact_length, contact_metal_size[0]),
        )

        contact_bot.xmin = contacts[0].xmin
        contact_top.xmin = contacts[0].xmin

        contact_top.ymin = contacts[0].ymax
        contact_bot.ymax = contacts[1].ymin

        c.add_ports(contact_top.ports, prefix="top_")
        c.add_ports(contact_bot.ports, prefix="bot_")
    return c
コード例 #28
0
def contact_slot(
    size: Float2 = (11.0, 11.0),
    layers: Layers = (LAYER.M1, LAYER.M2),
    layer_offsets: Optional[Floats] = (0, 1.0),
    layer_offsetsx: Optional[Floats] = None,
    layer_offsetsy: Optional[Floats] = None,
    layer_port: Optional[Layer] = None,
    via: ComponentOrFactory = via1,
    enclosure: float = 1.0,
    ysize: float = 0.5,
    yspacing: float = 2.0,
) -> Component:
    """Rectangular contact with slotted via in X direction

    Args:
        size: of the layers
        layers: layers on which to draw rectangles
        layer_offsets: cladding_offset for each layer
        layer_offsetsx: optional xoffset for layers, defaults to layer_offsets
        layer_offsetsx: optional yoffset for layers, defaults to layer_offsets
        layer_port: if None asumes port is on the last layer
        via: via to use to fill the rectangles
        enclosure: of the via by rectangle
        ysize: via height in y
        yspacing: via spacing pitch in y

    .. code::


         __________________________________________
        |                |                        |
        |                | layer_offsetsy[1]      |
        |  ______________|______________________  |
        |  |<--->                              |<>|
        |  |enclosure                          | layer_offsetsx[1]
        |  |      ______________________       |  |
        |  |     |                      |      |  |
        |  |     |     via              | ysize|  |
        |  |     |______________________|      |  |
        |  |  |                                |  |
        |  |  | yspacing                size[1]|  |
        |  |  |                                |  |
        |  |  |   ______________________       |  |
        |  |  |  |                      |      |  |
        |  |  |  |     via              | ysize|  |
        |  |  |  |______________________|      |  |
        |  |                                   |  |
        |  |                                   |  |
        |  |___________________________________|  |
        |                  size[0]                |
        |                                         |
        |_________________________________________|

    """
    if size[0] - 2 * enclosure < 0:
        raise ValueError(
            f"Contact length (size[0] = {size[0]}) < 2*enclosure ({2*enclosure}). "
        )
    if size[1] - 2 * enclosure < 0:
        raise ValueError(
            f"Contact width (size[1] = {size[1]}) < 2*enclosure ({2*enclosure}). "
        )

    layer_port = layer_port or layers[-1]

    c = Component()
    c.info.size = (float(size[0]), float(size[1]))

    layer_offsetsx = layer_offsetsx or layer_offsets
    layer_offsetsy = layer_offsetsy or layer_offsets

    layer_offsetsx = list(layer_offsetsx) + [0] * len(layers)
    layer_offsetsy = list(layer_offsetsy) + [0] * len(layers)

    for layer, offsetx, offsety in zip(layers, layer_offsetsx, layer_offsetsy):
        ref = c << compass(size=(size[0] + 2 * offsetx, size[1] + 2 * offsety),
                           layer=layer)

        if layer == layer_port:
            c.add_ports(ref.ports)

    via = via(size=(size[0] - 2 * enclosure, ysize)) if callable(via) else via

    nb_vias_y = (size[1] - 2 * enclosure) / yspacing
    nb_vias_y = int(floor(nb_vias_y)) or 1
    ref = c.add_array(via, columns=1, rows=nb_vias_y, spacing=(0, yspacing))
    dy = (size[1] - (nb_vias_y - 1) * yspacing - size[1]) / 2
    ref.move((0, dy))
    return c
コード例 #29
0
def ring_double_heater(
        gap: float = 0.2,
        radius: float = 10.0,
        length_x: float = 0.01,
        length_y: float = 0.01,
        coupler_ring: ComponentFactory = coupler_ring_function,
        straight: ComponentFactory = straight_function,
        bend: Optional[ComponentFactory] = None,
        cross_section_heater: gf.types.CrossSectionFactory = gf.cross_section.
    strip_heater_metal,
        cross_section: CrossSectionFactory = strip,
        contact: gf.types.ComponentFactory = contact_heater_m3_mini,
        port_orientation: int = 90,
        contact_offset: Float2 = (0, 0),
        **kwargs) -> Component:
    """Double bus ring made of two couplers (ct: top, cb: bottom)
    connected with two vertical straights (sl: left, sr: right)
    includes heater on top

    Args:
        gap: gap between for coupler
        radius: for the bend and coupler
        length_x: ring coupler length
        length_y: vertical straight length
        coupler_ring: ring coupler function
        straight: straight function
        bend: bend function
        cross_section_heater:
        cross_section:
        contact:
        port_orientation: for electrical ports to promote from contact
        contact_offset: for each contact
        kwargs: cross_section settings

    .. code::

         --==ct==--
          |      |
          sl     sr length_y
          |      |
         --==cb==-- gap

          length_x

    """
    assert_on_2nm_grid(gap)

    coupler_component = (coupler_ring(gap=gap,
                                      radius=radius,
                                      length_x=length_x,
                                      bend=bend,
                                      cross_section=cross_section,
                                      bend_cross_section=cross_section_heater,
                                      **kwargs)
                         if callable(coupler_ring) else coupler_ring)
    straight_component = call_if_func(straight,
                                      length=length_y,
                                      cross_section=cross_section_heater,
                                      **kwargs)

    c = Component()
    cb = c.add_ref(coupler_component)
    ct = c.add_ref(coupler_component)
    sl = c.add_ref(straight_component)
    sr = c.add_ref(straight_component)

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

    c1 = c << contact()
    c2 = c << contact()
    c1.xmax = -length_x / 2 + cb.x - contact_offset[0]
    c2.xmin = +length_x / 2 + cb.x + contact_offset[0]
    c1.movey(contact_offset[1])
    c2.movey(contact_offset[1])
    c.add_ports(c1.get_ports_list(orientation=port_orientation), prefix="e1")
    c.add_ports(c2.get_ports_list(orientation=port_orientation), prefix="e2")
    c.auto_rename_ports()
    return c
コード例 #30
0
def straight_pin_slot(
    length: float = 500.0,
    cross_section: CrossSectionFactory = pin,
    contact: Optional[ComponentFactory] = contact_m1_m3,
    contact_width: float = 10.0,
    contact_slab: Optional[ComponentFactory] = contact_slot_slab_m1,
    contact_slab_top: Optional[ComponentFactory] = None,
    contact_slab_bot: Optional[ComponentFactory] = None,
    contact_slab_width: Optional[float] = None,
    contact_spacing: float = 3.0,
    contact_slab_spacing: float = 2.0,
    taper: Optional[ComponentFactory] = taper_strip_to_ridge,
    **kwargs,
) -> Component:
    """Returns a PIN straight waveguide with slotted via

    https://doi.org/10.1364/OE.26.029983

    500um length for PI phase shift
    https://ieeexplore.ieee.org/document/8268112

    to go beyond 2PI, you will need at least 1mm
    https://ieeexplore.ieee.org/document/8853396/

    Args:
        length: of the waveguide
        cross_section: for the waveguide
        contact: for contacting the metal
        contact_width:
        contact_slab: function for the component contacting the slab
        contact_slab_top: Optional, defaults to contact_slab
        contact_slab_bot: Optional, defaults to contact_slab
        contact_slab_width: defaults to contact_width
        contact_spacing: spacing between contacts
        taper: optional taper
        kwargs: cross_section settings

    """
    c = Component()
    if taper:
        taper = taper() if callable(taper) else taper
        length -= 2 * taper.get_ports_xsize()

    wg = c << gf.c.straight(
        cross_section=cross_section,
        length=length,
        **kwargs,
    )

    contact_slab_width = contact_slab_width or contact_width
    contact_slab_spacing = contact_slab_spacing or contact_spacing

    if taper:
        t1 = c << taper
        t2 = c << taper
        t1.connect("o2", wg.ports["o1"])
        t2.connect("o2", wg.ports["o2"])
        c.add_port("o1", port=t1.ports["o1"])
        c.add_port("o2", port=t2.ports["o1"])

    else:
        c.add_ports(wg.get_ports_list())

    contact_length = length

    if contact:
        contact_top = c << contact(
            size=(contact_length, contact_width),
        )
        contact_bot = c << contact(
            size=(contact_length, contact_width),
        )

        contact_bot.x = wg.x
        contact_top.x = wg.x

        contact_top.ymin = +contact_spacing / 2
        contact_bot.ymax = -contact_spacing / 2
        c.add_ports(contact_bot.ports, prefix="bot_")
        c.add_ports(contact_top.ports, prefix="top_")

    contact_slab_top = contact_slab_top or contact_slab
    contact_slab_bot = contact_slab_bot or contact_slab

    if contact_slab_top:
        slot_top = c << contact_slab_top(
            size=(contact_length, contact_slab_width),
        )
        slot_top.x = wg.x
        slot_top.ymin = +contact_slab_spacing / 2

    if contact_slab_bot:
        slot_bot = c << contact_slab_bot(
            size=(contact_length, contact_slab_width),
        )
        slot_bot.x = wg.x
        slot_bot.ymax = -contact_slab_spacing / 2

    return c