Skip to content

PCells

Parametric Cells for the Generic PDK.

Consider them a foundation from which you can draw inspiration. Feel free to modify their cross-sections and layers to tailor a unique PDK suited for any foundry of your choice.

By doing so, you'll possess a versatile, retargetable PDK, empowering you to design your circuits with speed and flexibility.

components

analog

inductor

inductor(
    width: float = 2.0,
    space: float = 2.1,
    diameter: float = 25.35,
    resistance: float = 0.5777,
    inductance: float = 3.3303e-11,
    turns: int = 1,
    layer_metal: LayerSpec = "M3",
    layer_inductor: LayerSpec = "M1",
    layer_metal_pin: LayerSpec = "WG_PIN",
    layers_no_fill: LayerSpecs = ("DEVREC", "NO_TILE_SI"),
) -> Component

Create a 2-turn inductor.

Parameters:

Name Type Description Default
width float

Width of the inductor trace in micrometers.

2.0
space float

Space between turns in micrometers.

2.1
diameter float

Inner diameter in micrometers.

25.35
resistance float

Resistance in ohms.

0.5777
inductance float

Inductance in henries.

3.3303e-11
turns int

Number of turns (default 1 for inductor2).

1
layer_metal LayerSpec

Layer for the metal trace.

'M3'
layer_inductor LayerSpec

Layer for the inductor region.

'M1'
layer_metal_pin LayerSpec

Layer for the metal pins.

'WG_PIN'
layers_no_fill LayerSpecs

Layers to exclude from fill.

('DEVREC', 'NO_TILE_SI')

Returns:

Type Description
Component

Component with inductor layout.

Source code in gdsfactory/components/analog/inductors.py
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
@gf.cell_with_module_name(schematic_function=inductor_schematic, tags=["analog"])
def inductor(
    width: float = 2.0,
    space: float = 2.1,
    diameter: float = 25.35,
    resistance: float = 0.5777,
    inductance: float = 33.303e-12,
    turns: int = 1,
    layer_metal: LayerSpec = "M3",
    layer_inductor: LayerSpec = "M1",
    layer_metal_pin: LayerSpec = "WG_PIN",
    layers_no_fill: LayerSpecs = ("DEVREC", "NO_TILE_SI"),
) -> Component:
    """Create a 2-turn inductor.

    Args:
        width: Width of the inductor trace in micrometers.
        space: Space between turns in micrometers.
        diameter: Inner diameter in micrometers.
        resistance: Resistance in ohms.
        inductance: Inductance in henries.
        turns: Number of turns (default 1 for inductor2).
        layer_metal: Layer for the metal trace.
        layer_inductor: Layer for the inductor region.
        layer_metal_pin: Layer for the metal pins.
        layers_no_fill: Layers to exclude from fill.

    Returns:
        Component with inductor layout.
    """
    c = Component()

    # Grid fixing for manufacturing constraints
    grid = 0.01
    w = round(width / (2 * grid)) * 2 * grid
    s = round(space / grid) * grid
    d = round(diameter / (2 * grid)) * 2 * grid

    # Calculate geometry parameters
    r = d / 2 + s
    octagon_center_y = 3 * r
    pi_over_4 = math.radians(45)

    path_points = []
    path_points.append((+space / 2, octagon_center_y - r * math.cos(pi_over_4 / 2)))

    for i in range(-2, 6):
        angle = i * pi_over_4 + pi_over_4 / 2
        r = d / 2 + s
        x = r * math.cos(angle)
        y = r * math.sin(angle) + octagon_center_y

        if -2 <= i < 2:
            path_points.append((x, y))
        else:
            path_points.append((x, y))

    path_points.append((-space / 2, octagon_center_y - r * math.cos(pi_over_4 / 2)))

    # Create the path
    path = gf.Path(path_points)
    c = gf.path.extrude(path, layer=layer_metal, width=w)

    # Adding ports
    length = 2 * r + s

    port1_trace = c << gf.components.rectangle(size=(s, length), layer=layer_metal)
    port1_trace.move((-s - s / 2, 0))
    c.add_port(name="P1", center=(-s, s), width=s, orientation=270, layer=layer_metal)

    port2_trace = c << gf.components.rectangle(size=(s, length), layer=layer_metal)
    port2_trace.move((s - s / 2, 0))
    c.add_port(name="P2", center=(+s, s), width=s, orientation=270, layer=layer_metal)

    # Add IND layer
    outer_polygon_pts = []
    for i in range(8):
        r_outer = (d / 2 + length) / (math.cos(pi_over_4 / 2))
        angle = i * pi_over_4 + pi_over_4 / 2
        x = r_outer * math.cos(angle)
        y = r_outer * math.sin(angle) + octagon_center_y
        outer_polygon_pts.append((x, y))

    c.add_polygon(points=outer_polygon_pts, layer=layer_inductor)

    # Add No fill layers
    for layer in layers_no_fill:
        c.add_polygon(points=outer_polygon_pts, layer=layer)

    # Adding pins
    pin_1_trace = c << gf.components.rectangle(size=(s, s), layer=layer_metal_pin)
    pin_1_trace.move((s / 2, 0))

    pin_2_trace = c << gf.components.rectangle(size=(s, s), layer=layer_metal_pin)
    pin_2_trace.move((-s - s / 2, 0))

    # Add metadata
    c.info["resistance"] = resistance
    c.info["inductance"] = inductance
    c.info["model"] = "inductor2"
    c.info["turns"] = turns
    c.info["width"] = width
    c.info["space"] = space
    c.info["diameter"] = diameter
    return c

inductor

interdigital_capacitor

interdigital_capacitor

interdigital_capacitor(
    fingers: int = 4,
    finger_length: float | int = 20.0,
    finger_gap: float | int = 2.0,
    thickness: float | int = 5.0,
    layer: LayerSpec = "WG",
) -> Component

Generate an interdigital capacitor component with ports on both ends.

An interdigital capacitor consists of interleaved metal fingers that create a distributed capacitance. This component creates a planar capacitor with two sets of interleaved fingers extending from opposite ends.

See for example Zhu et al., Accurate circuit model of interdigital capacitor and its application to design of new quasi-lumped miniaturized filters with suppression of harmonic resonance, doi: 10.1109/22.826833.

Note

finger_length=0 effectively provides a parallel plate capacitor. The capacitance scales approximately linearly with the number of fingers and finger length.

Parameters:

Name Type Description Default
fingers int

Total number of fingers of the capacitor (must be >= 1).

4
finger_length float | int

Length of each finger in μm.

20.0
finger_gap float | int

Gap between adjacent fingers in μm.

2.0
thickness float | int

Thickness of fingers and the base section in μm.

5.0
layer LayerSpec

Layer specification for the capacitor geometry.

'WG'

Returns:

Name Type Description
Component Component

A gdsfactory component with the interdigital capacitor geometry

Component

and two ports ('o1' and 'o2') on opposing sides.

Source code in gdsfactory/components/analog/interdigital_capacitor.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
@gf.cell_with_module_name(schematic_function=capacitor_schematic, tags=["analog"])
def interdigital_capacitor(
    fingers: int = 4,
    finger_length: float | int = 20.0,
    finger_gap: float | int = 2.0,
    thickness: float | int = 5.0,
    layer: LayerSpec = "WG",
) -> Component:
    """Generate an interdigital capacitor component with ports on both ends.

    An interdigital capacitor consists of interleaved metal fingers that create
    a distributed capacitance. This component creates a planar capacitor with
    two sets of interleaved fingers extending from opposite ends.

    See for example Zhu et al., `Accurate circuit model of interdigital
    capacitor and its application to design of new quasi-lumped miniaturized
    filters with suppression of harmonic resonance`, doi: 10.1109/22.826833.

    Note:
        ``finger_length=0`` effectively provides a parallel plate capacitor.
        The capacitance scales approximately linearly with the number of fingers
        and finger length.

    Args:
        fingers: Total number of fingers of the capacitor (must be >= 1).
        finger_length: Length of each finger in μm.
        finger_gap: Gap between adjacent fingers in μm.
        thickness: Thickness of fingers and the base section in μm.
        layer: Layer specification for the capacitor geometry.

    Returns:
        Component: A gdsfactory component with the interdigital capacitor geometry
        and two ports ('o1' and 'o2') on opposing sides.
    """
    c = Component()

    assert fingers >= 1, "Must have at least 1 finger"

    width = 2 * thickness + finger_length + finger_gap  # total length
    height = fingers * thickness + (fingers - 1) * finger_gap  # total height
    points_1 = [
        (0, 0),
        (0, height),
        (thickness + finger_length, height),
        (thickness + finger_length, height - thickness),
        (thickness, height - thickness),
        *chain.from_iterable(
            (
                (thickness, height - (2 * i) * (thickness + finger_gap)),
                (
                    thickness + finger_length,
                    height - (2 * i) * (thickness + finger_gap),
                ),
                (
                    thickness + finger_length,
                    height - (2 * i) * (thickness + finger_gap) - thickness,
                ),
                (thickness, height - (2 * i) * (thickness + finger_gap) - thickness),
            )
            for i in range(ceil(fingers / 2))
        ),
        (thickness, 0),
        (0, 0),
    ]

    points_2 = [
        (width, 0),
        (width, height),
        (width - thickness, height),
        *chain.from_iterable(
            (
                (
                    width - thickness,
                    height - (1 + 2 * i) * thickness - (1 + 2 * i) * finger_gap,
                ),
                (
                    width - (thickness + finger_length),
                    height - (1 + 2 * i) * thickness - (1 + 2 * i) * finger_gap,
                ),
                (
                    width - (thickness + finger_length),
                    height - (2 + 2 * i) * thickness - (1 + 2 * i) * finger_gap,
                ),
                (
                    width - thickness,
                    height - (2 + 2 * i) * thickness - (1 + 2 * i) * finger_gap,
                ),
            )
            for i in range(floor(fingers / 2))
        ),
        (width - thickness, 0),
        (width, 0),
    ]

    c.add_polygon(points_1, layer=layer)
    c.add_polygon(points_2, layer=layer)
    c.add_port(
        name="o1",
        center=(0, height / 2),
        width=thickness,
        orientation=180,
        layer=layer,
    )
    c.add_port(
        name="o2",
        center=(width, height / 2),
        width=thickness,
        orientation=0,
        layer=layer,
    )
    return c

interdigital_capacitor

interdigitated_electrodes

interdigitated_electrodes

interdigitated_electrodes(
    n_fingers: int = 10,
    finger_width: float = 0.5,
    finger_length: float = 10.0,
    finger_gap: float = 0.5,
    bus_width: float = 2.0,
    bus_length: float | None = None,
    layer: LayerSpec = "MTOP",
    port_type: str = "electrical",
) -> Component

Interdigitated electrode pattern.

Two horizontal bus bars (top and bottom) with alternating fingers extending from each bus toward the opposite one. Fingers from the top bus extend downward and fingers from the bottom bus extend upward, interleaving with a gap between the finger tips and the opposite bus.

Parameters:

Name Type Description Default
n_fingers int

Total number of fingers (split between top and bottom buses).

10
finger_width float

Width of each finger in um.

0.5
finger_length float

Length of each finger in um.

10.0
finger_gap float

Gap between adjacent fingers (edge to edge) in um.

0.5
bus_width float

Width (height) of each bus bar in um.

2.0
bus_length float | None

Length of each bus bar in um. Defaults to the total width needed to accommodate all fingers.

None
layer LayerSpec

Layer specification for all geometry.

'MTOP'
port_type str

Port type for electrical ports at bus bar ends.

'electrical'
Source code in gdsfactory/components/analog/interdigitated_electrodes.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
@gf.cell_with_module_name(tags=["analog"])
def interdigitated_electrodes(
    n_fingers: int = 10,
    finger_width: float = 0.5,
    finger_length: float = 10.0,
    finger_gap: float = 0.5,
    bus_width: float = 2.0,
    bus_length: float | None = None,
    layer: LayerSpec = "MTOP",
    port_type: str = "electrical",
) -> Component:
    """Interdigitated electrode pattern.

    Two horizontal bus bars (top and bottom) with alternating fingers extending
    from each bus toward the opposite one. Fingers from the top bus extend
    downward and fingers from the bottom bus extend upward, interleaving
    with a gap between the finger tips and the opposite bus.

    Args:
        n_fingers: Total number of fingers (split between top and bottom buses).
        finger_width: Width of each finger in um.
        finger_length: Length of each finger in um.
        finger_gap: Gap between adjacent fingers (edge to edge) in um.
        bus_width: Width (height) of each bus bar in um.
        bus_length: Length of each bus bar in um. Defaults to the total width
            needed to accommodate all fingers.
        layer: Layer specification for all geometry.
        port_type: Port type for electrical ports at bus bar ends.
    """
    c = Component()

    finger_pitch = finger_width + finger_gap
    total_finger_span = n_fingers * finger_width + (n_fingers - 1) * finger_gap

    if bus_length is None:
        bus_length = total_finger_span + 2 * finger_gap

    # Vertical extent:
    #   bottom bus: y in [-bus_width - finger_length - finger_gap/2, -finger_length - finger_gap/2]
    #   bottom fingers extend up from bottom bus
    #   top fingers extend down from top bus
    #   top bus: y in [finger_length + finger_gap/2, finger_length + finger_gap/2 + bus_width]

    gap_half = finger_gap / 2  # gap between finger tip and opposite bus
    top_bus_bottom = finger_length + gap_half
    top_bus_top = top_bus_bottom + bus_width
    bottom_bus_top = -(finger_length + gap_half)
    bottom_bus_bottom = bottom_bus_top - bus_width

    # Bottom bus bar
    c.add_polygon(
        [
            (-bus_length / 2, bottom_bus_bottom),
            (bus_length / 2, bottom_bus_bottom),
            (bus_length / 2, bottom_bus_top),
            (-bus_length / 2, bottom_bus_top),
        ],
        layer=layer,
    )

    # Top bus bar
    c.add_polygon(
        [
            (-bus_length / 2, top_bus_bottom),
            (bus_length / 2, top_bus_bottom),
            (bus_length / 2, top_bus_top),
            (-bus_length / 2, top_bus_top),
        ],
        layer=layer,
    )

    # Fingers: center the finger array horizontally
    x_start = -total_finger_span / 2 + finger_width / 2

    for i in range(n_fingers):
        x_center = x_start + i * finger_pitch
        x_left = x_center - finger_width / 2
        x_right = x_center + finger_width / 2

        if i % 2 == 0:
            # Bottom bus finger: extends upward from bottom bus top
            c.add_polygon(
                [
                    (x_left, bottom_bus_top),
                    (x_right, bottom_bus_top),
                    (x_right, bottom_bus_top + finger_length),
                    (x_left, bottom_bus_top + finger_length),
                ],
                layer=layer,
            )
        else:
            # Top bus finger: extends downward from top bus bottom
            c.add_polygon(
                [
                    (x_left, top_bus_bottom),
                    (x_right, top_bus_bottom),
                    (x_right, top_bus_bottom - finger_length),
                    (x_left, top_bus_bottom - finger_length),
                ],
                layer=layer,
            )

    # Electrical ports at bus bar ends
    c.add_port(
        name="bot_left",
        center=(-bus_length / 2, (bottom_bus_bottom + bottom_bus_top) / 2),
        width=bus_width,
        orientation=180,
        layer=layer,
        port_type=port_type,
    )
    c.add_port(
        name="bot_right",
        center=(bus_length / 2, (bottom_bus_bottom + bottom_bus_top) / 2),
        width=bus_width,
        orientation=0,
        layer=layer,
        port_type=port_type,
    )
    c.add_port(
        name="top_left",
        center=(-bus_length / 2, (top_bus_bottom + top_bus_top) / 2),
        width=bus_width,
        orientation=180,
        layer=layer,
        port_type=port_type,
    )
    c.add_port(
        name="top_right",
        center=(bus_length / 2, (top_bus_bottom + top_bus_top) / 2),
        width=bus_width,
        orientation=0,
        layer=layer,
        port_type=port_type,
    )

    if port_type == "electrical":
        bot_ports = [p for p in c.ports if p.name and p.name.startswith("bot_")]
        top_ports = [p for p in c.ports if p.name and p.name.startswith("top_")]
        if bot_ports:
            c.create_pin(ports=bot_ports, name="bot")
        if top_ports:
            c.create_pin(ports=top_ports, name="top")

    return c

interdigitated_electrodes

bends

bend_circular

bend_circular

bend_circular(
    radius: float | None = None,
    angle: float = 90.0,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component

Returns a radial arc.

Parameters:

Name Type Description Default
radius float | None

in um. Defaults to cross_section_radius.

None
angle float

angle of arc (degrees).

90.0
npoints int | None

number of points.

None
angular_step float | None

If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.

None
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
cross_section CrossSectionSpec

spec (CrossSection, string or dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
Source code in gdsfactory/components/bends/bend_circular.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
@gf.cell_with_module_name(schematic_function=bend_schematic, tags=["bends"])
def bend_circular(
    radius: float | None = None,
    angle: float = 90.0,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: gf.typings.LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component:
    """Returns a radial arc.

    Args:
        radius: in um. Defaults to cross_section_radius.
        angle: angle of arc (degrees).
        npoints: number of points.
        angular_step: If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        cross_section: spec (CrossSection, string or dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
    """
    if angle not in {90, 180}:
        warnings.warn(
            f"bend_euler angle should be 90 or 180. Got {angle}. Use bend_euler_all_angle instead.",
            UserWarning,
            stacklevel=3,
        )
    return _bend_circular(
        radius=radius,
        angle=angle,
        npoints=npoints,
        layer=layer,
        width=width,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        all_angle=False,
        angular_step=angular_step,
    )

bend_circular_all_angle

bend_circular_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> ComponentAllAngle

Returns a radial arc.

Parameters:

Name Type Description Default
radius float | None

in um. Defaults to cross_section_radius.

None
angle float

angle of arc (degrees).

90.0
npoints int | None

number of points.

None
angular_step float | None

If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.

None
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
cross_section CrossSectionSpec

spec (CrossSection, string or dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
Source code in gdsfactory/components/bends/bend_circular.py
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
@gf.vcell
def bend_circular_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: gf.typings.LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> ComponentAllAngle:
    """Returns a radial arc.

    Args:
        radius: in um. Defaults to cross_section_radius.
        angle: angle of arc (degrees).
        npoints: number of points.
        angular_step: If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        cross_section: spec (CrossSection, string or dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
    """
    return _bend_circular(
        radius=radius,
        angle=angle,
        npoints=npoints,
        layer=layer,
        width=width,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        all_angle=True,
        angular_step=angular_step,
    )

bend_circular

bend_circular_all_angle

bend_circular_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> ComponentAllAngle

Returns a radial arc.

Parameters:

Name Type Description Default
radius float | None

in um. Defaults to cross_section_radius.

None
angle float

angle of arc (degrees).

90.0
npoints int | None

number of points.

None
angular_step float | None

If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.

None
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
cross_section CrossSectionSpec

spec (CrossSection, string or dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
Source code in gdsfactory/components/bends/bend_circular.py
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
@gf.vcell
def bend_circular_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: gf.typings.LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> ComponentAllAngle:
    """Returns a radial arc.

    Args:
        radius: in um. Defaults to cross_section_radius.
        angle: angle of arc (degrees).
        npoints: number of points.
        angular_step: If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        cross_section: spec (CrossSection, string or dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
    """
    return _bend_circular(
        radius=radius,
        angle=angle,
        npoints=npoints,
        layer=layer,
        width=width,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        all_angle=True,
        angular_step=angular_step,
    )
import gdsfactory as gf

gf.gpdk.PDK.activate()

c = gf.components.bend_circular_all_angle(angle=90, cross_section='strip', allow_min_radius_violation=False).copy()
c.draw_ports()
c.plot()

bend_circular_heater

bend_circular_heater

bend_circular_heater(
    radius: float | None = None,
    angle: float = 90,
    npoints: int | None = None,
    heater_to_wg_distance: float = 1.2,
    heater_width: float = 0.5,
    layer_heater: LayerSpec = "HEATER",
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component

Creates an arc of arclength theta starting at angle start_angle.

Parameters:

Name Type Description Default
radius float | None

in um. Defaults to cross_section.radius.

None
angle float

angle of arc (degrees).

90
npoints int | None

Number of points used per 360 degrees.

None
heater_to_wg_distance float

in um.

1.2
heater_width float

in um.

0.5
layer_heater LayerSpec

for heater.

'HEATER'
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
Source code in gdsfactory/components/bends/bend_circular_heater.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@gf.cell_with_module_name(schematic_function=bend_schematic, tags=["bends"])
def bend_circular_heater(
    radius: float | None = None,
    angle: float = 90,
    npoints: int | None = None,
    heater_to_wg_distance: float = 1.2,
    heater_width: float = 0.5,
    layer_heater: LayerSpec = "HEATER",
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component:
    """Creates an arc of arclength `theta` starting at angle `start_angle`.

    Args:
        radius: in um. Defaults to cross_section.radius.
        angle: angle of arc (degrees).
        npoints: Number of points used per 360 degrees.
        heater_to_wg_distance: in um.
        heater_width: in um.
        layer_heater: for heater.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
    """
    x = gf.get_cross_section(cross_section)
    radius = radius or x.radius
    assert radius is not None
    width = x.width

    offset = heater_to_wg_distance + width / 2
    s1 = gf.Section(
        width=heater_width,
        offset=+offset,
        layer=layer_heater,
    )
    s2 = gf.Section(
        width=heater_width,
        offset=-offset,
        layer=layer_heater,
    )
    sections = list(x.sections) + [s1, s2]

    xs = x.copy(sections=tuple(sections))
    p = arc(radius=radius, angle=angle, npoints=npoints)

    c = Component()
    path = p.extrude(xs)
    ref = c << path
    c.add_ports(ref.ports)
    c.info["length"] = p.length()
    c.info["dx"] = float(abs(p.points[0][0] - p.points[-1][0]))
    c.info["dy"] = float(abs(p.points[0][0] - p.points[-1][0]))
    if not allow_min_radius_violation:
        x.validate_radius(radius)
    c.flatten()
    return c

bend_circular_heater

bend_euler

bend_euler

bend_euler(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component

Regular degree euler bend.

Parameters:

Name Type Description Default
radius float | None

in um. Defaults to cross_section_radius.

None
angle float

total angle of the curve.

90.0
p float

Proportion of the curve that is an Euler curve.

0.5
with_arc_floorplan bool

if True the size of the bend will be adjusted to match an arc bend with the specified radius. If False: radius is the minimum radius of curvature.

True
npoints int | None

Number of points used per 360 degrees.

None
angular_step float | None

if not None, the angle step in degrees for the all_angle bend.

None
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
Source code in gdsfactory/components/bends/bend_euler.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
@gf.cell_with_module_name(schematic_function=bend_schematic, tags=["bends"])
def bend_euler(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component:
    """Regular degree euler bend.

    Args:
        radius: in um. Defaults to cross_section_radius.
        angle: total angle of the curve.
        p: Proportion of the curve that is an Euler curve.
        with_arc_floorplan: if True the size of the bend will be adjusted to match an arc bend with the specified radius. If False: `radius` is the minimum radius of curvature.
        npoints: Number of points used per 360 degrees.
        angular_step: if not None, the angle step in degrees for the all_angle bend.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
    """
    if abs(angle) not in {90, 180}:
        warnings.warn(
            f"bend_euler angle should be 90 or 180. Got {angle}. Use bend_euler_all_angle instead.",
            UserWarning,
            stacklevel=3,
        )

    return _bend_euler(
        radius=radius,
        angle=angle,
        p=p,
        with_arc_floorplan=with_arc_floorplan,
        npoints=npoints,
        angular_step=angular_step,
        layer=layer,
        width=width,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        all_angle=False,
    )

bend_euler_all_angle

bend_euler_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> ComponentAllAngle

Regular degree euler bend.

Parameters:

Name Type Description Default
radius float | None

in um. Defaults to cross_section_radius.

None
angle float

total angle of the curve.

90.0
p float

Proportion of the curve that is an Euler curve.

0.5
with_arc_floorplan bool

If False: radius is the minimum radius of curvature

True
npoints int | None

Number of points used per 360 degrees.

None
angular_step float | None

if not None, the angle step in degrees for the all_angle bend.

None
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
Source code in gdsfactory/components/bends/bend_euler.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
@gf.vcell
def bend_euler_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: gf.typings.LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> ComponentAllAngle:
    """Regular degree euler bend.

    Args:
        radius: in um. Defaults to cross_section_radius.
        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
        npoints: Number of points used per 360 degrees.
        angular_step: if not None, the angle step in degrees for the all_angle bend.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.

    """
    return _bend_euler(
        radius=radius,
        angle=angle,
        p=p,
        with_arc_floorplan=with_arc_floorplan,
        npoints=npoints,
        angular_step=angular_step,
        layer=layer,
        width=width,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        all_angle=True,
    )

bend_euler_s

bend_euler_s(
    radius: float | None = None,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    port1: str = "o1",
    port2: str = "o2",
) -> Component

Sbend made of 2 euler bends.

Parameters:

Name Type Description Default
radius float | None

in um. Defaults to cross_section_radius.

None
p float

Proportion of the curve that is an Euler curve.

0.5
with_arc_floorplan bool

If False: radius is the minimum radius of curvature.

True
npoints int | None

Number of points used per 360 degrees.

None
angular_step float | None

if not None, the angle step in degrees for the all_angle bend.

None
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
port1 str

input port name.

'o1'
port2 str

output port name.

        _____ o2
       /
      /
     /
    /
    |
   /
  /
 /

o1_____/

'o2'
Source code in gdsfactory/components/bends/bend_euler.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
@gf.cell_with_module_name(schematic_function=sbend_schematic, tags=["bends"])
def bend_euler_s(
    radius: float | None = None,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    port1: str = "o1",
    port2: str = "o2",
) -> Component:
    r"""Sbend made of 2 euler bends.

    Args:
        radius: in um. Defaults to cross_section_radius.
        p: Proportion of the curve that is an Euler curve.
        with_arc_floorplan: If False: `radius` is the minimum radius of curvature.
        npoints: Number of points used per 360 degrees.
        angular_step: if not None, the angle step in degrees for the all_angle bend.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
        port1: input port name.
        port2: output port name.

                        _____ o2
                       /
                      /
                     /
                    /
                    |
                   /
                  /
                 /
         o1_____/

    """
    c = Component()
    b = bend_euler(
        radius=radius,
        p=p,
        with_arc_floorplan=with_arc_floorplan,
        npoints=npoints,
        layer=layer,
        width=width,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        angular_step=angular_step,
    )
    b1 = c.add_ref(b)
    b2 = c.add_ref(b)
    b2.connect(port1, b1[port2], mirror=True)
    c.add_port(port1, port=b1[port1])
    c.add_port(port2, port=b2[port2])
    c.info["length"] = 2 * b.info["length"]
    return c

bend_euler

bend_euler_all_angle

bend_euler_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> ComponentAllAngle

Regular degree euler bend.

Parameters:

Name Type Description Default
radius float | None

in um. Defaults to cross_section_radius.

None
angle float

total angle of the curve.

90.0
p float

Proportion of the curve that is an Euler curve.

0.5
with_arc_floorplan bool

If False: radius is the minimum radius of curvature

True
npoints int | None

Number of points used per 360 degrees.

None
angular_step float | None

if not None, the angle step in degrees for the all_angle bend.

None
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
Source code in gdsfactory/components/bends/bend_euler.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
@gf.vcell
def bend_euler_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: gf.typings.LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> ComponentAllAngle:
    """Regular degree euler bend.

    Args:
        radius: in um. Defaults to cross_section_radius.
        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
        npoints: Number of points used per 360 degrees.
        angular_step: if not None, the angle step in degrees for the all_angle bend.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.

    """
    return _bend_euler(
        radius=radius,
        angle=angle,
        p=p,
        with_arc_floorplan=with_arc_floorplan,
        npoints=npoints,
        angular_step=angular_step,
        layer=layer,
        width=width,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        all_angle=True,
    )
import gdsfactory as gf

gf.gpdk.PDK.activate()

c = gf.components.bend_euler_all_angle(angle=90, p=0.5, with_arc_floorplan=True, cross_section='strip', allow_min_radius_violation=False).copy()
c.draw_ports()
c.plot()

bend_euler_s

bend_euler_s(
    radius: float | None = None,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    port1: str = "o1",
    port2: str = "o2",
) -> Component

Sbend made of 2 euler bends.

Parameters:

Name Type Description Default
radius float | None

in um. Defaults to cross_section_radius.

None
p float

Proportion of the curve that is an Euler curve.

0.5
with_arc_floorplan bool

If False: radius is the minimum radius of curvature.

True
npoints int | None

Number of points used per 360 degrees.

None
angular_step float | None

if not None, the angle step in degrees for the all_angle bend.

None
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
port1 str

input port name.

'o1'
port2 str

output port name.

        _____ o2
       /
      /
     /
    /
    |
   /
  /
 /

o1_____/

'o2'
Source code in gdsfactory/components/bends/bend_euler.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
@gf.cell_with_module_name(schematic_function=sbend_schematic, tags=["bends"])
def bend_euler_s(
    radius: float | None = None,
    p: float = 0.5,
    with_arc_floorplan: bool = True,
    npoints: int | None = None,
    angular_step: float | None = None,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    port1: str = "o1",
    port2: str = "o2",
) -> Component:
    r"""Sbend made of 2 euler bends.

    Args:
        radius: in um. Defaults to cross_section_radius.
        p: Proportion of the curve that is an Euler curve.
        with_arc_floorplan: If False: `radius` is the minimum radius of curvature.
        npoints: Number of points used per 360 degrees.
        angular_step: if not None, the angle step in degrees for the all_angle bend.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
        port1: input port name.
        port2: output port name.

                        _____ o2
                       /
                      /
                     /
                    /
                    |
                   /
                  /
                 /
         o1_____/

    """
    c = Component()
    b = bend_euler(
        radius=radius,
        p=p,
        with_arc_floorplan=with_arc_floorplan,
        npoints=npoints,
        layer=layer,
        width=width,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        angular_step=angular_step,
    )
    b1 = c.add_ref(b)
    b2 = c.add_ref(b)
    b2.connect(port1, b1[port2], mirror=True)
    c.add_port(port1, port=b1[port1])
    c.add_port(port2, port=b2[port2])
    c.info["length"] = 2 * b.info["length"]
    return c

bend_euler_s

bend_s

bend_s

bend_s(
    size: Size = (11.0, 1.8),
    npoints: int = 99,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    width: float | None = None,
) -> Component

Return S bend with bezier curve.

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

Parameters:

Name Type Description Default
size Size

in x and y direction.

(11.0, 1.8)
npoints int

number of points.

99
cross_section CrossSectionSpec

spec.

'strip'
allow_min_radius_violation bool

bool.

False
width float | None

width to use. Defaults to cross_section.width.

None
Source code in gdsfactory/components/bends/bend_s.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
@gf.cell_with_module_name(schematic_function=sbend_schematic, tags=["bends"])
def bend_s(
    size: Size = (11.0, 1.8),
    npoints: int = 99,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    width: float | None = None,
) -> Component:
    """Return 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.
        npoints: number of points.
        cross_section: spec.
        allow_min_radius_violation: bool.
        width: width to use. Defaults to cross_section.width.

    """
    dx, dy = size

    if dy == 0:
        return gf.components.straight(
            length=dx, cross_section=cross_section, width=width
        )

    return bezier(
        control_points=((0, 0), (dx / 2, 0), (dx / 2, dy), (dx, dy)),
        npoints=npoints,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        width=width,
    )

bend_s_offset

bend_s_offset(
    offset: float = 40.0,
    radius: float | None = 10.0,
    cross_section: CrossSectionSpec = "strip",
    width: float | None = None,
    with_euler: bool | None = None,
    p: float = 1,
    with_arc_floorplan: bool = False,
    npoints: int | None = None,
    angular_step: float | None = None,
) -> gf.Component

Return S bend made of two bends with a straight section.

Parameters:

Name Type Description Default
offset float

in um.

40.0
radius float | None

in um. if None, uses cross_section_radius.

10.0
cross_section CrossSectionSpec

spec.

'strip'
width float | None

width to use. Defaults to cross_section.width.

None
with_euler bool | None

deprecated, use p=0 for circular arc instead.

None
p float

1 means standard Euler bend. 0 means circular arc.

1
with_arc_floorplan bool

if True the size of the bend will be adjusted to match an arc bend with the specified radius. If False: radius is the minimum radius of curvature.

False
npoints int | None

number of points.

None
angular_step float | None

If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.

None
Source code in gdsfactory/components/bends/bend_s.py
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
@gf.cell_with_module_name(schematic_function=sbend_schematic, tags=["bends"])
def bend_s_offset(
    offset: float = 40.0,
    radius: float | None = 10.0,
    cross_section: CrossSectionSpec = "strip",
    width: float | None = None,
    with_euler: bool | None = None,
    p: float = 1,
    with_arc_floorplan: bool = False,
    npoints: int | None = None,
    angular_step: float | None = None,
) -> gf.Component:
    """Return S bend made of two bends with a straight section.

    Args:
        offset: in um.
        radius: in um. if None, uses cross_section_radius.
        cross_section: spec.
        width: width to use. Defaults to cross_section.width.
        with_euler: deprecated, use p=0 for circular arc instead.
        p: 1 means standard Euler bend. 0 means circular arc.
        with_arc_floorplan: if True the size of the bend will be adjusted to match an arc bend with the specified radius. If False: `radius` is the minimum radius of curvature.
        npoints: number of points.
        angular_step: If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.
    """
    if with_euler is not None:
        warnings.warn(
            "with_euler is deprecated. Use p=0 for circular arc instead. And p=1 for euler bend.",
            DeprecationWarning,
            stacklevel=2,
        )
        if not with_euler:
            p = 0

    if width:
        xs = gf.get_cross_section(cross_section, width=width)
    else:
        xs = gf.get_cross_section(cross_section)

    radius = radius or xs.radius
    assert radius is not None, "radius cannot be None"

    xs.validate_radius(radius)
    angle, middle_length = _get_euler_sbend_angle_middle_length_from_jog(
        jog=abs(offset) / 2, radius=radius, p=p, use_eff=with_arc_floorplan
    )
    angle = math.copysign(angle, offset)
    path = gf.path.euler(
        radius=radius,
        angle=+angle,
        p=p,
        use_eff=with_arc_floorplan,
        npoints=npoints,
        angular_step=angular_step,
    )
    if middle_length > 1e-6:
        path += gf.path.straight(length=middle_length)
    path += gf.path.euler(
        radius=radius,
        angle=-angle,
        p=p,
        use_eff=with_arc_floorplan,
        npoints=npoints,
        angular_step=angular_step,
    )

    return gf.path.extrude(path, cross_section=xs)

bezier

bezier(
    control_points: Coordinates = (
        (0.0, 0.0),
        (5.0, 0.0),
        (5.0, 1.8),
        (10.0, 1.8),
    ),
    npoints: int = 201,
    with_manhattan_facing_angles: bool = True,
    start_angle: int | None = None,
    end_angle: int | None = None,
    cross_section: CrossSectionSpec = "strip",
    bend_radius_error_type: ErrorType | None = None,
    allow_min_radius_violation: bool = False,
    width: float | None = None,
) -> Component

Returns Bezier bend.

Parameters:

Name Type Description Default
control_points Coordinates

list of points.

((0.0, 0.0), (5.0, 0.0), (5.0, 1.8), (10.0, 1.8))
npoints int

number of points varying between 0 and 1.

201
with_manhattan_facing_angles bool

bool.

True
start_angle int | None

optional start angle in deg.

None
end_angle int | None

optional end angle in deg.

None
cross_section CrossSectionSpec

spec.

'strip'
bend_radius_error_type ErrorType | None

error type.

None
allow_min_radius_violation bool

bool.

False
width float | None

width to use. Defaults to cross_section.width.

None
Source code in gdsfactory/components/bends/bend_s.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@gf.cell_with_module_name(schematic_function=sbend_schematic, tags=["bends"])
def bezier(
    control_points: Coordinates = ((0.0, 0.0), (5.0, 0.0), (5.0, 1.8), (10.0, 1.8)),
    npoints: int = 201,
    with_manhattan_facing_angles: bool = True,
    start_angle: int | None = None,
    end_angle: int | None = None,
    cross_section: CrossSectionSpec = "strip",
    bend_radius_error_type: ErrorType | None = None,
    allow_min_radius_violation: bool = False,
    width: float | None = None,
) -> Component:
    """Returns Bezier bend.

    Args:
        control_points: list of points.
        npoints: number of points varying between 0 and 1.
        with_manhattan_facing_angles: bool.
        start_angle: optional start angle in deg.
        end_angle: optional end angle in deg.
        cross_section: spec.
        bend_radius_error_type: error type.
        allow_min_radius_violation: bool.
        width: width to use. Defaults to cross_section.width.
    """
    if width:
        xs = gf.get_cross_section(cross_section, width=width)
    else:
        xs = gf.get_cross_section(cross_section)

    t = np.linspace(0, 1, npoints)
    path_points = bezier_curve(t, control_points)
    path = gf.Path(path_points)

    if with_manhattan_facing_angles:
        path.start_angle = start_angle or snap_angle(path.start_angle)
        path.end_angle = end_angle or snap_angle(path.end_angle)

    c = path.extrude(xs)
    curv = curvature(path_points, t)
    length = path.length()
    if max(np.abs(curv)) == 0:
        min_bend_radius = np.inf
    else:
        min_bend_radius = float(gf.snap.snap_to_grid(float(1 / np.max(np.abs(curv)))))

    c.info["length"] = length
    c.info["min_bend_radius"] = min_bend_radius
    c.info["start_angle"] = float(path.start_angle)
    c.info["end_angle"] = float(path.end_angle)
    c.add_route_info(
        cross_section=xs,
        length=c.info["length"],
        n_bend_s=1,
        min_bend_radius=min_bend_radius,
    )

    if not allow_min_radius_violation:
        xs.validate_radius(min_bend_radius, bend_radius_error_type)

    xs.add_bbox(c)
    return c

bezier_curve

bezier_curve(
    t: NDArray[floating[Any]], control_points: Coordinates
) -> npt.NDArray[np.floating[Any]]

Returns bezier coordinates.

Parameters:

Name Type Description Default
t NDArray[floating[Any]]

1D array of points varying between 0 and 1.

required
control_points Coordinates

for the bezier curve.

required
Source code in gdsfactory/components/bends/bend_s.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def bezier_curve(
    t: npt.NDArray[np.floating[Any]], control_points: Coordinates
) -> npt.NDArray[np.floating[Any]]:
    """Returns bezier coordinates.

    Args:
        t: 1D array of points varying between 0 and 1.
        control_points: for the bezier curve.
    """
    from scipy.special import binom

    xs = 0.0
    ys = 0.0
    n = len(control_points) - 1
    for k in range(n + 1):
        ank = binom(n, k) * (1 - t) ** (n - k) * t**k
        xs += ank * control_points[k][0]
        ys += ank * control_points[k][1]

    return np.column_stack([xs, ys])

find_min_curv_bezier_control_points

find_min_curv_bezier_control_points(
    start_point: Coordinate,
    end_point: Coordinate,
    start_angle: float,
    end_angle: float,
    npoints: int = 201,
    alpha: float = 0.05,
    nb_pts: int = 2,
) -> Coordinates

Returns bezier control points that minimize curvature.

Parameters:

Name Type Description Default
start_point Coordinate

start point.

required
end_point Coordinate

end point.

required
start_angle float

start angle in deg.

required
end_angle float

end angle in deg.

required
npoints int

number of points varying between 0 and 1.

201
alpha float

weight for angle mismatch.

0.05
nb_pts int

number of control points.

2
Source code in gdsfactory/components/bends/bend_s.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def find_min_curv_bezier_control_points(
    start_point: Coordinate,
    end_point: Coordinate,
    start_angle: float,
    end_angle: float,
    npoints: int = 201,
    alpha: float = 0.05,
    nb_pts: int = 2,
) -> Coordinates:
    """Returns bezier control points that minimize curvature.

    Args:
        start_point: start point.
        end_point: end point.
        start_angle: start angle in deg.
        end_angle: end angle in deg.
        npoints: number of points varying between 0 and 1.
        alpha: weight for angle mismatch.
        nb_pts: number of control points.
    """
    from scipy.optimize import minimize

    t = np.linspace(0, 1, npoints)

    def array_1d_to_cpts(a: npt.NDArray[np.float64]) -> list[tuple[float, float]]:
        xs = a[::2]
        ys = a[1::2]
        return list(zip(xs, ys, strict=False))

    def objective_func(p: npt.NDArray[np.float64]) -> float:
        """Minimize  max curvaturea and negligible start angle and end angle mismatch."""
        ps = array_1d_to_cpts(p)
        control_points = [start_point] + ps + [end_point]
        path_points = bezier_curve(t, control_points)

        max_curv = max(np.abs(curvature(path_points, t)))

        angles = angles_deg(path_points)
        dstart_angle = abs(angles[0] - start_angle)
        dend_angle = abs(angles[-2] - end_angle)
        angle_mismatch = dstart_angle + dend_angle
        return float(angle_mismatch * alpha + max_curv)

    x0, y0 = start_point[0], start_point[1]
    xn, yn = end_point[0], end_point[1]

    initial_guess: list[float] = []
    for i in range(nb_pts):
        x = (i + 1) * (x0 + xn) / nb_pts
        y = (i + 1) * (y0 + yn) / nb_pts
        initial_guess += [x, y]

    # initial_guess = [(x0 + xn) / 2, y0, (x0 + xn) / 2, yn]
    res = minimize(objective_func, initial_guess, method="Nelder-Mead")
    p = res.x
    points = [start_point] + array_1d_to_cpts(p) + [end_point]
    return tuple(points)

get_min_sbend_size

get_min_sbend_size(
    size: tuple[float | None, float | None] = (None, 10.0),
    cross_section: CrossSectionSpec = "strip",
    num_points: int = 100,
) -> float

Returns the minimum sbend size to comply with bend radius requirements.

Parameters:

Name Type Description Default
size tuple[float | None, float | None]

in x and y direction. One of them is None, which is the size we need to figure out.

(None, 10.0)
cross_section CrossSectionSpec

spec.

'strip'
num_points int

number of points to iterate over between max_size and 0.1 * max_size.

100
Source code in gdsfactory/components/bends/bend_s.py
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
def get_min_sbend_size(
    size: tuple[float | None, float | None] = (None, 10.0),
    cross_section: CrossSectionSpec = "strip",
    num_points: int = 100,
) -> float:
    """Returns the minimum sbend size to comply with bend radius requirements.

    Args:
        size: in x and y direction. One of them is None, which is the size we need to figure out.
        cross_section: spec.
        num_points: number of points to iterate over between max_size and 0.1 * max_size.
    """
    size_list = list(size)
    cross_section_f = gf.get_cross_section(cross_section)

    if size_list[0] is None:
        ind = 0
        known_s = size_list[1]
    elif size_list[1] is None:
        ind = 1
        known_s = size_list[0]
    else:
        raise ValueError("One of the two elements in size has to be None")

    min_radius = cross_section_f.radius

    if min_radius is None:
        raise ValueError("The min radius for the specified layer is not known!")

    min_size = np.inf

    assert known_s is not None

    # Guess sizes, iterate over them until we cannot achieve the min radius
    # the max size corresponds to an ellipsoid
    max_size = float(np.sqrt(np.abs(min_radius * known_s)) * 2.5)
    sizes = np.linspace(max_size, 0.1 * max_size, num_points)

    for s in sizes:
        sz = size_list
        sz[ind] = s
        dx, dy = size_list
        assert dx is not None and dy is not None
        control_points = ((0, 0), (dx / 2, 0), (dx / 2, dy), (dx, dy))
        npoints = 201
        t = np.linspace(0, 1, npoints)
        path_points = bezier_curve(t, control_points)
        curv = curvature(path_points, t)
        min_bend_radius = 1 / max(np.abs(curv))
        if min_bend_radius < min_radius:
            min_size = s
            break

    return min_size

bend_s

bend_s_offset

bend_s_offset(
    offset: float = 40.0,
    radius: float | None = 10.0,
    cross_section: CrossSectionSpec = "strip",
    width: float | None = None,
    with_euler: bool | None = None,
    p: float = 1,
    with_arc_floorplan: bool = False,
    npoints: int | None = None,
    angular_step: float | None = None,
) -> gf.Component

Return S bend made of two bends with a straight section.

Parameters:

Name Type Description Default
offset float

in um.

40.0
radius float | None

in um. if None, uses cross_section_radius.

10.0
cross_section CrossSectionSpec

spec.

'strip'
width float | None

width to use. Defaults to cross_section.width.

None
with_euler bool | None

deprecated, use p=0 for circular arc instead.

None
p float

1 means standard Euler bend. 0 means circular arc.

1
with_arc_floorplan bool

if True the size of the bend will be adjusted to match an arc bend with the specified radius. If False: radius is the minimum radius of curvature.

False
npoints int | None

number of points.

None
angular_step float | None

If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.

None
Source code in gdsfactory/components/bends/bend_s.py
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
@gf.cell_with_module_name(schematic_function=sbend_schematic, tags=["bends"])
def bend_s_offset(
    offset: float = 40.0,
    radius: float | None = 10.0,
    cross_section: CrossSectionSpec = "strip",
    width: float | None = None,
    with_euler: bool | None = None,
    p: float = 1,
    with_arc_floorplan: bool = False,
    npoints: int | None = None,
    angular_step: float | None = None,
) -> gf.Component:
    """Return S bend made of two bends with a straight section.

    Args:
        offset: in um.
        radius: in um. if None, uses cross_section_radius.
        cross_section: spec.
        width: width to use. Defaults to cross_section.width.
        with_euler: deprecated, use p=0 for circular arc instead.
        p: 1 means standard Euler bend. 0 means circular arc.
        with_arc_floorplan: if True the size of the bend will be adjusted to match an arc bend with the specified radius. If False: `radius` is the minimum radius of curvature.
        npoints: number of points.
        angular_step: If provided, determines the angular step (in degrees) between points. Mutually exclusive with npoints.
    """
    if with_euler is not None:
        warnings.warn(
            "with_euler is deprecated. Use p=0 for circular arc instead. And p=1 for euler bend.",
            DeprecationWarning,
            stacklevel=2,
        )
        if not with_euler:
            p = 0

    if width:
        xs = gf.get_cross_section(cross_section, width=width)
    else:
        xs = gf.get_cross_section(cross_section)

    radius = radius or xs.radius
    assert radius is not None, "radius cannot be None"

    xs.validate_radius(radius)
    angle, middle_length = _get_euler_sbend_angle_middle_length_from_jog(
        jog=abs(offset) / 2, radius=radius, p=p, use_eff=with_arc_floorplan
    )
    angle = math.copysign(angle, offset)
    path = gf.path.euler(
        radius=radius,
        angle=+angle,
        p=p,
        use_eff=with_arc_floorplan,
        npoints=npoints,
        angular_step=angular_step,
    )
    if middle_length > 1e-6:
        path += gf.path.straight(length=middle_length)
    path += gf.path.euler(
        radius=radius,
        angle=-angle,
        p=p,
        use_eff=with_arc_floorplan,
        npoints=npoints,
        angular_step=angular_step,
    )

    return gf.path.extrude(path, cross_section=xs)

bend_s_offset

bend_topic

bend_topic

bend_topic(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.1,
    npoints: int = 100,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    layer: LayerSpec | None = None,
    width: float | None = None,
) -> Component

Returns a regular degree Third Order Polynomial Interconnected Circular (TOPIC) bend component.

The implementation follows the description in this publication https://arxiv.org/html/2411.15025v1.

The bend consists of three parts: a. Initial transition from straight to bend, known as TOP segment. b. Circular part whose center and radius are calculated analytically. c. Mirroring of TOP segment with respect to the bisection of the angle.

Parameters:

Name Type Description Default
radius float | None

radius at the start and end of bend.

None
angle float

total angle of the curve in degrees.

90.0
p float

used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).

0.1
npoints int

Number of points used per 360 degrees.

100
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
Source code in gdsfactory/components/bends/bend_topic.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
@gf.cell_with_module_name(schematic_function=bend_schematic, tags=["bends"])
def bend_topic(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.1,
    npoints: int = 100,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    layer: LayerSpec | None = None,
    width: float | None = None,
) -> Component:
    """Returns a regular degree Third Order Polynomial Interconnected Circular (TOPIC) bend component.

    The implementation follows the description in this publication https://arxiv.org/html/2411.15025v1.

    The bend consists of three parts:
    a. Initial transition from straight to bend, known as TOP segment.
    b. Circular part whose center and radius are calculated analytically.
    c. Mirroring of TOP segment with respect to the bisection of the angle.

    Args:
        radius: radius at the start and end of bend.
        angle: total angle of the curve in degrees.
        p: used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).
        npoints: Number of points used per 360 degrees.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
    """
    if abs(angle) not in {90, 180}:
        warnings.warn(
            f"bend_topic angle should be 90 or 180. Got {angle}. Use bend_topic_all_angle instead.",
            UserWarning,
            stacklevel=3,
        )
    return _bend_topic(
        radius=radius,
        angle=angle,
        p=p,
        npoints=npoints,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        layer=layer,
        width=width,
        all_angle=False,
    )

bend_topic_all_angle

bend_topic_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.1,
    npoints: int = 100,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    layer: LayerSpec | None = None,
    width: float | None = None,
) -> ComponentAllAngle

Returns a Third Order Polynomial Interconnected Circular (TOPIC) bend component of arbitrary angle.

The implementation follows the description in this publication https://arxiv.org/html/2411.15025v1.

The bend consists of three parts: a. Initial transition from straight to bend, known as TOP segment. b. Circular part whose center and radius are calculated analytically. c. Mirroring of TOP segment with respect to the bisection of the angle.

Parameters:

Name Type Description Default
radius float | None

radius at the start and end of bend.

None
angle float

total angle of the curve in degrees.

90.0
p float

used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).

0.1
npoints int

Number of points used per 360 degrees.

100
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
Source code in gdsfactory/components/bends/bend_topic.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
@gf.vcell
def bend_topic_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.1,
    npoints: int = 100,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    layer: LayerSpec | None = None,
    width: float | None = None,
) -> ComponentAllAngle:
    """Returns a Third Order Polynomial Interconnected Circular (TOPIC) bend component of arbitrary angle.

    The implementation follows the description in this publication https://arxiv.org/html/2411.15025v1.

    The bend consists of three parts:
    a. Initial transition from straight to bend, known as TOP segment.
    b. Circular part whose center and radius are calculated analytically.
    c. Mirroring of TOP segment with respect to the bisection of the angle.

    Args:
        radius: radius at the start and end of bend.
        angle: total angle of the curve in degrees.
        p: used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).
        npoints: Number of points used per 360 degrees.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
    """
    return _bend_topic(
        radius=radius,
        angle=angle,
        p=p,
        npoints=npoints,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        layer=layer,
        width=width,
        all_angle=True,
    )

bend_topic_s

bend_topic_s(
    radius: float | None = None,
    p: float = 0.1,
    npoints: int = 100,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    port1: str = "o1",
    port2: str = "o2",
) -> Component

Sbend made of 2 topic bends.

Parameters:

Name Type Description Default
radius float | None

radius at the start and end of bend.

None
p float

used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).

0.1
npoints int

Number of points used per 360 degrees.

100
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
port1 str

input port name.

'o1'
port2 str

output port name.

        _____ o2
       /
      /
     /
    /
    |
   /
  /
 /

o1_____/

'o2'
Source code in gdsfactory/components/bends/bend_topic.py
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
@gf.cell_with_module_name(schematic_function=sbend_schematic, tags=["bends"])
def bend_topic_s(
    radius: float | None = None,
    p: float = 0.1,
    npoints: int = 100,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    port1: str = "o1",
    port2: str = "o2",
) -> Component:
    r"""Sbend made of 2 topic bends.

    Args:
        radius: radius at the start and end of bend.
        p: used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).
        npoints: Number of points used per 360 degrees.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        port1: input port name.
        port2: output port name.

                        _____ o2
                       /
                      /
                     /
                    /
                    |
                   /
                  /
                 /
         o1_____/

    """
    c = Component()
    b = bend_topic(
        radius=radius,
        angle=90,
        p=p,
        npoints=npoints,
        layer=layer,
        width=width,
        allow_min_radius_violation=allow_min_radius_violation,
        cross_section=cross_section,
    )
    b1 = c.add_ref(b)
    b2 = c.add_ref(b)
    b2.connect(port1, b1[port2], mirror=True)
    c.add_port(port1, port=b1[port1])
    c.add_port(port2, port=b2[port2])
    c.info["length"] = 2 * b.info["length"]
    return c

bend_topic

bend_topic180 module-attribute

bend_topic180 = partial(bend_topic, angle=180)

bend_topic180

bend_topic_all_angle

bend_topic_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.1,
    npoints: int = 100,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    layer: LayerSpec | None = None,
    width: float | None = None,
) -> ComponentAllAngle

Returns a Third Order Polynomial Interconnected Circular (TOPIC) bend component of arbitrary angle.

The implementation follows the description in this publication https://arxiv.org/html/2411.15025v1.

The bend consists of three parts: a. Initial transition from straight to bend, known as TOP segment. b. Circular part whose center and radius are calculated analytically. c. Mirroring of TOP segment with respect to the bisection of the angle.

Parameters:

Name Type Description Default
radius float | None

radius at the start and end of bend.

None
angle float

total angle of the curve in degrees.

90.0
p float

used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).

0.1
npoints int

Number of points used per 360 degrees.

100
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
Source code in gdsfactory/components/bends/bend_topic.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
@gf.vcell
def bend_topic_all_angle(
    radius: float | None = None,
    angle: float = 90.0,
    p: float = 0.1,
    npoints: int = 100,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    layer: LayerSpec | None = None,
    width: float | None = None,
) -> ComponentAllAngle:
    """Returns a Third Order Polynomial Interconnected Circular (TOPIC) bend component of arbitrary angle.

    The implementation follows the description in this publication https://arxiv.org/html/2411.15025v1.

    The bend consists of three parts:
    a. Initial transition from straight to bend, known as TOP segment.
    b. Circular part whose center and radius are calculated analytically.
    c. Mirroring of TOP segment with respect to the bisection of the angle.

    Args:
        radius: radius at the start and end of bend.
        angle: total angle of the curve in degrees.
        p: used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).
        npoints: Number of points used per 360 degrees.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
    """
    return _bend_topic(
        radius=radius,
        angle=angle,
        p=p,
        npoints=npoints,
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
        layer=layer,
        width=width,
        all_angle=True,
    )
import gdsfactory as gf

gf.gpdk.PDK.activate()

c = gf.components.bend_topic_all_angle(angle=90, p=0.1, npoints=100, cross_section='strip', allow_min_radius_violation=False).copy()
c.draw_ports()
c.plot()

bend_topic_s

bend_topic_s(
    radius: float | None = None,
    p: float = 0.1,
    npoints: int = 100,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    port1: str = "o1",
    port2: str = "o2",
) -> Component

Sbend made of 2 topic bends.

Parameters:

Name Type Description Default
radius float | None

radius at the start and end of bend.

None
p float

used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).

0.1
npoints int

Number of points used per 360 degrees.

100
cross_section CrossSectionSpec

specification (CrossSection, string, CrossSectionFactory dict).

'strip'
allow_min_radius_violation bool

if True allows radius to be smaller than cross_section radius.

False
layer LayerSpec | None

layer to use. Defaults to cross_section.layer.

None
width float | None

width to use. Defaults to cross_section.width.

None
port1 str

input port name.

'o1'
port2 str

output port name.

        _____ o2
       /
      /
     /
    /
    |
   /
  /
 /

o1_____/

'o2'
Source code in gdsfactory/components/bends/bend_topic.py
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
@gf.cell_with_module_name(schematic_function=sbend_schematic, tags=["bends"])
def bend_topic_s(
    radius: float | None = None,
    p: float = 0.1,
    npoints: int = 100,
    layer: LayerSpec | None = None,
    width: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    port1: str = "o1",
    port2: str = "o2",
) -> Component:
    r"""Sbend made of 2 topic bends.

    Args:
        radius: radius at the start and end of bend.
        p: used to calculate the angle of the bend at the end of TOP / start of circular arc, as p*angle. It should be within [0, 0.5).
        npoints: Number of points used per 360 degrees.
        cross_section: specification (CrossSection, string, CrossSectionFactory dict).
        allow_min_radius_violation: if True allows radius to be smaller than cross_section radius.
        layer: layer to use. Defaults to cross_section.layer.
        width: width to use. Defaults to cross_section.width.
        port1: input port name.
        port2: output port name.

                        _____ o2
                       /
                      /
                     /
                    /
                    |
                   /
                  /
                 /
         o1_____/

    """
    c = Component()
    b = bend_topic(
        radius=radius,
        angle=90,
        p=p,
        npoints=npoints,
        layer=layer,
        width=width,
        allow_min_radius_violation=allow_min_radius_violation,
        cross_section=cross_section,
    )
    b1 = c.add_ref(b)
    b2 = c.add_ref(b)
    b2.connect(port1, b1[port2], mirror=True)
    c.add_port(port1, port=b1[port1])
    c.add_port(port2, port=b2[port2])
    c.info["length"] = 2 * b.info["length"]
    return c

bend_topic_s

bezier

bezier(
    control_points: Coordinates = (
        (0.0, 0.0),
        (5.0, 0.0),
        (5.0, 1.8),
        (10.0, 1.8),
    ),
    npoints: int = 201,
    with_manhattan_facing_angles: bool = True,
    start_angle: int | None = None,
    end_angle: int | None = None,
    cross_section: CrossSectionSpec = "strip",
    bend_radius_error_type: ErrorType | None = None,
    allow_min_radius_violation: bool = False,
    width: float | None = None,
) -> Component

Returns Bezier bend.

Parameters:

Name Type Description Default
control_points Coordinates

list of points.

((0.0, 0.0), (5.0, 0.0), (5.0, 1.8), (10.0, 1.8))
npoints int

number of points varying between 0 and 1.

201
with_manhattan_facing_angles bool

bool.

True
start_angle int | None

optional start angle in deg.

None
end_angle int | None

optional end angle in deg.

None
cross_section CrossSectionSpec

spec.

'strip'
bend_radius_error_type ErrorType | None

error type.

None
allow_min_radius_violation bool

bool.

False
width float | None

width to use. Defaults to cross_section.width.

None
Source code in gdsfactory/components/bends/bend_s.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@gf.cell_with_module_name(schematic_function=sbend_schematic, tags=["bends"])
def bezier(
    control_points: Coordinates = ((0.0, 0.0), (5.0, 0.0), (5.0, 1.8), (10.0, 1.8)),
    npoints: int = 201,
    with_manhattan_facing_angles: bool = True,
    start_angle: int | None = None,
    end_angle: int | None = None,
    cross_section: CrossSectionSpec = "strip",
    bend_radius_error_type: ErrorType | None = None,
    allow_min_radius_violation: bool = False,
    width: float | None = None,
) -> Component:
    """Returns Bezier bend.

    Args:
        control_points: list of points.
        npoints: number of points varying between 0 and 1.
        with_manhattan_facing_angles: bool.
        start_angle: optional start angle in deg.
        end_angle: optional end angle in deg.
        cross_section: spec.
        bend_radius_error_type: error type.
        allow_min_radius_violation: bool.
        width: width to use. Defaults to cross_section.width.
    """
    if width:
        xs = gf.get_cross_section(cross_section, width=width)
    else:
        xs = gf.get_cross_section(cross_section)

    t = np.linspace(0, 1, npoints)
    path_points = bezier_curve(t, control_points)
    path = gf.Path(path_points)

    if with_manhattan_facing_angles:
        path.start_angle = start_angle or snap_angle(path.start_angle)
        path.end_angle = end_angle or snap_angle(path.end_angle)

    c = path.extrude(xs)
    curv = curvature(path_points, t)
    length = path.length()
    if max(np.abs(curv)) == 0:
        min_bend_radius = np.inf
    else:
        min_bend_radius = float(gf.snap.snap_to_grid(float(1 / np.max(np.abs(curv)))))

    c.info["length"] = length
    c.info["min_bend_radius"] = min_bend_radius
    c.info["start_angle"] = float(path.start_angle)
    c.info["end_angle"] = float(path.end_angle)
    c.add_route_info(
        cross_section=xs,
        length=c.info["length"],
        n_bend_s=1,
        min_bend_radius=min_bend_radius,
    )

    if not allow_min_radius_violation:
        xs.validate_radius(min_bend_radius, bend_radius_error_type)

    xs.add_bbox(c)
    return c

bezier

containers

add_fiber_array_optical_south_electrical_north

add_fiber_array_optical_south_electrical_north

add_fiber_array_optical_south_electrical_north(
    component: ComponentSpec = "straight_heater_metal",
    pad: ComponentSpec = "pad",
    grating_coupler: ComponentSpec = "grating_coupler_te",
    cross_section_metal: CrossSectionSpec = "metal_routing",
    with_loopback: bool = True,
    pad_pitch: float = 100.0,
    pitch: float = 127.0,
    pad_gc_spacing: float = 250.0,
    electrical_port_names: list[str] | None = None,
    electrical_port_orientation: AngleInDegrees | None = 90,
    npads: int | None = None,
    port_types_grating_couplers: list[str] | None = None,
    auto_taper_pads: bool = True,
    **kwargs: Any
) -> Component

Returns a fiber array with Optical gratings on South and Electrical pads on North.

This a test configuration for DC pads.

Parameters:

Name Type Description Default
component ComponentSpec

component spec to add fiber and pads.

'straight_heater_metal'
pad ComponentSpec

pad spec.

'pad'
grating_coupler ComponentSpec

grating coupler function.

'grating_coupler_te'
cross_section_metal CrossSectionSpec

metal cross section.

'metal_routing'
with_loopback bool

whether to add a loopback port.

True
pad_pitch float

spacing between pads.

100.0
pitch float

spacing between grating couplers.

127.0
pad_gc_spacing float

spacing between pads and grating couplers.

250.0
electrical_port_names list[str] | None

list of electrical port names. Defaults to all.

None
electrical_port_orientation AngleInDegrees | None

orientation of electrical ports. Defaults to 90.

90
npads int | None

number of pads. Defaults to one per electrical_port_names.

None
port_types_grating_couplers list[str] | None

port types for grating couplers. Defaults to vertical TE, TM, and dual.

None
auto_taper_pads bool

whether to add a taper to the pads.

True
kwargs Any

additional arguments.

{}

Other Parameters:

Name Type Description
layer_label

layer for settings label.

measurement

measurement name.

measurement_settings

measurement settings.

analysis

analysis name.

doe

Design of Experiment.

anchor

anchor point for the label. Defaults to south-west "sw". Valid options are: "n", "s", "e", "w", "ne", "nw", "se", "sw", "c".

gc_port_name

grating coupler input port name.

gc_port_labels

grating coupler list of labels.

component_name

optional for the label.

select_ports

function to select ports.

cross_section

cross_section function.

get_input_labels_function

function to get input labels. None skips labels.

layer_label

optional layer for grating coupler label.

bend

bend spec.

straight

straight spec.

taper

taper spec.

get_input_label_text_loopback_function

function to get input label test.

get_input_label_text_function

for labels.

fanout_length

if None, automatic calculation of fanout length.

max_y0_optical

in um.

with_loopback bool

True, adds loopback structures.

straight_separation

from edge to edge.

list_port_labels

None, adds TM labels to port indices in this list.

connected_port_list_ids

names of ports only for type 0 optical routing.

nb_optical_ports_lines

number of grating coupler lines.

force_manhattan

False

excluded_ports

list of port names to exclude when adding gratings.

grating_indices

list of grating coupler indices.

routing_straight

function to route.

routing_method

route_bundle.

gc_rotation

fiber coupler rotation in degrees. Defaults to -90.

input_port_indexes

to connect.

Source code in gdsfactory/components/containers/add_fiber_array_optical_south_electrical_north.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
@gf.cell_with_module_name(tags=["containers"])
def add_fiber_array_optical_south_electrical_north(
    component: ComponentSpec = "straight_heater_metal",
    pad: ComponentSpec = "pad",
    grating_coupler: ComponentSpec = "grating_coupler_te",
    cross_section_metal: CrossSectionSpec = "metal_routing",
    with_loopback: bool = True,
    pad_pitch: float = 100.0,
    pitch: float = 127.0,
    pad_gc_spacing: float = 250.0,
    electrical_port_names: list[str] | None = None,
    electrical_port_orientation: AngleInDegrees | None = 90,
    npads: int | None = None,
    port_types_grating_couplers: list[str] | None = None,
    auto_taper_pads: bool = True,
    **kwargs: Any,
) -> Component:
    """Returns a fiber array with Optical gratings on South and Electrical pads on North.

    This a test configuration for DC pads.

    Args:
        component: component spec to add fiber and pads.
        pad: pad spec.
        grating_coupler: grating coupler function.
        cross_section_metal: metal cross section.
        with_loopback: whether to add a loopback port.
        pad_pitch: spacing between pads.
        pitch: spacing between grating couplers.
        pad_gc_spacing: spacing between pads and grating couplers.
        electrical_port_names: list of electrical port names. Defaults to all.
        electrical_port_orientation: orientation of electrical ports. Defaults to 90.
        npads: number of pads. Defaults to one per electrical_port_names.
        port_types_grating_couplers: port types for grating couplers. Defaults to vertical TE, TM, and dual.
        auto_taper_pads: whether to add a taper to the pads.
        kwargs: additional arguments.

    Keyword Args:
        layer_label: layer for settings label.
        measurement: measurement name.
        measurement_settings: measurement settings.
        analysis: analysis name.
        doe: Design of Experiment.
        anchor: anchor point for the label. Defaults to south-west "sw". \
            Valid options are: "n", "s", "e", "w", "ne", "nw", "se", "sw", "c".
        gc_port_name: grating coupler input port name.
        gc_port_labels: grating coupler list of labels.
        component_name: optional for the label.
        select_ports: function to select ports.
        cross_section: cross_section function.
        get_input_labels_function: function to get input labels. None skips labels.
        layer_label: optional layer for grating coupler label.
        bend: bend spec.
        straight: straight spec.
        taper: taper spec.
        get_input_label_text_loopback_function: function to get input label test.
        get_input_label_text_function: for labels.
        fanout_length: if None, automatic calculation of fanout length.
        max_y0_optical: in um.
        with_loopback: True, adds loopback structures.
        straight_separation: from edge to edge.
        list_port_labels: None, adds TM labels to port indices in this list.
        connected_port_list_ids: names of ports only for type 0 optical routing.
        nb_optical_ports_lines: number of grating coupler lines.
        force_manhattan: False
        excluded_ports: list of port names to exclude when adding gratings.
        grating_indices: list of grating coupler indices.
        routing_straight: function to route.
        routing_method: route_bundle.
        gc_rotation: fiber coupler rotation in degrees. Defaults to -90.
        input_port_indexes: to connect.

    """
    c = Component()
    component = gf.get_component(component)
    r = c << gf.routing.add_fiber_array(
        component=component,
        grating_coupler=grating_coupler,
        with_loopback=with_loopback,
        pitch=pitch,
        **kwargs,
    )
    port_types_grating_couplers = (
        port_types_grating_couplers or gf.CONF.port_types_grating_couplers
    )
    optical_ports = [
        port for port in r.ports if port.port_type in port_types_grating_couplers
    ]
    c.add_ports(optical_ports)

    electrical_ports = r.ports.filter(
        port_type="electrical", orientation=electrical_port_orientation
    )
    electrical_port_names_list = electrical_port_names or [
        p.name for p in electrical_ports if p.name is not None
    ]

    npads = npads or len(electrical_port_names_list)
    pads = c << gf.components.array(
        component=pad,
        columns=npads,
        column_pitch=pad_pitch,
    )
    pads.x = r.x
    pads.ymin = r.ymin + pad_gc_spacing

    electrical_ports = [r[por_name] for por_name in electrical_port_names_list]
    nroutes = min(len(electrical_ports), npads)

    ports1 = electrical_ports[:nroutes]
    ports2 = list(pads.ports.filter(orientation=270))[:nroutes]

    gf.routing.route_bundle_electrical(
        c,
        ports1=ports1,
        ports2=ports2,
        cross_section=cross_section_metal,
        sort_ports=True,
        auto_taper=auto_taper_pads,
    )

    c.add_ports(ports2)

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    return c

add_fiber_array_optical_south_electrical_north

add_termination

add_termination

add_termination(
    component: ComponentSpec = "straight",
    port_names: tuple[str, ...] | None = None,
    terminator: ComponentSpec = _terminator_function,
    terminator_port_name: str | None = None,
) -> Component

Returns component with terminator on some ports.

Parameters:

Name Type Description Default
component ComponentSpec

to add terminator.

'straight'
port_names tuple[str, ...] | None

ports to add terminator.

None
terminator ComponentSpec

factory for the terminator.

_terminator_function
terminator_port_name str | None

for the terminator to connect to the component ports.

None
Source code in gdsfactory/components/containers/add_termination.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@gf.cell_with_module_name(tags=["containers"])
def add_termination(
    component: ComponentSpec = "straight",
    port_names: tuple[str, ...] | None = None,
    terminator: ComponentSpec = _terminator_function,
    terminator_port_name: str | None = None,
) -> Component:
    """Returns component with terminator on some ports.

    Args:
        component: to add terminator.
        port_names: ports to add terminator.
        terminator: factory for the terminator.
        terminator_port_name: for the terminator to connect to the component ports.
    """
    terminator = gf.get_component(terminator)
    terminator_port_name = terminator_port_name or terminator.ports[0].name

    assert terminator_port_name is not None

    c = Component()
    component = gf.get_component(component)
    ref = c.add_ref(component)

    ports_names_all = [p.name for p in component.ports]
    ports_names_to_terminate = port_names or ports_names_all

    for port_name in ports_names_all:
        if port_name in ports_names_to_terminate:
            t_ref = c.add_ref(terminator)
            t_ref.connect(port=terminator_port_name, other=ref[port_name])
        else:
            port = ref[port_name]
            c.add_port(name=port.name, port=port)

    c.copy_child_info(component)
    return c

add_termination

add_trenches

add_trenches

add_trenches(
    component: ComponentSpec = "coupler",
    layer_component: LayerSpec = "WG",
    layer_trench: LayerSpec = "DEEP_ETCH",
    width_trench: float = 2.0,
    cross_section: CrossSectionSpec | None = None,
    top: float | None = None,
    bot: float | None = None,
    right: float | None = 0,
    left: float | None = 0,
) -> gf.Component

Return inverted component with trenches.

Parameters:

Name Type Description Default
component ComponentSpec

component to add to the trenches.

'coupler'
layer_component LayerSpec

layer of the component to invert.

'WG'
layer_trench LayerSpec

layer of the trenches.

'DEEP_ETCH'
width_trench float

width of the trenches.

2.0
cross_section CrossSectionSpec | None

spec (CrossSection, string or dict).

None
top float | None

width of the trench on the top. If None uses width_trench.

None
bot float | None

width of the trench on the bottom. If None uses width_trench.

None
right float | None

width of the trench on the right. If None uses width_trench.

0
left float | None

width of the trench on the left. If None uses width_trench.

0
Source code in gdsfactory/components/containers/add_trenches.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@gf.cell_with_module_name(tags=["containers"])
def add_trenches(
    component: ComponentSpec = "coupler",
    layer_component: LayerSpec = "WG",
    layer_trench: LayerSpec = "DEEP_ETCH",
    width_trench: float = 2.0,
    cross_section: CrossSectionSpec | None = None,
    top: float | None = None,
    bot: float | None = None,
    right: float | None = 0,
    left: float | None = 0,
) -> gf.Component:
    """Return inverted component with trenches.

    Args:
        component: component to add to the trenches.
        layer_component: layer of the component to invert.
        layer_trench: layer of the trenches.
        width_trench: width of the trenches.
        cross_section: spec (CrossSection, string or dict).
        top: width of the trench on the top. If None uses width_trench.
        bot: width of the trench on the bottom. If None uses width_trench.
        right: width of the trench on the right. If None uses width_trench.
        left: width of the trench on the left. If None uses width_trench.
    """
    component = gf.get_component(component)
    top = top if top is not None else width_trench
    bot = bot if bot is not None else width_trench
    right = right if right is not None else width_trench
    left = left if left is not None else width_trench

    core = component
    clad = gf.c.bbox(
        core, layer=layer_trench, top=top, bottom=bot, left=left, right=right
    )
    c = gf.boolean(
        clad,
        core,
        operation="not",
        layer=layer_trench,
        layer1=layer_trench,
        layer2=layer_component,
    )

    c.add_ports(component.ports)
    c.copy_child_info(component)
    if cross_section is not None:
        xs = gf.get_cross_section(cross_section)
        xs.add_bbox(c)
    return c

add_trenches

add_trenches90 module-attribute

add_trenches90 = partial(
    add_trenches,
    component="bend_euler",
    top=0,
    left=0,
    right=None,
)

add_trenches90

array

array(
    component: ComponentSpec = "pad",
    columns: int = 6,
    rows: int = 1,
    column_pitch: float = 150,
    row_pitch: float = 150,
    add_ports: bool = True,
    size: Size | None = None,
    centered: bool = False,
    post_process: PostProcesses | None = None,
    auto_rename_ports: bool = False,
) -> Component

Returns an array of components.

Parameters:

Name Type Description Default
component ComponentSpec

to replicate.

'pad'
columns int

in x.

6
rows int

in y.

1
column_pitch float

pitch between columns.

150
row_pitch float

pitch between rows.

150
auto_rename_ports bool

True to auto rename ports.

False
add_ports bool

add ports from component into the array.

True
size Size | None

Optional x, y size. Overrides columns and rows.

None
centered bool

center the array around the origin.

False
post_process PostProcesses | None

function to apply to the array after creation.

None

Raises:

Type Description
ValueError

If columns > 1 and spacing[0] = 0.

ValueError

If rows > 1 and spacing[1] = 0.

Source code in gdsfactory/components/containers/array_component.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
@gf.cell_with_module_name(tags=["containers"])
def array(
    component: ComponentSpec = "pad",
    columns: int = 6,
    rows: int = 1,
    column_pitch: float = 150,
    row_pitch: float = 150,
    add_ports: bool = True,
    size: Size | None = None,
    centered: bool = False,
    post_process: PostProcesses | None = None,
    auto_rename_ports: bool = False,
) -> Component:
    """Returns an array of components.

    Args:
        component: to replicate.
        columns: in x.
        rows: in y.
        column_pitch: pitch between columns.
        row_pitch: pitch between rows.
        auto_rename_ports: True to auto rename ports.
        add_ports: add ports from component into the array.
        size: Optional x, y size. Overrides columns and rows.
        centered: center the array around the origin.
        post_process: function to apply to the array after creation.

    Raises:
        ValueError: If columns > 1 and spacing[0] = 0.
        ValueError: If rows > 1 and spacing[1] = 0.

        2 rows x 4 columns

          column_pitch
          <---------->
         ___        ___       ___        ___
        |   |      |   |     |   |      |   |
        |___|      |___|     |___|      |___|

         ___        ___       ___        ___
        |   |      |   |     |   |      |   |
        |___|      |___|     |___|      |___|
    """
    if size:
        columns = int(size[0] / column_pitch)
        rows = int(size[1] / row_pitch)

    if rows > 1 and row_pitch == 0:
        raise ValueError(f"rows = {rows} > 1 require {row_pitch=} > 0")

    if columns > 1 and column_pitch == 0:
        raise ValueError(f"columns = {columns} > 1 require {column_pitch} > 0")

    c = Component()
    component = gf.get_component(component)
    ref = c.add_ref(
        component,
        columns=columns,
        rows=rows,
        column_pitch=column_pitch,
        row_pitch=row_pitch,
    )
    if centered:
        ref.center = (0, 0)

    if add_ports and component.ports:
        for ix in range(ref.na or 1):
            for iy in range(ref.nb or 1):
                for port in component.ports:
                    port = port.copy(ref.trans * gf.kdb.Trans(ix * ref.a + iy * ref.b))
                    name = f"{port.name}_{iy + 1}_{ix + 1}"
                    c.add_port(name, port=port)

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    if post_process:
        for f in post_process:
            f(c)
    if auto_rename_ports:
        c.auto_rename_ports()
    return c

array

array_hexagonal

array_hexagonal

array_hexagonal(
    component: ComponentSpec = "circle",
    columns: int = 10,
    rows: int = 10,
    pitch: float = 25.0,
    centered: bool = True,
    add_ports: bool = True,
) -> Component

Returns a hexagonal close-packed array of components.

Even rows are placed normally, odd rows are offset by pitch/2. Row spacing is pitch * sqrt(3)/2.

Parameters:

Name Type Description Default
component ComponentSpec

component to replicate.

'circle'
columns int

number of columns.

10
rows int

number of rows.

10
pitch float

spacing between adjacent elements.

25.0
centered bool

center the array around the origin.

True
add_ports bool

add ports from each element.

True
Source code in gdsfactory/components/containers/array_hexagonal.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@gf.cell_with_module_name(tags=["containers"])
def array_hexagonal(
    component: ComponentSpec = "circle",
    columns: int = 10,
    rows: int = 10,
    pitch: float = 25.0,
    centered: bool = True,
    add_ports: bool = True,
) -> Component:
    """Returns a hexagonal close-packed array of components.

    Even rows are placed normally, odd rows are offset by pitch/2.
    Row spacing is pitch * sqrt(3)/2.

    Args:
        component: component to replicate.
        columns: number of columns.
        rows: number of rows.
        pitch: spacing between adjacent elements.
        centered: center the array around the origin.
        add_ports: add ports from each element.
    """
    c = Component()
    comp = gf.get_component(component)
    row_spacing = pitch * np.sqrt(3) / 2

    for row in range(rows):
        x_offset = pitch / 2 if row % 2 else 0.0
        for col in range(columns):
            ref = c.add_ref(comp)
            x = col * pitch + x_offset
            y = row * row_spacing
            ref.move((x, y))

            if add_ports and comp.ports:
                for port in comp.ports:
                    name = f"{port.name}_{row + 1}_{col + 1}"
                    c.add_port(name, port=port.copy(ref.trans))

    if centered:
        c.center = (0, 0)

    return c

array_hexagonal

array_polar

array_polar

array_polar(
    component: ComponentSpec = "C",
    n_items: int = 6,
    radius: float = 50.0,
    start_angle: float = 0.0,
    end_angle: float = 360.0,
    rotate_items: bool = True,
    add_ports: bool = True,
) -> Component

Returns a polar/circular array of components.

Places component refs at equal angular intervals around a circle.

Parameters:

Name Type Description Default
component ComponentSpec

component to replicate.

'C'
n_items int

number of items in the array.

6
radius float

radius of the circle.

50.0
start_angle float

starting angle in degrees.

0.0
end_angle float

ending angle in degrees.

360.0
rotate_items bool

if True, rotate each item to point radially outward.

True
add_ports bool

add ports from each element.

True
Source code in gdsfactory/components/containers/array_polar.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@gf.cell(
    with_module_name=True,
    check_instances=CheckInstances.IGNORE,
    tags=["containers"],
)
def array_polar(
    component: ComponentSpec = "C",
    n_items: int = 6,
    radius: float = 50.0,
    start_angle: float = 0.0,
    end_angle: float = 360.0,
    rotate_items: bool = True,
    add_ports: bool = True,
) -> Component:
    """Returns a polar/circular array of components.

    Places component refs at equal angular intervals around a circle.

    Args:
        component: component to replicate.
        n_items: number of items in the array.
        radius: radius of the circle.
        start_angle: starting angle in degrees.
        end_angle: ending angle in degrees.
        rotate_items: if True, rotate each item to point radially outward.
        add_ports: add ports from each element.
    """
    c = Component()
    comp = gf.get_component(component)

    if abs(end_angle - start_angle) >= 360.0:
        angles = np.linspace(start_angle, end_angle, n_items, endpoint=False)
    else:
        angles = np.linspace(start_angle, end_angle, n_items, endpoint=True)

    for i, angle_deg in enumerate(angles):
        angle_rad = np.radians(angle_deg)
        x = radius * np.cos(angle_rad)
        y = radius * np.sin(angle_rad)

        ref = c.add_ref(comp)
        if rotate_items:
            ref.rotate(angle_deg)
        ref.move((x, y))

        if add_ports and comp.ports:
            for port in comp.ports:
                name = f"{port.name}_{i + 1}"
                c.add_port(name, port=port.copy(ref.trans))

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    return c

array_polar

component_sequence

SequenceGenerator

Source code in gdsfactory/components/containers/component_sequence.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class SequenceGenerator:
    def __init__(
        self,
        start_sequence: str = "IL",
        repeated_sequence: str = "ASASBSBS",
        end_sequence: str = "LO",
    ) -> None:
        """Sequence generator.

        Main use case: any type of cascade of components with repeating patterns
        such as serpentine, cutbacks etc...
        Component sequences have two ports by default.
        it adds aliases for the components forming the sequence.
        They use the component symbol with a suffix index starting from 1,
        so you may access the ports from any subcomponent.

        Usually we can break these components in 3 parts:
        - there is a starting pattern with input and possibly some special
        connections
        - then a repeating pattern
        - An ending pattern with an output

        Example of symbol meaning

        A: bend connected with input W0
        B: bend connected with input N0
        I: taper with input '1'
        O: taper with input '2'
        S: short straight waveguide
        L: long straight waveguide

        Args:
            start_sequence: starting sequence.
            end_sequence: ending sequence.
            repeated_sequence: repeating sequence.
        """
        self.start_sequence = start_sequence
        self.end_sequence = end_sequence
        self.repeated_sequence = repeated_sequence

    def get_sequence(self, n: int = 2) -> str:
        return self.start_sequence + n * self.repeated_sequence + self.end_sequence

__init__

__init__(
    start_sequence: str = "IL",
    repeated_sequence: str = "ASASBSBS",
    end_sequence: str = "LO",
) -> None

Sequence generator.

Main use case: any type of cascade of components with repeating patterns such as serpentine, cutbacks etc... Component sequences have two ports by default. it adds aliases for the components forming the sequence. They use the component symbol with a suffix index starting from 1, so you may access the ports from any subcomponent.

Usually we can break these components in 3 parts: - there is a starting pattern with input and possibly some special connections - then a repeating pattern - An ending pattern with an output

Example of symbol meaning

A: bend connected with input W0 B: bend connected with input N0 I: taper with input '1' O: taper with input '2' S: short straight waveguide L: long straight waveguide

Parameters:

Name Type Description Default
start_sequence str

starting sequence.

'IL'
end_sequence str

ending sequence.

'LO'
repeated_sequence str

repeating sequence.

'ASASBSBS'
Source code in gdsfactory/components/containers/component_sequence.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def __init__(
    self,
    start_sequence: str = "IL",
    repeated_sequence: str = "ASASBSBS",
    end_sequence: str = "LO",
) -> None:
    """Sequence generator.

    Main use case: any type of cascade of components with repeating patterns
    such as serpentine, cutbacks etc...
    Component sequences have two ports by default.
    it adds aliases for the components forming the sequence.
    They use the component symbol with a suffix index starting from 1,
    so you may access the ports from any subcomponent.

    Usually we can break these components in 3 parts:
    - there is a starting pattern with input and possibly some special
    connections
    - then a repeating pattern
    - An ending pattern with an output

    Example of symbol meaning

    A: bend connected with input W0
    B: bend connected with input N0
    I: taper with input '1'
    O: taper with input '2'
    S: short straight waveguide
    L: long straight waveguide

    Args:
        start_sequence: starting sequence.
        end_sequence: ending sequence.
        repeated_sequence: repeating sequence.
    """
    self.start_sequence = start_sequence
    self.end_sequence = end_sequence
    self.repeated_sequence = repeated_sequence

component_sequence

component_sequence(
    sequence: str,
    symbol_to_component: dict[
        str, tuple[Component, str, str]
    ],
    ports_map: dict[str, tuple[str, str]] | None = None,
    port_name1: str = "o1",
    port_name2: str = "o2",
    start_orientation: AngleInDegrees = 0.0,
    **kwargs: Any
) -> Component

Returns component from ASCII sequence.

if you prefix a symbol with ! it mirrors the component

Parameters:

Name Type Description Default
sequence str

a string or a list of symbols.

required
symbol_to_component dict[str, tuple[Component, str, str]]

maps symbols to (component, input, output).

required
ports_map dict[str, tuple[str, str]] | None

(optional) extra port mapping using the convention.

None
port_name1 str

input port_name.

'o1'
port_name2 str

output port_name.

'o2'
start_orientation AngleInDegrees

in degrees.

0.0
**kwargs Any

additional keyword arguments passed to the connect method.

{}

Returns:

Name Type Description
component Component

containing the sequence of sub-components instantiated and connected together in the sequence order.

Example
import gdsfactory as gf

bend180 = gf.components.bend_circular180()
wg_pin = gf.components.straight_pin(length=40)
wg = gf.components.straight()

# Define a map between symbols and (component, input port, output port)
symbol_to_component = {
"A": (bend180, 'o1', 'o2'),
"B": (bend180, 'o2', 'o1'),
"H": (wg_pin, 'o1', 'o2'),
"-": (wg, 'o1', 'o2'),
}

# Each character in the sequence represents a component
s = "AB-H-H-H-H-BA"
c = gf.components.component_sequence(sequence=s, symbol_to_component=symbol_to_component)
c.plot()
Source code in gdsfactory/components/containers/component_sequence.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def component_sequence(
    sequence: str,
    symbol_to_component: dict[str, tuple[Component, str, str]],
    ports_map: dict[str, tuple[str, str]] | None = None,
    port_name1: str = "o1",
    port_name2: str = "o2",
    start_orientation: AngleInDegrees = 0.0,
    **kwargs: Any,
) -> Component:
    """Returns component from ASCII sequence.

    if you prefix a symbol with ! it mirrors the component

    Args:
        sequence: a string or a list of symbols.
        symbol_to_component: maps symbols to (component, input, output).
        ports_map: (optional) extra port mapping using the convention.
            {port_name: (alias_name, port_name)}
        port_name1: input port_name.
        port_name2: output port_name.
        start_orientation: in degrees.
        **kwargs: additional keyword arguments passed to the connect method.

    Returns:
        component: containing the sequence of sub-components
            instantiated and connected together in the sequence order.

    Example:
        ```python
        import gdsfactory as gf

        bend180 = gf.components.bend_circular180()
        wg_pin = gf.components.straight_pin(length=40)
        wg = gf.components.straight()

        # Define a map between symbols and (component, input port, output port)
        symbol_to_component = {
        "A": (bend180, 'o1', 'o2'),
        "B": (bend180, 'o2', 'o1'),
        "H": (wg_pin, 'o1', 'o2'),
        "-": (wg, 'o1', 'o2'),
        }

        # Each character in the sequence represents a component
        s = "AB-H-H-H-H-BA"
        c = gf.components.component_sequence(sequence=s, symbol_to_component=symbol_to_component)
        c.plot()
        ```
    """
    ports_map = ports_map or {}
    named_references_counter: Counter[str] = Counter()
    component = Component()

    # Add first component reference and input port
    symbol = sequence[0] if "!" not in sequence[0] else sequence[:2]
    index = 2 if "!" in sequence[0] else 1
    name_start_device, do_flip = parse_component_name(symbol)
    component_input, input_port, prev_port = symbol_to_component[name_start_device]
    prev_device = component.add_ref(component_input, name=f"{symbol}{index}")
    named_references_counter.update({name_start_device: 1})

    if do_flip:
        prev_device = _flip_ref(prev_device, input_port)

    prev_device.rotate(angle=start_orientation)

    try:
        component.add_port(name=port_name1, port=prev_device.ports[input_port])
    except KeyError as exc:
        port_names = [port.name for port in prev_device.ports]
        raise KeyError(
            f"{prev_device.parent_cell.name!r} input_port {input_port!r} not in {port_names}"
        ) from exc

    ref: ComponentReference | None = None
    next_port: str | None = None

    while index < len(sequence):
        s = sequence[index]

        if s == "!":
            # if it's the last character skip
            if index + 1 >= len(sequence):
                index += 1
                continue
            s = sequence[index + 1]
            do_flip = True
            index += 1
        else:
            do_flip = False

        index += 1
        component_i, input_port, next_port = symbol_to_component[s]
        component_i = gf.get_component(component_i)
        named_references_counter.update({s: 1})
        alias = f"{s}{named_references_counter[s]}"
        ref = component.add_ref(component_i, name=alias)

        if do_flip:
            ref = _flip_ref(ref, input_port)

        try:
            ref.connect(input_port, prev_device.ports[prev_port], **kwargs)
        except KeyError as exc:
            port_names = [port.name for port in prev_device.ports]
            raise KeyError(
                f"{prev_device.parent_cell.name!r} port {prev_port!r} not in {port_names}"
            ) from exc

        prev_device = ref
        prev_port = next_port

    # Deal with edge case where the sequence contains only one component
    if len(sequence) == 1:
        ref = prev_device
        next_port = prev_port

    assert ref is not None
    assert next_port is not None

    component.add_port(name=port_name2, port=ref.ports[next_port])

    # Add any extra port specified in ports_map
    for name, (ref_name, alias_port_name) in ports_map.items():
        component.add_port(
            name=name, port=component.insts[ref_name].ports[alias_port_name]
        )

    return component

parse_component_name

parse_component_name(name: str) -> tuple[str, bool]

If the component name has more than one character and starts with "!".

then we need to flip along the axis given by the input port angle.

Source code in gdsfactory/components/containers/component_sequence.py
57
58
59
60
61
62
def parse_component_name(name: str) -> tuple[str, bool]:
    """If the component name has more than one character and starts with "!".

    then we need to flip along the axis given by the input port angle.
    """
    return (name[1:], True) if len(name) != 1 and name[0] == "!" else (name, False)

copy_layers

copy_layers

copy_layers(
    factory: ComponentSpec = "cross",
    layers: LayerSpecs = ((1, 0), (2, 0)),
    flatten: bool = False,
    **kwargs: Any
) -> Component

Returns a component with the geometry copied in different layers.

Parameters:

Name Type Description Default
factory ComponentSpec

component spec.

'cross'
layers LayerSpecs

iterable of layers.

((1, 0), (2, 0))
flatten bool

flatten the result.

False
kwargs Any

keyword arguments passed to the component.

{}
Source code in gdsfactory/components/containers/copy_layers.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@gf.cell_with_module_name(tags=["containers"])
def copy_layers(
    factory: ComponentSpec = "cross",
    layers: LayerSpecs = ((1, 0), (2, 0)),
    flatten: bool = False,
    **kwargs: Any,
) -> Component:
    """Returns a component with the geometry copied in different layers.

    Args:
        factory: component spec.
        layers: iterable of layers.
        flatten: flatten the result.
        kwargs: keyword arguments passed to the component.
    """
    c = Component()

    ci = None
    for layer in layers:
        c << (ci := gf.get_component(factory, layer=layer, **kwargs))
    if ci is not None:
        c.copy_child_info(ci)

    if flatten:
        c.flatten()
    return c

copy_layers

extend_ports

extend_ports(
    component: ComponentSpec = "mmi1x2",
    port_names: PortNames | None = None,
    length: float = 5.0,
    extension: ComponentSpec | None = None,
    port1: str | None = None,
    port2: str | None = None,
    port_type: str = "optical",
    centered: bool = False,
    cross_section: CrossSectionSpec | None = None,
    extension_port_names: list[str] | None = None,
    allow_width_mismatch: bool = False,
    auto_taper: bool = True,
    **kwargs: Any
) -> Component

Returns a new component with some ports extended.

You can define extension Spec defaults to port cross_section of each port to extend.

Parameters:

Name Type Description Default
component ComponentSpec

component to extend ports.

'mmi1x2'
port_names PortNames | None

list of ports names to extend, if None it extends all ports.

None
length float

extension length.

5.0
extension ComponentSpec | None

function to extend ports (defaults to a straight).

None
port1 str | None

extension input port name.

None
port2 str | None

extension output port name.

None
port_type str

type of the ports to extend.

'optical'
centered bool

if True centers rectangle at (0, 0).

False
cross_section CrossSectionSpec | None

extension cross_section, defaults to port cross_section if port has no cross_section it creates one using width and layer.

None
extension_port_names list[str] | None

extension port names add to the new component.

None
allow_width_mismatch bool

allow width mismatches.

False
auto_taper bool

if True adds automatic tapers.

True
kwargs Any

cross_section settings.

{}

Other Parameters:

Name Type Description
layer

port GDS layer.

prefix

port name prefix.

orientation

in degrees.

width

port width.

layers_excluded

List of layers to exclude.

port_type str

optical, electrical, ....

clockwise

if True, sort ports clockwise, False: counter-clockwise.

Source code in gdsfactory/components/containers/extension.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
@gf.cell_with_module_name(tags=["containers"])
def extend_ports(
    component: ComponentSpec = "mmi1x2",
    port_names: PortNames | None = None,
    length: float = 5.0,
    extension: ComponentSpec | None = None,
    port1: str | None = None,
    port2: str | None = None,
    port_type: str = "optical",
    centered: bool = False,
    cross_section: CrossSectionSpec | None = None,
    extension_port_names: list[str] | None = None,
    allow_width_mismatch: bool = False,
    auto_taper: bool = True,
    **kwargs: Any,
) -> Component:
    """Returns a new component with some ports extended.

    You can define extension Spec
    defaults to port cross_section of each port to extend.

    Args:
        component: component to extend ports.
        port_names: list of ports names to extend, if None it extends all ports.
        length: extension length.
        extension: function to extend ports (defaults to a straight).
        port1: extension input port name.
        port2: extension output port name.
        port_type: type of the ports to extend.
        centered: if True centers rectangle at (0, 0).
        cross_section: extension cross_section, defaults to port cross_section
            if port has no cross_section it creates one using width and layer.
        extension_port_names: extension port names add to the new component.
        allow_width_mismatch: allow width mismatches.
        auto_taper: if True adds automatic tapers.
        kwargs: cross_section settings.

    Keyword Args:
        layer: port GDS layer.
        prefix: port name prefix.
        orientation: in degrees.
        width: port width.
        layers_excluded: List of layers to exclude.
        port_type: optical, electrical, ....
        clockwise: if True, sort ports clockwise, False: counter-clockwise.
    """
    c = gf.Component()
    component = gf.get_component(component)

    cref = c << component
    if centered:
        cref.x = 0
        cref.y = 0

    ports_all = cref.ports
    port_names_all = [p.name for p in ports_all if p.name is not None]

    ports_to_extend = list(
        gf.port.get_ports_list(cref.ports, port_type=port_type, **kwargs)
    )
    ports_to_extend_names = [p.name for p in ports_to_extend if p.name is not None]
    ports_to_extend_names = cast("list[str]", port_names or ports_to_extend_names)

    if auto_taper and cross_section:
        from gdsfactory.routing.auto_taper import add_auto_tapers

        _ = add_auto_tapers(
            component=c, ports=ports_to_extend, cross_section=cross_section
        )

    for port_name_to_extend in ports_to_extend_names:
        if port_name_to_extend not in port_names_all:
            warnings.warn(
                f"Port Name {port_name_to_extend!r} not in {port_names_all}",
                stacklevel=3,
                category=UserWarning,
            )

    for port in ports_all:
        port_name = port.name
        port = cref.ports[port_name]

        if port_name in ports_to_extend_names:
            if extension:
                extension_component = gf.get_component(extension)
            else:
                if cross_section:
                    cross_section_extension = cross_section
                else:
                    pdk = gf.get_active_pdk()
                    cross_section_names = list(pdk.cross_sections)
                    port_xs_name = port.info.get("cross_section", None)

                    if port_xs_name and port_xs_name in cross_section_names:
                        cross_section_extension = gf.get_cross_section(
                            port.info["cross_section"]
                        )

                    else:
                        cross_section_extension = cross_section_function(
                            layer=gf.get_layer_tuple(port.layer),
                            width=port.width,
                            port_types=(port_type, port_type),
                        )

                extension_component = gf.components.straight(
                    length=length,
                    cross_section=cross_section_extension,
                )
            port_labels = [p.name for p in extension_component.ports]
            port1 = port1 or port_labels[0]
            port2 = port2 or port_labels[-1]

            assert port1 is not None

            extension_ref = c << extension_component
            extension_ref.connect(
                port1, port, allow_width_mismatch=allow_width_mismatch
            )
            c.add_port(port_name, port=extension_ref.ports[port2])
            extension_port_names = extension_port_names or []
            [
                c.add_port(name, port=extension_ref.ports[name])
                for name in extension_port_names
            ]
        else:
            c.add_port(port_name, port=component.ports[port_name])

    c.copy_child_info(component)
    return c

extend_ports

extend_ports_list

extend_ports_list

extend_ports_list(
    component_spec: ComponentSpec,
    extension: ComponentSpec,
    extension_port_name: str | None = None,
    ignore_ports: Strs | None = None,
) -> Component

Returns a component with an extension attached to a list of ports.

Parameters:

Name Type Description Default
component_spec ComponentSpec

component from which to get ports.

required
extension ComponentSpec

function for extension.

required
extension_port_name str | None

to connect extension.

None
ignore_ports Strs | None

list of port names to ignore.

None
Source code in gdsfactory/components/containers/extend_ports_list.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@gf.cell(set_name=False, tags=["containers"])
def extend_ports_list(
    component_spec: ComponentSpec,
    extension: ComponentSpec,
    extension_port_name: str | None = None,
    ignore_ports: Strs | None = None,
) -> Component:
    """Returns a component with an extension attached to a list of ports.

    Args:
        component_spec: component from which to get ports.
        extension: function for extension.
        extension_port_name: to connect extension.
        ignore_ports: list of port names to ignore.
    """
    from gdsfactory.pdk import get_component

    ports = get_component(component_spec).ports

    c = Component()
    extension = get_component(extension)
    c.name = f"{extension.name}_extended_{c.cell_index()}"

    extension_port_name_or_port = extension_port_name or extension.ports[0]
    ignore_ports = ignore_ports or ()

    for i, port in enumerate(ports):
        extension_ref = c << extension
        extension_ref.connect(extension_port_name_or_port, port)

        for ext_port in extension_ref.ports:
            port_name = ext_port.name
            if port_name not in ignore_ports:
                c.add_port(f"{i}_{port_name}", port=ext_port)

    c.auto_rename_ports()
    return c

splitter_chain

splitter_chain

splitter_chain(
    splitter: ComponentSpec = "mmi1x2",
    columns: int = 3,
    bend: ComponentSpec = "bend_s",
) -> Component

Chain of splitters.

Parameters:

Name Type Description Default
splitter ComponentSpec

splitter to chain.

'mmi1x2'
columns int

number of splitters to chain.

3
bend ComponentSpec

bend to connect splitters.

'bend_s'
             __o5
          __|
       __|  |__o4
  o1 _|  |__o3
      |__o2
       __o2
  o1 _|
      |__o3
Source code in gdsfactory/components/containers/splitter_chain.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@gf.cell_with_module_name(tags=["containers"])
def splitter_chain(
    splitter: ComponentSpec = "mmi1x2",
    columns: int = 3,
    bend: ComponentSpec = "bend_s",
) -> Component:
    """Chain of splitters.

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

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

    ```text
           __o2
      o1 _|
          |__o3
    ```
    """
    c = gf.Component()
    splitter_component = gf.get_component(splitter)
    cref = c.add_ref(splitter_component)

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

    bend = gf.get_component(bend)
    c.add_port(name="o1", port=cref.ports["o1"])
    c.add_port(name="o2", port=cref.ports[e0_port_name])

    for i in range(1, columns):
        bref = c.add_ref(bend)
        bref.connect(port="o1", other=cref.ports[e1_port_name])
        cref = c.add_ref(splitter_component)
        cref.connect(port="o1", other=bref.ports["o2"])
        c.add_port(name=f"o{i + 2}", port=cref.ports[e0_port_name])

    c.add_port(name=f"o{i + 3}", port=cref.ports[e1_port_name])
    c.copy_child_info(splitter_component)
    return c

splitter_chain

splitter_tree

Returns a switch_tree.

          __
        _|  |_
  __   | |  |_   _
 |  |__| |__|    |
_|  |__          |dy
 |__|  |  __     |
       |_|  |_   |
         |  |_   -
         |__|

|<-dx->|

splitter_tree

splitter_tree(
    coupler: ComponentSpec = "mmi1x2",
    noutputs: int = 4,
    spacing: Spacing = (90.0, 50.0),
    bend_s: ComponentSpec | None = "bend_s",
    bend_s_xsize: float | None = None,
    cross_section: CrossSectionSpec = "strip",
) -> gf.Component

Tree of power splitters.

Parameters:

Name Type Description Default
coupler ComponentSpec

coupler factory.

'mmi1x2'
noutputs int

number of outputs.

4
spacing Spacing

x, y spacing between couplers.

(90.0, 50.0)
bend_s ComponentSpec | None

Sbend function for termination.

'bend_s'
bend_s_xsize float | None

xsize for the sbend.

None
cross_section CrossSectionSpec

cross_section.

| | |__

'strip'
Source code in gdsfactory/components/containers/splitter_tree.py
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
@gf.cell_with_module_name(tags=["containers"])
def splitter_tree(
    coupler: ComponentSpec = "mmi1x2",
    noutputs: int = 4,
    spacing: Spacing = (90.0, 50.0),
    bend_s: ComponentSpec | None = "bend_s",
    bend_s_xsize: float | None = None,
    cross_section: CrossSectionSpec = "strip",
) -> gf.Component:
    """Tree of power splitters.

    Args:
        coupler: coupler factory.
        noutputs: number of outputs.
        spacing: x, y spacing between couplers.
        bend_s: Sbend function for termination.
        bend_s_xsize: xsize for the sbend.
        cross_section: cross_section.

             __|
          __|  |__
        _|  |__
         |__        dy

          dx
    """
    c = gf.Component()

    dx, dy = spacing

    coupler = gf.get_component(coupler)
    coupler_ports_west = coupler.get_ports_list(
        port_type="optical", orientation=180, sort_ports=True
    )
    coupler_ports_east = coupler.get_ports_list(
        port_type="optical", orientation=0, sort_ports=True
    )

    e1_port_name = coupler_ports_east[0].name
    e0_port_name = coupler_ports_east[1].name
    w0_port_name = coupler_ports_west[0].name

    if bend_s:
        dy_coupler_ports = abs(
            coupler.ports[e0_port_name].center[1]
            - coupler.ports[e1_port_name].center[1]
        )
        bend_s_ysize = dy / 4 - dy_coupler_ports / 2
        bend_s_xsize = bend_s_xsize or dx
        bend_s = gf.get_component(
            bend_s,
            cross_section=cross_section,
            size=(bend_s_xsize, bend_s_ysize),
        )
        # c.info["bend_s"] = bend_s.info
    cols = int(np.log2(noutputs))
    i = 0

    for col in range(cols):
        ncouplers = int(2**col)
        y0 = -0.5 * dy * 2 ** (cols - 1)
        for row in range(ncouplers):
            x = col * dx
            y = y0 + (row + 0.5) * dy * 2 ** (cols - col - 1)
            coupler_ref = c.add_ref(coupler, name=f"coupler_{col}_{row}")
            coupler_ref.move((x, y))
            if col == 0:
                for port in coupler_ref.ports:
                    if port.name not in [e0_port_name, e1_port_name]:
                        c.add_port(name=f"{port.name}_{col}_{i}", port=port)
                        i += 1
            if col > 0:
                port_name = e0_port_name if row % 2 == 0 else e1_port_name
                gf.routing.route_bundle(
                    c,
                    c.insts[f"coupler_{col - 1}_{row // 2}"].ports[port_name],
                    coupler_ref["o1"],
                    cross_section=cross_section,
                )
            if cols > col > 0:
                for port in coupler_ref.ports:
                    if port.name not in [
                        "o1",
                        e0_port_name,
                        e1_port_name,
                        w0_port_name,
                    ]:
                        c.add_port(name=f"{port.name}_{col}_{i}", port=port)
                        i += 1
            if col == cols - 1 and bend_s is None:
                for port in coupler_ref.ports:
                    if port.name in [e1_port_name, e0_port_name]:
                        c.add_port(name=f"{port.name}_{col}_{i}", port=port)
                        i += 1
            if col == cols - 1 and bend_s:
                assert isinstance(bend_s, Component)
                btop = c << bend_s
                bbot = c << bend_s
                bbot.dmirror()
                btop.connect("o1", coupler_ref[e1_port_name])
                bbot.connect("o1", coupler_ref[e0_port_name])
                port = btop.ports["o2"]
                c.add_port(name=f"{port.name}_{col}_{i}", port=port)
                i += 1
                port = bbot.ports["o2"]
                c.add_port(name=f"{port.name}_{col}_{i}", port=port)
                i += 1

    return c

splitter_tree

switch_tree module-attribute

switch_tree = partial(
    splitter_tree, coupler=_mzi1x2_2x2, spacing=(500, 100)
)

switch_tree

couplers

coupler

coupler

coupler(
    gap: float = 0.236,
    length: float = 20.0,
    dy: Delta = 4.0,
    dx: Delta = 10.0,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    bend: ComponentSpec = "bend_s",
) -> Component

Symmetric coupler.

Parameters:

Name Type Description Default
gap float

between straights in um.

0.236
length float

of coupling region in um.

20.0
dy Delta

port to port vertical spacing in um.

4.0
dx Delta

length of bend in x direction in um.

10.0
cross_section CrossSectionSpec

spec (CrossSection, string or dict).

'strip'
allow_min_radius_violation bool

if True does not check for min bend radius.

False
bend ComponentSpec

input and output sbend components.

dx dx |------| |------| o2 __ __o3 \ / | \ length / | ======================= gap | dy / \ | __/ _____ | o1 o4

        coupler_straight  coupler_symmetric
'bend_s'
Source code in gdsfactory/components/couplers/coupler.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler(
    gap: float = 0.236,
    length: float = 20.0,
    dy: Delta = 4.0,
    dx: Delta = 10.0,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
    bend: ComponentSpec = "bend_s",
) -> Component:
    r"""Symmetric coupler.

    Args:
        gap: between straights in um.
        length: of coupling region in um.
        dy: port to port vertical spacing in um.
        dx: length of bend in x direction in um.
        cross_section: spec (CrossSection, string or dict).
        allow_min_radius_violation: if True does not check for min bend radius.
        bend: input and output sbend components.

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

                        coupler_straight  coupler_symmetric
    """
    c = Component()
    sbend = coupler_symmetric(
        gap=gap,
        dy=dy,
        dx=dx,
        cross_section=cross_section,
        bend=bend,
        allow_min_radius_violation=allow_min_radius_violation,
    )

    sr = c << sbend
    sl = c << sbend
    cs = c << coupler_straight(length=length, gap=gap, cross_section=cross_section)
    sl.connect("o2", other=cs.ports["o1"])
    sr.connect("o1", other=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.info["path_length"] = 2 * sbend.info["length"] + length
    c.info["min_bend_radius"] = sbend.info["min_bend_radius"]
    c.auto_rename_ports()

    x = gf.get_cross_section(cross_section)
    x.add_bbox(c)
    c.flatten()
    assert x.radius is not None
    if not allow_min_radius_violation:
        x.validate_radius(x.radius)
    return c

coupler_straight

coupler_straight(
    length: float = 10.0,
    gap: float = 0.27,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Coupler_straight with two parallel straights.

Parameters:

Name Type Description Default
length float

of straight.

10.0
gap float

between straights.

0.27
cross_section CrossSectionSpec

specification (CrossSection, string or dict).

'strip'
Source code in gdsfactory/components/couplers/coupler.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler_straight(
    length: float = 10.0,
    gap: float = 0.27,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Coupler_straight with two parallel straights.

    Args:
        length: of straight.
        gap: between straights.
        cross_section: specification (CrossSection, string or dict).

        o2──────▲─────────o3
                │gap
        o1──────▼─────────o4
    """
    c = Component()
    x = gf.get_cross_section(cross_section)
    _straight = gf.c.straight(length=length, cross_section=cross_section)

    top = c << _straight
    bot = c << _straight

    w = x.width
    y = w + gap

    top.movey(+y)

    if bot.ports and top.ports:
        c.add_port("o1", port=bot.ports[0])
        c.add_port("o2", port=top.ports[0])
        c.add_port("o3", port=bot.ports[1])
        c.add_port("o4", port=top.ports[1])
        c.auto_rename_ports()
    return c

coupler_symmetric

coupler_symmetric(
    bend: ComponentSpec = "bend_s",
    gap: float = 0.234,
    dy: Delta = 4.0,
    dx: Delta = 10.0,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component

Two coupled straights with bends.

Parameters:

Name Type Description Default
bend ComponentSpec

bend spec.

'bend_s'
gap float

in um.

0.234
dy Delta

port to port vertical spacing.

4.0
dx Delta

bend length in x direction.

10.0
cross_section CrossSectionSpec

section.

'strip'
allow_min_radius_violation bool

if True does not check for min bend radius.

       dx
    |-----|
       ___ o3
      /       |

o2 _/ | | o1 ___ | dy \ | ___ | o4

False
Source code in gdsfactory/components/couplers/coupler.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@gf.cell_with_module_name(tags=["couplers"])
def coupler_symmetric(
    bend: ComponentSpec = "bend_s",
    gap: float = 0.234,
    dy: Delta = 4.0,
    dx: Delta = 10.0,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component:
    r"""Two coupled straights with bends.

    Args:
        bend: bend spec.
        gap: in um.
        dy: port to port vertical spacing.
        dx: bend length in x direction.
        cross_section: section.
        allow_min_radius_violation: if True does not check for min bend radius.

                       dx
                    |-----|
                       ___ o3
                      /       |
             o2 _____/        |
                              |
             o1 _____         |  dy
                     \        |
                      \___    |
                           o4

    """
    c = Component()
    x = gf.get_cross_section(cross_section)
    width = x.width
    dy = (dy - gap - width) / 2

    bend_component = gf.get_component(
        bend,
        size=(dx, dy),
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
    )
    top_bend = c << bend_component
    bot_bend = c << bend_component
    bend_ports = top_bend.ports.filter(port_type="optical")
    bend_port1_name = bend_ports[0].name
    bend_port2_name = bend_ports[1].name

    w = bend_component[bend_port1_name].width
    y = w + gap
    y /= 2

    bot_bend.dmirror_y()
    top_bend.movey(+y)
    bot_bend.movey(-y)

    c.add_port("o1", port=bot_bend[bend_port1_name])
    c.add_port("o2", port=top_bend[bend_port1_name])
    c.add_port("o3", port=top_bend[bend_port2_name])
    c.add_port("o4", port=bot_bend[bend_port2_name])

    c.info["length"] = bend_component.info["length"]
    c.info["min_bend_radius"] = bend_component.info["min_bend_radius"]
    return c

coupler

coupler90

coupler90

coupler90(
    gap: float = 0.2,
    radius: float | None = None,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
    cross_section_bend: CrossSectionSpec | None = None,
    length_straight: float | None = None,
) -> Component

Straight coupled to a bend.

Parameters:

Name Type Description Default
gap float

um.

0.2
radius float | None

um.

None
straight ComponentSpec

for straight.

'straight'
bend ComponentSpec

bend spec.

'bend_euler'
cross_section CrossSectionSpec

cross_section spec.

'strip'
cross_section_bend CrossSectionSpec | None

optional bend cross_section spec.

None
length_straight float | None

optional length of the straight waveguide.

None
        o3
         |
        /
       /
   o2_/
   o1___o4
Source code in gdsfactory/components/couplers/coupler90.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler90(
    gap: float = 0.2,
    radius: float | None = None,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
    cross_section_bend: CrossSectionSpec | None = None,
    length_straight: float | None = None,
) -> Component:
    r"""Straight coupled to a bend.

    Args:
        gap: um.
        radius: um.
        straight: for straight.
        bend: bend spec.
        cross_section: cross_section spec.
        cross_section_bend: optional bend cross_section spec.
        length_straight: optional length of the straight waveguide.

    ```text
            o3
             |
            /
           /
       o2_/
       o1___o4
    ```

    """
    c = Component()
    x = gf.get_cross_section(cross_section, radius=radius)
    xs_bend = cross_section_bend or cross_section

    bend90 = gf.get_component(
        bend,
        radius=radius,
        cross_section=xs_bend,
    )
    bend_ref = c << bend90
    bend90_ports = bend_ref.ports.filter(port_type="optical")

    if length_straight is None:
        length_straight = bend90_ports[1].center[0] - bend90_ports[0].center[0]

    straight_component = gf.get_component(
        straight,
        cross_section=cross_section,
        length=length_straight,
    )
    wg_ref = c << straight_component
    width = x.width

    pbw = bend90_ports[0]
    bend_ref.movey(pbw.y + gap + width)
    c.add_ports(wg_ref.ports, prefix="wg")
    c.add_ports(bend_ref.ports, prefix="bend")
    c.auto_rename_ports()
    return c

coupler90

coupler90bend

coupler90bend

coupler90bend(
    radius: float = 10.0,
    gap: float = 0.2,
    bend: ComponentSpec = "bend_euler",
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
) -> Component

Returns 2 coupled bends.

Parameters:

Name Type Description Default
radius float

um.

10.0
gap float

um.

0.2
bend ComponentSpec

for bend.

'bend_euler'
cross_section_inner CrossSectionSpec

spec inner bend.

'strip'
cross_section_outer CrossSectionSpec

spec outer bend.

r 3 4 | | | | / / | / /

'strip'
Source code in gdsfactory/components/couplers/coupler90bend.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler90bend(
    radius: float = 10.0,
    gap: float = 0.2,
    bend: ComponentSpec = "bend_euler",
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
) -> Component:
    r"""Returns 2 coupled bends.

    Args:
        radius: um.
        gap: um.
        bend: for bend.
        cross_section_inner: spec inner bend.
        cross_section_outer: spec outer bend.

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

    """
    c = Component()

    xi = gf.get_cross_section(cross_section_inner)
    xo = gf.get_cross_section(cross_section_outer)

    width = xo.width / 2 + xi.width / 2
    spacing = gap + width

    bend90_inner = gf.get_component(
        bend, radius=radius, cross_section=cross_section_inner
    )
    bend90_outer = gf.get_component(
        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["o1"]
    bend_inner_ref.movey(pbw.center[1] + spacing)

    c.add_port("o1", port=bend_outer_ref["o1"])
    c.add_port("o2", port=bend_inner_ref["o1"])
    c.add_port("o3", port=bend_inner_ref["o2"])
    c.add_port("o4", port=bend_outer_ref["o2"])
    c.flatten()
    return c

coupler90bend

coupler90circular module-attribute

coupler90circular = partial(coupler90, bend="bend_circular")

coupler90circular

coupler_adiabatic

coupler_adiabatic

coupler_adiabatic(
    length1: float = 20.0,
    length2: float = 50.0,
    length3: float = 30.0,
    wg_sep: float = 1.0,
    input_wg_sep: float = 3.0,
    output_wg_sep: float = 3.0,
    dw: float = 0.1,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Returns 50/50 adiabatic coupler.

Design based on asymmetric adiabatic 3dB coupler designs, such as those. - https://doi.org/10.1364/CLEO.2010.CThAA2, - https://doi.org/10.1364/CLEO_SI.2017.SF1I.5 - https://doi.org/10.1364/CLEO_SI.2018.STh4B.4

input Bezier curves, with poles set to half of the x-length of the S-bend. 1. is the first half of input S-bend where input widths taper by +dw and -dw 2. is the second half of the S-bend straight with constant, unbalanced widths 3. is the region where the two asymmetric straights gradually come together 4. straights taper back to the original width at a fixed distance from one another 5. is the output S-bend straight.

Parameters:

Name Type Description Default
length1 float

region that gradually brings the two asymmetric straights together. In this region the straight widths gradually change to be different by dw.

20.0
length2 float

coupling region, where asymmetric straights gradually become the same width.

50.0
length3 float

output region where the two straights separate.

30.0
wg_sep float

Distance between center-to-center in the coupling region (Region 2).

1.0
input_wg_sep float

Separation of the two straights at the input, center-to-center.

3.0
output_wg_sep float

Separation of the two straights at the output, center-to-center.

3.0
dw float

Change in straight width. In Region 1, top arm tapers to width+dw/2.0, bottom taper to width-dw/2.0.

0.1
cross_section CrossSectionSpec

cross_section spec.

'strip'
Source code in gdsfactory/components/couplers/coupler_adiabatic.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler_adiabatic(
    length1: float = 20.0,
    length2: float = 50.0,
    length3: float = 30.0,
    wg_sep: float = 1.0,
    input_wg_sep: float = 3.0,
    output_wg_sep: float = 3.0,
    dw: float = 0.1,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Returns 50/50 adiabatic coupler.

    Design based on asymmetric adiabatic 3dB coupler designs, such as those.
    - https://doi.org/10.1364/CLEO.2010.CThAA2,
    - https://doi.org/10.1364/CLEO_SI.2017.SF1I.5
    - https://doi.org/10.1364/CLEO_SI.2018.STh4B.4

    input Bezier curves, with poles set to half of the x-length of the S-bend.
    1. is the first half of input S-bend where input widths taper by +dw and -dw
    2. is the second half of the S-bend straight with constant, unbalanced widths
    3. is the region where the two asymmetric straights gradually come together
    4. straights taper back to the original width at a fixed distance from one another
    5. is the output S-bend straight.

    Args:
        length1: region that gradually brings the two asymmetric straights together.
            In this region the straight widths gradually change to be different by `dw`.
        length2: coupling region, where asymmetric straights gradually
            become the same width.
        length3: output region where the two straights separate.
        wg_sep: Distance between center-to-center in the coupling region (Region 2).
        input_wg_sep: Separation of the two straights at the input, center-to-center.
        output_wg_sep: Separation of the two straights at the output, center-to-center.
        dw: Change in straight width.
            In Region 1, top arm tapers to width+dw/2.0, bottom taper to width-dw/2.0.
        cross_section: cross_section spec.

    """
    # Control points for input and output S-bends
    control_points_input_top = (
        (0, 0),
        (length1 / 2.0, 0),
        (length1 / 2.0, -input_wg_sep / 2.0 + wg_sep / 2.0),
        (length1, -input_wg_sep / 2.0 + wg_sep / 2.0),
    )

    control_points_input_bottom = (
        (0, -input_wg_sep),
        (length1 / 2.0, -input_wg_sep),
        (length1 / 2.0, -input_wg_sep / 2.0 - wg_sep / 2.0),
        (length1, -input_wg_sep / 2.0 - wg_sep / 2.0),
    )

    control_points_output_top = (
        (length1 + length2, -input_wg_sep / 2.0 + wg_sep / 2.0),
        (
            length1 + length2 + length3 / 2.0,
            -input_wg_sep / 2.0 + wg_sep / 2.0,
        ),
        (
            length1 + length2 + length3 / 2.0,
            -input_wg_sep / 2.0 + output_wg_sep / 2.0,
        ),
        (
            length1 + length2 + length3,
            -input_wg_sep / 2.0 + output_wg_sep / 2.0,
        ),
    )

    c = Component()

    x = gf.get_cross_section(cross_section)
    width = float(x.width)
    width_top = width + dw
    width_bot = width - dw
    x_top = x.copy(width=width_top)
    x_bot = x.copy(width=width_bot)

    coupler = c << gf.components.coupler_straight(length=length2, cross_section=x)

    taper_top = c << gf.components.taper(
        width1=width, width2=width_top, cross_section=cross_section
    )
    taper_bot = c << gf.components.taper(
        width1=width, width2=width_bot, cross_section=cross_section
    )

    taper_bot.connect("o1", coupler.ports["o1"])
    taper_top.connect("o1", coupler.ports["o2"])

    sbend_left_top = c << bezier(
        control_points=control_points_input_top, cross_section=x_top
    )
    sbend_left_bot = c << bezier(
        control_points=control_points_input_bottom, cross_section=x_bot
    )

    sbend_left_top.connect("o2", taper_top.ports["o2"])
    sbend_left_bot.connect("o2", taper_bot.ports["o2"])

    sbend_right = bezier(control_points=control_points_output_top, cross_section=x)
    sbend_right_top = c << sbend_right
    sbend_right_bot = c << sbend_right

    sbend_right_top.connect("o1", coupler.ports["o3"])
    sbend_right_bot.connect("o1", coupler.ports["o4"], mirror=True)

    c.add_port("o1", port=sbend_left_bot.ports["o1"])
    c.add_port("o2", port=sbend_left_top.ports["o1"])
    c.add_port("o3", port=sbend_right_top.ports["o2"])
    c.add_port("o4", port=sbend_right_bot.ports["o2"])
    c.flatten()
    return c

coupler_adiabatic

coupler_asymmetric

coupler_asymmetric

coupler_asymmetric(
    gap: float = 0.234,
    dy: Delta = 2.5,
    dx: Delta = 10.0,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Bend coupled to straight waveguide.

Parameters:

Name Type Description Default
gap float

um.

0.234
dy Delta

port to port vertical spacing.

2.5
dx Delta

bend length in x direction.

10.0
cross_section CrossSectionSpec

spec.

        dx
     |-----|
      _____ o2
     /         |

_/ | gap o1____ | dy o3

'strip'
Source code in gdsfactory/components/couplers/coupler_asymmetric.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@gf.cell_with_module_name(schematic_function=mmi_1x2_schematic, tags=["couplers"])
def coupler_asymmetric(
    gap: float = 0.234,
    dy: Delta = 2.5,
    dx: Delta = 10.0,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Bend coupled to straight waveguide.

    Args:
        gap: um.
        dy: port to port vertical spacing.
        dx: bend length in x direction.
        cross_section: spec.

                        dx
                     |-----|
                      _____ o2
                     /         |
               _____/          |
         gap o1____________    |  dy
                            o3
    """
    c = Component()
    x = gf.get_cross_section(cross_section)
    width = x.width
    bend = gf.c.bend_s(size=(dx, dy - gap - width), cross_section=cross_section)
    wg = gf.c.straight(cross_section=cross_section)

    w = bend.ports[0].width
    y = (w + gap) / 2

    wg_ref = c << wg
    bend_ref = c << bend
    bend_ref.dmirror_y()
    bend_ref.xmin = 0
    wg_ref.xmin = 0

    bend_ref.movey(-y)
    wg_ref.movey(+y)

    port_width = 2 * w + gap
    c.add_port(
        name="o1",
        center=(0, 0),
        width=port_width,
        orientation=180,
        cross_section=x,
    )
    c.add_port(name="o3", port=bend_ref.ports[1])
    c.add_port(name="o2", port=wg_ref.ports[0])
    c.flatten()
    return c

coupler_asymmetric

coupler_bent

coupler_bent

coupler_bent(
    gap: float = 0.2,
    radius: float = 26,
    length: float = 8.6,
    width1: float = 0.4,
    width2: float = 0.4,
    length_straight: float = 10,
    cross_section: str = "strip",
) -> gf.Component

Returns Broadband SOI curved / straight directional coupler.

based on: https://doi.org/10.1038/s41598-017-07618-6.

Parameters:

Name Type Description Default
gap float

gap.

0.2
radius float

radius coupling.

26
length float

coupler_length.

8.6
width1 float

width1.

0.4
width2 float

width2.

0.4
length_straight float

input and output straight length.

10
cross_section str

cross_section.

'strip'
Source code in gdsfactory/components/couplers/coupler_bent.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler_bent(
    gap: float = 0.200,
    radius: float = 26,
    length: float = 8.6,
    width1: float = 0.400,
    width2: float = 0.400,
    length_straight: float = 10,
    cross_section: str = "strip",
) -> gf.Component:
    """Returns Broadband SOI curved / straight directional coupler.

    based on: https://doi.org/10.1038/s41598-017-07618-6.

    Args:
        gap: gap.
        radius: radius coupling.
        length: coupler_length.
        width1: width1.
        width2: width2.
        length_straight: input and output straight length.
        cross_section: cross_section.
    """
    c = gf.Component()

    right_half = c << coupler_bent_half(
        gap=gap,
        radius=radius,
        length=length,
        width1=width1,
        width2=width2,
        length_straight=length_straight,
        cross_section=cross_section,
    )
    left_half = c << coupler_bent_half(
        gap=gap,
        radius=radius,
        length=length,
        width1=width1,
        width2=width2,
        length_straight=length_straight,
        cross_section=cross_section,
    )

    left_half.connect(port="o1", other=right_half.ports["o1"], mirror=True)

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

    c.flatten()
    return c

coupler_bent_half

coupler_bent_half(
    gap: float = 0.2,
    radius: float = 26,
    length: float = 8.6,
    width1: float = 0.4,
    width2: float = 0.4,
    length_straight: float = 10,
    length_straight_exit: float = 18,
    cross_section: str = "strip",
) -> gf.Component

Returns Broadband SOI curved / straight directional coupler.

Parameters:

Name Type Description Default
gap float

gap.

0.2
radius float

radius coupling.

26
length float

coupler_length.

8.6
width1 float

width1.

0.4
width2 float

width2.

0.4
length_straight float

input and output straight length.

10
length_straight_exit float

length straight exit.

18
cross_section str

cross_section.

'strip'
Source code in gdsfactory/components/couplers/coupler_bent.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
@gf.cell_with_module_name(tags=["couplers"])
def coupler_bent_half(
    gap: float = 0.200,
    radius: float = 26,
    length: float = 8.6,
    width1: float = 0.400,
    width2: float = 0.400,
    length_straight: float = 10,
    length_straight_exit: float = 18,
    cross_section: str = "strip",
) -> gf.Component:
    """Returns Broadband SOI curved / straight directional coupler.

    Args:
        gap: gap.
        radius: radius coupling.
        length: coupler_length.
        width1: width1.
        width2: width2.
        length_straight: input and output straight length.
        length_straight_exit: length straight exit.
        cross_section: cross_section.
    """
    radius_outer = radius + (width1 + gap) / 2
    radius_inner = radius - (width2 + gap) / 2
    alpha = round(np.rad2deg(length / (2 * radius)), 4)
    beta = alpha

    c = gf.Component()

    xs = gf.get_cross_section(cross_section)
    xs1 = xs.copy(radius=radius_outer, width=width1)
    xs2 = xs.copy(radius=radius_inner, width=width2)

    outer_bend = gf.path.arc(angle=-alpha, radius=radius_outer)
    inner_bend = gf.path.arc(angle=-alpha, radius=radius_inner)

    outer_straight = gf.path.straight(length=length, npoints=100)
    inner_straight = gf.path.straight(length=length, npoints=100)

    outer_exit_bend = gf.path.arc(angle=alpha, radius=radius_outer)
    inner_exit_bend_down = gf.path.arc(angle=-beta, radius=radius_inner)
    inner_exit_bend_up = gf.path.arc(angle=alpha + beta, radius=radius_inner)

    inner_exit_straight = gf.path.straight(
        length=length_straight,
        npoints=100,
    )
    outer_exit_straight = gf.path.straight(
        length=length_straight_exit,
        npoints=100,
    )

    outer = outer_bend + outer_straight + outer_exit_bend + outer_exit_straight
    inner = (
        inner_bend
        + inner_straight
        + inner_exit_bend_down
        + inner_exit_bend_up
        + inner_exit_straight
    )

    inner_component = c << inner.extrude(xs2)
    outer_component = c << outer.extrude(xs1)
    outer_component.movey(+(width1 + gap) / 2)
    inner_component.movey(-(width2 + gap) / 2)

    c.add_port("o1", port=outer_component.ports["o1"])
    c.add_port("o2", port=inner_component.ports["o1"])
    c.add_port("o3", port=outer_component.ports["o2"])
    c.add_port("o4", port=inner_component.ports["o2"])
    c.flatten()
    return c

coupler_bent

coupler_broadband

coupler_broadband

coupler_broadband(
    w_sc: float = 0.5,
    gap_sc: float = 0.2,
    w_top: float = 0.6,
    gap_pc: float = 0.3,
    legnth_taper: float = 1.0,
    bend: ComponentSpec = "bend_euler",
    coupler_straight: ComponentSpec = "coupler_straight",
    length_coupler_straight: float = 12.4,
    lenght_coupler_big_gap: float = 4.7,
    cross_section: CrossSectionSpec = "strip",
    radius: float = 10.0,
) -> Component

Returns broadband coupler component.

https://docs.flexcompute.com/projects/tidy3d/en/latest/notebooks/BroadbandDirectionalCoupler.html proposed in Zeqin Lu, Han Yun, Yun Wang, Zhitian Chen, Fan Zhang, Nicolas A. F. Jaeger, and Lukas Chrostowski, "Broadband silicon photonic directional coupler using asymmetric-waveguide based phase control," Opt. Express 23, 3795-3808 (2015), DOI: 10.1364/OE.23.003795.

Parameters:

Name Type Description Default
w_sc float

width of waveguides in the symmetric coupler section.

0.5
gap_sc float

gap size between the waveguides in the symmetric coupler section.

0.2
w_top float

width of the top waveguide in the phase control section.

0.6
gap_pc float

gap size in the phase control section.

0.3
legnth_taper float

length of the tapers.

1.0
bend ComponentSpec

bend factory.

'bend_euler'
coupler_straight ComponentSpec

coupler_straight factory.

'coupler_straight'
length_coupler_straight float

optimal L_1 from the 3d fdtd analysis.

12.4
lenght_coupler_big_gap float

optimal L_2 from the 3d fdtd analysis.

4.7
cross_section CrossSectionSpec

cross_section of the waveguides.

'strip'
radius float

bend radius.

10.0
Source code in gdsfactory/components/couplers/coupler_broadband.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler_broadband(
    w_sc: float = 0.5,  # width of waveguides in the symmetric coupler section
    gap_sc: float = 0.2,  # gap size between the waveguides in the symmetric coupler section
    w_top: float = 0.6,  # width of the top waveguide in the phase control section
    gap_pc: float = 0.3,  # gap size in the phase control section
    legnth_taper: float = 1.0,  # length of the tapers
    bend: ComponentSpec = "bend_euler",
    coupler_straight: ComponentSpec = "coupler_straight",
    length_coupler_straight: float = 12.4,  # optimal L_1 from the 3d fdtd analysis
    lenght_coupler_big_gap: float = 4.7,  # optimal L_2 from the 3d fdtd analysis
    cross_section: CrossSectionSpec = "strip",
    radius: float = 10.0,
) -> Component:
    """Returns broadband coupler component.

    https://docs.flexcompute.com/projects/tidy3d/en/latest/notebooks/BroadbandDirectionalCoupler.html
    proposed in Zeqin Lu, Han Yun, Yun Wang, Zhitian Chen, Fan Zhang, Nicolas A. F. Jaeger, and Lukas Chrostowski,
    "Broadband silicon photonic directional coupler using asymmetric-waveguide based phase control,"
    Opt. Express 23, 3795-3808 (2015), DOI: 10.1364/OE.23.003795.

    Args:
        w_sc: width of waveguides in the symmetric coupler section.
        gap_sc: gap size between the waveguides in the symmetric coupler section.
        w_top: width of the top waveguide in the phase control section.
        gap_pc: gap size in the phase control section.
        legnth_taper: length of the tapers.
        bend: bend factory.
        coupler_straight: coupler_straight factory.
        length_coupler_straight: optimal L_1 from the 3d fdtd analysis.
        lenght_coupler_big_gap: optimal L_2 from the 3d fdtd analysis.
        cross_section: cross_section of the waveguides.
        radius: bend radius.
    """
    c = gf.Component()

    xs = gf.get_cross_section(cross_section)
    assert xs.layer is not None
    layer = gf.get_layer(xs.layer)

    L_t = legnth_taper
    c = Component()
    L_2 = lenght_coupler_big_gap
    L_1 = length_coupler_straight

    y_coupler = -w_sc + xs.width / 2 + gap_pc / 2

    coupler = gf.get_component(
        coupler_straight, length=L_1, cross_section=cross_section, gap=gap_sc
    )
    coupler1 = c << coupler
    coupler1.xmin = -L_2 / 2 - L_t - L_1
    coupler1.y = y_coupler

    _bend = gf.get_component(bend, radius=radius, cross_section=cross_section)
    bend_lt = c << _bend
    bend_lb = c << _bend

    bend_lb.connect("o1", coupler1.ports["o1"])
    bend_lt.connect("o1", coupler1.ports["o2"], mirror=True)

    vertices_top = [
        (L_2 / 2 + L_t, 0),
        (L_2 / 2 + L_t, w_sc),
        (L_2 / 2 + L_t, w_sc),
        (L_2 / 2, w_top),
        (-L_2 / 2, w_top),
        (-L_2 / 2 - L_t, w_sc),
        (-L_2 / 2 - L_t, w_sc),
        (-L_2 / 2 - L_t, 0),
    ]

    c.add_polygon(vertices_top, layer=layer)

    # define vertices of the bottom waveguide
    vertices_bot = [
        (L_2 / 2 + L_t, -gap_sc - w_sc),
        (L_2 / 2 + L_t, -gap_sc),
        (L_2 / 2 + L_t, -gap_sc),
        (L_2 / 2, -gap_pc),
        (-L_2 / 2, -gap_pc),
        (-L_2 / 2 - L_t, -gap_sc),
        (-L_2 / 2 - L_t, -gap_sc),
        (-L_2 / 2 - L_t, -gap_sc - w_sc),
    ]
    c.add_polygon(vertices_bot, layer=layer)

    for section in xs.sections[1:]:
        w = section.width / 2
        layer_ = section.layer
        assert layer_ is not None
        vertices_top = [
            (L_2 / 2 + L_t, -w),
            (L_2 / 2 + L_t, w),
            (L_2 / 2 + L_t, w),
            (L_2 / 2, w_top + w),
            (-L_2 / 2, w_top + w),
            (-L_2 / 2 - L_t, w),
            (-L_2 / 2 - L_t, w),
            (-L_2 / 2 - L_t, -w),
        ]

        c.add_polygon(vertices_top, layer=layer_)

        # define vertices of the bottom waveguide
        vertices_bot = [
            (L_2 / 2 + L_t, -gap_sc - w),
            (L_2 / 2 + L_t, -gap_sc + w),
            (L_2 / 2 + L_t, -gap_sc + w),
            (L_2 / 2, -gap_pc + w),
            (-L_2 / 2, -gap_pc + w),
            (-L_2 / 2 - L_t, -gap_sc + w),
            (-L_2 / 2 - L_t, -gap_sc + w),
            (-L_2 / 2 - L_t, -gap_sc - w),
        ]
        c.add_polygon(vertices_bot, layer=layer_)

    coupler2 = c << coupler
    coupler2.xmax = L_2 / 2 + L_t + L_1
    coupler2.y = y_coupler

    _bend = gf.get_component(bend, radius=radius, cross_section=cross_section)
    bend_rt = c << _bend
    bend_rb = c << _bend

    bend_rb.connect("o1", coupler2.ports["o3"])
    bend_rt.connect("o1", coupler2.ports["o4"], mirror=True)

    c.add_port("o1", port=bend_lb.ports["o2"])
    c.add_port("o2", port=bend_lt.ports["o2"])
    c.add_port("o3", port=bend_rt.ports["o2"])
    c.add_port("o4", port=bend_rb.ports["o2"])
    return c

coupler_broadband

coupler_full

coupler_full

coupler_full(
    coupling_length: float = 40.0,
    dx: Delta = 10.0,
    dy: Delta = 4.8,
    gap: float = 0.5,
    dw: float = 0.1,
    cross_section: CrossSectionSpec = "strip",
    width: float | None = None,
) -> Component

Adiabatic Full coupler.

Design based on asymmetric adiabatic full coupler designs, such as the one reported in 'Integrated Optic Adiabatic Devices on Silicon' by Y. Shani, et al (IEEE Journal of Quantum Electronics, Vol. 27, No. 3 March 1991).

  1. is the first half of the input S-bend straight where the input straights widths taper by +dw and -dw,
  2. is the second half of the S-bend straight with constant, unbalanced widths,
  3. is the coupling region where the straights from unbalanced widths to balanced widths to reverse polarity unbalanced widths,
  4. is the fixed width straight that curves away from the coupling region, 5.is the final curve where the straights taper back to the regular width specified in the straight template.

Parameters:

Name Type Description Default
coupling_length float

Length of the coupling region in um.

40.0
dx Delta

Length of the bend regions in um.

10.0
dy Delta

Port-to-port distance between the bend regions in um.

4.8
gap float

Distance between the two straights in um.

0.5
dw float

delta width. Top arm tapers to width - dw, bottom to width + dw in um.

0.1
cross_section CrossSectionSpec

cross-section spec.

'strip'
width float | None

width of the waveguide. If None, it will use the width of the cross_section.

None
Source code in gdsfactory/components/couplers/coupler_full.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler_full(
    coupling_length: float = 40.0,
    dx: Delta = 10.0,
    dy: Delta = 4.8,
    gap: float = 0.5,
    dw: float = 0.1,
    cross_section: CrossSectionSpec = "strip",
    width: float | None = None,
) -> Component:
    """Adiabatic Full coupler.

    Design based on asymmetric adiabatic full
    coupler designs, such as the one reported in 'Integrated Optic Adiabatic
    Devices on Silicon' by Y. Shani, et al (IEEE Journal of Quantum
    Electronics, Vol. 27, No. 3 March 1991).

    1. is the first half of the input S-bend straight where the
    input straights widths taper by +dw and -dw,
    2. is the second half of the S-bend straight with constant,
    unbalanced widths,
    3. is the coupling region where the straights from unbalanced widths to
    balanced widths to reverse polarity unbalanced widths,
    4. is the fixed width straight that curves away from the coupling region,
    5.is the final curve where the straights taper back to the regular width
    specified in the straight template.

    Args:
        coupling_length: Length of the coupling region in um.
        dx: Length of the bend regions in um.
        dy: Port-to-port distance between the bend regions in um.
        gap: Distance between the two straights in um.
        dw: delta width. Top arm tapers to width - dw, bottom to width + dw in um.
        cross_section: cross-section spec.
        width: width of the waveguide. If None, it will use the width of the cross_section.

    """
    c = gf.Component()

    if width:
        x = gf.get_cross_section(cross_section=cross_section, width=width)
    else:
        x = gf.get_cross_section(cross_section=cross_section)
    x_top = x.copy(width=x.width + dw)
    x_bottom = x.copy(width=x.width - dw)

    taper_top = c << gf.components.taper(
        length=coupling_length,
        width1=x_top.width,
        width2=x_bottom.width,
        cross_section=cross_section,
    )

    taper_bottom = c << gf.components.taper(
        length=coupling_length,
        width1=x_bottom.width,
        width2=x_top.width,
        cross_section=cross_section,
    )

    bend_input_top = c << gf.c.bend_s(
        size=(dx, (dy - gap - x_top.width) / 2.0), cross_section=x_top
    )
    bend_input_top.movey((x_top.width + gap) / 2.0)

    bend_input_bottom = c << gf.c.bend_s(
        size=(dx, (-dy + gap + x_bottom.width) / 2.0), cross_section=x_bottom
    )
    bend_input_bottom.movey(-(x_bottom.width + gap) / 2.0)

    taper_top.connect("o1", bend_input_top.ports["o1"])
    taper_bottom.connect("o1", bend_input_bottom.ports["o1"])

    bend_output_top = c << gf.c.bend_s(
        size=(dx, (dy - gap - x_top.width) / 2.0), cross_section=x_bottom
    )

    bend_output_bottom = c << gf.c.bend_s(
        size=(dx, (-dy + gap + x_bottom.width) / 2.0), cross_section=x_top
    )

    bend_output_top.connect("o2", taper_top.ports["o2"], mirror=True)
    bend_output_bottom.connect("o2", taper_bottom.ports["o2"], mirror=True)

    x.add_bbox(c)

    c.add_port("o1", port=bend_input_bottom.ports["o2"])
    c.add_port("o2", port=bend_input_top.ports["o2"])
    c.add_port("o3", port=bend_output_top.ports["o1"])
    c.add_port("o4", port=bend_output_bottom.ports["o1"])
    c.auto_rename_ports()

    c.flatten()
    return c

coupler_full

coupler_pulley

coupler_pulley

coupler_pulley(
    radius: float = 10.0,
    ring_width: float | None = None,
    gap: float = 0.2,
    coupling_angle: float = 60.0,
    wg_length: float = 40.0,
    wg_height: float = 10.0,
    n_segments: int = 128,
    cross_section: CrossSectionSpec = "strip",
    layer: LayerSpec = "WG",
) -> Component

Returns a disc or ring with a pulley-coupled waveguide.

A waveguide wraps symmetrically around the top of a disc/ring over coupling_angle degrees. Bezier S-curves (following the CNST discPulley construction, eq. 2.22) route the waveguide from the coupling arc down to horizontal exits on both sides.

Parameters:

Name Type Description Default
radius float

radius of the disc, or outer radius of the ring.

10.0
ring_width float | None

width of the ring annulus. None for a solid disc.

None
gap float

gap between the waveguide inner edge and the disc/ring.

0.2
coupling_angle float

total wrap angle in degrees (symmetric about top).

60.0
wg_length float

horizontal half-length of the waveguide from the disc center to the exit end. Controls S-curve extent. Corresponds to CNST parameter L.

40.0
wg_height float

vertical drop from disc center to exit waveguide level. Corresponds to CNST parameter H.

10.0
n_segments int

number of points for each curved section.

128
cross_section CrossSectionSpec

cross-section spec for the coupling waveguide.

'strip'
layer LayerSpec

layer spec for the disc/ring.

'WG'
Source code in gdsfactory/components/couplers/coupler_pulley.py
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@gf.cell_with_module_name(tags=["couplers"])
def coupler_pulley(
    radius: float = 10.0,
    ring_width: float | None = None,
    gap: float = 0.2,
    coupling_angle: float = 60.0,
    wg_length: float = 40.0,
    wg_height: float = 10.0,
    n_segments: int = 128,
    cross_section: CrossSectionSpec = "strip",
    layer: LayerSpec = "WG",
) -> Component:
    """Returns a disc or ring with a pulley-coupled waveguide.

    A waveguide wraps symmetrically around the top of a disc/ring
    over coupling_angle degrees. Bezier S-curves (following the CNST
    discPulley construction, eq. 2.22) route the waveguide from the
    coupling arc down to horizontal exits on both sides.

    Args:
        radius: radius of the disc, or outer radius of the ring.
        ring_width: width of the ring annulus. None for a solid disc.
        gap: gap between the waveguide inner edge and the disc/ring.
        coupling_angle: total wrap angle in degrees (symmetric about top).
        wg_length: horizontal half-length of the waveguide from the disc
            center to the exit end. Controls S-curve extent. Corresponds
            to CNST parameter L.
        wg_height: vertical drop from disc center to exit waveguide level.
            Corresponds to CNST parameter H.
        n_segments: number of points for each curved section.
        cross_section: cross-section spec for the coupling waveguide.
        layer: layer spec for the disc/ring.
    """
    c = Component()
    xs = gf.get_cross_section(cross_section)
    waveguide_width = xs.width

    half_angle = np.radians(coupling_angle / 2)
    wg_r = radius + gap + waveguide_width / 2
    h_exit = -wg_height

    # 1. Disc or ring
    theta_full = np.linspace(0, 2 * np.pi, n_segments * 2, endpoint=False)
    if ring_width is not None:
        inner_r = radius - ring_width
        outer_pts = np.column_stack(
            [radius * np.cos(theta_full), radius * np.sin(theta_full)]
        )
        inner_pts = np.column_stack(
            [inner_r * np.cos(theta_full[::-1]), inner_r * np.sin(theta_full[::-1])]
        )
        c.add_polygon(np.vstack([outer_pts, inner_pts]), layer=layer)
    else:
        c.add_polygon(
            np.column_stack([radius * np.cos(theta_full), radius * np.sin(theta_full)]),
            layer=layer,
        )

    # 2. Coupling arc (wraps around the top of the disc/ring)
    theta_arc = np.linspace(np.pi / 2 + half_angle, np.pi / 2 - half_angle, n_segments)
    arc_center = np.column_stack([wg_r * np.cos(theta_arc), wg_r * np.sin(theta_arc)])

    # 3. Bezier S-curves from coupling arc endpoints to horizontal exits
    # Following CNST eq. 2.22:
    #   P1 = arc endpoint, P2 = (±L, -H)
    #   C1 = P1 + R/4 * tangent_at_P1
    #   C2 = (P2x ∓ L/2, P2y)
    #   R = sqrt(H² + L²)

    L = wg_length
    H_param = wg_height + wg_r * np.sin(
        half_angle
    )  # total vertical drop from arc endpoint to exit
    R_bezier = np.sqrt(H_param**2 + L**2)

    bezier_paths = {}
    for name, arc_angle, x_sign in [
        ("left", np.pi / 2 + half_angle, -1),
        ("right", np.pi / 2 - half_angle, 1),
    ]:
        p1 = np.array([wg_r * np.cos(arc_angle), wg_r * np.sin(arc_angle)])
        p2 = np.array([x_sign * L, h_exit])
        tangent = np.array([x_sign * np.sin(arc_angle), -x_sign * np.cos(arc_angle)])
        c1 = p1 + (R_bezier / 4) * tangent
        c2 = np.array([p2[0] - x_sign * L / 2, p2[1]])

        bezier = _cubic_bezier(tuple(p1), tuple(c1), tuple(c2), tuple(p2), n_segments)
        bezier_paths[name] = bezier

    # 4. Assemble full center-line and extrude as path
    # Skip first point of each segment to avoid duplicates at junctions
    left_exit = np.array([[-L - 5, h_exit], [-L, h_exit]])
    right_exit = np.array([[L, h_exit], [L + 5, h_exit]])

    center_line = np.vstack(
        [
            left_exit,
            bezier_paths["left"][::-1][1:],
            arc_center[1:],
            bezier_paths["right"][1:],
            right_exit[1:],
        ]
    )

    wg_path = gf.Path(center_line)
    wg_path.start_angle = 0.0
    wg_path.end_angle = 0.0
    wg_ref = c << wg_path.extrude(xs)

    c.add_port("o1", port=wg_ref.ports["o2"])
    c.add_port("o2", port=wg_ref.ports["o1"])
    c.flatten()
    c.auto_rename_ports()
    return c

coupler_pulley

coupler_ring

coupler_ring

coupler_ring(
    gap: float = 0.2,
    radius: float | None = None,
    length_x: float = 4.0,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
    cross_section_bend: CrossSectionSpec | None = None,
    length_extension: float | None = None,
) -> Component

Coupler for ring.

Parameters:

Name Type Description Default
gap float

spacing between parallel coupled straight waveguides.

0.2
radius float | None

of the bends. Default is None, which uses the default radius of the cross_section.

None
length_x float

length of the parallel coupled straight waveguides.

4.0
bend ComponentSpec

90 degrees bend spec.

'bend_euler'
straight ComponentSpec

straight spec.

'straight'
cross_section CrossSectionSpec

cross_section spec.

'strip'
cross_section_bend CrossSectionSpec | None

optional bend cross_section spec.

None
length_extension float | None

straight length extension at the end of the coupler bottom ports.

o2 o3 xx xx xx xx xx length_x x xx ◄───────────────► x xx xxx xx xxx xxx──────▲─────────xxx │gap o1──────▼─────────◄──────────────► o4 length_extension

None
Source code in gdsfactory/components/couplers/coupler_ring.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@gf.cell_with_module_name(schematic_function=coupler_ring_schematic, tags=["couplers"])
def coupler_ring(
    gap: float = 0.2,
    radius: float | None = None,
    length_x: float = 4.0,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
    cross_section_bend: CrossSectionSpec | None = None,
    length_extension: float | None = None,
) -> Component:
    r"""Coupler for ring.

    Args:
        gap: spacing between parallel coupled straight waveguides.
        radius: of the bends. Default is None, which uses the default radius of the cross_section.
        length_x: length of the parallel coupled straight waveguides.
        bend: 90 degrees bend spec.
        straight: straight spec.
        cross_section: cross_section spec.
        cross_section_bend: optional bend cross_section spec.
        length_extension: straight length extension at the end of the coupler bottom ports.

          o2                              o3
          xx                              xx
          xx                             xx
           xx          length_x          x
            xx     ◄───────────────►    x
             xx                       xxx
               xx                   xxx
                xxx──────▲─────────xxx
                         │gap
                 o1──────▼─────────◄──────────────► o4
                                    length_extension
    """
    if radius is None:
        radius = gf.get_cross_section(cross_section).radius
        assert radius is not None, "cross_section must have a radius"

    if length_extension is None:
        length_extension = 3.0 + radius

    c = Component()
    gap = gf.snap.snap_to_grid(gap, grid_factor=2)
    cross_section_bend = cross_section_bend or cross_section

    # define subcells
    coupler90_component = gf.get_component(
        coupler90,
        gap=gap,
        radius=radius,
        bend=bend,
        straight=straight,
        cross_section=cross_section,
        cross_section_bend=cross_section_bend,
        length_straight=length_extension,
    )
    coupler_straight_component = gf.get_component(
        coupler_straight,
        gap=gap,
        length=length_x,
        cross_section=cross_section,
    )

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

    # connect references
    cs.connect(port="o4", other=cbr.ports["o1"])
    cbl.connect(port="o2", other=cs.ports["o2"], mirror=True)

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

    c.add_ports(
        gf.port.select_ports_list(ports=cbl.ports, port_type="electrical"), prefix="cbl"
    )
    c.add_ports(
        gf.port.select_ports_list(ports=cbr.ports, port_type="electrical"), prefix="cbr"
    )

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    c.auto_rename_ports()
    c.flatten()
    c.info["radius"] = radius
    return c

coupler_ring

coupler_straight

coupler_straight(
    length: float = 10.0,
    gap: float = 0.27,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Coupler_straight with two parallel straights.

Parameters:

Name Type Description Default
length float

of straight.

10.0
gap float

between straights.

0.27
cross_section CrossSectionSpec

specification (CrossSection, string or dict).

'strip'
Source code in gdsfactory/components/couplers/coupler.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler_straight(
    length: float = 10.0,
    gap: float = 0.27,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Coupler_straight with two parallel straights.

    Args:
        length: of straight.
        gap: between straights.
        cross_section: specification (CrossSection, string or dict).

        o2──────▲─────────o3
                │gap
        o1──────▼─────────o4
    """
    c = Component()
    x = gf.get_cross_section(cross_section)
    _straight = gf.c.straight(length=length, cross_section=cross_section)

    top = c << _straight
    bot = c << _straight

    w = x.width
    y = w + gap

    top.movey(+y)

    if bot.ports and top.ports:
        c.add_port("o1", port=bot.ports[0])
        c.add_port("o2", port=top.ports[0])
        c.add_port("o3", port=bot.ports[1])
        c.add_port("o4", port=top.ports[1])
        c.auto_rename_ports()
    return c

coupler_straight

coupler_straight_asymmetric

coupler_straight_asymmetric

coupler_straight_asymmetric(
    length: float = 10.0,
    gap: float = 0.27,
    width_top: float = 0.5,
    width_bot: float = 1,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Coupler with two parallel straights of different widths.

Parameters:

Name Type Description Default
length float

of straight.

10.0
gap float

between straights.

0.27
width_top float

of top straight.

0.5
width_bot float

of bottom straight.

1
cross_section CrossSectionSpec

cross_section spec.

'strip'
Source code in gdsfactory/components/couplers/coupler_straight_asymmetric.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["couplers"])
def coupler_straight_asymmetric(
    length: float = 10.0,
    gap: float = 0.27,
    width_top: float = 0.5,
    width_bot: float = 1,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Coupler with two parallel straights of different widths.

    Args:
        length: of straight.
        gap: between straights.
        width_top: of top straight.
        width_bot: of bottom straight.
        cross_section: cross_section spec.
    """
    c = Component()

    xs_top = gf.get_cross_section(cross_section, width=width_top)
    xs_bot = gf.get_cross_section(cross_section, width=width_bot)

    top = c << gf.c.straight(length=length, cross_section=xs_top)
    bot = c << gf.c.straight(length=length, cross_section=xs_bot)

    dy = 0.5 * (width_top + width_bot) + gap
    top.movey(dy)
    c.add_port("o1", port=bot.ports[0])
    c.add_port("o2", port=top.ports[0])
    c.add_port("o3", port=top.ports[1])
    c.add_port("o4", port=bot.ports[1])
    c.flatten()
    return c

coupler_straight_asymmetric

coupler_symmetric

coupler_symmetric(
    bend: ComponentSpec = "bend_s",
    gap: float = 0.234,
    dy: Delta = 4.0,
    dx: Delta = 10.0,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component

Two coupled straights with bends.

Parameters:

Name Type Description Default
bend ComponentSpec

bend spec.

'bend_s'
gap float

in um.

0.234
dy Delta

port to port vertical spacing.

4.0
dx Delta

bend length in x direction.

10.0
cross_section CrossSectionSpec

section.

'strip'
allow_min_radius_violation bool

if True does not check for min bend radius.

       dx
    |-----|
       ___ o3
      /       |

o2 _/ | | o1 ___ | dy \ | ___ | o4

False
Source code in gdsfactory/components/couplers/coupler.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@gf.cell_with_module_name(tags=["couplers"])
def coupler_symmetric(
    bend: ComponentSpec = "bend_s",
    gap: float = 0.234,
    dy: Delta = 4.0,
    dx: Delta = 10.0,
    cross_section: CrossSectionSpec = "strip",
    allow_min_radius_violation: bool = False,
) -> Component:
    r"""Two coupled straights with bends.

    Args:
        bend: bend spec.
        gap: in um.
        dy: port to port vertical spacing.
        dx: bend length in x direction.
        cross_section: section.
        allow_min_radius_violation: if True does not check for min bend radius.

                       dx
                    |-----|
                       ___ o3
                      /       |
             o2 _____/        |
                              |
             o1 _____         |  dy
                     \        |
                      \___    |
                           o4

    """
    c = Component()
    x = gf.get_cross_section(cross_section)
    width = x.width
    dy = (dy - gap - width) / 2

    bend_component = gf.get_component(
        bend,
        size=(dx, dy),
        cross_section=cross_section,
        allow_min_radius_violation=allow_min_radius_violation,
    )
    top_bend = c << bend_component
    bot_bend = c << bend_component
    bend_ports = top_bend.ports.filter(port_type="optical")
    bend_port1_name = bend_ports[0].name
    bend_port2_name = bend_ports[1].name

    w = bend_component[bend_port1_name].width
    y = w + gap
    y /= 2

    bot_bend.dmirror_y()
    top_bend.movey(+y)
    bot_bend.movey(-y)

    c.add_port("o1", port=bot_bend[bend_port1_name])
    c.add_port("o2", port=top_bend[bend_port1_name])
    c.add_port("o3", port=top_bend[bend_port2_name])
    c.add_port("o4", port=bot_bend[bend_port2_name])

    c.info["length"] = bend_component.info["length"]
    c.info["min_bend_radius"] = bend_component.info["min_bend_radius"]
    return c

coupler_symmetric

detectors

ge_detector_straight_si_contacts

ge_detector_straight_si_contacts(
    length: float = 40.0,
    cross_section: CrossSectionSpec = "pn_ge_detector_si_contacts",
    via_stack: ComponentSpec = "via_stack_slab_m3",
    via_stack_width: float = 10.0,
    via_stack_spacing: float = 5.0,
    via_stack_offset: float = 0.0,
    taper_length: float = 20.0,
    taper_width: float = 0.8,
    taper_cros_section: CrossSectionSpec = "strip",
) -> Component

Returns a straight Ge on Si detector with silicon contacts.

There are no contacts on the Ge. These detectors could have lower dark current and sensitivity compared to those with contacts in the Ge. See Chen et al., "High-Responsivity Low-Voltage 28-Gb/s Ge p-i-n Photodetector With Silicon Contacts", Journal of Lightwave Technology 33(4), 2015.

https://doi.org/10.1109/JLT.2014.2367134

Parameters:

Name Type Description Default
length float

pd length.

40.0
cross_section CrossSectionSpec

for the waveguide.

'pn_ge_detector_si_contacts'
via_stack ComponentSpec

for the via_stacks. First element

'via_stack_slab_m3'
via_stack_width float

width of the via_stack.

10.0
via_stack_spacing float

spacing between via_stacks.

5.0
via_stack_offset float

with respect to the detector

0.0
taper_length float

length of the taper.

20.0
taper_width float

width of the taper.

0.8
taper_cros_section CrossSectionSpec

cross_section of the taper.

'strip'
Source code in gdsfactory/components/detectors/detector_ge.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@gf.cell_with_module_name(schematic_function=photodiode_schematic, tags=["detectors"])
def ge_detector_straight_si_contacts(
    length: float = 40.0,
    cross_section: CrossSectionSpec = "pn_ge_detector_si_contacts",
    via_stack: ComponentSpec = "via_stack_slab_m3",
    via_stack_width: float = 10.0,
    via_stack_spacing: float = 5.0,
    via_stack_offset: float = 0.0,
    taper_length: float = 20.0,
    taper_width: float = 0.8,
    taper_cros_section: CrossSectionSpec = "strip",
) -> Component:
    """Returns a straight Ge on Si detector with silicon contacts.

    There are no contacts on the Ge. These detectors could have lower
    dark current and sensitivity compared to those with contacts in the
    Ge. See Chen et al., "High-Responsivity Low-Voltage 28-Gb/s Ge p-i-n
    Photodetector With Silicon Contacts", Journal of Lightwave Technology 33(4), 2015.

    https://doi.org/10.1109/JLT.2014.2367134

    Args:
        length: pd length.
        cross_section: for the waveguide.
        via_stack: for the via_stacks. First element
        via_stack_width: width of the via_stack.
        via_stack_spacing: spacing between via_stacks.
        via_stack_offset: with respect to the detector
        taper_length: length of the taper.
        taper_width: width of the taper.
        taper_cros_section: cross_section of the taper.
    """
    c = Component()
    xs = gf.get_cross_section(taper_cros_section)

    taper = gf.c.taper(
        width1=xs.width,
        width2=taper_width,
        length=taper_length,
        cross_section=taper_cros_section,
    )

    via_stack = gf.get_component(
        via_stack,
        size=(length, via_stack_width),
    )

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

    t1 = c << taper
    t1.connect("o2", wg["o1"], allow_width_mismatch=True)
    c.add_port("o1", port=t1["o1"])

    via_stack_top = c << via_stack
    via_stack_bot = c << via_stack

    via_stack_bot.xmin = wg.xmin
    via_stack_top.xmin = wg.xmin

    via_stack_top.ymin = +via_stack_spacing / 2 + via_stack_offset
    via_stack_bot.ymax = -via_stack_spacing / 2 + via_stack_offset

    bot_port = c.add_port(port=via_stack_bot.ports["e3"], name="bot")
    top_port = c.add_port(port=via_stack_top.ports["e3"], name="top")
    c.create_pin(ports=[bot_port], name="bot")
    c.create_pin(ports=[top_port], name="top")
    return c

ge_detector_straight_si_contacts

dies

add_frame

add_frame(
    component: ComponentSpec = "rectangle",
    width: float = 10.0,
    spacing: float = 10.0,
    layer: LayerSpec = "WG",
) -> Component

Returns component with a frame around it.

Parameters:

Name Type Description Default
component ComponentSpec

Component to frame.

'rectangle'
width float

of the frame.

10.0
spacing float

of component to frame.

10.0
layer LayerSpec

frame layer.

'WG'
Source code in gdsfactory/components/dies/align.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
@gf.cell_with_module_name(tags=["dies"])
def add_frame(
    component: ComponentSpec = "rectangle",
    width: float = 10.0,
    spacing: float = 10.0,
    layer: LayerSpec = "WG",
) -> Component:
    """Returns component with a frame around it.

    Args:
        component: Component to frame.
        width: of the frame.
        spacing: of component to frame.
        layer: frame layer.
    """
    c = Component()
    layer = gf.get_layer(layer)
    component = gf.get_component(component)
    cref = c.add_ref(component)
    cref.x = 0
    cref.y = 0
    y = max([component.xsize, component.ysize]) / 2 + spacing + width / 2
    x = y
    w = width

    rh = gf.c.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 = gf.c.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.flatten()
    return c

add_frame

align_wafer

align_wafer(
    width: float = 10.0,
    spacing: float = 10.0,
    cross_length: float = 80.0,
    layer: LayerSpec = "WG",
    layer_cladding: tuple[int, int] | None = None,
    square_corner: str = "bottom_left",
) -> Component

Returns cross inside a frame to align wafer.

Parameters:

Name Type Description Default
width float

in um.

10.0
spacing float

in um.

10.0
cross_length float

for the cross.

80.0
layer LayerSpec

for the cross.

'WG'
layer_cladding tuple[int, int] | None

optional.

None
square_corner str

bottom_left, bottom_right, top_right, top_left.

'bottom_left'
Source code in gdsfactory/components/dies/align.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@gf.cell_with_module_name(tags=["dies"])
def align_wafer(
    width: float = 10.0,
    spacing: float = 10.0,
    cross_length: float = 80.0,
    layer: LayerSpec = "WG",
    layer_cladding: tuple[int, int] | None = None,
    square_corner: str = "bottom_left",
) -> Component:
    """Returns cross inside a frame to align wafer.

    Args:
        width: in um.
        spacing: in um.
        cross_length: for the cross.
        layer: for the cross.
        layer_cladding: optional.
        square_corner: bottom_left, bottom_right, top_right, top_left.
    """
    layer = gf.get_layer(layer)
    c = Component()
    cross = gf.components.cross(length=cross_length, width=width, layer=layer)
    c.add_ref(cross)

    b = cross_length / 2 + spacing + width / 2
    w = width

    rh = gf.c.rectangle(size=(2 * b + w, w), layer=layer, centered=True)
    rv = gf.c.rectangle(size=(w, 2 * b), layer=layer, centered=True)

    rtop = c.add_ref(rh)
    rbot = c.add_ref(rh)

    rtop.movey(+b)
    rbot.movey(-b)

    rl = c.add_ref(rv)
    rr = c.add_ref(rv)
    rl.movex(-b)
    rr.movex(+b)

    wsq = (cross_length + 2 * spacing) / 4
    square_mark = c << gf.c.rectangle(size=(wsq, wsq), layer=layer, centered=True)
    a = width / 2 + wsq / 2 + spacing

    corner_to_position = {
        "bottom_left": (-a, -a),
        "bottom_right": (a, -a),
        "top_right": (a, a),
        "top_left": (-a, a),
    }

    square_mark.move(corner_to_position[square_corner])

    if layer_cladding:
        rc_tile_excl = gf.c.rectangle(
            size=(2 * (b + spacing), 2 * (b + spacing)),
            layer=layer_cladding,
            centered=True,
        )
        c.add_ref(rc_tile_excl)

    return c

align_wafer

die

based on phidl.geometry.

die

die(
    size: Size = (10000.0, 10000.0),
    street_width: float = 100.0,
    street_length: float = 1000.0,
    die_name: str | None = "chip99",
    text_size: float = 100.0,
    text_location: str | Float2 = "SW",
    layer: LayerSpec | None = "FLOORPLAN",
    bbox_layer: LayerSpec | None = "FLOORPLAN",
    text_layer: LayerSpec = "WG",
    text: ComponentSpec = "text",
    draw_corners: bool = False,
) -> gf.Component

Returns die with optional markers marking the boundary of the die.

Parameters:

Name Type Description Default
size Size

x, y dimensions of the die.

(10000.0, 10000.0)
street_width float

Width of the corner marks for die-sawing.

100.0
street_length float

Length of the corner marks for die-sawing.

1000.0
die_name str | None

Label text. If None, no label is added.

'chip99'
text_size float

Label text size.

100.0
text_location str | Float2

{'NW', 'N', 'NE', 'SW', 'S', 'SE'} or (x, y) coordinate.

'SW'
layer LayerSpec | None

For street widths. None to not draw the street widths.

'FLOORPLAN'
bbox_layer LayerSpec | None

optional bbox layer drawn bounding box around the die.

'FLOORPLAN'
text_layer LayerSpec

Layer for the die name text.

'WG'
text ComponentSpec

function use for generating text. Needs to accept text, size, layer.

'text'
draw_corners bool

True draws only corners. False draws a square die.

False
Source code in gdsfactory/components/dies/die.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@gf.cell_with_module_name(tags=["dies"])
def die(
    size: Size = (10000.0, 10000.0),
    street_width: float = 100.0,
    street_length: float = 1000.0,
    die_name: str | None = "chip99",
    text_size: float = 100.0,
    text_location: str | Float2 = "SW",
    layer: LayerSpec | None = "FLOORPLAN",
    bbox_layer: LayerSpec | None = "FLOORPLAN",
    text_layer: LayerSpec = "WG",
    text: ComponentSpec = "text",
    draw_corners: bool = False,
) -> gf.Component:
    """Returns die with optional markers marking the boundary of the die.

    Args:
        size: x, y dimensions of the die.
        street_width: Width of the corner marks for die-sawing.
        street_length: Length of the corner marks for die-sawing.
        die_name: Label text. If None, no label is added.
        text_size: Label text size.
        text_location: {'NW', 'N', 'NE', 'SW', 'S', 'SE'} or (x, y) coordinate.
        layer: For street widths. None to not draw the street widths.
        bbox_layer: optional bbox layer drawn bounding box around the die.
        text_layer: Layer for the die name text.
        text: function use for generating text. Needs to accept text, size, layer.
        draw_corners: True draws only corners. False draws a square die.
    """
    c = gf.Component()
    sx, sy = size[0] / 2, size[1] / 2

    if layer:
        if not draw_corners:
            street_length = sx
        xpts = np.array(
            [
                sx,
                sx,
                sx - street_width,
                sx - street_width,
                sx - street_length,
                sx - street_length,
            ]
        )
        if not draw_corners:
            street_length = sy
        ypts = np.array(
            [
                sy,
                sy - street_length,
                sy - street_length,
                sy - street_width,
                sy - street_width,
                sy,
            ]
        )
        c.add_polygon(list(zip(xpts, ypts, strict=False)), layer=layer)
        c.add_polygon(list(zip(-xpts, ypts, strict=False)), layer=layer)
        c.add_polygon(list(zip(xpts, -ypts, strict=False)), layer=layer)
        c.add_polygon(list(zip(-xpts, -ypts, strict=False)), layer=layer)

    if bbox_layer:
        c.add_polygon([(sx, sy), (sx, -sy), (-sx, -sy), (-sx, sy)], layer=bbox_layer)

    if die_name:
        text_component = gf.get_component(
            text, text=die_name, size=text_size, layer=text_layer
        )
        t = c.add_ref(text_component)

        d = street_width + 20
        if isinstance(text_location, str):
            text_location = text_location.upper()
            if text_location == "N":
                t.x, t.ymax = [0, sy - d]
            elif text_location == "NE":
                t.xmax, t.ymax = [sx - d, sy - d]
            elif text_location == "NW":
                t.xmin, t.ymax = [-sx + d, sy - d]
            elif text_location == "S":
                t.x, t.ymin = [0, -sy + d]
            elif text_location == "SE":
                t.xmax, t.ymin = [sx - d, -sy + d]
            elif text_location == "SW":
                t.xmin, t.ymin = [-sx + d, -sy + d]
            else:
                raise ValueError(
                    f"Invalid text_location: {text_location} not in N, NE, NW, S, SE, SW"
                )
        else:
            t.x, t.y = text_location

    return c

die

die_frame

die_frame(
    size: Size = (11200.0, 5000.0),
    layer_floorplan: LayerSpec = "FLOORPLAN",
) -> gf.Component
Source code in gdsfactory/components/dies/die_frame_with_pads.py
15
16
17
18
19
20
21
22
@gf.cell(tags=["dies"])
def die_frame(
    size: Size = (11200.0, 5000.0),
    layer_floorplan: LayerSpec = "FLOORPLAN",
) -> gf.Component:
    return gf.c.rectangle(
        size=size, layer=layer_floorplan, centered=True, port_type=None
    )

die_frame

die_frame_phix

die_frame_phix(
    die_frame: ComponentSpec = "die_frame",
    nfibers: int = 32,
    npads: int = 60,
    npads_rf: int = 6,
    fiber_pitch: float = 127.0,
    pad_pitch: float = 150.0,
    pad_pitch_gsg: float = 720.0,
    edge_coupler: (
        ComponentSpec | None
    ) = "edge_coupler_silicon",
    grating_coupler: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    pad_gsg: ComponentSpec = "pad_gsg",
    edge_to_pad_distance: float = 200.0,
    edge_to_pad_distance_left: float | None = None,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
    pad_port_name_rf: str = "e2",
    layer_fiducial: LayerSpec = "M3",
    layer_ruler: LayerSpec = "WG",
    ruler_bbox_layers: tuple[LayerSpec, ...] | None = None,
    ruler_bbox_offset: float = 3.0,
    ruler_yoffset: float = 0,
    ruler_xoffset: float = 0,
    fiber_coupler_xoffset: float = 0,
    with_right_fiber_coupler: bool = True,
    with_left_fiber_coupler: bool = True,
    text_offset: Float2 = (20, 10),
    text: ComponentSpec | None = "text_rectangular",
    pad_side_distance: float = 1160.0,
    xoffset_rf_pads: float = 50,
    pad_rotation_dc_north: float = 0,
    pad_rotation_dc_south: float = 0,
    pad_rotation_rf: float = 0,
    with_loopback: bool = True,
) -> Component

A die_frame with grating couplers and pads.

Parameters:

Name Type Description Default
die_frame ComponentSpec

die_frame spec.

'die_frame'
nfibers int

the number of grating couplers.

32
npads int

the number of pads.

60
npads_rf int

the number of RF pads on the left side.

6
fiber_pitch float

the pitch of the grating couplers, in um.

127.0
pad_pitch float

the pitch of the pads, in um.

150.0
pad_pitch_gsg float

the pitch of the GSG pads, in um.

720.0
edge_coupler ComponentSpec | None

the grating coupler component.

'edge_coupler_silicon'
grating_coupler ComponentSpec | None

Optional grating coupler.

None
cross_section CrossSectionSpec

the cross section.

'strip'
pad ComponentSpec

the pad component.

'pad'
pad_gsg ComponentSpec

the GSG pad component.

'pad_gsg'
edge_to_pad_distance float

the distance from the edge to the pads, in um.

200.0
edge_to_pad_distance_left float | None

Optional distance from the left edge to the pads, in um. If None, uses edge_to_pad_distance for both sides.

None
pad_port_name_top str

name of the pad port name at the top facing south.

'e4'
pad_port_name_bot str

name of the pad port name at the bottom facing north.

'e2'
pad_port_name_rf str

name of the RF pad port name.

'e2'
layer_fiducial LayerSpec

layer for fiducials.

'M3'
layer_ruler LayerSpec

layer for ruler.

'WG'
ruler_bbox_layers tuple[LayerSpec, ...] | None

layers for bbox.

None
ruler_bbox_offset float

offset for bbox.

3.0
ruler_yoffset float

y-offset for ruler.

0
ruler_xoffset float

x-offset for ruler.

0
fiber_coupler_xoffset float

x-offset for fiber couplers.

0
with_right_fiber_coupler bool

if True, adds edge couplers on the right side.

True
with_left_fiber_coupler bool

if True, adds edge couplers on the left side.

True
text_offset Float2

offset for text.

(20, 10)
text ComponentSpec | None

text component spec.

'text_rectangular'
pad_side_distance float

distance from the die frame side to the first pad, in um.

1160.0
xoffset_rf_pads float

RF pads x-offset.

50
pad_rotation_dc_north float

rotation for DC pads.

0
pad_rotation_dc_south float

rotation for DC pads.

0
pad_rotation_rf float

rotation for RF pads.

0
with_loopback bool

if True, adds loopback structures.

True
Source code in gdsfactory/components/dies/die_frame_with_pads.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
def die_frame_phix(
    die_frame: ComponentSpec = "die_frame",
    nfibers: int = 32,
    npads: int = 60,
    npads_rf: int = 6,
    fiber_pitch: float = 127.0,
    pad_pitch: float = 150.0,
    pad_pitch_gsg: float = 720.0,
    edge_coupler: ComponentSpec | None = "edge_coupler_silicon",
    grating_coupler: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    pad_gsg: ComponentSpec = "pad_gsg",
    edge_to_pad_distance: float = 200.0,
    edge_to_pad_distance_left: float | None = None,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
    pad_port_name_rf: str = "e2",
    layer_fiducial: LayerSpec = "M3",
    layer_ruler: LayerSpec = "WG",
    ruler_bbox_layers: tuple[LayerSpec, ...] | None = None,
    ruler_bbox_offset: float = 3.0,
    ruler_yoffset: float = 0,
    ruler_xoffset: float = 0,
    fiber_coupler_xoffset: float = 0,
    with_right_fiber_coupler: bool = True,
    with_left_fiber_coupler: bool = True,
    text_offset: Float2 = (20, 10),
    text: ComponentSpec | None = "text_rectangular",
    pad_side_distance: float = 1160.0,
    xoffset_rf_pads: float = 50,
    pad_rotation_dc_north: float = 0,
    pad_rotation_dc_south: float = 0,
    pad_rotation_rf: float = 0,
    with_loopback: bool = True,
) -> Component:
    """A die_frame with grating couplers and pads.

    Args:
        die_frame: die_frame spec.
        nfibers: the number of grating couplers.
        npads: the number of pads.
        npads_rf: the number of RF pads on the left side.
        fiber_pitch: the pitch of the grating couplers, in um.
        pad_pitch: the pitch of the pads, in um.
        pad_pitch_gsg: the pitch of the GSG pads, in um.
        edge_coupler: the grating coupler component.
        grating_coupler: Optional grating coupler.
        cross_section: the cross section.
        pad: the pad component.
        pad_gsg: the GSG pad component.
        edge_to_pad_distance: the distance from the edge to the pads, in um.
        edge_to_pad_distance_left: Optional distance from the left edge to the pads, in um. If None, uses edge_to_pad_distance for both sides.
        pad_port_name_top: name of the pad port name at the top facing south.
        pad_port_name_bot: name of the pad port name at the bottom facing north.
        pad_port_name_rf: name of the RF pad port name.
        layer_fiducial: layer for fiducials.
        layer_ruler: layer for ruler.
        ruler_bbox_layers: layers for bbox.
        ruler_bbox_offset: offset for bbox.
        ruler_yoffset: y-offset for ruler.
        ruler_xoffset: x-offset for ruler.
        fiber_coupler_xoffset: x-offset for fiber couplers.
        with_right_fiber_coupler: if True, adds edge couplers on the right side.
        with_left_fiber_coupler: if True, adds edge couplers on the left side.
        text_offset: offset for text.
        text: text component spec.
        pad_side_distance: distance from the die frame side to the first pad, in um.
        xoffset_rf_pads: RF pads x-offset.
        pad_rotation_dc_north: rotation for DC pads.
        pad_rotation_dc_south: rotation for DC pads.
        pad_rotation_rf: rotation for RF pads.
        with_loopback: if True, adds loopback structures.
    """
    if npads > 60:
        raise ValueError("npads should be <= 60. Reach out to PHIX for support.")

    c = Component()

    d = gf.get_component(die_frame)
    fp = c << d
    fp.x = 0
    fp.y = 0
    xs, ys = fp.xsize, fp.ysize

    # Add optical ports
    x0 = xs / 2

    edge_to_pad_distance_left = edge_to_pad_distance_left or edge_to_pad_distance

    if edge_coupler or grating_coupler:
        if edge_coupler:
            if with_loopback:
                gca = gf.c.edge_coupler_array_with_loopback(
                    n=nfibers,
                    pitch=fiber_pitch,
                    edge_coupler=edge_coupler,
                    cross_section=cross_section,
                    text_offset=text_offset,
                    text=text,
                    x_reflection=False,
                )
                gca_left = gf.c.edge_coupler_array_with_loopback(
                    n=nfibers,
                    pitch=fiber_pitch,
                    edge_coupler=edge_coupler,
                    cross_section=cross_section,
                    text_offset=(-text_offset[0], text_offset[1]),
                    text=text,
                    x_reflection=True,
                )
            else:
                gca = gf.c.edge_coupler_array(
                    n=nfibers,
                    pitch=fiber_pitch,
                    edge_coupler=edge_coupler,
                    text_offset=text_offset,
                    text=text,
                    x_reflection=False,
                )
                gca_left = gf.c.edge_coupler_array(
                    n=nfibers,
                    pitch=fiber_pitch,
                    edge_coupler=edge_coupler,
                    text_offset=(-text_offset[0], text_offset[1]),
                    text=text,
                    x_reflection=True,
                )

            if with_left_fiber_coupler:
                left = c << gca_left
                left.xmin = -xs / 2 - fiber_coupler_xoffset
                left.y = fp.y
                c.add_ports(left.ports, prefix="W")

            if with_right_fiber_coupler:
                right = c << gca
                right.xmax = xs / 2 + fiber_coupler_xoffset
                right.y = fp.y
                c.add_ports(right.ports, prefix="E")

        else:
            gca = gf.c.grating_coupler_array(
                n=nfibers,
                pitch=fiber_pitch,
                cross_section=cross_section,
                with_loopback=True,
            )
            gca_left = gf.c.grating_coupler_array(
                n=nfibers,
                pitch=fiber_pitch,
                cross_section=cross_section,
                with_loopback=True,
            )
            fiber_coupler_xoffset -= 750

            if with_left_fiber_coupler:
                left = c << gca_left
                left.rotate(-90)
                left.xmin = -xs / 2 - fiber_coupler_xoffset
                left.y = fp.y
                c.add_ports(left.ports, prefix="W")

            if with_right_fiber_coupler:
                right = c << gca
                right.rotate(+90)
                right.xmax = xs / 2 + fiber_coupler_xoffset
                right.y = fp.y
                c.add_ports(right.ports, prefix="E")
    ruler = gf.c.ruler(
        layer=layer_ruler,
        bbox_layers=ruler_bbox_layers,
        bbox_offset=ruler_bbox_offset,
    )

    if with_right_fiber_coupler:
        ruler_top_right = c << ruler
        ruler_top_right.xmax = fp.xmax - ruler_xoffset
        ruler_top_right.ymax = fp.ymax - 300 + ruler_yoffset

        ruler_bot_right = c << ruler
        ruler_bot_right.xmax = fp.xmax - ruler_xoffset
        ruler_bot_right.ymin = fp.ymin + 300 - ruler_yoffset

    if with_left_fiber_coupler:
        ruler_top_left = c << ruler
        ruler_top_left.rotate(180)
        ruler_top_left.xmin = fp.xmin + ruler_xoffset
        ruler_top_left.ymax = fp.ymax - 300 + ruler_yoffset

        ruler_bot_left = c << ruler
        ruler_bot_left.rotate(180)
        ruler_bot_left.xmin = fp.xmin + ruler_xoffset
        ruler_bot_left.ymin = fp.ymin + 300 - ruler_yoffset

    else:
        # left RF pads
        y0 = fp.ymax - 390 - pad_pitch_gsg / 2 + 50
        for i in range(npads_rf):
            pad_ref = c << gf.get_component(pad_gsg)
            pad_ref.rotate(pad_rotation_rf)
            pad_ref.y = y0 - i * pad_pitch_gsg
            pad_ref.xmin = fp.xmin + xoffset_rf_pads
            c.add_port(
                name=f"e{i}",
                port=pad_ref.ports[pad_port_name_rf],
            )

    # Add electrical ports
    pad = gf.get_component(pad)

    x0_pads = -xs / 2 + pad_side_distance
    x0 = x0_pads

    top_left = c << gf.c.cross(layer=layer_fiducial, length=150, width=20)
    top_left.xmax = x0 - 75
    top_left.y = +ys / 2 - edge_to_pad_distance - 50

    # north pads
    for i in range(npads):
        pad_ref = c << pad
        pad_ref.rotate(pad_rotation_dc_north)
        pad_ref.xmin = x0 + i * pad_pitch
        pad_ref.ymax = ys / 2 - edge_to_pad_distance
        c.add_port(
            name=f"N{i}",
            port=pad_ref.ports[pad_port_name_top],
        )
    top_right = c << gf.c.circle(layer=layer_fiducial, radius=75)
    top_right.xmin = pad_ref.xmax + 480
    top_right.y = +ys / 2 - edge_to_pad_distance - 50

    bot_left = c << gf.c.circle(layer=layer_fiducial, radius=75)
    bot_left.xmax = x0 - 75
    bot_left.y = -ys / 2 + edge_to_pad_distance + 50

    x0 = x0_pads

    # south pads
    for i in range(npads):
        pad_ref = c << pad
        pad_ref.rotate(pad_rotation_dc_south)
        pad_ref.xmin = x0 + i * pad_pitch
        pad_ref.ymin = -ys / 2 + edge_to_pad_distance
        c.add_port(
            name=f"S{i}",
            port=pad_ref.ports[pad_port_name_bot],
        )

    bot_right = c << gf.c.circle(layer=layer_fiducial, radius=75)
    bot_right.xmin = pad_ref.xmax + 480
    bot_right.ymin = -ys / 2 + edge_to_pad_distance

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    c.auto_rename_ports()
    return c

die_frame_phix

die_frame_phix_dc

die_frame_phix_dc(
    die_frame: ComponentSpec = "die_frame",
    nfibers: int = 32,
    npads: int = 59,
    npads_rf: int = 6,
    fiber_pitch: float = 127.0,
    pad_pitch: float = 150.0,
    pad_pitch_gsg: float = 720.0,
    edge_coupler: (
        ComponentSpec | None
    ) = "edge_coupler_silicon",
    grating_coupler: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    pad_gsg: ComponentSpec = "pad_gsg",
    edge_to_pad_distance: float = 200.0,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
    layer_fiducial: LayerSpec = "M3",
    layer_ruler: LayerSpec = "WG",
    ruler_bbox_layers: tuple[LayerSpec, ...] | None = None,
    ruler_bbox_offset: float = 3.0,
    ruler_yoffset: float = 0,
    ruler_xoffset: float = 0,
    with_right_fiber_coupler: bool = True,
    with_left_fiber_coupler: bool = True,
    fiber_coupler_xoffset: float = 0,
    text_offset: Float2 = (20, 10),
    text: ComponentSpec | None = None,
    pad_rotation_dc_north: float = 0,
    pad_rotation_dc_south: float = 0,
    pad_side_distance: float = 1160.0,
) -> Component
Source code in gdsfactory/components/dies/die_frame_with_pads.py
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
@gf.cell_with_module_name(tags=["dies"])
def die_frame_phix_dc(
    die_frame: ComponentSpec = "die_frame",
    nfibers: int = 32,
    npads: int = 59,
    npads_rf: int = 6,
    fiber_pitch: float = 127.0,
    pad_pitch: float = 150.0,
    pad_pitch_gsg: float = 720.0,
    edge_coupler: ComponentSpec | None = "edge_coupler_silicon",
    grating_coupler: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    pad_gsg: ComponentSpec = "pad_gsg",
    edge_to_pad_distance: float = 200.0,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
    layer_fiducial: LayerSpec = "M3",
    layer_ruler: LayerSpec = "WG",
    ruler_bbox_layers: tuple[LayerSpec, ...] | None = None,
    ruler_bbox_offset: float = 3.0,
    ruler_yoffset: float = 0,
    ruler_xoffset: float = 0,
    with_right_fiber_coupler: bool = True,
    with_left_fiber_coupler: bool = True,
    fiber_coupler_xoffset: float = 0,
    text_offset: Float2 = (20, 10),
    text: ComponentSpec | None = None,
    pad_rotation_dc_north: float = 0,
    pad_rotation_dc_south: float = 0,
    pad_side_distance: float = 1160.0,
) -> Component:
    return die_frame_phix(
        die_frame=die_frame,
        nfibers=nfibers,
        npads=npads,
        npads_rf=npads_rf,
        fiber_pitch=fiber_pitch,
        pad_pitch=pad_pitch,
        pad_pitch_gsg=pad_pitch_gsg,
        edge_coupler=edge_coupler,
        grating_coupler=grating_coupler,
        cross_section=cross_section,
        pad=pad,
        pad_gsg=pad_gsg,
        edge_to_pad_distance=edge_to_pad_distance,
        pad_port_name_top=pad_port_name_top,
        pad_port_name_bot=pad_port_name_bot,
        layer_fiducial=layer_fiducial,
        layer_ruler=layer_ruler,
        ruler_bbox_layers=ruler_bbox_layers,
        ruler_bbox_offset=ruler_bbox_offset,
        ruler_yoffset=ruler_yoffset,
        ruler_xoffset=ruler_xoffset,
        with_right_fiber_coupler=with_right_fiber_coupler,
        with_left_fiber_coupler=with_left_fiber_coupler,
        text_offset=text_offset,
        text=text,
        fiber_coupler_xoffset=fiber_coupler_xoffset,
        pad_rotation_dc_north=pad_rotation_dc_north,
        pad_rotation_dc_south=pad_rotation_dc_south,
        pad_side_distance=pad_side_distance,
    )

die_frame_phix_dc

die_frame_phix_rf

die_frame_phix_rf(
    die_frame: ComponentSpec = "die_frame_rf",
    nfibers: int = 32,
    npads: int = 59,
    npads_rf: int = 6,
    fiber_pitch: float = 127.0,
    pad_pitch: float = 150.0,
    pad_pitch_gsg: float = 720.0,
    edge_coupler: (
        ComponentSpec | None
    ) = "edge_coupler_silicon",
    grating_coupler: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    pad_gsg: ComponentSpec = "pad_gsg",
    edge_to_pad_distance: float = 200.0,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
    pad_port_name_rf: str = "e2",
    layer_fiducial: LayerSpec = "M3",
    layer_ruler: LayerSpec = "WG",
    ruler_bbox_layers: tuple[LayerSpec, ...] | None = None,
    ruler_bbox_offset: float = 3.0,
    ruler_yoffset: float = 0,
    ruler_xoffset: float = 0,
    with_right_fiber_coupler: bool = True,
    with_left_fiber_coupler: bool = False,
    fiber_coupler_xoffset: float = 0,
    text_offset: Float2 = (20, 10),
    text: ComponentSpec | None = None,
    pad_side_distance: float = 350.0,
    xoffset_rf_pads: float = 50,
    pad_rotation_rf: float = 0,
    pad_rotation_dc_north: float = 0,
    pad_rotation_dc_south: float = 0,
) -> Component
Source code in gdsfactory/components/dies/die_frame_with_pads.py
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
@gf.cell_with_module_name(tags=["dies"])
def die_frame_phix_rf(
    die_frame: ComponentSpec = "die_frame_rf",
    nfibers: int = 32,
    npads: int = 59,
    npads_rf: int = 6,
    fiber_pitch: float = 127.0,
    pad_pitch: float = 150.0,
    pad_pitch_gsg: float = 720.0,
    edge_coupler: ComponentSpec | None = "edge_coupler_silicon",
    grating_coupler: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    pad_gsg: ComponentSpec = "pad_gsg",
    edge_to_pad_distance: float = 200.0,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
    pad_port_name_rf: str = "e2",
    layer_fiducial: LayerSpec = "M3",
    layer_ruler: LayerSpec = "WG",
    ruler_bbox_layers: tuple[LayerSpec, ...] | None = None,
    ruler_bbox_offset: float = 3.0,
    ruler_yoffset: float = 0,
    ruler_xoffset: float = 0,
    with_right_fiber_coupler: bool = True,
    with_left_fiber_coupler: bool = False,
    fiber_coupler_xoffset: float = 0,
    text_offset: Float2 = (20, 10),
    text: ComponentSpec | None = None,
    pad_side_distance: float = 350.0,
    xoffset_rf_pads: float = 50,
    pad_rotation_rf: float = 0,
    pad_rotation_dc_north: float = 0,
    pad_rotation_dc_south: float = 0,
) -> Component:
    return die_frame_phix(
        die_frame=die_frame,
        nfibers=nfibers,
        npads=npads,
        npads_rf=npads_rf,
        fiber_pitch=fiber_pitch,
        pad_pitch=pad_pitch,
        pad_pitch_gsg=pad_pitch_gsg,
        edge_coupler=edge_coupler,
        grating_coupler=grating_coupler,
        cross_section=cross_section,
        pad=pad,
        pad_gsg=pad_gsg,
        edge_to_pad_distance=edge_to_pad_distance,
        pad_port_name_top=pad_port_name_top,
        pad_port_name_bot=pad_port_name_bot,
        pad_port_name_rf=pad_port_name_rf,
        layer_fiducial=layer_fiducial,
        layer_ruler=layer_ruler,
        ruler_bbox_layers=ruler_bbox_layers,
        ruler_bbox_offset=ruler_bbox_offset,
        ruler_yoffset=ruler_yoffset,
        ruler_xoffset=ruler_xoffset,
        with_right_fiber_coupler=with_right_fiber_coupler,
        with_left_fiber_coupler=with_left_fiber_coupler,
        text_offset=text_offset,
        text=text,
        pad_side_distance=pad_side_distance,
        fiber_coupler_xoffset=fiber_coupler_xoffset,
        xoffset_rf_pads=xoffset_rf_pads,
        pad_rotation_dc_south=pad_rotation_dc_south,
        pad_rotation_dc_north=pad_rotation_dc_north,
        pad_rotation_rf=pad_rotation_rf,
    )

die_frame_phix_rf

die_frame_rf

die_frame_rf(
    size: Size = (10400.0, 5000.0),
    layer_floorplan: LayerSpec = "FLOORPLAN",
) -> gf.Component
Source code in gdsfactory/components/dies/die_frame_with_pads.py
25
26
27
28
29
30
31
32
@gf.cell(tags=["dies"])
def die_frame_rf(
    size: Size = (10400.0, 5000.0),
    layer_floorplan: LayerSpec = "FLOORPLAN",
) -> gf.Component:
    return gf.c.rectangle(
        size=size, layer=layer_floorplan, centered=True, port_type=None
    )

die_frame_rf

die_frame_with_pads

die_frame_phix

die_frame_phix(
    die_frame: ComponentSpec = "die_frame",
    nfibers: int = 32,
    npads: int = 60,
    npads_rf: int = 6,
    fiber_pitch: float = 127.0,
    pad_pitch: float = 150.0,
    pad_pitch_gsg: float = 720.0,
    edge_coupler: (
        ComponentSpec | None
    ) = "edge_coupler_silicon",
    grating_coupler: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    pad_gsg: ComponentSpec = "pad_gsg",
    edge_to_pad_distance: float = 200.0,
    edge_to_pad_distance_left: float | None = None,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
    pad_port_name_rf: str = "e2",
    layer_fiducial: LayerSpec = "M3",
    layer_ruler: LayerSpec = "WG",
    ruler_bbox_layers: tuple[LayerSpec, ...] | None = None,
    ruler_bbox_offset: float = 3.0,
    ruler_yoffset: float = 0,
    ruler_xoffset: float = 0,
    fiber_coupler_xoffset: float = 0,
    with_right_fiber_coupler: bool = True,
    with_left_fiber_coupler: bool = True,
    text_offset: Float2 = (20, 10),
    text: ComponentSpec | None = "text_rectangular",
    pad_side_distance: float = 1160.0,
    xoffset_rf_pads: float = 50,
    pad_rotation_dc_north: float = 0,
    pad_rotation_dc_south: float = 0,
    pad_rotation_rf: float = 0,
    with_loopback: bool = True,
) -> Component

A die_frame with grating couplers and pads.

Parameters:

Name Type Description Default
die_frame ComponentSpec

die_frame spec.

'die_frame'
nfibers int

the number of grating couplers.

32
npads int

the number of pads.

60
npads_rf int

the number of RF pads on the left side.

6
fiber_pitch float

the pitch of the grating couplers, in um.

127.0
pad_pitch float

the pitch of the pads, in um.

150.0
pad_pitch_gsg float

the pitch of the GSG pads, in um.

720.0
edge_coupler ComponentSpec | None

the grating coupler component.

'edge_coupler_silicon'
grating_coupler ComponentSpec | None

Optional grating coupler.

None
cross_section CrossSectionSpec

the cross section.

'strip'
pad ComponentSpec

the pad component.

'pad'
pad_gsg ComponentSpec

the GSG pad component.

'pad_gsg'
edge_to_pad_distance float

the distance from the edge to the pads, in um.

200.0
edge_to_pad_distance_left float | None

Optional distance from the left edge to the pads, in um. If None, uses edge_to_pad_distance for both sides.

None
pad_port_name_top str

name of the pad port name at the top facing south.

'e4'
pad_port_name_bot str

name of the pad port name at the bottom facing north.

'e2'
pad_port_name_rf str

name of the RF pad port name.

'e2'
layer_fiducial LayerSpec

layer for fiducials.

'M3'
layer_ruler LayerSpec

layer for ruler.

'WG'
ruler_bbox_layers tuple[LayerSpec, ...] | None

layers for bbox.

None
ruler_bbox_offset float

offset for bbox.

3.0
ruler_yoffset float

y-offset for ruler.

0
ruler_xoffset float

x-offset for ruler.

0
fiber_coupler_xoffset float

x-offset for fiber couplers.

0
with_right_fiber_coupler bool

if True, adds edge couplers on the right side.

True
with_left_fiber_coupler bool

if True, adds edge couplers on the left side.

True
text_offset Float2

offset for text.

(20, 10)
text ComponentSpec | None

text component spec.

'text_rectangular'
pad_side_distance float

distance from the die frame side to the first pad, in um.

1160.0
xoffset_rf_pads float

RF pads x-offset.

50
pad_rotation_dc_north float

rotation for DC pads.

0
pad_rotation_dc_south float

rotation for DC pads.

0
pad_rotation_rf float

rotation for RF pads.

0
with_loopback bool

if True, adds loopback structures.

True
Source code in gdsfactory/components/dies/die_frame_with_pads.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
def die_frame_phix(
    die_frame: ComponentSpec = "die_frame",
    nfibers: int = 32,
    npads: int = 60,
    npads_rf: int = 6,
    fiber_pitch: float = 127.0,
    pad_pitch: float = 150.0,
    pad_pitch_gsg: float = 720.0,
    edge_coupler: ComponentSpec | None = "edge_coupler_silicon",
    grating_coupler: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    pad_gsg: ComponentSpec = "pad_gsg",
    edge_to_pad_distance: float = 200.0,
    edge_to_pad_distance_left: float | None = None,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
    pad_port_name_rf: str = "e2",
    layer_fiducial: LayerSpec = "M3",
    layer_ruler: LayerSpec = "WG",
    ruler_bbox_layers: tuple[LayerSpec, ...] | None = None,
    ruler_bbox_offset: float = 3.0,
    ruler_yoffset: float = 0,
    ruler_xoffset: float = 0,
    fiber_coupler_xoffset: float = 0,
    with_right_fiber_coupler: bool = True,
    with_left_fiber_coupler: bool = True,
    text_offset: Float2 = (20, 10),
    text: ComponentSpec | None = "text_rectangular",
    pad_side_distance: float = 1160.0,
    xoffset_rf_pads: float = 50,
    pad_rotation_dc_north: float = 0,
    pad_rotation_dc_south: float = 0,
    pad_rotation_rf: float = 0,
    with_loopback: bool = True,
) -> Component:
    """A die_frame with grating couplers and pads.

    Args:
        die_frame: die_frame spec.
        nfibers: the number of grating couplers.
        npads: the number of pads.
        npads_rf: the number of RF pads on the left side.
        fiber_pitch: the pitch of the grating couplers, in um.
        pad_pitch: the pitch of the pads, in um.
        pad_pitch_gsg: the pitch of the GSG pads, in um.
        edge_coupler: the grating coupler component.
        grating_coupler: Optional grating coupler.
        cross_section: the cross section.
        pad: the pad component.
        pad_gsg: the GSG pad component.
        edge_to_pad_distance: the distance from the edge to the pads, in um.
        edge_to_pad_distance_left: Optional distance from the left edge to the pads, in um. If None, uses edge_to_pad_distance for both sides.
        pad_port_name_top: name of the pad port name at the top facing south.
        pad_port_name_bot: name of the pad port name at the bottom facing north.
        pad_port_name_rf: name of the RF pad port name.
        layer_fiducial: layer for fiducials.
        layer_ruler: layer for ruler.
        ruler_bbox_layers: layers for bbox.
        ruler_bbox_offset: offset for bbox.
        ruler_yoffset: y-offset for ruler.
        ruler_xoffset: x-offset for ruler.
        fiber_coupler_xoffset: x-offset for fiber couplers.
        with_right_fiber_coupler: if True, adds edge couplers on the right side.
        with_left_fiber_coupler: if True, adds edge couplers on the left side.
        text_offset: offset for text.
        text: text component spec.
        pad_side_distance: distance from the die frame side to the first pad, in um.
        xoffset_rf_pads: RF pads x-offset.
        pad_rotation_dc_north: rotation for DC pads.
        pad_rotation_dc_south: rotation for DC pads.
        pad_rotation_rf: rotation for RF pads.
        with_loopback: if True, adds loopback structures.
    """
    if npads > 60:
        raise ValueError("npads should be <= 60. Reach out to PHIX for support.")

    c = Component()

    d = gf.get_component(die_frame)
    fp = c << d
    fp.x = 0
    fp.y = 0
    xs, ys = fp.xsize, fp.ysize

    # Add optical ports
    x0 = xs / 2

    edge_to_pad_distance_left = edge_to_pad_distance_left or edge_to_pad_distance

    if edge_coupler or grating_coupler:
        if edge_coupler:
            if with_loopback:
                gca = gf.c.edge_coupler_array_with_loopback(
                    n=nfibers,
                    pitch=fiber_pitch,
                    edge_coupler=edge_coupler,
                    cross_section=cross_section,
                    text_offset=text_offset,
                    text=text,
                    x_reflection=False,
                )
                gca_left = gf.c.edge_coupler_array_with_loopback(
                    n=nfibers,
                    pitch=fiber_pitch,
                    edge_coupler=edge_coupler,
                    cross_section=cross_section,
                    text_offset=(-text_offset[0], text_offset[1]),
                    text=text,
                    x_reflection=True,
                )
            else:
                gca = gf.c.edge_coupler_array(
                    n=nfibers,
                    pitch=fiber_pitch,
                    edge_coupler=edge_coupler,
                    text_offset=text_offset,
                    text=text,
                    x_reflection=False,
                )
                gca_left = gf.c.edge_coupler_array(
                    n=nfibers,
                    pitch=fiber_pitch,
                    edge_coupler=edge_coupler,
                    text_offset=(-text_offset[0], text_offset[1]),
                    text=text,
                    x_reflection=True,
                )

            if with_left_fiber_coupler:
                left = c << gca_left
                left.xmin = -xs / 2 - fiber_coupler_xoffset
                left.y = fp.y
                c.add_ports(left.ports, prefix="W")

            if with_right_fiber_coupler:
                right = c << gca
                right.xmax = xs / 2 + fiber_coupler_xoffset
                right.y = fp.y
                c.add_ports(right.ports, prefix="E")

        else:
            gca = gf.c.grating_coupler_array(
                n=nfibers,
                pitch=fiber_pitch,
                cross_section=cross_section,
                with_loopback=True,
            )
            gca_left = gf.c.grating_coupler_array(
                n=nfibers,
                pitch=fiber_pitch,
                cross_section=cross_section,
                with_loopback=True,
            )
            fiber_coupler_xoffset -= 750

            if with_left_fiber_coupler:
                left = c << gca_left
                left.rotate(-90)
                left.xmin = -xs / 2 - fiber_coupler_xoffset
                left.y = fp.y
                c.add_ports(left.ports, prefix="W")

            if with_right_fiber_coupler:
                right = c << gca
                right.rotate(+90)
                right.xmax = xs / 2 + fiber_coupler_xoffset
                right.y = fp.y
                c.add_ports(right.ports, prefix="E")
    ruler = gf.c.ruler(
        layer=layer_ruler,
        bbox_layers=ruler_bbox_layers,
        bbox_offset=ruler_bbox_offset,
    )

    if with_right_fiber_coupler:
        ruler_top_right = c << ruler
        ruler_top_right.xmax = fp.xmax - ruler_xoffset
        ruler_top_right.ymax = fp.ymax - 300 + ruler_yoffset

        ruler_bot_right = c << ruler
        ruler_bot_right.xmax = fp.xmax - ruler_xoffset
        ruler_bot_right.ymin = fp.ymin + 300 - ruler_yoffset

    if with_left_fiber_coupler:
        ruler_top_left = c << ruler
        ruler_top_left.rotate(180)
        ruler_top_left.xmin = fp.xmin + ruler_xoffset
        ruler_top_left.ymax = fp.ymax - 300 + ruler_yoffset

        ruler_bot_left = c << ruler
        ruler_bot_left.rotate(180)
        ruler_bot_left.xmin = fp.xmin + ruler_xoffset
        ruler_bot_left.ymin = fp.ymin + 300 - ruler_yoffset

    else:
        # left RF pads
        y0 = fp.ymax - 390 - pad_pitch_gsg / 2 + 50
        for i in range(npads_rf):
            pad_ref = c << gf.get_component(pad_gsg)
            pad_ref.rotate(pad_rotation_rf)
            pad_ref.y = y0 - i * pad_pitch_gsg
            pad_ref.xmin = fp.xmin + xoffset_rf_pads
            c.add_port(
                name=f"e{i}",
                port=pad_ref.ports[pad_port_name_rf],
            )

    # Add electrical ports
    pad = gf.get_component(pad)

    x0_pads = -xs / 2 + pad_side_distance
    x0 = x0_pads

    top_left = c << gf.c.cross(layer=layer_fiducial, length=150, width=20)
    top_left.xmax = x0 - 75
    top_left.y = +ys / 2 - edge_to_pad_distance - 50

    # north pads
    for i in range(npads):
        pad_ref = c << pad
        pad_ref.rotate(pad_rotation_dc_north)
        pad_ref.xmin = x0 + i * pad_pitch
        pad_ref.ymax = ys / 2 - edge_to_pad_distance
        c.add_port(
            name=f"N{i}",
            port=pad_ref.ports[pad_port_name_top],
        )
    top_right = c << gf.c.circle(layer=layer_fiducial, radius=75)
    top_right.xmin = pad_ref.xmax + 480
    top_right.y = +ys / 2 - edge_to_pad_distance - 50

    bot_left = c << gf.c.circle(layer=layer_fiducial, radius=75)
    bot_left.xmax = x0 - 75
    bot_left.y = -ys / 2 + edge_to_pad_distance + 50

    x0 = x0_pads

    # south pads
    for i in range(npads):
        pad_ref = c << pad
        pad_ref.rotate(pad_rotation_dc_south)
        pad_ref.xmin = x0 + i * pad_pitch
        pad_ref.ymin = -ys / 2 + edge_to_pad_distance
        c.add_port(
            name=f"S{i}",
            port=pad_ref.ports[pad_port_name_bot],
        )

    bot_right = c << gf.c.circle(layer=layer_fiducial, radius=75)
    bot_right.xmin = pad_ref.xmax + 480
    bot_right.ymin = -ys / 2 + edge_to_pad_distance

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    c.auto_rename_ports()
    return c

die_frame_with_pads

die_frame_with_pads(
    die_frame: ComponentSpec = "die_frame",
    ngratings: int = 14,
    npads: int = 31,
    grating_pitch: float = 250.0,
    pad_pitch: float = 300.0,
    grating_coupler: (
        ComponentSpec | None
    ) = "grating_coupler_te",
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    edge_to_pad_distance: float = 150.0,
    edge_to_grating_distance: float = 150.0,
    with_loopback: bool = True,
    loopback_radius: float | None = None,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
) -> Component

A die_frame with grating couplers and pads.

Parameters:

Name Type Description Default
die_frame ComponentSpec

die_frame spec.

'die_frame'
ngratings int

the number of grating couplers.

14
npads int

the number of pads.

31
grating_pitch float

the pitch of the grating couplers, in um.

250.0
pad_pitch float

the pitch of the pads, in um.

300.0
grating_coupler ComponentSpec | None

the grating coupler component.

'grating_coupler_te'
cross_section CrossSectionSpec

the cross section.

'strip'
pad ComponentSpec

the pad component.

'pad'
edge_to_pad_distance float

the distance from the edge to the pads, in um.

150.0
edge_to_grating_distance float

the distance from the edge to the grating couplers, in um.

150.0
with_loopback bool

if True, adds a loopback between edge GCs. Only works for rotation = 90 for now.

True
loopback_radius float | None

optional radius for loopback.

None
pad_port_name_top str

name of the pad port name at the btop facing south.

'e4'
pad_port_name_bot str

name of the pad port name at the bottom facing north.

'e2'
Source code in gdsfactory/components/dies/die_frame_with_pads.py
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@gf.cell_with_module_name(tags=["dies"])
def die_frame_with_pads(
    die_frame: ComponentSpec = "die_frame",
    ngratings: int = 14,
    npads: int = 31,
    grating_pitch: float = 250.0,
    pad_pitch: float = 300.0,
    grating_coupler: ComponentSpec | None = "grating_coupler_te",
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    edge_to_pad_distance: float = 150.0,
    edge_to_grating_distance: float = 150.0,
    with_loopback: bool = True,
    loopback_radius: float | None = None,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
) -> Component:
    """A die_frame with grating couplers and pads.

    Args:
        die_frame: die_frame spec.
        ngratings: the number of grating couplers.
        npads: the number of pads.
        grating_pitch: the pitch of the grating couplers, in um.
        pad_pitch: the pitch of the pads, in um.
        grating_coupler: the grating coupler component.
        cross_section: the cross section.
        pad: the pad component.
        edge_to_pad_distance: the distance from the edge to the pads, in um.
        edge_to_grating_distance: the distance from the edge to the grating couplers, in um.
        with_loopback: if True, adds a loopback between edge GCs. Only works for rotation = 90 for now.
        loopback_radius: optional radius for loopback.
        pad_port_name_top: name of the pad port name at the btop facing south.
        pad_port_name_bot: name of the pad port name at the bottom facing north.
    """
    c = Component()

    d = gf.get_component(die_frame)
    fp = c << d
    fp.x = 0
    fp.y = 0
    xs, ys = fp.xsize, fp.ysize

    # Add optical ports
    x0 = xs / 2 + edge_to_grating_distance

    if grating_coupler:
        gca = gf.c.grating_coupler_array(
            n=ngratings,
            pitch=grating_pitch,
            with_loopback=with_loopback,
            grating_coupler=grating_coupler,
            cross_section=cross_section,
            radius=loopback_radius,
        )
        left = c << gca
        left.rotate(-90)
        left.xmin = -xs / 2 + edge_to_grating_distance
        left.y = fp.y
        c.add_ports(left.ports, prefix="W")

        right = c << gca
        right.rotate(+90)
        right.xmax = xs / 2 - edge_to_grating_distance
        right.y = fp.y
        c.add_ports(right.ports, prefix="E")

    # Add electrical ports
    pad = gf.get_component(pad)
    x0 = -npads * pad_pitch / 2 + edge_to_pad_distance

    # north pads
    for i in range(npads):
        pad_ref = c << pad
        pad_ref.xmin = x0 + i * pad_pitch
        pad_ref.ymax = ys / 2 - edge_to_pad_distance
        c.add_port(
            name=f"N{i}",
            port=pad_ref.ports[pad_port_name_top],
        )

    x0 = -npads * pad_pitch / 2 + edge_to_pad_distance

    # south pads
    for i in range(npads):
        pad_ref = c << pad
        pad_ref.xmin = x0 + i * pad_pitch
        pad_ref.ymin = -ys / 2 + edge_to_pad_distance
        c.add_port(
            name=f"S{i}",
            port=pad_ref.ports[pad_port_name_bot],
        )

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    c.auto_rename_ports()
    return c

die_frame_with_pads

die_with_pads

die_with_pads

die_with_pads(
    size: Size = (11470.0, 4900.0),
    ngratings: int = 14,
    npads: int = 31,
    grating_pitch: float = 250.0,
    pad_pitch: float = 300.0,
    grating_coupler: (
        ComponentSpec | None
    ) = "grating_coupler_te",
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    layer_floorplan: LayerSpec = "FLOORPLAN",
    edge_to_pad_distance: float = 150.0,
    edge_to_grating_distance: float = 150.0,
    with_loopback: bool = True,
    loopback_radius: float | None = None,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
) -> Component

A die with grating couplers and pads.

Parameters:

Name Type Description Default
size Size

the size of the die, in um.

(11470.0, 4900.0)
ngratings int

the number of grating couplers.

14
npads int

the number of pads.

31
grating_pitch float

the pitch of the grating couplers, in um.

250.0
pad_pitch float

the pitch of the pads, in um.

300.0
grating_coupler ComponentSpec | None

the grating coupler component.

'grating_coupler_te'
cross_section CrossSectionSpec

the cross section.

'strip'
pad ComponentSpec

the pad component.

'pad'
layer_floorplan LayerSpec

the layer of the floorplan.

'FLOORPLAN'
edge_to_pad_distance float

the distance from the edge to the pads, in um.

150.0
edge_to_grating_distance float

the distance from the edge to the grating couplers, in um.

150.0
with_loopback bool

if True, adds a loopback between edge GCs. Only works for rotation = 90 for now.

True
loopback_radius float | None

optional radius for loopback.

None
pad_port_name_top str

name of the pad port name at the btop facing south.

'e4'
pad_port_name_bot str

name of the pad port name at the bottom facing north.

'e2'
Source code in gdsfactory/components/dies/die_with_pads.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@gf.cell_with_module_name(tags=["dies"])
def die_with_pads(
    size: Size = (11470.0, 4900.0),
    ngratings: int = 14,
    npads: int = 31,
    grating_pitch: float = 250.0,
    pad_pitch: float = 300.0,
    grating_coupler: ComponentSpec | None = "grating_coupler_te",
    cross_section: CrossSectionSpec = "strip",
    pad: ComponentSpec = "pad",
    layer_floorplan: LayerSpec = "FLOORPLAN",
    edge_to_pad_distance: float = 150.0,
    edge_to_grating_distance: float = 150.0,
    with_loopback: bool = True,
    loopback_radius: float | None = None,
    pad_port_name_top: str = "e4",
    pad_port_name_bot: str = "e2",
) -> Component:
    """A die with grating couplers and pads.

    Args:
        size: the size of the die, in um.
        ngratings: the number of grating couplers.
        npads: the number of pads.
        grating_pitch: the pitch of the grating couplers, in um.
        pad_pitch: the pitch of the pads, in um.
        grating_coupler: the grating coupler component.
        cross_section: the cross section.
        pad: the pad component.
        layer_floorplan: the layer of the floorplan.
        edge_to_pad_distance: the distance from the edge to the pads, in um.
        edge_to_grating_distance: the distance from the edge to the grating couplers, in um.
        with_loopback: if True, adds a loopback between edge GCs. Only works for rotation = 90 for now.
        loopback_radius: optional radius for loopback.
        pad_port_name_top: name of the pad port name at the btop facing south.
        pad_port_name_bot: name of the pad port name at the bottom facing north.
    """
    warnings.warn(
        "die_with_pads is deprecated and will be removed soon. Please use die_frame_with_pads instead",
        stacklevel=2,
    )

    c = Component()
    fp = c << gf.c.rectangle(
        size=size, layer=layer_floorplan, centered=True, port_type=None
    )
    xs, ys = size

    # Add optical ports
    x0 = xs / 2 + edge_to_grating_distance

    if grating_coupler:
        gca = gf.c.grating_coupler_array(
            n=ngratings,
            pitch=grating_pitch,
            with_loopback=with_loopback,
            grating_coupler=grating_coupler,
            cross_section=cross_section,
            radius=loopback_radius,
        )
        left = c << gca
        left.rotate(-90)
        left.xmin = -xs / 2 + edge_to_grating_distance
        left.y = fp.y
        c.add_ports(left.ports, prefix="W")

        right = c << gca
        right.rotate(+90)
        right.xmax = xs / 2 - edge_to_grating_distance
        right.y = fp.y
        c.add_ports(right.ports, prefix="E")

    # Add electrical ports
    pad = gf.get_component(pad)
    x0 = -npads * pad_pitch / 2 + edge_to_pad_distance

    # north pads
    for i in range(npads):
        pad_ref = c << pad
        pad_ref.xmin = x0 + i * pad_pitch
        pad_ref.ymax = ys / 2 - edge_to_pad_distance
        c.add_port(
            name=f"N{i}",
            port=pad_ref.ports[pad_port_name_top],
        )

    x0 = -npads * pad_pitch / 2 + edge_to_pad_distance

    # south pads
    for i in range(npads):
        pad_ref = c << pad
        pad_ref.xmin = x0 + i * pad_pitch
        pad_ref.ymin = -ys / 2 + edge_to_pad_distance
        c.add_port(
            name=f"S{i}",
            port=pad_ref.ports[pad_port_name_bot],
        )

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    c.auto_rename_ports()
    return c

die_with_pads

seal_ring

seal_ring

seal_ring(
    size: Float2 = (500, 500),
    seal: ComponentSpec = "via_stack",
    width: float = 10,
    padding: float = 10.0,
    with_north: bool = True,
    with_south: bool = True,
    with_east: bool = True,
    with_west: bool = True,
) -> gf.Component

Returns a continuous seal ring boundary at the chip/die.

Prevents cracks from spreading and shields when connected to ground.

Parameters:

Name Type Description Default
size Float2

of the seal.

(500, 500)
seal ComponentSpec

function for the seal.

'via_stack'
width float

of the seal.

10
padding float

from component to seal.

10.0
with_north bool

includes seal.

True
with_south bool

includes seal.

True
with_east bool

includes seal.

True
with_west bool

includes seal.

True
Source code in gdsfactory/components/dies/seal_ring.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@gf.cell_with_module_name(tags=["dies"])
def seal_ring(
    size: Float2 = (500, 500),
    seal: ComponentSpec = "via_stack",
    width: float = 10,
    padding: float = 10.0,
    with_north: bool = True,
    with_south: bool = True,
    with_east: bool = True,
    with_west: bool = True,
) -> gf.Component:
    """Returns a continuous seal ring boundary at the chip/die.

    Prevents cracks from spreading and shields when connected to ground.

    Args:
        size: of the seal.
        seal: function for the seal.
        width: of the seal.
        padding: from component to seal.
        with_north: includes seal.
        with_south: includes seal.
        with_east: includes seal.
        with_west: includes seal.
    """
    c = gf.Component()

    xmin, ymin = 0, 0
    xmax = size[0]
    ymax = size[1]
    x = (xmax + xmin) / 2
    sx = xmax - xmin
    sy = ymax - ymin

    sx = snap_to_grid(sx, grid_factor=2)
    sy = snap_to_grid(sy, grid_factor=2)

    ymin_north = snap_to_grid(ymax + padding, grid_factor=2)
    ymax_south = snap_to_grid(ymax - sy - padding, grid_factor=2)

    # north south
    size_north_south = (sx + 2 * padding + 2 * width, width)
    size_east_west = (width, sy + 2 * padding)

    if with_north:
        north = c << gf.get_component(
            seal, size=size_north_south, port_orientations=None
        )
        north.ymin = ymin_north
        north.x = x

    if with_east:
        east = c << gf.get_component(seal, size=size_east_west, port_orientations=None)
        east.xmin = xmax + padding
        east.ymax = ymin_north

    if with_west:
        west = c << gf.get_component(seal, size=size_east_west, port_orientations=None)
        west.xmax = xmin - padding
        west.ymax = ymin_north

    if with_south:
        south = c << gf.get_component(
            seal, size=size_north_south, port_orientations=None
        )
        south.ymax = ymax_south
        south.x = x

    return c

seal_ring_segmented

seal_ring_segmented(
    size: Float2 = (500, 500),
    length_segment: float = 10,
    width_segment: float = 3,
    spacing_segment: float = 2,
    corner: ComponentSpec = "via_stack_corner45_extended",
    via_stack: ComponentSpec = "via_stack_m1_mtop",
    with_north: bool = True,
    with_south: bool = True,
    with_east: bool = True,
    with_west: bool = True,
) -> gf.Component

Segmented Seal ring.

Parameters:

Name Type Description Default
size Float2

of the seal ring.

(500, 500)
length_segment float

length of each segment.

10
width_segment float

width of each segment.

3
spacing_segment float

spacing between segments.

2
corner ComponentSpec

corner component.

'via_stack_corner45_extended'
via_stack ComponentSpec

via_stack component.

'via_stack_m1_mtop'
with_north bool

includes seal.

True
with_south bool

includes seal.

True
with_east bool

includes seal.

True
with_west bool

includes seal.

True
Source code in gdsfactory/components/dies/seal_ring.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
@gf.cell_with_module_name(tags=["dies"])
def seal_ring_segmented(
    size: Float2 = (500, 500),
    length_segment: float = 10,
    width_segment: float = 3,
    spacing_segment: float = 2,
    corner: ComponentSpec = "via_stack_corner45_extended",
    via_stack: ComponentSpec = "via_stack_m1_mtop",
    with_north: bool = True,
    with_south: bool = True,
    with_east: bool = True,
    with_west: bool = True,
) -> gf.Component:
    """Segmented Seal ring.

    Args:
        size: of the seal ring.
        length_segment: length of each segment.
        width_segment: width of each segment.
        spacing_segment: spacing between segments.
        corner: corner component.
        via_stack: via_stack component.
        with_north: includes seal.
        with_south: includes seal.
        with_east: includes seal.
        with_west: includes seal.
    """
    c = gf.Component()
    corner_component = gf.get_component(corner, width=width_segment)

    xmin, ymin = 0, 0
    xmax = size[0]
    ymax = size[1]

    tl = c << corner_component
    tr = c << corner_component

    tl.xmin = xmin
    tl.ymax = ymax

    tr.dmirror()
    tr.xmax = xmax
    tr.ymax = ymax

    bl = c << corner_component
    br = c << corner_component
    br.dmirror()
    br.dmirror_y()
    bl.dmirror_y()

    bl.xmin = xmin
    bl.ymin = ymin
    br.xmax = xmax
    br.ymin = ymin

    pitch = length_segment + spacing_segment

    # horizontal
    dx = abs(tl.xmax - tr.xmin)
    segment_horizontal = gf.get_component(
        via_stack, size=(length_segment, width_segment), port_orientations=None
    )
    horizontal = gf.c.array(
        component=segment_horizontal, columns=int(dx / pitch), column_pitch=pitch
    )

    if with_north:
        top = c << horizontal
        top.ymax = tl.ymax
        top.xmin = tl.xmax + spacing_segment

        # horizontal inner
        topi = c << horizontal
        topi.ymax = top.ymin - spacing_segment
        topi.xmin = top.xmin + pitch / 2

    if with_south:
        bot = c << horizontal
        bot.ymin = ymin
        bot.xmin = tl.xmax + spacing_segment

        boti = c << horizontal
        boti.ymin = bot.ymax + spacing_segment
        boti.xmin = bot.xmin + spacing_segment

    # vertical
    segment_vertical = gf.get_component(
        via_stack, size=(width_segment, length_segment), port_orientations=None
    )
    dy = abs(tl.ymin - bl.ymax)

    vertical = gf.c.array(
        component=segment_vertical, rows=int(dy / pitch), columns=1, row_pitch=pitch
    )

    if with_east:
        right = c << vertical
        right.xmax = xmax
        right.ymin = bl.ymax
        righti = c << vertical
        righti.xmax = right.xmin - spacing_segment
        righti.ymin = right.ymin + pitch / 2

    if with_west:
        left = c << vertical
        left.xmin = xmin
        left.ymin = bl.ymax

        # vertical inner
        lefti = c << vertical
        lefti.xmin = left.xmax + spacing_segment
        lefti.ymin = left.ymin + pitch / 2

    return c

seal_ring

seal_ring_segmented

seal_ring_segmented(
    size: Float2 = (500, 500),
    length_segment: float = 10,
    width_segment: float = 3,
    spacing_segment: float = 2,
    corner: ComponentSpec = "via_stack_corner45_extended",
    via_stack: ComponentSpec = "via_stack_m1_mtop",
    with_north: bool = True,
    with_south: bool = True,
    with_east: bool = True,
    with_west: bool = True,
) -> gf.Component

Segmented Seal ring.

Parameters:

Name Type Description Default
size Float2

of the seal ring.

(500, 500)
length_segment float

length of each segment.

10
width_segment float

width of each segment.

3
spacing_segment float

spacing between segments.

2
corner ComponentSpec

corner component.

'via_stack_corner45_extended'
via_stack ComponentSpec

via_stack component.

'via_stack_m1_mtop'
with_north bool

includes seal.

True
with_south bool

includes seal.

True
with_east bool

includes seal.

True
with_west bool

includes seal.

True
Source code in gdsfactory/components/dies/seal_ring.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
@gf.cell_with_module_name(tags=["dies"])
def seal_ring_segmented(
    size: Float2 = (500, 500),
    length_segment: float = 10,
    width_segment: float = 3,
    spacing_segment: float = 2,
    corner: ComponentSpec = "via_stack_corner45_extended",
    via_stack: ComponentSpec = "via_stack_m1_mtop",
    with_north: bool = True,
    with_south: bool = True,
    with_east: bool = True,
    with_west: bool = True,
) -> gf.Component:
    """Segmented Seal ring.

    Args:
        size: of the seal ring.
        length_segment: length of each segment.
        width_segment: width of each segment.
        spacing_segment: spacing between segments.
        corner: corner component.
        via_stack: via_stack component.
        with_north: includes seal.
        with_south: includes seal.
        with_east: includes seal.
        with_west: includes seal.
    """
    c = gf.Component()
    corner_component = gf.get_component(corner, width=width_segment)

    xmin, ymin = 0, 0
    xmax = size[0]
    ymax = size[1]

    tl = c << corner_component
    tr = c << corner_component

    tl.xmin = xmin
    tl.ymax = ymax

    tr.dmirror()
    tr.xmax = xmax
    tr.ymax = ymax

    bl = c << corner_component
    br = c << corner_component
    br.dmirror()
    br.dmirror_y()
    bl.dmirror_y()

    bl.xmin = xmin
    bl.ymin = ymin
    br.xmax = xmax
    br.ymin = ymin

    pitch = length_segment + spacing_segment

    # horizontal
    dx = abs(tl.xmax - tr.xmin)
    segment_horizontal = gf.get_component(
        via_stack, size=(length_segment, width_segment), port_orientations=None
    )
    horizontal = gf.c.array(
        component=segment_horizontal, columns=int(dx / pitch), column_pitch=pitch
    )

    if with_north:
        top = c << horizontal
        top.ymax = tl.ymax
        top.xmin = tl.xmax + spacing_segment

        # horizontal inner
        topi = c << horizontal
        topi.ymax = top.ymin - spacing_segment
        topi.xmin = top.xmin + pitch / 2

    if with_south:
        bot = c << horizontal
        bot.ymin = ymin
        bot.xmin = tl.xmax + spacing_segment

        boti = c << horizontal
        boti.ymin = bot.ymax + spacing_segment
        boti.xmin = bot.xmin + spacing_segment

    # vertical
    segment_vertical = gf.get_component(
        via_stack, size=(width_segment, length_segment), port_orientations=None
    )
    dy = abs(tl.ymin - bl.ymax)

    vertical = gf.c.array(
        component=segment_vertical, rows=int(dy / pitch), columns=1, row_pitch=pitch
    )

    if with_east:
        right = c << vertical
        right.xmax = xmax
        right.ymin = bl.ymax
        righti = c << vertical
        righti.xmax = right.xmin - spacing_segment
        righti.ymin = right.ymin + pitch / 2

    if with_west:
        left = c << vertical
        left.xmin = xmin
        left.ymin = bl.ymax

        # vertical inner
        lefti = c << vertical
        lefti.xmin = left.xmax + spacing_segment
        lefti.ymin = left.ymin + pitch / 2

    return c

seal_ring_segmented

wafer

wafer

wafer(
    reticle: ComponentSpec = "die",
    cols: tuple[int, ...] = _cols_200mm_wafer,
    xspacing: float | None = None,
    yspacing: float | None = None,
    die_name_col_row: bool = False,
) -> Component

Returns complete wafer. Useful for mask aligner steps.

Parameters:

Name Type Description Default
reticle ComponentSpec

spec for each wafer reticle.

'die'
cols tuple[int, ...]

how many columns per row.

_cols_200mm_wafer
xspacing float | None

optional spacing, defaults to reticle.xsize.

None
yspacing float | None

optional spacing, defaults to reticle.ysize.

None
die_name_col_row bool

if True, die name is row_col, otherwise is a number

False
Source code in gdsfactory/components/dies/wafer.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@gf.cell_with_module_name(tags=["dies"])
def wafer(
    reticle: ComponentSpec = "die",
    cols: tuple[int, ...] = _cols_200mm_wafer,
    xspacing: float | None = None,
    yspacing: float | None = None,
    die_name_col_row: bool = False,
) -> Component:
    """Returns complete wafer. Useful for mask aligner steps.

    Args:
        reticle: spec for each wafer reticle.
        cols: how many columns per row.
        xspacing: optional spacing, defaults to reticle.xsize.
        yspacing: optional spacing, defaults to reticle.ysize.
        die_name_col_row: if True, die name is row_col, otherwise is a number
    """
    c = gf.Component()
    die = gf.get_component(reticle)
    xspacing = xspacing or die.xsize
    yspacing = yspacing or die.ysize

    i = 1
    for col in range(len(cols)):
        for row in range(cols[col]):
            die_name = f"{col + 1}_{row + 1}" if die_name_col_row else str(i)
            die = gf.get_component(reticle, die_name=die_name)
            ref = c.add_ref(die)
            ref.movex((row - cols[col] / 2) * xspacing)
            ref.movey(col * yspacing)
            i += 1

    return c

wafer

edge_couplers

edge_coupler_array

edge_coupler_array

edge_coupler_array(
    edge_coupler: ComponentSpec = "edge_coupler_silicon",
    n: int = 5,
    pitch: float = 127.0,
    x_reflection: bool = False,
    text: ComponentSpec | None = "text_rectangular",
    text_offset: Float2 = (10, 20),
    text_rotation: float = 0,
) -> Component

Fiber array edge coupler based on an inverse taper.

Each edge coupler adds a ruler for polishing.

Parameters:

Name Type Description Default
edge_coupler ComponentSpec

edge coupler spec.

'edge_coupler_silicon'
n int

number of channels.

5
pitch float

Fiber pitch.

127.0
x_reflection bool

horizontal mirror.

False
text ComponentSpec | None

text spec.

'text_rectangular'
text_offset Float2

from edge coupler.

(10, 20)
text_rotation float

text rotation in degrees.

0
Source code in gdsfactory/components/edge_couplers/edge_coupler_array.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@gf.cell_with_module_name(tags=["edge_couplers"])
def edge_coupler_array(
    edge_coupler: ComponentSpec = "edge_coupler_silicon",
    n: int = 5,
    pitch: float = 127.0,
    x_reflection: bool = False,
    text: ComponentSpec | None = "text_rectangular",
    text_offset: Float2 = (10, 20),
    text_rotation: float = 0,
) -> Component:
    """Fiber array edge coupler based on an inverse taper.

    Each edge coupler adds a ruler for polishing.

    Args:
        edge_coupler: edge coupler spec.
        n: number of channels.
        pitch: Fiber pitch.
        x_reflection: horizontal mirror.
        text: text spec.
        text_offset: from edge coupler.
        text_rotation: text rotation in degrees.
    """
    edge_coupler = gf.get_component(edge_coupler)

    c = Component()
    for i in range(n):
        ref = c.add_ref(edge_coupler)
        ref.name = f"ec_{i}"
        ref.y = i * pitch

        if x_reflection:
            ref.mirror()

        for port in ref.ports:
            if port.port_type == "optical":
                c.add_port(name=f"o{i}", port=port)

        if text:
            t = c << gf.get_component(text, text=str(i + 1))
            t.rotate(text_rotation)
            t.movex(text_offset[0])
            t.movey(i * pitch + text_offset[1])

    c.auto_rename_ports()
    return c

edge_coupler_array_with_loopback

edge_coupler_array_with_loopback(
    edge_coupler: ComponentSpec = "edge_coupler_silicon",
    cross_section: CrossSectionSpec = "strip",
    radius: float | None = None,
    n: int = 8,
    pitch: float = 127.0,
    x_reflection: bool = False,
    text: ComponentSpec | None = "text_rectangular",
    text_offset: Float2 = (0, 10),
    text_rotation: float = 0,
) -> Component

Fiber array edge coupler.

Parameters:

Name Type Description Default
edge_coupler ComponentSpec

edge coupler.

'edge_coupler_silicon'
cross_section CrossSectionSpec

spec.

'strip'
radius float | None

bend radius loopback (um).

None
n int

number of channels.

8
pitch float

Fiber pitch (um).

127.0
x_reflection bool

horizontal mirror.

False
text ComponentSpec | None

Optional text spec.

'text_rectangular'
text_offset Float2

x, y.

(0, 10)
text_rotation float

text rotation in degrees.

0
Source code in gdsfactory/components/edge_couplers/edge_coupler_array.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
@gf.cell_with_module_name(tags=["edge_couplers"])
def edge_coupler_array_with_loopback(
    edge_coupler: ComponentSpec = "edge_coupler_silicon",
    cross_section: CrossSectionSpec = "strip",
    radius: float | None = None,
    n: int = 8,
    pitch: float = 127.0,
    x_reflection: bool = False,
    text: ComponentSpec | None = "text_rectangular",
    text_offset: Float2 = (0, 10),
    text_rotation: float = 0,
) -> Component:
    """Fiber array edge coupler.

    Args:
        edge_coupler: edge coupler.
        cross_section: spec.
        radius: bend radius loopback (um).
        n: number of channels.
        pitch: Fiber pitch (um).
        x_reflection: horizontal mirror.
        text: Optional text spec.
        text_offset: x, y.
        text_rotation: text rotation in degrees.
    """
    xs = gf.get_cross_section(cross_section)
    radius = radius or xs.radius

    c = Component()
    ec = edge_coupler_array(
        edge_coupler=edge_coupler,
        n=n,
        pitch=pitch,
        x_reflection=x_reflection,
        text=text,
        text_offset=text_offset,
        text_rotation=text_rotation,
    )
    ec_ref = c << ec
    if x_reflection:
        ec_ref_ports = ec_ref.ports.filter(orientation=0)
    else:
        ec_ref_ports = ec_ref.ports.filter(orientation=180)

    p1 = ec_ref_ports[0]
    p2 = ec_ref_ports[1]
    p3 = ec_ref_ports[-2]
    p4 = ec_ref_ports[-1]

    gf.routing.route_single(
        c,
        p1,
        p2,
        cross_section=cross_section,
        radius=radius,
    )
    gf.routing.route_single(
        c,
        p4,
        p3,
        cross_section=cross_section,
        radius=radius,
    )

    for i, port in enumerate(ec_ref_ports):
        if port not in [p1, p2, p3, p4]:
            c.add_port(str(i), port=port)

    c.auto_rename_ports()
    return c

edge_coupler_silicon

edge_coupler_silicon(
    length: float = 100,
    width1: float = 0.5,
    width2: float = 0.2,
    with_two_ports: bool = True,
    port_names: tuple[str, str] = ("o1", "o2"),
    port_types: tuple[str, str] = (
        "optical",
        "edge_coupler",
    ),
    cross_section: CrossSectionSpec = "strip",
) -> Component

Edge coupler for silicon photonics.

Parameters:

Name Type Description Default
length float

length of the taper.

100
width1 float

width1 of the taper.

0.5
width2 float

width2 of the taper.

0.2
with_two_ports bool

add two ports.

True
port_names tuple[str, str]

tuple with port names.

('o1', 'o2')
port_types tuple[str, str]

tuple with port types.

('optical', 'edge_coupler')
cross_section CrossSectionSpec

cross_section spec.

'strip'
Source code in gdsfactory/components/edge_couplers/edge_coupler_array.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@gf.cell_with_module_name(schematic_function=taper_schematic, tags=["edge_couplers"])
def edge_coupler_silicon(
    length: float = 100,
    width1: float = 0.5,
    width2: float = 0.2,
    with_two_ports: bool = True,
    port_names: tuple[str, str] = ("o1", "o2"),
    port_types: tuple[str, str] = ("optical", "edge_coupler"),
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Edge coupler for silicon photonics.

    Args:
        length: length of the taper.
        width1: width1 of the taper.
        width2: width2 of the taper.
        with_two_ports: add two ports.
        port_names: tuple with port names.
        port_types: tuple with port types.
        cross_section: cross_section spec.

    """
    return gf.c.taper(
        width1=width1,
        width2=width2,
        length=length,
        with_two_ports=with_two_ports,
        port_names=port_names,
        port_types=port_types,
        cross_section=cross_section,
    )

edge_coupler_array

edge_coupler_array_with_loopback

edge_coupler_array_with_loopback(
    edge_coupler: ComponentSpec = "edge_coupler_silicon",
    cross_section: CrossSectionSpec = "strip",
    radius: float | None = None,
    n: int = 8,
    pitch: float = 127.0,
    x_reflection: bool = False,
    text: ComponentSpec | None = "text_rectangular",
    text_offset: Float2 = (0, 10),
    text_rotation: float = 0,
) -> Component

Fiber array edge coupler.

Parameters:

Name Type Description Default
edge_coupler ComponentSpec

edge coupler.

'edge_coupler_silicon'
cross_section CrossSectionSpec

spec.

'strip'
radius float | None

bend radius loopback (um).

None
n int

number of channels.

8
pitch float

Fiber pitch (um).

127.0
x_reflection bool

horizontal mirror.

False
text ComponentSpec | None

Optional text spec.

'text_rectangular'
text_offset Float2

x, y.

(0, 10)
text_rotation float

text rotation in degrees.

0
Source code in gdsfactory/components/edge_couplers/edge_coupler_array.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
@gf.cell_with_module_name(tags=["edge_couplers"])
def edge_coupler_array_with_loopback(
    edge_coupler: ComponentSpec = "edge_coupler_silicon",
    cross_section: CrossSectionSpec = "strip",
    radius: float | None = None,
    n: int = 8,
    pitch: float = 127.0,
    x_reflection: bool = False,
    text: ComponentSpec | None = "text_rectangular",
    text_offset: Float2 = (0, 10),
    text_rotation: float = 0,
) -> Component:
    """Fiber array edge coupler.

    Args:
        edge_coupler: edge coupler.
        cross_section: spec.
        radius: bend radius loopback (um).
        n: number of channels.
        pitch: Fiber pitch (um).
        x_reflection: horizontal mirror.
        text: Optional text spec.
        text_offset: x, y.
        text_rotation: text rotation in degrees.
    """
    xs = gf.get_cross_section(cross_section)
    radius = radius or xs.radius

    c = Component()
    ec = edge_coupler_array(
        edge_coupler=edge_coupler,
        n=n,
        pitch=pitch,
        x_reflection=x_reflection,
        text=text,
        text_offset=text_offset,
        text_rotation=text_rotation,
    )
    ec_ref = c << ec
    if x_reflection:
        ec_ref_ports = ec_ref.ports.filter(orientation=0)
    else:
        ec_ref_ports = ec_ref.ports.filter(orientation=180)

    p1 = ec_ref_ports[0]
    p2 = ec_ref_ports[1]
    p3 = ec_ref_ports[-2]
    p4 = ec_ref_ports[-1]

    gf.routing.route_single(
        c,
        p1,
        p2,
        cross_section=cross_section,
        radius=radius,
    )
    gf.routing.route_single(
        c,
        p4,
        p3,
        cross_section=cross_section,
        radius=radius,
    )

    for i, port in enumerate(ec_ref_ports):
        if port not in [p1, p2, p3, p4]:
            c.add_port(str(i), port=port)

    c.auto_rename_ports()
    return c

edge_coupler_array_with_loopback

edge_coupler_silicon

edge_coupler_silicon(
    length: float = 100,
    width1: float = 0.5,
    width2: float = 0.2,
    with_two_ports: bool = True,
    port_names: tuple[str, str] = ("o1", "o2"),
    port_types: tuple[str, str] = (
        "optical",
        "edge_coupler",
    ),
    cross_section: CrossSectionSpec = "strip",
) -> Component

Edge coupler for silicon photonics.

Parameters:

Name Type Description Default
length float

length of the taper.

100
width1 float

width1 of the taper.

0.5
width2 float

width2 of the taper.

0.2
with_two_ports bool

add two ports.

True
port_names tuple[str, str]

tuple with port names.

('o1', 'o2')
port_types tuple[str, str]

tuple with port types.

('optical', 'edge_coupler')
cross_section CrossSectionSpec

cross_section spec.

'strip'
Source code in gdsfactory/components/edge_couplers/edge_coupler_array.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@gf.cell_with_module_name(schematic_function=taper_schematic, tags=["edge_couplers"])
def edge_coupler_silicon(
    length: float = 100,
    width1: float = 0.5,
    width2: float = 0.2,
    with_two_ports: bool = True,
    port_names: tuple[str, str] = ("o1", "o2"),
    port_types: tuple[str, str] = ("optical", "edge_coupler"),
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Edge coupler for silicon photonics.

    Args:
        length: length of the taper.
        width1: width1 of the taper.
        width2: width2 of the taper.
        with_two_ports: add two ports.
        port_names: tuple with port names.
        port_types: tuple with port types.
        cross_section: cross_section spec.

    """
    return gf.c.taper(
        width1=width1,
        width2=width2,
        length=length,
        with_two_ports=with_two_ports,
        port_names=port_names,
        port_types=port_types,
        cross_section=cross_section,
    )

edge_coupler_silicon

filters

awg

Sample AWG.

awg

awg(
    arms: int = 10,
    outputs: int = 3,
    free_propagation_region_input_function: ComponentSpec = free_propagation_region_input,
    free_propagation_region_output_function: ComponentSpec = free_propagation_region_output,
    fpr_spacing: float = 50.0,
    arm_spacing: float = 1.0,
    length_increment: float = 0.0,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Returns an Arrayed Waveguide grating.

To simulate you can use https://github.com/dnrobin/awg-python

Parameters:

Name Type Description Default
arms int

number of arms.

10
outputs int

number of outputs.

3
free_propagation_region_input_function ComponentSpec

for input.

free_propagation_region_input
free_propagation_region_output_function ComponentSpec

for output.

free_propagation_region_output
fpr_spacing float

x separation between input/output free propagation region.

50.0
arm_spacing float

y separation between arms (used when length_increment == 0).

1.0
length_increment float

constant length step dL (um); when > 0 the arms form a nested fan where arm i is exactly i*dL longer than arm 0 -- the property that makes an AWG disperse light by wavelength.

0.0
cross_section CrossSectionSpec

cross_section function.

'strip'
Source code in gdsfactory/components/filters/awg.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
@gf.cell_with_module_name(tags=["filters"])
def awg(
    arms: int = 10,
    outputs: int = 3,
    free_propagation_region_input_function: ComponentSpec = free_propagation_region_input,
    free_propagation_region_output_function: ComponentSpec = free_propagation_region_output,
    fpr_spacing: float = 50.0,
    arm_spacing: float = 1.0,
    length_increment: float = 0.0,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Returns an Arrayed Waveguide grating.

    To simulate you can use
    https://github.com/dnrobin/awg-python

    Args:
        arms: number of arms.
        outputs: number of outputs.
        free_propagation_region_input_function: for input.
        free_propagation_region_output_function: for output.
        fpr_spacing: x separation between input/output free propagation region.
        arm_spacing: y separation between arms (used when length_increment == 0).
        length_increment: constant length step dL (um); when > 0 the arms form a
            nested fan where arm i is exactly i*dL longer than arm 0 -- the property
            that makes an AWG disperse light by wavelength.
        cross_section: cross_section function.
    """
    c = Component()
    fpr_in = gf.get_component(
        free_propagation_region_input_function,
        inputs=1,
        outputs=arms,
        cross_section=cross_section,
    )
    fpr_out = gf.get_component(
        free_propagation_region_output_function,
        inputs=outputs,
        outputs=arms,
        cross_section=cross_section,
    )

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

    if length_increment <= 0:
        fpr_in_ref.rotate(90)
        fpr_out_ref.rotate(90)
        fpr_out_ref.x += fpr_spacing
        _ = gf.routing.route_bundle(
            c,
            gf.port.get_ports_list(fpr_out_ref, prefix="E"),
            gf.port.get_ports_list(fpr_in_ref, prefix="E"),
            sort_ports=True,
            separation=arm_spacing,
            cross_section=cross_section,
        )
    else:
        fpr_out_ref.mirror_x()
        e0 = fpr_in_ref.ports["E0"]
        fpr_out_ref.movex(e0.x + fpr_spacing - fpr_out_ref.ports["E0"].x)
        fpr_out_ref.movey(e0.y - fpr_out_ref.ports["E0"].y)
        gap = fpr_out_ref.ports["E0"].x - e0.x
        xs = gf.get_cross_section(cross_section)
        bend_radius = xs.radius or 10.0
        margin = max(4.0, bend_radius)
        max_rise = (gap - 2 * bend_radius) / 2
        stagger = max(0.0, min(4.0, (max_rise - margin) / max(arms - 2, 1)))
        lengths: list[float] = []
        for i in range(arms):
            p_in = fpr_in_ref.ports[f"E{i}"]
            p_out = fpr_out_ref.ports[f"E{i}"]
            rise_x = margin + (arms - 1 - i) * stagger
            h = i * length_increment / 2.0
            if h > 0:
                steps: list[Step] = [
                    {"dx": rise_x},
                    {"dy": h},
                    {"dx": gap - 2 * rise_x},
                    {"dy": -h},
                ]
                route = gf.routing.route_single(
                    c, p_in, p_out, cross_section=cross_section, steps=steps
                )
            else:
                route = gf.routing.route_single(
                    c, p_in, p_out, cross_section=cross_section
                )
            lengths.append(route.length_backbone / 1000.0)
        c.info["arm_lengths"] = [round(x, 4) for x in lengths]

    c.add_port("o1", port=fpr_in_ref.ports["o1"])
    for i, port in enumerate(gf.port.get_ports_list(fpr_out_ref, prefix="W")):
        c.add_port(f"E{i}", port=port)

    return c

free_propagation_region

free_propagation_region(
    width1: float = 2.0,
    width2: float = 20.0,
    length: float = 20.0,
    wg_width: float = 0.5,
    inputs: int = 1,
    outputs: int = 10,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Free propagation region.

Parameters:

Name Type Description Default
width1 float

width of the input region.

2.0
width2 float

width of the output region.

20.0
length float

length of the free propagation region.

20.0
wg_width float

waveguide width.

0.5
inputs int

number of inputs.

1
outputs int

number of outputs.

10
cross_section CrossSectionSpec

cross_section function.

 length
 <-->
   /|
  / |

width1| | width2 \ | |

'strip'
Source code in gdsfactory/components/filters/awg.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@gf.cell_with_module_name(tags=["filters"])
def free_propagation_region(
    width1: float = 2.0,
    width2: float = 20.0,
    length: float = 20.0,
    wg_width: float = 0.5,
    inputs: int = 1,
    outputs: int = 10,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""Free propagation region.

    Args:
        width1: width of the input region.
        width2: width of the output region.
        length: length of the free propagation region.
        wg_width: waveguide width.
        inputs: number of inputs.
        outputs: number of outputs.
        cross_section: cross_section function.

                 length
                 <-->
                   /|
                  / |
           width1|  | width2
                  \ |
                   \|
    """
    y1 = width1 / 2
    y2 = width2 / 2
    xs = gf.get_cross_section(cross_section)
    layer = xs.layer
    assert layer is not None

    xpts = [0, length, length, 0]
    ypts = [y1, y2, -y2, -y1]

    c = gf.Component()
    c.add_polygon(list(zip(xpts, ypts, strict=False)), layer=layer)

    if inputs == 1:
        c.add_port(
            "o1",
            center=(0, 0),
            width=wg_width,
            orientation=180,
            layer=layer,
        )
    else:
        y = np.linspace(-width1 / 2 + wg_width / 2, width1 / 2 - wg_width / 2, inputs)
        y = gf.snap.snap_to_grid(y)
        for i, yi in enumerate(y):
            c.add_port(
                f"W{i}",
                center=(0, float(yi)),
                width=wg_width,
                orientation=180,
                layer=layer,
            )

    y = np.linspace(-width2 / 2 + wg_width / 2, width2 / 2 - wg_width / 2, outputs)
    y = gf.snap.snap_to_grid(y)
    for i, yi in enumerate(y):
        c.add_port(
            f"E{i}",
            center=(length, float(yi)),
            width=wg_width,
            orientation=0,
            layer=layer,
        )

    c.info["length"] = length
    c.info["width1"] = width1
    c.info["width2"] = width2
    return c

awg

dbr

DBR gratings.

wavelength = 2periodneff period = wavelength/2/neff

dbr default parameters are from Stephen Lin thesis https://open.library.ubc.ca/cIRcle/collections/ubctheses/24/items/1.0388871

Period: 318nm, width: 500nm, dw: 20 ~ 120 nm.

dbr

dbr(
    w1: float = w1,
    w2: float = w2,
    l1: float = period / 2,
    l2: float = period / 2,
    n: int = 10,
    cross_section: CrossSectionSpec = "strip",
    straight_length: float = 0.01,
) -> Component

Distributed Bragg Reflector.

Parameters:

Name Type Description Default
w1 float

thin width in um.

w1
w2 float

thick width in um.

w2
l1 float

thin length in um.

period / 2
l2 float

thick length in um.

period / 2
n int

number of periods.

10
cross_section CrossSectionSpec

cross_section spec.

'strip'
straight_length float

length of the straight section between cutbacks.

l1 l2

0.01
Source code in gdsfactory/components/filters/dbr.py
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
@gf.cell_with_module_name(tags=["filters"])
def dbr(
    w1: float = w1,
    w2: float = w2,
    l1: float = period / 2,
    l2: float = period / 2,
    n: int = 10,
    cross_section: CrossSectionSpec = "strip",
    straight_length: float = 10e-3,
) -> Component:
    """Distributed Bragg Reflector.

    Args:
        w1: thin width in um.
        w2: thick width in um.
        l1: thin length in um.
        l2: thick length in um.
        n: number of periods.
        cross_section: cross_section spec.
        straight_length: length of the straight section between cutbacks.

           l1      l2
        <-----><-------->
                _________
        _______|

          w1       w2       ...  n times
        _______
               |_________
    """
    c = Component()
    xs = gf.get_cross_section(cross_section)
    s1 = c << gf.c.straight(cross_section=xs, length=straight_length)
    s2 = c << gf.c.straight(cross_section=xs, length=straight_length)

    cell = dbr_cell(w1=w1, w2=w2, l1=l1, l2=l2, cross_section=cross_section)
    ref = c.add_ref(cell, columns=n, rows=1, column_pitch=l1 + l2)

    s1.connect(port="o1", other=cell.ports["o1"], allow_width_mismatch=True)
    s2.connect(port="o1", other=cell.ports["o2"], allow_width_mismatch=True)
    s2.xmin = ref.xmax

    c.add_port("o1", port=s1.ports["o2"])
    return c

dbr_cell

dbr_cell(
    w1: float = w1,
    w2: float = w2,
    l1: float = period / 2,
    l2: float = period / 2,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Distributed Bragg Reflector unit cell.

Parameters:

Name Type Description Default
w1 float

thin width in um.

w1
l1 float

thin length in um.

period / 2
w2 float

thick width in um.

w2
l2 float

thick length in um.

period / 2
n

number of periods.

required
cross_section CrossSectionSpec

cross_section spec.

l1 l2

'strip'
Source code in gdsfactory/components/filters/dbr.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@gf.cell_with_module_name(tags=["filters"])
def dbr_cell(
    w1: float = w1,
    w2: float = w2,
    l1: float = period / 2,
    l2: float = period / 2,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Distributed Bragg Reflector unit cell.

    Args:
        w1: thin width in um.
        l1: thin length in um.
        w2: thick width in um.
        l2: thick length in um.
        n: number of periods.
        cross_section: cross_section spec.

           l1      l2
        <-----><-------->
                _________
        _______|

          w1       w2
        _______
               |_________
    """
    l1 = snap_to_grid(l1)
    l2 = snap_to_grid(l2)
    w1 = snap_to_grid(w1, 2)
    w2 = snap_to_grid(w2, 2)
    xs1 = gf.get_cross_section(cross_section, width=w1)
    xs2 = gf.get_cross_section(cross_section, width=w2)

    c = Component()
    c1 = c << gf.c.straight(length=l1, cross_section=xs1)
    c2 = c << gf.c.straight(length=l2, cross_section=xs2)
    c2.connect(port="o1", other=c1.ports["o2"], allow_width_mismatch=True)
    c.add_port("o1", port=c1.ports["o1"])
    c.add_port("o2", port=c2.ports["o2"])
    c.flatten()
    return c

dbr

dbr_cell

dbr_cell(
    w1: float = w1,
    w2: float = w2,
    l1: float = period / 2,
    l2: float = period / 2,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Distributed Bragg Reflector unit cell.

Parameters:

Name Type Description Default
w1 float

thin width in um.

w1
l1 float

thin length in um.

period / 2
w2 float

thick width in um.

w2
l2 float

thick length in um.

period / 2
n

number of periods.

required
cross_section CrossSectionSpec

cross_section spec.

l1 l2

'strip'
Source code in gdsfactory/components/filters/dbr.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@gf.cell_with_module_name(tags=["filters"])
def dbr_cell(
    w1: float = w1,
    w2: float = w2,
    l1: float = period / 2,
    l2: float = period / 2,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Distributed Bragg Reflector unit cell.

    Args:
        w1: thin width in um.
        l1: thin length in um.
        w2: thick width in um.
        l2: thick length in um.
        n: number of periods.
        cross_section: cross_section spec.

           l1      l2
        <-----><-------->
                _________
        _______|

          w1       w2
        _______
               |_________
    """
    l1 = snap_to_grid(l1)
    l2 = snap_to_grid(l2)
    w1 = snap_to_grid(w1, 2)
    w2 = snap_to_grid(w2, 2)
    xs1 = gf.get_cross_section(cross_section, width=w1)
    xs2 = gf.get_cross_section(cross_section, width=w2)

    c = Component()
    c1 = c << gf.c.straight(length=l1, cross_section=xs1)
    c2 = c << gf.c.straight(length=l2, cross_section=xs2)
    c2.connect(port="o1", other=c1.ports["o2"], allow_width_mismatch=True)
    c.add_port("o1", port=c1.ports["o1"])
    c.add_port("o2", port=c2.ports["o2"])
    c.flatten()
    return c

dbr_cell

dbr_tapered

dbr_tapered

dbr_tapered(
    length: float = 10.0,
    period: float = 0.85,
    dc: float = 0.5,
    w1: float = 0.4,
    w2: float = 1.0,
    taper_length: float = 20.0,
    fins: bool = False,
    fin_size: Size = (0.2, 0.05),
    cross_section: CrossSectionSpec = "strip",
) -> Component

Distributed Bragg Reflector Cell class.

Tapers the input straight to a periodic straight structure with varying width (1-D photonic crystal).

Parameters:

Name Type Description Default
length float

Length of the DBR region.

10.0
period float

Period of the repeated unit.

0.85
dc float

Duty cycle of the repeated unit (must be a float between 0 and 1.0).

0.5
w1 float

thin section width. w1 = 0 corresponds to disconnected periodic blocks.

0.4
w2 float

wide section width.

1.0
taper_length float

between the input/output straight and the DBR region.

20.0
fins bool

If True, adds fins to the input/output straights.

False
fin_size Size

Specifies the x- and y-size of the fins. Defaults to 200 nm x 50 nm

(0.2, 0.05)
cross_section CrossSectionSpec

cross_section spec.

   period

<-----><--------> ___ _|

w1 w2 ... n times


 |_________
'strip'
Source code in gdsfactory/components/filters/dbr_tapered.py
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
@gf.cell_with_module_name(tags=["filters"])
def dbr_tapered(
    length: float = 10.0,
    period: float = 0.85,
    dc: float = 0.5,
    w1: float = 0.4,
    w2: float = 1.0,
    taper_length: float = 20.0,
    fins: bool = False,
    fin_size: Size = (0.2, 0.05),
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Distributed Bragg Reflector Cell class.

    Tapers the input straight to a
    periodic straight structure with varying width (1-D photonic crystal).

    Args:
       length: Length of the DBR region.
       period: Period of the repeated unit.
       dc: Duty cycle of the repeated unit (must be a float between 0 and 1.0).
       w1: thin section width. w1 = 0 corresponds to disconnected periodic blocks.
       w2: wide section width.
       taper_length: between the input/output straight and the DBR region.
       fins: If `True`, adds fins to the input/output straights.
       fin_size: Specifies the x- and y-size of the `fins`. Defaults to 200 nm x 50 nm
       cross_section: cross_section spec.

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

          w1       w2       ...  n times
        _______
               |_________
    """
    c = gf.Component()

    xs = gf.get_cross_section(cross_section=cross_section, width=w2)

    input_taper = c << gf.components.taper(
        length=taper_length,
        width1=xs.width,
        width2=w1,
        cross_section=cross_section,
    )

    straight = c << gf.components.straight(
        length=length, cross_section=cross_section, width=w1
    )
    straight.x = 0
    straight.y = 0

    output_taper = c << gf.components.taper(
        length=taper_length,
        width1=w1,
        width2=xs.width,
        cross_section=cross_section,
    )

    input_taper.connect("o2", straight.ports["o1"])
    output_taper.connect("o1", straight.ports["o2"])
    num = (2 * taper_length + length) // period

    size = cast("tuple[float, float]", tuple(snap_to_grid2x((period * dc, w2))))
    assert xs.layer is not None
    teeth = gf.components.rectangle(size=size, layer=xs.layer, port_type=None)

    periodic_structures = c << gf.components.array(
        component=teeth, columns=int(num), column_pitch=period
    )
    periodic_structures.x = 0
    periodic_structures.y = 0

    if fins:
        _generate_fins(
            c=c,
            fin_size=fin_size,
            taper_length=taper_length,
            length=length,
            cross_section=xs,
        )

    xs.add_bbox(c)
    c.add_port("o1", port=input_taper.ports["o1"])
    c.add_port("o2", port=output_taper.ports["o2"])
    return c

dbr_tapered

fiber

fiber

fiber(
    core_diameter: float = 10,
    cladding_diameter: float = 125,
    layer_core: LayerSpec = "WG",
    layer_cladding: LayerSpec = "WGCLAD",
) -> Component

Returns a fiber.

Parameters:

Name Type Description Default
core_diameter float

in um.

10
cladding_diameter float

in um.

125
layer_core LayerSpec

layer spec for fiber core.

'WG'
layer_cladding LayerSpec

layer spec for fiber cladding.

'WGCLAD'
Source code in gdsfactory/components/filters/fiber.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@gf.cell_with_module_name(tags=["filters"])
def fiber(
    core_diameter: float = 10,
    cladding_diameter: float = 125,
    layer_core: LayerSpec = "WG",
    layer_cladding: LayerSpec = "WGCLAD",
) -> Component:
    """Returns a fiber.

    Args:
        core_diameter: in um.
        cladding_diameter: in um.
        layer_core: layer spec for fiber core.
        layer_cladding: layer spec for fiber cladding.
    """
    c = Component()

    c.add_ref(circle(radius=core_diameter / 2, layer=layer_core))
    c.add_ref(circle(radius=cladding_diameter / 2, layer=layer_cladding))

    layer_core = gf.get_layer(layer_core)
    c.add_port(
        name="F0", width=core_diameter, orientation=0, center=(0, 0), layer=layer_core
    )
    return c

fiber

fiber_array

fiber_array

fiber_array(
    n: int = 8,
    pitch: float = 127.0,
    core_diameter: float = 10,
    cladding_diameter: float = 125,
    layer_core: LayerSpec = "WG",
    layer_cladding: LayerSpec = "WGCLAD",
) -> Component

Returns a fiber array.

Parameters:

Name Type Description Default
n int

number of fibers.

8
pitch float

spacing.

127.0
core_diameter float

10um.

10
cladding_diameter float

in um.

125
layer_core LayerSpec

layer spec for fiber core.

'WG'
layer_cladding LayerSpec

layer spec for fiber cladding.

'WGCLAD'
    pitch
     <->
    _________
   |         | lid
   | o o o o |
   |         | base
   |_________|
      length
Source code in gdsfactory/components/filters/fiber_array.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@gf.cell_with_module_name(tags=["filters"])
def fiber_array(
    n: int = 8,
    pitch: float = 127.0,
    core_diameter: float = 10,
    cladding_diameter: float = 125,
    layer_core: LayerSpec = "WG",
    layer_cladding: LayerSpec = "WGCLAD",
) -> Component:
    """Returns a fiber array.

    Args:
        n: number of fibers.
        pitch: spacing.
        core_diameter: 10um.
        cladding_diameter: in um.
        layer_core: layer spec for fiber core.
        layer_cladding: layer spec for fiber cladding.

    ```text
        pitch
         <->
        _________
       |         | lid
       | o o o o |
       |         | base
       |_________|
          length
    ```
    """
    c = Component()
    layer_core = gf.get_layer(layer_core)

    for i in range(n):
        core = c.add_ref(circle(radius=core_diameter / 2, layer=layer_core))
        cladding = c.add_ref(circle(radius=cladding_diameter / 2, layer=layer_cladding))
        core.movex(i * pitch)
        cladding.movex(i * pitch)
        c.add_port(
            name=f"F{i}",
            width=core_diameter,
            orientation=0,
            layer=layer_core,
            center=(i * pitch, 0),
        )

    return c

fiber_array

free_propagation_region

free_propagation_region(
    width1: float = 2.0,
    width2: float = 20.0,
    length: float = 20.0,
    wg_width: float = 0.5,
    inputs: int = 1,
    outputs: int = 10,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Free propagation region.

Parameters:

Name Type Description Default
width1 float

width of the input region.

2.0
width2 float

width of the output region.

20.0
length float

length of the free propagation region.

20.0
wg_width float

waveguide width.

0.5
inputs int

number of inputs.

1
outputs int

number of outputs.

10
cross_section CrossSectionSpec

cross_section function.

 length
 <-->
   /|
  / |

width1| | width2 \ | |

'strip'
Source code in gdsfactory/components/filters/awg.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@gf.cell_with_module_name(tags=["filters"])
def free_propagation_region(
    width1: float = 2.0,
    width2: float = 20.0,
    length: float = 20.0,
    wg_width: float = 0.5,
    inputs: int = 1,
    outputs: int = 10,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""Free propagation region.

    Args:
        width1: width of the input region.
        width2: width of the output region.
        length: length of the free propagation region.
        wg_width: waveguide width.
        inputs: number of inputs.
        outputs: number of outputs.
        cross_section: cross_section function.

                 length
                 <-->
                   /|
                  / |
           width1|  | width2
                  \ |
                   \|
    """
    y1 = width1 / 2
    y2 = width2 / 2
    xs = gf.get_cross_section(cross_section)
    layer = xs.layer
    assert layer is not None

    xpts = [0, length, length, 0]
    ypts = [y1, y2, -y2, -y1]

    c = gf.Component()
    c.add_polygon(list(zip(xpts, ypts, strict=False)), layer=layer)

    if inputs == 1:
        c.add_port(
            "o1",
            center=(0, 0),
            width=wg_width,
            orientation=180,
            layer=layer,
        )
    else:
        y = np.linspace(-width1 / 2 + wg_width / 2, width1 / 2 - wg_width / 2, inputs)
        y = gf.snap.snap_to_grid(y)
        for i, yi in enumerate(y):
            c.add_port(
                f"W{i}",
                center=(0, float(yi)),
                width=wg_width,
                orientation=180,
                layer=layer,
            )

    y = np.linspace(-width2 / 2 + wg_width / 2, width2 / 2 - wg_width / 2, outputs)
    y = gf.snap.snap_to_grid(y)
    for i, yi in enumerate(y):
        c.add_port(
            f"E{i}",
            center=(length, float(yi)),
            width=wg_width,
            orientation=0,
            layer=layer,
        )

    c.info["length"] = length
    c.info["width1"] = width1
    c.info["width2"] = width2
    return c

free_propagation_region

loop_mirror

Sagnac loop_mirror.

loop_mirror

loop_mirror(
    component: ComponentSpec = "mmi1x2",
    bend90: ComponentSpec = "bend_euler",
    cross_section: CrossSectionSpec = "strip",
) -> Component

Returns Sagnac loop_mirror.

Parameters:

Name Type Description Default
component ComponentSpec

1x2 splitter.

'mmi1x2'
bend90 ComponentSpec

90 deg bend.

'bend_euler'
cross_section CrossSectionSpec

cross_section settings.

'strip'
Source code in gdsfactory/components/filters/loop_mirror.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@gf.cell_with_module_name(tags=["filters"])
def loop_mirror(
    component: ComponentSpec = "mmi1x2",
    bend90: ComponentSpec = "bend_euler",
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Returns Sagnac loop_mirror.

    Args:
        component: 1x2 splitter.
        bend90: 90 deg bend.
        cross_section: cross_section settings.

    """
    c = Component()
    component = gf.get_component(component)
    bend90 = gf.get_component(bend90)
    cref = c.add_ref(component)
    gf.routing.route_single(
        c,
        cref.ports["o3"],
        cref.ports["o2"],
        straight=gf.components.straight,
        bend=bend90,
        cross_section=cross_section,
    )
    c.add_port(name="o1", port=cref.ports["o1"])
    return c

loop_mirror

mode_converter

mode_converter

mode_converter(
    gap: float = 0.3,
    length: float = 10,
    coupler_straight_asymmetric: ComponentSpec = "coupler_straight_asymmetric",
    bend: ComponentSpec = partial(bend_s, size=(25, 3)),
    taper: ComponentSpec = "taper",
    mm_width: float = 1.2,
    mc_mm_width: float = 1,
    sm_width: float = 0.5,
    taper_length: float = 25,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Returns Mode converter from TE0 to TE1.

By matching the effective indices of two waveguides with different widths, light can couple from different transverse modes e.g. TE0 <-> TE1. https://doi.org/10.1109/JPHOT.2019.2941742

Parameters:

Name Type Description Default
gap float

directional coupler gap.

0.3
length float

coupler length interaction.

10
coupler_straight_asymmetric ComponentSpec

spec.

'coupler_straight_asymmetric'
bend ComponentSpec

spec.

partial(bend_s, size=(25, 3))
taper ComponentSpec

spec.

'taper'
mm_width float

input/output multimode waveguide width.

1.2
mc_mm_width float

mode converter multimode waveguide width

1
sm_width float

single mode waveguide width.

0.5
taper_length float

taper length.

25
cross_section CrossSectionSpec

cross_section spec.

'strip'
=

multimode width

required
-

singlemode width

required
Source code in gdsfactory/components/filters/mode_converter.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
@gf.cell_with_module_name(schematic_function=ckt_schematic, tags=["filters"])
def mode_converter(
    gap: float = 0.3,
    length: float = 10,
    coupler_straight_asymmetric: ComponentSpec = "coupler_straight_asymmetric",
    bend: ComponentSpec = partial(bend_s, size=(25, 3)),
    taper: ComponentSpec = "taper",
    mm_width: float = 1.2,
    mc_mm_width: float = 1,
    sm_width: float = 0.5,
    taper_length: float = 25,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""Returns Mode converter from TE0 to TE1.

    By matching the effective indices of two waveguides with different widths,
    light can couple from different transverse modes e.g. TE0 <-> TE1.
    https://doi.org/10.1109/JPHOT.2019.2941742

    Args:
        gap: directional coupler gap.
        length: coupler length interaction.
        coupler_straight_asymmetric: spec.
        bend: spec.
        taper: spec.
        mm_width: input/output multimode waveguide width.
        mc_mm_width: mode converter multimode waveguide width
        sm_width: single mode waveguide width.
        taper_length: taper length.
        cross_section: cross_section spec.

        o2 ---           --- o4
              \         /
               \       /
                -------
        o1 -----=======----- o3
                |-----|
                length

        = : multimode width
        - : singlemode width
    """
    c = Component()

    coupler = gf.get_component(
        coupler_straight_asymmetric,
        length=length,
        gap=gap,
        width_bot=mc_mm_width,
        width_top=sm_width,
        cross_section=cross_section,
    )

    bend = gf.get_component(bend, cross_section=cross_section)

    bot_taper = gf.get_component(
        taper,
        width1=mc_mm_width,
        width2=mm_width,
        length=taper_length,
        cross_section=cross_section,
    )

    # directional coupler
    dc = c << coupler

    # straight waveguides at the bottom
    l_bot_straight = c << bot_taper
    r_bot_straight = c << bot_taper

    l_bot_straight.connect("o1", dc.ports["o1"])
    r_bot_straight.connect("o1", dc.ports["o4"])

    # top right bend with termination
    r_bend = c << bend
    l_bend = c << bend

    l_bend.connect("o1", dc.ports["o2"], mirror=True)
    r_bend.connect("o1", dc.ports["o3"])

    # define ports of mode converter
    c.add_port("o1", port=l_bot_straight.ports["o2"])
    c.add_port("o3", port=r_bot_straight.ports["o2"])
    c.add_port("o2", port=l_bend.ports["o2"])
    c.add_port("o4", port=r_bend.ports["o2"])
    return c

mode_converter

polarization_splitter_rotator

polarization_splitter_rotator

polarization_splitter_rotator(
    width_taper_in: Float3 = (0.54, 0.69, 0.83),
    length_taper_in: Float2 | Float3 = (4.0, 44.0),
    width_coupler: Float2 = (0.9, 0.404),
    length_coupler: float = 7.0,
    gap: float = 0.15,
    width_out: float = 0.54,
    length_out: float = 14.33,
    dy: Delta = 5.0,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Returns polarization splitter rotator.

"Novel concept for ultracompact polarization splitter-rotator based on silicon nanowires." By D. Dai, and J. E. Bowers (Optics express vol 19, no. 11 pp. 10940-10949 (2011)).

Parameters:

Name Type Description Default
width_taper_in Float3

Three west widths of the input tapers in um.

(0.54, 0.69, 0.83)
length_taper_in Float2 | Float3

Two or three length of the bend regions in um.

(4.0, 44.0)
width_coupler Float2

Top and bottom widths of the coupling region in um.

(0.9, 0.404)
length_coupler float

Length of the coupling region in um.

7.0
gap float

Distance between the coupler in um.

0.15
width_out float

Width of the splitter region in um.

0.54
length_out float

Length of the splitter region in um.

14.33
dy Delta

Port-to-port distance between the splitter region in um.

5.0
cross_section CrossSectionSpec

cross-section spec.

'strip'
Notes

The length of third input taper is automatically determined if only two lengths are in arguments.

Source code in gdsfactory/components/filters/polarization_splitter_rotator.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
@gf.cell_with_module_name(tags=["filters"])
def polarization_splitter_rotator(
    width_taper_in: Float3 = (0.54, 0.69, 0.83),
    length_taper_in: Float2 | Float3 = (4.0, 44.0),
    width_coupler: Float2 = (0.9, 0.404),
    length_coupler: float = 7.0,
    gap: float = 0.15,
    width_out: float = 0.54,
    length_out: float = 14.33,
    dy: Delta = 5.0,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Returns polarization splitter rotator.

    "Novel concept for ultracompact polarization splitter-rotator
    based on silicon nanowires." By D. Dai, and J. E. Bowers
    (Optics express vol 19, no. 11 pp. 10940-10949 (2011)).

    Args:
        width_taper_in: Three west widths of the input tapers in um.
        length_taper_in: Two or three length of the bend regions in um.
        width_coupler: Top and bottom widths of the coupling region in um.
        length_coupler: Length of the coupling region in um.
        gap: Distance between the coupler in um.
        width_out: Width of the splitter region in um.
        length_out: Length of the splitter region in um.
        dy: Port-to-port distance between the splitter region in um.
        cross_section: cross-section spec.


    Notes:
        The length of third input taper is automatically determined
        if only two lengths are in arguments.
    """
    c = gf.Component()
    x = gf.get_cross_section(cross_section=cross_section)

    w0, w1, w2 = width_taper_in
    w3, w4 = width_coupler
    if len(length_taper_in) == 2:
        l1, l2 = length_taper_in
        l3 = l1 * (w3 - w2) / (w1 - w0)
    else:
        l1, l2, l3 = length_taper_in

    taper_in1 = c << gf.c.taper(
        length=l1, width1=w0, width2=w1, cross_section=cross_section
    )
    taper_in2 = c << gf.c.taper(
        length=l2, width1=w1, width2=w2, cross_section=cross_section
    )
    taper_in3 = c << gf.c.taper(
        length=l3, width1=w2, width2=w3, cross_section=cross_section
    )

    coupler = c << gf.c.coupler_straight_asymmetric(
        length=length_coupler,
        gap=gap,
        width_top=w4,
        width_bot=w3,
        cross_section=cross_section,
    )

    def bend_s_width(t: npt.NDArray[np.floating[Any]]) -> npt.NDArray[np.floating[Any]]:
        return w4 + (width_out - w4) * t

    x_bend = x.copy(width_function=bend_s_width)

    bend_s_var = c << bezier(
        control_points=(
            (0, 0),
            (length_out / 2, 0),
            (length_out / 2, dy),
            (length_out, dy),
        ),
        cross_section=x_bend,
    )

    taper_out = c << gf.c.taper(
        length=length_out, width1=w3, width2=width_out, cross_section=cross_section
    )

    taper_in3.connect("o2", other=coupler.ports["o1"])
    taper_in2.connect("o2", other=taper_in3.ports["o1"])
    taper_in1.connect("o2", other=taper_in2.ports["o1"])
    taper_out.connect("o1", other=coupler.ports["o4"])
    bend_s_var.connect("o1", other=coupler.ports["o3"])

    c.add_port("o1", port=taper_in1.ports["o1"])
    c.add_port("o2", port=bend_s_var.ports["o2"])
    c.add_port("o3", port=taper_out.ports["o2"])

    c.auto_rename_ports()
    c.flatten()
    return c

polarization_splitter_rotator

terminator

terminator

terminator(
    length: float | None = 50,
    cross_section_input: CrossSectionSpec = strip,
    cross_section_tip: CrossSectionSpec | None = None,
    tapered_width: float = 0.2,
    doping_layers: LayerSpecs = ("NPP",),
    doping_offset: float = 1.0,
) -> gf.Component

Returns doped taper to terminate waveguides.

Parameters:

Name Type Description Default
length float | None

distance between input and narrow tapered end.

50
cross_section_input CrossSectionSpec

input cross-section.

strip
cross_section_tip CrossSectionSpec | None

cross-section at the end of the termination.

None
tapered_width float

width of the default cross-section at the end of the termination. Only used if cross_section_tip is not None.

0.2
doping_layers LayerSpecs

doping layers to superimpose on the taper. Default N++.

('NPP',)
doping_offset float

offset of the doping layer beyond the bbox

1.0
Source code in gdsfactory/components/filters/terminator.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@gf.cell_with_module_name(schematic_function=terminator_schematic, tags=["filters"])
def terminator(
    length: float | None = 50,
    cross_section_input: CrossSectionSpec = strip,
    cross_section_tip: CrossSectionSpec | None = None,
    tapered_width: float = 0.2,
    doping_layers: LayerSpecs = ("NPP",),
    doping_offset: float = 1.0,
) -> gf.Component:
    """Returns doped taper to terminate waveguides.

    Args:
        length: distance between input and narrow tapered end.
        cross_section_input: input cross-section.
        cross_section_tip: cross-section at the end of the termination.
        tapered_width: width of the default cross-section at the end of the termination.
            Only used if cross_section_tip is not None.
        doping_layers: doping layers to superimpose on the taper. Default N++.
        doping_offset: offset of the doping layer beyond the bbox
    """
    c = Component()

    cross_section_tip = cross_section_tip or gf.get_cross_section(
        cross_section_input, width=tapered_width
    )

    taper = c << gf.get_component(
        gf.c.taper_cross_section,
        length=length,
        cross_section1=cross_section_input,
        cross_section2=cross_section_tip,
    )

    points = get_padding_points(
        taper, default=0, top=doping_offset, bottom=doping_offset
    )
    for layer in doping_layers:
        c.add_polygon(points, layer=layer)
    c.add_port(name="o1", port=taper.ports["o1"])
    return c

terminator

terminator_spiral

terminator_spiral

terminator_spiral(
    separation: float = 3.0,
    width_tip: float = 0.2,
    number_of_loops: float = 1,
    npoints: int = 1000,
    min_bend_radius: float | None = None,
    cross_section: CrossSectionSpec = "strip",
) -> gf.Component

Returns doped taper to terminate waveguides.

Parameters:

Name Type Description Default
separation float

separation between the loops.

3.0
width_tip float

width of the default cross-section at the end of the termination. Only used if cross_section_tip is not None.

0.2
number_of_loops float

number of loops in the spiral.

1
npoints int

points for the spiral.

1000
min_bend_radius float | None

minimum bend radius for the spiral.

None
cross_section CrossSectionSpec

input cross-section.

'strip'
Source code in gdsfactory/components/filters/terminator_spiral.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@gf.cell_with_module_name(schematic_function=terminator_schematic, tags=["filters"])
def terminator_spiral(
    separation: float = 3.0,
    width_tip: float = 0.2,
    number_of_loops: float = 1,
    npoints: int = 1000,
    min_bend_radius: float | None = None,
    cross_section: CrossSectionSpec = "strip",
) -> gf.Component:
    """Returns doped taper to terminate waveguides.

    Args:
        separation: separation between the loops.
        width_tip: width of the default cross-section at the end of the termination.
            Only used if cross_section_tip is not None.
        number_of_loops: number of loops in the spiral.
        npoints: points for the spiral.
        min_bend_radius: minimum bend radius for the spiral.
        cross_section: input cross-section.
    """
    cross_section_main = gf.get_cross_section(cross_section)
    cross_section_tip = gf.get_cross_section(cross_section, width=width_tip)

    xs = transition(
        cross_section2=cross_section_main,
        cross_section1=cross_section_tip,
        width_type="linear",
    )

    min_bend_radius = min_bend_radius or cross_section_main.radius_min
    assert min_bend_radius

    path = spiral_archimedean(
        min_bend_radius=min_bend_radius,
        separation=separation / 2,
        number_of_loops=number_of_loops,
        npoints=npoints,
    )
    path.start_angle = 0
    path.end_angle = 0

    spiral = extrude_transition(path, transition=xs)
    c = gf.Component()
    ref = c << spiral
    c.add_port("o1", port=ref["o2"])
    c.flatten()
    return c

terminator_spiral

grating_couplers

grating_coupler_array

grating_coupler_array

grating_coupler_array(
    grating_coupler: ComponentSpec = "grating_coupler_elliptical",
    pitch: float = 127.0,
    n: int = 6,
    port_name: str = "o1",
    rotation: int = -90,
    with_loopback: bool = False,
    cross_section: CrossSectionSpec = "strip",
    straight_to_grating_spacing: float = 10.0,
    centered: bool = True,
    radius: float | None = None,
    bend: ComponentSpec = "bend_euler",
    mirror_grating_coupler: bool = False,
) -> Component

Array of grating couplers.

Parameters:

Name Type Description Default
grating_coupler ComponentSpec

ComponentSpec.

'grating_coupler_elliptical'
pitch float

x spacing.

127.0
n int

number of grating couplers.

6
port_name str

port name.

'o1'
rotation int

rotation angle for each reference.

-90
with_loopback bool

if True, adds a loopback between edge GCs. Only works for rotation = 90 for now.

False
cross_section CrossSectionSpec

cross_section for the routing.

'strip'
straight_to_grating_spacing float

spacing between the last grating coupler and the loopback.

10.0
centered bool

if True, centers the array around the origin.

True
radius float | None

optional radius for routing the loopback.

None
bend ComponentSpec

ComponentSpec for the bend used in the loopback.

'bend_euler'
mirror_grating_coupler bool

if True, mirrors the grating coupler.

False
Source code in gdsfactory/components/grating_couplers/grating_coupler_array.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
@gf.cell_with_module_name(tags=["grating_couplers"])
def grating_coupler_array(
    grating_coupler: ComponentSpec = "grating_coupler_elliptical",
    pitch: float = 127.0,
    n: int = 6,
    port_name: str = "o1",
    rotation: int = -90,
    with_loopback: bool = False,
    cross_section: CrossSectionSpec = "strip",
    straight_to_grating_spacing: float = 10.0,
    centered: bool = True,
    radius: float | None = None,
    bend: ComponentSpec = "bend_euler",
    mirror_grating_coupler: bool = False,
) -> Component:
    """Array of grating couplers.

    Args:
        grating_coupler: ComponentSpec.
        pitch: x spacing.
        n: number of grating couplers.
        port_name: port name.
        rotation: rotation angle for each reference.
        with_loopback: if True, adds a loopback between edge GCs. Only works for rotation = 90 for now.
        cross_section: cross_section for the routing.
        straight_to_grating_spacing: spacing between the last grating coupler and the loopback.
        centered: if True, centers the array around the origin.
        radius: optional radius for routing the loopback.
        bend: ComponentSpec for the bend used in the loopback.
        mirror_grating_coupler: if True, mirrors the grating coupler.
    """
    c = Component()
    grating_coupler = gf.get_component(grating_coupler)
    if mirror_grating_coupler:
        grating_coupler = gf.functions.mirror(grating_coupler)
    ports: dict[str, kf.DPort] = {}

    for i in range(n):
        gc = c << grating_coupler
        gc.rotate(rotation)
        gc.x = (i - (n - 1) / 2) * pitch if centered else i * pitch
        port_name_new = f"o{i}"
        ports[port_name_new] = gc.ports[port_name]
        if not with_loopback or i not in [0, n - 1]:
            c.add_port(port=gc.ports[port_name], name=port_name_new)

    if with_loopback:
        if rotation != -90:
            raise ValueError(
                f"with_loopback works only with rotation = -90, got {rotation=}"
            )
        routing_xs = gf.get_cross_section(cross_section)
        radius = radius or routing_xs.radius
        if radius is None:
            bend_component = gf.get_component(bend, cross_section=cross_section)
            try:
                radius = _get_routing_radius(bend_component, cross_section)
                bend = bend_component
            except KeyError as err:
                raise ValueError(
                    "Radius must be set in the cross_section or bend component if not provided explicitly."
                ) from err

        port0 = ports["o0"]
        port1 = ports[f"o{n - 1}"]
        assert radius is not None
        radius_dbu = c.kcl.to_dbu(radius)
        d_loop_um = straight_to_grating_spacing + max(
            [
                grating_coupler.ysize,
                grating_coupler.xsize,
            ]
        )
        d_loop = c.kcl.to_dbu(d_loop_um) + radius_dbu

        port0 = add_auto_tapers(c, [port0], cross_section)[0]
        port1 = add_auto_tapers(c, [port1], cross_section)[0]
        waypoints = kf.routing.optical.route_loopback(
            port0.to_itype(),
            port1.to_itype(),
            bend90_radius=radius_dbu,
            d_loop=d_loop,
        )

        waypoints_ = [point.to_dtype(c.kcl.dbu) for point in waypoints]

        gf.routing.route_single(
            c,
            port0,
            port1,
            waypoints=waypoints_,
            cross_section=cross_section,
            radius=radius,
            bend=bend,
        )

    return c

grating_coupler_array

grating_coupler_dual_pol

grating_coupler_dual_pol

grating_coupler_dual_pol(
    unit_cell: ComponentSpec = _unit_cell,
    period_x: float = 0.58,
    period_y: float = 0.58,
    x_span: float = 11,
    y_span: float = 11,
    length_taper: float = 150.0,
    width_taper: float = 10.0,
    polarization: str = "te",
    wavelength: float = 1.55,
    taper: ComponentSpec = "taper",
    base_layer: LayerSpec = "WG",
    cross_section: CrossSectionSpec = "strip",
) -> Component

2 dimensional, dual polarization grating coupler.

Based on a photonic crystal with a unit cell that is usually an ellipse, a rectangle or a circle. The default values are loosely based on Taillaert et al, "A Compact Two-Dimensional Grating Coupler Used as a Polarization Splitter", IEEE Phot. Techn. Lett. 15(9), 2003.

Parameters:

Name Type Description Default
unit_cell ComponentSpec

component describing the unit cell of the photonic crystal.

_unit_cell
period_x float

spacing between unit cells in the x direction [um].

0.58
period_y float

spacing between unit cells in the y direction [um].

0.58
x_span float

full x span of the photonic crystal.

11
y_span float

full y span of the photonic crystal.

11
length_taper float

taper length [um].

150.0
width_taper float

width of the taper at the grating coupler side [um].

10.0
polarization str

polarization of the grating coupler.

'te'
wavelength float

operation wavelength [um]

1.55
taper ComponentSpec

function to generate the tapers.

'taper'
base_layer LayerSpec

layer to draw over the whole photonic crystal (necessary if the unit cells are etched into a base layer).

'WG'
cross_section CrossSectionSpec

for the routing waveguides.

'strip'
Source code in gdsfactory/components/grating_couplers/grating_coupler_dual_pol.py
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
@gf.cell_with_module_name(
    schematic_function=grating_coupler_schematic, tags=["grating_couplers"]
)
def grating_coupler_dual_pol(
    unit_cell: ComponentSpec = _unit_cell,
    period_x: float = 0.58,
    period_y: float = 0.58,
    x_span: float = 11,
    y_span: float = 11,
    length_taper: float = 150.0,
    width_taper: float = 10.0,
    polarization: str = "te",
    wavelength: float = 1.55,
    taper: ComponentSpec = "taper",
    base_layer: LayerSpec = "WG",
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""2 dimensional, dual polarization grating coupler.

    Based on a photonic crystal with a unit cell that is usually an ellipse,
    a rectangle or a circle.
    The default values are loosely based on Taillaert et al,
    "A Compact Two-Dimensional Grating Coupler Used as a Polarization Splitter",
    IEEE Phot. Techn. Lett. 15(9), 2003.

    Args:
        unit_cell: component describing the unit cell of the photonic crystal.
        period_x: spacing between unit cells in the x direction [um].
        period_y: spacing between unit cells in the y direction [um].
        x_span: full x span of the photonic crystal.
        y_span: full y span of the photonic crystal.
        length_taper: taper length [um].
        width_taper: width of the taper at the grating coupler side [um].
        polarization: polarization of the grating coupler.
        wavelength: operation wavelength [um]
        taper: function to generate the tapers.
        base_layer: layer to draw over the whole photonic crystal
            (necessary if the unit cells are etched into a base layer).
        cross_section: for the routing waveguides.

        side view
                      fiber

                   /  /  /  /
                  /  /  /  /

                _|-|_|-|_|-|___  --> unit_cells
                   base_layer |
            o1  ______________|


        top view

                   -------------
               // | o   o   o  |
        o1 __ //  | o   o   o  |
              \\  | o   o   o  |
               \\ | o   o   o  |
                   -------------
                   \\         //
                    \\       //
                         |
                         o2

    """
    xs = gf.get_cross_section(cross_section)
    wg_width = xs.width
    layer = xs.layer

    c = Component()

    _ = c << gf.c.rectangle(
        size=(x_span, y_span), layer=base_layer, centered=True, port_type=None
    )

    # Photonic crystal
    num_x = int(np.floor(x_span / period_x))
    num_y = int(np.floor(y_span / period_y))
    x_start = -(num_x * period_x) / 2
    y_start = -(num_y * period_y) / 2

    unit_cell_grating = gf.get_component(unit_cell)
    g = c.add_ref(
        unit_cell_grating,
        columns=num_x,
        rows=num_y,
        column_pitch=period_x,
        row_pitch=period_y,
    )
    g.xmin = x_start
    g.ymin = y_start

    port_type = f"vertical_{polarization.lower()}"
    c.add_port(
        name=port_type,
        port_type=port_type,
        center=(0, 0),
        orientation=0,
        width=x_span,
        layer=layer,
    )
    c.info["polarization"] = polarization
    c.info["wavelength"] = wavelength
    taper = gf.get_component(
        taper,
        length=length_taper,
        width2=width_taper,
        width1=wg_width,
        cross_section=cross_section,
    )

    taper1 = c << taper
    taper1.xmax = -x_span / 2
    taper1.y = 0
    c.add_port(port=taper1.ports["o1"], name="o1")

    taper2 = c << taper
    taper2.rotate(90)
    taper2.x = 0
    taper2.ymax = -y_span / 2
    c.add_port(port=taper2.ports["o1"], name="o2")

    xs.add_bbox(c)
    return c

grating_coupler_dual_pol

grating_coupler_elliptical

grating_coupler_elliptical

grating_coupler_elliptical(
    polarization: str = "te",
    taper_length: float = 16.6,
    taper_angle: float = 40.0,
    wavelength: float = 1.554,
    fiber_angle: float = 15.0,
    grating_line_width: float = 0.343,
    neff: float = 2.638,
    nclad: float = 1.443,
    n_periods: int = 30,
    big_last_tooth: bool = False,
    layer_slab: LayerSpec | None = "SLAB150",
    slab_xmin: float = -1.0,
    slab_offset: float = 2.0,
    spiked: bool = True,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Grating coupler with parametrization based on Lumerical FDTD simulation.

Parameters:

Name Type Description Default
polarization str

te or tm.

'te'
taper_length float

taper length from input.

16.6
taper_angle float

grating flare angle.

40.0
wavelength float

grating transmission central wavelength (um).

1.554
fiber_angle float

fibre angle in degrees determines ellipticity.

15.0
grating_line_width float

in um.

0.343
neff float

tooth effective index.

2.638
nclad float

cladding effective index.

1.443
n_periods int

number of periods.

30
big_last_tooth bool

adds a big_last_tooth.

False
layer_slab LayerSpec | None

layer that protects the slab under the grating.

'SLAB150'
slab_xmin float

where 0 is at the start of the taper.

-1.0
slab_offset float

in um.

2.0
spiked bool

grating teeth have sharp spikes to avoid non-manhattan drc errors.

True
cross_section CrossSectionSpec

specification (CrossSection, string or dict).

      fiber

   /  /  /  /
  /  /  /  /

_|-|_|-|_|-|___ layer
   layer_slab |

o1 __|

'strip'
Source code in gdsfactory/components/grating_couplers/grating_coupler_elliptical.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
@gf.cell_with_module_name(
    schematic_function=grating_coupler_schematic, tags=["grating_couplers"]
)
def grating_coupler_elliptical(
    polarization: str = "te",
    taper_length: float = 16.6,
    taper_angle: float = 40.0,
    wavelength: float = 1.554,
    fiber_angle: float = 15.0,
    grating_line_width: float = 0.343,
    neff: float = 2.638,  # tooth effective index
    nclad: float = 1.443,
    n_periods: int = 30,
    big_last_tooth: bool = False,
    layer_slab: LayerSpec | None = "SLAB150",
    slab_xmin: float = -1.0,
    slab_offset: float = 2.0,
    spiked: bool = True,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""Grating coupler with parametrization based on Lumerical FDTD simulation.

    Args:
        polarization: te or tm.
        taper_length: taper length from input.
        taper_angle: grating flare angle.
        wavelength: grating transmission central wavelength (um).
        fiber_angle: fibre angle in degrees determines ellipticity.
        grating_line_width: in um.
        neff: tooth effective index.
        nclad: cladding effective index.
        n_periods: number of periods.
        big_last_tooth: adds a big_last_tooth.
        layer_slab: layer that protects the slab under the grating.
        slab_xmin: where 0 is at the start of the taper.
        slab_offset: in um.
        spiked: grating teeth have sharp spikes to avoid non-manhattan drc errors.
        cross_section: specification (CrossSection, string or dict).

                      fiber

                   /  /  /  /
                  /  /  /  /

                _|-|_|-|_|-|___ layer
                   layer_slab |
            o1  ______________|

    """
    xs = gf.get_cross_section(cross_section)

    wg_width = xs.width
    layer = xs.layer
    assert layer is not None

    # Compute some ellipse parameters
    sthc = np.sin(fiber_angle * DEG2RAD)
    d = neff**2 - nclad**2 * sthc**2
    a1 = wavelength * neff / d
    b1 = wavelength / np.sqrt(d)
    x1 = wavelength * nclad * sthc / d

    a1 = float(round(a1, 3))
    b1 = float(round(b1, 3))
    x1 = float(round(x1, 3))

    period = a1 + x1

    c = gf.Component()
    c.info["polarization"] = polarization
    c.info["wavelength"] = wavelength

    # Make the taper
    p = taper_length / period
    a_taper = a1 * p
    b_taper = b1 * p
    x_taper = x1 * p

    x_output = a_taper + x_taper - taper_length
    pts = grating_taper_points(
        a=a_taper,
        b=b_taper,
        x0=x_output,
        taper_length=x_taper,
        taper_angle=taper_angle,
        wg_width=wg_width,
    )
    c.add_polygon(pts, layer)

    width = gf.snap.snap_to_grid(grating_line_width)
    gap = gf.snap.snap_to_grid(period - grating_line_width)

    xi = taper_length
    for p in range(n_periods):
        xi += gap + width / 2
        p = xi / period
        pts = grating_tooth_points(
            p * a1, p * b1, p * x1, width, taper_angle, spiked=spiked
        )
        c.add_polygon(pts, layer)
        xi += width / 2

    w = 1.0
    total_length = (
        period * n_periods
        + taper_length
        + grating_line_width / 2
        + period
        - grating_line_width
        + w / 2
    )

    if big_last_tooth:
        # Add last "large tooth" after the standard grating teeth
        a = total_length / (1 + x1 / a1)
        b = b1 / a1 * a
        x = x1 / a1 * a

        pts = grating_tooth_points(a, b, x, w, taper_angle, spiked=False)
        c.add_polygon(pts, layer)

    x = np.round(taper_length + x_output, 3)

    c.add_port(
        name="o1",
        center=(x_output, 0),
        width=wg_width,
        orientation=180,
        layer=layer,
        port_type="optical",
    )

    if layer_slab:
        slab_xmin += x_output + taper_length
        slab_length = total_length + slab_offset
        slab_width = (c.ysize + 2 * slab_offset) / 2
        c.add_polygon(
            [
                (slab_xmin, slab_width),
                (slab_length, slab_width),
                (slab_length, -slab_width),
                (slab_xmin, -slab_width),
            ],
            layer_slab,
        )

    xs.add_bbox(c)
    c.add_port(
        name="o2",
        center=(x, 0),
        width=10,
        orientation=0,
        layer=layer,
        port_type=f"vertical_{polarization}",
    )
    return c

grating_coupler_elliptical

grating_coupler_elliptical_arbitrary

grating_coupler_elliptical_arbitrary

grating_coupler_elliptical_arbitrary(
    gaps: Floats = _gaps,
    widths: Floats = _widths,
    taper_length: float = 16.6,
    taper_angle: float = 60.0,
    wavelength: float = 1.554,
    fiber_angle: float = 15.0,
    nclad: float = 1.443,
    layer_slab: LayerSpec | None = "SLAB150",
    layer_grating: LayerSpec | None = None,
    taper_to_slab_offset: float = -3.0,
    polarization: str = "te",
    spiked: bool = True,
    bias_gap: float = 0,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Grating coupler with parametrization based on Lumerical FDTD simulation.

The ellipticity is derived from Lumerical knowledge base it depends on fiber_angle (degrees), neff, and nclad

Parameters:

Name Type Description Default
gaps Floats

list of gaps.

_gaps
widths Floats

list of widths.

_widths
taper_length float

taper length from input.

16.6
taper_angle float

grating flare angle.

60.0
wavelength float

grating transmission central wavelength (um).

1.554
fiber_angle float

fibre angle in degrees determines ellipticity.

15.0
nclad float

cladding effective index to compute ellipticity.

1.443
layer_slab LayerSpec | None

Optional slab.

'SLAB150'
layer_grating LayerSpec | None

Optional layer for grating. by default None uses cross_section.layer. if different from cross_section.layer expands taper.

None
taper_to_slab_offset float

0 is where taper ends.

-3.0
polarization str

te or tm.

'te'
spiked bool

grating teeth have spikes to avoid drc errors.

True
bias_gap float

etch gap (um). Positive bias increases gap and reduces width to keep period constant.

0
cross_section CrossSectionSpec

cross_section spec for waveguide port.

'strip'

https://en.wikipedia.org/wiki/Ellipse c = (a1 ** 2 - b1 ** 2) ** 0.5 e = (1 - (b1 / a1) ** 2) ** 0.5 print(e)

              fiber

           /  /  /  /
          /  /  /  /

        _|-|_|-|_|-|___ layer
           layer_slab |
    o1  ______________|
Source code in gdsfactory/components/grating_couplers/grating_coupler_elliptical_arbitrary.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
@gf.cell_with_module_name(
    schematic_function=grating_coupler_schematic, tags=["grating_couplers"]
)
def grating_coupler_elliptical_arbitrary(
    gaps: Floats = _gaps,
    widths: Floats = _widths,
    taper_length: float = 16.6,
    taper_angle: float = 60.0,
    wavelength: float = 1.554,
    fiber_angle: float = 15.0,
    nclad: float = 1.443,
    layer_slab: LayerSpec | None = "SLAB150",
    layer_grating: LayerSpec | None = None,
    taper_to_slab_offset: float = -3.0,
    polarization: str = "te",
    spiked: bool = True,
    bias_gap: float = 0,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""Grating coupler with parametrization based on Lumerical FDTD simulation.

    The ellipticity is derived from Lumerical knowledge base
    it depends on fiber_angle (degrees), neff, and nclad

    Args:
        gaps: list of gaps.
        widths: list of widths.
        taper_length: taper length from input.
        taper_angle: grating flare angle.
        wavelength: grating transmission central wavelength (um).
        fiber_angle: fibre angle in degrees determines ellipticity.
        nclad: cladding effective index to compute ellipticity.
        layer_slab: Optional slab.
        layer_grating: Optional layer for grating.
            by default None uses cross_section.layer.
            if different from cross_section.layer expands taper.
        taper_to_slab_offset: 0 is where taper ends.
        polarization: te or tm.
        spiked: grating teeth have spikes to avoid drc errors.
        bias_gap: etch gap (um).
            Positive bias increases gap and reduces width to keep period constant.
        cross_section: cross_section spec for waveguide port.

    https://en.wikipedia.org/wiki/Ellipse
    c = (a1 ** 2 - b1 ** 2) ** 0.5
    e = (1 - (b1 / a1) ** 2) ** 0.5
    print(e)

                      fiber

                   /  /  /  /
                  /  /  /  /

                _|-|_|-|_|-|___ layer
                   layer_slab |
            o1  ______________|

    """
    xs = gf.get_cross_section(cross_section)
    wg_width = xs.width
    assert xs.layer is not None
    layer_wg = gf.get_layer(xs.layer)

    layer_grating = layer_grating or layer_wg
    layer_grating = gf.get_layer(layer_grating)
    sthc = np.sin(fiber_angle * DEG2RAD)

    # generate component
    c = gf.Component()
    c.info["polarization"] = polarization
    c.info["wavelength"] = wavelength

    # get the physical parameters needed to compute ellipses
    gaps_array = gf.snap.snap_to_grid(np.array(gaps) + bias_gap)
    widths_array = gf.snap.snap_to_grid(np.array(widths) - bias_gap)
    periods = [g + w for g, w in zip(gaps_array, widths_array, strict=False)]
    neffs = [wavelength / p + nclad * sthc for p in periods]
    ds = [neff**2 - nclad**2 * sthc**2 for neff in neffs]
    a1s = [round(wavelength * neff / d, 3) for neff, d in zip(neffs, ds, strict=False)]
    b1s = [round(wavelength / np.sqrt(d), 3) for d in ds]
    x1s = [round(wavelength * nclad * sthc / d, 3) for d in ds]
    xis = np.add(
        taper_length + np.cumsum(periods), -widths_array / 2
    )  # position of middle of each tooth
    ps = np.divide(xis, periods)

    # grating teeth
    for a1, b1, x1, p, width in zip(a1s, b1s, x1s, ps, widths_array, strict=False):
        pts = grating_tooth_points(
            p * a1, p * b1, p * x1, float(width), taper_angle, spiked=spiked
        )
        c.add_polygon(pts, layer_grating)

    # taper
    p = taper_length / periods[0]  # (gaps[0]+widths[0])
    a_taper = p * a1s[0]
    b_taper = p * b1s[0]
    x_taper = p * x1s[0]
    x_output = a_taper + x_taper - taper_length + widths_array[0] / 2

    if layer_grating == layer_wg:
        pts = grating_taper_points(
            a_taper, b_taper, x_output, x_taper, taper_angle, wg_width=wg_width
        )
        c.add_polygon(pts, layer_wg)

    else:
        pts = grating_taper_points(
            a_taper,
            b_taper,
            x_output,
            x_taper + np.sum(widths_array) + np.sum(gaps_array) + 1,
            taper_angle,
            wg_width=wg_width,
        )
        c.add_polygon(pts, layer=layer_wg)

    c.add_port(
        name="o1",
        center=(x_output, 0),
        width=wg_width,
        orientation=180,
        layer=layer_wg,
        cross_section=xs,
    )

    if layer_slab:
        slab_xmin = taper_length + taper_to_slab_offset
        slab_xmax = c.xmax + 0.5
        slab_ysize = c.ysize + 2.0
        yslab = slab_ysize / 2
        c.add_polygon(
            [
                (slab_xmin, yslab),
                (slab_xmax, yslab),
                (slab_xmax, -yslab),
                (slab_xmin, -yslab),
            ],
            layer_slab,
        )

    xs.add_bbox(c)
    x = (taper_length + xis[-1]) / 2
    c.add_port(
        name="o2",
        center=(x, 0),
        width=10,
        orientation=0,
        layer=xs.layer,
        port_type=f"vertical_{polarization}",
    )
    return c

grating_coupler_elliptical_uniform

grating_coupler_elliptical_uniform(
    n_periods: int = 20,
    period: float = 0.75,
    fill_factor: float = 0.5,
    **kwargs: Any
) -> Component

Grating coupler with parametrization based on Lumerical FDTD simulation.

The ellipticity is derived from Lumerical knowledge base it depends on fiber_angle (degrees), neff, and nclad

Parameters:

Name Type Description Default
n_periods int

number of grating periods.

20
period float

grating pitch in um.

0.75
fill_factor float

ratio of grating width vs gap.

0.5

Other Parameters:

Name Type Description
taper_length

taper length from input.

taper_angle

grating flare angle.

wavelength

grating transmission central wavelength (um).

fiber_angle

fibre angle in degrees determines ellipticity.

neff

tooth effective index to compute ellipticity.

nclad

cladding effective index to compute ellipticity.

layer_slab

Optional slab.

taper_to_slab_offset

where 0 is at the start of the taper.

polarization

te or tm.

spiked

grating teeth have spikes to avoid drc errors..

bias_gap

etch gap (um). Positive bias increases gap and reduces width to keep period constant.

cross_section

cross_section spec for waveguide port.

kwargs Any

cross_section settings.

      fiber

   /  /  /  /
  /  /  /  /

_|-|_|-|_|-|___ layer
   layer_slab |

o1 __|

Source code in gdsfactory/components/grating_couplers/grating_coupler_elliptical_arbitrary.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
@gf.cell_with_module_name(
    schematic_function=grating_coupler_schematic, tags=["grating_couplers"]
)
def grating_coupler_elliptical_uniform(
    n_periods: int = 20,
    period: float = 0.75,
    fill_factor: float = 0.5,
    **kwargs: Any,
) -> Component:
    r"""Grating coupler with parametrization based on Lumerical FDTD simulation.

    The ellipticity is derived from Lumerical knowledge base
    it depends on fiber_angle (degrees), neff, and nclad

    Args:
        n_periods: number of grating periods.
        period: grating pitch in um.
        fill_factor: ratio of grating width vs gap.

    Keyword Args:
        taper_length: taper length from input.
        taper_angle: grating flare angle.
        wavelength: grating transmission central wavelength (um).
        fiber_angle: fibre angle in degrees determines ellipticity.
        neff: tooth effective index to compute ellipticity.
        nclad: cladding effective index to compute ellipticity.
        layer_slab: Optional slab.
        taper_to_slab_offset: where 0 is at the start of the taper.
        polarization: te or tm.
        spiked: grating teeth have spikes to avoid drc errors..
        bias_gap: etch gap (um).
            Positive bias increases gap and reduces width to keep period constant.
        cross_section: cross_section spec for waveguide port.
        kwargs: cross_section settings.

                      fiber

                   /  /  /  /
                  /  /  /  /

                _|-|_|-|_|-|___ layer
                   layer_slab |
            o1  ______________|

    """
    widths = (period * fill_factor,) * n_periods
    gaps = (period * (1 - fill_factor),) * n_periods
    return grating_coupler_elliptical_arbitrary(gaps=gaps, widths=widths, **kwargs)

grating_coupler_elliptical_arbitrary

grating_coupler_elliptical_lumerical

grating_coupler_elliptical_lumerical

grating_coupler_elliptical_lumerical(
    parameters: Floats = parameters,
    layer_slab: LayerSpec | None = "SLAB150",
    taper_angle: float = 55,
    taper_length: float = 12.24 + 0.36,
    fiber_angle: float = 5,
    info: dict[str, Any] | None = None,
    bias_gap: float = 0,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Returns a grating coupler from lumerical inverse design 3D optimization.

this is a wrapper of components.grating_coupler_elliptical_arbitrary https://support.lumerical.com/hc/en-us/articles/1500000306621 https://support.lumerical.com/hc/en-us/articles/360042800573

Here are the simulation settings used in lumerical

n_bg=1.44401 #Refractive index of the background material (cladding)
wg=3.47668   # Refractive index of the waveguide material (core)
lambda0=1550e-9
bandwidth = 0e-9
polarization = 'TE'
wg_width=500e-9 # Waveguide width
wg_height=220e-9 # Waveguide height
etch_depth=80e-9 # etch depth
theta_fib_mat = 5 # Angle of the fiber mode in material
theta_taper=30
efficiency=0.55 # 5.2 dB

Parameters:

Name Type Description Default
parameters Floats

xinput, gap1, width1, gap2, width2 ...

parameters
layer

for waveguide.

required
layer_slab LayerSpec | None

for slab.

'SLAB150'
taper_angle float

in deg.

55
taper_length float

in um.

12.24 + 0.36
fiber_angle float

used to compute ellipticity.

5
info dict[str, Any] | None

optional simulation settings.

None
bias_gap float

gap/trenches bias (um) to compensate for etching bias.

0

Other Parameters:

Name Type Description
taper_length float

taper length from input in um.

taper_angle float

grating flare angle in degrees.

wavelength

grating transmission central wavelength (um).

fiber_angle float

fibre angle in degrees determines ellipticity.

neff

tooth effective index.

nclad

cladding effective index.

polarization

te or tm.

spiked

grating teeth include sharp spikes to avoid non-manhattan drc errors.

cross_section CrossSectionSpec

cross_section spec for waveguide port.

Source code in gdsfactory/components/grating_couplers/grating_coupler_elliptical_lumerical.py
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
@gf.cell_with_module_name(
    schematic_function=grating_coupler_schematic, tags=["grating_couplers"]
)
def grating_coupler_elliptical_lumerical(
    parameters: Floats = parameters,
    layer_slab: LayerSpec | None = "SLAB150",
    taper_angle: float = 55,
    taper_length: float = 12.24 + 0.36,
    fiber_angle: float = 5,
    info: dict[str, Any] | None = None,
    bias_gap: float = 0,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Returns a grating coupler from lumerical inverse design 3D optimization.

    this is a wrapper of components.grating_coupler_elliptical_arbitrary
    https://support.lumerical.com/hc/en-us/articles/1500000306621
    https://support.lumerical.com/hc/en-us/articles/360042800573

    Here are the simulation settings used in lumerical

        n_bg=1.44401 #Refractive index of the background material (cladding)
        wg=3.47668   # Refractive index of the waveguide material (core)
        lambda0=1550e-9
        bandwidth = 0e-9
        polarization = 'TE'
        wg_width=500e-9 # Waveguide width
        wg_height=220e-9 # Waveguide height
        etch_depth=80e-9 # etch depth
        theta_fib_mat = 5 # Angle of the fiber mode in material
        theta_taper=30
        efficiency=0.55 # 5.2 dB

    Args:
        parameters: xinput, gap1, width1, gap2, width2 ...
        layer: for waveguide.
        layer_slab: for slab.
        taper_angle: in deg.
        taper_length: in um.
        fiber_angle: used to compute ellipticity.
        info: optional simulation settings.
        bias_gap: gap/trenches bias (um) to compensate for etching bias.

    Keyword Args:
        taper_length: taper length from input in um.
        taper_angle: grating flare angle in degrees.
        wavelength: grating transmission central wavelength (um).
        fiber_angle: fibre angle in degrees determines ellipticity.
        neff: tooth effective index.
        nclad: cladding effective index.
        polarization: te or tm.
        spiked: grating teeth include sharp spikes to avoid non-manhattan drc errors.
        cross_section: cross_section spec for waveguide port.
    """
    parameters = tuple(parameters)
    xinput = parameters[0]
    teeth_list = parameters[1:]
    gaps = teeth_list[::2]
    widths = teeth_list[1::2]
    info = info or {}
    gaps = tuple(gap + bias_gap for gap in gaps)

    c = grating_coupler_elliptical_arbitrary(
        gaps=gaps,
        widths=widths,
        taper_angle=taper_angle,
        taper_length=taper_length,
        layer_slab=layer_slab,
        fiber_angle=fiber_angle,
        cross_section=cross_section,
    )
    c.info.update(info)
    c.info["xinput"] = xinput
    return c

grating_coupler_elliptical_lumerical

grating_coupler_elliptical_lumerical_etch70 module-attribute

grating_coupler_elliptical_lumerical_etch70 = partial(
    grating_coupler_elliptical_lumerical,
    info=dict(
        etch_depth=0.08,
        link="https://support.lumerical.com/hc/en-us/articles/1500000306621",
        fiber_angle=5,
        width_min=0.1,
        gap_min=0.1,
        efficiency=0.55,
    ),
)

grating_coupler_elliptical_lumerical_etch70

grating_coupler_elliptical_trenches

grating_coupler_elliptical_trenches

grating_coupler_elliptical_trenches(
    polarization: str = "te",
    taper_length: float = 16.6,
    taper_angle: float = 30.0,
    trenches_extra_angle: float = 9.0,
    wavelength: float = 1.53,
    fiber_angle: float = 15.0,
    grating_line_width: float = 0.343,
    neff: float = 2.638,
    ncladding: float = 1.443,
    layer_trench: LayerSpec = "SHALLOW_ETCH",
    p_start: int = 26,
    n_periods: int = 30,
    end_straight_length: float = 0.2,
    taper: ComponentSpec = "taper",
    cross_section: CrossSectionSpec = "strip",
) -> Component

Returns Grating coupler with defined trenches.

Some foundries define the grating coupler by a shallow etch step (trenches) Others define the slab that they keep (see grating_coupler_elliptical)

Parameters:

Name Type Description Default
polarization str

'te' or 'tm'.

'te'
taper_length float

taper length from straight I/O.

16.6
taper_angle float

grating flare angle.

30.0
trenches_extra_angle float

extra angle for the trenches.

9.0
wavelength float

grating transmission central wavelength.

1.53
fiber_angle float

fibre polish angle in degrees.

15.0
grating_line_width float

of the 220 ridge.

0.343
neff float

tooth effective index.

2.638
ncladding float

cladding index.

1.443
layer_trench LayerSpec

for the trench.

'SHALLOW_ETCH'
p_start int

first tooth.

26
n_periods int

number of grating teeth.

30
end_straight_length float

at the end of straight.

0.2
taper ComponentSpec

taper function.

'taper'
cross_section CrossSectionSpec

cross_section spec.

      fiber

   /  /  /  /
  /  /  /  /
_|-|_|-|_|-|___
'strip'
Source code in gdsfactory/components/grating_couplers/grating_coupler_elliptical_trenches.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
@gf.cell_with_module_name(
    schematic_function=grating_coupler_schematic, tags=["grating_couplers"]
)
def grating_coupler_elliptical_trenches(
    polarization: str = "te",
    taper_length: float = 16.6,
    taper_angle: float = 30.0,
    trenches_extra_angle: float = 9.0,
    wavelength: float = 1.53,
    fiber_angle: float = 15.0,
    grating_line_width: float = 0.343,
    neff: float = 2.638,  # tooth effective index
    ncladding: float = 1.443,  # cladding index
    layer_trench: LayerSpec = "SHALLOW_ETCH",
    p_start: int = 26,
    n_periods: int = 30,
    end_straight_length: float = 0.2,
    taper: ComponentSpec = "taper",
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""Returns Grating coupler with defined trenches.

    Some foundries define the grating coupler by a shallow etch step (trenches)
    Others define the slab that they keep (see grating_coupler_elliptical)

    Args:
        polarization: 'te' or 'tm'.
        taper_length: taper length from straight I/O.
        taper_angle: grating flare angle.
        trenches_extra_angle: extra angle for the trenches.
        wavelength: grating transmission central wavelength.
        fiber_angle: fibre polish angle in degrees.
        grating_line_width: of the 220 ridge.
        neff: tooth effective index.
        ncladding: cladding index.
        layer_trench: for the trench.
        p_start: first tooth.
        n_periods: number of grating teeth.
        end_straight_length: at the end of straight.
        taper: taper function.
        cross_section: cross_section spec.

                      fiber

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

    """
    xs = gf.get_cross_section(cross_section)
    wg_width = xs.width
    layer = xs.layer

    # Compute some ellipse parameters
    sthc = np.sin(fiber_angle * DEG2RAD)
    d = neff**2 - ncladding**2 * sthc**2
    a1 = wavelength * neff / d
    b1 = wavelength / np.sqrt(d)
    x1 = wavelength * ncladding * sthc / d

    a1 = round(a1, 3)
    b1 = round(b1, 3)
    x1 = round(x1, 3)

    period = float(a1 + x1)
    trench_line_width = period - grating_line_width

    c = gf.Component()

    # Make each grating line
    for p in range(p_start, p_start + n_periods + 1):
        pts = grating_tooth_points(
            p * a1,
            p * b1,
            p * x1,
            width=trench_line_width,
            taper_angle=taper_angle + trenches_extra_angle,
        )
        c.add_polygon(pts, layer_trench)

    # Make the taper
    p_taper = p_start - 1
    p_taper_eff = p_taper
    a_taper = a1 * p_taper_eff
    # b_taper = b1 * p_taper_eff
    x_taper = x1 * p_taper_eff
    x_output = a_taper + x_taper - taper_length + grating_line_width / 2

    xmax = x_output + taper_length + n_periods * period + 3
    y = wg_width / 2 + np.tan(taper_angle / 2 * np.pi / 180) * xmax

    taper_length2 = (xmax + end_straight_length) - x_output
    taper_component = c << gf.get_component(
        taper,
        width1=wg_width,
        width2=2 * y,
        length=taper_length2,
        cross_section=cross_section,
    )
    taper_component.xmin = x_output

    c.add_port(
        name="o1",
        port=taper_component.ports["o1"],
    )
    c.info["period"] = float(np.round(period, 3))
    c.info["polarization"] = polarization
    c.info["wavelength"] = wavelength

    x = np.round(taper_length + period * n_periods / 2, 3)
    c.flatten()
    c.add_port(
        name="o2",
        center=(x, 0),
        width=10,
        orientation=0,
        layer=layer,
        port_type=f"vertical_{polarization}",
    )
    return c

grating_coupler_elliptical_trenches

grating_coupler_elliptical_uniform

grating_coupler_elliptical_uniform(
    n_periods: int = 20,
    period: float = 0.75,
    fill_factor: float = 0.5,
    **kwargs: Any
) -> Component

Grating coupler with parametrization based on Lumerical FDTD simulation.

The ellipticity is derived from Lumerical knowledge base it depends on fiber_angle (degrees), neff, and nclad

Parameters:

Name Type Description Default
n_periods int

number of grating periods.

20
period float

grating pitch in um.

0.75
fill_factor float

ratio of grating width vs gap.

0.5

Other Parameters:

Name Type Description
taper_length

taper length from input.

taper_angle

grating flare angle.

wavelength

grating transmission central wavelength (um).

fiber_angle

fibre angle in degrees determines ellipticity.

neff

tooth effective index to compute ellipticity.

nclad

cladding effective index to compute ellipticity.

layer_slab

Optional slab.

taper_to_slab_offset

where 0 is at the start of the taper.

polarization

te or tm.

spiked

grating teeth have spikes to avoid drc errors..

bias_gap

etch gap (um). Positive bias increases gap and reduces width to keep period constant.

cross_section

cross_section spec for waveguide port.

kwargs Any

cross_section settings.

      fiber

   /  /  /  /
  /  /  /  /

_|-|_|-|_|-|___ layer
   layer_slab |

o1 __|

Source code in gdsfactory/components/grating_couplers/grating_coupler_elliptical_arbitrary.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
@gf.cell_with_module_name(
    schematic_function=grating_coupler_schematic, tags=["grating_couplers"]
)
def grating_coupler_elliptical_uniform(
    n_periods: int = 20,
    period: float = 0.75,
    fill_factor: float = 0.5,
    **kwargs: Any,
) -> Component:
    r"""Grating coupler with parametrization based on Lumerical FDTD simulation.

    The ellipticity is derived from Lumerical knowledge base
    it depends on fiber_angle (degrees), neff, and nclad

    Args:
        n_periods: number of grating periods.
        period: grating pitch in um.
        fill_factor: ratio of grating width vs gap.

    Keyword Args:
        taper_length: taper length from input.
        taper_angle: grating flare angle.
        wavelength: grating transmission central wavelength (um).
        fiber_angle: fibre angle in degrees determines ellipticity.
        neff: tooth effective index to compute ellipticity.
        nclad: cladding effective index to compute ellipticity.
        layer_slab: Optional slab.
        taper_to_slab_offset: where 0 is at the start of the taper.
        polarization: te or tm.
        spiked: grating teeth have spikes to avoid drc errors..
        bias_gap: etch gap (um).
            Positive bias increases gap and reduces width to keep period constant.
        cross_section: cross_section spec for waveguide port.
        kwargs: cross_section settings.

                      fiber

                   /  /  /  /
                  /  /  /  /

                _|-|_|-|_|-|___ layer
                   layer_slab |
            o1  ______________|

    """
    widths = (period * fill_factor,) * n_periods
    gaps = (period * (1 - fill_factor),) * n_periods
    return grating_coupler_elliptical_arbitrary(gaps=gaps, widths=widths, **kwargs)

grating_coupler_elliptical_uniform

grating_coupler_loss

grating_coupler_loss

grating_coupler_loss(
    pitch: float = 127.0,
    grating_coupler: ComponentSpec = "grating_coupler_elliptical_trenches",
    cross_section: CrossSectionSpec = "strip",
    port_name: str = "o1",
    rotation: float = -90,
    nfibers: int = 10,
    grating_coupler_spacing: float = 5.0,
) -> Component

Grating coupler test structure for de-embeding fiber array.

Connects channel 1->3, 1->5 ... 1->nfibers with grating couplers.

Only odd channels are connected to the grating couplers as even channels in the align_tree.

Parameters:

Name Type Description Default
pitch float

um.

127.0
grating_coupler ComponentSpec

spec.

'grating_coupler_elliptical_trenches'
cross_section CrossSectionSpec

spec.

'strip'
port_name str

for the grating_coupler port.

'o1'
rotation float

degrees.

-90
nfibers int

number of fibers to connect.

10
grating_coupler_spacing float

um.

5.0
Source code in gdsfactory/components/grating_couplers/grating_coupler_loss.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@gf.cell_with_module_name(tags=["grating_couplers"])
def grating_coupler_loss(
    pitch: float = 127.0,
    grating_coupler: ComponentSpec = "grating_coupler_elliptical_trenches",
    cross_section: CrossSectionSpec = "strip",
    port_name: str = "o1",
    rotation: float = -90,
    nfibers: int = 10,
    grating_coupler_spacing: float = 5.0,
) -> Component:
    """Grating coupler test structure for de-embeding fiber array.

    Connects channel 1->3, 1->5 ... 1->nfibers with grating couplers.

    Only odd channels are connected to the grating couplers as even channels in the align_tree.

    Args:
        pitch: um.
        grating_coupler: spec.
        cross_section: spec.
        port_name: for the grating_coupler port.
        rotation: degrees.
        nfibers: number of fibers to connect.
        grating_coupler_spacing: um.
    """
    gc = gf.get_component(grating_coupler)
    c = gf.Component()
    xmin = 0.0

    for i in range(2, nfibers - 1, 2):
        g1 = c << gc
        g1.rotate(rotation)
        g1.x = xmin

        g2 = c << gc
        g2.rotate(rotation)
        g2.x = xmin + i * pitch

        route_bundle(
            c,
            g1[port_name],
            g2[port_name],
            start_straight_length=40.0,
            cross_section=cross_section,
        )

        xmin = g2.xmax + grating_coupler_spacing + gc.xsize / 2

    return c

grating_coupler_loss

grating_coupler_rectangular

grating_coupler_rectangular

grating_coupler_rectangular(
    n_periods: int = 20,
    period: float = 0.75,
    fill_factor: float = 0.5,
    width_grating: float = 11.0,
    length_taper: float = 150.0,
    polarization: str = "te",
    wavelength: float = 1.55,
    taper: ComponentSpec = "taper",
    layer_slab: LayerSpec | None = "SLAB150",
    layer_grating: LayerSpec | None = None,
    fiber_angle: float = 15,
    slab_xmin: float = -1.0,
    slab_offset: float = 1.0,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Grating coupler with rectangular shapes (not elliptical).

Needs longer taper than elliptical. Grating teeth are straight. For a focusing grating take a look at grating_coupler_elliptical.

Parameters:

Name Type Description Default
n_periods int

number of grating teeth.

20
period float

grating pitch.

0.75
fill_factor float

ratio of grating width vs gap.

0.5
width_grating float

11.

11.0
length_taper float

150.

150.0
polarization str

'te' or 'tm'.

'te'
wavelength float

in um.

1.55
taper ComponentSpec

function.

'taper'
layer_slab LayerSpec | None

layer that protects the slab under the grating.

'SLAB150'
layer_grating LayerSpec | None

layer for the grating.

None
fiber_angle float

in degrees.

15
slab_xmin float

where 0 is at the start of the taper.

-1.0
slab_offset float

from edge of grating to edge of the slab.

1.0
cross_section CrossSectionSpec

for input waveguide port.

'strip'
Source code in gdsfactory/components/grating_couplers/grating_coupler_rectangular.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
@gf.cell_with_module_name(
    schematic_function=grating_coupler_schematic, tags=["grating_couplers"]
)
def grating_coupler_rectangular(
    n_periods: int = 20,
    period: float = 0.75,
    fill_factor: float = 0.5,
    width_grating: float = 11.0,
    length_taper: float = 150.0,
    polarization: str = "te",
    wavelength: float = 1.55,
    taper: ComponentSpec = "taper",
    layer_slab: LayerSpec | None = "SLAB150",
    layer_grating: LayerSpec | None = None,
    fiber_angle: float = 15,
    slab_xmin: float = -1.0,
    slab_offset: float = 1.0,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""Grating coupler with rectangular shapes (not elliptical).

    Needs longer taper than elliptical.
    Grating teeth are straight.
    For a focusing grating take a look at grating_coupler_elliptical.

    Args:
        n_periods: number of grating teeth.
        period: grating pitch.
        fill_factor: ratio of grating width vs gap.
        width_grating: 11.
        length_taper: 150.
        polarization: 'te' or 'tm'.
        wavelength: in um.
        taper: function.
        layer_slab: layer that protects the slab under the grating.
        layer_grating: layer for the grating.
        fiber_angle: in degrees.
        slab_xmin: where 0 is at the start of the taper.
        slab_offset: from edge of grating to edge of the slab.
        cross_section: for input waveguide port.

        side view
                      fiber

                   /  /  /  /
                  /  /  /  /

                _|-|_|-|_|-|___ layer
                   layer_slab |
            o1  ______________|


        top view     _________
                    /| | | | |
                   / | | | | |
                  /taper_angle
                 /_ _| | | | |
        wg_width |   | | | | |
                 \   | | | | |
                  \  | | | | |
                   \ | | | | |
                    \|_|_|_|_|
                 <-->
                taper_length
    """
    xs = gf.get_cross_section(cross_section)
    wg_width = xs.width
    layer = layer_grating or xs.layer
    assert layer is not None

    c = Component()
    taper_ref = c << gf.get_component(
        taper,
        length=length_taper,
        width2=width_grating,
        width1=wg_width,
        cross_section=cross_section,
    )

    c.add_port(port=taper_ref.ports["o1"], name="o1")
    x0 = length_taper
    for i in range(n_periods):
        xsize = gf.snap.snap_to_grid(period * fill_factor)
        cgrating = c.add_ref(
            gf.c.rectangle(size=(xsize, width_grating), layer=layer, port_type=None)
        )
        cgrating.xmin = gf.snap.snap_to_grid(x0 + i * period)
        cgrating.y = 0

    c.info["polarization"] = polarization
    c.info["wavelength"] = wavelength
    c.info["fiber_angle"] = fiber_angle

    if layer_slab:
        slab_xmin = length_taper - slab_offset
        slab_xmax = length_taper + n_periods * period + slab_offset
        slab_ysize = width_grating + 2 * slab_offset
        yslab = slab_ysize / 2
        c.add_polygon(
            [
                (slab_xmin, yslab),
                (slab_xmax, yslab),
                (slab_xmax, -yslab),
                (slab_xmin, -yslab),
            ],
            layer_slab,
        )
    xs.add_bbox(c)
    xport = np.round((x0 + cgrating.x) / 2, 3)
    c.add_port(
        name="o2",
        port_type=f"vertical_{polarization}",
        center=(xport, 0),
        orientation=0,
        width=width_grating,
        layer=layer,
    )
    c.flatten()
    return c

grating_coupler_rectangular

grating_coupler_rectangular_arbitrary

grating_coupler_rectangular_arbitrary

grating_coupler_rectangular_arbitrary(
    gaps: Floats = _gaps,
    widths: Floats = _widths,
    width_grating: float = 11.0,
    length_taper: float = 150.0,
    polarization: str = "te",
    wavelength: float = 1.55,
    layer_grating: LayerSpec | None = None,
    layer_slab: LayerSpec | None = None,
    slab_xmin: float = -1.0,
    slab_offset: float = 1.0,
    fiber_angle: float = 15,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Grating coupler uniform with rectangular shape (not elliptical).

Therefore it needs a longer taper. Grating teeth are straight instead of elliptical.

Parameters:

Name Type Description Default
gaps Floats

list of gaps between grating teeth.

_gaps
widths Floats

list of grating widths.

_widths
width_grating float

grating teeth width.

11.0
length_taper float

taper length (um).

150.0
polarization str

'te' or 'tm'.

'te'
wavelength float

in um.

1.55
layer_grating LayerSpec | None

Optional layer for grating. \ by default None uses cross_section.layer. \ if different from cross_section.layer expands taper.

None
layer_slab LayerSpec | None

layer that protects the slab under the grating.

None
slab_xmin float

where 0 is at the start of the taper.

-1.0
slab_offset float

from edge of grating to edge of the slab.

1.0
fiber_angle float

in degrees.

15
cross_section CrossSectionSpec

for input waveguide port.

      fiber

   /  /  /  /
  /  /  /  /

_|-|_|-|_|-|___ layer
   layer_slab |

o1 __|

'strip'
Source code in gdsfactory/components/grating_couplers/grating_coupler_rectangular_arbitrary.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@gf.cell_with_module_name(
    schematic_function=grating_coupler_schematic, tags=["grating_couplers"]
)
def grating_coupler_rectangular_arbitrary(
    gaps: Floats = _gaps,
    widths: Floats = _widths,
    width_grating: float = 11.0,
    length_taper: float = 150.0,
    polarization: str = "te",
    wavelength: float = 1.55,
    layer_grating: LayerSpec | None = None,
    layer_slab: LayerSpec | None = None,
    slab_xmin: float = -1.0,
    slab_offset: float = 1.0,
    fiber_angle: float = 15,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""Grating coupler uniform with rectangular shape (not elliptical).

    Therefore it needs a longer taper.
    Grating teeth are straight instead of elliptical.

    Args:
        gaps: list of gaps between grating teeth.
        widths: list of grating widths.
        width_grating: grating teeth width.
        length_taper: taper length (um).
        polarization: 'te' or 'tm'.
        wavelength: in um.
        layer_grating: Optional layer for grating. \
                by default None uses cross_section.layer. \
                if different from cross_section.layer expands taper.
        layer_slab: layer that protects the slab under the grating.
        slab_xmin: where 0 is at the start of the taper.
        slab_offset: from edge of grating to edge of the slab.
        fiber_angle: in degrees.
        cross_section: for input waveguide port.

                      fiber

                   /  /  /  /
                  /  /  /  /

                _|-|_|-|_|-|___ layer
                   layer_slab |
            o1  ______________|



        top view     _________
                    /| | | | |
                   / | | | | |
                  /taper_angle
                 /_ _| | | | |
        wg_width |   | | | | |
                 \   | | | | |
                  \  | | | | |
                   \ | | | | |
                    \|_|_|_|_|
                 <-->
                taper_length

    """
    xs = gf.get_cross_section(cross_section)
    assert xs.layer is not None
    layer_wg = gf.get_layer(xs.layer)
    layer_grating = layer_grating or layer_wg

    layer_grating = gf.get_layer(layer_grating)
    c = Component()

    taper_ref = c << taper(
        length=length_taper,
        width1=xs.width,
        width2=width_grating,
        cross_section=cross_section,
    )

    c.add_port(port=taper_ref.ports["o1"], name="o1")
    xi = length_taper

    y0 = width_grating / 2

    for width, gap in zip(widths, gaps, strict=False):
        xi += gap
        points = np.array(
            [
                [xi, -y0],
                [xi, +y0],
                [xi + width, +y0],
                [xi + width, -y0],
            ]
        )
        c.add_polygon(
            points,
            layer_grating,
        )
        xi += width

    if layer_slab:
        slab_xmin = length_taper - slab_offset
        slab_xmax = length_taper + np.sum(widths) + np.sum(gaps) + slab_offset
        slab_ysize = width_grating + 2 * slab_offset
        yslab = slab_ysize / 2
        c.add_polygon(
            [
                (slab_xmin, yslab),
                (slab_xmax, yslab),
                (slab_xmax, -yslab),
                (slab_xmin, -yslab),
            ],
            layer_slab,
        )
    xport = np.round((xi + length_taper) / 2, 3)
    c.add_port(
        name="o2",
        port_type=f"vertical_{polarization}",
        center=(xport, 0),
        orientation=0,
        width=width_grating,
        layer=xs.layer,
    )
    c.info["polarization"] = polarization
    c.info["wavelength"] = wavelength
    c.info["fiber_angle"] = fiber_angle

    xs.add_bbox(c)
    return c

grating_coupler_rectangular_arbitrary

grating_coupler_tree

grating_coupler_tree

grating_coupler_tree(
    n: int = 4,
    straight_spacing: float = 4.0,
    grating_coupler: ComponentSpec = "grating_coupler_elliptical_te",
    with_loopback: bool = False,
    bend: ComponentSpec = "bend_euler",
    fanout_length: float = 0.0,
    cross_section: CrossSectionSpec = "strip",
    **kwargs: Any
) -> Component

Array of straights connected with grating couplers.

useful to align the 4 corners of the chip

Parameters:

Name Type Description Default
n int

number of gratings.

4
straight_spacing float

in um.

4.0
grating_coupler ComponentSpec

spec.

'grating_coupler_elliptical_te'
with_loopback bool

adds loopback.

False
bend ComponentSpec

bend spec.

'bend_euler'
fanout_length float

in um.

0.0
cross_section CrossSectionSpec

cross_section function.

'strip'
kwargs Any

additional arguments.

{}
Source code in gdsfactory/components/grating_couplers/grating_coupler_tree.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@gf.cell_with_module_name(tags=["grating_couplers"])
def grating_coupler_tree(
    n: int = 4,
    straight_spacing: float = 4.0,
    grating_coupler: ComponentSpec = "grating_coupler_elliptical_te",
    with_loopback: bool = False,
    bend: ComponentSpec = "bend_euler",
    fanout_length: float = 0.0,
    cross_section: CrossSectionSpec = "strip",
    **kwargs: Any,
) -> Component:
    """Array of straights connected with grating couplers.

    useful to align the 4 corners of the chip

    Args:
        n: number of gratings.
        straight_spacing: in um.
        grating_coupler: spec.
        with_loopback: adds loopback.
        bend: bend spec.
        fanout_length: in um.
        cross_section: cross_section function.
        kwargs: additional arguments.
    """
    c = gf.c.straight_array(
        n=n,
        spacing=straight_spacing,
    )

    return gf.routing.add_fiber_array(
        component=c,
        with_loopback=with_loopback,
        grating_coupler=grating_coupler,
        fanout_length=fanout_length,
        bend=bend,
        cross_section=cross_section,
        **kwargs,
    )

grating_coupler_tree

mems

anchored_flexure

anchored_flexure

anchored_flexure(
    hinge_width: float = 0.3,
    hinge_length: float = 5.0,
    pad_width: float = 10.0,
    pad_length: float = 10.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component

Returns a flexure hinge between two pads.

Two rectangular pads connected by a thin hinge, all centered vertically.

Parameters:

Name Type Description Default
hinge_width float

width of the thin flexure hinge.

0.3
hinge_length float

length of the hinge connecting the two pads.

5.0
pad_width float

width (vertical) of each pad.

10.0
pad_length float

length (horizontal) of each pad.

10.0
layer LayerSpec

layer spec.

'WG'
port_type str

port type for electrical ports.

'electrical'
Source code in gdsfactory/components/mems/anchored_flexure.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
@gf.cell_with_module_name(tags=["mems"])
def anchored_flexure(
    hinge_width: float = 0.3,
    hinge_length: float = 5.0,
    pad_width: float = 10.0,
    pad_length: float = 10.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component:
    """Returns a flexure hinge between two pads.

    Two rectangular pads connected by a thin hinge, all centered vertically.

    Args:
        hinge_width: width of the thin flexure hinge.
        hinge_length: length of the hinge connecting the two pads.
        pad_width: width (vertical) of each pad.
        pad_length: length (horizontal) of each pad.
        layer: layer spec.
        port_type: port type for electrical ports.
    """
    c = Component()

    total_length = 2 * pad_length + hinge_length
    x_start = -total_length / 2

    # Left pad
    c.add_polygon(
        [
            (x_start, -pad_width / 2),
            (x_start + pad_length, -pad_width / 2),
            (x_start + pad_length, pad_width / 2),
            (x_start, pad_width / 2),
        ],
        layer=layer,
    )

    # Thin hinge
    hinge_x0 = x_start + pad_length
    c.add_polygon(
        [
            (hinge_x0, -hinge_width / 2),
            (hinge_x0 + hinge_length, -hinge_width / 2),
            (hinge_x0 + hinge_length, hinge_width / 2),
            (hinge_x0, hinge_width / 2),
        ],
        layer=layer,
    )

    # Right pad
    right_x0 = hinge_x0 + hinge_length
    c.add_polygon(
        [
            (right_x0, -pad_width / 2),
            (right_x0 + pad_length, -pad_width / 2),
            (right_x0 + pad_length, pad_width / 2),
            (right_x0, pad_width / 2),
        ],
        layer=layer,
    )

    # Port at left pad outer edge
    c.add_port(
        "e1",
        center=(x_start, 0),
        width=pad_width,
        orientation=180,
        layer=layer,
        port_type=port_type,
    )

    # Port at right pad outer edge
    c.add_port(
        "e2",
        center=(right_x0 + pad_length, 0),
        width=pad_width,
        orientation=0,
        layer=layer,
        port_type=port_type,
    )

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

anchored_flexure

bent_beam

bent_beam

bent_beam(
    beam_width: float = 1.0,
    beam_length: float = 40.0,
    bend_angle: float = 170.0,
    anchor_width: float = 5.0,
    anchor_length: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component

Returns a V-shaped bent beam thermal actuator.

Two straight beam segments meeting at an apex that points upward, with anchor pads at both ends.

Parameters:

Name Type Description Default
beam_width float

width of the beam.

1.0
beam_length float

length of each beam segment (center-line).

40.0
bend_angle float

angle between the two beam segments in degrees.

170.0
anchor_width float

width (vertical) of each anchor pad.

5.0
anchor_length float

length (horizontal) of each anchor pad.

5.0
layer LayerSpec

layer spec.

'WG'
port_type str

port type for electrical ports.

'electrical'
Source code in gdsfactory/components/mems/bent_beam.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@gf.cell_with_module_name(tags=["mems"])
def bent_beam(
    beam_width: float = 1.0,
    beam_length: float = 40.0,
    bend_angle: float = 170.0,
    anchor_width: float = 5.0,
    anchor_length: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component:
    """Returns a V-shaped bent beam thermal actuator.

    Two straight beam segments meeting at an apex that points upward,
    with anchor pads at both ends.

    Args:
        beam_width: width of the beam.
        beam_length: length of each beam segment (center-line).
        bend_angle: angle between the two beam segments in degrees.
        anchor_width: width (vertical) of each anchor pad.
        anchor_length: length (horizontal) of each anchor pad.
        layer: layer spec.
        port_type: port type for electrical ports.
    """
    c = Component()

    # The bend_angle is the angle between the two segments.
    # The half-angle from horizontal for each arm:
    half_angle = (180.0 - bend_angle) / 2.0
    half_angle_rad = np.radians(half_angle)

    # Segment projected lengths
    dx = beam_length * np.cos(half_angle_rad)
    dy = beam_length * np.sin(half_angle_rad)

    # Apex at (0, dy), left anchor at (-dx, 0), right anchor at (dx, 0)
    # Build the V-shape as a polygon with width

    # Build as two arms merged into one polygon

    # Left arm direction: from (-dx, 0) to (0, dy)
    left_dir = np.array([dx, dy])
    left_dir = left_dir / np.linalg.norm(left_dir)
    left_norm = np.array([-left_dir[1], left_dir[0]])  # points "up-left"

    # Right arm direction: from (0, dy) to (dx, 0)
    right_dir = np.array([dx, -dy])
    right_dir = right_dir / np.linalg.norm(right_dir)
    right_norm = np.array([-right_dir[1], right_dir[0]])  # points "up-right"

    hw = beam_width / 2

    # Left arm polygon corners
    la_start = np.array([-dx, 0.0])
    la_end = np.array([0.0, dy])

    # Right arm polygon corners
    ra_start = np.array([0.0, dy])
    ra_end = np.array([dx, 0.0])

    # Build the full V as a single polygon (outer contour going clockwise)
    poly_points = [
        tuple(la_start + left_norm * hw),
        tuple(la_end + left_norm * hw),
        tuple(ra_start + right_norm * hw),
        tuple(ra_end + right_norm * hw),
        tuple(ra_end - right_norm * hw),
        tuple(ra_start - right_norm * hw),
        tuple(la_end - left_norm * hw),
        tuple(la_start - left_norm * hw),
    ]

    c.add_polygon(poly_points, layer=layer)

    # Left anchor pad
    c.add_polygon(
        [
            (-dx - anchor_length, -anchor_width / 2),
            (-dx, -anchor_width / 2),
            (-dx, anchor_width / 2),
            (-dx - anchor_length, anchor_width / 2),
        ],
        layer=layer,
    )

    # Right anchor pad
    c.add_polygon(
        [
            (dx, -anchor_width / 2),
            (dx + anchor_length, -anchor_width / 2),
            (dx + anchor_length, anchor_width / 2),
            (dx, anchor_width / 2),
        ],
        layer=layer,
    )

    # Port at left anchor center
    c.add_port(
        "e1",
        center=(-dx - anchor_length, 0),
        width=anchor_width,
        orientation=180,
        layer=layer,
        port_type=port_type,
    )

    # Port at right anchor center
    c.add_port(
        "e2",
        center=(dx + anchor_length, 0),
        width=anchor_width,
        orientation=0,
        layer=layer,
        port_type=port_type,
    )

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

bent_beam

bolometer

bolometer

bolometer(
    absorber_width: float = 20.0,
    absorber_length: float = 20.0,
    leg_width: float = 0.5,
    leg_length: float = 15.0,
    n_legs: int = 4,
    pad_width: float = 5.0,
    pad_length: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component

Returns a bolometer thermal detector.

A central absorber rectangle with thin L-shaped support legs extending outward to anchor pads distributed around the perimeter.

Parameters:

Name Type Description Default
absorber_width float

width of the central absorber.

20.0
absorber_length float

length of the central absorber.

20.0
leg_width float

width of each support leg.

0.5
leg_length float

length of each support leg.

15.0
n_legs int

number of support legs (distributed around perimeter).

4
pad_width float

width of each anchor pad.

5.0
pad_length float

length of each anchor pad.

5.0
layer LayerSpec

layer spec.

'WG'
port_type str

port type for electrical ports.

'electrical'
Source code in gdsfactory/components/mems/bolometer.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
@gf.cell_with_module_name(tags=["mems"])
def bolometer(
    absorber_width: float = 20.0,
    absorber_length: float = 20.0,
    leg_width: float = 0.5,
    leg_length: float = 15.0,
    n_legs: int = 4,
    pad_width: float = 5.0,
    pad_length: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component:
    """Returns a bolometer thermal detector.

    A central absorber rectangle with thin L-shaped support legs extending
    outward to anchor pads distributed around the perimeter.

    Args:
        absorber_width: width of the central absorber.
        absorber_length: length of the central absorber.
        leg_width: width of each support leg.
        leg_length: length of each support leg.
        n_legs: number of support legs (distributed around perimeter).
        pad_width: width of each anchor pad.
        pad_length: length of each anchor pad.
        layer: layer spec.
        port_type: port type for electrical ports.
    """
    c = Component()

    ahw = absorber_width / 2
    ahl = absorber_length / 2

    # Central absorber rectangle centered at origin
    c.add_polygon(
        [
            (-ahl, -ahw),
            (ahl, -ahw),
            (ahl, ahw),
            (-ahl, ahw),
        ],
        layer=layer,
    )

    # Distribute legs around the absorber perimeter
    # Place legs at evenly spaced angles
    lhw = leg_width / 2

    for i in range(n_legs):
        angle = 2 * np.pi * i / n_legs
        cos_a = np.cos(angle)
        sin_a = np.sin(angle)

        # Determine attachment point on absorber edge
        # Find which edge the ray from center at this angle hits first
        if abs(cos_a) < 1e-10:
            # Vertical ray
            attach_x = 0.0
            attach_y = ahw * np.sign(sin_a)
        elif abs(sin_a) < 1e-10:
            # Horizontal ray
            attach_x = ahl * np.sign(cos_a)
            attach_y = 0.0
        else:
            # Check intersection with vertical edges (x = +/-ahl)
            tx = ahl / abs(cos_a)
            # Check intersection with horizontal edges (y = +/-ahw)
            ty = ahw / abs(sin_a)
            if tx < ty:
                attach_x = ahl * np.sign(cos_a)
                attach_y = tx * sin_a
            else:
                attach_x = ty * cos_a
                attach_y = ahw * np.sign(sin_a)

        # L-shaped leg: first segment goes outward radially, second goes
        # along the tangential direction. For simplicity, use straight legs
        # going outward from attachment point.

        # Determine primary direction (outward from center)
        # Use the dominant axis for the leg direction
        if abs(cos_a) >= abs(sin_a):
            # Horizontal leg
            sign_x = np.sign(cos_a)
            leg_end_x = attach_x + sign_x * leg_length
            leg_end_y = attach_y

            # Horizontal leg segment
            x0 = attach_x
            x1 = leg_end_x
            if x0 > x1:
                x0, x1 = x1, x0

            c.add_polygon(
                [
                    (x0, attach_y - lhw),
                    (x1, attach_y - lhw),
                    (x1, attach_y + lhw),
                    (x0, attach_y + lhw),
                ],
                layer=layer,
            )

            # Pad at end of leg
            pad_cx = leg_end_x + sign_x * pad_length / 2
            pad_cy = leg_end_y
            c.add_polygon(
                [
                    (pad_cx - pad_length / 2, pad_cy - pad_width / 2),
                    (pad_cx + pad_length / 2, pad_cy - pad_width / 2),
                    (pad_cx + pad_length / 2, pad_cy + pad_width / 2),
                    (pad_cx - pad_length / 2, pad_cy + pad_width / 2),
                ],
                layer=layer,
            )

            # Port at outer edge of pad
            port_x = pad_cx + sign_x * pad_length / 2
            c.add_port(
                f"e{i + 1}",
                center=(port_x, pad_cy),
                width=pad_width,
                orientation=0 if sign_x > 0 else 180,
                layer=layer,
                port_type=port_type,
            )
        else:
            # Vertical leg
            sign_y = np.sign(sin_a)
            leg_end_x = attach_x
            leg_end_y = attach_y + sign_y * leg_length

            # Vertical leg segment
            y0 = attach_y
            y1 = leg_end_y
            if y0 > y1:
                y0, y1 = y1, y0

            c.add_polygon(
                [
                    (attach_x - lhw, y0),
                    (attach_x + lhw, y0),
                    (attach_x + lhw, y1),
                    (attach_x - lhw, y1),
                ],
                layer=layer,
            )

            # Pad at end of leg
            pad_cx = leg_end_x
            pad_cy = leg_end_y + sign_y * pad_width / 2
            c.add_polygon(
                [
                    (pad_cx - pad_length / 2, pad_cy - pad_width / 2),
                    (pad_cx + pad_length / 2, pad_cy - pad_width / 2),
                    (pad_cx + pad_length / 2, pad_cy + pad_width / 2),
                    (pad_cx - pad_length / 2, pad_cy + pad_width / 2),
                ],
                layer=layer,
            )

            # Port at outer edge of pad
            port_y = pad_cy + sign_y * pad_width / 2
            c.add_port(
                f"e{i + 1}",
                center=(pad_cx, port_y),
                width=pad_length,
                orientation=90 if sign_y > 0 else 270,
                layer=layer,
                port_type=port_type,
            )

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

bolometer

cantilever

cantilever

cantilever(
    beam_width: float = 2.0,
    beam_length: float = 20.0,
    anchor_width: float = 5.0,
    anchor_length: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component

Returns a simple cantilever beam with an anchor.

A rectangular anchor on the left with a thinner beam extending to the right.

Parameters:

Name Type Description Default
beam_width float

width of the cantilever beam.

2.0
beam_length float

length of the cantilever beam.

20.0
anchor_width float

width (vertical) of the anchor pad.

5.0
anchor_length float

length (horizontal) of the anchor pad.

5.0
layer LayerSpec

layer spec.

'WG'
port_type str

port type for electrical ports.

'electrical'
Source code in gdsfactory/components/mems/cantilever.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
@gf.cell_with_module_name(tags=["mems"])
def cantilever(
    beam_width: float = 2.0,
    beam_length: float = 20.0,
    anchor_width: float = 5.0,
    anchor_length: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component:
    """Returns a simple cantilever beam with an anchor.

    A rectangular anchor on the left with a thinner beam extending to the right.

    Args:
        beam_width: width of the cantilever beam.
        beam_length: length of the cantilever beam.
        anchor_width: width (vertical) of the anchor pad.
        anchor_length: length (horizontal) of the anchor pad.
        layer: layer spec.
        port_type: port type for electrical ports.
    """
    c = Component()

    # Anchor rectangle on the left, centered vertically at y=0
    c.add_polygon(
        [
            (0, -anchor_width / 2),
            (anchor_length, -anchor_width / 2),
            (anchor_length, anchor_width / 2),
            (0, anchor_width / 2),
        ],
        layer=layer,
    )

    # Beam rectangle extending right from anchor, centered vertically at y=0
    c.add_polygon(
        [
            (anchor_length, -beam_width / 2),
            (anchor_length + beam_length, -beam_width / 2),
            (anchor_length + beam_length, beam_width / 2),
            (anchor_length, beam_width / 2),
        ],
        layer=layer,
    )

    # Port at the left edge of the anchor
    c.add_port(
        "e1",
        center=(0, 0),
        width=anchor_width,
        orientation=180,
        layer=layer,
        port_type=port_type,
    )

    # Port at the free tip of the beam
    c.add_port(
        "e2",
        center=(anchor_length + beam_length, 0),
        width=beam_width,
        orientation=0,
        layer=layer,
        port_type=port_type,
    )

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

cantilever

comb_drive

comb_drive

comb_drive(
    finger_width: float = 0.5,
    finger_length: float = 10.0,
    finger_gap: float = 0.5,
    n_fingers: int = 20,
    finger_overlap: float = 5.0,
    shuttle_width: float = 5.0,
    shuttle_length: float = 30.0,
    spring_width: float = 0.5,
    spring_length: float = 20.0,
    n_spring_folds: int = 4,
    anchor_size: float = 10.0,
    layer: LayerSpec = "WG",
) -> Component

Returns a comb drive actuator with interdigitated fingers and folded springs.

A central shuttle with comb fingers on both sides, fixed electrodes with interleaving fingers, and folded springs connecting the shuttle to corner anchor pads.

Parameters:

Name Type Description Default
finger_width float

width of each comb finger.

0.5
finger_length float

length of each comb finger.

10.0
finger_gap float

gap between adjacent moving and fixed fingers.

0.5
n_fingers int

number of moving fingers on each side.

20
finger_overlap float

overlap length between moving and fixed fingers in the actuation direction.

5.0
shuttle_width float

width (vertical) of the shuttle mass.

5.0
shuttle_length float

length (horizontal) of the shuttle mass.

30.0
spring_width float

width of spring beam segments.

0.5
spring_length float

length of each spring fold segment.

20.0
n_spring_folds int

number of folds in each folded spring.

4
anchor_size float

size of each square anchor pad.

10.0
layer LayerSpec

layer spec.

'WG'
Source code in gdsfactory/components/mems/comb_drive.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
@gf.cell_with_module_name(tags=["mems"])
def comb_drive(
    finger_width: float = 0.5,
    finger_length: float = 10.0,
    finger_gap: float = 0.5,
    n_fingers: int = 20,
    finger_overlap: float = 5.0,
    shuttle_width: float = 5.0,
    shuttle_length: float = 30.0,
    spring_width: float = 0.5,
    spring_length: float = 20.0,
    n_spring_folds: int = 4,
    anchor_size: float = 10.0,
    layer: LayerSpec = "WG",
) -> Component:
    """Returns a comb drive actuator with interdigitated fingers and folded springs.

    A central shuttle with comb fingers on both sides, fixed electrodes
    with interleaving fingers, and folded springs connecting the shuttle
    to corner anchor pads.

    Args:
        finger_width: width of each comb finger.
        finger_length: length of each comb finger.
        finger_gap: gap between adjacent moving and fixed fingers.
        n_fingers: number of moving fingers on each side.
        finger_overlap: overlap length between moving and fixed fingers in the actuation direction.
        shuttle_width: width (vertical) of the shuttle mass.
        shuttle_length: length (horizontal) of the shuttle mass.
        spring_width: width of spring beam segments.
        spring_length: length of each spring fold segment.
        n_spring_folds: number of folds in each folded spring.
        anchor_size: size of each square anchor pad.
        layer: layer spec.
    """
    c = Component()

    shw = shuttle_width / 2
    shl = shuttle_length / 2

    # 1. Shuttle rectangle centered at origin
    c.add_polygon(
        [(-shl, -shw), (shl, -shw), (shl, shw), (-shl, shw)],
        layer=layer,
    )

    # Finger pitch (center-to-center of same-side fingers)
    finger_pitch = 2 * (finger_width + finger_gap)

    # Compute total finger array height
    total_finger_height = n_fingers * finger_pitch
    finger_start_y = -total_finger_height / 2

    # 2. Moving fingers extending left and right from shuttle
    for i in range(n_fingers):
        fy = finger_start_y + i * finger_pitch

        # Right-extending moving fingers
        c.add_polygon(
            [
                (shl, fy),
                (shl + finger_length, fy),
                (shl + finger_length, fy + finger_width),
                (shl, fy + finger_width),
            ],
            layer=layer,
        )

        # Left-extending moving fingers
        c.add_polygon(
            [
                (-shl - finger_length, fy),
                (-shl, fy),
                (-shl, fy + finger_width),
                (-shl - finger_length, fy + finger_width),
            ],
            layer=layer,
        )

    # 3. Fixed electrode bars and interleaving fingers
    fixed_bar_width = shuttle_width
    fixed_bar_x_right = shl + 2 * finger_length - finger_overlap
    fixed_bar_x_left = -fixed_bar_x_right

    # Right fixed electrode bar
    c.add_polygon(
        [
            (fixed_bar_x_right, -total_finger_height / 2 - fixed_bar_width),
            (
                fixed_bar_x_right + shuttle_width,
                -total_finger_height / 2 - fixed_bar_width,
            ),
            (
                fixed_bar_x_right + shuttle_width,
                total_finger_height / 2 + fixed_bar_width,
            ),
            (fixed_bar_x_right, total_finger_height / 2 + fixed_bar_width),
        ],
        layer=layer,
    )

    # Left fixed electrode bar
    c.add_polygon(
        [
            (
                fixed_bar_x_left - shuttle_width,
                -total_finger_height / 2 - fixed_bar_width,
            ),
            (fixed_bar_x_left, -total_finger_height / 2 - fixed_bar_width),
            (fixed_bar_x_left, total_finger_height / 2 + fixed_bar_width),
            (
                fixed_bar_x_left - shuttle_width,
                total_finger_height / 2 + fixed_bar_width,
            ),
        ],
        layer=layer,
    )

    # Fixed fingers interleaving with moving fingers
    for i in range(n_fingers):
        fy = finger_start_y + i * finger_pitch + finger_width + finger_gap

        # Right fixed fingers (extending left from right bar)
        c.add_polygon(
            [
                (fixed_bar_x_right - finger_length, fy),
                (fixed_bar_x_right, fy),
                (fixed_bar_x_right, fy + finger_width),
                (fixed_bar_x_right - finger_length, fy + finger_width),
            ],
            layer=layer,
        )

        # Left fixed fingers (extending right from left bar)
        c.add_polygon(
            [
                (fixed_bar_x_left, fy),
                (fixed_bar_x_left + finger_length, fy),
                (fixed_bar_x_left + finger_length, fy + finger_width),
                (fixed_bar_x_left, fy + finger_width),
            ],
            layer=layer,
        )

    # 4. Folded springs connecting shuttle to anchors (top and bottom)
    spring_fold_pitch = spring_width + finger_gap
    for y_sign in [1, -1]:  # top and bottom
        for x_sign in [1, -1]:  # left and right springs
            # Spring attachment point on shuttle
            attach_x = x_sign * shl * 0.5
            attach_y = y_sign * shw

            # Spring extends in y direction away from shuttle
            spring_dir = y_sign

            # Build folded spring segments
            x_cursor = attach_x
            y_cursor = attach_y

            for fold in range(n_spring_folds):
                # Vertical segment
                y_end = y_cursor + spring_dir * spring_length

                c.add_polygon(
                    [
                        (x_cursor - spring_width / 2, min(y_cursor, y_end)),
                        (x_cursor + spring_width / 2, min(y_cursor, y_end)),
                        (x_cursor + spring_width / 2, max(y_cursor, y_end)),
                        (x_cursor - spring_width / 2, max(y_cursor, y_end)),
                    ],
                    layer=layer,
                )

                # Connecting horizontal segment at the end (if not last fold)
                if fold < n_spring_folds - 1:
                    next_x = x_cursor + x_sign * spring_fold_pitch
                    conn_y = y_end

                    c.add_polygon(
                        [
                            (
                                min(x_cursor, next_x) - spring_width / 2,
                                conn_y - spring_width / 2,
                            ),
                            (
                                max(x_cursor, next_x) + spring_width / 2,
                                conn_y - spring_width / 2,
                            ),
                            (
                                max(x_cursor, next_x) + spring_width / 2,
                                conn_y + spring_width / 2,
                            ),
                            (
                                min(x_cursor, next_x) - spring_width / 2,
                                conn_y + spring_width / 2,
                            ),
                        ],
                        layer=layer,
                    )

                    x_cursor = next_x
                    spring_dir = -spring_dir  # Reverse direction for next fold

                y_cursor = y_end

    # 5. Anchor pads at the four corners
    for x_sign in [1, -1]:
        for y_sign in [1, -1]:
            # Compute where the last spring fold ends
            last_x = (
                x_sign * shl * 0.5 + (n_spring_folds - 1) * x_sign * spring_fold_pitch
            )
            if n_spring_folds % 2 == 1:
                last_y = y_sign * (shw + spring_length)
            else:
                last_y = y_sign * shw

            c.add_polygon(
                [
                    (
                        last_x - anchor_size / 2,
                        last_y - anchor_size / 2 * y_sign + anchor_size * y_sign / 2,
                    ),
                    (
                        last_x + anchor_size / 2,
                        last_y - anchor_size / 2 * y_sign + anchor_size * y_sign / 2,
                    ),
                    (
                        last_x + anchor_size / 2,
                        last_y + anchor_size / 2 * y_sign + anchor_size * y_sign / 2,
                    ),
                    (
                        last_x - anchor_size / 2,
                        last_y + anchor_size / 2 * y_sign + anchor_size * y_sign / 2,
                    ),
                ],
                layer=layer,
            )

    return c

comb_drive

doubly_clamped_beam

doubly_clamped_beam

doubly_clamped_beam(
    beam_width: float = 1.0,
    beam_length: float = 30.0,
    anchor_width: float = 5.0,
    anchor_length: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component

Returns a doubly clamped beam fixed at both ends.

Two anchor pads connected by a thin beam, centered at the origin.

Parameters:

Name Type Description Default
beam_width float

width of the beam.

1.0
beam_length float

length of the beam between the two anchors.

30.0
anchor_width float

width (vertical) of each anchor pad.

5.0
anchor_length float

length (horizontal) of each anchor pad.

5.0
layer LayerSpec

layer spec.

'WG'
port_type str

port type for electrical ports.

'electrical'
Source code in gdsfactory/components/mems/doubly_clamped_beam.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
@gf.cell_with_module_name(tags=["mems"])
def doubly_clamped_beam(
    beam_width: float = 1.0,
    beam_length: float = 30.0,
    anchor_width: float = 5.0,
    anchor_length: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component:
    """Returns a doubly clamped beam fixed at both ends.

    Two anchor pads connected by a thin beam, centered at the origin.

    Args:
        beam_width: width of the beam.
        beam_length: length of the beam between the two anchors.
        anchor_width: width (vertical) of each anchor pad.
        anchor_length: length (horizontal) of each anchor pad.
        layer: layer spec.
        port_type: port type for electrical ports.
    """
    c = Component()

    total_length = 2 * anchor_length + beam_length
    x_start = -total_length / 2

    # Left anchor
    c.add_polygon(
        [
            (x_start, -anchor_width / 2),
            (x_start + anchor_length, -anchor_width / 2),
            (x_start + anchor_length, anchor_width / 2),
            (x_start, anchor_width / 2),
        ],
        layer=layer,
    )

    # Central beam
    beam_x0 = x_start + anchor_length
    c.add_polygon(
        [
            (beam_x0, -beam_width / 2),
            (beam_x0 + beam_length, -beam_width / 2),
            (beam_x0 + beam_length, beam_width / 2),
            (beam_x0, beam_width / 2),
        ],
        layer=layer,
    )

    # Right anchor
    right_x0 = beam_x0 + beam_length
    c.add_polygon(
        [
            (right_x0, -anchor_width / 2),
            (right_x0 + anchor_length, -anchor_width / 2),
            (right_x0 + anchor_length, anchor_width / 2),
            (right_x0, anchor_width / 2),
        ],
        layer=layer,
    )

    # Port at left anchor outside edge
    c.add_port(
        "e1",
        center=(x_start, 0),
        width=anchor_width,
        orientation=180,
        layer=layer,
        port_type=port_type,
    )

    # Port at right anchor outside edge
    c.add_port(
        "e2",
        center=(right_x0 + anchor_length, 0),
        width=anchor_width,
        orientation=0,
        layer=layer,
        port_type=port_type,
    )

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

doubly_clamped_beam

folded_spring

folded_spring

folded_spring(
    beam_width: float = 0.5,
    beam_length: float = 20.0,
    n_folds: int = 4,
    fold_gap: float = 1.0,
    anchor_width: float = 5.0,
    anchor_length: float = 3.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component

Returns a folded flexure spring (serpentine meander).

Alternating horizontal beams connected at their ends forming a serpentine pattern. Starts at a bottom anchor and ends at a top anchor.

Parameters:

Name Type Description Default
beam_width float

width of each beam segment.

0.5
beam_length float

length of each horizontal beam segment.

20.0
n_folds int

number of horizontal beam segments.

4
fold_gap float

vertical gap between adjacent beams.

1.0
anchor_width float

width (horizontal) of the anchor pads.

5.0
anchor_length float

length (vertical) of the anchor pads.

3.0
layer LayerSpec

layer spec.

'WG'
port_type str

port type for electrical ports.

'electrical'
Source code in gdsfactory/components/mems/folded_spring.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
@gf.cell_with_module_name(tags=["mems"])
def folded_spring(
    beam_width: float = 0.5,
    beam_length: float = 20.0,
    n_folds: int = 4,
    fold_gap: float = 1.0,
    anchor_width: float = 5.0,
    anchor_length: float = 3.0,
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component:
    """Returns a folded flexure spring (serpentine meander).

    Alternating horizontal beams connected at their ends forming a
    serpentine pattern. Starts at a bottom anchor and ends at a top anchor.

    Args:
        beam_width: width of each beam segment.
        beam_length: length of each horizontal beam segment.
        n_folds: number of horizontal beam segments.
        fold_gap: vertical gap between adjacent beams.
        anchor_width: width (horizontal) of the anchor pads.
        anchor_length: length (vertical) of the anchor pads.
        layer: layer spec.
        port_type: port type for electrical ports.
    """
    c = Component()

    # Bottom anchor, centered horizontally at x=0
    anchor_bottom_y = -anchor_length
    c.add_polygon(
        [
            (-anchor_width / 2, anchor_bottom_y),
            (anchor_width / 2, anchor_bottom_y),
            (anchor_width / 2, 0),
            (-anchor_width / 2, 0),
        ],
        layer=layer,
    )

    # Draw serpentine beams
    # Each beam is horizontal. Even-indexed beams go from x=0 to x=beam_length,
    # odd-indexed beams go from x=0 to x=beam_length as well, but connected
    # on alternating sides.
    y_cursor = 0.0

    for i in range(n_folds):
        y_bottom = y_cursor
        y_top = y_cursor + beam_width

        if i % 2 == 0:
            # Beam extends to the right from x=0
            c.add_polygon(
                [
                    (0, y_bottom),
                    (beam_length, y_bottom),
                    (beam_length, y_top),
                    (0, y_top),
                ],
                layer=layer,
            )
        else:
            # Beam extends to the left from x=beam_length
            c.add_polygon(
                [
                    (0, y_bottom),
                    (beam_length, y_bottom),
                    (beam_length, y_top),
                    (0, y_top),
                ],
                layer=layer,
            )

        # Add connecting segment at the end to the next beam
        if i < n_folds - 1:
            conn_y_bottom = y_top
            conn_y_top = y_top + fold_gap

            if i % 2 == 0:
                # Connect on the right side
                c.add_polygon(
                    [
                        (beam_length - beam_width, conn_y_bottom),
                        (beam_length, conn_y_bottom),
                        (beam_length, conn_y_top),
                        (beam_length - beam_width, conn_y_top),
                    ],
                    layer=layer,
                )
            else:
                # Connect on the left side
                c.add_polygon(
                    [
                        (0, conn_y_bottom),
                        (beam_width, conn_y_bottom),
                        (beam_width, conn_y_top),
                        (0, conn_y_top),
                    ],
                    layer=layer,
                )

        y_cursor += beam_width + fold_gap

    # Top anchor
    top_y = y_cursor - fold_gap  # top of last beam
    # Determine x position of top anchor based on last beam's exit side
    if (n_folds - 1) % 2 == 0:
        # Last beam exits on right side (x=beam_length)
        anchor_top_x = beam_length
    else:
        # Last beam exits on left side (x=0)
        anchor_top_x = 0.0

    c.add_polygon(
        [
            (anchor_top_x - anchor_width / 2, top_y),
            (anchor_top_x + anchor_width / 2, top_y),
            (anchor_top_x + anchor_width / 2, top_y + anchor_length),
            (anchor_top_x - anchor_width / 2, top_y + anchor_length),
        ],
        layer=layer,
    )

    # Port at bottom anchor
    c.add_port(
        "e1",
        center=(0, anchor_bottom_y),
        width=anchor_width,
        orientation=270,
        layer=layer,
        port_type=port_type,
    )

    # Port at top anchor
    c.add_port(
        "e2",
        center=(anchor_top_x, top_y + anchor_length),
        width=anchor_width,
        orientation=90,
        layer=layer,
        port_type=port_type,
    )

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

folded_spring

gear

gear

gear(
    n_teeth: int = 20,
    module_size: float = 2.0,
    pressure_angle: float = 20.0,
    hub_radius: float | None = None,
    hub_hole_radius: float = 0.0,
    layer: LayerSpec = "WG",
) -> Component

Returns a gear with simplified trapezoidal teeth.

Parameters:

Name Type Description Default
n_teeth int

number of teeth.

20
module_size float

gear module (pitch diameter / number of teeth).

2.0
pressure_angle float

pressure angle in degrees.

20.0
hub_radius float | None

radius of the central hub disc. Defaults to root_radius * 0.6.

None
hub_hole_radius float

radius of a center hole (0 to disable).

0.0
layer LayerSpec

layer spec.

'WG'
Source code in gdsfactory/components/mems/gear.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
@gf.cell_with_module_name(tags=["mems"])
def gear(
    n_teeth: int = 20,
    module_size: float = 2.0,
    pressure_angle: float = 20.0,
    hub_radius: float | None = None,
    hub_hole_radius: float = 0.0,
    layer: LayerSpec = "WG",
) -> Component:
    """Returns a gear with simplified trapezoidal teeth.

    Args:
        n_teeth: number of teeth.
        module_size: gear module (pitch diameter / number of teeth).
        pressure_angle: pressure angle in degrees.
        hub_radius: radius of the central hub disc. Defaults to root_radius * 0.6.
        hub_hole_radius: radius of a center hole (0 to disable).
        layer: layer spec.
    """
    c = Component()

    pitch_radius = n_teeth * module_size / 2
    outer_radius = pitch_radius + module_size
    root_radius = pitch_radius - 1.25 * module_size

    if hub_radius is None:
        hub_radius = root_radius * 0.6

    pa_rad = np.radians(pressure_angle)

    # Angular pitch (full tooth + gap)
    angular_pitch = 2 * np.pi / n_teeth
    # Half-tooth angular width at pitch circle
    half_tooth_angle = angular_pitch / 4

    # Build gear outline as polygon points
    points = []

    for i in range(n_teeth):
        theta = i * angular_pitch

        # Tooth tip is narrower, root is wider based on pressure angle
        tip_half_angle = half_tooth_angle - np.tan(pa_rad) * module_size / pitch_radius
        root_half_angle = (
            half_tooth_angle + np.tan(pa_rad) * 1.25 * module_size / pitch_radius
        )

        # Root start (leading edge)
        a = theta - root_half_angle
        points.append((root_radius * np.cos(a), root_radius * np.sin(a)))

        # Tooth tip leading edge
        a = theta - tip_half_angle
        points.append((outer_radius * np.cos(a), outer_radius * np.sin(a)))

        # Tooth tip trailing edge
        a = theta + tip_half_angle
        points.append((outer_radius * np.cos(a), outer_radius * np.sin(a)))

        # Root end (trailing edge)
        a = theta + root_half_angle
        points.append((root_radius * np.cos(a), root_radius * np.sin(a)))

    c.add_polygon(points, layer=layer)

    # Hub disc
    n_hub_pts = 64
    hub_angles = np.linspace(0, 2 * np.pi, n_hub_pts, endpoint=False)
    hub_points = [(hub_radius * np.cos(a), hub_radius * np.sin(a)) for a in hub_angles]
    c.add_polygon(hub_points, layer=layer)

    # Center hole cutout
    if hub_hole_radius > 0:
        n_hole_pts = 64
        hole_angles = np.linspace(0, 2 * np.pi, n_hole_pts, endpoint=False)
        hole_points = [
            (hub_hole_radius * np.cos(a), hub_hole_radius * np.sin(a))
            for a in hole_angles
        ]
        c.add_polygon(hole_points, layer=layer)

    return c

gear

microfluidics

arrow_junction

arrow_junction

arrow_junction(
    main_width: float = 1.0,
    branch_width: float = 1.0,
    main_length: float = 20.0,
    branch_length: float = 10.0,
    branch_angle: float = 35.0,
    reservoir_radius: float = 0.0,
    n_reservoir_points: int = 64,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component

Returns a microfluidic arrow (Y) junction.

A main horizontal channel on the right side with two angled branches diverging to the left at +/- branch_angle from horizontal. The junction point is at the origin.

Parameters:

Name Type Description Default
main_width float

width of the main horizontal channel.

1.0
branch_width float

width of each angled branch channel.

1.0
main_length float

length of the main channel (extends to the right).

20.0
branch_length float

length of each angled branch.

10.0
branch_angle float

angle of each branch from horizontal (degrees).

35.0
reservoir_radius float

radius of circular reservoirs at endpoints (0 to disable).

0.0
n_reservoir_points int

number of polygon points for reservoir circles.

64
layer LayerSpec

layer spec.

'WG'
port_type str | None

None, optical, or electrical.

'optical'
Source code in gdsfactory/components/microfluidics/arrow_junction.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
@gf.cell_with_module_name(tags=["microfluidics"])
def arrow_junction(
    main_width: float = 1.0,
    branch_width: float = 1.0,
    main_length: float = 20.0,
    branch_length: float = 10.0,
    branch_angle: float = 35.0,
    reservoir_radius: float = 0.0,
    n_reservoir_points: int = 64,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component:
    """Returns a microfluidic arrow (Y) junction.

    A main horizontal channel on the right side with two angled branches
    diverging to the left at +/- branch_angle from horizontal. The
    junction point is at the origin.

    Args:
        main_width: width of the main horizontal channel.
        branch_width: width of each angled branch channel.
        main_length: length of the main channel (extends to the right).
        branch_length: length of each angled branch.
        branch_angle: angle of each branch from horizontal (degrees).
        reservoir_radius: radius of circular reservoirs at endpoints (0 to disable).
        n_reservoir_points: number of polygon points for reservoir circles.
        layer: layer spec.
        port_type: None, optical, or electrical.
    """
    c = Component()
    mhw = main_width / 2
    bhw = branch_width / 2
    angle_rad = np.radians(branch_angle)

    # Main horizontal channel from origin going right
    c.add_polygon(
        [
            (0, -mhw),
            (main_length, -mhw),
            (main_length, mhw),
            (0, mhw),
        ],
        layer=layer,
    )

    # Direction vectors for each branch (going to the left)
    for sign in [1, -1]:
        a = sign * angle_rad
        # Branch direction (pointing left/away from junction)
        dx = -np.cos(a)
        dy = np.sin(a)
        # Perpendicular to branch direction
        nx = -dy
        ny = dx

        # Four corners of branch rectangle
        x0 = 0.0
        y0 = 0.0
        x1 = x0 + branch_length * dx
        y1 = y0 + branch_length * dy

        pts = [
            (x0 + bhw * nx, y0 + bhw * ny),
            (x0 - bhw * nx, y0 - bhw * ny),
            (x1 - bhw * nx, y1 - bhw * ny),
            (x1 + bhw * nx, y1 + bhw * ny),
        ]
        c.add_polygon(pts, layer=layer)

    # Branch endpoint positions
    upper_end_x = -branch_length * np.cos(angle_rad)
    upper_end_y = branch_length * np.sin(angle_rad)
    lower_end_x = -branch_length * np.cos(angle_rad)
    lower_end_y = -branch_length * np.sin(angle_rad)

    # Reservoir circles at endpoints
    if reservoir_radius > 0:
        angles = np.linspace(0, 2 * np.pi, n_reservoir_points, endpoint=False)
        for cx, cy in [
            (main_length, 0),
            (upper_end_x, upper_end_y),
            (lower_end_x, lower_end_y),
        ]:
            pts = [
                (cx + reservoir_radius * np.cos(a), cy + reservoir_radius * np.sin(a))
                for a in angles
            ]
            c.add_polygon(pts, layer=layer)

    if port_type:
        prefix = "o" if port_type == "optical" else "e"
        # Main end (right)
        c.add_port(
            f"{prefix}1",
            center=(main_length, 0),
            width=main_width,
            orientation=0,
            layer=layer,
            port_type=port_type,
        )
        # Upper branch end
        c.add_port(
            f"{prefix}2",
            center=(upper_end_x, upper_end_y),
            width=branch_width,
            orientation=180 + branch_angle,
            layer=layer,
            port_type=port_type,
        )
        # Lower branch end
        c.add_port(
            f"{prefix}3",
            center=(lower_end_x, lower_end_y),
            width=branch_width,
            orientation=180 - branch_angle,
            layer=layer,
            port_type=port_type,
        )
        c.auto_rename_ports()

    return c

arrow_junction

h_junction

h_junction

h_junction(
    main_width: float = 1.0,
    branch_width: float = 1.0,
    main_length: float = 20.0,
    branch_length: float = 10.0,
    reservoir_radius: float = 0.0,
    n_reservoir_points: int = 64,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component

Returns a microfluidic H-junction.

A horizontal main channel with two vertical branches extending up and down from the center. Optionally adds circular reservoirs at the four endpoints.

Parameters:

Name Type Description Default
main_width float

width of the main horizontal channel.

1.0
branch_width float

width of the vertical branch channels.

1.0
main_length float

total length of the main channel.

20.0
branch_length float

length of each vertical branch.

10.0
reservoir_radius float

radius of circular reservoirs at endpoints (0 to disable).

0.0
n_reservoir_points int

number of polygon points for reservoir circles.

64
layer LayerSpec

layer spec.

'WG'
port_type str | None

None, optical, or electrical.

'optical'
Source code in gdsfactory/components/microfluidics/h_junction.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
@gf.cell_with_module_name(tags=["microfluidics"])
def h_junction(
    main_width: float = 1.0,
    branch_width: float = 1.0,
    main_length: float = 20.0,
    branch_length: float = 10.0,
    reservoir_radius: float = 0.0,
    n_reservoir_points: int = 64,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component:
    """Returns a microfluidic H-junction.

    A horizontal main channel with two vertical branches extending
    up and down from the center. Optionally adds circular reservoirs
    at the four endpoints.

    Args:
        main_width: width of the main horizontal channel.
        branch_width: width of the vertical branch channels.
        main_length: total length of the main channel.
        branch_length: length of each vertical branch.
        reservoir_radius: radius of circular reservoirs at endpoints (0 to disable).
        n_reservoir_points: number of polygon points for reservoir circles.
        layer: layer spec.
        port_type: None, optical, or electrical.
    """
    c = Component()
    mhw = main_width / 2
    bhw = branch_width / 2
    half_main = main_length / 2

    # Main horizontal channel centered at origin
    c.add_polygon(
        [
            (-half_main, -mhw),
            (half_main, -mhw),
            (half_main, mhw),
            (-half_main, mhw),
        ],
        layer=layer,
    )

    # Vertical branch going up from center
    c.add_polygon(
        [
            (-bhw, mhw),
            (bhw, mhw),
            (bhw, mhw + branch_length),
            (-bhw, mhw + branch_length),
        ],
        layer=layer,
    )

    # Vertical branch going down from center
    c.add_polygon(
        [
            (-bhw, -mhw),
            (bhw, -mhw),
            (bhw, -mhw - branch_length),
            (-bhw, -mhw - branch_length),
        ],
        layer=layer,
    )

    # Reservoir circles at endpoints
    if reservoir_radius > 0:
        angles = np.linspace(0, 2 * np.pi, n_reservoir_points, endpoint=False)
        for cx, cy in [
            (-half_main, 0),
            (half_main, 0),
            (0, mhw + branch_length),
            (0, -mhw - branch_length),
        ]:
            pts = [
                (cx + reservoir_radius * np.cos(a), cy + reservoir_radius * np.sin(a))
                for a in angles
            ]
            c.add_polygon(pts, layer=layer)

    if port_type:
        prefix = "o" if port_type == "optical" else "e"
        c.add_port(
            f"{prefix}1",
            center=(-half_main, 0),
            width=main_width,
            orientation=180,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}2",
            center=(half_main, 0),
            width=main_width,
            orientation=0,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}3",
            center=(0, mhw + branch_length),
            width=branch_width,
            orientation=90,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}4",
            center=(0, -mhw - branch_length),
            width=branch_width,
            orientation=270,
            layer=layer,
            port_type=port_type,
        )
        c.auto_rename_ports()

    return c

h_junction

meander_channel

meander_channel

meander_channel(
    channel_width: float = 1.0,
    n_turns: int = 5,
    turn_spacing: float = 5.0,
    straight_length: float = 20.0,
    reservoir_length: float = 5.0,
    reservoir_height: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component

Returns a microfluidic meander (serpentine) channel.

Builds a series of connected rectangles forming a serpentine path. Starts from the left, goes right for straight_length, turns up/down by turn_spacing, goes back left, and repeats for n_turns. Rectangular reservoirs are added at the inlet and outlet when reservoir_length > 0.

Parameters:

Name Type Description Default
channel_width float

width of the channel.

1.0
n_turns int

number of straight segments (horizontal passes).

5
turn_spacing float

center-to-center vertical spacing between passes.

5.0
straight_length float

length of each horizontal segment.

20.0
reservoir_length float

length of the reservoirs at inlet/outlet.

5.0
reservoir_height float

height of the reservoirs at inlet/outlet.

5.0
layer LayerSpec

layer spec.

'WG'
port_type str | None

None, optical, or electrical.

'optical'
Source code in gdsfactory/components/microfluidics/meander_channel.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
@gf.cell_with_module_name(tags=["microfluidics"])
def meander_channel(
    channel_width: float = 1.0,
    n_turns: int = 5,
    turn_spacing: float = 5.0,
    straight_length: float = 20.0,
    reservoir_length: float = 5.0,
    reservoir_height: float = 5.0,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component:
    """Returns a microfluidic meander (serpentine) channel.

    Builds a series of connected rectangles forming a serpentine path.
    Starts from the left, goes right for straight_length, turns up/down
    by turn_spacing, goes back left, and repeats for n_turns.
    Rectangular reservoirs are added at the inlet and outlet when
    reservoir_length > 0.

    Args:
        channel_width: width of the channel.
        n_turns: number of straight segments (horizontal passes).
        turn_spacing: center-to-center vertical spacing between passes.
        straight_length: length of each horizontal segment.
        reservoir_length: length of the reservoirs at inlet/outlet.
        reservoir_height: height of the reservoirs at inlet/outlet.
        layer: layer spec.
        port_type: None, optical, or electrical.
    """
    c = Component()
    hw = channel_width / 2

    x = 0.0
    y = 0.0

    for i in range(n_turns):
        going_right = i % 2 == 0

        # Horizontal segment
        if going_right:
            x_start = x
            x_end = x + straight_length
        else:
            x_start = x
            x_end = x - straight_length

        c.add_polygon(
            [
                (x_start, y - hw),
                (x_end, y - hw),
                (x_end, y + hw),
                (x_start, y + hw),
            ],
            layer=layer,
        )

        if going_right:
            x = x_end
        else:
            x = x_end

        # Vertical connector to next pass (skip after last segment)
        if i < n_turns - 1:
            y_next = y + turn_spacing
            cx = x
            c.add_polygon(
                [
                    (cx - hw, y - hw),
                    (cx + hw, y - hw),
                    (cx + hw, y_next + hw),
                    (cx - hw, y_next + hw),
                ],
                layer=layer,
            )
            y = y_next

    # Determine inlet and outlet positions
    # Inlet is at the start of the first segment
    inlet_x = 0.0
    inlet_y = 0.0

    # Outlet is at the end of the last segment
    last_going_right = (n_turns - 1) % 2 == 0
    outlet_x = x
    outlet_y = y

    # Add reservoirs
    if reservoir_length > 0:
        rh = reservoir_height / 2
        # Inlet reservoir (extends to the left)
        c.add_polygon(
            [
                (inlet_x - reservoir_length, inlet_y - rh),
                (inlet_x, inlet_y - rh),
                (inlet_x, inlet_y + rh),
                (inlet_x - reservoir_length, inlet_y + rh),
            ],
            layer=layer,
        )
        # Outlet reservoir (extends beyond the outlet)
        if last_going_right:
            c.add_polygon(
                [
                    (outlet_x, outlet_y - rh),
                    (outlet_x + reservoir_length, outlet_y - rh),
                    (outlet_x + reservoir_length, outlet_y + rh),
                    (outlet_x, outlet_y + rh),
                ],
                layer=layer,
            )
            outlet_port_x = outlet_x + reservoir_length
            outlet_orientation = 0
        else:
            c.add_polygon(
                [
                    (outlet_x - reservoir_length, outlet_y - rh),
                    (outlet_x, outlet_y - rh),
                    (outlet_x, outlet_y + rh),
                    (outlet_x - reservoir_length, outlet_y + rh),
                ],
                layer=layer,
            )
            outlet_port_x = outlet_x - reservoir_length
            outlet_orientation = 180

        inlet_port_x = inlet_x - reservoir_length
        inlet_port_width = reservoir_height
        outlet_port_width = reservoir_height
    else:
        inlet_port_x = inlet_x
        inlet_port_width = channel_width
        if last_going_right:
            outlet_port_x = outlet_x
            outlet_orientation = 0
        else:
            outlet_port_x = outlet_x
            outlet_orientation = 180
        outlet_port_width = channel_width

    if port_type:
        prefix = "o" if port_type == "optical" else "e"
        c.add_port(
            f"{prefix}1",
            center=(inlet_port_x, inlet_y),
            width=inlet_port_width,
            orientation=180,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}2",
            center=(outlet_port_x, outlet_y),
            width=outlet_port_width,
            orientation=outlet_orientation,
            layer=layer,
            port_type=port_type,
        )
        c.auto_rename_ports()

    return c

meander_channel

t_junction

t_junction

t_junction(
    main_width: float = 1.0,
    branch_width: float = 1.0,
    main_length: float = 20.0,
    branch_length: float = 10.0,
    reservoir_radius: float = 0.0,
    n_reservoir_points: int = 64,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component

Returns a microfluidic T-junction.

A horizontal main channel with a vertical branch going up from the center. Optionally adds circular reservoirs at the three endpoints.

Parameters:

Name Type Description Default
main_width float

width of the main horizontal channel.

1.0
branch_width float

width of the vertical branch channel.

1.0
main_length float

total length of the main channel.

20.0
branch_length float

length of the vertical branch.

10.0
reservoir_radius float

radius of circular reservoirs at endpoints (0 to disable).

0.0
n_reservoir_points int

number of polygon points for reservoir circles.

64
layer LayerSpec

layer spec.

'WG'
port_type str | None

None, optical, or electrical.

'optical'
Source code in gdsfactory/components/microfluidics/t_junction.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
@gf.cell_with_module_name(tags=["microfluidics"])
def t_junction(
    main_width: float = 1.0,
    branch_width: float = 1.0,
    main_length: float = 20.0,
    branch_length: float = 10.0,
    reservoir_radius: float = 0.0,
    n_reservoir_points: int = 64,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component:
    """Returns a microfluidic T-junction.

    A horizontal main channel with a vertical branch going up from
    the center. Optionally adds circular reservoirs at the three
    endpoints.

    Args:
        main_width: width of the main horizontal channel.
        branch_width: width of the vertical branch channel.
        main_length: total length of the main channel.
        branch_length: length of the vertical branch.
        reservoir_radius: radius of circular reservoirs at endpoints (0 to disable).
        n_reservoir_points: number of polygon points for reservoir circles.
        layer: layer spec.
        port_type: None, optical, or electrical.
    """
    c = Component()
    mhw = main_width / 2
    bhw = branch_width / 2
    half_main = main_length / 2

    # Main horizontal channel centered at origin
    c.add_polygon(
        [
            (-half_main, -mhw),
            (half_main, -mhw),
            (half_main, mhw),
            (-half_main, mhw),
        ],
        layer=layer,
    )

    # Vertical branch going up from center
    c.add_polygon(
        [
            (-bhw, mhw),
            (bhw, mhw),
            (bhw, mhw + branch_length),
            (-bhw, mhw + branch_length),
        ],
        layer=layer,
    )

    # Reservoir circles at endpoints
    if reservoir_radius > 0:
        angles = np.linspace(0, 2 * np.pi, n_reservoir_points, endpoint=False)
        for cx, cy in [
            (-half_main, 0),
            (half_main, 0),
            (0, mhw + branch_length),
        ]:
            pts = [
                (cx + reservoir_radius * np.cos(a), cy + reservoir_radius * np.sin(a))
                for a in angles
            ]
            c.add_polygon(pts, layer=layer)

    if port_type:
        prefix = "o" if port_type == "optical" else "e"
        c.add_port(
            f"{prefix}1",
            center=(-half_main, 0),
            width=main_width,
            orientation=180,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}2",
            center=(half_main, 0),
            width=main_width,
            orientation=0,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}3",
            center=(0, mhw + branch_length),
            width=branch_width,
            orientation=90,
            layer=layer,
            port_type=port_type,
        )
        c.auto_rename_ports()

    return c

t_junction

mmis

mmi

mmi

mmi(
    inputs: int = 1,
    outputs: int = 4,
    width: float | None = None,
    width_taper: float = 1.0,
    length_taper: float = 10.0,
    length_mmi: float = 5.5,
    width_mmi: float = 5,
    gap_input_tapers: float = 0.25,
    gap_output_tapers: float = 0.25,
    taper: ComponentSpec = "taper",
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
    input_positions: list[float] | None = None,
    output_positions: list[float] | None = None,
) -> Component

Mxn MultiMode Interferometer (MMI).

Parameters:

Name Type Description Default
inputs int

number of inputs.

1
outputs int

number of outputs.

4
width float | None

input and output straight width. Defaults to cross_section.

None
width_taper float

interface between input straights and mmi region.

1.0
length_taper float

into the mmi region.

10.0
length_mmi float

in x direction.

5.5
width_mmi float

in y direction.

5
gap_input_tapers float

gap between input tapers from edge to edge.

0.25
gap_output_tapers float

gap between output tapers from edge to edge.

0.25
taper ComponentSpec

taper function.

'taper'
straight ComponentSpec

straight function.

'straight'
cross_section CrossSectionSpec

specification (CrossSection, string or dict).

'strip'
input_positions list[float] | None

optional positions of the inputs.

None
output_positions list[float] | None

optional positions of the outputs.

   length_mmi
    <------>
    ________
   |        |
__/          \__

o2 __ __ o3 \ / _ _ _ | | _ _ _ | gap_output_tapers / _ o1 __ __ o4 \ / |_____| | | <-> length_taper

None
Source code in gdsfactory/components/mmis/mmi.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
@gf.cell_with_module_name(schematic_function=ckt_schematic, tags=["mmis"])
def mmi(
    inputs: int = 1,
    outputs: int = 4,
    width: float | None = None,
    width_taper: float = 1.0,
    length_taper: float = 10.0,
    length_mmi: float = 5.5,
    width_mmi: float = 5,
    gap_input_tapers: float = 0.25,
    gap_output_tapers: float = 0.25,
    taper: ComponentSpec = "taper",
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
    input_positions: list[float] | None = None,
    output_positions: list[float] | None = None,
) -> Component:
    r"""Mxn MultiMode Interferometer (MMI).

    Args:
        inputs: number of inputs.
        outputs: number of outputs.
        width: input and output straight width. Defaults to cross_section.
        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_input_tapers: gap between input tapers from edge to edge.
        gap_output_tapers: gap between output tapers from edge to edge.
        taper: taper function.
        straight: straight function.
        cross_section: specification (CrossSection, string or dict).
        input_positions: optional positions of the inputs.
        output_positions: optional positions of the outputs.

                   length_mmi
                    <------>
                    ________
                   |        |
                __/          \__
            o2  __            __  o3
                  \          /_ _ _ _
                  |         | _ _ _ _| gap_output_tapers
                __/          \__
            o1  __            __  o4
                  \          /
                   |________|
                 | |
                 <->
            length_taper
    """
    c = Component()
    gap_input_tapers = gf.snap.snap_to_grid(gap_input_tapers, grid_factor=2)
    gap_output_tapers = gf.snap.snap_to_grid(gap_output_tapers, grid_factor=2)
    w_taper = width_taper
    x = gf.get_cross_section(cross_section)
    xs_mmi = gf.get_cross_section(cross_section, width=width_mmi)
    width = width or x.width

    _taper = gf.get_component(
        taper,
        length=length_taper,
        width1=width,
        width2=w_taper,
        cross_section=cross_section,
    )

    _ = c << gf.get_component(straight, length=length_mmi, cross_section=xs_mmi)
    wg_spacing_input = gap_input_tapers + width_taper
    wg_spacing_output = gap_output_tapers + width_taper

    yi = -(inputs - 1) * wg_spacing_input / 2
    yo = -(outputs - 1) * wg_spacing_output / 2

    input_positions = input_positions or [
        yi + i * wg_spacing_input for i in range(inputs)
    ]
    output_positions = output_positions or [
        yo + i * wg_spacing_output for i in range(outputs)
    ]

    temp_component = Component()

    ports = [
        temp_component.add_port(
            name=f"in_{i}",
            orientation=180,
            center=(0, y),
            width=w_taper,
            layer=gf.get_layer(x.layer),
            cross_section=x,
        )
        for i, y in enumerate(input_positions)
    ]

    ports += [
        temp_component.add_port(
            name=f"out_{i}",
            orientation=0,
            center=(+length_mmi, y),
            width=w_taper,
            layer=gf.get_layer(x.layer),
            cross_section=x,
        )
        for i, y in enumerate(output_positions)
    ]

    for port in ports:
        taper_ref = c << _taper
        taper_ref.connect("o2", port, allow_width_mismatch=True)
        c.add_port(name=port.name, port=taper_ref.ports["o1"])

    x.add_bbox(c)
    c.auto_rename_ports()
    c.flatten()
    return c

mmi

mmi1x2

mmi1x2

mmi1x2(
    width: float | None = None,
    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: ComponentSpec = taper_function,
    straight: ComponentSpec = straight_function,
    cross_section: CrossSectionSpec = "strip",
) -> Component

1x2 MultiMode Interferometer (MMI).

Parameters:

Name Type Description Default
width float | None

input and output straight width. Defaults to cross_section width.

None
width_taper float

interface between input straights and mmi region.

1.0
length_taper float

into the mmi region.

10.0
length_mmi float

in x direction.

5.5
width_mmi float

in y direction.

2.5
gap_mmi float

gap between tapered wg.

0.25
taper ComponentSpec

taper function.

taper
straight ComponentSpec

straight function.

straight
cross_section CrossSectionSpec

specification (CrossSection, string or dict).

length_mmi <------> _ | | | _ | __ o2 / / _ _ _ o1 __ | _ _ _ | gap_mmi \ _ | __ o3 | / |_____|

<->

'strip'
Source code in gdsfactory/components/mmis/mmi1x2.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
@gf.cell_with_module_name(schematic_function=mmi_1x2_schematic, tags=["mmis"])
def mmi1x2(
    width: float | None = None,
    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: ComponentSpec = taper_function,
    straight: ComponentSpec = straight_function,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""1x2 MultiMode Interferometer (MMI).

    Args:
        width: input and output straight width. Defaults to cross_section 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.
        straight: straight function.
        cross_section: specification (CrossSection, string or dict).

               length_mmi
                <------>
                ________
               |        |
               |         \__
               |          __  o2
            __/          /_ _ _ _
         o1 __          | _ _ _ _| gap_mmi
              \          \__
               |          __  o3
               |         /
               |________|

             <->
        length_taper

    """
    c = Component()
    gap_mmi = gf.snap.snap_to_grid(gap_mmi, grid_factor=2)
    x = gf.get_cross_section(cross_section)
    xs_mmi = gf.get_cross_section(cross_section, width=width_mmi)
    width = width or x.width

    _taper = gf.get_component(
        taper,
        length=length_taper,
        width1=width,
        width2=width_taper,
        cross_section=cross_section,
    )

    a = gap_mmi / 2 + width_taper / 2
    _ = c << gf.get_component(straight, length=length_mmi, cross_section=xs_mmi)

    temp_component = Component()

    ports = [
        temp_component.add_port(
            name="o1",
            orientation=180,
            center=(0, 0),
            width=width_taper,
            layer=gf.get_layer(x.layer),
            cross_section=x,
        ),
        temp_component.add_port(
            name="o2",
            orientation=0,
            center=(+length_mmi, +a),
            width=width_taper,
            layer=gf.get_layer(x.layer),
            cross_section=x,
        ),
        temp_component.add_port(
            name="o3",
            orientation=0,
            center=(+length_mmi, -a),
            width=width_taper,
            layer=gf.get_layer(x.layer),
            cross_section=x,
        ),
    ]

    for port in ports:
        taper_ref = c << _taper
        taper_ref.connect(port="o2", other=port, allow_width_mismatch=True)
        c.add_port(name=port.name, port=taper_ref.ports["o1"])

    c.flatten()
    return c

mmi1x2

mmi1x2_with_sbend

mmi1x2_with_sbend

mmi1x2_with_sbend(
    with_sbend: bool = True,
    s_bend: ComponentFactory = bend_s,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Returns 1x2 splitter for Cband.

https://opg.optica.org/oe/fulltext.cfm?uri=oe-21-1-1310&id=248418

Parameters:

Name Type Description Default
with_sbend bool

add sbend.

True
s_bend ComponentFactory

S-bend spec.

bend_s
cross_section CrossSectionSpec

spec.

'strip'
Source code in gdsfactory/components/mmis/mmi1x2_with_sbend.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@gf.cell_with_module_name(schematic_function=mmi_1x2_schematic, tags=["mmis"])
def mmi1x2_with_sbend(
    with_sbend: bool = True,
    s_bend: ComponentFactory = bend_s,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Returns 1x2 splitter for Cband.

    https://opg.optica.org/oe/fulltext.cfm?uri=oe-21-1-1310&id=248418

    Args:
        with_sbend: add sbend.
        s_bend: S-bend spec.
        cross_section: spec.
    """
    c = gf.Component()

    P = gf.path.straight(length=2, npoints=100)
    xs = gf.get_cross_section(cross_section)
    xs0 = xs.copy(width_function=mmi_widths)
    _ = c << gf.path.extrude(P, cross_section=xs0)

    # Add "stub" straight sections for ports
    straight = gf.components.straight(length=0.25, cross_section=cross_section)
    sl = c << straight
    sl.center = (-0.125, 0)
    s_topr = c << straight
    s_topr.center = (2.125, 0.35)
    s_botr = c << straight
    s_botr.center = (2.125, -0.35)

    if with_sbend:
        sbend = s_bend(cross_section=cross_section)
        top_sbend = c << sbend
        bot_sbend = c << sbend
        top_sbend.connect("o1", other=s_topr.ports["o2"])
        bot_sbend.connect("o1", other=s_botr.ports["o2"], mirror=True)
        c.add_port("o1", port=sl.ports["o1"])
        c.add_port("o2", port=top_sbend.ports["o2"])
        c.add_port("o3", port=bot_sbend.ports["o2"])

    else:
        c.add_port("o1", port=sl.ports["o1"])
        c.add_port("o2", port=s_topr.ports["o2"])
        c.add_port("o3", port=s_botr.ports["o2"])

    c.flatten()
    return c

mmi1x2_with_sbend

mmi2x2

mmi2x2

mmi2x2(
    width: float | None = None,
    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: ComponentSpec = taper_function,
    straight: ComponentSpec = straight_function,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Mmi 2x2.

Parameters:

Name Type Description Default
width float | None

input and output straight width.

None
width_taper float

interface between input straights and mmi region.

1.0
length_taper float

into the mmi region.

10.0
length_mmi float

in x direction.

5.5
width_mmi float

in y direction.

2.5
gap_mmi float

(width_taper + gap between tapered wg)/2.

0.25
taper ComponentSpec

taper function.

taper
straight ComponentSpec

straight function.

straight
cross_section CrossSectionSpec

spec.

   length_mmi
    <------>
    ________
   |        |
__/          \__

o2 __ __ o3 \ / _ _ _ | | _ _ _ | gap_mmi / _ o1 __ __ o4 \ / |_____|

 <->

length_taper

'strip'
Source code in gdsfactory/components/mmis/mmi2x2.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@gf.cell_with_module_name(schematic_function=mmi_2x2_schematic, tags=["mmis"])
def mmi2x2(
    width: float | None = None,
    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: ComponentSpec = taper_function,
    straight: ComponentSpec = straight_function,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""Mmi 2x2.

    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: (width_taper + gap between tapered wg)/2.
        taper: taper function.
        straight: straight function.
        cross_section: spec.

                   length_mmi
                    <------>
                    ________
                   |        |
                __/          \__
            o2  __            __  o3
                  \          /_ _ _ _
                  |         | _ _ _ _| gap_mmi
                __/          \__
            o1  __            __  o4
                  \          /
                   |________|

                 <->
            length_taper

    """
    c = gf.Component()
    gap_mmi = gf.snap.snap_to_grid(gap_mmi, grid_factor=2)
    w_taper = width_taper
    x = gf.get_cross_section(cross_section)
    width = width or x.width

    _taper = gf.get_component(
        taper,
        length=length_taper,
        width1=width,
        width2=w_taper,
        cross_section=cross_section,
    )

    a = gap_mmi / 2 + width_taper / 2
    _ = c << gf.get_component(
        straight, length=length_mmi, width=width_mmi, cross_section=cross_section
    )

    temp_component = Component()

    ports = [
        temp_component.add_port(
            name="o1", orientation=180, center=(0, -a), width=w_taper, cross_section=x
        ),
        temp_component.add_port(
            name="o2", orientation=180, center=(0, +a), width=w_taper, cross_section=x
        ),
        temp_component.add_port(
            name="o3",
            orientation=0,
            center=(length_mmi, +a),
            width=w_taper,
            cross_section=x,
        ),
        temp_component.add_port(
            name="o4",
            orientation=0,
            center=(length_mmi, -a),
            width=w_taper,
            cross_section=x,
        ),
    ]

    for port in ports:
        taper_ref = c << _taper
        taper_ref.connect(port="o2", other=port, allow_width_mismatch=True)
        c.add_port(name=port.name, port=taper_ref.ports["o1"])

    c.flatten()
    return c

mmi2x2

mmi2x2_with_sbend

mmi2x2_with_sbend

mmi2x2_with_sbend(
    with_sbend: bool = True,
    s_bend: ComponentFactory = bend_s,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Returns mmi2x2 for Cband.

C_band 2x2MMI in 220nm thick silicon https://opg.optica.org/oe/fulltext.cfm?uri=oe-25-23-28957&id=376719

Parameters:

Name Type Description Default
with_sbend bool

add sbend.

True
s_bend ComponentFactory

S-bend function.

bend_s
cross_section CrossSectionSpec

spec.

'strip'
Source code in gdsfactory/components/mmis/mmi2x2_with_sbend.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@gf.cell_with_module_name(schematic_function=mmi_2x2_schematic, tags=["mmis"])
def mmi2x2_with_sbend(
    with_sbend: bool = True,
    s_bend: ComponentFactory = bend_s,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Returns mmi2x2 for Cband.

    C_band 2x2MMI in 220nm thick silicon
    https://opg.optica.org/oe/fulltext.cfm?uri=oe-25-23-28957&id=376719

    Args:
        with_sbend: add sbend.
        s_bend: S-bend function.
        cross_section: spec.
    """

    def mmi_widths(t: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]:
        return np.array([2 * 0.7 + 0.2, 1.48, 1.48, 1.48, 1.6])

    c = gf.Component()

    P = gf.path.straight(length=2 * 2.4 + 2 * 1.6, npoints=5)
    xs = gf.get_cross_section(cross_section)
    xs0 = xs.copy(width_function=mmi_widths)
    _ = c << gf.path.extrude(P, cross_section=xs0)

    # Add input and output tapers
    taper = gf.components.taper(
        length=1, width1=0.5, width2=0.7, cross_section=cross_section
    )
    topl_taper = c << taper
    topl_taper.move((-1, 0.45))
    botl_taper = c << taper
    botl_taper.move((-1, -0.45))

    topr_taper = c << taper
    topr_taper.dmirror(p1=(0, 1), p2=(0, 0))
    topr_taper.move((9, 0.45))

    botr_taper = c << taper
    botr_taper.dmirror(p1=(0, 1), p2=(0, 0))
    botr_taper.move((9, -0.45))

    if with_sbend:
        sbend = s_bend(cross_section=cross_section)

        topl_sbend = c << sbend
        botl_sbend = c << sbend
        topr_sbend = c << sbend
        botr_sbend = c << sbend

        topl_sbend.connect("o1", other=topl_taper.ports["o1"], mirror=True)
        botl_sbend.connect("o1", other=botl_taper.ports["o1"])
        topr_sbend.connect("o1", other=topr_taper.ports["o1"])
        botr_sbend.connect("o1", other=botr_taper.ports["o1"], mirror=True)

        c.add_port("o1", port=botl_sbend.ports["o2"])
        c.add_port("o2", port=topl_sbend.ports["o2"])
        c.add_port("o3", port=topr_sbend.ports["o2"])
        c.add_port("o4", port=botr_sbend.ports["o2"])

    else:
        c.add_port("o2", port=topl_taper.ports["o1"])
        c.add_port("o1", port=botl_taper.ports["o1"])
        c.add_port("o3", port=topr_taper.ports["o1"])
        c.add_port("o4", port=botr_taper.ports["o1"])

    c.flatten()
    return c

mmi2x2_with_sbend

mmi_90degree_hybrid

mmi_90degree_hybrid

mmi_90degree_hybrid(
    width: float = 0.5,
    width_taper: float = 1.7,
    length_taper: float = 40.0,
    length_mmi: float = 175.0,
    width_mmi: float = 10.0,
    gap_mmi: float = 0.8,
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
) -> Component

90 degree hybrid based on a 4x4 MMI.

Default values from Watanabe et al., "Coherent few mode demultiplexer realized as a 2D grating coupler array in silicon", Optics Express 28(24), 2020

It could be interesting to consider the design in Guan et al., "Compact and low loss 90° optical hybrid on a silicon-on-insulator platform", Optics Express 25(23), 2017

Parameters:

Name Type Description Default
width float

input and output straight width.

0.5
width_taper float

interface between input straights and mmi region.

1.7
length_taper float

into the mmi region.

40.0
length_mmi float

in x direction.

175.0
width_mmi float

in y direction.

10.0
gap_mmi float

(width_taper + gap between tapered wg)/2.

0.8
straight ComponentSpec

straight function.

'straight'
with_bbox

box in bbox_layers and bbox_offsets avoid DRC sharp edges.

required
cross_section CrossSectionSpec

spec.

'strip'
               length_mmi
                <------>
                ________
               |        |
            __/          \__
 signal_in  __            __  I_out1
              \          /_ _ _ _
              |         | _ _ _ _| gap_mmi
              |          \__
              |           __  Q_out1
              |          /
              |        |
              |
            __/          \__
    LO_in   __            __  Q_out2
              \          /_ _ _ _
              |         | _ _ _ _| gap_mmi
              |          \__
              |           __  I_out2
              |          /
              | ________|
         <->
    length_taper
Source code in gdsfactory/components/mmis/mmi_90degree_hybrid.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
@gf.cell_with_module_name(schematic_function=ckt_schematic, tags=["mmis"])
def mmi_90degree_hybrid(
    width: float = 0.5,
    width_taper: float = 1.7,
    length_taper: float = 40.0,
    length_mmi: float = 175.0,
    width_mmi: float = 10.0,
    gap_mmi: float = 0.8,
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    r"""90 degree hybrid based on a 4x4 MMI.

    Default values from Watanabe et al.,
    "Coherent few mode demultiplexer realized as a
    2D grating coupler array in silicon", Optics Express 28(24), 2020

    It could be interesting to consider the design in Guan et al.,
    "Compact and low loss 90° optical hybrid on a silicon-on-insulator
    platform", Optics Express 25(23), 2017

    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: (width_taper + gap between tapered wg)/2.
        straight: straight function.
        with_bbox: box in bbox_layers and bbox_offsets avoid DRC sharp edges.
        cross_section: spec.

    ```text
                   length_mmi
                    <------>
                    ________
                   |        |
                __/          \__
     signal_in  __            __  I_out1
                  \          /_ _ _ _
                  |         | _ _ _ _| gap_mmi
                  |          \__
                  |           __  Q_out1
                  |          /
                  |        |
                  |
                __/          \__
        LO_in   __            __  Q_out2
                  \          /_ _ _ _
                  |         | _ _ _ _| gap_mmi
                  |          \__
                  |           __  I_out2
                  |          /
                  | ________|
    ```


                 <->
            length_taper
    """
    c = gf.Component()

    gap_mmi = gf.snap.snap_to_grid(gap_mmi, grid_factor=2)
    w_mmi = width_mmi
    w_taper = width_taper

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

    x = gf.get_cross_section(cross_section)

    _ = c << gf.get_component(
        straight,
        length=length_mmi,
        width=w_mmi,
        cross_section=cross_section,
    )

    y_signal_in = gap_mmi * 3 / 2 + width_taper * 3 / 2
    y_lo_in = -gap_mmi / 2 - width_taper / 2

    temp_component = Component()

    ports = [
        # Inputs
        temp_component.add_port(
            name="signal_in",
            orientation=180,
            center=(0, y_signal_in),
            width=w_taper,
            cross_section=x,
        ),
        temp_component.add_port(
            name="LO_in",
            orientation=180,
            center=(0, y_lo_in),
            width=w_taper,
            cross_section=x,
        ),
        # Outputs
        temp_component.add_port(
            name="I_out1",
            orientation=0,
            center=(length_mmi, y_signal_in),
            width=w_taper,
            cross_section=x,
        ),
        temp_component.add_port(
            name="Q_out1",
            orientation=0,
            center=(length_mmi, y_signal_in - gap_mmi - w_taper),
            width=w_taper,
            cross_section=x,
        ),
        temp_component.add_port(
            name="Q_out2",
            orientation=0,
            center=(length_mmi, y_lo_in),
            width=w_taper,
            cross_section=x,
        ),
        temp_component.add_port(
            name="I_out2",
            orientation=0,
            center=(length_mmi, y_lo_in - gap_mmi - w_taper),
            width=w_taper,
            cross_section=x,
        ),
    ]

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

    c.flatten()
    x.add_bbox(c)
    return c

mmi_90degree_hybrid

mmi_tapered

mmi_tapered

mmi_tapered(
    inputs: int = 1,
    outputs: int = 2,
    width: float | None = None,
    width_taper_in: float = 2.0,
    length_taper_in: float = 1.0,
    width_taper_out: float | None = None,
    length_taper_out: float | None = None,
    width_taper: float = 1.0,
    length_taper: float = 10.0,
    length_taper_start: float | None = None,
    length_taper_end: float | None = None,
    length_mmi: float = 5.5,
    width_mmi: float = 5,
    width_mmi_inner: float | None = None,
    gap_input_tapers: float = 0.25,
    gap_output_tapers: float = 0.25,
    taper: ComponentFactory = taper_function,
    cross_section: CrossSectionSpec = "strip",
    input_positions: list[float] | None = None,
    output_positions: list[float] | None = None,
) -> Component

Mxn MultiMode Interferometer (MMI).

This is jut a more general version of the mmi component. Make sure you simulate and optimize the component before using it.

Parameters:

Name Type Description Default
inputs int

number of inputs.

1
outputs int

number of outputs.

2
width float | None

input and output straight width. Defaults to cross_section.

None
width_taper_in float

interface between input straights and mmi region.

2.0
length_taper_in float

into the mmi region.

1.0
width_taper_out float | None

interface between mmi region and output straights.

None
length_taper_out float | None

into the mmi region.

None
width_taper float

interface between mmi region and output straights.

1.0
length_taper float

into the mmi region.

10.0
length_taper_start float | None

length of the taper at the start. Defaults to length_taper.

None
length_taper_end float | None

length of the taper at the end. Defaults to length_taper.

None
length_mmi float

in x direction.

5.5
width_mmi float

in y direction.

5
width_mmi_inner float | None

allows adding a different width for the inner mmi region.

None
gap_input_tapers float

gap between input tapers from edge to edge.

0.25
gap_output_tapers float

gap between output tapers from edge to edge.

0.25
taper ComponentFactory

taper function.

taper
cross_section CrossSectionSpec

specification (CrossSection, string or dict).

'strip'
input_positions list[float] | None

optional positions of the inputs.

None
output_positions list[float] | None

optional positions of the outputs.

                       ┌───────────┐
                       │           ├───────────────┐
                       │           │               ├────────────┐

width_taper │ │ │ │ ▲ ┌────────────────┤ │ ├────────────┘ │ │ │ ├───────────────┘

None
Source code in gdsfactory/components/mmis/mmi_tapered.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
@gf.cell_with_module_name(schematic_function=mmi_1x2_schematic, tags=["mmis"])
def mmi_tapered(
    inputs: int = 1,
    outputs: int = 2,
    width: float | None = None,
    width_taper_in: float = 2.0,
    length_taper_in: float = 1.0,
    width_taper_out: float | None = None,
    length_taper_out: float | None = None,
    width_taper: float = 1.0,
    length_taper: float = 10.0,
    length_taper_start: float | None = None,
    length_taper_end: float | None = None,
    length_mmi: float = 5.5,
    width_mmi: float = 5,
    width_mmi_inner: float | None = None,
    gap_input_tapers: float = 0.25,
    gap_output_tapers: float = 0.25,
    taper: ComponentFactory = taper_function,
    cross_section: CrossSectionSpec = "strip",
    input_positions: list[float] | None = None,
    output_positions: list[float] | None = None,
) -> Component:
    r"""Mxn MultiMode Interferometer (MMI).

    This is jut a more general version of the mmi component.
    Make sure you simulate and optimize the component before using it.

    Args:
        inputs: number of inputs.
        outputs: number of outputs.
        width: input and output straight width. Defaults to cross_section.
        width_taper_in: interface between input straights and mmi region.
        length_taper_in: into the mmi region.
        width_taper_out: interface between mmi region and output straights.
        length_taper_out: into the mmi region.
        width_taper: interface between mmi region and output straights.
        length_taper: into the mmi region.
        length_taper_start: length of the taper at the start. Defaults to length_taper.
        length_taper_end: length of the taper at the end. Defaults to length_taper.
        length_mmi: in x direction.
        width_mmi: in y direction.
        width_mmi_inner: allows adding a different width for the inner mmi region.
        gap_input_tapers: gap between input tapers from edge to edge.
        gap_output_tapers: gap between output tapers from edge to edge.
        taper: taper function.
        cross_section: specification (CrossSection, string or dict).
        input_positions: optional positions of the inputs.
        output_positions: optional positions of the outputs.

                                       ┌───────────┐
                                       │           ├───────────────┐
                                       │           │               ├────────────┐
               width_taper             │           │               │            │
                    ▲ ┌────────────────┤           │               ├────────────┘
                    │ │                │           ├───────────────┘
        ┌───────────┼─┤                │           │
        │           │ │                │           │
        ◄───────────┼─►                │           ├───────────────┐
        └───────────┼─┐                │           │               ├─────────────┐
                    ▼ └────────────────┤           │               │             │
                      ◄───────────────►│           │               ├─────────────┘
        length_taper    length_taper_in│           ├───────────────┘ length_taper
        ◄────────────►                 └───────────┘◄────────────►  ◄────────────►
            start                                  length_taper_out      end
                                       ◄───────────►
                                        length_mmi
    """
    c = Component()
    gap_input_tapers = gf.snap.snap_to_grid(gap_input_tapers, grid_factor=2)
    gap_output_tapers = gf.snap.snap_to_grid(gap_output_tapers, grid_factor=2)
    x = gf.get_cross_section(cross_section)
    width = width or x.width
    width_taper_out = width_taper_out or width_taper_in

    _taper_in = taper(
        length=length_taper_in,
        width1=width_taper,
        width2=width_taper_in,
        cross_section=cross_section,
    )
    _taper_out = taper(
        length=length_taper_out or length_taper_in,
        width2=width_taper_out,
        width1=width_taper,
        cross_section=cross_section,
    )
    _taper_start = taper(
        length=length_taper_start or length_taper,
        width1=width,
        width2=width_taper,
        cross_section=cross_section,
    )

    _taper_end = taper(
        length=length_taper_end or length_taper,
        width2=width_taper,
        width1=width,
        cross_section=cross_section,
    )

    width_mmi_inner = width_mmi_inner or width_mmi

    # _ = c << straight(length=length_mmi, cross_section=xs_mmi)
    mmi_left = c << taper(
        length=length_mmi / 2,
        width1=width_mmi,
        width2=width_mmi_inner,
        cross_section=cross_section,
    )
    mmi_right = c << taper(
        length=length_mmi / 2,
        width1=width_mmi_inner,
        width2=width_mmi,
        cross_section=cross_section,
    )
    mmi_right.connect("o1", mmi_left.ports["o2"])

    wg_spacing_input = gap_input_tapers + width_taper_in
    wg_spacing_output = gap_output_tapers + width_taper_out

    yi = -(inputs - 1) * wg_spacing_input / 2
    yo = -(outputs - 1) * wg_spacing_output / 2

    input_positions = input_positions or [
        yi + i * wg_spacing_input for i in range(inputs)
    ]
    output_positions = output_positions or [
        yo + i * wg_spacing_output for i in range(outputs)
    ]

    temp_component = Component()

    in_ports = [
        temp_component.add_port(
            name=f"in_{i}",
            orientation=180,
            center=(0, y),
            width=width_taper_in,
            layer=gf.get_layer(x.layer),
            cross_section=x,
        )
        for i, y in enumerate(input_positions)
    ]

    out_ports = [
        temp_component.add_port(
            name=f"out_{i}",
            orientation=0,
            center=(+length_mmi, y),
            width=width_taper_out,
            layer=gf.get_layer(x.layer),
            cross_section=x,
        )
        for i, y in enumerate(output_positions)
    ]

    for port in in_ports:
        taper_ref = c << _taper_in
        taper_ref.connect("o2", port, allow_width_mismatch=True)
        taper_outer_ref = c << _taper_start
        taper_outer_ref.connect("o2", taper_ref["o1"], allow_width_mismatch=True)
        c.add_port(name=port.name, port=taper_outer_ref.ports["o1"])

    for port in out_ports:
        taper_ref = c << _taper_out
        taper_ref.connect("o2", port, allow_width_mismatch=True)
        taper_outer_ref = c << _taper_end
        taper_outer_ref.connect("o2", taper_ref["o1"], allow_width_mismatch=True)
        c.add_port(name=port.name, port=taper_outer_ref.ports["o1"])

    x.add_bbox(c)
    c.auto_rename_ports()
    c.flatten()
    return c

mmi_tapered

mzis

mzi

mzi

mzi(
    delta_length: float = 10.0,
    length_y: float = 2.0,
    length_x: float | None = 0.1,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    straight_y: ComponentSpec | None = None,
    straight_x_top: ComponentSpec | None = None,
    straight_x_bot: ComponentSpec | None = None,
    splitter: ComponentSpec = "mmi1x2",
    combiner: ComponentSpec | None = 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",
    port1: str = "o1",
    port2: str = "o2",
    nbends: int = 2,
    cross_section: CrossSectionSpec = "strip",
    cross_section_x_top: CrossSectionSpec | None = None,
    cross_section_x_bot: CrossSectionSpec | None = None,
    mirror_bot: bool = False,
    add_optical_ports_arms: bool = False,
    min_length: float = 0.01,
    auto_rename_ports: bool = True,
    auto_detect_port_names: bool = False,
) -> Component

Mzi.

Parameters:

Name Type Description Default
delta_length float

bottom arm vertical extra length.

10.0
length_y float

vertical length for both and top arms.

2.0
length_x float | None

horizontal length. None uses to the straight_x_bot/top defaults.

0.1
bend ComponentSpec

90 degrees bend library.

'bend_euler'
straight ComponentSpec

straight function.

'straight'
straight_y ComponentSpec | None

straight for length_y and delta_length.

None
straight_x_top ComponentSpec | None

top straight for length_x.

None
straight_x_bot ComponentSpec | None

bottom straight for length_x.

None
splitter ComponentSpec

splitter function.

'mmi1x2'
combiner ComponentSpec | None

combiner function.

None
with_splitter bool

if False removes splitter.

True
port_e1_splitter str

east top splitter port.

'o2'
port_e0_splitter str

east bot splitter port.

'o3'
port_e1_combiner str

east top combiner port.

'o2'
port_e0_combiner str

east bot combiner port.

'o3'
port1 str

input port name.

'o1'
port2 str

output port name.

'o2'
nbends int

from straight top/bot to combiner (at least 2).

2
cross_section CrossSectionSpec

for routing (sxtop/sxbot to combiner).

'strip'
cross_section_x_top CrossSectionSpec | None

optional top cross_section (defaults to cross_section).

None
cross_section_x_bot CrossSectionSpec | None

optional bottom cross_section (defaults to cross_section).

None
mirror_bot bool

if true, mirrors the bottom arm.

False
add_optical_ports_arms bool

add all other optical ports in the arms with top\ and bot\ prefix.

False
min_length float

minimum length for the straight.

0.01
auto_rename_ports bool

if True, renames ports.

True
auto_detect_port_names bool

whether to auto detect ports names. Ignores port_e* arguments if True.

       b2______b3
      |  sxtop  |

straight_y | | | b1 b4 splitter==| |==combiner b5 b8 | | straight_y | | |

False
Source code in gdsfactory/components/mzis/mzi.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
@gf.cell_with_module_name(schematic_function=mzi_2x2_schematic, tags=["mzis"])
def mzi(
    delta_length: float = 10.0,
    length_y: float = 2.0,
    length_x: float | None = 0.1,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    straight_y: ComponentSpec | None = None,
    straight_x_top: ComponentSpec | None = None,
    straight_x_bot: ComponentSpec | None = None,
    splitter: ComponentSpec = "mmi1x2",
    combiner: ComponentSpec | None = 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",
    port1: str = "o1",
    port2: str = "o2",
    nbends: int = 2,
    cross_section: CrossSectionSpec = "strip",
    cross_section_x_top: CrossSectionSpec | None = None,
    cross_section_x_bot: CrossSectionSpec | None = None,
    mirror_bot: bool = False,
    add_optical_ports_arms: bool = False,
    min_length: float = 10e-3,
    auto_rename_ports: bool = True,
    auto_detect_port_names: bool = False,
) -> Component:
    r"""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_splitter: east top splitter port.
        port_e0_splitter: east bot splitter port.
        port_e1_combiner: east top combiner port.
        port_e0_combiner: east bot combiner port.
        port1: input port name.
        port2: output port name.
        nbends: from straight top/bot to combiner (at least 2).
        cross_section: for routing (sxtop/sxbot to combiner).
        cross_section_x_top: optional top cross_section (defaults to cross_section).
        cross_section_x_bot: optional bottom cross_section (defaults to cross_section).
        mirror_bot: if true, mirrors the bottom arm.
        add_optical_ports_arms: add all other optical ports in the arms
            with top\\_ and bot\\_ prefix.
        min_length: minimum length for the straight.
        auto_rename_ports: if True, renames ports.
        auto_detect_port_names: whether to auto detect ports names. Ignores port_e* arguments if True.

                       b2______b3
                      |  sxtop  |
              straight_y        |
                      |         |
                      b1        b4
            splitter==|         |==combiner
                      b5        b8
                      |         |
              straight_y        |
                      |         |
        delta_length/2          |
                      |         |
                     b6__sxbot__b7
                          Lx
    """
    if auto_detect_port_names:
        splitter_instance = gf.get_component(splitter)
        combiner_instance = gf.get_component(combiner or splitter)
        splitter_ports = splitter_instance.get_ports_list(
            port_type="optical", orientation=0
        )
        combiner_ports = combiner_instance.get_ports_list(
            port_type="optical", orientation=0
        )
        _name1 = splitter_ports[0].name
        _name2 = splitter_ports[1].name
        _name3 = combiner_ports[0].name
        _name4 = combiner_ports[1].name
        assert _name1 is not None, "splitter port 1 must have a name"
        assert _name2 is not None, "splitter port 2 must have a name"
        assert _name3 is not None, "combiner port 1 must have a name"
        assert _name4 is not None, "combiner port 2 must have a name"
        port_e1_splitter = _name1
        port_e0_splitter = _name2
        port_e1_combiner = _name3
        port_e0_combiner = _name4

    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

    cross_section_x_bot = cross_section_x_bot or cross_section
    cross_section_x_top = cross_section_x_top or cross_section
    bend = gf.get_component(bend, cross_section=cross_section)

    c = Component()
    cp1 = gf.get_component(splitter)
    cp2 = gf.get_component(combiner) if combiner else cp1

    if with_splitter:
        cp1 = c << cp1  # type: ignore[assignment]
        cp1.name = "cp1"

    cp2_reference = c << cp2
    b5 = c << bend
    b5.connect(port1, cp1.ports[port_e0_splitter], mirror=True)
    b5.name = "b5"

    gap_ports_splitter = cp1.ports[port_e0_splitter].y - cp1.ports[port_e1_splitter].y
    gap_ports_combiner = (
        cp2_reference.ports[port_e0_combiner].y
        - cp2_reference.ports[port_e1_combiner].y
    )
    delta_gap_ports = gap_ports_splitter - gap_ports_combiner

    # Offset applied only to the right-side vertical sections so that
    # b4/b8 end at the combiner port y-positions (not the splitter ones).
    combiner_offset = -delta_gap_ports / 2

    # Use sign of ``delta_length`` to determine which arm to lengthen
    short_arm_length = length_y
    if short_arm_length + combiner_offset < 0:
        raise ValueError(
            "Computed arm length is negative, which would result in a negative "
            "straight section length. This usually happens when `length_y` is too "
            "small relative to the splitter/combiner port gaps. "
            f"Got length_y={length_y}, gap_ports_combiner={gap_ports_combiner}, "
            f"gap_ports_splitter={gap_ports_splitter}, delta_gap_ports={delta_gap_ports}, "
            f"combiner_offset={combiner_offset}."
        )
    long_arm_length = abs(delta_length) / 2 + short_arm_length

    # Keep to the previous convention of the bottom arm being longer
    # for positive ``delta_length``
    if delta_length > 0:
        # Make bottom arm longer
        bot_arm_length = long_arm_length
        top_arm_length = short_arm_length
    else:
        # Make top arm longer
        bot_arm_length = short_arm_length
        top_arm_length = long_arm_length

    syl = c << gf.get_component(
        straight_y, length=bot_arm_length, cross_section=cross_section
    )
    syl.connect(port1, b5.ports[port2])
    b6 = c << bend
    b6.connect(port1, syl.ports[port2])
    b6.name = "b6"

    straight_x_top = (
        gf.get_component(
            straight_x_top, length=length_x, cross_section=cross_section_x_top
        )
        if length_x
        else gf.get_component(straight_x_top, cross_section=cross_section_x_top)
    )
    sxt = c << straight_x_top

    length_x = length_x or abs(sxt.ports[port1].x - sxt.ports[port2].x)

    straight_x_bot = (
        gf.get_component(
            straight_x_bot, length=length_x, cross_section=cross_section_x_bot
        )
        if length_x
        else gf.get_component(straight_x_bot, cross_section=cross_section_x_bot)
    )
    sxb = c << straight_x_bot
    sxb.connect(port1, b6.ports[port2], mirror=mirror_bot)

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

    sytl = c << gf.get_component(
        straight_y, length=top_arm_length, cross_section=cross_section
    )
    sytl.connect(port1, b1.ports[port2])

    b2 = c << bend
    b2.connect(port2, sytl.ports[port2])
    b2.name = "b2"

    sxt.connect(port1, b2.ports[port1])
    cp2_reference.mirror_x()
    cp2_reference.xmin = (
        sxt.ports[port2].x + bend.info["radius"] * nbends + 2 * min_length
    )

    # Top arm
    b3 = c << bend
    b3.connect(port2, sxt.ports[port2])
    b3.name = "b3"

    sytr = c << gf.get_component(
        straight_y, length=top_arm_length + combiner_offset, cross_section=cross_section
    )
    sytr.connect(port2, b3.ports[port1])
    b4 = c << bend
    b4.connect(port1, sytr.ports[port1])
    b4.name = "b4"

    # Bot arm
    b7 = c << bend
    b7.connect(port1, sxb.ports[port2])
    b7.name = "b7"

    sybr = c << gf.get_component(
        straight_y,
        length=bot_arm_length + combiner_offset,
        cross_section=cross_section,
    )
    sybr.connect(port1, b7.ports[port2])
    b8 = c << bend
    b8.connect(port2, sybr.ports[port2])
    b8.name = "b8"

    cp2_reference.connect(port_e1_combiner, b4.ports[port2])

    sytl.name = "sytl"
    syl.name = "syl"
    sxt.name = "sxt"
    sxb.name = "sxb"
    cp2_reference.name = "cp2"

    sytr.name = "sytr"
    sybr.name = "sybr"

    if with_splitter:
        c.add_ports(cp1.ports.filter(orientation=180), prefix="in_")
    else:
        c.add_port(port1, port=b1.ports[port1])
        c.add_port(port2, port=b5.ports[port1])

    c.add_ports(cp2_reference.ports.filter(orientation=0), prefix="ou_")
    c.add_ports(sxt.ports.filter(port_type="electrical"), prefix="top_")
    c.add_ports(sxb.ports.filter(port_type="electrical"), prefix="bot_")
    c.add_ports(sxt.ports.filter(port_type="placement"), prefix="top_")
    c.add_ports(sxb.ports.filter(port_type="placement"), prefix="bot_")

    if add_optical_ports_arms:
        c.add_ports(sxt.ports.filter(port_type="optical"), prefix="top_")
        c.add_ports(sxb.ports.filter(port_type="optical"), prefix="bot_")

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    if auto_rename_ports:
        c.auto_rename_ports()
    return c

mzi

mzi1x2_2x2 module-attribute

mzi1x2_2x2 = partial(
    mzi,
    combiner="mmi2x2",
    port_e1_combiner="o3",
    port_e0_combiner="o4",
)

mzi1x2_2x2

mzi2x2_2x2_phase_shifter module-attribute

mzi2x2_2x2_phase_shifter = partial(
    mzi2x2_2x2,
    straight_x_top="straight_heater_metal",
    length_x=200,
)

mzi2x2_2x2_phase_shifter

mzi_lattice

mzi_lattice

mzi_lattice(
    coupler_lengths: Sequence[float] = (10.0, 20.0),
    coupler_gaps: Sequence[float] = (0.2, 0.3),
    delta_lengths: Sequence[float] = (10.0,),
    mzi: str = "mzi_coupler",
    splitter: str = "coupler",
    **kwargs: Any
) -> Component

Mzi lattice filter.

Parameters:

Name Type Description Default
coupler_lengths Sequence[float]

list of length for each coupler.

(10.0, 20.0)
coupler_gaps Sequence[float]

list of coupler gaps.

(0.2, 0.3)
delta_lengths Sequence[float]

list of length differences.

(10.0,)
mzi str

function for the mzi.

'mzi_coupler'
splitter str

splitter function.

'coupler'
kwargs Any

additional settings.

{}

Other Parameters:

Name Type Description
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).

__ _ | | | | | | | | cp1==| |===cp2=====| |=== .... ===cplast=== | | | | | | | | DL1 | DL2 | | | | | |_| | | |___|

Source code in gdsfactory/components/mzis/mzi_lattice.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
@gf.cell_with_module_name(schematic_function=mzi_2x2_schematic, tags=["mzis"])
def mzi_lattice(
    coupler_lengths: Sequence[float] = (10.0, 20.0),
    coupler_gaps: Sequence[float] = (0.2, 0.3),
    delta_lengths: Sequence[float] = (10.0,),
    mzi: str = "mzi_coupler",
    splitter: str = "coupler",
    **kwargs: Any,
) -> 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.
        kwargs: additional settings.

    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).

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

    """
    if len(coupler_lengths) != len(coupler_gaps):
        raise ValueError(
            f"Got {len(coupler_lengths)} coupler_lengths and "
            f"{len(coupler_gaps)} coupler_gaps"
        )
    if len(coupler_lengths) != len(delta_lengths) + 1:
        raise ValueError(
            f"Got {len(coupler_lengths)} coupler_lengths and "
            f"{len(delta_lengths)} delta_lengths. "
            "You need one more coupler_length than delta_lengths "
        )

    c = Component()

    cp1 = splitter1 = gf.get_component(
        splitter, gap=coupler_gaps[0], length=coupler_lengths[0]
    )
    combiner1 = gf.get_component(
        splitter, gap=coupler_gaps[1], length=coupler_lengths[1]
    )

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

    stages: list[ComponentReference] = []

    for length, gap, delta_length in zip(
        coupler_lengths[2:], coupler_gaps[2:], delta_lengths[1:], strict=False
    ):
        splitter_settings = dict(gap=coupler_gaps[1], length=coupler_lengths[1])
        combiner_settings = dict(length=length, gap=gap)
        splitter1 = gf.get_component(splitter, settings=None, **splitter_settings)
        combiner1 = gf.get_component(splitter, settings=None, **combiner_settings)

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

        stages.append(stage)
        c.add_ports(stage.ports.filter(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.ports.filter(orientation=180, port_type="optical"):
        c.add_port(port.name, port=port)

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

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    c.auto_rename_ports()
    return c

mzi_lattice_mmi

mzi_lattice_mmi(
    coupler_widths: tuple[float | None, float | None] = (
        None,
        None,
    ),
    coupler_widths_tapers: tuple[float, ...] = (1.0, 1.0),
    coupler_lengths_tapers: tuple[float, ...] = (
        10.0,
        10.0,
    ),
    coupler_lengths_mmis: tuple[float, ...] = (5.5, 5.5),
    coupler_widths_mmis: tuple[float, ...] = (2.5, 2.5),
    coupler_gaps_mmis: tuple[float, ...] = (0.25, 0.25),
    taper_functions_mmis: tuple[str, ...] = (
        "taper",
        "taper",
    ),
    straight_functions_mmis: tuple[str, ...] = (
        "straight",
        "straight",
    ),
    cross_sections_mmis: tuple[str, ...] = (
        "strip",
        "strip",
    ),
    delta_lengths: tuple[float, ...] = (10.0,),
    mzi: str = "mzi2x2_2x2",
    splitter: str = "mmi2x2",
    **kwargs: Any
) -> Component

Mzi lattice filter, with MMI couplers.

Parameters:

Name Type Description Default
coupler_widths tuple[float | None, float | None]

(for each MMI coupler, list of) input and output straight width.

(None, None)
coupler_widths_tapers tuple[float, ...]

(for each MMI coupler, list of) interface between input straights and mmi region.

(1.0, 1.0)
coupler_lengths_tapers tuple[float, ...]

(for each MMI coupler, list of) into the mmi region.

(10.0, 10.0)
coupler_lengths_mmis tuple[float, ...]

(for each MMI coupler, list of) in x direction.

(5.5, 5.5)
coupler_widths_mmis tuple[float, ...]

(for each MMI coupler, list of) in y direction.

(2.5, 2.5)
coupler_gaps_mmis tuple[float, ...]

(for each MMI coupler, list of) (width_taper + gap between tapered wg)/2.

(0.25, 0.25)
taper_functions_mmis tuple[str, ...]

(for each MMI coupler, list of) taper function.

('taper', 'taper')
straight_functions_mmis tuple[str, ...]

(for each MMI coupler, list of) straight function.

('straight', 'straight')
cross_sections_mmis tuple[str, ...]

(for each MMI coupler, list of) spec.

('strip', 'strip')
delta_lengths tuple[float, ...]

list of length differences.

(10.0,)
mzi str

function for the mzi.

'mzi2x2_2x2'
splitter str

splitter function.

'mmi2x2'
kwargs Any

additional settings.

{}

Other Parameters:

Name Type Description
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).

__ _ | | | | | | | | cp1==| |===cp2=====| |=== .... ===cplast=== | | | | | | | | DL1 | DL2 | | | | | |_| | | |___|

Source code in gdsfactory/components/mzis/mzi_lattice.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
@gf.cell_with_module_name(schematic_function=mzi_2x2_schematic, tags=["mzis"])
def mzi_lattice_mmi(
    coupler_widths: tuple[float | None, float | None] = (None, None),
    coupler_widths_tapers: tuple[float, ...] = (
        1.0,
        1.0,
    ),
    coupler_lengths_tapers: tuple[float, ...] = (
        10.0,
        10.0,
    ),
    coupler_lengths_mmis: tuple[float, ...] = (
        5.5,
        5.5,
    ),
    coupler_widths_mmis: tuple[float, ...] = (
        2.5,
        2.5,
    ),
    coupler_gaps_mmis: tuple[float, ...] = (
        0.25,
        0.25,
    ),
    taper_functions_mmis: tuple[str, ...] = (
        "taper",
        "taper",
    ),
    straight_functions_mmis: tuple[str, ...] = ("straight", "straight"),
    cross_sections_mmis: tuple[str, ...] = ("strip", "strip"),
    delta_lengths: tuple[float, ...] = (10.0,),
    mzi: str = "mzi2x2_2x2",
    splitter: str = "mmi2x2",
    **kwargs: Any,
) -> Component:
    r"""Mzi lattice filter, with MMI couplers.

    Args:
        coupler_widths: (for each MMI coupler, list of) input and output straight width.
        coupler_widths_tapers: (for each MMI coupler, list of) interface between input straights and mmi region.
        coupler_lengths_tapers: (for each MMI coupler, list of) into the mmi region.
        coupler_lengths_mmis: (for each MMI coupler, list of) in x direction.
        coupler_widths_mmis: (for each MMI coupler, list of) in y direction.
        coupler_gaps_mmis: (for each MMI coupler, list of) (width_taper + gap between tapered wg)/2.
        taper_functions_mmis: (for each MMI coupler, list of) taper function.
        straight_functions_mmis: (for each MMI coupler, list of) straight function.
        cross_sections_mmis: (for each MMI coupler, list of) spec.
        delta_lengths: list of length differences.
        mzi: function for the mzi.
        splitter: splitter function.
        kwargs: additional settings.

    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).

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

    """
    length = len(coupler_widths)
    if all(
        len(lst) != length
        for lst in [
            coupler_widths_tapers,
            coupler_lengths_tapers,
            coupler_lengths_mmis,
            coupler_widths_mmis,
            coupler_gaps_mmis,
            taper_functions_mmis,
            straight_functions_mmis,
            cross_sections_mmis,
        ]
    ):
        raise ValueError("All MMI-related argument lists must be the same length.")
    if len(coupler_widths) != len(delta_lengths) + 1:
        raise ValueError(
            f"Got {len(coupler_widths)} coupler_widths and "
            f"{len(delta_lengths)} delta_lengths. "
            "You need one more coupler_width than delta_lengths "
        )

    c = Component()

    splitter_settings = dict(
        width=coupler_widths[0],
        width_taper=coupler_widths_tapers[0],
        length_taper=coupler_lengths_tapers[0],
        length_mmi=coupler_lengths_mmis[0],
        width_mmi=coupler_widths_mmis[0],
        gap_mmi=coupler_gaps_mmis[0],
        taper=taper_functions_mmis[0],
        straight=straight_functions_mmis[0],
        cross_section=cross_sections_mmis[0],
    )
    combiner_settings = dict(
        width=coupler_widths[1],
        width_taper=coupler_widths_tapers[1],
        length_taper=coupler_lengths_tapers[1],
        length_mmi=coupler_lengths_mmis[1],
        width_mmi=coupler_widths_mmis[1],
        gap_mmi=coupler_gaps_mmis[1],
        taper=taper_functions_mmis[1],
        straight=straight_functions_mmis[1],
        cross_section=cross_sections_mmis[1],
    )

    cp1 = splitter1 = gf.get_component(splitter, settings=None, **splitter_settings)
    combiner1 = gf.get_component(splitter, settings=None, **combiner_settings)

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

    stages: list[ComponentReference] = []

    for (
        coupler_width,
        coupler_width_taper,
        coupler_length_taper,
        coupler_length_mmi,
        coupler_width_mmi,
        coupler_gap_mmi,
        taper,
        straight,
        cross_section,
        delta_length,
    ) in zip(
        coupler_widths[2:],
        coupler_widths_tapers[2:],
        coupler_lengths_tapers[2:],
        coupler_lengths_mmis[2:],
        coupler_widths_mmis[2:],
        coupler_gaps_mmis[2:],
        taper_functions_mmis[2:],
        straight_functions_mmis[2:],
        cross_sections_mmis[2:],
        delta_lengths[1:],
        strict=False,
    ):
        splitter_settings = dict(
            width=coupler_widths[1],
            width_taper=coupler_widths_tapers[1],
            length_taper=coupler_lengths_tapers[1],
            length_mmi=coupler_lengths_mmis[1],
            width_mmi=coupler_widths_mmis[1],
            gap_mmi=coupler_gaps_mmis[1],
            taper=taper_functions_mmis[1],
            straight=straight_functions_mmis[1],
            cross_section=cross_sections_mmis[1],
        )
        combiner_settings = dict(
            width=coupler_width,
            width_taper=coupler_width_taper,
            length_taper=coupler_length_taper,
            length_mmi=coupler_length_mmi,
            width_mmi=coupler_width_mmi,
            gap_mmi=coupler_gap_mmi,
            taper=taper,
            straight=straight,
            cross_section=cross_section,
        )
        splitter1 = gf.get_component(splitter, settings=None, **splitter_settings)
        combiner1 = gf.get_component(splitter, settings=None, **combiner_settings)

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

        stages.append(stage)
        c.add_ports(stage.ports.filter(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.ports.filter(orientation=180, port_type="optical"):
        c.add_port(port.name, port=port)

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

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    c.auto_rename_ports()
    return c

mzi_lattice

mzi_lattice_mmi

mzi_lattice_mmi(
    coupler_widths: tuple[float | None, float | None] = (
        None,
        None,
    ),
    coupler_widths_tapers: tuple[float, ...] = (1.0, 1.0),
    coupler_lengths_tapers: tuple[float, ...] = (
        10.0,
        10.0,
    ),
    coupler_lengths_mmis: tuple[float, ...] = (5.5, 5.5),
    coupler_widths_mmis: tuple[float, ...] = (2.5, 2.5),
    coupler_gaps_mmis: tuple[float, ...] = (0.25, 0.25),
    taper_functions_mmis: tuple[str, ...] = (
        "taper",
        "taper",
    ),
    straight_functions_mmis: tuple[str, ...] = (
        "straight",
        "straight",
    ),
    cross_sections_mmis: tuple[str, ...] = (
        "strip",
        "strip",
    ),
    delta_lengths: tuple[float, ...] = (10.0,),
    mzi: str = "mzi2x2_2x2",
    splitter: str = "mmi2x2",
    **kwargs: Any
) -> Component

Mzi lattice filter, with MMI couplers.

Parameters:

Name Type Description Default
coupler_widths tuple[float | None, float | None]

(for each MMI coupler, list of) input and output straight width.

(None, None)
coupler_widths_tapers tuple[float, ...]

(for each MMI coupler, list of) interface between input straights and mmi region.

(1.0, 1.0)
coupler_lengths_tapers tuple[float, ...]

(for each MMI coupler, list of) into the mmi region.

(10.0, 10.0)
coupler_lengths_mmis tuple[float, ...]

(for each MMI coupler, list of) in x direction.

(5.5, 5.5)
coupler_widths_mmis tuple[float, ...]

(for each MMI coupler, list of) in y direction.

(2.5, 2.5)
coupler_gaps_mmis tuple[float, ...]

(for each MMI coupler, list of) (width_taper + gap between tapered wg)/2.

(0.25, 0.25)
taper_functions_mmis tuple[str, ...]

(for each MMI coupler, list of) taper function.

('taper', 'taper')
straight_functions_mmis tuple[str, ...]

(for each MMI coupler, list of) straight function.

('straight', 'straight')
cross_sections_mmis tuple[str, ...]

(for each MMI coupler, list of) spec.

('strip', 'strip')
delta_lengths tuple[float, ...]

list of length differences.

(10.0,)
mzi str

function for the mzi.

'mzi2x2_2x2'
splitter str

splitter function.

'mmi2x2'
kwargs Any

additional settings.

{}

Other Parameters:

Name Type Description
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).

__ _ | | | | | | | | cp1==| |===cp2=====| |=== .... ===cplast=== | | | | | | | | DL1 | DL2 | | | | | |_| | | |___|

Source code in gdsfactory/components/mzis/mzi_lattice.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
@gf.cell_with_module_name(schematic_function=mzi_2x2_schematic, tags=["mzis"])
def mzi_lattice_mmi(
    coupler_widths: tuple[float | None, float | None] = (None, None),
    coupler_widths_tapers: tuple[float, ...] = (
        1.0,
        1.0,
    ),
    coupler_lengths_tapers: tuple[float, ...] = (
        10.0,
        10.0,
    ),
    coupler_lengths_mmis: tuple[float, ...] = (
        5.5,
        5.5,
    ),
    coupler_widths_mmis: tuple[float, ...] = (
        2.5,
        2.5,
    ),
    coupler_gaps_mmis: tuple[float, ...] = (
        0.25,
        0.25,
    ),
    taper_functions_mmis: tuple[str, ...] = (
        "taper",
        "taper",
    ),
    straight_functions_mmis: tuple[str, ...] = ("straight", "straight"),
    cross_sections_mmis: tuple[str, ...] = ("strip", "strip"),
    delta_lengths: tuple[float, ...] = (10.0,),
    mzi: str = "mzi2x2_2x2",
    splitter: str = "mmi2x2",
    **kwargs: Any,
) -> Component:
    r"""Mzi lattice filter, with MMI couplers.

    Args:
        coupler_widths: (for each MMI coupler, list of) input and output straight width.
        coupler_widths_tapers: (for each MMI coupler, list of) interface between input straights and mmi region.
        coupler_lengths_tapers: (for each MMI coupler, list of) into the mmi region.
        coupler_lengths_mmis: (for each MMI coupler, list of) in x direction.
        coupler_widths_mmis: (for each MMI coupler, list of) in y direction.
        coupler_gaps_mmis: (for each MMI coupler, list of) (width_taper + gap between tapered wg)/2.
        taper_functions_mmis: (for each MMI coupler, list of) taper function.
        straight_functions_mmis: (for each MMI coupler, list of) straight function.
        cross_sections_mmis: (for each MMI coupler, list of) spec.
        delta_lengths: list of length differences.
        mzi: function for the mzi.
        splitter: splitter function.
        kwargs: additional settings.

    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).

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

    """
    length = len(coupler_widths)
    if all(
        len(lst) != length
        for lst in [
            coupler_widths_tapers,
            coupler_lengths_tapers,
            coupler_lengths_mmis,
            coupler_widths_mmis,
            coupler_gaps_mmis,
            taper_functions_mmis,
            straight_functions_mmis,
            cross_sections_mmis,
        ]
    ):
        raise ValueError("All MMI-related argument lists must be the same length.")
    if len(coupler_widths) != len(delta_lengths) + 1:
        raise ValueError(
            f"Got {len(coupler_widths)} coupler_widths and "
            f"{len(delta_lengths)} delta_lengths. "
            "You need one more coupler_width than delta_lengths "
        )

    c = Component()

    splitter_settings = dict(
        width=coupler_widths[0],
        width_taper=coupler_widths_tapers[0],
        length_taper=coupler_lengths_tapers[0],
        length_mmi=coupler_lengths_mmis[0],
        width_mmi=coupler_widths_mmis[0],
        gap_mmi=coupler_gaps_mmis[0],
        taper=taper_functions_mmis[0],
        straight=straight_functions_mmis[0],
        cross_section=cross_sections_mmis[0],
    )
    combiner_settings = dict(
        width=coupler_widths[1],
        width_taper=coupler_widths_tapers[1],
        length_taper=coupler_lengths_tapers[1],
        length_mmi=coupler_lengths_mmis[1],
        width_mmi=coupler_widths_mmis[1],
        gap_mmi=coupler_gaps_mmis[1],
        taper=taper_functions_mmis[1],
        straight=straight_functions_mmis[1],
        cross_section=cross_sections_mmis[1],
    )

    cp1 = splitter1 = gf.get_component(splitter, settings=None, **splitter_settings)
    combiner1 = gf.get_component(splitter, settings=None, **combiner_settings)

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

    stages: list[ComponentReference] = []

    for (
        coupler_width,
        coupler_width_taper,
        coupler_length_taper,
        coupler_length_mmi,
        coupler_width_mmi,
        coupler_gap_mmi,
        taper,
        straight,
        cross_section,
        delta_length,
    ) in zip(
        coupler_widths[2:],
        coupler_widths_tapers[2:],
        coupler_lengths_tapers[2:],
        coupler_lengths_mmis[2:],
        coupler_widths_mmis[2:],
        coupler_gaps_mmis[2:],
        taper_functions_mmis[2:],
        straight_functions_mmis[2:],
        cross_sections_mmis[2:],
        delta_lengths[1:],
        strict=False,
    ):
        splitter_settings = dict(
            width=coupler_widths[1],
            width_taper=coupler_widths_tapers[1],
            length_taper=coupler_lengths_tapers[1],
            length_mmi=coupler_lengths_mmis[1],
            width_mmi=coupler_widths_mmis[1],
            gap_mmi=coupler_gaps_mmis[1],
            taper=taper_functions_mmis[1],
            straight=straight_functions_mmis[1],
            cross_section=cross_sections_mmis[1],
        )
        combiner_settings = dict(
            width=coupler_width,
            width_taper=coupler_width_taper,
            length_taper=coupler_length_taper,
            length_mmi=coupler_length_mmi,
            width_mmi=coupler_width_mmi,
            gap_mmi=coupler_gap_mmi,
            taper=taper,
            straight=straight,
            cross_section=cross_section,
        )
        splitter1 = gf.get_component(splitter, settings=None, **splitter_settings)
        combiner1 = gf.get_component(splitter, settings=None, **combiner_settings)

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

        stages.append(stage)
        c.add_ports(stage.ports.filter(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.ports.filter(orientation=180, port_type="optical"):
        c.add_port(port.name, port=port)

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

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    c.auto_rename_ports()
    return c

mzi_lattice_mmi

mzi_pads_center

mzi_pads_center

mzi_pads_center(
    ps_top: ComponentSpec = "straight_heater_metal",
    ps_bot: ComponentSpec = "straight_heater_metal",
    mzi: ComponentSpec = "mzi",
    pad: ComponentSpec = "pad_small",
    length_x: float = 500,
    length_y: float = 40,
    mzi_sig_top: str | None = "top_r_e2",
    mzi_gnd_top: str | None = "top_l_e2",
    mzi_sig_bot: str | None = "bot_l_e2",
    mzi_gnd_bot: str | None = "bot_r_e2",
    pad_sig_bot: str = "e1_1_1",
    pad_sig_top: str = "e3_1_3",
    pad_gnd_bot: str = "e4_1_2",
    pad_gnd_top: str = "e2_1_2",
    delta_length: float = 40.0,
    cross_section: CrossSectionSpec = "strip",
    cross_section_metal: CrossSectionSpec = "metal_routing",
    pad_pitch: float | str = "pad_pitch",
    auto_taper: bool = False,
    **kwargs: Any
) -> gf.Component

Return Mzi phase shifter with pads in the middle.

GND is the middle pad and is shared between top and bottom phase shifters.

Parameters:

Name Type Description Default
ps_top ComponentSpec

phase shifter top.

'straight_heater_metal'
ps_bot ComponentSpec

phase shifter bottom.

'straight_heater_metal'
mzi ComponentSpec

interferometer.

'mzi'
pad ComponentSpec

pad function.

'pad_small'
length_x float

horizontal length.

500
length_y float

vertical length.

40
mzi_sig_top str | None

port name for top phase shifter signal. None if no connection.

'top_r_e2'
mzi_gnd_top str | None

port name for top phase shifter GND. None if no connection.

'top_l_e2'
mzi_sig_bot str | None

port name for top phase shifter signal. None if no connection.

'bot_l_e2'
mzi_gnd_bot str | None

port name for top phase shifter GND. None if no connection.

'bot_r_e2'
pad_sig_bot str

port name for top pad.

'e1_1_1'
pad_sig_top str

port name for top pad.

'e3_1_3'
pad_gnd_bot str

port name for top pad.

'e4_1_2'
pad_gnd_top str

port name for top pad.

'e2_1_2'
delta_length float

mzi length imbalance.

40.0
cross_section CrossSectionSpec

for the mzi.

'strip'
cross_section_metal CrossSectionSpec

for routing metal.

'metal_routing'
pad_pitch float | str

pad pitch in um.

'pad_pitch'
auto_taper bool

add taper if cross_section width is different between mzi and pad.

False
kwargs Any

routing settings.

{}
Source code in gdsfactory/components/mzis/mzi_pads_center.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
@gf.cell_with_module_name(schematic_function=ckt_schematic, tags=["mzis"])
def mzi_pads_center(
    ps_top: ComponentSpec = "straight_heater_metal",
    ps_bot: ComponentSpec = "straight_heater_metal",
    mzi: ComponentSpec = "mzi",
    pad: ComponentSpec = "pad_small",
    length_x: float = 500,
    length_y: float = 40,
    mzi_sig_top: str | None = "top_r_e2",
    mzi_gnd_top: str | None = "top_l_e2",
    mzi_sig_bot: str | None = "bot_l_e2",
    mzi_gnd_bot: str | None = "bot_r_e2",
    pad_sig_bot: str = "e1_1_1",
    pad_sig_top: str = "e3_1_3",
    pad_gnd_bot: str = "e4_1_2",
    pad_gnd_top: str = "e2_1_2",
    delta_length: float = 40.0,
    cross_section: CrossSectionSpec = "strip",
    cross_section_metal: CrossSectionSpec = "metal_routing",
    pad_pitch: float | str = "pad_pitch",
    auto_taper: bool = False,
    **kwargs: Any,
) -> gf.Component:
    """Return Mzi phase shifter with pads in the middle.

    GND is the middle pad and is shared between top and bottom phase shifters.

    Args:
        ps_top: phase shifter top.
        ps_bot: phase shifter bottom.
        mzi: interferometer.
        pad: pad function.
        length_x: horizontal length.
        length_y: vertical length.
        mzi_sig_top: port name for top phase shifter signal. None if no connection.
        mzi_gnd_top: port name for top phase shifter GND. None if no connection.
        mzi_sig_bot: port name for top phase shifter signal. None if no connection.
        mzi_gnd_bot: port name for top phase shifter GND. None if no connection.
        pad_sig_bot: port name for top pad.
        pad_sig_top: port name for top pad.
        pad_gnd_bot: port name for top pad.
        pad_gnd_top: port name for top pad.
        delta_length: mzi length imbalance.
        cross_section: for the mzi.
        cross_section_metal: for routing metal.
        pad_pitch: pad pitch in um.
        auto_taper: add taper if cross_section width is different between mzi and pad.
        kwargs: routing settings.
    """
    c = gf.Component()

    pad_pitch = gf.get_constant(pad_pitch)

    assert isinstance(pad_pitch, float)

    mzi_ps = gf.get_component(
        mzi,
        length_x=length_x,
        straight_x_top=ps_top,
        straight_x_bot=ps_bot,
        length_y=length_y,
        delta_length=delta_length,
        cross_section=cross_section,
        auto_rename_ports=False,
    )

    port_names = [p.name for p in mzi_ps.ports]
    for port_name in [mzi_sig_top, mzi_gnd_top, mzi_sig_bot, mzi_gnd_bot]:
        if port_name and port_name not in port_names:
            raise ValueError(f"port {port_name!r} not in {port_names}")

    m = c << mzi_ps
    pads = c << gf.components.array(
        component=pad, columns=3, rows=1, column_pitch=pad_pitch
    )
    pads.x = m.x
    pads.y = m.y

    if mzi_sig_top is not None:
        gf.routing.route_bundle_electrical(
            c,
            m.ports[mzi_sig_bot],
            pads.ports[pad_sig_bot],
            cross_section=cross_section_metal,
            auto_taper=auto_taper,
            **kwargs,
        )

    if mzi_gnd_bot:
        gf.routing.route_bundle_electrical(
            c,
            m.ports[mzi_gnd_bot],
            pads.ports[pad_gnd_bot],
            cross_section=cross_section_metal,
            auto_taper=auto_taper,
            **kwargs,
        )

    if mzi_gnd_top:
        gf.routing.route_bundle_electrical(
            c,
            m.ports[mzi_gnd_top],
            pads.ports[pad_gnd_top],
            cross_section=cross_section_metal,
            auto_taper=auto_taper,
            **kwargs,
        )

    if mzi_sig_top:
        gf.routing.route_bundle_electrical(
            c,
            m.ports[mzi_sig_top],
            pads.ports[pad_sig_top],
            cross_section=cross_section_metal,
            auto_taper=auto_taper,
            **kwargs,
        )

    c.add_ports(m.ports)

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    return c

mzi_pads_center

mzi_phase_shifter module-attribute

mzi_phase_shifter = partial(
    mzi,
    straight_x_top="straight_heater_metal",
    length_x=200,
)

mzi_phase_shifter

mzi_phase_shifter_top_heater_metal module-attribute

mzi_phase_shifter_top_heater_metal = partial(
    mzi_phase_shifter,
    straight_x_top="straight_heater_metal",
)

mzi_phase_shifter_top_heater_metal

mzi_pin module-attribute

mzi_pin = partial(
    mzi,
    straight_x_top="straight_pin",
    cross_section_x_top="pin",
    delta_length=0.0,
    length_x=100,
)

mzi_pin

mzit

mzit

mzit(
    w0: float = 0.5,
    w1: float = 0.45,
    w2: float = 0.55,
    dy: Delta = 2.0,
    delta_length: float = 10.0,
    length: float = 1.0,
    coupler_length1: float = 5.0,
    coupler_length2: float = 10.0,
    coupler_gap1: float = 0.2,
    coupler_gap2: float = 0.3,
    taper: ComponentSpec = "taper",
    taper_length: float = 5.0,
    bend90: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    coupler1: ComponentSpec | None = "coupler",
    coupler2: ComponentSpec = "coupler",
    cross_section: str = "strip",
) -> Component

Mzi tolerant to fabrication variations.

based on Yufei Xing thesis http://photonics.intec.ugent.be/publications/PhD.asp?ID=250

Parameters:

Name Type Description Default
w0 float

input waveguide width (um).

0.5
w1 float

narrow waveguide width (um).

0.45
w2 float

wide waveguide width (um).

0.55
dy Delta

port to port vertical spacing.

2.0
delta_length float

length difference between arms (um).

10.0
length float

shared length for w1 and w2.

1.0
coupler_length1 float

length of coupler1.

5.0
coupler_length2 float

length of coupler2.

10.0
coupler_gap1 float

coupler1.

0.2
coupler_gap2 float

coupler2.

0.3
taper ComponentSpec

taper spec.

'taper'
taper_length float

from w0 to w1.

5.0
bend90 ComponentSpec

bend spec.

'bend_euler'
straight ComponentSpec

spec.

'straight'
coupler1 ComponentSpec | None

coupler1 spec (optional).

'coupler'
coupler2 ComponentSpec

coupler2 spec.

'coupler'
cross_section str

cross_section spec.

           cp1

4 2 __ __ 3w0t2 _w2_ \ / \ \ length1 / | ============== gap1 | / \ | / _____w0___t1 w1 | 3 1 4 \ | | | 2 2 | | __ __w0_t1_w1/ | \ / | \ length2 / | ============== gap2 | / \ | | __/ \ E0_w0__t2 __w1______/ 1 1 cp2

'strip'
Source code in gdsfactory/components/mzis/mzit.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
@gf.cell_with_module_name(schematic_function=mzi_2x2_schematic, tags=["mzis"])
def mzit(
    w0: float = 0.5,
    w1: float = 0.45,
    w2: float = 0.55,
    dy: Delta = 2.0,
    delta_length: float = 10.0,
    length: float = 1.0,
    coupler_length1: float = 5.0,
    coupler_length2: float = 10.0,
    coupler_gap1: float = 0.2,
    coupler_gap2: float = 0.3,
    taper: ComponentSpec = "taper",
    taper_length: float = 5.0,
    bend90: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    coupler1: ComponentSpec | None = "coupler",
    coupler2: ComponentSpec = "coupler",
    cross_section: str = "strip",
) -> Component:
    r"""Mzi tolerant to fabrication variations.

    based on Yufei Xing thesis
    http://photonics.intec.ugent.be/publications/PhD.asp?ID=250

    Args:
        w0: input waveguide width (um).
        w1: narrow waveguide width (um).
        w2: wide waveguide width (um).
        dy: port to port vertical spacing.
        delta_length: length difference between arms (um).
        length: shared length for w1 and w2.
        coupler_length1: length of coupler1.
        coupler_length2: length of coupler2.
        coupler_gap1: coupler1.
        coupler_gap2: coupler2.
        taper: taper spec.
        taper_length: from w0 to w1.
        bend90: bend spec.
        straight: spec.
        coupler1: coupler1 spec (optional).
        coupler2: coupler2 spec.
        cross_section: cross_section spec.

                           cp1
            4   2 __                  __  3___w0_t2   _w2___
                    \                /                      \
                     \    length1   /                        |
                      ============== gap1                    |
                     /              \                        |
                  __/                \_____w0___t1   _w1     |
            3   1                        4               \   |
                                                         |   |
            2   2                                        |   |
                  __                  __w0____t1____w1___/   |
                    \                /                       |
                     \    length2   /                        |
                      ============== gap2                    |
                     /               \                       |                       |
                  __/                 \ E0_w0__t2 __w1______/
            1   1
                           cp2


    """
    c = gf.Component()

    cp2 = c << gf.get_component(
        coupler2,
        length=coupler_length2,
        gap=coupler_gap2,
        dy=dy,
        cross_section=cross_section,
    )

    # inner arm (w1)
    t1 = c << gf.get_component(
        taper,
        width1=w0,
        width2=w1,
        length=taper_length,
        cross_section=cross_section,
    )
    t1.connect("o1", cp2.ports["o3"])

    b1 = gf.get_component(bend90, cross_section=cross_section, width=w1)
    b1t = c << b1
    b1b = c << b1

    b1b.connect("o1", t1.ports["o2"])
    b1t.connect("o1", b1b.ports["o2"])

    t3b = c << gf.get_component(
        taper,
        width1=w1,
        width2=w2,
        length=taper_length,
        cross_section=cross_section,
    )
    t3b.connect("o1", b1t.ports["o2"])
    wgs2 = c << gf.get_component(
        straight, length=length, cross_section=cross_section, width=w2
    )
    wgs2.connect("o1", t3b.ports["o2"])
    t20i = c << gf.get_component(
        taper,
        width1=w2,
        width2=w0,
        length=taper_length,
        cross_section=cross_section,
    )
    t20i.connect("o1", wgs2.ports["o2"])

    # outer_arm (w2)
    t2 = c << gf.get_component(
        taper,
        width1=w0,
        width2=w2,
        length=taper_length,
        cross_section=cross_section,
    )
    t2.connect("o1", cp2.ports["o4"])

    dx = (delta_length - 2 * dy) / 2
    assert delta_length >= 4 * dy, (
        f"`delta_length`={delta_length} needs to be at least {4 * dy}"
    )

    wg2b = c << gf.get_component(
        straight, length=dx, cross_section=cross_section, width=w2
    )
    wg2b.connect("o1", t2.ports["o2"])

    b2 = gf.get_component(bend90, cross_section=cross_section, width=w2)
    b2t = c << b2
    b2b = c << b2
    wy = gf.get_component(
        straight, length=2 * dy, cross_section=cross_section, width=w2
    )
    wx = gf.get_component(straight, length=dx, cross_section=cross_section, width=w2)

    b2b.connect("o1", wg2b.ports["o2"])

    # vertical straight
    wg2y = c << wy
    wg2y.connect("o1", b2b.ports["o2"])
    b2t.connect("o1", wg2y.ports["o2"])

    wg2t = c << wx
    wg2t.connect("o1", b2t.ports["o2"])

    t3t = c << gf.get_component(
        taper,
        width1=w2,
        width2=w1,
        length=taper_length,
        cross_section=cross_section,
    )
    t3t.connect("o1", wg2t.ports["o2"])
    wgs1 = c << gf.get_component(
        straight, length=length, cross_section=cross_section, width=w1
    )
    wgs1.connect("o1", t3t.ports["o2"])
    t20o = c << gf.get_component(
        taper,
        width1=w1,
        width2=w0,
        length=taper_length,
        cross_section=cross_section,
    )
    t20o.connect("o1", wgs1.ports["o2"])

    if coupler1 is not None:
        cp1 = c << gf.get_component(
            coupler1,
            length=coupler_length1,
            gap=coupler_gap1,
            dy=dy,
            cross_section=cross_section,
        )

        cp1.connect("o3", t20o.ports["o2"])
        cp1.connect("o4", t20i.ports["o2"])
        c.add_port("W3", port=cp1.ports["o2"])
        c.add_port("W2", port=cp1.ports["o1"])
    else:
        c.add_port("W3", port=t20o.ports["o2"])
        c.add_port("W2", port=t20i.ports["o2"])

    c.add_port("o2", port=cp2.ports["o2"])
    c.add_port("o1", port=cp2.ports["o1"])
    c.auto_rename_ports()
    return c

mzit_lattice

mzit_lattice(
    coupler_lengths: Sequence[float] = (10.0, 20.0),
    coupler_gaps: Sequence[float] = (0.2, 0.3),
    delta_lengths: Sequence[float] = (10.0,),
    mzi: ComponentSpec = mzit,
) -> Component

Mzi fab tolerant lattice filter.

                cp1
o4  o2 __                  __ o3___w0_t2   _w2___
         \                /                      \
                 \    length1   /                        |
           ============== gap1                    |
          /              \                        |
       __/                \_____w0___t1   _w1     |
o3  o1                       o4               \   | .
                 ...                          |   | .
o2  o2                    o3                  |   | .
       __                  _____w0___t1___w1__/   |
         \                /                       |
          \    lengthN   /                        |
           ============== gapN                    |
          /               \                       |
       __/                 \_                     |
o1  o1                      \___w0___t2___w1_____/
                cpN       o4
Source code in gdsfactory/components/mzis/mzit.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
@gf.cell_with_module_name(schematic_function=mzi_2x2_schematic, tags=["mzis"])
def mzit_lattice(
    coupler_lengths: Sequence[float] = (10.0, 20.0),
    coupler_gaps: Sequence[float] = (0.2, 0.3),
    delta_lengths: Sequence[float] = (10.0,),
    mzi: ComponentSpec = mzit,
) -> Component:
    r"""Mzi fab tolerant lattice filter.

    ```text
                    cp1
    o4  o2 __                  __ o3___w0_t2   _w2___
             \                /                      \
                     \    length1   /                        |
               ============== gap1                    |
              /              \                        |
           __/                \_____w0___t1   _w1     |
    o3  o1                       o4               \   | .
                     ...                          |   | .
    o2  o2                    o3                  |   | .
           __                  _____w0___t1___w1__/   |
             \                /                       |
              \    lengthN   /                        |
               ============== gapN                    |
              /               \                       |
           __/                 \_                     |
    o1  o1                      \___w0___t2___w1_____/
                    cpN       o4
    ```


    """
    if len(coupler_lengths) != len(coupler_gaps):
        raise ValueError(
            f"Got {len(coupler_lengths)} coupler_lengths and "
            f"{len(coupler_gaps)} coupler_gaps"
        )
    if len(coupler_lengths) != len(delta_lengths) + 1:
        raise ValueError(
            f"Got {len(coupler_lengths)} coupler_lengths and "
            f"{len(delta_lengths)} delta_lengths. "
            "You need one more coupler_length than delta_lengths "
        )

    assert len(coupler_lengths) >= 2

    c = Component()

    cp1 = coupler0 = c << gf.get_component(
        mzi,
        coupler_gap1=coupler_gaps[0],
        coupler_gap2=coupler_gaps[1],
        coupler_length1=coupler_lengths[0],
        coupler_length2=coupler_lengths[1],
        delta_length=delta_lengths[0],
    )

    couplers = [
        c
        << gf.get_component(
            mzi,
            coupler_gap2=coupler_gap,
            coupler_length2=coupler_length,
            coupler1=None,
            delta_length=delta_length,
        )
        for coupler_length, coupler_gap, delta_length in zip(
            coupler_lengths[2:], coupler_gaps[2:], delta_lengths[1:], strict=False
        )
    ]

    for i, coupler in enumerate(couplers):
        if i % 2 == 0:
            coupler.dmirror()
        coupler.connect("o3", coupler0.ports["o1"])
        coupler.connect("o4", coupler0.ports["o2"])
        coupler0 = coupler

    c.add_port("o1", port=coupler0.ports["o1"])
    c.add_port("o2", port=coupler0.ports["o2"])
    c.add_port("o3", port=cp1.ports["o3"])
    c.add_port("o4", port=cp1.ports["o4"])
    return c

mzit

mzit_lattice

mzit_lattice(
    coupler_lengths: Sequence[float] = (10.0, 20.0),
    coupler_gaps: Sequence[float] = (0.2, 0.3),
    delta_lengths: Sequence[float] = (10.0,),
    mzi: ComponentSpec = mzit,
) -> Component

Mzi fab tolerant lattice filter.

                cp1
o4  o2 __                  __ o3___w0_t2   _w2___
         \                /                      \
                 \    length1   /                        |
           ============== gap1                    |
          /              \                        |
       __/                \_____w0___t1   _w1     |
o3  o1                       o4               \   | .
                 ...                          |   | .
o2  o2                    o3                  |   | .
       __                  _____w0___t1___w1__/   |
         \                /                       |
          \    lengthN   /                        |
           ============== gapN                    |
          /               \                       |
       __/                 \_                     |
o1  o1                      \___w0___t2___w1_____/
                cpN       o4
Source code in gdsfactory/components/mzis/mzit.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
@gf.cell_with_module_name(schematic_function=mzi_2x2_schematic, tags=["mzis"])
def mzit_lattice(
    coupler_lengths: Sequence[float] = (10.0, 20.0),
    coupler_gaps: Sequence[float] = (0.2, 0.3),
    delta_lengths: Sequence[float] = (10.0,),
    mzi: ComponentSpec = mzit,
) -> Component:
    r"""Mzi fab tolerant lattice filter.

    ```text
                    cp1
    o4  o2 __                  __ o3___w0_t2   _w2___
             \                /                      \
                     \    length1   /                        |
               ============== gap1                    |
              /              \                        |
           __/                \_____w0___t1   _w1     |
    o3  o1                       o4               \   | .
                     ...                          |   | .
    o2  o2                    o3                  |   | .
           __                  _____w0___t1___w1__/   |
             \                /                       |
              \    lengthN   /                        |
               ============== gapN                    |
              /               \                       |
           __/                 \_                     |
    o1  o1                      \___w0___t2___w1_____/
                    cpN       o4
    ```


    """
    if len(coupler_lengths) != len(coupler_gaps):
        raise ValueError(
            f"Got {len(coupler_lengths)} coupler_lengths and "
            f"{len(coupler_gaps)} coupler_gaps"
        )
    if len(coupler_lengths) != len(delta_lengths) + 1:
        raise ValueError(
            f"Got {len(coupler_lengths)} coupler_lengths and "
            f"{len(delta_lengths)} delta_lengths. "
            "You need one more coupler_length than delta_lengths "
        )

    assert len(coupler_lengths) >= 2

    c = Component()

    cp1 = coupler0 = c << gf.get_component(
        mzi,
        coupler_gap1=coupler_gaps[0],
        coupler_gap2=coupler_gaps[1],
        coupler_length1=coupler_lengths[0],
        coupler_length2=coupler_lengths[1],
        delta_length=delta_lengths[0],
    )

    couplers = [
        c
        << gf.get_component(
            mzi,
            coupler_gap2=coupler_gap,
            coupler_length2=coupler_length,
            coupler1=None,
            delta_length=delta_length,
        )
        for coupler_length, coupler_gap, delta_length in zip(
            coupler_lengths[2:], coupler_gaps[2:], delta_lengths[1:], strict=False
        )
    ]

    for i, coupler in enumerate(couplers):
        if i % 2 == 0:
            coupler.dmirror()
        coupler.connect("o3", coupler0.ports["o1"])
        coupler.connect("o4", coupler0.ports["o2"])
        coupler0 = coupler

    c.add_port("o1", port=coupler0.ports["o1"])
    c.add_port("o2", port=coupler0.ports["o2"])
    c.add_port("o3", port=cp1.ports["o3"])
    c.add_port("o4", port=cp1.ports["o4"])
    return c

mzit_lattice

mzm module-attribute

mzm = partial(
    mzi_phase_shifter,
    straight_x_top="straight_pin",
    straight_x_bot="straight_pin",
)

mzm

pads

bump_pad

bump_pad

bump_pad(
    size: float = 36.244,
    layer: LayerSpec = "MTOP",
    port_width: float = 10.0,
    port_layer: LayerSpec = "M2",
    port_type: str = "pad",
    add_via: bool = True,
) -> Component

Returns rectangular pad with ports.

Parameters:

Name Type Description Default
size float

octagon edge of octagon.

36.244
layer LayerSpec

bump pad layer.

'MTOP'
port_width float

width of the port for electrical routing.

10.0
port_layer LayerSpec

layer of the port for electrical routing.

'M2'
port_type str

port type for pad port.

'pad'
add_via bool

whether to add a via stack.

True
Source code in gdsfactory/components/pads/bump_pad.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
@gf.cell_with_module_name(tags=["pads"])
def bump_pad(
    size: float = 36.244,
    layer: LayerSpec = "MTOP",
    port_width: float = 10.0,
    port_layer: LayerSpec = "M2",
    port_type: str = "pad",
    add_via: bool = True,
) -> Component:
    """Returns rectangular pad with ports.

    Args:
        size: octagon edge of octagon.
        layer: bump pad layer.
        port_width: width of the port for electrical routing.
        port_layer: layer of the port for electrical routing.
        port_type: port type for pad port.
        add_via: whether to add a via stack.
    """
    c = Component()
    layer = gf.get_layer(layer)
    size_ = gf.get_constant(size)
    rect = octagon(
        side_length=size, layer=layer, port_width=port_width, port_type=port_type
    )
    c_ref = c.add_ref(rect)
    if add_via:
        via_n = c << via_stack()
        via_n.y = c_ref.ports["o5"].y - via_n.ports["e1"].width / 2
        p = via_n.ports["e2"]
        c.add_port(
            name="e2",
            center=p.center,
            width=p.width,
            orientation=p.orientation,
            port_type=p.port_type,
            layer=port_layer,
        )

        via_e = c << via_stack()
        via_e.x = c_ref.ports["o3"].x - via_e.ports["e1"].width / 2
        p = via_e.ports["e3"]
        c.add_port(
            name="e3",
            center=p.center,
            width=p.width,
            orientation=p.orientation,
            port_type=p.port_type,
            layer=port_layer,
        )

        via_s = c << via_stack()
        via_s.y = c_ref.ports["o1"].y + via_s.ports["e1"].width / 2
        p = via_s.ports["e4"]
        c.add_port(
            name="e4",
            center=p.center,
            width=p.width,
            orientation=p.orientation,
            port_type=p.port_type,
            layer=port_layer,
        )

        via_w = c << via_stack()
        via_w.x = c_ref.ports["o7"].x + via_w.ports["e1"].width / 2
        p = via_w.ports["e1"]
        c.add_port(
            name="e1",
            center=p.center,
            width=p.width,
            orientation=p.orientation,
            port_type=p.port_type,
            layer=port_layer,
        )
    else:
        for i, j in enumerate([1, 3, 5, 7]):
            p = c_ref.ports[f"o{j}"]
            c.add_port(
                name=f"e{i + 1}",
                center=p.center,
                width=port_width,
                orientation=p.orientation,
                port_type=port_type,
                layer=layer,
            )
    c.info["size"] = size_

    elec = [p for p in c.ports if p.port_type in {"electrical", "pad"}]
    if elec:
        c.create_pin(ports=elec, name="pad")

    return c

bump_pad_grid

bump_pad_grid(
    columns: int = 6,
    rows: int = 6,
    column_pitch: float = 121.89,
    row_pitch: float = 132.66,
    offset: float = 66.33,
    port_width: float = 10,
    port_layer: LayerSpec = "M2",
    size: float = 36.244,
    layer: LayerSpec = "MTOP",
    auto_rename_ports: bool = False,
    skip_pads: list[tuple[int, int]] | None = None,
    add_via: bool = True,
) -> Component

Returns 2D array of bump pads.

Parameters:

Name Type Description Default
columns int

number of columns.

6
rows int

number of rows.

6
column_pitch float

x pitch.

121.89
row_pitch float

y pitch.

132.66
offset float

offset for alternating columns.

66.33
port_width float

width of the port for electrical routing.

10
port_layer LayerSpec

layer of the port for electrical routing.

'M2'
size float

pad size.

36.244
layer LayerSpec

bump pad layer.

'MTOP'
auto_rename_ports bool

True to auto rename ports.

False
skip_pads list[tuple[int, int]] | None

list of (col, row) tuples to skip.

None
add_via bool

whether to add a via stack.

True
Source code in gdsfactory/components/pads/bump_pad.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
@gf.cell_with_module_name(tags=["pads"])
def bump_pad_grid(
    columns: int = 6,
    rows: int = 6,
    column_pitch: float = 121.89,
    row_pitch: float = 132.66,
    offset: float = 66.33,
    port_width: float = 10,
    port_layer: LayerSpec = "M2",
    size: float = 36.244,
    layer: LayerSpec = "MTOP",
    auto_rename_ports: bool = False,
    skip_pads: list[tuple[int, int]] | None = None,
    add_via: bool = True,
) -> Component:
    """Returns 2D array of bump pads.

    Args:
        columns: number of columns.
        rows: number of rows.
        column_pitch: x pitch.
        row_pitch: y pitch.
        offset: offset for alternating columns.
        port_width: width of the port for electrical routing.
        port_layer: layer of the port for electrical routing.
        size: pad size.
        layer: bump pad layer.
        auto_rename_ports: True to auto rename ports.
        skip_pads: list of (col, row) tuples to skip.
        add_via: whether to add a via stack.
    """
    c = Component()

    pad_kwargs: dict[str, Any] = {}
    if layer is not None:
        pad_kwargs["layer"] = layer
    if size is not None:
        pad_kwargs["size"] = size

    pad_component = bump_pad(
        size=size,
        layer=layer,
        port_width=port_width,
        port_layer=port_layer,
        add_via=add_via,
    )

    for col in range(columns):
        for row in range(rows):
            if skip_pads is not None and (col, row) in skip_pads:
                continue
            pad = c << pad_component
            center = (col * column_pitch, row * row_pitch + (col % 2) * offset)
            pad.center = center
            c.add_ports(pad.ports, prefix=f"e{row + 1}_{col + 1}_")

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    if auto_rename_ports:
        c.auto_rename_ports()
    return c

bump_pad

bump_pad_grid

bump_pad_grid(
    columns: int = 6,
    rows: int = 6,
    column_pitch: float = 121.89,
    row_pitch: float = 132.66,
    offset: float = 66.33,
    port_width: float = 10,
    port_layer: LayerSpec = "M2",
    size: float = 36.244,
    layer: LayerSpec = "MTOP",
    auto_rename_ports: bool = False,
    skip_pads: list[tuple[int, int]] | None = None,
    add_via: bool = True,
) -> Component

Returns 2D array of bump pads.

Parameters:

Name Type Description Default
columns int

number of columns.

6
rows int

number of rows.

6
column_pitch float

x pitch.

121.89
row_pitch float

y pitch.

132.66
offset float

offset for alternating columns.

66.33
port_width float

width of the port for electrical routing.

10
port_layer LayerSpec

layer of the port for electrical routing.

'M2'
size float

pad size.

36.244
layer LayerSpec

bump pad layer.

'MTOP'
auto_rename_ports bool

True to auto rename ports.

False
skip_pads list[tuple[int, int]] | None

list of (col, row) tuples to skip.

None
add_via bool

whether to add a via stack.

True
Source code in gdsfactory/components/pads/bump_pad.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
@gf.cell_with_module_name(tags=["pads"])
def bump_pad_grid(
    columns: int = 6,
    rows: int = 6,
    column_pitch: float = 121.89,
    row_pitch: float = 132.66,
    offset: float = 66.33,
    port_width: float = 10,
    port_layer: LayerSpec = "M2",
    size: float = 36.244,
    layer: LayerSpec = "MTOP",
    auto_rename_ports: bool = False,
    skip_pads: list[tuple[int, int]] | None = None,
    add_via: bool = True,
) -> Component:
    """Returns 2D array of bump pads.

    Args:
        columns: number of columns.
        rows: number of rows.
        column_pitch: x pitch.
        row_pitch: y pitch.
        offset: offset for alternating columns.
        port_width: width of the port for electrical routing.
        port_layer: layer of the port for electrical routing.
        size: pad size.
        layer: bump pad layer.
        auto_rename_ports: True to auto rename ports.
        skip_pads: list of (col, row) tuples to skip.
        add_via: whether to add a via stack.
    """
    c = Component()

    pad_kwargs: dict[str, Any] = {}
    if layer is not None:
        pad_kwargs["layer"] = layer
    if size is not None:
        pad_kwargs["size"] = size

    pad_component = bump_pad(
        size=size,
        layer=layer,
        port_width=port_width,
        port_layer=port_layer,
        add_via=add_via,
    )

    for col in range(columns):
        for row in range(rows):
            if skip_pads is not None and (col, row) in skip_pads:
                continue
            pad = c << pad_component
            center = (col * column_pitch, row * row_pitch + (col % 2) * offset)
            pad.center = center
            c.add_ports(pad.ports, prefix=f"e{row + 1}_{col + 1}_")

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    if auto_rename_ports:
        c.auto_rename_ports()
    return c

bump_pad_grid

pad

pad

pad(
    size: Size | str = (100.0, 100.0),
    layer: LayerSpec = "MTOP",
    bbox_layers: tuple[LayerSpec, ...] | None = None,
    bbox_offsets: tuple[float, ...] | None = None,
    port_inclusion: float = 0,
    port_orientation: AngleInDegrees | None = 0,
    port_orientations: Ints | None = (180, 90, 0, -90),
    port_type: str = "pad",
) -> Component

Returns rectangular pad with ports.

Parameters:

Name Type Description Default
size Size | str

x, y size.

(100.0, 100.0)
layer LayerSpec

pad layer.

'MTOP'
bbox_layers tuple[LayerSpec, ...] | None

list of layers.

None
bbox_offsets tuple[float, ...] | None

Optional offsets for each layer with respect to size. positive grows, negative shrinks the size.

None
port_inclusion float

from edge.

0
port_orientation AngleInDegrees | None

in degrees for the center port.

0
port_orientations Ints | None

list of port_orientations to add. None does not add ports.

(180, 90, 0, -90)
port_type str

port type for pad port.

'pad'
Source code in gdsfactory/components/pads/pad.py
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@gf.cell_with_module_name(schematic_function=pad_schematic, tags=["pads"])
def pad(
    size: Size | str = (100.0, 100.0),
    layer: LayerSpec = "MTOP",
    bbox_layers: tuple[LayerSpec, ...] | None = None,
    bbox_offsets: tuple[float, ...] | None = None,
    port_inclusion: float = 0,
    port_orientation: AngleInDegrees | None = 0,
    port_orientations: Ints | None = (180, 90, 0, -90),
    port_type: str = "pad",
) -> Component:
    """Returns rectangular pad with ports.

    Args:
        size: x, y size.
        layer: pad layer.
        bbox_layers: list of layers.
        bbox_offsets: Optional offsets for each layer with respect to size.
            positive grows, negative shrinks the size.
        port_inclusion: from edge.
        port_orientation: in degrees for the center port.
        port_orientations: list of port_orientations to add. None does not add ports.
        port_type: port type for pad port.
    """
    c = Component()
    layer = gf.get_layer(layer)
    size_ = gf.get_constant(size)
    rect = gf.c.compass(
        size=size_,
        layer=layer,
        port_inclusion=port_inclusion,
        port_type="electrical",
        port_orientations=port_orientations,
    )
    c_ref = c.add_ref(rect)
    c.add_ports(c_ref.ports)
    c.info["size"] = size_
    c.info["xsize"] = size_[0]
    c.info["ysize"] = size_[1]

    if port_orientation is not None and port_orientation not in valid_port_orientations:
        raise ValueError(f"{port_orientation=} must be in {valid_port_orientations}")

    width = size_[1] if port_orientation in {0, 180} else size_[0]

    if port_orientation is not None:
        c.add_port(
            name="pad",
            port_type=port_type,
            layer=layer,
            center=(0, 0),
            orientation=port_orientation,
            width=width,
        )

    if bbox_layers and bbox_offsets:
        sizes: list[Size] = []
        for cladding_offset in bbox_offsets:
            size_new = (size_[0] + 2 * cladding_offset, size_[1] + 2 * cladding_offset)
            sizes.append(size_new)

        for layer, size_new in zip(bbox_layers, sizes, strict=False):
            c.add_ref(
                gf.c.compass(
                    size=size_new,
                    layer=layer,
                )
            )
    c.flatten()
    elec = [p for p in c.ports if p.port_type in {"electrical", "pad"}]
    if elec:
        c.create_pin(ports=elec, name="pad")
    return c

pad_array

pad_array(
    pad: ComponentSpec = "pad",
    columns: int = 6,
    rows: int = 1,
    column_pitch: float = 150.0,
    row_pitch: float = 150.0,
    port_orientation: AngleInDegrees = 0,
    size: Float2 | None = None,
    layer: LayerSpec | None = "MTOP",
    centered_ports: bool = False,
    auto_rename_ports: bool = False,
) -> Component

Returns 2D array of pads.

Parameters:

Name Type Description Default
pad ComponentSpec

pad element.

'pad'
columns int

number of columns.

6
rows int

number of rows.

1
column_pitch float

x pitch.

150.0
row_pitch float

y pitch.

150.0
port_orientation AngleInDegrees

port orientation in deg. None for low speed DC ports.

0
size Float2 | None

pad size.

None
layer LayerSpec | None

pad layer.

'MTOP'
centered_ports bool

True add ports to center. False add ports to the edge.

False
auto_rename_ports bool

True to auto rename ports.

False
Source code in gdsfactory/components/pads/pad.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
@gf.cell_with_module_name(schematic_function=pad_schematic, tags=["pads"])
def pad_array(
    pad: ComponentSpec = "pad",
    columns: int = 6,
    rows: int = 1,
    column_pitch: float = 150.0,
    row_pitch: float = 150.0,
    port_orientation: AngleInDegrees = 0,
    size: Float2 | None = None,
    layer: LayerSpec | None = "MTOP",
    centered_ports: bool = False,
    auto_rename_ports: bool = False,
) -> Component:
    """Returns 2D array of pads.

    Args:
        pad: pad element.
        columns: number of columns.
        rows: number of rows.
        column_pitch: x pitch.
        row_pitch: y pitch.
        port_orientation: port orientation in deg. None for low speed DC ports.
        size: pad size.
        layer: pad layer.
        centered_ports: True add ports to center. False add ports to the edge.
        auto_rename_ports: True to auto rename ports.
    """
    c = Component()

    pad_kwargs: dict[str, Any] = {}
    if layer is not None:
        pad_kwargs["layer"] = layer
    if size is not None:
        pad_kwargs["size"] = size
    pad_component = gf.get_component(
        pad,
        port_orientation=port_orientation,
        port_orientations=(port_orientation,) if not centered_ports else None,
        **pad_kwargs,
    )

    pad_size: Float2 = size or pad_component.info["size"]
    pad_layer: LayerSpec = layer or pad_component.ports[0].layer

    c.add_ref(
        pad_component,
        columns=columns,
        rows=rows,
        column_pitch=column_pitch,
        row_pitch=row_pitch,
    )
    width = pad_size[0] if int(port_orientation) in {90, 270} else pad_size[1]

    for col in range(columns):
        for row in range(rows):
            center = (col * column_pitch, row * row_pitch)
            port_orientation = int(port_orientation)
            center_list = [center[0], center[1]]

            if not centered_ports:
                if port_orientation == 0:
                    center_list[0] += pad_size[0] / 2
                elif port_orientation == 90:
                    center_list[1] += pad_size[1] / 2
                elif port_orientation == 180:
                    center_list[0] -= pad_size[0] / 2
                elif port_orientation == 270:
                    center_list[1] -= pad_size[1] / 2

            center = (center_list[0], center_list[1])
            c.add_port(
                name=f"e{row + 1}{col + 1}",
                center=center,
                width=width,
                orientation=port_orientation,
                port_type="electrical",
                layer=pad_layer,
            )
    if auto_rename_ports:
        c.auto_rename_ports()
    for port in c.ports:
        if port.port_type == "electrical":
            c.create_pin(ports=[port], name=f"pad_{port.name}")
    return c

pad

pad_array

pad_array(
    pad: ComponentSpec = "pad",
    columns: int = 6,
    rows: int = 1,
    column_pitch: float = 150.0,
    row_pitch: float = 150.0,
    port_orientation: AngleInDegrees = 0,
    size: Float2 | None = None,
    layer: LayerSpec | None = "MTOP",
    centered_ports: bool = False,
    auto_rename_ports: bool = False,
) -> Component

Returns 2D array of pads.

Parameters:

Name Type Description Default
pad ComponentSpec

pad element.

'pad'
columns int

number of columns.

6
rows int

number of rows.

1
column_pitch float

x pitch.

150.0
row_pitch float

y pitch.

150.0
port_orientation AngleInDegrees

port orientation in deg. None for low speed DC ports.

0
size Float2 | None

pad size.

None
layer LayerSpec | None

pad layer.

'MTOP'
centered_ports bool

True add ports to center. False add ports to the edge.

False
auto_rename_ports bool

True to auto rename ports.

False
Source code in gdsfactory/components/pads/pad.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
@gf.cell_with_module_name(schematic_function=pad_schematic, tags=["pads"])
def pad_array(
    pad: ComponentSpec = "pad",
    columns: int = 6,
    rows: int = 1,
    column_pitch: float = 150.0,
    row_pitch: float = 150.0,
    port_orientation: AngleInDegrees = 0,
    size: Float2 | None = None,
    layer: LayerSpec | None = "MTOP",
    centered_ports: bool = False,
    auto_rename_ports: bool = False,
) -> Component:
    """Returns 2D array of pads.

    Args:
        pad: pad element.
        columns: number of columns.
        rows: number of rows.
        column_pitch: x pitch.
        row_pitch: y pitch.
        port_orientation: port orientation in deg. None for low speed DC ports.
        size: pad size.
        layer: pad layer.
        centered_ports: True add ports to center. False add ports to the edge.
        auto_rename_ports: True to auto rename ports.
    """
    c = Component()

    pad_kwargs: dict[str, Any] = {}
    if layer is not None:
        pad_kwargs["layer"] = layer
    if size is not None:
        pad_kwargs["size"] = size
    pad_component = gf.get_component(
        pad,
        port_orientation=port_orientation,
        port_orientations=(port_orientation,) if not centered_ports else None,
        **pad_kwargs,
    )

    pad_size: Float2 = size or pad_component.info["size"]
    pad_layer: LayerSpec = layer or pad_component.ports[0].layer

    c.add_ref(
        pad_component,
        columns=columns,
        rows=rows,
        column_pitch=column_pitch,
        row_pitch=row_pitch,
    )
    width = pad_size[0] if int(port_orientation) in {90, 270} else pad_size[1]

    for col in range(columns):
        for row in range(rows):
            center = (col * column_pitch, row * row_pitch)
            port_orientation = int(port_orientation)
            center_list = [center[0], center[1]]

            if not centered_ports:
                if port_orientation == 0:
                    center_list[0] += pad_size[0] / 2
                elif port_orientation == 90:
                    center_list[1] += pad_size[1] / 2
                elif port_orientation == 180:
                    center_list[0] -= pad_size[0] / 2
                elif port_orientation == 270:
                    center_list[1] -= pad_size[1] / 2

            center = (center_list[0], center_list[1])
            c.add_port(
                name=f"e{row + 1}{col + 1}",
                center=center,
                width=width,
                orientation=port_orientation,
                port_type="electrical",
                layer=pad_layer,
            )
    if auto_rename_ports:
        c.auto_rename_ports()
    for port in c.ports:
        if port.port_type == "electrical":
            c.create_pin(ports=[port], name=f"pad_{port.name}")
    return c

pad_array

pad_array0 module-attribute

pad_array0 = partial(
    pad_array, port_orientation=0, columns=1, rows=3
)

pad_array0

pad_array180 module-attribute

pad_array180 = partial(
    pad_array, port_orientation=180, columns=1, rows=3
)

pad_array180

pad_array270 module-attribute

pad_array270 = partial(pad_array, port_orientation=270)

pad_array270

pad_array90 module-attribute

pad_array90 = partial(pad_array, port_orientation=90)

pad_array90

pad_gs

pad_gs(
    length: float = 100, cross_section: str = "gs"
) -> gf.Component
Source code in gdsfactory/components/pads/pad_gsg.py
89
90
91
@gf.cell_with_module_name(tags=["pads"])
def pad_gs(length: float = 100, cross_section: str = "gs") -> gf.Component:
    return gf.c.straight(cross_section=cross_section, length=length)

pad_gs

pad_gsg

High speed GSG pads.

pad_gsg_short

pad_gsg_short(
    size: Float2 = (22, 7),
    layer_metal: LayerSpec = "MTOP",
    metal_spacing: float = 5.0,
    short: bool = True,
    pad: ComponentSpec = "pad",
    pad_pitch: float = 150,
    route_xsize: float = 50,
) -> gf.Component

Returns high speed GSG pads for calibrating the RF probes.

Parameters:

Name Type Description Default
size Float2

for the short.

(22, 7)
layer_metal LayerSpec

for the short.

'MTOP'
metal_spacing float

in um.

5.0
short bool

if False returns an open.

True
pad ComponentSpec

function for pad.

'pad'
pad_pitch float

in um.

150
route_xsize float

in um.

50
Source code in gdsfactory/components/pads/pad_gsg.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@gf.cell_with_module_name(tags=["pads"])
def pad_gsg_short(
    size: Float2 = (22, 7),
    layer_metal: LayerSpec = "MTOP",
    metal_spacing: float = 5.0,
    short: bool = True,
    pad: ComponentSpec = "pad",
    pad_pitch: float = 150,
    route_xsize: float = 50,
) -> gf.Component:
    """Returns high speed GSG pads for calibrating the RF probes.

    Args:
        size: for the short.
        layer_metal: for the short.
        metal_spacing: in um.
        short: if False returns an open.
        pad: function for pad.
        pad_pitch: in um.
        route_xsize: in um.
    """
    c = gf.Component()
    via = gf.c.rectangle(size=size, layer=layer_metal)
    gnd_top = c << via

    if short:
        _ = c << via
    gnd_bot = c << via

    gnd_bot.ymax = via.ymin
    gnd_top.ymin = via.ymax

    gnd_top.movex(-metal_spacing)
    gnd_bot.movex(-metal_spacing)

    pads = c << gf.components.array(
        pad, columns=1, rows=3, column_pitch=0, row_pitch=pad_pitch, centered=True
    )
    pads.xmin = via.xmax + route_xsize
    pads.y = 0

    gf.routing.route_quad(
        c, gnd_bot.ports["e4"], pads.ports["e1_1_1"], layer=layer_metal
    )
    gf.routing.route_quad(
        c,
        cast("kf.DPort", gnd_top.ports["e2"]),  # type: ignore[redundant-cast]
        cast("kf.DPort", pads.ports["e1_3_1"]),  # type: ignore[redundant-cast]
        layer=layer_metal,
    )
    gf.routing.route_quad(
        c,
        cast("kf.DPort", via.ports["e3"]),  # type: ignore[redundant-cast]
        cast("kf.DPort", pads.ports["e1_2_1"]),  # type: ignore[redundant-cast]
        layer=layer_metal,
    )
    return c

pad_gsg

pad_gsg_open module-attribute

pad_gsg_open = partial(pad_gsg_short, short=False)

pad_gsg_open

pad_gsg_short

pad_gsg_short(
    size: Float2 = (22, 7),
    layer_metal: LayerSpec = "MTOP",
    metal_spacing: float = 5.0,
    short: bool = True,
    pad: ComponentSpec = "pad",
    pad_pitch: float = 150,
    route_xsize: float = 50,
) -> gf.Component

Returns high speed GSG pads for calibrating the RF probes.

Parameters:

Name Type Description Default
size Float2

for the short.

(22, 7)
layer_metal LayerSpec

for the short.

'MTOP'
metal_spacing float

in um.

5.0
short bool

if False returns an open.

True
pad ComponentSpec

function for pad.

'pad'
pad_pitch float

in um.

150
route_xsize float

in um.

50
Source code in gdsfactory/components/pads/pad_gsg.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@gf.cell_with_module_name(tags=["pads"])
def pad_gsg_short(
    size: Float2 = (22, 7),
    layer_metal: LayerSpec = "MTOP",
    metal_spacing: float = 5.0,
    short: bool = True,
    pad: ComponentSpec = "pad",
    pad_pitch: float = 150,
    route_xsize: float = 50,
) -> gf.Component:
    """Returns high speed GSG pads for calibrating the RF probes.

    Args:
        size: for the short.
        layer_metal: for the short.
        metal_spacing: in um.
        short: if False returns an open.
        pad: function for pad.
        pad_pitch: in um.
        route_xsize: in um.
    """
    c = gf.Component()
    via = gf.c.rectangle(size=size, layer=layer_metal)
    gnd_top = c << via

    if short:
        _ = c << via
    gnd_bot = c << via

    gnd_bot.ymax = via.ymin
    gnd_top.ymin = via.ymax

    gnd_top.movex(-metal_spacing)
    gnd_bot.movex(-metal_spacing)

    pads = c << gf.components.array(
        pad, columns=1, rows=3, column_pitch=0, row_pitch=pad_pitch, centered=True
    )
    pads.xmin = via.xmax + route_xsize
    pads.y = 0

    gf.routing.route_quad(
        c, gnd_bot.ports["e4"], pads.ports["e1_1_1"], layer=layer_metal
    )
    gf.routing.route_quad(
        c,
        cast("kf.DPort", gnd_top.ports["e2"]),  # type: ignore[redundant-cast]
        cast("kf.DPort", pads.ports["e1_3_1"]),  # type: ignore[redundant-cast]
        layer=layer_metal,
    )
    gf.routing.route_quad(
        c,
        cast("kf.DPort", via.ports["e3"]),  # type: ignore[redundant-cast]
        cast("kf.DPort", pads.ports["e1_2_1"]),  # type: ignore[redundant-cast]
        layer=layer_metal,
    )
    return c

pad_gsg_short

pad_rectangular module-attribute

pad_rectangular = partial(pad, size='pad_size')

pad_rectangular

pad_small module-attribute

pad_small = partial(pad, size=(80, 80))

pad_small

pads_shorted

pads_shorted

pads_shorted(
    pad: ComponentSpec = "pad",
    columns: int = 8,
    pad_pitch: float = 150.0,
    layer_metal: LayerSpec = "MTOP",
    metal_width: float = 10,
) -> Component

Returns a 1D array of shorted_pads.

Parameters:

Name Type Description Default
pad ComponentSpec

pad spec.

'pad'
columns int

number of columns.

8
pad_pitch float

in um

150.0
layer_metal LayerSpec

for the short.

'MTOP'
metal_width float

for the short.

10
Source code in gdsfactory/components/pads/pads_shorted.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@gf.cell_with_module_name(tags=["pads"])
def pads_shorted(
    pad: ComponentSpec = "pad",
    columns: int = 8,
    pad_pitch: float = 150.0,
    layer_metal: LayerSpec = "MTOP",
    metal_width: float = 10,
) -> Component:
    """Returns a 1D array of shorted_pads.

    Args:
        pad: pad spec.
        columns: number of columns.
        pad_pitch: in um
        layer_metal: for the short.
        metal_width: for the short.
    """
    c = Component()
    pad = gf.get_component(pad)
    for i in range(columns):
        pad_ref = c.add_ref(pad)
        pad_ref.movex(i * pad_pitch - columns / 2 * pad_pitch + pad_pitch / 2)

    short = gf.c.rectangle(
        size=(pad_pitch * (columns - 1), metal_width),
        layer=layer_metal,
        centered=True,
    )
    c.add_ref(short)
    elec = [p for p in c.ports if p.port_type == "electrical"]
    if elec:
        c.create_pin(ports=elec, name="pad")
    return c

pads_shorted

rectangle_with_slits

rectangle_with_slits

rectangle_with_slits(
    size: Size = (100.0, 200.0),
    layer: LayerSpec = "WG",
    layer_slit: LayerSpec | None = None,
    centered: bool = False,
    port_type: str | None = None,
    slit_size: Size = (1.0, 1.0),
    slit_column_pitch: float = 20,
    slit_row_pitch: float = 20,
    slit_enclosure: float = 10,
) -> Component

Returns a rectangle with slits.

Metal slits reduce stress.

Parameters:

Name Type Description Default
size Size

(tuple) Width and height of rectangle.

(100.0, 200.0)
layer LayerSpec

Specific layer to put polygon geometry on.

'WG'
layer_slit LayerSpec | None

does a boolean NOT when None.

None
centered bool

True sets center to (0, 0), False sets south-west to (0, 0)

False
port_type str | None

for the rectangle.

None
slit_size Size

x, y slit size.

(1.0, 1.0)
slit_column_pitch float

pitch for columns of slits.

20
slit_row_pitch float

pitch for rows of slits.

20
slit_enclosure float

from slit to rectangle edge.

10
Source code in gdsfactory/components/pads/rectangle_with_slits.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@gf.cell_with_module_name(tags=["pads"])
def rectangle_with_slits(
    size: Size = (100.0, 200.0),
    layer: LayerSpec = "WG",
    layer_slit: LayerSpec | None = None,
    centered: bool = False,
    port_type: str | None = None,
    slit_size: Size = (1.0, 1.0),
    slit_column_pitch: float = 20,
    slit_row_pitch: float = 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 boolean 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_column_pitch: pitch for columns of slits.
        slit_row_pitch: pitch for rows of slits.
        slit_enclosure: from slit to rectangle edge.

        slit_enclosure
        _____________________________________
        |<--->                              |
        |                                   |
        |      ______________________       |
        |     |                      |      |
        |     |                      | slit_size[1]
        |  _  |______________________|      |
        |  |                                |
        |  | slit_row_pitch                 |
        |  |                                |  size[1]
        |  |   ______________________       |
        |  |  |                      |      |
        |  |  |                      |      |
        |  _  |______________________|      |
        |     <--------------------->       |
        |            slit_size[0]           |
        |___________________________________|
                        size[0]
    """
    c = Component()
    layer_tuple = gf.get_layer_tuple(layer)

    rectangle = gf.c.rectangle(
        size=size, layer=layer, port_type=port_type, centered=centered
    )
    r = c << rectangle
    c.add_ports(r.ports)
    columns = int(np.floor((size[0] - 2 * slit_enclosure) / slit_column_pitch))
    rows = int(np.floor((size[1] - 2 * slit_enclosure) / slit_row_pitch))

    if layer_slit is None:
        layer2 = (layer_tuple[0], layer_tuple[1] + 1)
        slit = gf.c.rectangle(size=slit_size, port_type=None, layer=layer2)
        slits = gf.c.array(
            slit,
            columns=columns,
            rows=rows,
            column_pitch=slit_column_pitch,
            row_pitch=slit_row_pitch,
            centered=centered,
        )
        slits_ref = c << slits
        slits_ref.center = r.center
        c = gf.boolean(
            rectangle,
            slits_ref,
            operation="not",
            layer1=layer,
            layer2=layer2,
            layer=layer,
        )
        c.add_ports(rectangle.ports)

    else:
        slit = gf.c.rectangle(size=slit_size, port_type=None, layer=layer_slit)
        slits_ref = c << gf.c.array(
            slit,
            columns=columns,
            rows=rows,
            column_pitch=slit_column_pitch,
            row_pitch=slit_row_pitch,
            centered=centered,
        )
        slits_ref.center = r.center
    return c

rectangle_with_slits

pcms

alignment_mark_cross

alignment_mark_cross

alignment_mark_cross(
    arm_width: float = 2.0,
    arm_length: float = 20.0,
    layer: LayerSpec = "WG",
    port_type: str | None = None,
) -> Component

Custom alignment cross mark for lithography alignment.

Creates a cross shape centered at the origin composed of two overlapping rectangles (horizontal and vertical arms).

Parameters:

Name Type Description Default
arm_width float

Width of each arm in um.

2.0
arm_length float

Length of each arm in um (full extent from tip to tip is 2 * arm_length).

20.0
layer LayerSpec

Layer specification for the cross geometry.

'WG'
port_type str | None

Optional port type. If provided, ports are added at the four arm tips.

None
Source code in gdsfactory/components/pcms/alignment_mark_cross.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
@gf.cell_with_module_name(tags=["pcms"])
def alignment_mark_cross(
    arm_width: float = 2.0,
    arm_length: float = 20.0,
    layer: LayerSpec = "WG",
    port_type: str | None = None,
) -> Component:
    """Custom alignment cross mark for lithography alignment.

    Creates a cross shape centered at the origin composed of two
    overlapping rectangles (horizontal and vertical arms).

    Args:
        arm_width: Width of each arm in um.
        arm_length: Length of each arm in um (full extent from tip to tip
            is 2 * arm_length).
        layer: Layer specification for the cross geometry.
        port_type: Optional port type. If provided, ports are added at the
            four arm tips.
    """
    c = Component()

    hw = arm_width / 2

    # Horizontal arm
    c.add_polygon(
        [(-arm_length, -hw), (arm_length, -hw), (arm_length, hw), (-arm_length, hw)],
        layer=layer,
    )

    # Vertical arm
    c.add_polygon(
        [(-hw, -arm_length), (hw, -arm_length), (hw, arm_length), (-hw, arm_length)],
        layer=layer,
    )

    if port_type is not None:
        c.add_port(
            name="o1",
            center=(-arm_length, 0),
            width=arm_width,
            orientation=180,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            name="o2",
            center=(arm_length, 0),
            width=arm_width,
            orientation=0,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            name="o3",
            center=(0, -arm_length),
            width=arm_width,
            orientation=270,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            name="o4",
            center=(0, arm_length),
            width=arm_width,
            orientation=90,
            layer=layer,
            port_type=port_type,
        )

    return c

alignment_mark_cross

cavity

cavity

cavity(
    component: ComponentSpec = "dbr",
    coupler: ComponentSpec = "coupler",
    length: float = 0.1,
    gap: float = 0.2,
    **kwargs: Any
) -> Component

Returns cavity from a coupler and a mirror.

connects the W0 port of the mirror to E1 and W1 coupler ports creating a resonant cavity

Parameters:

Name Type Description Default
component ComponentSpec

mirror.

'dbr'
coupler ComponentSpec

coupler library.

'coupler'
length float

coupler length.

0.1
gap float

coupler gap.

0.2
kwargs Any

coupler_settings.

{}
  ml (mirror left)              mr (mirror right)
   |                               |
   |o1 - o2__             __o3 - o1|
   |         \           /         |
              \         /
            ---=========---
     o1  o1    length      o4    o2
Source code in gdsfactory/components/pcms/cavity.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@gf.cell_with_module_name(tags=["pcms"])
def cavity(
    component: ComponentSpec = "dbr",
    coupler: ComponentSpec = "coupler",
    length: float = 0.1,
    gap: float = 0.2,
    **kwargs: Any,
) -> Component:
    r"""Returns  cavity from a coupler and a mirror.

    connects the W0 port of the mirror to E1 and W1 coupler ports
    creating a resonant cavity

    Args:
        component: mirror.
        coupler: coupler library.
        length: coupler length.
        gap: coupler gap.
        kwargs: coupler_settings.

    ```text
      ml (mirror left)              mr (mirror right)
       |                               |
       |o1 - o2__             __o3 - o1|
       |         \           /         |
                  \         /
                ---=========---
         o1  o1    length      o4    o2
    ```

    """
    mirror = gf.get_component(component)
    coupler = gf.get_component(coupler, length=length, gap=gap, **kwargs)

    c = gf.Component()
    cr = c << coupler
    ml = c << mirror
    mr = c << mirror

    ml.connect("o1", other=cr.ports["o2"])
    mr.connect("o1", other=cr.ports["o3"])
    c.add_port("o1", port=cr.ports["o1"])
    c.add_port("o2", port=cr.ports["o4"])
    c.copy_child_info(mirror)
    return c

cavity

cdsem_all

CD SEM structures.

cdsem_all

cdsem_all(
    widths: tuple[float, ...] = (
        0.4,
        0.45,
        0.5,
        0.6,
        0.8,
        1.0,
    ),
    dense_lines_width: float | None = 0.3,
    dense_lines_width_difference: float = 0.02,
    dense_lines_gap: float = 0.3,
    dense_lines_labels: tuple[str, ...] = (
        "DL",
        "DM",
        "DH",
    ),
    straight: ComponentSpec = "straight",
    bend90: ComponentSpec | None = "bend_circular",
    cross_section: CrossSectionSpec = "strip",
    text: ComponentSpec = "text_rectangular",
    spacing: float = 5,
    cdsem_bend180: ComponentSpec = "cdsem_bend180",
    text_size: float = 1,
) -> Component

Column with all optical PCMs.

Parameters:

Name Type Description Default
widths tuple[float, ...]

for straight lines.

(0.4, 0.45, 0.5, 0.6, 0.8, 1.0)
dense_lines_width float | None

in um.

0.3
dense_lines_width_difference float

in um.

0.02
dense_lines_gap float

in um.

0.3
dense_lines_labels tuple[str, ...]

strings.

('DL', 'DM', 'DH')
straight ComponentSpec

spec.

'straight'
bend90 ComponentSpec | None

spec.

'bend_circular'
cross_section CrossSectionSpec

spec.

'strip'
text ComponentSpec

spec.

'text_rectangular'
spacing float

from group to group.

5
cdsem_bend180 ComponentSpec

spec.

'cdsem_bend180'
text_size float

in um.

1
Source code in gdsfactory/components/pcms/cdsem_all.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@gf.cell_with_module_name(tags=["pcms"])
def cdsem_all(
    widths: tuple[float, ...] = (0.4, 0.45, 0.5, 0.6, 0.8, 1.0),
    dense_lines_width: float | None = 0.3,
    dense_lines_width_difference: float = 20e-3,
    dense_lines_gap: float = 0.3,
    dense_lines_labels: tuple[str, ...] = ("DL", "DM", "DH"),
    straight: ComponentSpec = "straight",
    bend90: ComponentSpec | None = "bend_circular",
    cross_section: CrossSectionSpec = "strip",
    text: ComponentSpec = "text_rectangular",
    spacing: float = 5,
    cdsem_bend180: ComponentSpec = "cdsem_bend180",
    text_size: float = 1,
) -> Component:
    """Column with all optical PCMs.

    Args:
        widths: for straight lines.
        dense_lines_width: in um.
        dense_lines_width_difference: in um.
        dense_lines_gap: in um.
        dense_lines_labels: strings.
        straight: spec.
        bend90: spec.
        cross_section: spec.
        text: spec.
        spacing: from group to group.
        cdsem_bend180: spec.
        text_size: in um.
    """
    c = Component()
    _c1 = gf.get_component(
        "cdsem_straight",
        widths=widths,
        cross_section=cross_section,
    )

    all_devices = [_c1]

    if bend90:
        all_devices += [
            gf.get_component(
                cdsem_bend180,
                width=width,
                straight=straight,
                bend90=bend90,
                cross_section=cross_section,
                text=text,
                text_size=text_size,
            )
            for width in widths
        ]

    if dense_lines_width:
        density_params = [
            (
                dense_lines_width - dense_lines_width_difference,
                dense_lines_gap - dense_lines_width_difference,
                dense_lines_labels[0],
            ),
            (dense_lines_width, dense_lines_gap, dense_lines_labels[1]),
            (
                dense_lines_width + dense_lines_width_difference,
                dense_lines_gap + dense_lines_width_difference,
                dense_lines_labels[2],
            ),
        ]

        all_devices += [
            gf.get_component(
                "cdsem_straight_density",
                widths=(w,) * 10,
                gaps=(g,) * 10,
                label=lbl,
                cross_section=cross_section,
                text=text,
                text_size=text_size,
            )
            for w, g, lbl in density_params
        ]

    ymin = 0.0
    for d in all_devices:
        ref = c.add_ref(d)
        ref.xmin = 0
        ref.ymin = ymin
        ymin += ref.ysize + spacing

    return c

cdsem_all

cdsem_bend180

CD SEM structures.

cdsem_bend180

cdsem_bend180(
    width: float = 0.5,
    radius: float = 10.0,
    wg_length: float | None = LINE_LENGTH,
    straight: ComponentSpec = "straight",
    bend90: ComponentSpec = "bend_circular",
    cross_section: CrossSectionSpec = "strip",
    text: ComponentSpec = "text_rectangular",
    text_size: float = 1.0,
) -> Component

Returns CDSEM structures.

Parameters:

Name Type Description Default
width float

of the line.

0.5
radius float

um.

10.0
wg_length float | None

in um.

LINE_LENGTH
straight ComponentSpec

spec.

'straight'
bend90 ComponentSpec

spec.

'bend_circular'
cross_section CrossSectionSpec

spec.

'strip'
text ComponentSpec

spec.

'text_rectangular'
text_size float

um.

1.0
Source code in gdsfactory/components/pcms/cdsem_bend180.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@gf.cell_with_module_name(tags=["pcms"])
def cdsem_bend180(
    width: float = 0.5,
    radius: float = 10.0,
    wg_length: float | None = LINE_LENGTH,
    straight: ComponentSpec = "straight",
    bend90: ComponentSpec = "bend_circular",
    cross_section: CrossSectionSpec = "strip",
    text: ComponentSpec = "text_rectangular",
    text_size: float = 1.0,
) -> Component:
    """Returns CDSEM structures.

    Args:
        width: of the line.
        radius: um.
        wg_length: in um.
        straight: spec.
        bend90: spec.
        cross_section: spec.
        text: spec.
        text_size: um.
    """
    c = Component()
    r = radius

    if wg_length is None:
        wg_length = 2 * r

    bend90 = gf.get_component(
        bend90,
        cross_section=cross_section,
        radius=r,
        width=width,
        allow_min_radius_violation=True,
    )
    wg = gf.get_component(
        straight, cross_section=cross_section, length=wg_length, width=width
    )

    # Add the U-turn on straight layer
    b1 = c.add_ref(bend90)
    b2 = c.add_ref(bend90)
    b2.connect("o2", b1.ports["o1"])

    wg1 = c.add_ref(wg)
    wg1.connect("o1", b1.ports["o2"])

    wg2 = c.add_ref(wg)
    wg2.connect("o1", b2.ports["o1"])

    label = c << gf.get_component(text, text=str(int(width * 1e3)), size=text_size)
    label.ymax = b2.ymin - 5
    label.x = 0

    c2 = gf.Component()
    ref = c2 << c
    ref.rotate(90)
    c2.flatten()
    return c2

cdsem_bend180

cdsem_coupler

CD SEM structures.

cdsem_coupler

cdsem_coupler(
    length: float = 420.0,
    gaps: Sequence[float] = (0.15, 0.2, 0.25),
    cross_section: CrossSectionSpec = "strip_no_ports",
    text: ComponentSpec | None = "text_rectangular",
    spacing: float = 7.0,
    positions: Sequence[float | None] | None = None,
    width: float | None = None,
    text_size: float = 1.0,
) -> Component

Returns 2 coupled waveguides gap sweep.

Parameters:

Name Type Description Default
length float

for the line.

420.0
gaps Sequence[float]

list of gaps for the sweep.

(0.15, 0.2, 0.25)
cross_section CrossSectionSpec

for the lines.

'strip_no_ports'
text ComponentSpec | None

optional text for labels.

'text_rectangular'
spacing float

Optional center to center spacing.

7.0
positions Sequence[float | None] | None

Optional positions for the text labels.

None
width float | None

width of the waveguide. If None, it will use the width of the cross_section.

None
text_size float

size of the text.

1.0
Source code in gdsfactory/components/pcms/cdsem_coupler.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@gf.cell_with_module_name(tags=["pcms"])
def cdsem_coupler(
    length: float = 420.0,
    gaps: Sequence[float] = (0.15, 0.2, 0.25),
    cross_section: CrossSectionSpec = "strip_no_ports",
    text: ComponentSpec | None = "text_rectangular",
    spacing: float = 7.0,
    positions: Sequence[float | None] | None = None,
    width: float | None = None,
    text_size: float = 1.0,
) -> Component:
    """Returns 2 coupled waveguides gap sweep.

    Args:
        length: for the line.
        gaps: list of gaps for the sweep.
        cross_section: for the lines.
        text: optional text for labels.
        spacing: Optional center to center spacing.
        positions: Optional positions for the text labels.
        width: width of the waveguide. If None, it will use the width of the cross_section.
        text_size: size of the text.
    """
    c = Component()
    if width:
        xs = gf.get_cross_section(cross_section, width=width)
    else:
        xs = gf.get_cross_section(cross_section)
    p = 0.0

    if positions is not None:
        positions = positions or [None] * len(gaps)
    else:
        positions = [i * spacing for i in range(len(gaps))]

    for gap, position in zip(gaps, positions, strict=False):
        line = c << gf.c.coupler_straight(length=length, cross_section=xs, gap=gap)
        p = position or p
        line.ymin = p
        if text:
            t = c << gf.get_component(text, text=str(int(gap * 1e3)), size=text_size)
            t.xmin = line.xmax + 5
            t.ymin = p

    return c

cdsem_coupler

cdsem_straight

CD SEM structures.

cdsem_straight

cdsem_straight(
    widths: Sequence[float] = (
        0.4,
        0.45,
        0.5,
        0.6,
        0.8,
        1.0,
    ),
    length: float = LINE_LENGTH,
    cross_section: CrossSectionSpec = "strip_no_ports",
    text: ComponentSpec | None = "text_rectangular",
    spacing: float = 7.0,
    positions: Sequence[float | None] | None = None,
    text_size: float = 1,
) -> Component

Returns straight waveguide lines width sweep.

Parameters:

Name Type Description Default
widths Sequence[float]

for the sweep.

(0.4, 0.45, 0.5, 0.6, 0.8, 1.0)
length float

for the line.

LINE_LENGTH
cross_section CrossSectionSpec

for the lines.

'strip_no_ports'
text ComponentSpec | None

optional text for labels.

'text_rectangular'
spacing float

Optional center to center spacing.

7.0
positions Sequence[float | None] | None

Optional positions for the text labels.

None
text_size float

in um.

1
Source code in gdsfactory/components/pcms/cdsem_straight.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@gf.cell_with_module_name(tags=["pcms"])
def cdsem_straight(
    widths: Sequence[float] = (0.4, 0.45, 0.5, 0.6, 0.8, 1.0),
    length: float = LINE_LENGTH,
    cross_section: CrossSectionSpec = "strip_no_ports",
    text: ComponentSpec | None = "text_rectangular",
    spacing: float = 7.0,
    positions: Sequence[float | None] | None = None,
    text_size: float = 1,
) -> Component:
    """Returns straight waveguide lines width sweep.

    Args:
        widths: for the sweep.
        length: for the line.
        cross_section: for the lines.
        text: optional text for labels.
        spacing: Optional center to center spacing.
        positions: Optional positions for the text labels.
        text_size: in um.
    """
    c = Component()
    p = 0.0
    if positions is not None:
        positions = positions or [None] * len(widths)
    else:
        positions = [i * spacing for i in range(len(widths))]

    for width, position in zip(widths, positions, strict=False):
        line = c << gf.c.straight(
            length=length, cross_section=cross_section, width=width
        )
        p = position or p
        line.ymin = p
        if text:
            t = c << gf.get_component(text, text=str(int(width * 1e3)), size=text_size)
            t.xmin = line.xmax + 5
            t.ymin = p

    return c

cdsem_straight

cdsem_straight_density

CD SEM structures.

cdsem_straight_density

cdsem_straight_density(
    widths: Floats = widths,
    gaps: Floats = gaps,
    length: float = 420.0,
    label: str = "",
    cross_section: CrossSectionSpec = "strip_no_ports",
    text: ComponentSpec | None = "text_rectangular",
    text_size: float = 1.0,
) -> Component

Returns sweep of dense straight lines.

Parameters:

Name Type Description Default
widths Floats

list of widths.

widths
gaps Floats

list of gaps.

gaps
length float

of the lines.

420.0
label str

defaults to widths[0] gaps[0].

''
cross_section CrossSectionSpec

spec.

'strip_no_ports'
text ComponentSpec | None

optional function for text.

'text_rectangular'
text_size float

size of the text.

1.0
Source code in gdsfactory/components/pcms/cdsem_straight_density.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@gf.cell_with_module_name(tags=["pcms"])
def cdsem_straight_density(
    widths: Floats = widths,
    gaps: Floats = gaps,
    length: float = 420.0,
    label: str = "",
    cross_section: CrossSectionSpec = "strip_no_ports",
    text: ComponentSpec | None = "text_rectangular",
    text_size: float = 1.0,
) -> Component:
    """Returns sweep of dense straight lines.

    Args:
        widths: list of widths.
        gaps: list of gaps.
        length: of the lines.
        label: defaults to widths[0] gaps[0].
        cross_section: spec.
        text: optional function for text.
        text_size: size of the text.
    """
    c = Component()
    label = label or f"{int(widths[0] * 1e3)} {int(gaps[0] * 1e3)}"

    ymin = 0.0
    tooth_ref: ComponentReference | None = None
    for width, gap in zip(widths, gaps, strict=False):
        tooth_ref = c << gf.c.straight(
            length=length, cross_section=cross_section, width=width
        )
        tooth_ref.ymin = ymin
        ymin += width + gap

    if text and tooth_ref is not None:
        marker_label = c << gf.get_component(text, text=f"{label}", size=text_size)
        marker_label.xmin = tooth_ref.xmax + 5
    return c

cdsem_straight_density

cutback_2x2

cutback_2x2

cutback_2x2(
    component: ComponentSpec = "mmi2x2",
    cols: int = 4,
    rows: int = 5,
    port1: str = "o1",
    port2: str = "o2",
    port3: str = "o3",
    port4: str = "o4",
    bend180: ComponentSpec = "bend_circular180",
    mirror: bool = False,
    straight_length: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    straight: ComponentSpec = "straight",
) -> Component

Returns a daisy chain of splitters for measuring their loss.

Parameters:

Name Type Description Default
component ComponentSpec

for cutback.

'mmi2x2'
cols int

number of columns.

4
rows int

number of rows.

5
port1 str

name of first optical port.

'o1'
port2 str

name of second optical port.

'o2'
port3 str

name of third optical port.

'o3'
port4 str

name of fourth optical port.

'o4'
bend180 ComponentSpec

ubend.

'bend_circular180'
mirror bool

Flips component. Useful when 'o2' is the port that you want to route to.

False
straight_length float | None

length of the straight section between cutbacks.

None
cross_section CrossSectionSpec

specification (CrossSection, string or dict).

'strip'
straight ComponentSpec

straight spec.

'straight'
Source code in gdsfactory/components/pcms/cutback_2x2.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
@gf.cell_with_module_name(tags=["pcms"])
def cutback_2x2(
    component: ComponentSpec = "mmi2x2",
    cols: int = 4,
    rows: int = 5,
    port1: str = "o1",
    port2: str = "o2",
    port3: str = "o3",
    port4: str = "o4",
    bend180: ComponentSpec = "bend_circular180",
    mirror: bool = False,
    straight_length: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    straight: ComponentSpec = "straight",
) -> Component:
    """Returns a daisy chain of splitters for measuring their loss.

    Args:
        component: for cutback.
        cols: number of columns.
        rows: number of rows.
        port1: name of first optical port.
        port2: name of second optical port.
        port3: name of third optical port.
        port4: name of fourth optical port.
        bend180: ubend.
        mirror: Flips component. Useful when 'o2' is the port that you want to route to.
        straight_length: length of the straight section between cutbacks.
        cross_section: specification (CrossSection, string or dict).
        straight: straight spec.
    """
    component = gf.get_component(component)

    bendu = _bendu_double(
        component=component,
        cross_section=cross_section,
        bend180=bend180,
        port1=port1,
        port2=port2,
    )

    straight_component = _straight_double(
        component=component,
        cross_section=cross_section,
        straight_length=straight_length,
        port1=port1,
        port2=port2,
        straight=straight,
    )

    # Define a map between symbols and (component, input port, output port)
    symbol_to_component = {
        "A": (component, port1, port3),
        "B": (component, port4, port2),
        "D": (bendu, "o2", "o3"),
        "C": (bendu, "o4", "o1"),
        "-": (straight_component, "o1", "o3"),
        "_": (straight_component, "o2", "o4"),
    }

    # Generate the sequence of staircases
    s = ""
    for i in range(rows):
        s += "AB" * cols
        if mirror:
            s += "C" if i % 2 == 0 else "D"
        else:
            s += "D" if i % 2 == 0 else "C"

    s = s[:-1]
    s += "-_"

    for i in range(rows):
        s += "AB" * cols
        s += "D" if (i + rows) % 2 == 0 else "C"

    s = s[:-1]
    n = cols * rows * 2
    c = component_sequence(sequence=s, symbol_to_component=symbol_to_component)
    c.ports.clear()
    c.add_port("o1", port=c.insts["A1"].ports["o1"])
    c.add_port("o2", port=c.insts["A1"].ports["o2"])

    c.add_port("o3", port=c.insts[f"B{n}"].ports["o2"])
    c.add_port("o4", port=c.insts[f"B{n}"].ports["o1"])

    c.info["components"] = 2 * n
    return c

cutback_2x2

cutback_bend

cutback_bend

cutback_bend(
    component: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 5,
    **kwargs: Any
) -> Component

We recommend using cutback_bend90 instead for a smaller footprint.

Parameters:

Name Type Description Default
component ComponentSpec

bend spec.

'bend_euler'
straight ComponentSpec

straight spec.

'straight'
straight_length float

in um.

5.0
rows int

number of rows.

6
cols int

number of cols.

5
kwargs Any

cross_section settings.

{}
Source code in gdsfactory/components/pcms/cutback_bend.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
@gf.cell_with_module_name(tags=["pcms"])
def cutback_bend(
    component: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 5,
    **kwargs: Any,
) -> Component:
    """We recommend using cutback_bend90 instead for a smaller footprint.

    Args:
        component: bend spec.
        straight: straight spec.
        straight_length: in um.
        rows: number of rows.
        cols: number of cols.
        kwargs: cross_section settings.

        this is a column
            _
          _|
        _|

        _ this is a row
    """
    from gdsfactory.pdk import get_component

    bend90 = get_component(component, **kwargs)
    straightx = gf.get_component(straight, length=straight_length, **kwargs)

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

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

    c = component_sequence(
        sequence=s, symbol_to_component=symbol_to_component, start_orientation=90
    )
    c.info["components"] = rows * cols * 2 + cols * 2 - 2
    return c

cutback_bend180

cutback_bend180(
    component: ComponentSpec = "bend_euler180",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 6,
    spacing: float = 3.0,
    **kwargs: Any
) -> Component

Returns cutback to measure u bend loss.

Parameters:

Name Type Description Default
component ComponentSpec

bend spec.

'bend_euler180'
straight ComponentSpec

straight spec.

'straight'
straight_length float

in um.

5.0
rows int

number of rows.

6
cols int

number of cols.

6
spacing float

in um.

3.0
kwargs Any

cross_section settings.

_

{}
Source code in gdsfactory/components/pcms/cutback_bend.py
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
@gf.cell_with_module_name(tags=["pcms"])
def cutback_bend180(
    component: ComponentSpec = "bend_euler180",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 6,
    spacing: float = 3.0,
    **kwargs: Any,
) -> Component:
    """Returns cutback to measure u bend loss.

    Args:
        component: bend spec.
        straight: straight spec.
        straight_length: in um.
        rows: number of rows.
        cols: number of cols.
        spacing: in um.
        kwargs: cross_section settings.

          _
        _| |_  this is a row

        _ this is a column
    """
    bend180 = gf.get_component(component, **kwargs)
    straightx = gf.get_component(straight, length=straight_length, **kwargs)
    wg_vertical = gf.get_component(
        straight,
        length=2 * bend180.xsize + straight_length + spacing,
        **kwargs,
    )

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

    # Generate the sequence of staircases
    s = "".join(
        "D-C-" * rows + "|" if i % 2 == 0 else "C-D-" * rows + "|" for i in range(cols)
    )

    s = s[:-1]

    c = component_sequence(
        sequence=s, symbol_to_component=symbol_to_component, start_orientation=0
    )
    c.info["components"] = rows * cols * 2 + cols * 2 - 2
    return c

cutback_bend90

cutback_bend90(
    component: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 6,
    spacing: int = 5,
    **kwargs: Any
) -> Component

Returns bend90 cutback.

Parameters:

Name Type Description Default
component ComponentSpec

bend spec.

'bend_euler'
straight ComponentSpec

straight spec.

'straight'
straight_length float

in um.

5.0
rows int

number of rows.

6
cols int

number of cols.

6
spacing int

in um.

5
kwargs Any

cross_section settings.

_

{}
Source code in gdsfactory/components/pcms/cutback_bend.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
@gf.cell_with_module_name(tags=["pcms"])
def cutback_bend90(
    component: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 6,
    spacing: int = 5,
    **kwargs: Any,
) -> Component:
    """Returns bend90 cutback.

    Args:
        component: bend spec.
        straight: straight spec.
        straight_length: in um.
        rows: number of rows.
        cols: number of cols.
        spacing: in um.
        kwargs: cross_section settings.

           _
        |_| |
    """
    bend90 = gf.get_component(component, **kwargs)
    straightx = gf.get_component(straight, length=straight_length, **kwargs)
    straight_length = 2 * _get_bend_size(bend90) + spacing + straight_length
    straighty = gf.get_component(straight, length=straight_length, **kwargs)

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

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

    # Create the component from the sequence
    c = component_sequence(
        sequence=s, symbol_to_component=symbol_to_component, start_orientation=0
    )
    c.info["components"] = rows * cols * 4
    return c

staircase

staircase(
    component: ComponentSpec | Component = "bend_euler",
    straight: ComponentSpec = "straight",
    length_v: float = 5.0,
    length_h: float = 5.0,
    rows: int = 4,
    **kwargs: Any
) -> Component

Returns staircase.

Parameters:

Name Type Description Default
component ComponentSpec | Component

bend spec.

'bend_euler'
straight ComponentSpec

straight spec.

'straight'
length_v float

vertical length.

5.0
length_h float

vertical length.

5.0
rows int

number of rows.

4
cols

number of cols.

required
kwargs Any

cross_section settings.

{}
Source code in gdsfactory/components/pcms/cutback_bend.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
@gf.cell_with_module_name(tags=["pcms"])
def staircase(
    component: ComponentSpec | Component = "bend_euler",
    straight: ComponentSpec = "straight",
    length_v: float = 5.0,
    length_h: float = 5.0,
    rows: int = 4,
    **kwargs: Any,
) -> Component:
    """Returns staircase.

    Args:
        component: bend spec.
        straight: straight spec.
        length_v: vertical length.
        length_h: vertical length.
        rows: number of rows.
        cols: number of cols.
        kwargs: cross_section settings.
    """
    bend90 = (
        component
        if isinstance(component, Component)
        else gf.get_component(component, **kwargs)
    )

    wgh = gf.get_component(straight, length=length_h, **kwargs)
    wgv = gf.get_component(straight, length=length_v, **kwargs)

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

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

    c = component_sequence(
        sequence=s,
        symbol_to_component=symbol_to_component,
        start_orientation=0,
    )
    c.info["components"] = 2 * rows
    return c

cutback_bend

cutback_bend180

cutback_bend180(
    component: ComponentSpec = "bend_euler180",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 6,
    spacing: float = 3.0,
    **kwargs: Any
) -> Component

Returns cutback to measure u bend loss.

Parameters:

Name Type Description Default
component ComponentSpec

bend spec.

'bend_euler180'
straight ComponentSpec

straight spec.

'straight'
straight_length float

in um.

5.0
rows int

number of rows.

6
cols int

number of cols.

6
spacing float

in um.

3.0
kwargs Any

cross_section settings.

_

{}
Source code in gdsfactory/components/pcms/cutback_bend.py
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
@gf.cell_with_module_name(tags=["pcms"])
def cutback_bend180(
    component: ComponentSpec = "bend_euler180",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 6,
    spacing: float = 3.0,
    **kwargs: Any,
) -> Component:
    """Returns cutback to measure u bend loss.

    Args:
        component: bend spec.
        straight: straight spec.
        straight_length: in um.
        rows: number of rows.
        cols: number of cols.
        spacing: in um.
        kwargs: cross_section settings.

          _
        _| |_  this is a row

        _ this is a column
    """
    bend180 = gf.get_component(component, **kwargs)
    straightx = gf.get_component(straight, length=straight_length, **kwargs)
    wg_vertical = gf.get_component(
        straight,
        length=2 * bend180.xsize + straight_length + spacing,
        **kwargs,
    )

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

    # Generate the sequence of staircases
    s = "".join(
        "D-C-" * rows + "|" if i % 2 == 0 else "C-D-" * rows + "|" for i in range(cols)
    )

    s = s[:-1]

    c = component_sequence(
        sequence=s, symbol_to_component=symbol_to_component, start_orientation=0
    )
    c.info["components"] = rows * cols * 2 + cols * 2 - 2
    return c

cutback_bend180

cutback_bend180circular module-attribute

cutback_bend180circular = partial(
    cutback_bend180, component="bend_circular180"
)

cutback_bend180circular

cutback_bend90

cutback_bend90(
    component: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 6,
    spacing: int = 5,
    **kwargs: Any
) -> Component

Returns bend90 cutback.

Parameters:

Name Type Description Default
component ComponentSpec

bend spec.

'bend_euler'
straight ComponentSpec

straight spec.

'straight'
straight_length float

in um.

5.0
rows int

number of rows.

6
cols int

number of cols.

6
spacing int

in um.

5
kwargs Any

cross_section settings.

_

{}
Source code in gdsfactory/components/pcms/cutback_bend.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
@gf.cell_with_module_name(tags=["pcms"])
def cutback_bend90(
    component: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    straight_length: float = 5.0,
    rows: int = 6,
    cols: int = 6,
    spacing: int = 5,
    **kwargs: Any,
) -> Component:
    """Returns bend90 cutback.

    Args:
        component: bend spec.
        straight: straight spec.
        straight_length: in um.
        rows: number of rows.
        cols: number of cols.
        spacing: in um.
        kwargs: cross_section settings.

           _
        |_| |
    """
    bend90 = gf.get_component(component, **kwargs)
    straightx = gf.get_component(straight, length=straight_length, **kwargs)
    straight_length = 2 * _get_bend_size(bend90) + spacing + straight_length
    straighty = gf.get_component(straight, length=straight_length, **kwargs)

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

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

    # Create the component from the sequence
    c = component_sequence(
        sequence=s, symbol_to_component=symbol_to_component, start_orientation=0
    )
    c.info["components"] = rows * cols * 4
    return c

cutback_bend90

cutback_bend90circular module-attribute

cutback_bend90circular = partial(
    cutback_bend90, component="bend_circular"
)

cutback_bend90circular

cutback_component

cutback_component

cutback_component(
    component: ComponentSpec = "taper_0p5_to_3_l36",
    cols: int = 4,
    rows: int = 5,
    port1: str = "o1",
    port2: str = "o2",
    bend180: ComponentSpec = "bend_euler180",
    mirror: bool = False,
    mirror1: bool = False,
    mirror2: bool = False,
    straight_length: float | None = None,
    straight_length_pair: float | None = None,
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
    radius: float | None = None,
    **kwargs: Any
) -> Component

Returns a daisy chain of components for measuring their loss.

Works only for components with 2 ports (input, output).

Parameters:

Name Type Description Default
component ComponentSpec

for cutback.

'taper_0p5_to_3_l36'
cols int

number of columns.

4
rows int

number of rows.

5
port1 str

name of first optical port.

'o1'
port2 str

name of second optical port.

'o2'
bend180 ComponentSpec

ubend.

'bend_euler180'
mirror bool

Flips component. Useful when 'o2' is the port that you want to route to.

False
mirror1 bool

mirrors first component.

False
mirror2 bool

mirrors second component.

False
straight_length float | None

length of the straight section between cutbacks.

None
straight_length_pair float | None

length of the straight section between each component pair.

None
cross_section CrossSectionSpec

specification (CrossSection, string or dict).

'strip'
straight ComponentSpec

straight spec.

'straight'
radius float | None

radius for the bends. Defaults to cross_section radius.

None
kwargs Any

component settings.

{}
Source code in gdsfactory/components/pcms/cutback_component.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
@gf.cell_with_module_name(tags=["pcms"])
def cutback_component(
    component: ComponentSpec = "taper_0p5_to_3_l36",
    cols: int = 4,
    rows: int = 5,
    port1: str = "o1",
    port2: str = "o2",
    bend180: ComponentSpec = "bend_euler180",
    mirror: bool = False,
    mirror1: bool = False,
    mirror2: bool = False,
    straight_length: float | None = None,
    straight_length_pair: float | None = None,
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip",
    radius: float | None = None,
    **kwargs: Any,
) -> Component:
    """Returns a daisy chain of components for measuring their loss.

    Works only for components with 2 ports (input, output).

    Args:
        component: for cutback.
        cols: number of columns.
        rows: number of rows.
        port1: name of first optical port.
        port2: name of second optical port.
        bend180: ubend.
        mirror: Flips component. Useful when 'o2' is the port that you want to route to.
        mirror1: mirrors first component.
        mirror2: mirrors second component.
        straight_length: length of the straight section between cutbacks.
        straight_length_pair: length of the straight section between each component pair.
        cross_section: specification (CrossSection, string or dict).
        straight: straight spec.
        radius: radius for the bends. Defaults to cross_section radius.
        kwargs: component settings.
    """
    xs = gf.get_cross_section(cross_section)

    component = gf.get_component(component, **kwargs)
    bendu = gf.get_component(bend180, cross_section=xs)

    radius = radius or xs.radius
    assert radius is not None
    straight_length = radius * 2 if straight_length is None else straight_length
    straight_component = gf.get_component(
        straight, length=straight_length, cross_section=xs
    )
    # Define a map between symbols and (component, input port, output port)
    symbol_to_component = {
        "A": (component, port1, port2),
        "B": (component, port2, port1),
        "D": (bendu, "o1", "o2"),
        "C": (bendu, "o2", "o1"),
        "-": (straight_component, "o1", "o2"),
        "_": (straight_component, "o2", "o1"),
    }
    if straight_length_pair:
        straight_pair = gf.get_component(
            straight, length=straight_length_pair, cross_section=xs
        )
        symbol_to_component["."] = (straight_pair, "o2", "o1")

    # Generate the sequence of staircases
    s = ""
    a = "!A" if mirror1 else "A"
    b = "!B" if mirror2 else "B"

    for i in range(rows):
        if straight_length_pair:
            s += f"{a}.{b}" * cols
        else:
            s += (a + b) * cols

        if mirror:
            s += "C" if i % 2 == 0 else "D"
        else:
            s += "D" if i % 2 == 0 else "C"

    s = s[:-1]
    s += "-_"

    for i in range(rows):
        if straight_length_pair:
            s += f"{a}.{b}" * cols
        else:
            s += (a + b) * cols
        s += "D" if (i + rows) % 2 == 0 else "C"

    s = s[:-1]

    c = component_sequence(sequence=s, symbol_to_component=symbol_to_component)
    n = 2 * s.count("A")
    c.info["components"] = n
    return c

cutback_component

cutback_component_mirror module-attribute

cutback_component_mirror = partial(
    cutback_component, mirror=True
)

cutback_component_mirror

cutback_splitter

cutback_splitter

cutback_splitter(
    component: ComponentSpec = "mmi1x2",
    cols: int = 4,
    rows: int = 5,
    port1: str = "o1",
    port2: str = "o2",
    port3: str = "o3",
    bend180: ComponentSpec = "bend_euler180",
    mirror: bool = False,
    straight: ComponentSpec = "straight",
    straight_length: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    **kwargs: Any
) -> Component

Returns a daisy chain of splitters for measuring their loss.

Parameters:

Name Type Description Default
component ComponentSpec

for cutback.

'mmi1x2'
cols int

number of columns.

4
rows int

number of rows.

5
port1 str

name of first optical port.

'o1'
port2 str

name of second optical port.

'o2'
port3 str

name of third optical port.

'o3'
bend180 ComponentSpec

ubend.

'bend_euler180'
mirror bool

Flips component. Useful when 'o2' is the port that you want to route to.

False
straight ComponentSpec

waveguide spec to connect both sides.

'straight'
straight_length float | None

length of the straight section between cutbacks.

None
cross_section CrossSectionSpec

specification (CrossSection, string or dict).

'strip'
kwargs Any

cross_section settings.

{}
Source code in gdsfactory/components/pcms/cutback_splitter.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@gf.cell_with_module_name(tags=["pcms"])
def cutback_splitter(
    component: ComponentSpec = "mmi1x2",
    cols: int = 4,
    rows: int = 5,
    port1: str = "o1",
    port2: str = "o2",
    port3: str = "o3",
    bend180: ComponentSpec = "bend_euler180",
    mirror: bool = False,
    straight: ComponentSpec = "straight",
    straight_length: float | None = None,
    cross_section: CrossSectionSpec = "strip",
    **kwargs: Any,
) -> Component:
    """Returns a daisy chain of splitters for measuring their loss.

    Args:
        component: for cutback.
        cols: number of columns.
        rows: number of rows.
        port1: name of first optical port.
        port2: name of second optical port.
        port3: name of third optical port.
        bend180: ubend.
        mirror: Flips component. Useful when 'o2' is the port that you want to route to.
        straight: waveguide spec to connect both sides.
        straight_length: length of the straight section between cutbacks.
        cross_section: specification (CrossSection, string or dict).
        kwargs: cross_section settings.
    """
    xs = gf.get_cross_section(cross_section, **kwargs)

    component = gf.get_component(component)
    bendu = gf.get_component(bend180, cross_section=xs)
    radius = xs.radius
    assert radius is not None
    straight_component = gf.get_component(
        straight,
        length=straight_length or radius * 2,
        cross_section=xs,
    )

    # Define a map between symbols and (component, input port, output port)
    symbol_to_component = {
        "A": (component, port1, port2),
        "B": (component, port3, port1),
        "D": (bendu, "o1", "o2"),
        "C": (bendu, "o2", "o1"),
        "-": (straight_component, "o1", "o2"),
        "_": (straight_component, "o2", "o1"),
    }

    s = ""
    for i in range(rows):
        s += "AB" * cols
        if mirror:
            s += "C" if i % 2 == 0 else "D"
        else:
            s += "D" if i % 2 == 0 else "C"

    s = s[:-1]
    s += "-_"

    for i in range(rows):
        s += "AB" * cols
        s += "D" if (i + rows) % 2 == 0 else "C"

    s = s[:-1]

    c = component_sequence(sequence=s, symbol_to_component=symbol_to_component)
    n = len(s) - 2
    c.info["components"] = n
    return c

cutback_splitter

greek_cross

Greek cross test structure.

greek_cross

greek_cross(
    length: float = 30,
    layers: LayerSpecs = ("WG", "N"),
    widths: Floats = (2.0, 3.0),
    offsets: Floats | None = None,
    via_stack: ComponentSpec = "via_stack_npp_m1",
    layer_index: int = 0,
) -> gf.Component

Simple greek cross with via stacks at the endpoints.

Process control monitor for dopant sheet resistivity and linewidth variation.

Parameters:

Name Type Description Default
length float

length of cross arms.

30
layers LayerSpecs

list of layers.

('WG', 'N')
widths Floats

list of widths (same order as layers).

(2.0, 3.0)
offsets Floats | None

how much to extend each layer beyond the cross length negative shorter, positive longer.

None
via_stack ComponentSpec

via component to attach to the cross.

'via_stack_npp_m1'
layer_index int

index of the layer to connect the via_stack to.

via_stack <-------> ___ length __ | |<-------------------->| |

0

References: - Walton, Anthony J.. “MICROELECTRONIC TEST STRUCTURES.” (1999). - W. Versnel, Analysis of the Greek cross, a Van der Pauw structure with finite contacts, Solid-State Electronics, Volume 22, Issue 11, 1979, Pages 911-914, ISSN 0038-1101, https://doi.org/10.1016/0038-1101(79)90061-3. - S. Enderling et al., "Sheet resistance measurement of non-standard cleanroom materials using suspended Greek cross test structures," IEEE Transactions on Semiconductor Manufacturing, vol. 19, no. 1, pp. 2-9, Feb. 2006, doi: 10.1109/TSM.2005.863248. - https://download.tek.com/document/S530_VanDerPauwSheetRstnce.pdf

Source code in gdsfactory/components/pcms/greek_cross.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@gf.cell_with_module_name(tags=["pcms"])
def greek_cross(
    length: float = 30,
    layers: LayerSpecs = ("WG", "N"),
    widths: Floats = (2.0, 3.0),
    offsets: Floats | None = None,
    via_stack: ComponentSpec = "via_stack_npp_m1",
    layer_index: int = 0,
) -> gf.Component:
    """Simple greek cross with via stacks at the endpoints.

    Process control monitor for dopant sheet resistivity and linewidth variation.

    Args:
        length: length of cross arms.
        layers: list of layers.
        widths: list of widths (same order as layers).
        offsets: how much to extend each layer beyond the cross length
            negative shorter, positive longer.
        via_stack: via component to attach to the cross.
        layer_index: index of the layer to connect the via_stack to.

            via_stack
            <------->
            _________       length          ________
            |       |<-------------------->|        |
        2x  |       |     |   ↓       |<-->|        |
            |       |======== width =======|        |
            |_______|<--> |   ↑       |<-->|________|
                    offset            offset


    References:
    - Walton, Anthony J.. “MICROELECTRONIC TEST STRUCTURES.” (1999).
    - W. Versnel, Analysis of the Greek cross, a Van der Pauw structure with finite
      contacts, Solid-State Electronics, Volume 22, Issue 11, 1979, Pages 911-914,
      ISSN 0038-1101, https://doi.org/10.1016/0038-1101(79)90061-3.
    - S. Enderling et al., "Sheet resistance measurement of non-standard cleanroom
      materials using suspended Greek cross test structures," IEEE Transactions on
      Semiconductor Manufacturing, vol. 19, no. 1, pp. 2-9, Feb. 2006,
      doi: 10.1109/TSM.2005.863248.
    - https://download.tek.com/document/S530_VanDerPauwSheetRstnce.pdf

    """
    c = gf.Component()

    if len(layers) != len(widths):
        raise ValueError("len(layers) must equal len(widths).")

    offsets = offsets or (0.0,) * len(layers)

    for index, (layer, width, offset) in enumerate(
        zip(layers, widths, offsets, strict=False)
    ):
        ref = c << gf.c.cross(
            length=length + 2 * offset,
            width=width,
            layer=layer,
            port_type="electrical",
        )
        if index == layer_index:
            cross_ref = ref

    # Add via
    for port in cross_ref.ports:
        via_stack_ref = c << gf.get_component(via_stack)
        via_stack_ref.connect(
            "e1",
            port,
            allow_layer_mismatch=True,
            allow_width_mismatch=True,
        )
        c.add_port(name=port.name, port=via_stack_ref.ports["e3"])

    c.flatten()
    c.auto_rename_ports()
    for port in c.ports:
        if port.port_type == "electrical":
            c.create_pin(ports=[port], name=port.name)
    return c

greek_cross_with_pads

greek_cross_with_pads(
    pad: ComponentSpec = "pad",
    pad_pitch: float = 150.0,
    greek_cross_component: ComponentSpec = "greek_cross",
    pad_via: ComponentSpec = "via_stack_m1_mtop",
    cross_section: CrossSectionSpec = metal1,
    pad_port_name: str = "e4",
) -> gf.Component

Greek cross under 4 DC pads, ready to test.

Parameters:

Name Type Description Default
pad ComponentSpec

component to use for probe pads.

'pad'
pad_pitch float

spacing between pads.

150.0
greek_cross_component ComponentSpec

component to use for greek cross.

'greek_cross'
pad_via ComponentSpec

via to add to the pad.

'via_stack_m1_mtop'
cross_section CrossSectionSpec

cross-section for cross via to pad via wiring.

metal1
pad_port_name str

name of the port to connect to the greek cross.

'e4'
Source code in gdsfactory/components/pcms/greek_cross.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@gf.cell_with_module_name(tags=["pcms"])
def greek_cross_with_pads(
    pad: ComponentSpec = "pad",
    pad_pitch: float = 150.0,
    greek_cross_component: ComponentSpec = "greek_cross",
    pad_via: ComponentSpec = "via_stack_m1_mtop",
    cross_section: CrossSectionSpec = metal1,
    pad_port_name: str = "e4",
) -> gf.Component:
    """Greek cross under 4 DC pads, ready to test.

    Arguments:
        pad: component to use for probe pads.
        pad_pitch: spacing between pads.
        greek_cross_component: component to use for greek cross.
        pad_via: via to add to the pad.
        cross_section: cross-section for cross via to pad via wiring.
        pad_port_name: name of the port to connect to the greek cross.
    """
    c = gf.Component()

    # Cross
    cross_ref = c << gf.get_component(greek_cross_component)
    cross_ref.x = (
        2 * pad_pitch - (pad_pitch - gf.get_component(pad).info["size"][0]) / 2
    )

    cross_pad_via_port_pairs = {
        0: ("e1", "e2"),
        1: ("e4", "e2"),
        2: ("e2", "e4"),
        3: ("e3", "e4"),
    }

    # Vias to pads
    for index in range(4):
        pad_ref = c << gf.get_component(pad)
        pad_ref.x = index * pad_pitch + pad_ref.xsize / 2
        via_ref = c << gf.get_component(pad_via)
        if index < 2:
            via_ref.connect(
                "e2",
                other=pad_ref.ports["e4"],
                allow_layer_mismatch=True,
                allow_width_mismatch=True,
            )
        else:
            via_ref.connect(
                "e4",
                other=pad_ref.ports["e2"],
                allow_layer_mismatch=True,
                allow_width_mismatch=True,
            )

        gf.routing.route_single_electrical(
            c,
            cross_ref[cross_pad_via_port_pairs[index][0]],
            via_ref[cross_pad_via_port_pairs[index][1]],
            cross_section=cross_section,
            start_straight_length=5,
            end_straight_length=5,
        )
        c.add_port(
            name=f"e{index + 1}",
            port=pad_ref.ports[pad_port_name],
        )

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    return c

greek_cross

greek_cross_with_pads

greek_cross_with_pads(
    pad: ComponentSpec = "pad",
    pad_pitch: float = 150.0,
    greek_cross_component: ComponentSpec = "greek_cross",
    pad_via: ComponentSpec = "via_stack_m1_mtop",
    cross_section: CrossSectionSpec = metal1,
    pad_port_name: str = "e4",
) -> gf.Component

Greek cross under 4 DC pads, ready to test.

Parameters:

Name Type Description Default
pad ComponentSpec

component to use for probe pads.

'pad'
pad_pitch float

spacing between pads.

150.0
greek_cross_component ComponentSpec

component to use for greek cross.

'greek_cross'
pad_via ComponentSpec

via to add to the pad.

'via_stack_m1_mtop'
cross_section CrossSectionSpec

cross-section for cross via to pad via wiring.

metal1
pad_port_name str

name of the port to connect to the greek cross.

'e4'
Source code in gdsfactory/components/pcms/greek_cross.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@gf.cell_with_module_name(tags=["pcms"])
def greek_cross_with_pads(
    pad: ComponentSpec = "pad",
    pad_pitch: float = 150.0,
    greek_cross_component: ComponentSpec = "greek_cross",
    pad_via: ComponentSpec = "via_stack_m1_mtop",
    cross_section: CrossSectionSpec = metal1,
    pad_port_name: str = "e4",
) -> gf.Component:
    """Greek cross under 4 DC pads, ready to test.

    Arguments:
        pad: component to use for probe pads.
        pad_pitch: spacing between pads.
        greek_cross_component: component to use for greek cross.
        pad_via: via to add to the pad.
        cross_section: cross-section for cross via to pad via wiring.
        pad_port_name: name of the port to connect to the greek cross.
    """
    c = gf.Component()

    # Cross
    cross_ref = c << gf.get_component(greek_cross_component)
    cross_ref.x = (
        2 * pad_pitch - (pad_pitch - gf.get_component(pad).info["size"][0]) / 2
    )

    cross_pad_via_port_pairs = {
        0: ("e1", "e2"),
        1: ("e4", "e2"),
        2: ("e2", "e4"),
        3: ("e3", "e4"),
    }

    # Vias to pads
    for index in range(4):
        pad_ref = c << gf.get_component(pad)
        pad_ref.x = index * pad_pitch + pad_ref.xsize / 2
        via_ref = c << gf.get_component(pad_via)
        if index < 2:
            via_ref.connect(
                "e2",
                other=pad_ref.ports["e4"],
                allow_layer_mismatch=True,
                allow_width_mismatch=True,
            )
        else:
            via_ref.connect(
                "e4",
                other=pad_ref.ports["e2"],
                allow_layer_mismatch=True,
                allow_width_mismatch=True,
            )

        gf.routing.route_single_electrical(
            c,
            cross_ref[cross_pad_via_port_pairs[index][0]],
            via_ref[cross_pad_via_port_pairs[index][1]],
            cross_section=cross_section,
            start_straight_length=5,
            end_straight_length=5,
        )
        c.add_port(
            name=f"e{index + 1}",
            port=pad_ref.ports[pad_port_name],
        )

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    return c

greek_cross_with_pads

litho_calipers

litho_calipers

litho_calipers(
    notch_size: Size = (2.0, 5.0),
    notch_spacing: float = 2.0,
    num_notches: int = 11,
    offset_per_notch: float = 0.1,
    row_spacing: float = 0.0,
    layer1: LayerSpec = "WG",
    layer2: LayerSpec = "SLAB150",
) -> Component

Vernier caliper structure to test lithography alignment.

Only the middle finger is aligned and the rest are offset. adapted from phidl

Parameters:

Name Type Description Default
notch_size Size

[xwidth, yheight].

(2.0, 5.0)
notch_spacing float

in um.

2.0
num_notches int

number of notches.

11
offset_per_notch float

in um.

0.1
row_spacing float

0

0.0
layer1 LayerSpec

layer.

'WG'
layer2 LayerSpec

layer.

'SLAB150'
Source code in gdsfactory/components/pcms/litho_calipers.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@gf.cell_with_module_name(tags=["pcms"])
def litho_calipers(
    notch_size: Size = (2.0, 5.0),
    notch_spacing: float = 2.0,
    num_notches: int = 11,
    offset_per_notch: float = 0.1,
    row_spacing: float = 0.0,
    layer1: LayerSpec = "WG",
    layer2: LayerSpec = "SLAB150",
) -> Component:
    """Vernier caliper structure to test lithography alignment.

    Only the middle finger is aligned and the rest are offset.
    adapted from phidl

    Args:
        notch_size: [xwidth, yheight].
        notch_spacing: in um.
        num_notches: number of notches.
        offset_per_notch: in um.
        row_spacing: 0
        layer1: layer.
        layer2: layer.
    """
    D = gf.Component()
    num_notches_total = num_notches * 2 + 1
    centre_notch = num_notches
    R1 = gf.c.rectangle(size=notch_size, layer=layer1, port_type=None)
    R2 = gf.c.rectangle(size=notch_size, layer=layer2, port_type=None)

    for i in range(num_notches_total):
        if i == centre_notch:
            ref = D.add_ref(R1)
            ref.movex(i * (notch_size[0] + notch_spacing)).movey(notch_size[1])
            ref = D.add_ref(R2)
            ref.movex(
                i * (notch_size[0] + notch_spacing)
                + offset_per_notch * (centre_notch - i)
            ).movey(-2 * notch_size[1] - row_spacing)
        ref = D.add_ref(R1)
        ref.movex(i * (notch_size[0] + notch_spacing))
        ref = D.add_ref(R2)
        ref.movex(
            i * (notch_size[0] + notch_spacing) + offset_per_notch * (centre_notch - i)
        )
        ref.movey(-notch_size[1] - row_spacing)
    return D

litho_calipers

litho_ruler

litho_ruler

litho_ruler(
    height: float = 2,
    width: float = 0.5,
    spacing: float = 2.0,
    scale: tuple[float, ...] = (
        3,
        1,
        1,
        1,
        1,
        2,
        1,
        1,
        1,
        1,
    ),
    num_marks: int = 21,
    layer: LayerSpec = "WG",
) -> gf.Component

Ruler structure for lithographic measurement.

Includes marks of varying scales to allow for easy reading by eye.

based on phidl.geometry

Parameters:

Name Type Description Default
height float

Height of the ruling marks in um.

2
width float

Width of the ruling marks in um.

0.5
spacing float

Center-to-center spacing of the ruling marks in um.

2.0
scale tuple[float, ...]

Height scale pattern of marks.

(3, 1, 1, 1, 1, 2, 1, 1, 1, 1)
num_marks int

Total number of marks to generate.

21
layer LayerSpec

Specific layer to put the ruler geometry on.

'WG'
Source code in gdsfactory/components/pcms/litho_ruler.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@gf.cell_with_module_name(tags=["pcms"])
def litho_ruler(
    height: float = 2,
    width: float = 0.5,
    spacing: float = 2.0,
    scale: tuple[float, ...] = (3, 1, 1, 1, 1, 2, 1, 1, 1, 1),
    num_marks: int = 21,
    layer: LayerSpec = "WG",
) -> gf.Component:
    """Ruler structure for lithographic measurement.

    Includes marks of varying scales to allow for easy reading by eye.

    based on phidl.geometry

    Args:
        height: Height of the ruling marks in um.
        width: Width of the ruling marks in um.
        spacing: Center-to-center spacing of the ruling marks in um.
        scale: Height scale pattern of marks.
        num_marks: Total number of marks to generate.
        layer: Specific layer to put the ruler geometry on.
    """
    pitch = spacing + width
    c = gf.Component()
    for n in range(num_marks):
        h = height * scale[n % len(scale)]
        ref = c << gf.components.rectangle(size=(width, h), layer=layer)
        ref.movex((n - num_marks / 2) * pitch + spacing / 2.0)

    return c

litho_ruler

litho_steps

litho_steps

litho_steps(
    line_widths: tuple[float, ...] = (
        1.0,
        2.0,
        4.0,
        8.0,
        16.0,
    ),
    line_spacing: float = 10.0,
    height: float = 100.0,
    layer: LayerSpec = "WG",
) -> Component

Positive + negative tone linewidth test.

used for lithography resolution test patterning based on phidl

Parameters:

Name Type Description Default
line_widths tuple[float, ...]

in um.

(1.0, 2.0, 4.0, 8.0, 16.0)
line_spacing float

in um.

10.0
height float

in um.

100.0
layer LayerSpec

Specific layer to put the ruler geometry on.

'WG'
Source code in gdsfactory/components/pcms/litho_steps.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@gf.cell_with_module_name(tags=["pcms"])
def litho_steps(
    line_widths: tuple[float, ...] = (1.0, 2.0, 4.0, 8.0, 16.0),
    line_spacing: float = 10.0,
    height: float = 100.0,
    layer: LayerSpec = "WG",
) -> Component:
    """Positive + negative tone linewidth test.

    used for lithography resolution test patterning
    based on phidl

    Args:
        line_widths: in um.
        line_spacing: in um.
        height: in um.
        layer: Specific layer to put the ruler geometry on.
    """
    D = gf.Component()

    height /= 2
    T1 = pc.text(
        text=f"{line_widths[-1]!s}", size=height, justify="center", layer=layer
    )

    ref = D.add_ref(T1)
    ref.rotate(90)
    ref.movex(-height / 10)

    R1 = pc.rectangle(size=(line_spacing, height), layer=layer)
    D.add_ref(R1).movey(-height)
    count = 0.0
    for i in reversed(line_widths):
        count += line_spacing + i
        R2 = pc.rectangle(size=(i, height), layer=layer)
        r = D.add_ref(R1)
        r.movex(count)
        r.movey(-height)
        r = D.add_ref(R2)
        r.movex(count - i)

    return D

litho_steps

pixel

pixel(size: int = 1, layer: LayerSpec = 'WG') -> Component
Source code in gdsfactory/components/pcms/version_stamp.py
15
16
17
18
19
20
@gf.cell_with_module_name(tags=["pcms"])
def pixel(size: int = 1, layer: LayerSpec = "WG") -> Component:
    c = gf.Component()
    a = size / 2
    c.add_polygon([(a, a), (a, -a), (-a, -a), (-a, a)], layer)
    return c

pixel

qrcode

qrcode(
    data: str = "mask01",
    psize: int = 1,
    layer: LayerSpec = "WG",
) -> Component

Returns QRCode.

Parameters:

Name Type Description Default
data str

string to encode.

'mask01'
psize int

pixel size.

1
layer LayerSpec

layer to use.

'WG'
Source code in gdsfactory/components/pcms/version_stamp.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@gf.cell_with_module_name(tags=["pcms"])
def qrcode(data: str = "mask01", psize: int = 1, layer: LayerSpec = "WG") -> Component:
    """Returns QRCode.

    Args:
        data: string to encode.
        psize: pixel size.
        layer: layer to use.
    """
    import qrcode

    pix = pixel(size=psize, layer=layer)
    q = qrcode.QRCode()
    q.add_data(data)
    matrix = q.get_matrix()
    c = gf.Component()
    for i, row in enumerate(matrix):
        for j, value in enumerate(row):
            if value:
                ref = c << pix
                ref.center = (i * psize, j * psize)
    c.flatten()
    return c

qrcode

resistance_meander

resistance_meander

resistance_meander(
    name: str = "net",
    pad_size: Size = (50.0, 50.0),
    num_squares: int = 1000,
    width: float = 1.0,
    res_layer: LayerSpec = "MTOP",
    pad_layer: LayerSpec = "MTOP",
) -> Component

Return meander to test resistance.

based on phidl.geometry

Parameters:

Name Type Description Default
name str

Name of the component.

'net'
pad_size Size

Size of the two matched impedance pads (microns).

(50.0, 50.0)
num_squares int

Number of squares comprising the resonator wire.

1000
width float

The width of the squares (microns).

1.0
res_layer LayerSpec

resistance layer.

'MTOP'
pad_layer LayerSpec

pad layer.

'MTOP'
Source code in gdsfactory/components/pcms/resistance_meander.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
@gf.cell_with_module_name(tags=["pcms"])
def resistance_meander(
    name: str = "net",
    pad_size: Size = (50.0, 50.0),
    num_squares: int = 1000,
    width: float = 1.0,
    res_layer: LayerSpec = "MTOP",
    pad_layer: LayerSpec = "MTOP",
) -> Component:
    """Return meander to test resistance.

    based on phidl.geometry

    Args:
        name: Name of the component.
        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: resistance layer.
        pad_layer: pad 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")
    if width > z:
        raise ValueError("Width of cell cannot be greater than height of pad")
    if num_squares <= 0:
        raise ValueError("Number of squares must be a positive real number")
    if 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 = gf.c.rectangle(size=(length_row, width), layer=res_layer)
    Col = gf.c.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(name=name)
    n = 1
    for i in range(num_rows):
        d = N.add_ref(T) if i != num_rows - 1 else N.add_ref(Row)
        if n % 2 == 0:
            d.dmirror_x(d.x)
        d.movey(-(n - 1) * T.ysize)
        n += 1
    ref = N.add_ref(Col)
    ref.movex(-width)

    end = N.add_ref(Col)
    end.movey(-(n - 2) * T.ysize)
    end.movex(length_row)

    # Creating pads
    P = Component()
    pad = gf.c.rectangle(size=(x, z), layer=pad_layer)
    pad1 = P.add_ref(pad)
    pad1.movex(-x - width)
    pad2 = P.add_ref(pad)
    pad2.movex(length_row + width)
    net = P.add_ref(N)
    net.ymin = pad1.ymin
    P.flatten()
    return P

resistance_meander

resistance_sheet

resistance_sheet

resistance_sheet(
    width: float = 10.0,
    layers: LayerSpecs = ("HEATER",),
    layer_offsets: Floats = (0, 0.2),
    pad: ComponentSpec = "via_stack_heater_mtop",
    pad_size: Size = (50.0, 50.0),
    pad_pitch: float = 100.0,
    ohms_per_square: float | None = None,
    pad_port_name: str = "e4",
) -> Component

Returns Sheet resistance.

keeps connectivity for pads and first layer in layers

Parameters:

Name Type Description Default
width float

in um.

10.0
layers LayerSpecs

for the middle part.

('HEATER',)
layer_offsets Floats

from edge, positive: over, negative: inclusion.

(0, 0.2)
pad ComponentSpec

function to create a pad.

'via_stack_heater_mtop'
pad_size Size

in um.

(50.0, 50.0)
pad_pitch float

in um.

100.0
ohms_per_square float | None

optional sheet resistance to compute info.resistance.

None
pad_port_name str

port name for the pad.

'e4'
Source code in gdsfactory/components/pcms/resistance_sheet.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@gf.cell_with_module_name(tags=["pcms"])
def resistance_sheet(
    width: float = 10.0,
    layers: LayerSpecs = ("HEATER",),
    layer_offsets: Floats = (0, 0.2),
    pad: ComponentSpec = "via_stack_heater_mtop",
    pad_size: Size = (50.0, 50.0),
    pad_pitch: float = 100.0,
    ohms_per_square: float | None = None,
    pad_port_name: str = "e4",
) -> Component:
    """Returns Sheet resistance.

    keeps connectivity for pads and first layer in layers

    Args:
        width: in um.
        layers: for the middle part.
        layer_offsets: from edge, positive: over, negative: inclusion.
        pad: function to create a pad.
        pad_size: in um.
        pad_pitch: in um.
        ohms_per_square: optional sheet resistance to compute info.resistance.
        pad_port_name: port name for the pad.
    """
    c = Component()

    pad = gf.get_component(pad, size=pad_size)
    length = pad_pitch - pad_size[0]

    pad1 = c << pad
    pad2 = c << pad
    r0 = c << gf.c.compass(
        size=(length + layer_offsets[0], width + layer_offsets[0]), layer=layers[0]
    )

    for layer, offset in zip(layers[1:], layer_offsets[1:], strict=False):
        _ = c << gf.c.compass(
            size=(length + 2 * offset, width + 2 * offset), layer=layer
        )

    pad1.connect(
        "e3", r0.ports["e1"], allow_width_mismatch=True, allow_layer_mismatch=True
    )
    pad2.connect(
        "e1", r0.ports["e3"], allow_width_mismatch=True, allow_layer_mismatch=True
    )

    c.info["resistance"] = ohms_per_square * width * length if ohms_per_square else 0
    c.info["length"] = length
    c.info["width"] = width
    p1 = c.add_port(
        name="pad1",
        port=pad1.ports[pad_port_name],
    )
    p2 = c.add_port(
        name="pad2",
        port=pad2.ports[pad_port_name],
    )
    if p1.port_type == "electrical":
        c.create_pin(ports=[p1], name="pad1")
    if p2.port_type == "electrical":
        c.create_pin(ports=[p2], name="pad2")
    return c

resistance_sheet

resolution_test_pattern

resolution_test_pattern

resolution_test_pattern(
    radius: float = 50.0,
    n_spokes: int = 36,
    width: float = 1.0,
    layer: LayerSpec = "WG",
) -> Component

Radial Siemens star resolution test pattern.

Creates a circle of alternating filled/empty pie-shaped wedges. The pattern is useful for evaluating lithographic resolution, as the feature size decreases toward the center.

Parameters:

Name Type Description Default
radius float

Outer radius of the star pattern in um.

50.0
n_spokes int

Total number of spokes (filled + empty). Must be even.

36
width float

Target outer edge width of each spoke in um (informational; actual angular width is 360/n_spokes degrees).

1.0
layer LayerSpec

Layer specification for the filled wedges.

'WG'
Source code in gdsfactory/components/pcms/resolution_test_pattern.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@gf.cell_with_module_name(tags=["pcms"])
def resolution_test_pattern(
    radius: float = 50.0,
    n_spokes: int = 36,
    width: float = 1.0,
    layer: LayerSpec = "WG",
) -> Component:
    """Radial Siemens star resolution test pattern.

    Creates a circle of alternating filled/empty pie-shaped wedges.
    The pattern is useful for evaluating lithographic resolution,
    as the feature size decreases toward the center.

    Args:
        radius: Outer radius of the star pattern in um.
        n_spokes: Total number of spokes (filled + empty). Must be even.
        width: Target outer edge width of each spoke in um (informational;
            actual angular width is 360/n_spokes degrees).
        layer: Layer specification for the filled wedges.
    """
    c = Component()

    angle_step = 2 * np.pi / n_spokes
    n_arc_pts = 64  # points along each wedge arc for smoothness

    for i in range(0, n_spokes, 2):
        theta_start = i * angle_step
        theta_end = theta_start + angle_step

        arc_angles = np.linspace(theta_start, theta_end, n_arc_pts)
        arc_x = radius * np.cos(arc_angles)
        arc_y = radius * np.sin(arc_angles)

        points = [(0.0, 0.0)]
        points.extend(zip(arc_x, arc_y, strict=False))
        points.append((0.0, 0.0))

        c.add_polygon(points, layer=layer)

    return c

resolution_test_pattern

ruler

ruler

ruler(
    height_long: float = 55,
    height_short: float = 5,
    height_numbered: float = 10,
    width: float = 2,
    spacing: float = 5.0,
    marks: tuple[float | None, ...] = (
        -100,
        None,
        -90,
        None,
        -80,
        None,
        -70,
        None,
        -60,
        None,
        -50,
        None,
        -40,
        None,
        -30,
        None,
        -20,
        None,
        -10,
        None,
        0,
    ),
    layer: LayerSpec = "WG",
    bbox_layers: tuple[LayerSpec, ...] | None = None,
    bbox_offset: float = 3.0,
    long_marks: tuple[float, ...] = (-50, 0),
    text_size: float = 3.5,
) -> gf.Component

Ruler structure for lithographic measurement.

Includes marks of varying scales to allow for easy reading by eye.

Parameters:

Name Type Description Default
height_long float

Height of the long ruling marks in um.

55
height_short float

Height of the short ruling marks in um.

5
height_numbered float

Height of the numbered ruling marks in um.

10
width float

Width of the ruling marks in um.

2
spacing float

Center-to-center spacing of the ruling marks in um.

5.0
marks tuple[float | None, ...]

Height scale pattern of marks.

(-100, None, -90, None, -80, None, -70, None, -60, None, -50, None, -40, None, -30, None, -20, None, -10, None, 0)
layer LayerSpec

Specific layer to put the ruler geometry on.

'WG'
bbox_layers tuple[LayerSpec, ...] | None

Layers to include in the bounding box.

None
bbox_offset float

Offsets for each bounding box layer.

3.0
cross_section

Cross-section spec for the ruler. Overrides layer if provided.

required
long_marks tuple[float, ...]

Marks that are long.

(-50, 0)
text_size float

Size of the text in um.

3.5
Source code in gdsfactory/components/pcms/ruler.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@gf.cell_with_module_name(tags=["pcms"])
def ruler(
    height_long: float = 55,
    height_short: float = 5,
    height_numbered: float = 10,
    width: float = 2,
    spacing: float = 5.0,
    marks: tuple[float | None, ...] = (
        -100,
        None,
        -90,
        None,
        -80,
        None,
        -70,
        None,
        -60,
        None,
        -50,
        None,
        -40,
        None,
        -30,
        None,
        -20,
        None,
        -10,
        None,
        0,
    ),
    layer: LayerSpec = "WG",
    bbox_layers: tuple[LayerSpec, ...] | None = None,
    bbox_offset: float = 3.0,
    long_marks: tuple[float, ...] = (-50, 0),
    text_size: float = 3.5,
) -> gf.Component:
    """Ruler structure for lithographic measurement.

    Includes marks of varying scales to allow for easy reading by eye.

    Args:
        height_long: Height of the long ruling marks in um.
        height_short: Height of the short ruling marks in um.
        height_numbered: Height of the numbered ruling marks in um.
        width: Width of the ruling marks in um.
        spacing: Center-to-center spacing of the ruling marks in um.
        marks: Height scale pattern of marks.
        layer: Specific layer to put the ruler geometry on.
        bbox_layers: Layers to include in the bounding box.
        bbox_offset: Offsets for each bounding box layer.
        cross_section: Cross-section spec for the ruler. Overrides layer if provided.
        long_marks: Marks that are long.
        text_size: Size of the text in um.
    """
    ymin = 0.0
    c = gf.Component()
    for i, mark in enumerate(marks):
        h = height_numbered if mark else height_short
        h = height_long if mark in long_marks else h

        if mark in long_marks:
            ymin = 0.0
        else:
            ymin += height_short

        ref = c << gf.components.rectangle(size=(width, h), layer=layer, port_type=None)
        ref.xmin = i * spacing
        ref.ymin = ymin

        if mark is not None:
            t = c << gf.c.text_rectangular(
                text=str(mark), size=text_size / 5, layer=layer
            )
            t.rotate(90)
            t.ymin = ref.ymin + 1
            t.xmax = ref.xmin - 1
    if bbox_layers:
        gf.add_padding(c, layers=bbox_layers, default=bbox_offset)
    return c

ruler

staircase

staircase(
    component: ComponentSpec | Component = "bend_euler",
    straight: ComponentSpec = "straight",
    length_v: float = 5.0,
    length_h: float = 5.0,
    rows: int = 4,
    **kwargs: Any
) -> Component

Returns staircase.

Parameters:

Name Type Description Default
component ComponentSpec | Component

bend spec.

'bend_euler'
straight ComponentSpec

straight spec.

'straight'
length_v float

vertical length.

5.0
length_h float

vertical length.

5.0
rows int

number of rows.

4
cols

number of cols.

required
kwargs Any

cross_section settings.

{}
Source code in gdsfactory/components/pcms/cutback_bend.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
@gf.cell_with_module_name(tags=["pcms"])
def staircase(
    component: ComponentSpec | Component = "bend_euler",
    straight: ComponentSpec = "straight",
    length_v: float = 5.0,
    length_h: float = 5.0,
    rows: int = 4,
    **kwargs: Any,
) -> Component:
    """Returns staircase.

    Args:
        component: bend spec.
        straight: straight spec.
        length_v: vertical length.
        length_h: vertical length.
        rows: number of rows.
        cols: number of cols.
        kwargs: cross_section settings.
    """
    bend90 = (
        component
        if isinstance(component, Component)
        else gf.get_component(component, **kwargs)
    )

    wgh = gf.get_component(straight, length=length_h, **kwargs)
    wgv = gf.get_component(straight, length=length_v, **kwargs)

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

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

    c = component_sequence(
        sequence=s,
        symbol_to_component=symbol_to_component,
        start_orientation=0,
    )
    c.info["components"] = 2 * rows
    return c

staircase

vernier_scale

vernier_scale

vernier_scale(
    n_divisions: int = 10,
    pitch_main: float = 10.0,
    pitch_vernier: float = 9.8,
    mark_width: float = 1.0,
    mark_height_main: float = 20.0,
    mark_height_vernier: float = 15.0,
    layer_main: LayerSpec = "WG",
    layer_vernier: LayerSpec = (2, 0),
) -> Component

Vernier scale for overlay measurement.

Creates two rows of rectangular marks: a main scale and a vernier scale. The slight pitch difference between the two scales allows sub-pitch overlay measurement. The zero marks of both scales are centered at the origin.

Parameters:

Name Type Description Default
n_divisions int

Number of marks on each side of the center mark (total marks per scale = 2 * n_divisions + 1).

10
pitch_main float

Center-to-center spacing of main scale marks in um.

10.0
pitch_vernier float

Center-to-center spacing of vernier scale marks in um.

9.8
mark_width float

Width of each rectangular mark in um.

1.0
mark_height_main float

Height of each main scale mark in um.

20.0
mark_height_vernier float

Height of each vernier scale mark in um.

15.0
layer_main LayerSpec

Layer specification for the main scale marks.

'WG'
layer_vernier LayerSpec

Layer specification for the vernier scale marks.

(2, 0)
Source code in gdsfactory/components/pcms/vernier_scale.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@gf.cell_with_module_name(tags=["pcms"])
def vernier_scale(
    n_divisions: int = 10,
    pitch_main: float = 10.0,
    pitch_vernier: float = 9.8,
    mark_width: float = 1.0,
    mark_height_main: float = 20.0,
    mark_height_vernier: float = 15.0,
    layer_main: LayerSpec = "WG",
    layer_vernier: LayerSpec = (2, 0),
) -> Component:
    """Vernier scale for overlay measurement.

    Creates two rows of rectangular marks: a main scale and a vernier scale.
    The slight pitch difference between the two scales allows sub-pitch
    overlay measurement. The zero marks of both scales are centered at the
    origin.

    Args:
        n_divisions: Number of marks on each side of the center mark
            (total marks per scale = 2 * n_divisions + 1).
        pitch_main: Center-to-center spacing of main scale marks in um.
        pitch_vernier: Center-to-center spacing of vernier scale marks in um.
        mark_width: Width of each rectangular mark in um.
        mark_height_main: Height of each main scale mark in um.
        mark_height_vernier: Height of each vernier scale mark in um.
        layer_main: Layer specification for the main scale marks.
        layer_vernier: Layer specification for the vernier scale marks.
    """
    c = Component()

    hw = mark_width / 2

    # Main scale marks (below y=0)
    for i in range(-n_divisions, n_divisions + 1):
        x = i * pitch_main
        c.add_polygon(
            [
                (x - hw, 0),
                (x + hw, 0),
                (x + hw, -mark_height_main),
                (x - hw, -mark_height_main),
            ],
            layer=layer_main,
        )

    # Vernier scale marks (above y=0)
    for i in range(-n_divisions, n_divisions + 1):
        x = i * pitch_vernier
        c.add_polygon(
            [
                (x - hw, 0),
                (x + hw, 0),
                (x + hw, mark_height_vernier),
                (x - hw, mark_height_vernier),
            ],
            layer=layer_vernier,
        )

    return c

vernier_scale

verniers

verniers

verniers(
    widths: Floats = (0.1, 0.2, 0.3, 0.4, 0.5),
    gap: float = 0.1,
    xsize: float = 100.0,
    layer_label: LayerSpec = "TEXT",
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip_no_ports",
    **kwargs: Any
) -> Component

Returns a component with verniers.

Parameters:

Name Type Description Default
widths Floats

list of widths.

(0.1, 0.2, 0.3, 0.4, 0.5)
gap float

gap between verniers.

0.1
xsize float

size of the component.

100.0
layer_label LayerSpec

layer for the labels.

'TEXT'
straight ComponentSpec

straight function.

'straight'
cross_section CrossSectionSpec

cross_section spec.

'strip_no_ports'
kwargs Any

straight settings.

{}
Source code in gdsfactory/components/pcms/verniers.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@gf.cell_with_module_name(tags=["pcms"])
def verniers(
    widths: Floats = (0.1, 0.2, 0.3, 0.4, 0.5),
    gap: float = 0.1,
    xsize: float = 100.0,
    layer_label: LayerSpec = "TEXT",
    straight: ComponentSpec = "straight",
    cross_section: CrossSectionSpec = "strip_no_ports",
    **kwargs: Any,
) -> Component:
    """Returns a component with verniers.

    Args:
        widths: list of widths.
        gap: gap between verniers.
        xsize: size of the component.
        layer_label: layer for the labels.
        straight: straight function.
        cross_section: cross_section spec.
        kwargs: straight settings.
    """
    c = gf.Component()
    y = 0.0

    for width in widths:
        w = c << gf.get_component(
            straight, width=width, length=xsize, cross_section=cross_section, **kwargs
        )
        y += width / 2
        w.y = y
        c.add_label(text=str(int(width * 1e3)), position=(0, y), layer=layer_label)
        y += width / 2 + gap

    return c

verniers

version_stamp

qrcode

qrcode(
    data: str = "mask01",
    psize: int = 1,
    layer: LayerSpec = "WG",
) -> Component

Returns QRCode.

Parameters:

Name Type Description Default
data str

string to encode.

'mask01'
psize int

pixel size.

1
layer LayerSpec

layer to use.

'WG'
Source code in gdsfactory/components/pcms/version_stamp.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@gf.cell_with_module_name(tags=["pcms"])
def qrcode(data: str = "mask01", psize: int = 1, layer: LayerSpec = "WG") -> Component:
    """Returns QRCode.

    Args:
        data: string to encode.
        psize: pixel size.
        layer: layer to use.
    """
    import qrcode

    pix = pixel(size=psize, layer=layer)
    q = qrcode.QRCode()
    q.add_data(data)
    matrix = q.get_matrix()
    c = gf.Component()
    for i, row in enumerate(matrix):
        for j, value in enumerate(row):
            if value:
                ref = c << pix
                ref.center = (i * psize, j * psize)
    c.flatten()
    return c

version_stamp

version_stamp(
    labels: tuple[str, ...] = ("demo_label",),
    with_qr_code: bool = False,
    layer: LayerSpec = "WG",
    pixel_size: int = 1,
    version: str | None = None,
    text_size: int = 10,
) -> Component

Component with module version and date.

Parameters:

Name Type Description Default
labels tuple[str, ...]

Iterable of labels.

('demo_label',)
with_qr_code bool

Whether to add a QR code with the date.

False
layer LayerSpec

Layer to use.

'WG'
pixel_size int

Pixel size.

1
version str | None

Version string.

None
text_size int

Text size.

10
Source code in gdsfactory/components/pcms/version_stamp.py
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
@gf.cell_with_module_name(tags=["pcms"])
def version_stamp(
    labels: tuple[str, ...] = ("demo_label",),
    with_qr_code: bool = False,
    layer: LayerSpec = "WG",
    pixel_size: int = 1,
    version: str | None = None,
    text_size: int = 10,
) -> Component:
    """Component with module version and date.

    Args:
        labels: Iterable of labels.
        with_qr_code: Whether to add a QR code with the date.
        layer: Layer to use.
        pixel_size: Pixel size.
        version: Version string.
        text_size: Text size.

    """
    now = datetime.datetime.now()
    timestamp = f"{now:%Y-%m-%d %H:%M:%S}"
    short_stamp = f"{now:%y.%m.%d.%H.%M.%S}"

    c = gf.Component()
    if with_qr_code:
        data = f"{timestamp}/{platform.node()}"
        q = c << qrcode(layer=layer, data=data, psize=pixel_size)
        q.center = (0, 0)
        x = q.xsize * 0.5 + 10

    else:
        x = 0

    _ = c << text(
        position=(x, text_size + 2 * pixel_size),
        text=short_stamp,
        layer=layer,
        justify="left",
        size=text_size,
    )

    if version:
        _ = c << text(
            position=(x, 0), text=version, layer=layer, justify="left", size=text_size
        )

    for i, line in enumerate(labels):
        _ = c << text(
            position=(x, -(i + 1) * (text_size + 2 * pixel_size)),
            text=line,
            layer=layer,
            justify="left",
            size=text_size,
        )

    c.flatten()
    return c

version_stamp

quantum

coupler_capacitive

coupler_capacitive

coupler_capacitive(
    pad_width: float = 20.0,
    pad_height: float = 50.0,
    gap: float = 2.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component

Creates a capacitive coupler for quantum circuits.

A capacitive coupler consists of two metal pads separated by a small gap, providing capacitive coupling between circuit elements like qubits and resonators.

            ______               ______
  _______  |      |             |      | _______
 |       | |      |             |      ||       |
 | feed1 | | pad1 | ====gap==== | pad2 || feed2 |
 |       | |      |             |      ||       |
 |_______| |      |             |      ||_______|
           |______|             |______|

Parameters:

Name Type Description Default
pad_width float

Width of each coupling pad in μm.

20.0
pad_height float

Height of each coupling pad in μm.

50.0
gap float

Gap between the coupling pads in μm.

2.0
feed_width float

Width of the feed lines in μm.

10.0
feed_length float

Length of the feed lines in μm.

30.0
layer_metal LayerSpec

Layer for the metal structures.

(1, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the capacitive coupler geometry.

Source code in gdsfactory/components/quantum/coupler_capacitive.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
@gf.cell_with_module_name(tags=["quantum"])
def coupler_capacitive(
    pad_width: float = 20.0,
    pad_height: float = 50.0,
    gap: float = 2.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a capacitive coupler for quantum circuits.

    A capacitive coupler consists of two metal pads separated by a small gap,
    providing capacitive coupling between circuit elements like qubits and resonators.

                    ______               ______
          _______  |      |             |      | _______
         |       | |      |             |      ||       |
         | feed1 | | pad1 | ====gap==== | pad2 || feed2 |
         |       | |      |             |      ||       |
         |_______| |      |             |      ||_______|
                   |______|             |______|

    Args:
        pad_width: Width of each coupling pad in μm.
        pad_height: Height of each coupling pad in μm.
        gap: Gap between the coupling pads in μm.
        feed_width: Width of the feed lines in μm.
        feed_length: Length of the feed lines in μm.
        layer_metal: Layer for the metal structures.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the capacitive coupler geometry.
    """
    c = Component()

    # Create left coupling pad
    left_pad = gf.components.rectangle(
        size=(pad_width, pad_height),
        layer=layer_metal,
    )
    left_pad_ref = c.add_ref(left_pad)
    left_pad_ref.move((-pad_width - gap / 2, -pad_height / 2))

    # Create right coupling pad
    right_pad = gf.components.rectangle(
        size=(pad_width, pad_height),
        layer=layer_metal,
    )
    right_pad_ref = c.add_ref(right_pad)
    right_pad_ref.move((gap / 2, -pad_height / 2))

    # Create left feed line
    left_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    left_feed_ref = c.add_ref(left_feed)
    left_feed_ref.move((-pad_width - gap / 2 - feed_length, -feed_width / 2))

    # Create right feed line
    right_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    right_feed_ref = c.add_ref(right_feed)
    right_feed_ref.move((gap / 2 + pad_width, -feed_width / 2))

    # Add ports
    c.add_port(
        name="left",
        center=(-pad_width - gap / 2 - feed_length, 0),
        width=feed_width,
        orientation=180,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="right",
        center=(gap / 2 + pad_width + feed_length, 0),
        width=feed_width,
        orientation=0,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add metadata
    c.info["coupler_type"] = "capacitive"
    c.info["pad_width"] = pad_width
    c.info["pad_height"] = pad_height
    c.info["gap"] = gap
    c.info["coupling_area"] = pad_width * pad_height

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

coupler_interdigital

coupler_interdigital(
    fingers: int = 6,
    finger_length: float = 30.0,
    finger_width: float = 2.0,
    finger_gap_vertical: float = 2.0,
    finger_gap_horizontal: float = 3.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component

Creates an interdigital capacitive coupler.

Each side includes a base column (a vertical metal block) to which the fingers are attached.

  • The width of the base column is equal to the height of the fingers.
  • The finger_length parameter refers only to the length of the fingers extending from the base, and does NOT include the base column width

Parameters:

Name Type Description Default
fingers int

Number of fingers per side.

6
finger_length float

Length of each finger in μm (see note above).

30.0
finger_width float

Width of each finger in μm.

2.0
finger_gap_vertical float

Vertical gap between fingers in μm (g1).

2.0
finger_gap_horizontal float

Horizontal gap between fingers in μm (g2).

3.0
feed_width float

Width of the feed lines in μm.

10.0
feed_length float

Length of the feed lines in μm.

30.0
layer_metal LayerSpec

Layer for the metal structures.

(1, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the interdigital coupler geometry.

    ┌────────┐
   base columns
   ↓                    ↓

┌────────┐ ┌────────┐ │ │█████████████ █│ │ │ │█ g1 █│ │ │ │█ <─g2─> █████████████│ │ │ │█ █│ │ │ feed1 │█████████████ █│ feed2 │ │ │█ █│ │ │ │█ █████████████│ │ │ │█ █│ │ │ │█████████████ █│ │ └────────┘█ █└────────┘

Source code in gdsfactory/components/quantum/coupler_capacitive.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
@gf.cell_with_module_name(tags=["quantum"])
def coupler_interdigital(
    fingers: int = 6,
    finger_length: float = 30.0,
    finger_width: float = 2.0,
    finger_gap_vertical: float = 2.0,
    finger_gap_horizontal: float = 3.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates an interdigital capacitive coupler.

    Each side includes a base column (a vertical metal block) to which the fingers are attached.

    - The width of the base column is equal to the height of the fingers.
    - The finger_length parameter refers only to the length of the fingers *extending from the base*,
      and does NOT include the base column width

    Args:
        fingers: Number of fingers per side.
        finger_length: Length of each finger in μm (see note above).
        finger_width: Width of each finger in μm.
        finger_gap_vertical: Vertical gap between fingers in μm (g1).
        finger_gap_horizontal: Horizontal gap between fingers in μm (g2).
        feed_width: Width of the feed lines in μm.
        feed_length: Length of the feed lines in μm.
        layer_metal: Layer for the metal structures.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the interdigital coupler geometry.

                    ┌────────┐
                   base columns
                   ↓                    ↓
         ┌────────┐                      ┌────────┐
         │        │█████████████        █│        │
         │        │█        g1          █│        │
         │        │█ <─g2─> █████████████│        │
         │        │█                    █│        │
         │ feed1  │█████████████        █│ feed2  │
         │        │█                    █│        │
         │        │█        █████████████│        │
         │        │█                    █│        │
         │        │█████████████        █│        │
         └────────┘█                    █└────────┘

    """
    c = Component()

    # Calculate total dimensions
    total_width = finger_length + finger_gap_horizontal
    total_height = fingers * finger_width + (fingers - 1) * finger_gap_vertical

    # Create left side base column
    left_base = gf.components.rectangle(
        size=(finger_width, total_height),
        layer=layer_metal,
    )
    left_base_ref = c.add_ref(left_base)
    left_base_ref.move((-total_width / 2 - finger_width, -total_height / 2))

    # Create right side base column
    right_base = gf.components.rectangle(
        size=(finger_width, total_height),
        layer=layer_metal,
    )
    right_base_ref = c.add_ref(right_base)
    right_base_ref.move((total_width / 2, -total_height / 2))

    # Create interdigital fingers
    for i in range(fingers):
        left_finger = gf.components.rectangle(
            size=(finger_length, finger_width),
            layer=layer_metal,
        )
        left_finger_ref = c.add_ref(left_finger)

        # We start from a left finger
        x_pos = -finger_length / 2 + (-1) ** (i + 1) * finger_gap_horizontal / 2
        y_pos = (
            total_height / 2 - finger_width - i * (finger_width + finger_gap_vertical)
        )
        left_finger_ref.move((x_pos, y_pos))

    # Create feed lines
    left_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    left_feed_ref = c.add_ref(left_feed)
    left_feed_ref.move((-total_width / 2 - finger_width - feed_length, -feed_width / 2))

    right_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    right_feed_ref = c.add_ref(right_feed)
    right_feed_ref.move((total_width / 2 + finger_width, -feed_width / 2))

    # Add ports
    c.add_port(
        name="left",
        center=(-total_width / 2 - finger_width - feed_length, 0),
        width=feed_width,
        orientation=180,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="right",
        center=(total_width / 2 + finger_width + feed_length, 0),
        width=feed_width,
        orientation=0,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add metadata
    c.info["coupler_type"] = "interdigital"
    c.info["fingers"] = fingers
    c.info["finger_length"] = finger_length
    c.info["finger_width"] = finger_width
    c.info["finger_gap_horizontal"] = finger_gap_horizontal
    c.info["finger_gap_vertical"] = finger_gap_vertical

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

coupler_tunable

coupler_tunable(
    pad_width: float = 30.0,
    pad_height: float = 40.0,
    gap: float = 3.0,
    tuning_pad_width: float = 15.0,
    tuning_pad_height: float = 20.0,
    tuning_gap: float = 1.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    layer_tuning: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component

Creates a tunable capacitive coupler with voltage control.

A tunable coupler includes additional electrodes that can be voltage-biased to change the coupling strength dynamically.

Parameters:

Name Type Description Default
pad_width float

Width of main coupling pads in μm.

30.0
pad_height float

Height of main coupling pads in μm.

40.0
gap float

Gap between main coupling pads in μm.

3.0
tuning_pad_width float

Width of tuning pads in μm.

15.0
tuning_pad_height float

Height of tuning pads in μm.

20.0
tuning_gap float

Gap to tuning pads in μm.

1.0
feed_width float

Width of feed lines in μm.

10.0
feed_length float

Length of feed lines in μm.

30.0
layer_metal LayerSpec

Layer for main metal structures.

(1, 0)
layer_tuning LayerSpec

Layer for tuning electrodes.

(3, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the tunable coupler geometry.

    (connected to feed)
         _______
        |       |
        | tpad1 |
        |       |
        |_______|
        tuning gap
   ______        ______

_ | | | | _

Component

| | | | | || |

Component

| feed1 | | pad1 | gap | pad2 || feed2 |

Component

| | | | | || |

Component

|_| | | | ||_| |__| |_| tuning gap _ | | | tpad2 | | | |____| (connected to feed)

Source code in gdsfactory/components/quantum/coupler_capacitive.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
@gf.cell_with_module_name(tags=["quantum"])
def coupler_tunable(
    pad_width: float = 30.0,
    pad_height: float = 40.0,
    gap: float = 3.0,
    tuning_pad_width: float = 15.0,
    tuning_pad_height: float = 20.0,
    tuning_gap: float = 1.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    layer_tuning: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a tunable capacitive coupler with voltage control.

    A tunable coupler includes additional electrodes that can be voltage-biased
    to change the coupling strength dynamically.


    Args:
        pad_width: Width of main coupling pads in μm.
        pad_height: Height of main coupling pads in μm.
        gap: Gap between main coupling pads in μm.
        tuning_pad_width: Width of tuning pads in μm.
        tuning_pad_height: Height of tuning pads in μm.
        tuning_gap: Gap to tuning pads in μm.
        feed_width: Width of feed lines in μm.
        feed_length: Length of feed lines in μm.
        layer_metal: Layer for main metal structures.
        layer_tuning: Layer for tuning electrodes.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the tunable coupler geometry.

                    (connected to feed)
                         _______
                        |       |
                        | tpad1 |
                        |       |
                        |_______|
                        tuning gap
                   ______        ______
         _______  |      |      |      | _______
        |       | |      |      |      ||       |
        | feed1 | | pad1 | gap  | pad2 || feed2 |
        |       | |      |      |      ||       |
        |_______| |      |      |      ||_______|
                  |______|      |______|
                        tuning gap
                         _______
                        |       |
                        | tpad2 |
                        |       |
                        |_______|
                    (connected to feed)
    """
    c = Component()

    # Create main coupling pads
    left_pad = gf.components.rectangle(
        size=(pad_width, pad_height),
        layer=layer_metal,
    )
    left_pad_ref = c.add_ref(left_pad)
    left_pad_ref.move((-pad_width - gap / 2, -pad_height / 2))

    right_pad = gf.components.rectangle(
        size=(pad_width, pad_height),
        layer=layer_metal,
    )
    right_pad_ref = c.add_ref(right_pad)
    right_pad_ref.move((gap / 2, -pad_height / 2))

    # Create tuning pads above and below
    top_tuning_pad = gf.components.rectangle(
        size=(tuning_pad_width, tuning_pad_height),
        layer=layer_tuning,
    )
    top_tuning_ref = c.add_ref(top_tuning_pad)
    top_tuning_ref.move((-tuning_pad_width / 2, pad_height / 2 + tuning_gap))

    bottom_tuning_pad = gf.components.rectangle(
        size=(tuning_pad_width, tuning_pad_height),
        layer=layer_tuning,
    )
    bottom_tuning_ref = c.add_ref(bottom_tuning_pad)
    bottom_tuning_ref.move(
        (-tuning_pad_width / 2, -pad_height / 2 - tuning_gap - tuning_pad_height)
    )

    # Create feed lines for main pads
    left_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    left_feed_ref = c.add_ref(left_feed)
    left_feed_ref.move((-pad_width - gap / 2 - feed_length, -feed_width / 2))

    right_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    right_feed_ref = c.add_ref(right_feed)
    right_feed_ref.move((gap / 2 + pad_width, -feed_width / 2))

    # Create tuning feed lines
    top_tuning_feed = gf.components.rectangle(
        size=(feed_width, feed_length),
        layer=layer_tuning,
    )
    top_tuning_feed_ref = c.add_ref(top_tuning_feed)
    top_tuning_feed_ref.move(
        (-feed_width / 2, pad_height / 2 + tuning_gap + tuning_pad_height)
    )

    bottom_tuning_feed = gf.components.rectangle(
        size=(feed_width, feed_length),
        layer=layer_tuning,
    )
    bottom_tuning_feed_ref = c.add_ref(bottom_tuning_feed)
    bottom_tuning_feed_ref.move(
        (
            -feed_width / 2,
            -pad_height / 2 - tuning_gap - tuning_pad_height - feed_length,
        )
    )

    # Add ports
    c.add_port(
        name="left",
        center=(-pad_width - gap / 2 - feed_length, 0),
        width=feed_width,
        orientation=180,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="right",
        center=(gap / 2 + pad_width + feed_length, 0),
        width=feed_width,
        orientation=0,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="tuning_top",
        center=(0, pad_height / 2 + tuning_gap + tuning_pad_height + feed_length),
        width=feed_width,
        orientation=90,
        layer=layer_tuning,
        port_type=port_type,
    )

    c.add_port(
        name="tuning_bottom",
        center=(0, -pad_height / 2 - tuning_gap - tuning_pad_height - feed_length),
        width=feed_width,
        orientation=270,
        layer=layer_tuning,
        port_type=port_type,
    )

    # Add metadata
    c.info["coupler_type"] = "tunable"
    c.info["pad_width"] = pad_width
    c.info["pad_height"] = pad_height
    c.info["gap"] = gap
    c.info["tuning_pad_width"] = tuning_pad_width
    c.info["tuning_pad_height"] = tuning_pad_height
    c.info["tuning_gap"] = tuning_gap

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

coupler_capacitive

coupler_interdigital

coupler_interdigital(
    fingers: int = 6,
    finger_length: float = 30.0,
    finger_width: float = 2.0,
    finger_gap_vertical: float = 2.0,
    finger_gap_horizontal: float = 3.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component

Creates an interdigital capacitive coupler.

Each side includes a base column (a vertical metal block) to which the fingers are attached.

  • The width of the base column is equal to the height of the fingers.
  • The finger_length parameter refers only to the length of the fingers extending from the base, and does NOT include the base column width

Parameters:

Name Type Description Default
fingers int

Number of fingers per side.

6
finger_length float

Length of each finger in μm (see note above).

30.0
finger_width float

Width of each finger in μm.

2.0
finger_gap_vertical float

Vertical gap between fingers in μm (g1).

2.0
finger_gap_horizontal float

Horizontal gap between fingers in μm (g2).

3.0
feed_width float

Width of the feed lines in μm.

10.0
feed_length float

Length of the feed lines in μm.

30.0
layer_metal LayerSpec

Layer for the metal structures.

(1, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the interdigital coupler geometry.

    ┌────────┐
   base columns
   ↓                    ↓

┌────────┐ ┌────────┐ │ │█████████████ █│ │ │ │█ g1 █│ │ │ │█ <─g2─> █████████████│ │ │ │█ █│ │ │ feed1 │█████████████ █│ feed2 │ │ │█ █│ │ │ │█ █████████████│ │ │ │█ █│ │ │ │█████████████ █│ │ └────────┘█ █└────────┘

Source code in gdsfactory/components/quantum/coupler_capacitive.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
@gf.cell_with_module_name(tags=["quantum"])
def coupler_interdigital(
    fingers: int = 6,
    finger_length: float = 30.0,
    finger_width: float = 2.0,
    finger_gap_vertical: float = 2.0,
    finger_gap_horizontal: float = 3.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates an interdigital capacitive coupler.

    Each side includes a base column (a vertical metal block) to which the fingers are attached.

    - The width of the base column is equal to the height of the fingers.
    - The finger_length parameter refers only to the length of the fingers *extending from the base*,
      and does NOT include the base column width

    Args:
        fingers: Number of fingers per side.
        finger_length: Length of each finger in μm (see note above).
        finger_width: Width of each finger in μm.
        finger_gap_vertical: Vertical gap between fingers in μm (g1).
        finger_gap_horizontal: Horizontal gap between fingers in μm (g2).
        feed_width: Width of the feed lines in μm.
        feed_length: Length of the feed lines in μm.
        layer_metal: Layer for the metal structures.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the interdigital coupler geometry.

                    ┌────────┐
                   base columns
                   ↓                    ↓
         ┌────────┐                      ┌────────┐
         │        │█████████████        █│        │
         │        │█        g1          █│        │
         │        │█ <─g2─> █████████████│        │
         │        │█                    █│        │
         │ feed1  │█████████████        █│ feed2  │
         │        │█                    █│        │
         │        │█        █████████████│        │
         │        │█                    █│        │
         │        │█████████████        █│        │
         └────────┘█                    █└────────┘

    """
    c = Component()

    # Calculate total dimensions
    total_width = finger_length + finger_gap_horizontal
    total_height = fingers * finger_width + (fingers - 1) * finger_gap_vertical

    # Create left side base column
    left_base = gf.components.rectangle(
        size=(finger_width, total_height),
        layer=layer_metal,
    )
    left_base_ref = c.add_ref(left_base)
    left_base_ref.move((-total_width / 2 - finger_width, -total_height / 2))

    # Create right side base column
    right_base = gf.components.rectangle(
        size=(finger_width, total_height),
        layer=layer_metal,
    )
    right_base_ref = c.add_ref(right_base)
    right_base_ref.move((total_width / 2, -total_height / 2))

    # Create interdigital fingers
    for i in range(fingers):
        left_finger = gf.components.rectangle(
            size=(finger_length, finger_width),
            layer=layer_metal,
        )
        left_finger_ref = c.add_ref(left_finger)

        # We start from a left finger
        x_pos = -finger_length / 2 + (-1) ** (i + 1) * finger_gap_horizontal / 2
        y_pos = (
            total_height / 2 - finger_width - i * (finger_width + finger_gap_vertical)
        )
        left_finger_ref.move((x_pos, y_pos))

    # Create feed lines
    left_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    left_feed_ref = c.add_ref(left_feed)
    left_feed_ref.move((-total_width / 2 - finger_width - feed_length, -feed_width / 2))

    right_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    right_feed_ref = c.add_ref(right_feed)
    right_feed_ref.move((total_width / 2 + finger_width, -feed_width / 2))

    # Add ports
    c.add_port(
        name="left",
        center=(-total_width / 2 - finger_width - feed_length, 0),
        width=feed_width,
        orientation=180,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="right",
        center=(total_width / 2 + finger_width + feed_length, 0),
        width=feed_width,
        orientation=0,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add metadata
    c.info["coupler_type"] = "interdigital"
    c.info["fingers"] = fingers
    c.info["finger_length"] = finger_length
    c.info["finger_width"] = finger_width
    c.info["finger_gap_horizontal"] = finger_gap_horizontal
    c.info["finger_gap_vertical"] = finger_gap_vertical

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

coupler_interdigital

coupler_tunable

coupler_tunable(
    pad_width: float = 30.0,
    pad_height: float = 40.0,
    gap: float = 3.0,
    tuning_pad_width: float = 15.0,
    tuning_pad_height: float = 20.0,
    tuning_gap: float = 1.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    layer_tuning: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component

Creates a tunable capacitive coupler with voltage control.

A tunable coupler includes additional electrodes that can be voltage-biased to change the coupling strength dynamically.

Parameters:

Name Type Description Default
pad_width float

Width of main coupling pads in μm.

30.0
pad_height float

Height of main coupling pads in μm.

40.0
gap float

Gap between main coupling pads in μm.

3.0
tuning_pad_width float

Width of tuning pads in μm.

15.0
tuning_pad_height float

Height of tuning pads in μm.

20.0
tuning_gap float

Gap to tuning pads in μm.

1.0
feed_width float

Width of feed lines in μm.

10.0
feed_length float

Length of feed lines in μm.

30.0
layer_metal LayerSpec

Layer for main metal structures.

(1, 0)
layer_tuning LayerSpec

Layer for tuning electrodes.

(3, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the tunable coupler geometry.

    (connected to feed)
         _______
        |       |
        | tpad1 |
        |       |
        |_______|
        tuning gap
   ______        ______

_ | | | | _

Component

| | | | | || |

Component

| feed1 | | pad1 | gap | pad2 || feed2 |

Component

| | | | | || |

Component

|_| | | | ||_| |__| |_| tuning gap _ | | | tpad2 | | | |____| (connected to feed)

Source code in gdsfactory/components/quantum/coupler_capacitive.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
@gf.cell_with_module_name(tags=["quantum"])
def coupler_tunable(
    pad_width: float = 30.0,
    pad_height: float = 40.0,
    gap: float = 3.0,
    tuning_pad_width: float = 15.0,
    tuning_pad_height: float = 20.0,
    tuning_gap: float = 1.0,
    feed_width: float = 10.0,
    feed_length: float = 30.0,
    layer_metal: LayerSpec = (1, 0),
    layer_tuning: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a tunable capacitive coupler with voltage control.

    A tunable coupler includes additional electrodes that can be voltage-biased
    to change the coupling strength dynamically.


    Args:
        pad_width: Width of main coupling pads in μm.
        pad_height: Height of main coupling pads in μm.
        gap: Gap between main coupling pads in μm.
        tuning_pad_width: Width of tuning pads in μm.
        tuning_pad_height: Height of tuning pads in μm.
        tuning_gap: Gap to tuning pads in μm.
        feed_width: Width of feed lines in μm.
        feed_length: Length of feed lines in μm.
        layer_metal: Layer for main metal structures.
        layer_tuning: Layer for tuning electrodes.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the tunable coupler geometry.

                    (connected to feed)
                         _______
                        |       |
                        | tpad1 |
                        |       |
                        |_______|
                        tuning gap
                   ______        ______
         _______  |      |      |      | _______
        |       | |      |      |      ||       |
        | feed1 | | pad1 | gap  | pad2 || feed2 |
        |       | |      |      |      ||       |
        |_______| |      |      |      ||_______|
                  |______|      |______|
                        tuning gap
                         _______
                        |       |
                        | tpad2 |
                        |       |
                        |_______|
                    (connected to feed)
    """
    c = Component()

    # Create main coupling pads
    left_pad = gf.components.rectangle(
        size=(pad_width, pad_height),
        layer=layer_metal,
    )
    left_pad_ref = c.add_ref(left_pad)
    left_pad_ref.move((-pad_width - gap / 2, -pad_height / 2))

    right_pad = gf.components.rectangle(
        size=(pad_width, pad_height),
        layer=layer_metal,
    )
    right_pad_ref = c.add_ref(right_pad)
    right_pad_ref.move((gap / 2, -pad_height / 2))

    # Create tuning pads above and below
    top_tuning_pad = gf.components.rectangle(
        size=(tuning_pad_width, tuning_pad_height),
        layer=layer_tuning,
    )
    top_tuning_ref = c.add_ref(top_tuning_pad)
    top_tuning_ref.move((-tuning_pad_width / 2, pad_height / 2 + tuning_gap))

    bottom_tuning_pad = gf.components.rectangle(
        size=(tuning_pad_width, tuning_pad_height),
        layer=layer_tuning,
    )
    bottom_tuning_ref = c.add_ref(bottom_tuning_pad)
    bottom_tuning_ref.move(
        (-tuning_pad_width / 2, -pad_height / 2 - tuning_gap - tuning_pad_height)
    )

    # Create feed lines for main pads
    left_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    left_feed_ref = c.add_ref(left_feed)
    left_feed_ref.move((-pad_width - gap / 2 - feed_length, -feed_width / 2))

    right_feed = gf.components.rectangle(
        size=(feed_length, feed_width),
        layer=layer_metal,
    )
    right_feed_ref = c.add_ref(right_feed)
    right_feed_ref.move((gap / 2 + pad_width, -feed_width / 2))

    # Create tuning feed lines
    top_tuning_feed = gf.components.rectangle(
        size=(feed_width, feed_length),
        layer=layer_tuning,
    )
    top_tuning_feed_ref = c.add_ref(top_tuning_feed)
    top_tuning_feed_ref.move(
        (-feed_width / 2, pad_height / 2 + tuning_gap + tuning_pad_height)
    )

    bottom_tuning_feed = gf.components.rectangle(
        size=(feed_width, feed_length),
        layer=layer_tuning,
    )
    bottom_tuning_feed_ref = c.add_ref(bottom_tuning_feed)
    bottom_tuning_feed_ref.move(
        (
            -feed_width / 2,
            -pad_height / 2 - tuning_gap - tuning_pad_height - feed_length,
        )
    )

    # Add ports
    c.add_port(
        name="left",
        center=(-pad_width - gap / 2 - feed_length, 0),
        width=feed_width,
        orientation=180,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="right",
        center=(gap / 2 + pad_width + feed_length, 0),
        width=feed_width,
        orientation=0,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="tuning_top",
        center=(0, pad_height / 2 + tuning_gap + tuning_pad_height + feed_length),
        width=feed_width,
        orientation=90,
        layer=layer_tuning,
        port_type=port_type,
    )

    c.add_port(
        name="tuning_bottom",
        center=(0, -pad_height / 2 - tuning_gap - tuning_pad_height - feed_length),
        width=feed_width,
        orientation=270,
        layer=layer_tuning,
        port_type=port_type,
    )

    # Add metadata
    c.info["coupler_type"] = "tunable"
    c.info["pad_width"] = pad_width
    c.info["pad_height"] = pad_height
    c.info["gap"] = gap
    c.info["tuning_pad_width"] = tuning_pad_width
    c.info["tuning_pad_height"] = tuning_pad_height
    c.info["tuning_gap"] = tuning_gap

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

coupler_tunable

flux_qubit

flux_qubit

flux_qubit(
    loop_width: float = 50.0,
    loop_height: float = 50.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    alpha_junction_width: float = 0.12,
    alpha_junction_height: float = 0.25,
    wire_width: float = 2.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_alpha_junction: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component

Creates a flux qubit (persistent current qubit).

A flux qubit consists of a superconducting loop interrupted by three Josephson junctions. Two junctions are identical (beta junctions) while the third is smaller (alpha junction) with roughly 0.5-0.8 times the critical current.

Parameters:

Name Type Description Default
loop_width float

Width of the superconducting loop in μm.

50.0
loop_height float

Height of the superconducting loop in μm.

50.0
junction_width float

Width of the beta Josephson junctions in μm.

0.15
junction_height float

Height of the beta Josephson junctions in μm.

0.3
alpha_junction_width float

Width of the alpha Josephson junction in μm.

0.12
alpha_junction_height float

Height of the alpha Josephson junction in μm.

0.25
wire_width float

Width of the superconducting wires in μm.

2.0
layer_metal LayerSpec

Layer for the metal wires.

(1, 0)
layer_junction LayerSpec

Layer for the beta Josephson junctions.

(2, 0)
layer_alpha_junction LayerSpec

Layer for the alpha Josephson junction.

(3, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the flux qubit geometry.

Source code in gdsfactory/components/quantum/flux_qubit.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
@gf.cell_with_module_name(tags=["quantum"])
def flux_qubit(
    loop_width: float = 50.0,
    loop_height: float = 50.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    alpha_junction_width: float = 0.12,
    alpha_junction_height: float = 0.25,
    wire_width: float = 2.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_alpha_junction: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a flux qubit (persistent current qubit).

    A flux qubit consists of a superconducting loop interrupted by three Josephson junctions.
    Two junctions are identical (beta junctions) while the third is smaller (alpha junction)
    with roughly 0.5-0.8 times the critical current.

    Args:
        loop_width: Width of the superconducting loop in μm.
        loop_height: Height of the superconducting loop in μm.
        junction_width: Width of the beta Josephson junctions in μm.
        junction_height: Height of the beta Josephson junctions in μm.
        alpha_junction_width: Width of the alpha Josephson junction in μm.
        alpha_junction_height: Height of the alpha Josephson junction in μm.
        wire_width: Width of the superconducting wires in μm.
        layer_metal: Layer for the metal wires.
        layer_junction: Layer for the beta Josephson junctions.
        layer_alpha_junction: Layer for the alpha Josephson junction.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the flux qubit geometry.
    """
    c = Component()

    # Create the superconducting loop as a hollow rectangle
    outer_rect = gf.components.rectangle(
        size=(loop_width, loop_height),
        layer=layer_metal,
    )

    inner_rect = gf.components.rectangle(
        size=(loop_width - 2 * wire_width, loop_height - 2 * wire_width),
        layer=layer_metal,
    )

    # Create the loop by boolean difference
    loop = gf.boolean(
        outer_rect,
        inner_rect,
        operation="not",
        layer=layer_metal,
    )

    # Move loop to be centered at origin (use a temporary component)
    loop_centered = Component()
    loop_ref = loop_centered.add_ref(loop)
    loop_ref.move((-loop_width / 2, -loop_height / 2))
    loop_centered.flatten()

    # Create gaps for junctions (standalone components, not added to c)
    # Bottom gap for alpha junction
    alpha_gap = Component()
    alpha_gap_rect = alpha_gap.add_ref(
        gf.components.rectangle(
            size=(alpha_junction_width + 0.1, wire_width + 0.1),
            layer=layer_metal,
        )
    )
    alpha_gap_rect.move((-alpha_junction_width / 2 - 0.05, -loop_height / 2 - 0.05))
    alpha_gap.flatten()

    # Left gap for beta junction
    beta_gap_left = Component()
    beta_gap_left_rect = beta_gap_left.add_ref(
        gf.components.rectangle(
            size=(wire_width + 0.1, junction_height + 0.1),
            layer=layer_metal,
        )
    )
    beta_gap_left_rect.move((-loop_width / 2 - 0.05, -junction_height / 2 - 0.05))
    beta_gap_left.flatten()

    # Right gap for beta junction
    beta_gap_right = Component()
    beta_gap_right_rect = beta_gap_right.add_ref(
        gf.components.rectangle(
            size=(wire_width + 0.1, junction_height + 0.1),
            layer=layer_metal,
        )
    )
    beta_gap_right_rect.move(
        (loop_width / 2 - wire_width - 0.05, -junction_height / 2 - 0.05)
    )
    beta_gap_right.flatten()

    # Remove gaps from the loop sequentially
    loop_with_gaps = gf.boolean(
        loop_centered,
        alpha_gap,
        operation="not",
        layer=layer_metal,
    )
    loop_with_gaps = gf.boolean(
        loop_with_gaps,
        beta_gap_left,
        operation="not",
        layer=layer_metal,
    )
    loop_with_gaps = gf.boolean(
        loop_with_gaps,
        beta_gap_right,
        operation="not",
        layer=layer_metal,
    )
    c.add_ref(loop_with_gaps)

    # Create the alpha junction (smaller)
    alpha_junction = gf.components.rectangle(
        size=(alpha_junction_width, alpha_junction_height),
        layer=layer_alpha_junction,
    )
    alpha_junction_ref = c.add_ref(alpha_junction)
    alpha_junction_ref.move(
        (
            -alpha_junction_width / 2,
            -loop_height / 2 + wire_width / 2 - alpha_junction_height / 2,
        )
    )

    # Create the beta junctions (larger, identical)
    beta_junction_left = gf.components.rectangle(
        size=(junction_width, junction_height),
        layer=layer_junction,
    )
    beta_junction_left_ref = c.add_ref(beta_junction_left)
    beta_junction_left_ref.move(
        (-loop_width / 2 + wire_width / 2 - junction_width / 2, -junction_height / 2)
    )

    beta_junction_right = gf.components.rectangle(
        size=(junction_width, junction_height),
        layer=layer_junction,
    )
    beta_junction_right_ref = c.add_ref(beta_junction_right)
    beta_junction_right_ref.move(
        (loop_width / 2 - wire_width / 2 - junction_width / 2, -junction_height / 2)
    )

    # Add control lines for flux bias
    control_line_left = gf.components.rectangle(
        size=(20.0, 2.0),
        layer=layer_metal,
    )
    control_line_left_ref = c.add_ref(control_line_left)
    control_line_left_ref.move((-loop_width / 2 - 30.0, -1.0))

    control_line_right = gf.components.rectangle(
        size=(20.0, 2.0),
        layer=layer_metal,
    )
    control_line_right_ref = c.add_ref(control_line_right)
    control_line_right_ref.move((loop_width / 2 + 10.0, -1.0))
    c.flatten()

    # Add ports for flux control
    c.add_port(
        name="flux_control_left",
        center=(-loop_width / 2 - 40.0, 0),
        width=2.0,
        orientation=180,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="flux_control_right",
        center=(loop_width / 2 + 40.0, 0),
        width=2.0,
        orientation=0,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add readout connection
    c.add_port(
        name="readout",
        center=(0, loop_height / 2 + 10.0),
        width=wire_width,
        orientation=90,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add metadata
    c.info["qubit_type"] = "flux_qubit"
    c.info["loop_width"] = loop_width
    c.info["loop_height"] = loop_height
    c.info["beta_junction_area"] = junction_width * junction_height
    c.info["alpha_junction_area"] = alpha_junction_width * alpha_junction_height
    c.info["alpha_beta_ratio"] = (alpha_junction_width * alpha_junction_height) / (
        junction_width * junction_height
    )

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

flux_qubit_asymmetric

flux_qubit_asymmetric(
    loop_width: float = 60.0,
    loop_height: float = 40.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    alpha_junction_width: float = 0.12,
    alpha_junction_height: float = 0.25,
    wire_width: float = 2.0,
    asymmetry_angle: float = 15.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_alpha_junction: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component

Creates an asymmetric flux qubit for reduced flux noise sensitivity.

An asymmetric flux qubit has a loop geometry that is not perfectly symmetric, which can help reduce sensitivity to flux noise while maintaining controllability.

Parameters:

Name Type Description Default
loop_width float

Width of the superconducting loop in μm.

60.0
loop_height float

Height of the superconducting loop in μm.

40.0
junction_width float

Width of the beta Josephson junctions in μm.

0.15
junction_height float

Height of the beta Josephson junctions in μm.

0.3
alpha_junction_width float

Width of the alpha Josephson junction in μm.

0.12
alpha_junction_height float

Height of the alpha Josephson junction in μm.

0.25
wire_width float

Width of the superconducting wires in μm.

2.0
asymmetry_angle float

Angle of asymmetry in degrees.

15.0
layer_metal LayerSpec

Layer for the metal wires.

(1, 0)
layer_junction LayerSpec

Layer for the beta Josephson junctions.

(2, 0)
layer_alpha_junction LayerSpec

Layer for the alpha Josephson junction.

(3, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the asymmetric flux qubit geometry.

Source code in gdsfactory/components/quantum/flux_qubit.py
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
@gf.cell_with_module_name(tags=["quantum"])
def flux_qubit_asymmetric(
    loop_width: float = 60.0,
    loop_height: float = 40.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    alpha_junction_width: float = 0.12,
    alpha_junction_height: float = 0.25,
    wire_width: float = 2.0,
    asymmetry_angle: float = 15.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_alpha_junction: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates an asymmetric flux qubit for reduced flux noise sensitivity.

    An asymmetric flux qubit has a loop geometry that is not perfectly symmetric,
    which can help reduce sensitivity to flux noise while maintaining controllability.

    Args:
        loop_width: Width of the superconducting loop in μm.
        loop_height: Height of the superconducting loop in μm.
        junction_width: Width of the beta Josephson junctions in μm.
        junction_height: Height of the beta Josephson junctions in μm.
        alpha_junction_width: Width of the alpha Josephson junction in μm.
        alpha_junction_height: Height of the alpha Josephson junction in μm.
        wire_width: Width of the superconducting wires in μm.
        asymmetry_angle: Angle of asymmetry in degrees.
        layer_metal: Layer for the metal wires.
        layer_junction: Layer for the beta Josephson junctions.
        layer_alpha_junction: Layer for the alpha Josephson junction.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the asymmetric flux qubit geometry.
    """
    c = Component()

    angle_rad = np.radians(asymmetry_angle)
    x_offset = loop_height * np.tan(angle_rad)

    # Create asymmetric loop as outer/inner polygon boolean
    # The right side is tilted by asymmetry_angle
    outer_points = [
        (-loop_width / 2, -loop_height / 2),
        (loop_width / 2, -loop_height / 2),
        (loop_width / 2 + x_offset, loop_height / 2),
        (-loop_width / 2, loop_height / 2),
    ]

    inner_points = [
        (-loop_width / 2 + wire_width, -loop_height / 2 + wire_width),
        (loop_width / 2 - wire_width, -loop_height / 2 + wire_width),
        (loop_width / 2 - wire_width + x_offset, loop_height / 2 - wire_width),
        (-loop_width / 2 + wire_width, loop_height / 2 - wire_width),
    ]

    outer_comp = Component()
    outer_comp.add_polygon(outer_points, layer=layer_metal)

    inner_comp = Component()
    inner_comp.add_polygon(inner_points, layer=layer_metal)

    loop = gf.boolean(outer_comp, inner_comp, operation="not", layer=layer_metal)
    c.add_ref(loop)

    # Create the alpha junction (smaller, at bottom)
    alpha_junction = gf.components.rectangle(
        size=(alpha_junction_width, alpha_junction_height),
        layer=layer_alpha_junction,
    )
    alpha_junction_ref = c.add_ref(alpha_junction)
    alpha_junction_ref.move(
        (
            -alpha_junction_width / 2,
            -loop_height / 2 + wire_width / 2 - alpha_junction_height / 2,
        )
    )

    # Create the beta junctions (larger, identical, at sides)
    beta_junction_left = gf.components.rectangle(
        size=(junction_width, junction_height),
        layer=layer_junction,
    )
    beta_junction_left_ref = c.add_ref(beta_junction_left)
    beta_junction_left_ref.move(
        (-loop_width / 2 + wire_width / 2 - junction_width / 2, -junction_height / 2)
    )

    # Right side center x is shifted by half the x_offset
    right_center_x = loop_width / 2 - wire_width / 2 + x_offset / 2
    beta_junction_right = gf.components.rectangle(
        size=(junction_width, junction_height),
        layer=layer_junction,
    )
    beta_junction_right_ref = c.add_ref(beta_junction_right)
    beta_junction_right_ref.move(
        (right_center_x - junction_width / 2, -junction_height / 2)
    )

    # Add control and readout ports
    c.add_port(
        name="flux_control",
        center=(0, loop_height / 2 + 10.0),
        width=wire_width,
        orientation=90,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="readout",
        center=(loop_width / 2 + x_offset + 10.0, 0),
        width=wire_width,
        orientation=0,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add metadata
    c.info["qubit_type"] = "flux_qubit_asymmetric"
    c.info["loop_width"] = loop_width
    c.info["loop_height"] = loop_height
    c.info["asymmetry_angle"] = asymmetry_angle
    c.info["beta_junction_area"] = junction_width * junction_height
    c.info["alpha_junction_area"] = alpha_junction_width * alpha_junction_height
    c.info["alpha_beta_ratio"] = (alpha_junction_width * alpha_junction_height) / (
        junction_width * junction_height
    )

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    c.flatten()
    return c

flux_qubit

flux_qubit_asymmetric

flux_qubit_asymmetric(
    loop_width: float = 60.0,
    loop_height: float = 40.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    alpha_junction_width: float = 0.12,
    alpha_junction_height: float = 0.25,
    wire_width: float = 2.0,
    asymmetry_angle: float = 15.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_alpha_junction: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component

Creates an asymmetric flux qubit for reduced flux noise sensitivity.

An asymmetric flux qubit has a loop geometry that is not perfectly symmetric, which can help reduce sensitivity to flux noise while maintaining controllability.

Parameters:

Name Type Description Default
loop_width float

Width of the superconducting loop in μm.

60.0
loop_height float

Height of the superconducting loop in μm.

40.0
junction_width float

Width of the beta Josephson junctions in μm.

0.15
junction_height float

Height of the beta Josephson junctions in μm.

0.3
alpha_junction_width float

Width of the alpha Josephson junction in μm.

0.12
alpha_junction_height float

Height of the alpha Josephson junction in μm.

0.25
wire_width float

Width of the superconducting wires in μm.

2.0
asymmetry_angle float

Angle of asymmetry in degrees.

15.0
layer_metal LayerSpec

Layer for the metal wires.

(1, 0)
layer_junction LayerSpec

Layer for the beta Josephson junctions.

(2, 0)
layer_alpha_junction LayerSpec

Layer for the alpha Josephson junction.

(3, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the asymmetric flux qubit geometry.

Source code in gdsfactory/components/quantum/flux_qubit.py
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
@gf.cell_with_module_name(tags=["quantum"])
def flux_qubit_asymmetric(
    loop_width: float = 60.0,
    loop_height: float = 40.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    alpha_junction_width: float = 0.12,
    alpha_junction_height: float = 0.25,
    wire_width: float = 2.0,
    asymmetry_angle: float = 15.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_alpha_junction: LayerSpec = (3, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates an asymmetric flux qubit for reduced flux noise sensitivity.

    An asymmetric flux qubit has a loop geometry that is not perfectly symmetric,
    which can help reduce sensitivity to flux noise while maintaining controllability.

    Args:
        loop_width: Width of the superconducting loop in μm.
        loop_height: Height of the superconducting loop in μm.
        junction_width: Width of the beta Josephson junctions in μm.
        junction_height: Height of the beta Josephson junctions in μm.
        alpha_junction_width: Width of the alpha Josephson junction in μm.
        alpha_junction_height: Height of the alpha Josephson junction in μm.
        wire_width: Width of the superconducting wires in μm.
        asymmetry_angle: Angle of asymmetry in degrees.
        layer_metal: Layer for the metal wires.
        layer_junction: Layer for the beta Josephson junctions.
        layer_alpha_junction: Layer for the alpha Josephson junction.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the asymmetric flux qubit geometry.
    """
    c = Component()

    angle_rad = np.radians(asymmetry_angle)
    x_offset = loop_height * np.tan(angle_rad)

    # Create asymmetric loop as outer/inner polygon boolean
    # The right side is tilted by asymmetry_angle
    outer_points = [
        (-loop_width / 2, -loop_height / 2),
        (loop_width / 2, -loop_height / 2),
        (loop_width / 2 + x_offset, loop_height / 2),
        (-loop_width / 2, loop_height / 2),
    ]

    inner_points = [
        (-loop_width / 2 + wire_width, -loop_height / 2 + wire_width),
        (loop_width / 2 - wire_width, -loop_height / 2 + wire_width),
        (loop_width / 2 - wire_width + x_offset, loop_height / 2 - wire_width),
        (-loop_width / 2 + wire_width, loop_height / 2 - wire_width),
    ]

    outer_comp = Component()
    outer_comp.add_polygon(outer_points, layer=layer_metal)

    inner_comp = Component()
    inner_comp.add_polygon(inner_points, layer=layer_metal)

    loop = gf.boolean(outer_comp, inner_comp, operation="not", layer=layer_metal)
    c.add_ref(loop)

    # Create the alpha junction (smaller, at bottom)
    alpha_junction = gf.components.rectangle(
        size=(alpha_junction_width, alpha_junction_height),
        layer=layer_alpha_junction,
    )
    alpha_junction_ref = c.add_ref(alpha_junction)
    alpha_junction_ref.move(
        (
            -alpha_junction_width / 2,
            -loop_height / 2 + wire_width / 2 - alpha_junction_height / 2,
        )
    )

    # Create the beta junctions (larger, identical, at sides)
    beta_junction_left = gf.components.rectangle(
        size=(junction_width, junction_height),
        layer=layer_junction,
    )
    beta_junction_left_ref = c.add_ref(beta_junction_left)
    beta_junction_left_ref.move(
        (-loop_width / 2 + wire_width / 2 - junction_width / 2, -junction_height / 2)
    )

    # Right side center x is shifted by half the x_offset
    right_center_x = loop_width / 2 - wire_width / 2 + x_offset / 2
    beta_junction_right = gf.components.rectangle(
        size=(junction_width, junction_height),
        layer=layer_junction,
    )
    beta_junction_right_ref = c.add_ref(beta_junction_right)
    beta_junction_right_ref.move(
        (right_center_x - junction_width / 2, -junction_height / 2)
    )

    # Add control and readout ports
    c.add_port(
        name="flux_control",
        center=(0, loop_height / 2 + 10.0),
        width=wire_width,
        orientation=90,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="readout",
        center=(loop_width / 2 + x_offset + 10.0, 0),
        width=wire_width,
        orientation=0,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add metadata
    c.info["qubit_type"] = "flux_qubit_asymmetric"
    c.info["loop_width"] = loop_width
    c.info["loop_height"] = loop_height
    c.info["asymmetry_angle"] = asymmetry_angle
    c.info["beta_junction_area"] = junction_width * junction_height
    c.info["alpha_junction_area"] = alpha_junction_width * alpha_junction_height
    c.info["alpha_beta_ratio"] = (alpha_junction_width * alpha_junction_height) / (
        junction_width * junction_height
    )

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    c.flatten()
    return c

flux_qubit_asymmetric

resonator_cpw

resonator_cpw(
    length: float = 1000.0,
    width: float = 10.0,
    gap: float = 6.0,
    meander_pitch: float = 50.0,
    meander_width: float = 200.0,
    coupling_gap: float = 5.0,
    coupling_length: float = 100.0,
    layer_metal: LayerSpec = (1, 0),
    layer_gap: LayerSpec = (2, 0),
    port_type: str = "electrical",
) -> Component

Creates a coplanar waveguide (CPW) resonator.

A CPW resonator consists of a meandered coplanar waveguide with coupling gaps for capacitive coupling to feedlines or qubits.

Parameters:

Name Type Description Default
length float

Total length of the resonator in μm.

1000.0
width float

Width of the center conductor in μm.

10.0
gap float

Gap width on each side of the center conductor in μm.

6.0
meander_pitch float

Pitch between meander segments in μm.

50.0
meander_width float

Width of each meander section in μm.

200.0
coupling_gap float

Gap for capacitive coupling in μm.

5.0
coupling_length float

Length of the coupling region in μm.

100.0
layer_metal LayerSpec

Layer for the metal conductor.

(1, 0)
layer_gap LayerSpec

Layer for the gaps (ground plane).

(2, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the CPW resonator geometry.

Source code in gdsfactory/components/quantum/resonator.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
@gf.cell_with_module_name(tags=["quantum"])
def resonator_cpw(
    length: float = 1000.0,
    width: float = 10.0,
    gap: float = 6.0,
    meander_pitch: float = 50.0,
    meander_width: float = 200.0,
    coupling_gap: float = 5.0,
    coupling_length: float = 100.0,
    layer_metal: LayerSpec = (1, 0),
    layer_gap: LayerSpec = (2, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a coplanar waveguide (CPW) resonator.

    A CPW resonator consists of a meandered coplanar waveguide with coupling gaps
    for capacitive coupling to feedlines or qubits.

    Args:
        length: Total length of the resonator in μm.
        width: Width of the center conductor in μm.
        gap: Gap width on each side of the center conductor in μm.
        meander_pitch: Pitch between meander segments in μm.
        meander_width: Width of each meander section in μm.
        coupling_gap: Gap for capacitive coupling in μm.
        coupling_length: Length of the coupling region in μm.
        layer_metal: Layer for the metal conductor.
        layer_gap: Layer for the gaps (ground plane).
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the CPW resonator geometry.
    """
    c = Component()

    # Calculate number of meander sections needed
    num_meanders = int(length / meander_width) if meander_width > 0 else 1
    actual_length = num_meanders * meander_width

    # Create meander path
    path_points = []
    x, y = 0.0, 0.0

    for i in range(num_meanders):
        if i == 0:
            # First segment
            path_points.extend([(x, y), (x + meander_width, y)])
            x += meander_width
        else:
            # Alternate up and down
            if i % 2 == 1:
                y += meander_pitch
                path_points.extend([(x, y), (x - meander_width, y)])
                x -= meander_width
            else:
                y += meander_pitch
                path_points.extend([(x, y), (x + meander_width, y)])
                x += meander_width

    # Create the CPW path
    path = gf.Path(np.array(path_points))

    # Create cross section for CPW
    cpw_xs = gf.cross_section.strip(width=width, layer=layer_metal)

    # Create the resonator structure
    resonator_path = gf.path.extrude(path, cpw_xs)
    c.add_ref(resonator_path)

    # Create ground plane with gaps
    total_width = meander_width + 2 * meander_pitch
    total_height = (num_meanders - 1) * meander_pitch + width + 2 * gap

    # Ground plane
    ground_plane = gf.components.rectangle(
        size=(total_width + 2 * gap, total_height + 2 * gap),
        layer=layer_metal,
    )
    ground_ref = c.add_ref(ground_plane)
    ground_ref.move((-gap, -gap))

    # Create gaps by boolean subtraction
    gap_width = width + 2 * gap

    # Create gap path along the resonator
    gap_path = gf.Path(np.array(path_points))
    gap_xs = gf.cross_section.strip(width=gap_width, layer=layer_gap)
    gap_structure = gf.path.extrude(gap_path, gap_xs)

    # Subtract gaps from ground plane
    gf.boolean(
        ground_ref,
        gap_structure,
        operation="not",
        layer=layer_metal,
    )

    # Add coupling regions
    # Input coupling
    coupling_in = gf.components.rectangle(
        size=(coupling_length, coupling_gap),
        layer=layer_gap,
    )
    coupling_in_ref = c.add_ref(coupling_in)
    coupling_in_ref.move((-coupling_length / 2, -width / 2 - gap - coupling_gap / 2))

    # Output coupling
    coupling_out = gf.components.rectangle(
        size=(coupling_length, coupling_gap),
        layer=layer_gap,
    )
    coupling_out_ref = c.add_ref(coupling_out)
    coupling_out_ref.move(
        (x - coupling_length / 2, y + width / 2 + gap + coupling_gap / 2)
    )

    # Add ports for coupling
    c.add_port(
        name="input",
        center=(0, -width / 2 - gap - coupling_gap),
        width=coupling_length,
        orientation=270,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="output",
        center=(x, y + width / 2 + gap + coupling_gap),
        width=coupling_length,
        orientation=90,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add metadata
    c.info["resonator_type"] = "cpw"
    c.info["length"] = actual_length
    c.info["width"] = width
    c.info["gap"] = gap
    c.info["frequency_estimate"] = (
        3e8 / (2 * actual_length * 1e-6) / 1e9
    )  # GHz, rough estimate

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    c.flatten()
    return c

resonator_cpw

resonator_lumped

resonator_lumped(
    capacitor_fingers: int = 4,
    capacitor_finger_length: float = 20.0,
    capacitor_finger_gap: float = 2.0,
    capacitor_thickness: float = 5.0,
    inductor_width: float = 2.0,
    inductor_turns: int = 3,
    inductor_radius: float = 20.0,
    coupling_gap: float = 5.0,
    layer_metal: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component

Creates a lumped element resonator with interdigital capacitor and spiral inductor.

A lumped resonator consists of a capacitive element (interdigital capacitor) and an inductive element (spiral inductor) forming an LC circuit.

Parameters:

Name Type Description Default
capacitor_fingers int

Number of fingers in the interdigital capacitor.

4
capacitor_finger_length float

Length of each capacitor finger in μm.

20.0
capacitor_finger_gap float

Gap between capacitor fingers in μm.

2.0
capacitor_thickness float

Thickness of capacitor fingers in μm.

5.0
inductor_width float

Width of the inductor wire in μm.

2.0
inductor_turns int

Number of turns in the spiral inductor.

3
inductor_radius float

Radius of the spiral inductor in μm.

20.0
coupling_gap float

Gap for capacitive coupling in μm.

5.0
layer_metal LayerSpec

Layer for the metal structures.

(1, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the lumped resonator geometry.

Source code in gdsfactory/components/quantum/resonator.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
@gf.cell_with_module_name(tags=["quantum"])
def resonator_lumped(
    capacitor_fingers: int = 4,
    capacitor_finger_length: float = 20.0,
    capacitor_finger_gap: float = 2.0,
    capacitor_thickness: float = 5.0,
    inductor_width: float = 2.0,
    inductor_turns: int = 3,
    inductor_radius: float = 20.0,
    coupling_gap: float = 5.0,
    layer_metal: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a lumped element resonator with interdigital capacitor and spiral inductor.

    A lumped resonator consists of a capacitive element (interdigital capacitor)
    and an inductive element (spiral inductor) forming an LC circuit.

    Args:
        capacitor_fingers: Number of fingers in the interdigital capacitor.
        capacitor_finger_length: Length of each capacitor finger in μm.
        capacitor_finger_gap: Gap between capacitor fingers in μm.
        capacitor_thickness: Thickness of capacitor fingers in μm.
        inductor_width: Width of the inductor wire in μm.
        inductor_turns: Number of turns in the spiral inductor.
        inductor_radius: Radius of the spiral inductor in μm.
        coupling_gap: Gap for capacitive coupling in μm.
        layer_metal: Layer for the metal structures.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the lumped resonator geometry.
    """
    c = Component()

    # Create interdigital capacitor
    capacitor = gf.get_component(
        "interdigital_capacitor",
        fingers=capacitor_fingers,
        finger_length=capacitor_finger_length,
        finger_gap=capacitor_finger_gap,
        thickness=capacitor_thickness,
        layer=layer_metal,
    )
    c.add_ref(capacitor)

    # Create spiral inductor
    # Create a cross section with the specified width
    inductor_cross_section = gf.cross_section.strip(
        width=inductor_width,
        layer=layer_metal,
    )
    inductor = gf.components.spiral(
        n_loops=inductor_turns,
        cross_section=inductor_cross_section,
    )
    ind_ref = c.add_ref(inductor)

    # Position inductor next to capacitor
    cap_width = 2 * capacitor_thickness + capacitor_finger_length + capacitor_finger_gap
    ind_ref.move((cap_width + 20.0, 0))

    # Connect capacitor and inductor
    connection = gf.components.rectangle(
        size=(20.0, inductor_width),
        layer=layer_metal,
    )
    conn_ref = c.add_ref(connection)
    conn_ref.move((cap_width, -inductor_width / 2))

    # Connect to inductor input
    connection2 = gf.components.rectangle(
        size=(inductor_width, 20.0),
        layer=layer_metal,
    )
    conn2_ref = c.add_ref(connection2)
    conn2_ref.move((cap_width + 20.0 - inductor_width / 2, -20.0))

    # Add coupling elements for input/output
    coupling_cap_in = gf.components.rectangle(
        size=(capacitor_thickness, coupling_gap),
        layer=layer_metal,
    )
    coupling_in_ref = c.add_ref(coupling_cap_in)
    coupling_in_ref.move(
        (
            -coupling_gap - capacitor_thickness,
            capacitor_fingers * capacitor_thickness / 2,
        )
    )

    coupling_cap_out = gf.components.rectangle(
        size=(capacitor_thickness, coupling_gap),
        layer=layer_metal,
    )
    coupling_out_ref = c.add_ref(coupling_cap_out)
    coupling_out_ref.move(
        (cap_width + coupling_gap, capacitor_fingers * capacitor_thickness / 2)
    )

    # Add ports
    c.add_port(
        name="input",
        center=(
            -coupling_gap - capacitor_thickness / 2,
            capacitor_fingers * capacitor_thickness / 2,
        ),
        width=coupling_gap,
        orientation=180,
        layer=layer_metal,
        port_type=port_type,
    )

    c.add_port(
        name="output",
        center=(
            cap_width + coupling_gap + capacitor_thickness / 2,
            capacitor_fingers * capacitor_thickness / 2,
        ),
        width=coupling_gap,
        orientation=0,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add metadata
    c.info["resonator_type"] = "lumped"
    c.info["capacitor_fingers"] = capacitor_fingers
    c.info["inductor_turns"] = inductor_turns
    c.info["inductor_radius"] = inductor_radius

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

resonator_lumped

resonator_quarter_wave

resonator_quarter_wave(
    length: float = 2500.0,
    width: float = 10.0,
    gap: float = 6.0,
    short_stub_length: float = 50.0,
    coupling_gap: float = 5.0,
    coupling_length: float = 100.0,
    layer_metal: LayerSpec = (1, 0),
    layer_gap: LayerSpec = (2, 0),
    port_type: str = "electrical",
) -> Component

Creates a quarter-wave coplanar waveguide resonator.

A quarter-wave resonator is shorted at one end and has maximum electric field at the open end, making it suitable for capacitive coupling.

Parameters:

Name Type Description Default
length float

Length of the quarter-wave resonator in μm.

2500.0
width float

Width of the center conductor in μm.

10.0
gap float

Gap width on each side of the center conductor in μm.

6.0
short_stub_length float

Length of the shorting stub in μm.

50.0
coupling_gap float

Gap for capacitive coupling in μm.

5.0
coupling_length float

Length of the coupling region in μm.

100.0
layer_metal LayerSpec

Layer for the metal conductor.

(1, 0)
layer_gap LayerSpec

Layer for the gaps.

(2, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the quarter-wave resonator geometry.

Source code in gdsfactory/components/quantum/resonator.py
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
@gf.cell_with_module_name(tags=["quantum"])
def resonator_quarter_wave(
    length: float = 2500.0,
    width: float = 10.0,
    gap: float = 6.0,
    short_stub_length: float = 50.0,
    coupling_gap: float = 5.0,
    coupling_length: float = 100.0,
    layer_metal: LayerSpec = (1, 0),
    layer_gap: LayerSpec = (2, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a quarter-wave coplanar waveguide resonator.

    A quarter-wave resonator is shorted at one end and has maximum electric field
    at the open end, making it suitable for capacitive coupling.

    Args:
        length: Length of the quarter-wave resonator in μm.
        width: Width of the center conductor in μm.
        gap: Gap width on each side of the center conductor in μm.
        short_stub_length: Length of the shorting stub in μm.
        coupling_gap: Gap for capacitive coupling in μm.
        coupling_length: Length of the coupling region in μm.
        layer_metal: Layer for the metal conductor.
        layer_gap: Layer for the gaps.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the quarter-wave resonator geometry.
    """
    c = Component()

    # Create main resonator line
    main_line = gf.components.rectangle(
        size=(length, width),
        layer=layer_metal,
    )
    c.add_ref(main_line)

    # Create shorting stub at one end
    short_stub = gf.components.rectangle(
        size=(short_stub_length, width + 2 * gap),
        layer=layer_metal,
    )
    short_ref = c.add_ref(short_stub)
    short_ref.move((length, -gap))

    # Create ground planes
    ground_top = gf.components.rectangle(
        size=(length + short_stub_length + 2 * gap, gap),
        layer=layer_metal,
    )
    ground_top_ref = c.add_ref(ground_top)
    ground_top_ref.move((-gap, width))

    ground_bottom = gf.components.rectangle(
        size=(length + short_stub_length + 2 * gap, gap),
        layer=layer_metal,
    )
    ground_bottom_ref = c.add_ref(ground_bottom)
    ground_bottom_ref.move((-gap, -gap))

    # Create coupling region at open end
    coupling_region = gf.components.rectangle(
        size=(coupling_length, coupling_gap),
        layer=layer_gap,
    )
    coupling_ref = c.add_ref(coupling_region)
    coupling_ref.move((-coupling_length, width / 2 - coupling_gap / 2))

    # Add port for coupling
    c.add_port(
        name="coupling",
        center=(-coupling_length / 2, width / 2),
        width=coupling_gap,
        orientation=180,
        layer=layer_metal,
        port_type=port_type,
    )

    # Add metadata
    c.info["resonator_type"] = "quarter_wave"
    c.info["length"] = length
    c.info["width"] = width
    c.info["gap"] = gap
    c.info["frequency_estimate"] = (
        3e8 / (4 * length * 1e-6) / 1e9
    )  # GHz, rough estimate

    if port_type == "electrical":
        for p in list(c.ports):
            if p.name and p.port_type == "electrical":
                c.create_pin(ports=[p], name=p.name)

    return c

resonator_quarter_wave

transmon

transmon

transmon(
    pad_width: float = 200.0,
    pad_height: float = 100.0,
    pad_gap: float = 6.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    island_width: float = 10.0,
    island_height: float = 4.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_island: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component

Creates a transmon qubit with Josephson junction.

A transmon qubit consists of two capacitor pads connected by a Josephson junction. The junction creates an anharmonic oscillator that can be used as a qubit.

Parameters:

Name Type Description Default
pad_width float

Width of each capacitor pad in μm.

200.0
pad_height float

Height of each capacitor pad in μm.

100.0
pad_gap float

Gap between the two pads in μm.

6.0
junction_width float

Width of the Josephson junction in μm.

0.15
junction_height float

Height of the Josephson junction in μm.

0.3
island_width float

Width of the central island in μm.

10.0
island_height float

Height of the central island in μm.

4.0
layer_metal LayerSpec

Layer for the metal pads.

(1, 0)
layer_junction LayerSpec

Layer for the Josephson junction.

(2, 0)
layer_island LayerSpec

Layer for the central island.

(1, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the transmon geometry.

Source code in gdsfactory/components/quantum/transmon.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
@gf.cell_with_module_name(tags=["quantum"])
def transmon(
    pad_width: float = 200.0,
    pad_height: float = 100.0,
    pad_gap: float = 6.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    island_width: float = 10.0,
    island_height: float = 4.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_island: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a transmon qubit with Josephson junction.

    A transmon qubit consists of two capacitor pads connected by a Josephson junction.
    The junction creates an anharmonic oscillator that can be used as a qubit.

    Args:
        pad_width: Width of each capacitor pad in μm.
        pad_height: Height of each capacitor pad in μm.
        pad_gap: Gap between the two pads in μm.
        junction_width: Width of the Josephson junction in μm.
        junction_height: Height of the Josephson junction in μm.
        island_width: Width of the central island in μm.
        island_height: Height of the central island in μm.
        layer_metal: Layer for the metal pads.
        layer_junction: Layer for the Josephson junction.
        layer_island: Layer for the central island.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the transmon geometry.
    """
    c = Component()

    # Create left capacitor pad
    left_pad = gf.components.rectangle(
        size=(pad_width, pad_height),
        layer=layer_metal,
        port_type=port_type,
    )
    left_pad_ref = c.add_ref(left_pad)
    left_pad_ref.move((-pad_width - pad_gap / 2, -pad_height / 2))

    # Create right capacitor pad
    right_pad = gf.components.rectangle(
        size=(pad_width, pad_height),
        layer=layer_metal,
        port_type=port_type,
    )
    right_pad_ref = c.add_ref(right_pad)
    right_pad_ref.move((pad_gap / 2, -pad_height / 2))

    # Create central island
    island = gf.components.rectangle(
        size=(island_width, island_height),
        layer=layer_island,
    )
    island_ref = c.add_ref(island)
    island_ref.move((-island_width / 2, -island_height / 2))

    # Create Josephson junction
    junction = gf.components.rectangle(
        size=(junction_width, junction_height),
        layer=layer_junction,
    )
    junction_ref = c.add_ref(junction)
    junction_ref.move((-junction_width / 2, -junction_height / 2))

    # Add connection lines from pads to island
    # Only add connections if there is a gap between island and pads
    connection_width = abs(pad_gap / 2 - island_width / 2)
    if pad_gap / 2 > island_width / 2:
        left_connection = gf.components.rectangle(
            size=(connection_width, junction_height / 2),
            layer=layer_metal,
        )
        left_conn_ref = c.add_ref(left_connection)
        left_conn_ref.move((-pad_gap / 2, -junction_height / 4))

        right_connection = gf.components.rectangle(
            size=(connection_width, junction_height / 2),
            layer=layer_metal,
        )
        right_conn_ref = c.add_ref(right_connection)
        right_conn_ref.move((island_width / 2, -junction_height / 4))

    # Add ports for connections
    c.add_port(
        name="left_pad",
        center=(-pad_width - pad_gap / 2, 0),
        width=pad_height,
        orientation=180,
        layer=layer_metal,
    )

    c.add_port(
        name="right_pad",
        center=(pad_width + pad_gap / 2, 0),
        width=pad_height,
        orientation=0,
        layer=layer_metal,
    )

    # Add metadata
    c.info["qubit_type"] = "transmon"
    c.info["pad_width"] = pad_width
    c.info["pad_height"] = pad_height
    c.info["pad_gap"] = pad_gap
    c.info["junction_area"] = junction_width * junction_height

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    return c

transmon_circular

transmon_circular(
    pad_radius: float = 100.0,
    pad_gap: float = 6.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    island_radius: float = 5.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_island: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component

Creates a circular transmon qubit with Josephson junction.

A circular variant of the transmon qubit with circular capacitor pads.

Parameters:

Name Type Description Default
pad_radius float

Radius of each circular capacitor pad in μm.

100.0
pad_gap float

Gap between the two pads in μm.

6.0
junction_width float

Width of the Josephson junction in μm.

0.15
junction_height float

Height of the Josephson junction in μm.

0.3
island_radius float

Radius of the central circular island in μm.

5.0
layer_metal LayerSpec

Layer for the metal pads.

(1, 0)
layer_junction LayerSpec

Layer for the Josephson junction.

(2, 0)
layer_island LayerSpec

Layer for the central island.

(1, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the circular transmon geometry.

Source code in gdsfactory/components/quantum/transmon.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
@gf.cell_with_module_name(tags=["quantum"])
def transmon_circular(
    pad_radius: float = 100.0,
    pad_gap: float = 6.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    island_radius: float = 5.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_island: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a circular transmon qubit with Josephson junction.

    A circular variant of the transmon qubit with circular capacitor pads.

    Args:
        pad_radius: Radius of each circular capacitor pad in μm.
        pad_gap: Gap between the two pads in μm.
        junction_width: Width of the Josephson junction in μm.
        junction_height: Height of the Josephson junction in μm.
        island_radius: Radius of the central circular island in μm.
        layer_metal: Layer for the metal pads.
        layer_junction: Layer for the Josephson junction.
        layer_island: Layer for the central island.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the circular transmon geometry.
    """
    c = Component()

    # Create left circular pad
    left_pad = gf.components.circle(
        radius=pad_radius,
        layer=layer_metal,
    )
    left_pad_ref = c.add_ref(left_pad)
    left_pad_ref.move((-pad_radius - pad_gap / 2, 0))

    # Create right circular pad
    right_pad = gf.components.circle(
        radius=pad_radius,
        layer=layer_metal,
    )
    right_pad_ref = c.add_ref(right_pad)
    right_pad_ref.move((pad_radius + pad_gap / 2, 0))

    # Create central circular island
    island = gf.components.circle(
        radius=island_radius,
        layer=layer_island,
    )
    c.add_ref(island)

    # Create Josephson junction
    junction = gf.components.rectangle(
        size=(junction_width, junction_height),
        layer=layer_junction,
    )
    junction_ref = c.add_ref(junction)
    junction_ref.move((-junction_width / 2, -junction_height / 2))

    # Add connection lines from pads to island
    # Only add connections if there is a gap between island and pads
    connection_width = abs(pad_gap / 2 - island_radius)
    if pad_gap / 2 > island_radius:
        left_connection = gf.components.rectangle(
            size=(connection_width, junction_height / 2),
            layer=layer_metal,
        )
        left_conn_ref = c.add_ref(left_connection)
        left_conn_ref.move((-pad_gap / 2, -junction_height / 4))

        right_connection = gf.components.rectangle(
            size=(connection_width, junction_height / 2),
            layer=layer_metal,
        )
        right_conn_ref = c.add_ref(right_connection)
        right_conn_ref.move((island_radius, -junction_height / 4))

    # Add ports for connections
    c.add_port(
        name="left_pad",
        center=(-2 * pad_radius - pad_gap / 2, 0),
        width=2 * pad_radius,
        orientation=180,
        layer=layer_metal,
    )

    c.add_port(
        name="right_pad",
        center=(2 * pad_radius + pad_gap / 2, 0),
        width=2 * pad_radius,
        orientation=0,
        layer=layer_metal,
    )

    # Add metadata
    c.info["qubit_type"] = "transmon_circular"
    c.info["pad_radius"] = pad_radius
    c.info["pad_gap"] = pad_gap
    c.info["junction_area"] = junction_width * junction_height

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    return c

transmon

transmon_circular

transmon_circular(
    pad_radius: float = 100.0,
    pad_gap: float = 6.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    island_radius: float = 5.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_island: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component

Creates a circular transmon qubit with Josephson junction.

A circular variant of the transmon qubit with circular capacitor pads.

Parameters:

Name Type Description Default
pad_radius float

Radius of each circular capacitor pad in μm.

100.0
pad_gap float

Gap between the two pads in μm.

6.0
junction_width float

Width of the Josephson junction in μm.

0.15
junction_height float

Height of the Josephson junction in μm.

0.3
island_radius float

Radius of the central circular island in μm.

5.0
layer_metal LayerSpec

Layer for the metal pads.

(1, 0)
layer_junction LayerSpec

Layer for the Josephson junction.

(2, 0)
layer_island LayerSpec

Layer for the central island.

(1, 0)
port_type str

Type of port to add to the component.

'electrical'

Returns:

Name Type Description
Component Component

A gdsfactory component with the circular transmon geometry.

Source code in gdsfactory/components/quantum/transmon.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
@gf.cell_with_module_name(tags=["quantum"])
def transmon_circular(
    pad_radius: float = 100.0,
    pad_gap: float = 6.0,
    junction_width: float = 0.15,
    junction_height: float = 0.3,
    island_radius: float = 5.0,
    layer_metal: LayerSpec = (1, 0),
    layer_junction: LayerSpec = (2, 0),
    layer_island: LayerSpec = (1, 0),
    port_type: str = "electrical",
) -> Component:
    """Creates a circular transmon qubit with Josephson junction.

    A circular variant of the transmon qubit with circular capacitor pads.

    Args:
        pad_radius: Radius of each circular capacitor pad in μm.
        pad_gap: Gap between the two pads in μm.
        junction_width: Width of the Josephson junction in μm.
        junction_height: Height of the Josephson junction in μm.
        island_radius: Radius of the central circular island in μm.
        layer_metal: Layer for the metal pads.
        layer_junction: Layer for the Josephson junction.
        layer_island: Layer for the central island.
        port_type: Type of port to add to the component.

    Returns:
        Component: A gdsfactory component with the circular transmon geometry.
    """
    c = Component()

    # Create left circular pad
    left_pad = gf.components.circle(
        radius=pad_radius,
        layer=layer_metal,
    )
    left_pad_ref = c.add_ref(left_pad)
    left_pad_ref.move((-pad_radius - pad_gap / 2, 0))

    # Create right circular pad
    right_pad = gf.components.circle(
        radius=pad_radius,
        layer=layer_metal,
    )
    right_pad_ref = c.add_ref(right_pad)
    right_pad_ref.move((pad_radius + pad_gap / 2, 0))

    # Create central circular island
    island = gf.components.circle(
        radius=island_radius,
        layer=layer_island,
    )
    c.add_ref(island)

    # Create Josephson junction
    junction = gf.components.rectangle(
        size=(junction_width, junction_height),
        layer=layer_junction,
    )
    junction_ref = c.add_ref(junction)
    junction_ref.move((-junction_width / 2, -junction_height / 2))

    # Add connection lines from pads to island
    # Only add connections if there is a gap between island and pads
    connection_width = abs(pad_gap / 2 - island_radius)
    if pad_gap / 2 > island_radius:
        left_connection = gf.components.rectangle(
            size=(connection_width, junction_height / 2),
            layer=layer_metal,
        )
        left_conn_ref = c.add_ref(left_connection)
        left_conn_ref.move((-pad_gap / 2, -junction_height / 4))

        right_connection = gf.components.rectangle(
            size=(connection_width, junction_height / 2),
            layer=layer_metal,
        )
        right_conn_ref = c.add_ref(right_connection)
        right_conn_ref.move((island_radius, -junction_height / 4))

    # Add ports for connections
    c.add_port(
        name="left_pad",
        center=(-2 * pad_radius - pad_gap / 2, 0),
        width=2 * pad_radius,
        orientation=180,
        layer=layer_metal,
    )

    c.add_port(
        name="right_pad",
        center=(2 * pad_radius + pad_gap / 2, 0),
        width=2 * pad_radius,
        orientation=0,
        layer=layer_metal,
    )

    # Add metadata
    c.info["qubit_type"] = "transmon_circular"
    c.info["pad_radius"] = pad_radius
    c.info["pad_gap"] = pad_gap
    c.info["junction_area"] = junction_width * junction_height

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    return c

transmon_circular

rings

coupler_bend

coupler_bend(
    radius: float | None = None,
    coupler_gap: float = 0.2,
    coupling_angle_coverage: float = 120.0,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    bend: AnyComponentFactory = bend_circular_all_angle,
    bend_output: ComponentSpec = "bend_euler",
) -> Component

Compact curved coupler with bezier escape.

TODO: fix for euler bends.

Parameters:

Name Type Description Default
radius float | None

um.

None
coupler_gap float

um.

0.2
coupling_angle_coverage float

degrees.

120.0
cross_section_inner CrossSectionSpec

spec inner bend.

'strip'
cross_section_outer CrossSectionSpec

spec outer bend.

'strip'
bend AnyComponentFactory

for bend.

bend_circular_all_angle
bend_output ComponentSpec

for bend.

r 4 | | | / ___3 | / /

'bend_euler'
Source code in gdsfactory/components/rings/ring_single_bend_coupler.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["rings"])
def coupler_bend(
    radius: float | None = None,
    coupler_gap: float = 0.2,
    coupling_angle_coverage: float = 120.0,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    bend: AnyComponentFactory = bend_circular_all_angle,
    bend_output: ComponentSpec = "bend_euler",
) -> Component:
    r"""Compact curved coupler with bezier escape.

    TODO: fix for euler bends.

    Args:
        radius: um.
        coupler_gap: um.
        coupling_angle_coverage: degrees.
        cross_section_inner: spec inner bend.
        cross_section_outer: spec outer bend.
        bend: for bend.
        bend_output: for bend.

            r   4
            |   |
            |  / ___3
            | / /
        2____/ /
        1_____/
    """
    c = Component()

    xi = gf.get_cross_section(cross_section_inner)
    xo = gf.get_cross_section(cross_section_outer)

    angle_inner = 90
    angle_outer = coupling_angle_coverage / 2
    gap = coupler_gap

    width = xo.width / 2 + xi.width / 2
    spacing = gap + width

    if radius is None:
        radius = xi.radius or xo.radius
        assert radius is not None, "cross_section must have a radius"

    bend90_inner_right = gf.get_component(
        bend,  # type: ignore[arg-type]
        radius=radius,
        cross_section=cross_section_inner,
        angle=angle_inner,
    )
    bend_output_right = gf.get_component(
        bend,  # type: ignore[arg-type]
        radius=radius + spacing,
        cross_section=cross_section_outer,
        angle=angle_outer,
    )
    bend_inner_ref = c.add_ref_off_grid(bend90_inner_right)
    bend_output_ref = c.add_ref_off_grid(bend_output_right)

    output = gf.get_component(
        bend_output, angle=angle_outer, cross_section=cross_section_outer
    )
    output_ref = c.add_ref_off_grid(output)
    output_ref.connect("o1", bend_output_ref.ports["o2"], mirror=True)

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

    c.add_port("o1", port=bend_output_ref.ports["o1"])
    c.add_port("o2", port=bend_inner_ref.ports["o1"])
    c.add_port("o3", port=output_ref.ports["o2"])
    c.add_port("o4", port=bend_inner_ref.ports["o2"])
    return c

coupler_bend

coupler_ring_bend

coupler_ring_bend(
    radius: float | None = None,
    coupler_gap: float = 0.2,
    coupling_angle_coverage: float = 90.0,
    length_x: float = 0.0,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    bend: AnyComponentFactory = bend_circular_all_angle,
    bend_output: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
) -> Component

Two back-to-back coupler_bend.

Parameters:

Name Type Description Default
radius float | None

um. Default is None, which uses the default radius of the cross_section.

None
coupler_gap float

um.

0.2
angle_inner

of the inner bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.

required
angle_outer

of the outer bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.

required
coupling_angle_coverage float

degrees.

90.0
length_x float

horizontal straight length.

0.0
cross_section_inner CrossSectionSpec

spec inner bend.

'strip'
cross_section_outer CrossSectionSpec

spec outer bend.

'strip'
bend AnyComponentFactory

for bend.

bend_circular_all_angle
bend_output ComponentSpec

for bend.

'bend_euler'
straight ComponentSpec

for straight.

'straight'
Source code in gdsfactory/components/rings/ring_single_bend_coupler.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
@gf.cell_with_module_name(schematic_function=coupler_ring_schematic, tags=["rings"])
def coupler_ring_bend(
    radius: float | None = None,
    coupler_gap: float = 0.2,
    coupling_angle_coverage: float = 90.0,
    length_x: float = 0.0,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    bend: AnyComponentFactory = bend_circular_all_angle,
    bend_output: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
) -> Component:
    r"""Two back-to-back coupler_bend.

    Args:
        radius: um. Default is None, which uses the default radius of the cross_section.
        coupler_gap: um.
        angle_inner: of the inner bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.
        angle_outer: of the outer bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.
        coupling_angle_coverage: degrees.
        length_x: horizontal straight length.
        cross_section_inner: spec inner bend.
        cross_section_outer: spec outer bend.
        bend: for bend.
        bend_output: for bend.
        straight: for straight.
    """
    c = Component()
    cp = coupler_bend(
        radius=radius,
        coupler_gap=coupler_gap,
        coupling_angle_coverage=coupling_angle_coverage,
        cross_section_inner=cross_section_inner,
        cross_section_outer=cross_section_outer,
        bend=bend,
        bend_output=bend_output,
    )
    sin = gf.get_component(straight, length=length_x, cross_section=cross_section_inner)
    sout = gf.get_component(
        straight, length=length_x, cross_section=cross_section_outer
    )

    coupler_right = c << cp
    coupler_left = c << cp
    straight_inner = c << sin
    straight_inner.movex(-length_x / 2)
    straight_outer = c << sout
    straight_outer.movex(-length_x / 2)

    coupler_left.connect("o1", straight_outer.ports["o1"])
    straight_inner.connect("o1", coupler_left.ports["o2"])
    coupler_right.connect("o2", straight_inner.ports["o2"], mirror=True)
    straight_outer.connect("o2", coupler_right.ports["o1"])

    c.add_port("o1", port=coupler_left.ports["o3"])
    c.add_port("o2", port=coupler_left.ports["o4"])
    c.add_port("o4", port=coupler_right.ports["o3"])
    c.add_port("o3", port=coupler_right.ports["o4"])
    # c.flatten()
    return c

coupler_ring_bend

disk

disk

disk(
    radius: float = 10.0,
    gap: float = 0.2,
    wrap_angle_deg: float = 180.0,
    parity: int = 1,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Disk Resonator.

Parameters:

Name Type Description Default
radius float

disk resonator radius.

10.0
gap float

Distance between the bus straight and resonator.

0.2
wrap_angle_deg float

Angle in degrees between 0 and 180. determines how much the bus straight wraps along the resonator. 0 corresponds to a straight bus straight. 180 corresponds to a bus straight wrapped around half of the resonator.

180.0
parity 1 or -1

1, resonator left from bus straight, -1 resonator to the right.

1
cross_section CrossSectionSpec

cross_section spec.

'strip'
Source code in gdsfactory/components/rings/disk.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@gf.cell_with_module_name(schematic_function=ring_single_schematic, tags=["rings"])
def disk(
    radius: float = 10.0,
    gap: float = 0.2,
    wrap_angle_deg: float = 180.0,
    parity: int = 1,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Disk Resonator.

    Args:
       radius: disk resonator radius.
       gap: Distance between the bus straight and resonator.
       wrap_angle_deg: Angle in degrees between 0 and 180.
        determines how much the bus straight wraps along the resonator.
        0 corresponds to a straight bus straight.
        180 corresponds to a bus straight wrapped around half of the resonator.
       parity (1 or -1): 1, resonator left from bus straight, -1 resonator to the right.
       cross_section: cross_section spec.

    """
    if parity not in (1, -1):
        raise ValueError("parity must be 1 or -1")

    if wrap_angle_deg < 0.0 or wrap_angle_deg > 180.0:
        raise ValueError("wrap_angle_deg must be between 0.0 and 180.0")

    c = gf.Component()

    xs = gf.get_cross_section(cross_section=cross_section)
    radius_disk = radius
    radius = radius + xs.width / 2.0 + gap
    xs_bend = xs.copy(radius=radius)

    r_bend, size_x, dy, bus_length = _compute_parameters(
        xs_bend, wrap_angle_deg, radius
    )

    c, bend_input, bend_middle, bend_output = _generate_bends(
        c, r_bend, wrap_angle_deg, xs_bend
    )

    c, straight_left, straight_right = _generate_straights(
        c, bus_length, size_x, bend_input, bend_output, xs_bend
    )
    assert xs.layer is not None
    circle = c << gf.components.circle(radius=radius_disk, layer=xs.layer)

    circle_cladding = None
    if bend_middle is not None:
        dx = (bend_middle.ports["o1"].x + bend_middle.ports["o2"].x) / 2.0
        dy = straight_left.ports["o2"].y - 2 * dy + r_bend
        circle.move((dx, dy))
    else:
        center = straight_left.ports["o2"].center
        circle.move((center[0], center[1] + r_bend))

    if circle_cladding:
        circle_cladding.move(circle.center)

    c.add_port("o1", port=straight_left.ports["o1"])
    c.add_port("o2", port=straight_right.ports["o2"])
    xs.add_bbox(c)
    if parity == -1:
        c = c.rotate(180)

    c.flatten()
    return c

disk_heater

disk_heater(
    radius: float = 10.0,
    gap: float = 0.2,
    wrap_angle_deg: float = 180.0,
    parity: int = 1,
    cross_section: CrossSectionSpec = "strip",
    heater_layer: LayerSpec = "HEATER",
    via_stack: ComponentSpec = "via_stack_heater_mtop",
    heater_width: float = 5.0,
    heater_extent: float = 2.0,
    via_width: float = 10.0,
    port_orientation: AngleInDegrees | None = 90,
) -> Component

Disk Resonator with top metal heater.

Parameters:

Name Type Description Default
radius float

disk resonator radius.

10.0
gap float

Distance between the bus straight and resonator.

0.2
wrap_angle_deg float

Angle in degrees between 0 and 180. determines how much the bus straight wraps along the resonator. 0 corresponds to a straight bus straight. 180 corresponds to a bus straight wrapped around half of the resonator.

180.0
parity 1 or -1

1, resonator left from bus straight, -1 resonator to the right.

1
cross_section CrossSectionSpec

cross_section spec.

'strip'
heater_layer LayerSpec

layer of the heater.

'HEATER'
via_stack ComponentSpec

via stack component.

'via_stack_heater_mtop'
heater_width float

width of the heater.

5.0
heater_extent float

length of heater beyond disk.

2.0
via_width float

size of the square via at the end of the heater.

10.0
port_orientation AngleInDegrees | None

in degrees.

90
Source code in gdsfactory/components/rings/disk.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
@gf.cell_with_module_name(schematic_function=ring_single_schematic, tags=["rings"])
def disk_heater(
    radius: float = 10.0,
    gap: float = 0.2,
    wrap_angle_deg: float = 180.0,
    parity: int = 1,
    cross_section: CrossSectionSpec = "strip",
    heater_layer: LayerSpec = "HEATER",
    via_stack: ComponentSpec = "via_stack_heater_mtop",
    heater_width: float = 5.0,
    heater_extent: float = 2.0,
    via_width: float = 10.0,
    port_orientation: AngleInDegrees | None = 90,
) -> Component:
    """Disk Resonator with top metal heater.

    Args:
       radius: disk resonator radius.
       gap: Distance between the bus straight and resonator.
       wrap_angle_deg: Angle in degrees between 0 and 180.
        determines how much the bus straight wraps along the resonator.
        0 corresponds to a straight bus straight.
        180 corresponds to a bus straight wrapped around half of the resonator.
       parity (1 or -1): 1, resonator left from bus straight, -1 resonator to the right.
       cross_section: cross_section spec.
       heater_layer: layer of the heater.
       via_stack: via stack component.
       heater_width: width of the heater.
       heater_extent: length of heater beyond disk.
       via_width: size of the square via at the end of the heater.
       port_orientation: in degrees.
    """
    c = gf.Component()
    xs = gf.get_cross_section(cross_section=cross_section)

    disk_instance = c << disk(
        radius=radius,
        gap=gap,
        wrap_angle_deg=wrap_angle_deg,
        parity=parity,
        cross_section=cross_section,
    )

    dx = disk_instance.xmax - disk_instance.xmin
    dy = disk_instance.ymax - disk_instance.ymin
    heater = c << gf.get_component(
        gf.components.rectangle,
        size=(dx + 2 * heater_extent, heater_width),
        layer=heater_layer,
    )
    heater.x = disk_instance.x
    heater.y = dy / 2 + disk_instance.ymin + (xs.width + gap) / 2

    via = gf.get_component(via_stack, size=(via_width, via_width))
    c1 = c << via
    c2 = c << via
    c1.xmax = heater.xmin
    c1.y = heater.y
    c2.xmin = heater.xmax
    c2.y = heater.y
    c.add_ports(disk_instance.ports)
    c.add_ports(c1.ports.filter(orientation=port_orientation), prefix="e1")
    c.add_ports(c2.ports.filter(orientation=port_orientation), prefix="e2")

    e1_ports = [p for p in c.ports if p.name and p.name.startswith("e1")]
    e2_ports = [p for p in c.ports if p.name and p.name.startswith("e2")]
    if e1_ports:
        c.create_pin(ports=e1_ports, name="e1")
    if e2_ports:
        c.create_pin(ports=e2_ports, name="e2")

    c.auto_rename_ports()
    return c

disk

disk_heater

disk_heater(
    radius: float = 10.0,
    gap: float = 0.2,
    wrap_angle_deg: float = 180.0,
    parity: int = 1,
    cross_section: CrossSectionSpec = "strip",
    heater_layer: LayerSpec = "HEATER",
    via_stack: ComponentSpec = "via_stack_heater_mtop",
    heater_width: float = 5.0,
    heater_extent: float = 2.0,
    via_width: float = 10.0,
    port_orientation: AngleInDegrees | None = 90,
) -> Component

Disk Resonator with top metal heater.

Parameters:

Name Type Description Default
radius float

disk resonator radius.

10.0
gap float

Distance between the bus straight and resonator.

0.2
wrap_angle_deg float

Angle in degrees between 0 and 180. determines how much the bus straight wraps along the resonator. 0 corresponds to a straight bus straight. 180 corresponds to a bus straight wrapped around half of the resonator.

180.0
parity 1 or -1

1, resonator left from bus straight, -1 resonator to the right.

1
cross_section CrossSectionSpec

cross_section spec.

'strip'
heater_layer LayerSpec

layer of the heater.

'HEATER'
via_stack ComponentSpec

via stack component.

'via_stack_heater_mtop'
heater_width float

width of the heater.

5.0
heater_extent float

length of heater beyond disk.

2.0
via_width float

size of the square via at the end of the heater.

10.0
port_orientation AngleInDegrees | None

in degrees.

90
Source code in gdsfactory/components/rings/disk.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
@gf.cell_with_module_name(schematic_function=ring_single_schematic, tags=["rings"])
def disk_heater(
    radius: float = 10.0,
    gap: float = 0.2,
    wrap_angle_deg: float = 180.0,
    parity: int = 1,
    cross_section: CrossSectionSpec = "strip",
    heater_layer: LayerSpec = "HEATER",
    via_stack: ComponentSpec = "via_stack_heater_mtop",
    heater_width: float = 5.0,
    heater_extent: float = 2.0,
    via_width: float = 10.0,
    port_orientation: AngleInDegrees | None = 90,
) -> Component:
    """Disk Resonator with top metal heater.

    Args:
       radius: disk resonator radius.
       gap: Distance between the bus straight and resonator.
       wrap_angle_deg: Angle in degrees between 0 and 180.
        determines how much the bus straight wraps along the resonator.
        0 corresponds to a straight bus straight.
        180 corresponds to a bus straight wrapped around half of the resonator.
       parity (1 or -1): 1, resonator left from bus straight, -1 resonator to the right.
       cross_section: cross_section spec.
       heater_layer: layer of the heater.
       via_stack: via stack component.
       heater_width: width of the heater.
       heater_extent: length of heater beyond disk.
       via_width: size of the square via at the end of the heater.
       port_orientation: in degrees.
    """
    c = gf.Component()
    xs = gf.get_cross_section(cross_section=cross_section)

    disk_instance = c << disk(
        radius=radius,
        gap=gap,
        wrap_angle_deg=wrap_angle_deg,
        parity=parity,
        cross_section=cross_section,
    )

    dx = disk_instance.xmax - disk_instance.xmin
    dy = disk_instance.ymax - disk_instance.ymin
    heater = c << gf.get_component(
        gf.components.rectangle,
        size=(dx + 2 * heater_extent, heater_width),
        layer=heater_layer,
    )
    heater.x = disk_instance.x
    heater.y = dy / 2 + disk_instance.ymin + (xs.width + gap) / 2

    via = gf.get_component(via_stack, size=(via_width, via_width))
    c1 = c << via
    c2 = c << via
    c1.xmax = heater.xmin
    c1.y = heater.y
    c2.xmin = heater.xmax
    c2.y = heater.y
    c.add_ports(disk_instance.ports)
    c.add_ports(c1.ports.filter(orientation=port_orientation), prefix="e1")
    c.add_ports(c2.ports.filter(orientation=port_orientation), prefix="e2")

    e1_ports = [p for p in c.ports if p.name and p.name.startswith("e1")]
    e2_ports = [p for p in c.ports if p.name and p.name.startswith("e2")]
    if e1_ports:
        c.create_pin(ports=e1_ports, name="e1")
    if e2_ports:
        c.create_pin(ports=e2_ports, name="e2")

    c.auto_rename_ports()
    return c

disk_heater

ring

ring

ring(
    radius: float = 10.0,
    width: float = 0.5,
    angle_resolution: float = 2.5,
    layer: LayerSpec = "WG",
    angle: float = 360,
    distance_resolution: float | None = None,
) -> Component

Returns a ring.

Parameters:

Name Type Description Default
radius float

ring radius.

10.0
width float

of the ring.

0.5
angle_resolution float

max number of degrees per point.

2.5
layer LayerSpec

layer.

'WG'
angle float

angular coverage of the ring

360
distance_resolution float | None

max distance between points. This is an alternate way to describe the resolution besides setting angle_resolution. If distance_resolution and angle_resolution are both set, distance_resolution determines the resolution.

None
Source code in gdsfactory/components/rings/ring.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@gf.cell_with_module_name(tags=["rings"])
def ring(
    radius: float = 10.0,
    width: float = 0.5,
    angle_resolution: float = 2.5,
    layer: LayerSpec = "WG",
    angle: float = 360,
    distance_resolution: float | None = None,
) -> Component:
    """Returns a ring.

    Args:
        radius: ring radius.
        width: of the ring.
        angle_resolution: max number of degrees per point.
        layer: layer.
        angle: angular coverage of the ring
        distance_resolution: max distance between points. This is an alternate way to describe the resolution besides setting angle_resolution. If distance_resolution and angle_resolution are both set, distance_resolution determines the resolution.
    """
    if radius < width / 2:
        raise ValueError(
            f"Error: radius is {radius} and width is {width}. radius must be >= width / 2."
        )

    if width < 0:
        raise ValueError(f"Error: width is {width}, but it must be nonnegative.")

    if angle > 360 or angle < 0:
        raise ValueError(f"Error: angle is {angle}, but it must be in [0, 360].")

    if distance_resolution is not None and distance_resolution <= 0:
        raise ValueError(
            f"Error: distance_resolution is {distance_resolution}, but it must be positive if given."
        )

    if distance_resolution is None and angle_resolution <= 0:
        raise ValueError(
            f"Error: angle_resolution is {angle_resolution}, but it must be positive."
        )

    D = gf.Component()
    inner_radius = radius - width / 2
    outer_radius = radius + width / 2
    if distance_resolution is not None:
        num_points = int(
            np.ceil(2 * pi * outer_radius * angle / 360 / distance_resolution)
        )
    else:
        num_points = int(np.ceil(angle / angle_resolution))
    t = np.linspace(0, angle, num_points + 1) * pi / 180
    inner_points_x = inner_radius * cos(t)
    inner_points_y = inner_radius * sin(t)
    outer_points_x = outer_radius * cos(t)
    outer_points_y = outer_radius * sin(t)
    xpts = np.concatenate([inner_points_x, outer_points_x[::-1]])
    ypts = np.concatenate([inner_points_y, outer_points_y[::-1]])
    D.add_polygon(points=list(zip(xpts, ypts, strict=False)), layer=layer)
    return D

ring

ring_asymmetric

ring_asymmetric(
    radius: float = 10.0,
    length_x: float = 2.0,
    length_y: float = 4.0,
    straight: ComponentSpec = "straight",
    bend: ComponentSpec = "bend_circular",
    cross_section: CrossSectionSpec = "strip",
) -> Component

An asymmetric ring with straight waveguides between the bends.

Parameters:

Name Type Description Default
radius float

of the ring.

10.0
length_x float

horizontal straight length.

2.0
length_y float

vertical straight length.

4.0
straight ComponentSpec

straight component spec.

'straight'
bend ComponentSpec

bend component spec.

'bend_circular'
cross_section CrossSectionSpec

cross_section spec.

'strip'
Source code in gdsfactory/components/rings/ring_crow.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
@gf.cell_with_module_name(tags=["rings"])
def ring_asymmetric(
    radius: float = 10.0,
    length_x: float = 2.0,
    length_y: float = 4.0,
    straight: ComponentSpec = "straight",
    bend: ComponentSpec = "bend_circular",
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """An asymmetric ring with straight waveguides between the bends.

    Args:
        radius: of the ring.
        length_x: horizontal straight length.
        length_y: vertical straight length.
        straight: straight component spec.
        bend: bend component spec.
        cross_section: cross_section spec.
    """
    ring = Component()

    bend_c = gf.get_component(bend, radius=radius, cross_section=cross_section)

    bend1 = ring.add_ref(bend_c, name="bot_right_bend_ring")
    bend2 = ring.add_ref(bend_c, name="top_right_bend_ring")
    bend3 = ring.add_ref(bend_c, name="top_left_bend_ring")
    bend4 = ring.add_ref(bend_c, name="bot_left_bend_ring")

    straight_hor_c = gf.get_component(
        straight, length=length_x, cross_section=cross_section
    )
    straight_ver_c = gf.get_component(
        straight, length=length_y, cross_section=cross_section
    )
    straight_hor1 = ring.add_ref(straight_hor_c, name="bot_hor_waveguide_ring")
    straight_hor2 = ring.add_ref(straight_hor_c, name="top_hor_waveguide_ring")
    straight_ver1 = ring.add_ref(straight_ver_c, name="right_ver_waveguide_ring")
    straight_ver2 = ring.add_ref(straight_ver_c, name="left_ver_waveguide_ring")

    bend1.connect("o1", straight_hor1.ports["o2"])
    straight_ver1.connect("o1", bend1.ports["o2"])
    bend2.connect("o1", straight_ver1.ports["o2"])
    straight_hor2.connect("o1", bend2.ports["o2"])
    bend3.connect("o1", straight_hor2.ports["o2"])
    straight_ver2.connect("o1", bend3.ports["o2"])
    bend4.connect("o1", straight_ver2.ports["o2"])

    return ring

ring_asymmetric

ring_crow

ring_asymmetric

ring_asymmetric(
    radius: float = 10.0,
    length_x: float = 2.0,
    length_y: float = 4.0,
    straight: ComponentSpec = "straight",
    bend: ComponentSpec = "bend_circular",
    cross_section: CrossSectionSpec = "strip",
) -> Component

An asymmetric ring with straight waveguides between the bends.

Parameters:

Name Type Description Default
radius float

of the ring.

10.0
length_x float

horizontal straight length.

2.0
length_y float

vertical straight length.

4.0
straight ComponentSpec

straight component spec.

'straight'
bend ComponentSpec

bend component spec.

'bend_circular'
cross_section CrossSectionSpec

cross_section spec.

'strip'
Source code in gdsfactory/components/rings/ring_crow.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
@gf.cell_with_module_name(tags=["rings"])
def ring_asymmetric(
    radius: float = 10.0,
    length_x: float = 2.0,
    length_y: float = 4.0,
    straight: ComponentSpec = "straight",
    bend: ComponentSpec = "bend_circular",
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """An asymmetric ring with straight waveguides between the bends.

    Args:
        radius: of the ring.
        length_x: horizontal straight length.
        length_y: vertical straight length.
        straight: straight component spec.
        bend: bend component spec.
        cross_section: cross_section spec.
    """
    ring = Component()

    bend_c = gf.get_component(bend, radius=radius, cross_section=cross_section)

    bend1 = ring.add_ref(bend_c, name="bot_right_bend_ring")
    bend2 = ring.add_ref(bend_c, name="top_right_bend_ring")
    bend3 = ring.add_ref(bend_c, name="top_left_bend_ring")
    bend4 = ring.add_ref(bend_c, name="bot_left_bend_ring")

    straight_hor_c = gf.get_component(
        straight, length=length_x, cross_section=cross_section
    )
    straight_ver_c = gf.get_component(
        straight, length=length_y, cross_section=cross_section
    )
    straight_hor1 = ring.add_ref(straight_hor_c, name="bot_hor_waveguide_ring")
    straight_hor2 = ring.add_ref(straight_hor_c, name="top_hor_waveguide_ring")
    straight_ver1 = ring.add_ref(straight_ver_c, name="right_ver_waveguide_ring")
    straight_ver2 = ring.add_ref(straight_ver_c, name="left_ver_waveguide_ring")

    bend1.connect("o1", straight_hor1.ports["o2"])
    straight_ver1.connect("o1", bend1.ports["o2"])
    bend2.connect("o1", straight_ver1.ports["o2"])
    straight_hor2.connect("o1", bend2.ports["o2"])
    bend3.connect("o1", straight_hor2.ports["o2"])
    straight_ver2.connect("o1", bend3.ports["o2"])
    bend4.connect("o1", straight_ver2.ports["o2"])

    return ring

ring_crow

ring_crow(
    gaps: tuple[float, ...] = (0.2, 0.2, 0.2, 0.2),
    radius: tuple[float, ...] = (10.0, 10.0, 10.0),
    bends: tuple[ComponentSpec, ...] | None = None,
    ring_cross_sections: tuple[CrossSectionSpec, ...] = (
        "strip",
        "strip",
        "strip",
    ),
    length_x: float = 0,
    lengths_y: tuple[float, ...] = (0, 0, 0),
    input_straight_cross_section: (
        CrossSectionSpec | None
    ) = None,
    output_straight_cross_section: (
        CrossSectionSpec | None
    ) = None,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Coupled ring resonators.

Parameters:

Name Type Description Default
gaps tuple[float, ...]

gap between rings.

(0.2, 0.2, 0.2, 0.2)
radius tuple[float, ...]

for each ring.

(10.0, 10.0, 10.0)
bends tuple[ComponentSpec, ...] | None

bend spec for each ring.

None
ring_cross_sections tuple[CrossSectionSpec, ...]

cross_section spec for each ring.

('strip', 'strip', 'strip')
length_x float

ring coupler length.

0
lengths_y tuple[float, ...]

vertical straight length.

(0, 0, 0)
input_straight_cross_section CrossSectionSpec | None

cross_section spec for input and output straight. Defaults to cross_section.

None
output_straight_cross_section CrossSectionSpec | None

cross_section spec for input and output straight. Defaults to cross_section.

None
cross_section CrossSectionSpec

cross_section spec for input and output straight.

--==ct==-- gap[N-1] | | sl sr ring[N-1] | | --==cb==-- gap[N-2]

. . .

--==ct==-- | | sl sr lengths_y[1], ring[1] | | --==cb==-- gap[1]

--==ct==-- | | sl sr lengths_y[0], ring[0] | | --==cb==-- gap[0]

length_x

'strip'
Source code in gdsfactory/components/rings/ring_crow.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
@gf.cell_with_module_name(schematic_function=ring_double_schematic, tags=["rings"])
def ring_crow(
    gaps: tuple[float, ...] = (0.2, 0.2, 0.2, 0.2),
    radius: tuple[float, ...] = (10.0, 10.0, 10.0),
    bends: tuple[ComponentSpec, ...] | None = None,
    ring_cross_sections: tuple[CrossSectionSpec, ...] = ("strip", "strip", "strip"),
    length_x: float = 0,
    lengths_y: tuple[float, ...] = (0, 0, 0),
    input_straight_cross_section: CrossSectionSpec | None = None,
    output_straight_cross_section: CrossSectionSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Coupled ring resonators.

    Args:
        gaps: gap between rings.
        radius: for each ring.
        bends: bend spec for each ring.
        ring_cross_sections: cross_section spec for each ring.
        length_x: ring coupler length.
        lengths_y: vertical straight length.
        input_straight_cross_section: cross_section spec for input and output straight. Defaults to cross_section.
        output_straight_cross_section: cross_section spec for input and output straight. Defaults to cross_section.
        cross_section: cross_section spec for input and output straight.

         --==ct==-- gap[N-1]
          |      |
          sl     sr ring[N-1]
          |      |
         --==cb==-- gap[N-2]

             .
             .
             .

         --==ct==--
          |      |
          sl     sr lengths_y[1], ring[1]
          |      |
         --==cb==-- gap[1]

         --==ct==--
          |      |
          sl     sr lengths_y[0], ring[0]
          |      |
         --==cb==-- gap[0]

          length_x
    """
    c = Component()

    bends = bends or (gf.c.bend_circular,) * len(radius)
    input_straight_cross_section = input_straight_cross_section or cross_section
    output_straight_cross_section = output_straight_cross_section or cross_section

    output_straight_cross_section = gf.get_cross_section(output_straight_cross_section)
    input_straight_cross_section = gf.get_cross_section(input_straight_cross_section)

    straight = gf.c.straight

    # Input bus
    input_straight = gf.get_component(
        straight,
        length=2 * radius[0] + length_x,
        cross_section=input_straight_cross_section,
    )
    input_straight_cross_section = gf.get_cross_section(input_straight_cross_section)
    input_straight_width = input_straight_cross_section.width

    input_straight_waveguide = c.add_ref(input_straight).movex(-radius[0])
    c.add_port(name="o1", port=input_straight_waveguide.ports["o1"])
    c.add_port(name="o2", port=input_straight_waveguide.ports["o2"])

    # Cascade rings
    cum_y_dist = input_straight_width / 2

    for gap, r, bend, cross_section, length_y in zip(
        gaps, radius, bends, ring_cross_sections, lengths_y, strict=False
    ):
        gap = gf.snap.snap_to_grid(gap, grid_factor=2)
        ring = ring_asymmetric(
            radius=r,
            length_x=length_x,
            length_y=length_y,
            straight=straight,
            bend=bend,
            cross_section=cross_section,
        )
        xs = gf.get_cross_section(cross_section)
        bend_width = xs.width
        ring_ref = c.add_ref(ring)
        ring_ref.movey(cum_y_dist + gap + bend_width / 2)
        cum_y_dist += gap + bend_width + 2 * r + length_y

    # Output bus
    output_straight = gf.get_component(
        straight,
        length=2 * radius[-1] + length_x,
        cross_section=output_straight_cross_section,
    )
    output_straight_width = output_straight_cross_section.width
    output_straight_waveguide = (
        c.add_ref(output_straight)
        .movey(cum_y_dist + gaps[-1] + output_straight_width / 2)
        .movex(-radius[-1])
    )
    c.add_port(name="o3", port=output_straight_waveguide.ports["o1"])
    c.add_port(name="o4", port=output_straight_waveguide.ports["o2"])
    return c

ring_crow

ring_crow_couplers

ring_crow_couplers

ring_crow_couplers(
    radius: Sequence[float] = (10.0,) * 3,
    bends: Sequence[ComponentSpec] = ("bend_circular",) * 3,
    ring_cross_sections: Sequence[CrossSectionSpec] = (
        "strip",
    )
    * 3,
    couplers: Sequence[ComponentSpec] = ("coupler",) * 4,
) -> Component

Coupled ring resonators with coupler components between gaps.

Parameters:

Name Type Description Default
radius Sequence[float]

for the bend and coupler.

(10.0,) * 3
bends Sequence[ComponentSpec]

bend specs.

('bend_circular',) * 3
ring_cross_sections Sequence[CrossSectionSpec]

cross_section for the ring.

('strip',) * 3
couplers Sequence[ComponentSpec]

coupling component between rings and bus.

--==ct==-- gap[N-1] <------- couplers[N-1] | | sl sr ring[N-1] | | --==cb==-- gap[N-2] <------- couplers[N-2]

. . .

--==ct==-- | | sl sr lengths_y[1], ring[1] | | --==cb==-- gap[1] <------- couplers[1] --==ct==-- | | sl sr lengths_y[0], ring[0] | | --==cb==-- gap[0] <------- couplers[0]

length_x

('coupler',) * 4
Source code in gdsfactory/components/rings/ring_crow_couplers.py
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
@gf.cell_with_module_name(schematic_function=ring_double_schematic, tags=["rings"])
def ring_crow_couplers(
    radius: Sequence[float] = (10.0,) * 3,
    bends: Sequence[ComponentSpec] = ("bend_circular",) * 3,
    ring_cross_sections: Sequence[CrossSectionSpec] = ("strip",) * 3,
    couplers: Sequence[ComponentSpec] = ("coupler",) * 4,
) -> Component:
    """Coupled ring resonators with coupler components between gaps.

    Args:
        radius: for the bend and coupler.
        bends: bend specs.
        ring_cross_sections: cross_section for the ring.
        couplers: coupling component between rings and bus.

         --==ct==-- gap[N-1]   <------- couplers[N-1]
          |      |
          sl     sr ring[N-1]
          |      |
         --==cb==-- gap[N-2]   <------- couplers[N-2]

             .
             .
             .

         --==ct==--
          |      |
          sl     sr lengths_y[1], ring[1]
          |      |
         --==cb==-- gap[1]
                                <------- couplers[1]
         --==ct==--
          |      |
          sl     sr lengths_y[0], ring[0]
          |      |
         --==cb==-- gap[0]      <------- couplers[0]

          length_x
    """
    c = Component()

    couplers_refs: list[ComponentReference] = []
    for cp in couplers:
        coupler_ref = c.add_ref(gf.get_component(cp))
        couplers_refs.append(coupler_ref)

    # Input bus
    c.add_port(name="o1", port=couplers_refs[0].ports["o1"])
    c.add_port(name="o2", port=couplers_refs[0].ports["o4"])

    # Cascade rings
    for index, (r, bend, cross_section) in enumerate(
        zip(radius, bends, ring_cross_sections, strict=False)
    ):
        # Add ring
        bend_c = gf.get_component(bend, radius=r, cross_section=cross_section)
        bend1 = c.add_ref(bend_c, name=f"bot_right_bend_ring_{index}")
        bend2 = c.add_ref(bend_c, name=f"top_right_bend_ring_{index}")
        bend3 = c.add_ref(bend_c, name=f"top_left_bend_ring_{index}")
        bend4 = c.add_ref(bend_c, name=f"bot_left_bend_ring_{index}")

        # We need to account for the chance that the top and bottom couplers
        # have a different length --> In this case we need to add straights
        coup1_extent = couplers_refs[index].xmax - couplers_refs[index].xmin
        coup2_extent = couplers_refs[index + 1].xmax - couplers_refs[index + 1].xmin

        if coup1_extent == coup2_extent:
            # Length of the couplers is the same -- we are good
            bend1.connect("o1", couplers_refs[index].ports["o3"])
            bend2.connect("o1", bend1.ports["o2"])
            couplers_refs[index + 1].connect("o4", bend2.ports["o2"])
            bend3.connect("o1", couplers_refs[index + 1].ports["o1"])
        else:
            str_len = np.abs(coup1_extent - coup2_extent) / 2
            str_sec = gf.components.straight(
                cross_section=cross_section, length=str_len
            )

            str1 = c << str_sec
            str2 = c << str_sec

            if coup1_extent > coup2_extent:
                # The straight are connected to coupler 2
                bend1.connect("o1", couplers_refs[index].ports["o3"])
                bend2.connect("o1", bend1.ports["o2"])
                str1.connect("o1", bend2.ports["o2"])
                couplers_refs[index + 1].connect("o4", str1.ports["o2"])
                str2.connect("o1", couplers_refs[index + 1].ports["o1"])
                bend3.connect("o1", str2.ports["o2"])
            else:
                # The straights are connected to coupler 1
                str1.connect("o1", couplers_refs[index].ports["o3"])
                str2.connect("o2", couplers_refs[index].ports["o2"])
                bend1.connect("o1", str1.ports["o2"])
                bend2.connect("o1", bend1.ports["o2"])
                couplers_refs[index + 1].connect("o4", bend2.ports["o2"])
                bend3.connect("o1", couplers_refs[index + 1].ports["o1"])
        bend4.connect("o1", bend3.ports["o2"])

    # Output bus
    c.add_port(name="o3", port=couplers_refs[-1].ports["o2"])
    c.add_port(name="o4", port=couplers_refs[-1].ports["o3"])
    return c

ring_crow_couplers

ring_double

ring_double

ring_double(
    gap: float = 0.2,
    gap_top: float | None = None,
    gap_bot: float | None = None,
    radius: float | None = None,
    length_x: float = 0.01,
    length_y: float = 0.01,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    coupler_ring: ComponentSpec = "coupler_ring",
    coupler_ring_top: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    length_extension: float | None = None,
) -> Component

Returns a double bus ring.

two couplers (ct: top, cb: bottom) connected with two vertical straights (sl: left, sr: right)

Parameters:

Name Type Description Default
gap float

gap between for coupler.

0.2
gap_top float | None

gap for the top coupler. Defaults to gap.

None
gap_bot float | None

gap for the bottom coupler. Defaults to gap.

None
radius float | None

for the bend and coupler.

None
length_x float

ring coupler length.

0.01
length_y float

vertical straight length.

0.01
bend ComponentSpec

90 degrees bend spec.

'bend_euler'
straight ComponentSpec

straight spec.

'straight'
coupler_ring ComponentSpec

ring coupler spec.

'coupler_ring'
coupler_ring_top ComponentSpec | None

top ring coupler spec. Defaults to coupler_ring.

None
cross_section CrossSectionSpec

cross_section spec.

'strip'
length_extension float | None

straight length extension at the end of the coupler bottom ports.

o2──────▲─────────o3 │gap_top xx──────▼─────────xxx xxx xxx

None

xx xxx x xxx xx xx▲ xx xx│length_y xx xx▼ xx xx xx length_x x xx ◄───────────────► x xx xxx xx xxx xxx──────▲─────────xxx │gap o1──────▼─────────◄──────────────► o4 length_extension

Source code in gdsfactory/components/rings/ring_double.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
@gf.cell_with_module_name(schematic_function=ring_double_schematic, tags=["rings"])
def ring_double(
    gap: float = 0.2,
    gap_top: float | None = None,
    gap_bot: float | None = None,
    radius: float | None = None,
    length_x: float = 0.01,
    length_y: float = 0.01,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    coupler_ring: ComponentSpec = "coupler_ring",
    coupler_ring_top: ComponentSpec | None = None,
    cross_section: CrossSectionSpec = "strip",
    length_extension: float | None = None,
) -> Component:
    """Returns a double bus ring.

    two couplers (ct: top, cb: bottom)
    connected with two vertical straights (sl: left, sr: right)

    Args:
        gap: gap between for coupler.
        gap_top: gap for the top coupler. Defaults to gap.
        gap_bot: gap for the bottom coupler. Defaults to gap.
        radius: for the bend and coupler.
        length_x: ring coupler length.
        length_y: vertical straight length.
        bend: 90 degrees bend spec.
        straight: straight spec.
        coupler_ring: ring coupler spec.
        coupler_ring_top: top ring coupler spec. Defaults to coupler_ring.
        cross_section: cross_section spec.
        length_extension: straight length extension at the end of the coupler bottom ports.

           o2──────▲─────────o3
                   │gap_top
           xx──────▼─────────xxx
          xxx                   xxx
        xxx                       xxx
       xx                           xxx
       x                             xxx
      xx                              xx▲
      xx                              xx│length_y
      xx                              xx▼
      xx                             xx
       xx          length_x          x
        xx     ◄───────────────►    x
         xx                       xxx
           xx                   xxx
            xxx──────▲─────────xxx
                     │gap
             o1──────▼─────────◄──────────────► o4
                                length_extension
    """
    gap_top = gap_top or gap
    gap_bot = gap_bot or gap
    coupler_component_bot = gf.get_component(
        coupler_ring,
        gap=gap_bot,
        radius=radius,
        length_x=length_x,
        cross_section=cross_section,
        straight=straight,
        bend=bend,
        length_extension=length_extension,
    )
    coupler_component_top = gf.get_component(
        coupler_ring_top or coupler_ring,
        gap=gap_top,
        radius=radius,
        length_x=length_x,
        cross_section=cross_section,
        straight=straight,
        bend=bend,
        length_extension=length_extension,
    )

    c = Component()
    cb = c.add_ref(coupler_component_bot)
    ct = c.add_ref(coupler_component_top)

    if length_y > 0:
        # Add vertical straights when length_y > 0
        straight_component = gf.get_component(
            straight,
            length=length_y,
            cross_section=cross_section,
        )
        sl = c << straight_component
        sr = c << straight_component

        sl.connect(port="o1", other=cb.ports["o2"])
        sr.connect(port="o2", other=cb.ports["o3"])
        ct.connect(port="o3", other=sl.ports["o2"])
    else:
        # When length_y=0, connect couplers directly
        ct.connect(port="o3", other=cb.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"])
    c.info["radius"] = coupler_component_bot.info["radius"]
    return c

ring_double

ring_double_bend_coupler

ring_double_bend_coupler

ring_double_bend_coupler(
    radius: float = 5.0,
    gap: float = 0.2,
    coupling_angle_coverage: float = 70.0,
    bend: ComponentAllAngleFactory = bend_circular_all_angle,
    length_x: float = 0.6,
    length_y: float = 0.6,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
) -> Component

Returns ring with double curved couplers.

Parameters:

Name Type Description Default
radius float

um.

5.0
gap float

um.

0.2
coupling_angle_coverage float

degrees.

70.0
bend ComponentAllAngleFactory

for bend.

bend_circular_all_angle
length_x float

horizontal straight length.

0.6
length_y float

vertical straight length.

0.6
cross_section_inner CrossSectionSpec

spec inner bend.

'strip'
cross_section_outer CrossSectionSpec

spec outer bend.

'strip'
Source code in gdsfactory/components/rings/ring_double_bend_coupler.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
@gf.cell_with_module_name(schematic_function=ring_double_schematic, tags=["rings"])
def ring_double_bend_coupler(
    radius: float = 5.0,
    gap: float = 0.2,
    coupling_angle_coverage: float = 70.0,
    bend: ComponentAllAngleFactory = bend_circular_all_angle,
    length_x: float = 0.6,
    length_y: float = 0.6,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
) -> Component:
    r"""Returns ring with double curved couplers.

    Args:
        radius: um.
        gap: um.
        coupling_angle_coverage: degrees.
        bend: for bend.
        length_x: horizontal straight length.
        length_y: vertical straight length.
        cross_section_inner: spec inner bend.
        cross_section_outer: spec outer bend.
    """
    c = Component()

    c_halfring = gf.c.coupler_ring_bend(
        radius=radius,
        coupler_gap=gap,
        coupling_angle_coverage=coupling_angle_coverage,
        length_x=length_x,
        cross_section_inner=cross_section_inner,
        cross_section_outer=cross_section_outer,
        bend=bend,
    )

    xi = gf.get_cross_section(cross_section_inner)
    xo = gf.get_cross_section(cross_section_outer)
    half_height = radius + xi.width / 2 + gap + xo.width + length_y / 2

    if c_halfring.ysize > half_height:
        raise ValueError(
            "The coupling_angle_coverage is too large for the given bend radius: "
            + "the coupling waveguides will overlap."
        )

    cb = c << c_halfring
    ct = c << c_halfring

    cross_section = cross_section_inner
    sy = gf.c.straight(length=length_y, cross_section=cross_section)
    sl = c << sy
    sr = c << sy

    sl.connect(port="o1", other=cb.ports["o2"])
    ct.connect(port="o3", other=sl.ports["o2"])
    sr.connect(port="o1", other=ct.ports["o2"])
    cb.connect(port="o3", other=sr.ports["o2"])

    c.add_port("o1", port=cb.ports["o1"])
    c.add_port("o2", port=ct.ports["o4"])
    c.add_port("o3", port=ct.ports["o1"])
    c.add_port("o4", port=cb.ports["o4"])
    c.flatten()
    return c

ring_double_bend_coupler

ring_double_heater

ring_double_heater(
    gap: float = 0.2,
    gap_top: float | None = None,
    gap_bot: float | None = None,
    radius: float | None = None,
    length_x: float = 1.0,
    length_y: float = 0.01,
    coupler_ring: ComponentSpec = "coupler_ring",
    coupler_ring_top: ComponentSpec | None = None,
    straight: ComponentSpec = "straight",
    bend: ComponentSpec = "bend_euler",
    cross_section_heater: CrossSectionSpec = "heater_metal",
    cross_section_waveguide_heater: CrossSectionSpec = "strip_heater_metal",
    cross_section: CrossSectionSpec = "strip",
    via_stack: ComponentSpec = "via_stack_heater_mtop_mini",
    port_orientation: AngleInDegrees | None = None,
    via_stack_offset: Float2 = (1, 0),
    via_stack_size: Float2 | None = None,
    with_drop: bool = True,
    length_extension: float | None = None,
    length_extension_top: float | None = None,
    length_extension_bot: float | None = None,
) -> Component

Returns a double bus ring with heater on top.

two couplers (ct: top, cb: bottom) connected with two vertical straights (sl: left, sr: right)

Parameters:

Name Type Description Default
gap float

gap between for coupler.

0.2
gap_top float | None

gap for the top coupler. Defaults to gap.

None
gap_bot float | None

gap for the bottom coupler. Defaults to gap.

None
radius float | None

for the bend and coupler.

None
length_x float

ring coupler length.

1.0
length_y float

vertical straight length.

0.01
coupler_ring ComponentSpec

ring coupler spec.

'coupler_ring'
coupler_ring_top ComponentSpec | None

ring coupler spec for coupler away from vias (defaults to coupler_ring)

None
straight ComponentSpec

straight spec.

'straight'
bend ComponentSpec

bend spec.

'bend_euler'
cross_section_heater CrossSectionSpec

for heater.

'heater_metal'
cross_section_waveguide_heater CrossSectionSpec

for waveguide with heater.

'strip_heater_metal'
cross_section CrossSectionSpec

for regular waveguide.

'strip'
via_stack ComponentSpec

for heater to routing metal.

'via_stack_heater_mtop_mini'
port_orientation AngleInDegrees | None

for electrical ports to promote from via_stack.

None
via_stack_size Float2 | None

size of via_stack.

None
via_stack_offset Float2

x,y offset for via_stack.

(1, 0)
with_drop bool

adds drop ports.

True
length_extension float | None

straight length extension at the end of the coupler bottom ports.

None
length_extension_top float | None

straight length extension at the end of the coupler top ports.

None
length_extension_bot float | None

straight length extension at the end of the coupler bottom ports.

o2──────▲─────────o3 │gap_top xx──────▼─────────xxx xxx xxx

None

xx xxx x xxx xx xx▲ xx xx│length_y xx xx▼ xx xx xx length_x x xx ◄───────────────► x xx xxx xx xxx xxx──────▲─────────xxx │gap o1──────▼─────────o4

Source code in gdsfactory/components/rings/ring_heater.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
@gf.cell_with_module_name(schematic_function=ring_double_schematic, tags=["rings"])
def ring_double_heater(
    gap: float = 0.2,
    gap_top: float | None = None,
    gap_bot: float | None = None,
    radius: float | None = None,
    length_x: float = 1.0,
    length_y: float = 0.01,
    coupler_ring: ComponentSpec = "coupler_ring",
    coupler_ring_top: ComponentSpec | None = None,
    straight: ComponentSpec = "straight",
    bend: ComponentSpec = "bend_euler",
    cross_section_heater: CrossSectionSpec = "heater_metal",
    cross_section_waveguide_heater: CrossSectionSpec = "strip_heater_metal",
    cross_section: CrossSectionSpec = "strip",
    via_stack: ComponentSpec = "via_stack_heater_mtop_mini",
    port_orientation: AngleInDegrees | None = None,
    via_stack_offset: Float2 = (1, 0),
    via_stack_size: Float2 | None = None,
    with_drop: bool = True,
    length_extension: float | None = None,
    length_extension_top: float | None = None,
    length_extension_bot: float | None = None,
) -> Component:
    """Returns a double bus ring with heater on top.

    two couplers (ct: top, cb: bottom)
    connected with two vertical straights (sl: left, sr: right)

    Args:
        gap: gap between for coupler.
        gap_top: gap for the top coupler. Defaults to gap.
        gap_bot: gap for the bottom coupler. Defaults to gap.
        radius: for the bend and coupler.
        length_x: ring coupler length.
        length_y: vertical straight length.
        coupler_ring: ring coupler spec.
        coupler_ring_top: ring coupler spec for coupler away from vias (defaults to coupler_ring)
        straight: straight spec.
        bend: bend spec.
        cross_section_heater: for heater.
        cross_section_waveguide_heater: for waveguide with heater.
        cross_section: for regular waveguide.
        via_stack: for heater to routing metal.
        port_orientation: for electrical ports to promote from via_stack.
        via_stack_size: size of via_stack.
        via_stack_offset: x,y offset for via_stack.
        with_drop: adds drop ports.
        length_extension: straight length extension at the end of the coupler bottom ports.
        length_extension_top: straight length extension at the end of the coupler top ports.
        length_extension_bot: straight length extension at the end of the coupler bottom ports.

           o2──────▲─────────o3
                   │gap_top
           xx──────▼─────────xxx
          xxx                   xxx
        xxx                       xxx
       xx                           xxx
       x                             xxx
      xx                              xx▲
      xx                              xx│length_y
      xx                              xx▼
      xx                             xx
       xx          length_x          x
        xx     ◄───────────────►    x
         xx                       xxx
           xx                   xxx
            xxx──────▲─────────xxx
                     │gap
             o1──────▼─────────o4
    """
    gap_top = gap_top or gap
    gap_bot = gap_bot or gap

    gap = gf.snap.snap_to_grid(gap, grid_factor=2)
    gap_top = gf.snap.snap_to_grid(gap_top, grid_factor=2)
    gap_bot = gf.snap.snap_to_grid(gap_bot, grid_factor=2)

    coupler_ring_top = coupler_ring_top or coupler_ring

    if length_extension_bot is None:
        length_extension_bot = length_extension

    if length_extension_top is None:
        length_extension_top = length_extension

    coupler_component = gf.get_component(
        coupler_ring,
        gap=gap_bot,
        radius=radius,
        length_x=length_x,
        bend=bend,
        cross_section=cross_section,
        cross_section_bend=cross_section_waveguide_heater,
        length_extension=length_extension_bot,
    )
    coupler_component_top = gf.get_component(
        coupler_ring_top,
        gap=gap_top,
        radius=radius,
        length_x=length_x,
        bend=bend,
        cross_section=cross_section,
        cross_section_bend=cross_section_waveguide_heater,
        length_extension=length_extension_top,
    )
    straight_component = gf.get_component(
        straight,
        length=length_y,
        cross_section=cross_section_waveguide_heater,
    )

    c = Component()

    cb = c.add_ref(coupler_component)
    sl = c.add_ref(straight_component)
    sr = c.add_ref(straight_component)
    c.add_port("o1", port=cb.ports["o1"])
    c.add_port("o2", port=cb.ports["o4"])

    if with_drop:
        ct = c.add_ref(coupler_component_top)
        sl.connect(port="o1", other=cb.ports["o2"])
        ct.connect(port="o3", other=sl.ports["o2"])
        sr.connect(port="o2", other=ct.ports["o2"])
        c.add_port("o3", port=ct.ports["o4"])
        c.add_port("o4", port=ct.ports["o1"])
        heater_top = c << gf.get_component(
            straight,
            length=length_x,
            cross_section=cross_section_heater,
        )
        heater_top.connect("e1", ct["e1"])

    else:
        straight_top = gf.get_component(
            straight,
            length=length_x,
            cross_section=cross_section_waveguide_heater,
        )
        bend = gf.get_component(
            bend,
            radius=radius,
            cross_section=cross_section_waveguide_heater,
        )
        bl = c << bend
        br = c << bend
        st = c << straight_top

        sl.connect(port="o1", other=cb.ports["o2"])
        bl.connect(port="o2", other=sl.ports["o2"])

        st.connect(port="o2", other=bl.ports["o1"])
        br.connect(port="o2", other=st.ports["o1"])
        sr.connect(port="o1", other=br.ports["o1"])
        sr.connect(port="o2", other=cb.ports["o3"])

    if via_stack_size:
        via = gf.get_component(via_stack, size=via_stack_size)

    else:
        via = gf.get_component(via_stack)

    c1 = c << via
    c2 = c << via
    c1.xmax = -length_x / 2 + cb.x - via_stack_offset[0]
    c2.xmin = +length_x / 2 + cb.x + via_stack_offset[0]
    c1.movey(via_stack_offset[1])
    c2.movey(via_stack_offset[1])

    p1 = c1.ports.filter(orientation=port_orientation)
    p2 = c2.ports.filter(orientation=port_orientation)
    valid_orientations = {p.orientation for p in via.ports}

    if not p1:
        raise ValueError(
            f"No ports found for port_orientation {port_orientation} in {valid_orientations}"
        )

    c.flatten()
    c.add_ports(p1, prefix="l_")
    c.add_ports(p2, prefix="r_")

    l_ports = [p for p in c.ports if p.name and p.name.startswith("l_")]
    r_ports = [p for p in c.ports if p.name and p.name.startswith("r_")]
    if l_ports:
        c.create_pin(ports=l_ports, name="l")
    if r_ports:
        c.create_pin(ports=r_ports, name="r")

    return c

ring_double_heater

ring_double_pn

ring_double_pn(
    add_gap: float = 0.3,
    drop_gap: float = 0.3,
    radius: float = 5.0,
    doping_angle: float = 85,
    cross_section: CrossSectionFactory = rib,
    pn_cross_section: CrossSectionFactory = cross_section_pn,
    doped_heater: bool = True,
    doped_heater_angle_buffer: float = 10,
    doped_heater_layer: LayerSpec = "NPP",
    doped_heater_width: float = 0.5,
    doped_heater_waveguide_offset: float = 2.175,
    heater_vias: ComponentSpec = _heater_vias,
    with_drop: bool = True,
    **kwargs: Any
) -> gf.Component

Returns add-drop pn ring with optional doped heater.

Parameters:

Name Type Description Default
add_gap float

gap to add waveguide. Bottom gap.

0.3
drop_gap float

gap to drop waveguide. Top gap.

0.3
radius float

for the bend and coupler.

5.0
doping_angle float

angle in degrees representing portion of ring that is doped.

85
length_x

ring coupler length.

required
length_y

vertical straight length.

required
cross_section CrossSectionFactory

cross_section spec for non-PN doped rib waveguide sections.

rib
pn_cross_section CrossSectionFactory

cross section of pn junction.

cross_section_pn
doped_heater bool

boolean for if we include doped heater or not.

True
doped_heater_angle_buffer float

angle in degrees buffering heater from pn junction.

10
doped_heater_layer LayerSpec

doping layer for heater.

'NPP'
doped_heater_width float

width of doped heater.

0.5
doped_heater_waveguide_offset float

distance from the center of the ring waveguide to the center of the doped heater.

2.175
heater_vias ComponentSpec

components specifications for heater vias

_heater_vias
with_drop bool

boolean for if we include drop waveguide or not.

True
kwargs Any

cross_section settings.

{}
Source code in gdsfactory/components/rings/ring_pn.py
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
@gf.cell_with_module_name(schematic_function=ring_double_schematic, tags=["rings"])
def ring_double_pn(
    add_gap: float = 0.3,
    drop_gap: float = 0.3,
    radius: float = 5.0,
    doping_angle: float = 85,
    cross_section: CrossSectionFactory = rib,
    pn_cross_section: CrossSectionFactory = cross_section_pn,
    doped_heater: bool = True,
    doped_heater_angle_buffer: float = 10,
    doped_heater_layer: LayerSpec = "NPP",
    doped_heater_width: float = 0.5,
    doped_heater_waveguide_offset: float = 2.175,
    heater_vias: ComponentSpec = _heater_vias,
    with_drop: bool = True,
    **kwargs: Any,
) -> gf.Component:
    """Returns add-drop pn ring with optional doped heater.

    Args:
        add_gap: gap to add waveguide. Bottom gap.
        drop_gap: gap to drop waveguide. Top gap.
        radius: for the bend and coupler.
        doping_angle: angle in degrees representing portion of ring that is doped.
        length_x: ring coupler length.
        length_y: vertical straight length.
        cross_section: cross_section spec for non-PN doped rib waveguide sections.
        pn_cross_section: cross section of pn junction.
        doped_heater: boolean for if we include doped heater or not.
        doped_heater_angle_buffer: angle in degrees buffering heater from pn junction.
        doped_heater_layer: doping layer for heater.
        doped_heater_width: width of doped heater.
        doped_heater_waveguide_offset: distance from the center of the ring waveguide to the center of the doped heater.
        heater_vias: components specifications for heater vias
        with_drop: boolean for if we include drop waveguide or not.
        kwargs: cross_section settings.

    """
    add_gap = gf.snap.snap_to_grid(add_gap, grid_factor=2)
    drop_gap = gf.snap.snap_to_grid(drop_gap, grid_factor=2)
    c = gf.Component()

    pn_cross_section_ = gf.get_cross_section(pn_cross_section, **kwargs)
    cross_section_ = gf.get_cross_section(cross_section, **kwargs)
    cross_section_ = cross_section_.copy(**kwargs)

    heater_vias = gf.get_component(heater_vias)
    undoping_angle = 180 - doping_angle

    th_waveguide_path = gf.Path()
    th_waveguide_path.append(
        gf.path.straight(length=2 * radius * np.sin(np.pi / 360 * undoping_angle))
    )
    th_waveguide = c << th_waveguide_path.extrude(cross_section=cross_section_)
    th_waveguide.x = 0
    th_waveguide.y = (
        -radius
        - add_gap
        - th_waveguide.ports["o1"].width / 2
        - pn_cross_section_.width / 2
    )

    doped_path = gf.Path()
    doped_path.append(gf.path.arc(radius=radius, angle=-doping_angle))
    undoped_path = gf.Path()
    undoped_path.append(gf.path.arc(radius=radius, angle=undoping_angle))

    r = gf.ComponentAllAngle()
    left_doped_ring_ref = r.add_ref_off_grid(
        doped_path.extrude(cross_section=pn_cross_section_, all_angle=True)
    )
    right_doped_ring_ref = r.add_ref_off_grid(
        doped_path.extrude(cross_section=pn_cross_section_, all_angle=True)
    )
    bottom_undoped_ring_ref = r.add_ref_off_grid(
        undoped_path.extrude(cross_section=cross_section_, all_angle=True)
    )
    top_undoped_ring_ref = r.add_ref_off_grid(
        undoped_path.extrude(cross_section=cross_section_, all_angle=True)
    )

    bottom_undoped_ring_ref.rotate(-undoping_angle / 2)
    bottom_undoped_ring_ref.x = th_waveguide.x

    left_doped_ring_ref.connect("o1", bottom_undoped_ring_ref.ports["o1"])
    right_doped_ring_ref.connect("o2", bottom_undoped_ring_ref.ports["o2"])
    top_undoped_ring_ref.connect("o2", left_doped_ring_ref.ports["o2"])

    ring = c.add_ref_off_grid(r)
    ring.center = (0, 0)

    drop_waveguide_dy = (
        radius
        + drop_gap
        + th_waveguide.ports["o1"].width / 2
        + pn_cross_section_.width / 2
    )

    if doped_heater:
        heater_radius = radius - doped_heater_waveguide_offset
        heater_path = gf.Path()
        heater_path.append(
            gf.path.arc(
                radius=heater_radius, angle=undoping_angle - doped_heater_angle_buffer
            )
        )

        heater = heater_path.extrude(width=0.5, layer=doped_heater_layer)

        bottom_heater_ref = c << heater
        bottom_heater_ref.rotate(-(undoping_angle - doped_heater_angle_buffer) / 2)
        bottom_heater_ref.x = th_waveguide.x
        bottom_heater_ref.y = th_waveguide.y + (
            doped_heater_waveguide_offset + doped_heater_width / 2 + add_gap
        )

        bottom_l_heater_via = c << heater_vias
        bottom_r_heater_via = c << heater_vias
        bottom_l_heater_via.x = bottom_heater_ref.ports["o1"].x
        bottom_l_heater_via.y = bottom_heater_ref.ports["o1"].y
        bottom_r_heater_via.x = bottom_heater_ref.ports["o2"].x
        bottom_r_heater_via.y = bottom_heater_ref.ports["o2"].y

        top_heater_ref = c << heater
        top_heater_ref.rotate(180 - (undoping_angle - doped_heater_angle_buffer) / 2)
        top_heater_ref.x = th_waveguide.x
        top_heater_ref.y = drop_waveguide_dy - (
            doped_heater_waveguide_offset + doped_heater_width / 2 + drop_gap
        )

        top_l_heater_via = c << heater_vias
        top_r_heater_via = c << heater_vias
        top_l_heater_via.x = top_heater_ref.ports["o1"].x
        top_l_heater_via.y = top_heater_ref.ports["o1"].y
        top_r_heater_via.x = top_heater_ref.ports["o2"].x
        top_r_heater_via.y = top_heater_ref.ports["o2"].y

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

    htr_top_sig = c.add_port(name="htr_top_sig", port=top_l_heater_via["e2"])
    htr_top_gnd = c.add_port(name="htr_top_gnd", port=top_r_heater_via["e2"])
    htr_bot_sig = c.add_port(name="htr_bot_sig", port=bottom_l_heater_via["e2"])
    htr_bot_gnd = c.add_port(name="htr_bot_gnd", port=bottom_r_heater_via["e2"])
    c.create_pin(ports=[htr_top_sig], name="htr_top_sig")
    c.create_pin(ports=[htr_top_gnd], name="htr_top_gnd")
    c.create_pin(ports=[htr_bot_sig], name="htr_bot_sig")
    c.create_pin(ports=[htr_bot_gnd], name="htr_bot_gnd")

    if with_drop:
        drop_waveguide_path = gf.Path()
        drop_waveguide_path.append(
            gf.path.straight(length=2 * radius * np.sin(np.pi / 360 * undoping_angle))
        )
        drop_waveguide = c << drop_waveguide_path.extrude(cross_section=cross_section_)
        drop_waveguide.x = 0
        drop_waveguide.y = drop_waveguide_dy
        c.add_port("o3", port=drop_waveguide.ports["o2"])
        c.add_port("o4", port=drop_waveguide.ports["o1"])
    c.flatten()
    return c

ring_double_pn

ring_single

ring_single

ring_single(
    gap: float = 0.2,
    radius: float | None = None,
    length_x: float = 4.0,
    length_y: float = 0.6,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    coupler_ring: ComponentSpec = "coupler_ring",
    cross_section: CrossSectionSpec = "strip",
    length_extension: float | None = None,
) -> gf.Component

Returns a single ring resonator with a directional coupler.

This component creates a ring resonator that consists of: - A directional coupler (cb) at the bottom - Two vertical straights (sl, sr) on the left and right sides - Two bends (bl, br) connecting the vertical straights - A horizontal straight (st) at the top

The ring resonator is commonly used in photonic integrated circuits for: - Wavelength filtering - Optical modulation - Sensing applications - Optical switching

Parameters:

Name Type Description Default
gap float

Gap between the ring and the straight waveguide in the coupler (μm).

0.2
radius float | None

Radius of the ring bends (μm). If None, it will use the radius from the cross section.

None
length_x float

Length of the horizontal straight section (μm).

4.0
length_y float

Length of the vertical straight sections (μm).

0.6
bend ComponentSpec

Component spec for the 90-degree bends. Default is "bend_euler".

'bend_euler'
straight ComponentSpec

Component spec for the straight waveguides. Default is "straight".

'straight'
coupler_ring ComponentSpec

Component spec for the ring coupler. Default is "coupler_ring".

'coupler_ring'
cross_section CrossSectionSpec

Cross section spec for all waveguides. Default is "strip".

'strip'
length_extension float | None

straight length extension at the end of the coupler bottom ports.

None

Returns:

Name Type Description
Component Component

A gdsfactory Component containing the ring resonator with: - Two ports: "o1" (input) and "o2" (through) - All waveguide sections properly connected - Cross section applied to all waveguides

Raises:

Type Description
ValueError

If length_x or length_y is negative.

    xxxxxxxxxxxxx
xxxxx           xxxx

xxx xxx xxx xxx xx xxx x xxx xx xx▲ xx xx│length_y xx xx▼ xx xx xx length_x x xx ◄───────────────► x xx xxx xx xxx xxx──────▲─────────xxx │gap o1──────▼─────────o2◄──────────────► length_extension

Source code in gdsfactory/components/rings/ring_single.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
@gf.cell_with_module_name(schematic_function=ring_single_schematic, tags=["rings"])
def ring_single(
    gap: float = 0.2,
    radius: float | None = None,
    length_x: float = 4.0,
    length_y: float = 0.6,
    bend: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
    coupler_ring: ComponentSpec = "coupler_ring",
    cross_section: CrossSectionSpec = "strip",
    length_extension: float | None = None,
) -> gf.Component:
    """Returns a single ring resonator with a directional coupler.

    This component creates a ring resonator that consists of:
    - A directional coupler (cb) at the bottom
    - Two vertical straights (sl, sr) on the left and right sides
    - Two bends (bl, br) connecting the vertical straights
    - A horizontal straight (st) at the top

    The ring resonator is commonly used in photonic integrated circuits for:
    - Wavelength filtering
    - Optical modulation
    - Sensing applications
    - Optical switching

    Args:
        gap: Gap between the ring and the straight waveguide in the coupler (μm).
        radius: Radius of the ring bends (μm). If None, it will use the radius from the cross section.
        length_x: Length of the horizontal straight section (μm).
        length_y: Length of the vertical straight sections (μm).
        bend: Component spec for the 90-degree bends. Default is "bend_euler".
        straight: Component spec for the straight waveguides. Default is "straight".
        coupler_ring: Component spec for the ring coupler. Default is "coupler_ring".
        cross_section: Cross section spec for all waveguides. Default is "strip".
        length_extension: straight length extension at the end of the coupler bottom ports.

    Returns:
        Component: A gdsfactory Component containing the ring resonator with:
            - Two ports: "o1" (input) and "o2" (through)
            - All waveguide sections properly connected
            - Cross section applied to all waveguides

    Raises:
        ValueError: If length_x or length_y is negative.

                    xxxxxxxxxxxxx
                xxxxx           xxxx
              xxx                   xxx
            xxx                       xxx
           xx                           xxx
           x                             xxx
          xx                              xx▲
          xx                              xx│length_y
          xx                              xx▼
          xx                             xx
           xx          length_x          x
            xx     ◄───────────────►    x
             xx                       xxx
               xx                   xxx
                xxx──────▲─────────xxx
                         │gap
                 o1──────▼─────────o2◄──────────────►
                                     length_extension
    """
    if length_y < 0:
        raise ValueError(f"length_y={length_y} must be >= 0")

    if length_x < 0:
        raise ValueError(f"length_x={length_x} must be >= 0")

    # Create main component
    c = gf.Component()

    settings = dict(
        gap=gap,
        radius=radius,
        length_x=length_x,
        cross_section=cross_section,
        bend=bend,
        straight=straight,
    )

    if length_extension is not None:
        settings["length_extension"] = length_extension

    # Create and place the coupler
    coupler = gf.get_component(coupler_ring, settings=settings)
    cb = c << coupler

    # Create waveguide components
    b = gf.get_component(bend, cross_section=cross_section, radius=radius)

    # Place waveguide components
    bl = c << b  # Left bend
    br = c << b  # Right bend

    if length_y > 0 and length_x > 0:
        sx = gf.get_component(straight, length=length_x, cross_section=cross_section)
        sy = gf.get_component(straight, length=length_y, cross_section=cross_section)
        st = c << sx
        sl = c << sy
        sr = c << sy

        sl.connect(port="o1", other=cb.ports["o2"])
        bl.connect(port="o2", other=sl.ports["o2"])
        st.connect(port="o2", other=bl.ports["o1"])
        br.connect(port="o2", other=st.ports["o1"])
        sr.connect(port="o1", other=br.ports["o1"])
        sr.connect(port="o2", other=cb.ports["o3"])
    elif length_y > 0:
        sy = gf.get_component(straight, length=length_y, cross_section=cross_section)
        sl = c << sy
        sr = c << sy

        sl.connect(port="o1", other=cb.ports["o2"])
        bl.connect(port="o2", other=sl.ports["o2"])
        br.connect(port="o2", other=bl.ports["o1"])
        sr.connect(port="o1", other=br.ports["o1"])
        sr.connect(port="o2", other=cb.ports["o3"])
    elif length_x > 0:
        sx = gf.get_component(straight, length=length_x, cross_section=cross_section)
        st = c << sx

        bl.connect(port="o2", other=cb.ports["o2"])
        st.connect(port="o2", other=bl.ports["o1"])
        br.connect(port="o2", other=st.ports["o1"])
        br.connect(port="o1", other=cb.ports["o3"])
    else:
        bl.connect(port="o2", other=cb.ports["o2"])
        br.connect(port="o2", other=bl.ports["o1"])
        br.connect(port="o1", other=cb.ports["o3"])

    # Add ports
    c.add_port("o2", port=cb.ports["o4"])
    c.add_port("o1", port=cb.ports["o1"])
    c.info["radius"] = coupler.info["radius"]
    return c

ring_single

ring_single_array

ring_single_array

ring_single_array(
    ring: ComponentSpec = "ring_single",
    spacing: float = 15.0,
    list_of_dicts: tuple[dict[str, Any], ...] | None = None,
    cross_section: CrossSectionSpec = "strip",
) -> Component

Ring of single bus connected with straights.

Parameters:

Name Type Description Default
ring ComponentSpec

ring spec.

'ring_single'
spacing float

between rings.

15.0
list_of_dicts tuple[dict[str, Any], ...] | None

settings for each ring.

None
cross_section CrossSectionSpec

spec.

__ ____ | | | | | | length_y | | | | | | --======-- spacing ----==gap==--

length_x

'strip'
Source code in gdsfactory/components/rings/ring_single_array.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@gf.cell_with_module_name(tags=["rings"])
def ring_single_array(
    ring: ComponentSpec = "ring_single",
    spacing: float = 15.0,
    list_of_dicts: tuple[dict[str, Any], ...] | None = None,
    cross_section: CrossSectionSpec = "strip",
) -> Component:
    """Ring of single bus connected with straights.

    Args:
        ring: ring spec.
        spacing: between rings.
        list_of_dicts: settings for each ring.
        cross_section: spec.

           ______               ______
          |      |             |      |
          |      |  length_y   |      |
          |      |             |      |
         --======-- spacing ----==gap==--

          length_x
    """
    list_of_dicts = list_of_dicts or _list_of_dicts
    c = Component()
    settings0 = list_of_dicts[0]
    ring1 = c << gf.get_component(ring, cross_section=cross_section, **settings0)

    ring0 = ring1
    wg = gf.c.straight(length=spacing, cross_section=cross_section)

    for settings in list_of_dicts[1:]:
        ringi = c << gf.get_component(ring, cross_section=cross_section, **settings)
        wgi = c << wg
        wgi.connect("o1", ring0.ports["o2"])
        ringi.connect("o1", wgi.ports["o2"])
        ring0 = ringi

    c.add_port("o1", port=ring1.ports["o1"])
    c.add_port("o2", port=ringi.ports["o2"])
    return c

ring_single_array

ring_single_bend_coupler

coupler_bend

coupler_bend(
    radius: float | None = None,
    coupler_gap: float = 0.2,
    coupling_angle_coverage: float = 120.0,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    bend: AnyComponentFactory = bend_circular_all_angle,
    bend_output: ComponentSpec = "bend_euler",
) -> Component

Compact curved coupler with bezier escape.

TODO: fix for euler bends.

Parameters:

Name Type Description Default
radius float | None

um.

None
coupler_gap float

um.

0.2
coupling_angle_coverage float

degrees.

120.0
cross_section_inner CrossSectionSpec

spec inner bend.

'strip'
cross_section_outer CrossSectionSpec

spec outer bend.

'strip'
bend AnyComponentFactory

for bend.

bend_circular_all_angle
bend_output ComponentSpec

for bend.

r 4 | | | / ___3 | / /

'bend_euler'
Source code in gdsfactory/components/rings/ring_single_bend_coupler.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
@gf.cell_with_module_name(schematic_function=coupler_schematic, tags=["rings"])
def coupler_bend(
    radius: float | None = None,
    coupler_gap: float = 0.2,
    coupling_angle_coverage: float = 120.0,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    bend: AnyComponentFactory = bend_circular_all_angle,
    bend_output: ComponentSpec = "bend_euler",
) -> Component:
    r"""Compact curved coupler with bezier escape.

    TODO: fix for euler bends.

    Args:
        radius: um.
        coupler_gap: um.
        coupling_angle_coverage: degrees.
        cross_section_inner: spec inner bend.
        cross_section_outer: spec outer bend.
        bend: for bend.
        bend_output: for bend.

            r   4
            |   |
            |  / ___3
            | / /
        2____/ /
        1_____/
    """
    c = Component()

    xi = gf.get_cross_section(cross_section_inner)
    xo = gf.get_cross_section(cross_section_outer)

    angle_inner = 90
    angle_outer = coupling_angle_coverage / 2
    gap = coupler_gap

    width = xo.width / 2 + xi.width / 2
    spacing = gap + width

    if radius is None:
        radius = xi.radius or xo.radius
        assert radius is not None, "cross_section must have a radius"

    bend90_inner_right = gf.get_component(
        bend,  # type: ignore[arg-type]
        radius=radius,
        cross_section=cross_section_inner,
        angle=angle_inner,
    )
    bend_output_right = gf.get_component(
        bend,  # type: ignore[arg-type]
        radius=radius + spacing,
        cross_section=cross_section_outer,
        angle=angle_outer,
    )
    bend_inner_ref = c.add_ref_off_grid(bend90_inner_right)
    bend_output_ref = c.add_ref_off_grid(bend_output_right)

    output = gf.get_component(
        bend_output, angle=angle_outer, cross_section=cross_section_outer
    )
    output_ref = c.add_ref_off_grid(output)
    output_ref.connect("o1", bend_output_ref.ports["o2"], mirror=True)

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

    c.add_port("o1", port=bend_output_ref.ports["o1"])
    c.add_port("o2", port=bend_inner_ref.ports["o1"])
    c.add_port("o3", port=output_ref.ports["o2"])
    c.add_port("o4", port=bend_inner_ref.ports["o2"])
    return c

coupler_ring_bend

coupler_ring_bend(
    radius: float | None = None,
    coupler_gap: float = 0.2,
    coupling_angle_coverage: float = 90.0,
    length_x: float = 0.0,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    bend: AnyComponentFactory = bend_circular_all_angle,
    bend_output: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
) -> Component

Two back-to-back coupler_bend.

Parameters:

Name Type Description Default
radius float | None

um. Default is None, which uses the default radius of the cross_section.

None
coupler_gap float

um.

0.2
angle_inner

of the inner bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.

required
angle_outer

of the outer bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.

required
coupling_angle_coverage float

degrees.

90.0
length_x float

horizontal straight length.

0.0
cross_section_inner CrossSectionSpec

spec inner bend.

'strip'
cross_section_outer CrossSectionSpec

spec outer bend.

'strip'
bend AnyComponentFactory

for bend.

bend_circular_all_angle
bend_output ComponentSpec

for bend.

'bend_euler'
straight ComponentSpec

for straight.

'straight'
Source code in gdsfactory/components/rings/ring_single_bend_coupler.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
@gf.cell_with_module_name(schematic_function=coupler_ring_schematic, tags=["rings"])
def coupler_ring_bend(
    radius: float | None = None,
    coupler_gap: float = 0.2,
    coupling_angle_coverage: float = 90.0,
    length_x: float = 0.0,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    bend: AnyComponentFactory = bend_circular_all_angle,
    bend_output: ComponentSpec = "bend_euler",
    straight: ComponentSpec = "straight",
) -> Component:
    r"""Two back-to-back coupler_bend.

    Args:
        radius: um. Default is None, which uses the default radius of the cross_section.
        coupler_gap: um.
        angle_inner: of the inner bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.
        angle_outer: of the outer bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.
        coupling_angle_coverage: degrees.
        length_x: horizontal straight length.
        cross_section_inner: spec inner bend.
        cross_section_outer: spec outer bend.
        bend: for bend.
        bend_output: for bend.
        straight: for straight.
    """
    c = Component()
    cp = coupler_bend(
        radius=radius,
        coupler_gap=coupler_gap,
        coupling_angle_coverage=coupling_angle_coverage,
        cross_section_inner=cross_section_inner,
        cross_section_outer=cross_section_outer,
        bend=bend,
        bend_output=bend_output,
    )
    sin = gf.get_component(straight, length=length_x, cross_section=cross_section_inner)
    sout = gf.get_component(
        straight, length=length_x, cross_section=cross_section_outer
    )

    coupler_right = c << cp
    coupler_left = c << cp
    straight_inner = c << sin
    straight_inner.movex(-length_x / 2)
    straight_outer = c << sout
    straight_outer.movex(-length_x / 2)

    coupler_left.connect("o1", straight_outer.ports["o1"])
    straight_inner.connect("o1", coupler_left.ports["o2"])
    coupler_right.connect("o2", straight_inner.ports["o2"], mirror=True)
    straight_outer.connect("o2", coupler_right.ports["o1"])

    c.add_port("o1", port=coupler_left.ports["o3"])
    c.add_port("o2", port=coupler_left.ports["o4"])
    c.add_port("o4", port=coupler_right.ports["o3"])
    c.add_port("o3", port=coupler_right.ports["o4"])
    # c.flatten()
    return c

ring_single_bend_coupler

ring_single_bend_coupler(
    radius: float = 5.0,
    gap: float = 0.2,
    coupling_angle_coverage: float = 180.0,
    bend_all_angle: AnyComponentFactory = bend_circular_all_angle,
    bend: ComponentSpec = "bend_circular",
    bend_output: ComponentSpec = "bend_euler",
    length_x: float = 0.6,
    length_y: float = 0.6,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    **kwargs: Any
) -> Component

Returns ring with curved coupler.

TODO: enable euler bends.

Parameters:

Name Type Description Default
radius float

um.

5.0
gap float

um.

0.2
coupling_angle_coverage float

degrees.

180.0
angle_inner

of the inner bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.

required
angle_outer

of the outer bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.

required
bend_all_angle AnyComponentFactory

for bend.

bend_circular_all_angle
bend ComponentSpec

for bend.

'bend_circular'
bend_output ComponentSpec

for bend.

'bend_euler'
length_x float

horizontal straight length.

0.6
length_y float

vertical straight length.

0.6
cross_section_inner CrossSectionSpec

spec inner bend.

'strip'
cross_section_outer CrossSectionSpec

spec outer bend.

'strip'
kwargs Any

cross_section settings.

{}
Source code in gdsfactory/components/rings/ring_single_bend_coupler.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
@gf.cell_with_module_name(schematic_function=ring_single_schematic, tags=["rings"])
def ring_single_bend_coupler(
    radius: float = 5.0,
    gap: float = 0.2,
    coupling_angle_coverage: float = 180.0,
    bend_all_angle: AnyComponentFactory = bend_circular_all_angle,
    bend: ComponentSpec = "bend_circular",
    bend_output: ComponentSpec = "bend_euler",
    length_x: float = 0.6,
    length_y: float = 0.6,
    cross_section_inner: CrossSectionSpec = "strip",
    cross_section_outer: CrossSectionSpec = "strip",
    **kwargs: Any,
) -> Component:
    r"""Returns ring with curved coupler.

    TODO: enable euler bends.

    Args:
        radius: um.
        gap: um.
        coupling_angle_coverage: degrees.
        angle_inner: of the inner bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.
        angle_outer: of the outer bend, from beginning to end. Depending on the bend chosen, gap may not be preserved.
        bend_all_angle: for bend.
        bend: for bend.
        bend_output: for bend.
        length_x: horizontal straight length.
        length_y: vertical straight length.
        cross_section_inner: spec inner bend.
        cross_section_outer: spec outer bend.
        kwargs: cross_section settings.
    """
    c = Component()

    coupler = coupler_ring_bend(
        radius=radius,
        coupler_gap=gap,
        coupling_angle_coverage=coupling_angle_coverage,
        length_x=length_x,
        cross_section_inner=cross_section_inner,
        cross_section_outer=cross_section_outer,
        bend=bend_all_angle,
        bend_output=bend_output,
    )
    cb = c << coupler

    cross_section = cross_section_inner
    straight = gf.c.straight
    sx = gf.get_component(
        straight, length=length_x, cross_section=cross_section, **kwargs
    )
    sy = gf.get_component(
        straight, length=length_y, cross_section=cross_section, **kwargs
    )
    b = gf.get_component(bend, cross_section=cross_section, radius=radius, **kwargs)
    sl = c << sy
    sr = c << sy
    bl = c << b
    br = c << b
    st = c << sx

    sl.connect(port="o1", other=cb["o2"])
    bl.connect(port="o2", other=sl["o2"], mirror=True)
    st.connect(port="o2", other=bl["o1"])
    sr.connect(port="o1", other=br["o1"])
    sr.connect(port="o2", other=cb["o3"])
    br.connect(port="o2", other=st["o1"], mirror=True)

    c.add_port("o2", port=cb["o4"])
    c.add_port("o1", port=cb["o1"])
    c.flatten()
    return c

ring_single_bend_coupler

ring_single_dut

ring_single_dut

ring_single_dut(
    component: ComponentSpec = "straight",
    gap: float = 0.2,
    length_x: float = 4,
    length_y: float = 0,
    radius: float | None = None,
    coupler: ComponentSpec = "coupler_ring",
    bend: ComponentSpec = "bend_euler",
    with_component: bool = True,
    port_name: str = "o1",
    length_extension: float | None = None,
    **kwargs: Any
) -> Component

Single bus ring made of two couplers (ct: top, cb: bottom) connected.

with two vertical straights (wyl: left, wyr: right) (Component Under Test) in the middle to extract loss from quality factor.

Parameters:

Name Type Description Default
component ComponentSpec

device under test.

'straight'
gap float

in um.

0.2
length_x float

in um.

4
length_y float

in um.

0
radius float | None

in um. Default is None, which uses the default radius of the cross_section.

None
coupler ComponentSpec

coupler function.

'coupler_ring'
bend ComponentSpec

bend function.

'bend_euler'
with_component bool

True adds component. False adds waveguide.

True
port_name str

for component input.

'o1'
length_extension float | None

optional length extension for the coupler bottom ports.

None
kwargs Any

cross_section settings.

{}

Parameters:

Name Type Description Default
with_component bool

if False changes component for just a straight.

bl-wt-br | | length_y wl component | | --==cb==-- gap

length_x

True
Source code in gdsfactory/components/rings/ring_single_dut.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
@gf.cell_with_module_name(schematic_function=ring_single_schematic, tags=["rings"])
def ring_single_dut(
    component: ComponentSpec = "straight",
    gap: float = 0.2,
    length_x: float = 4,
    length_y: float = 0,
    radius: float | None = None,
    coupler: ComponentSpec = "coupler_ring",
    bend: ComponentSpec = "bend_euler",
    with_component: bool = True,
    port_name: str = "o1",
    length_extension: float | None = None,
    **kwargs: Any,
) -> Component:
    """Single bus ring made of two couplers (ct: top, cb: bottom) connected.

    with two vertical straights (wyl: left, wyr: right) (Component Under Test) in
    the middle to extract loss from quality factor.

    Args:
        component: device under test.
        gap: in um.
        length_x: in um.
        length_y: in um.
        radius: in um. Default is None, which uses the default radius of the cross_section.
        coupler: coupler function.
        bend: bend function.
        with_component: True adds component. False adds waveguide.
        port_name: for component input.
        length_extension: optional length extension for the coupler bottom ports.
        kwargs: cross_section settings.

    Args:
        with_component: if False changes component for just a straight.

          bl-wt-br
          |      | length_y
          wl     component
          |      |
         --==cb==-- gap

          length_x
    """
    component = gf.get_component(component)
    assert_on_2x_grid(gap)

    coupler = gf.get_component(
        coupler,
        gap=gap,
        length_x=length_x,
        radius=radius,
        length_extension=length_extension,
        **kwargs,
    )

    component_xsize = component.xsize
    straight_side = gf.c.straight(length=length_y + component_xsize, **kwargs)
    straight_top = gf.c.straight(length=length_x, **kwargs)
    bend = gf.get_component(bend, radius=radius, **kwargs)

    c = Component()
    cb = c << coupler
    wl = c << straight_side
    dut = c << component if with_component else c << straight_side
    bl = c << bend
    br = c << bend
    wt = c << straight_top

    wl.connect(port="o2", other=cb.ports["o2"])
    bl.connect(port="o2", other=wl.ports["o1"])

    wt.connect(port="o1", other=bl.ports["o1"])
    br.connect(port="o2", other=wt.ports["o2"])
    dut.connect(port=port_name, other=br.ports["o1"])

    c.add_port("o2", port=cb.ports["o4"])
    c.add_port("o1", port=cb.ports["o1"])
    return c

ring_single_dut

ring_single_heater module-attribute

ring_single_heater = partial(
    ring_double_heater, with_drop=False
)

ring_single_heater

ring_single_pn

ring_single_pn(
    gap: float = 0.3,
    radius: float = 5.0,
    doping_angle: float = 250,
    cross_section: CrossSectionSpec = rib,
    pn_cross_section: CrossSectionSpec = cross_section_pn,
    doped_heater: bool = True,
    doped_heater_angle_buffer: float = 10,
    doped_heater_layer: LayerSpec = "NPP",
    doped_heater_width: float = 0.5,
    doped_heater_waveguide_offset: float = 1.175,
    heater_vias: ComponentSpec = _heater_vias,
    pn_vias: ComponentSpec = "via_stack_slab_m3",
    pn_vias_width: float = 3,
) -> gf.Component

Returns single pn ring with optional doped heater.

Parameters:

Name Type Description Default
gap float

gap between for coupler.

0.3
radius float

for the bend and coupler.

5.0
doping_angle float

angle in degrees representing portion of ring that is doped.

250
length_x

ring coupler length.

required
length_y

vertical straight length.

required
cross_section CrossSectionSpec

cross_section spec for non-PN doped rib waveguide sections.

rib
pn_cross_section CrossSectionSpec

cross section of pn junction.

cross_section_pn
doped_heater bool

boolean for if we include doped heater or not.

True
doped_heater_angle_buffer float

angle in degrees buffering heater from pn junction.

10
doped_heater_layer LayerSpec

doping layer for heater.

'NPP'
doped_heater_width float

width of doped heater.

0.5
doped_heater_waveguide_offset float

distance from the center of the ring waveguide to the center of the doped heater.

1.175
heater_vias ComponentSpec

components specifications for heater vias.

_heater_vias
pn_vias ComponentSpec

components specifications for pn vias.

'via_stack_slab_m3'
pn_vias_width float

width of pn vias.

3
Source code in gdsfactory/components/rings/ring_pn.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
@gf.cell_with_module_name(schematic_function=ring_single_schematic, tags=["rings"])
def ring_single_pn(
    gap: float = 0.3,
    radius: float = 5.0,
    doping_angle: float = 250,
    cross_section: CrossSectionSpec = rib,
    pn_cross_section: CrossSectionSpec = cross_section_pn,
    doped_heater: bool = True,
    doped_heater_angle_buffer: float = 10,
    doped_heater_layer: LayerSpec = "NPP",
    doped_heater_width: float = 0.5,
    doped_heater_waveguide_offset: float = 1.175,
    heater_vias: ComponentSpec = _heater_vias,
    pn_vias: ComponentSpec = "via_stack_slab_m3",
    pn_vias_width: float = 3,
) -> gf.Component:
    """Returns single pn ring with optional doped heater.

    Args:
        gap: gap between for coupler.
        radius: for the bend and coupler.
        doping_angle: angle in degrees representing portion of ring that is doped.
        length_x: ring coupler length.
        length_y: vertical straight length.
        cross_section: cross_section spec for non-PN doped rib waveguide sections.
        pn_cross_section: cross section of pn junction.
        doped_heater: boolean for if we include doped heater or not.
        doped_heater_angle_buffer: angle in degrees buffering heater from pn junction.
        doped_heater_layer: doping layer for heater.
        doped_heater_width: width of doped heater.
        doped_heater_waveguide_offset: distance from the center of the ring waveguide to the center of the doped heater.
        heater_vias: components specifications for heater vias.
        pn_vias: components specifications for pn vias.
        pn_vias_width: width of pn vias.
    """
    gap = gf.snap.snap_to_grid(gap, grid_factor=2)
    c = gf.Component()

    undoping_angle = 360 - doping_angle

    pn_xs = gf.get_cross_section(pn_cross_section)
    bus_waveguide_path = gf.Path()
    bus_waveguide_path.append(
        gf.path.straight(length=2 * radius * np.sin(np.pi / 360 * undoping_angle))
    )
    bus_waveguide = c << bus_waveguide_path.extrude(cross_section=cross_section)
    bus_waveguide.x = 0
    bus_waveguide.y = (
        -radius
        - gap
        - bus_waveguide.ports["o1"].width / 2
        - pn_xs.width / 2
        + 0.576  # adjust gap # TODO: remove this
    )

    r = gf.Component()
    doped_path = gf.Path()
    doped_path.append(gf.path.arc(radius=radius, angle=-doping_angle))
    undoped_path = gf.Path()
    undoped_path.append(gf.path.arc(radius=radius, angle=undoping_angle))

    doped_ring_ref = r << doped_path.extrude(cross_section=pn_xs, all_angle=False)
    undoped_ring_ref = r << undoped_path.extrude(
        cross_section=cross_section, all_angle=False
    )
    undoped_ring_ref.rotate(-undoping_angle / 2)
    undoped_ring_ref.center = (0, 0)
    doped_ring_ref.connect("o1", undoped_ring_ref.ports["o1"])

    via = gf.get_component(pn_vias, size=(pn_vias_width, pn_vias_width))
    gnd = r << via
    gnd.x = doped_ring_ref.ports["e1_top"].x
    gnd.y = doped_ring_ref.ports["e1_top"].y

    sig = r << via
    sig.x = doped_ring_ref.ports["e2_bot"].x
    sig.y = doped_ring_ref.ports["e2_bot"].y
    r.add_port("sig", port=sig["e2"])
    r.add_port("gnd", port=gnd["e2"])

    ring = c << r
    ring.center = (0, 0)

    if doped_heater:
        heater_radius = radius - doped_heater_waveguide_offset
        heater_path = gf.Path()
        heater_path.append(
            gf.path.arc(
                radius=heater_radius, angle=undoping_angle - doped_heater_angle_buffer
            )
        )

        bottom_heater_ref = c << heater_path.extrude(
            width=0.5, layer=doped_heater_layer
        )
        bottom_heater_ref.rotate(-(undoping_angle - doped_heater_angle_buffer) / 2)
        bottom_heater_ref.x = bus_waveguide.x
        bottom_heater_ref.y = (
            bus_waveguide.y
            + doped_heater_waveguide_offset
            + doped_heater_width / 2
            + gap
            + radius / 4
        )

        heater_vias = gf.get_component(heater_vias)

        bottom_l_heater_via = c << heater_vias
        bottom_r_heater_via = c << heater_vias
        bottom_l_heater_via.xmin = bottom_heater_ref.ports["o1"].x
        bottom_l_heater_via.ymax = bottom_heater_ref.ports["o1"].y

        bottom_r_heater_via.xmax = bottom_heater_ref.ports["o2"].x
        bottom_r_heater_via.ymax = bottom_heater_ref.ports["o2"].y

        c.add_port(name="heater_sig", port=bottom_l_heater_via["e4"])
        c.add_port(name="heater_gnd", port=bottom_r_heater_via["e4"])

    c.add_port("o1", port=bus_waveguide.ports["o1"])
    c.add_port("o2", port=bus_waveguide.ports["o2"])
    c.add_ports(ring.ports)

    elec_ports = [p for p in c.ports if p.name and p.port_type == "electrical"]
    for p in elec_ports:
        c.create_pin(ports=[p], name=p.name)

    c.flatten()
    return c

ring_single_pn

shapes

C

C

C(
    width: float = 1.0,
    size: Size = (10.0, 20.0),
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component

C geometry with ports on both ends.

based on phidl.

Parameters:

Name Type Description Default
width float

of the line.

1.0
size Size

length and height of the base.

(10.0, 20.0)
layer LayerSpec

layer spec.

'WG'
port_type str

optical or electrical.


'electrical'
Source code in gdsfactory/components/shapes/C.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@gf.cell_with_module_name(tags=["shapes"])
def C(
    width: float = 1.0,
    size: Size = (10.0, 20.0),
    layer: LayerSpec = "WG",
    port_type: str = "electrical",
) -> Component:
    """C geometry with ports on both ends.

    based on phidl.

    Args:
        width: of the line.
        size: length and height of the base.
        layer: layer spec.
        port_type: optical or electrical.

         ______
        |       o1
        |   ___
        |  |
        |  |___
        ||<---> size[0]
        |______ o2
    """
    layer = gf.get_layer(layer)
    c = Component()
    w = width / 2
    s1, s2 = size
    points = [
        (-w, -w),
        (s1, -w),
        (s1, w),
        (w, w),
        (w, s2 - w),
        (s1, s2 - w),
        (s1, s2 + w),
        (-w, s2 + w),
        (-w, -w),
    ]
    c.add_polygon(points, layer=layer)

    for name, center in (("o1", (s1, s2)), ("o2", (s1, 0))):
        c.add_port(
            name=name,
            center=center,
            width=width,
            orientation=0,
            layer=layer,
            port_type=port_type,
        )
    if port_type == "electrical":
        for port in c.ports:
            c.create_pin(ports=[port], name=port.name)
    return c

C

L

L

L(
    width: int | float = 1,
    size: tuple[int, int] = (10, 20),
    layer: LayerSpec = "MTOP",
    port_type: str = "electrical",
) -> Component

Generates an 'L' geometry with ports on both ends.

Based on phidl.

Parameters:

Name Type Description Default
width int | float

of the line.

1
size tuple[int, int]

length and height of the base.

(10, 20)
layer LayerSpec

spec.

'MTOP'
port_type str

for port.

'electrical'
Source code in gdsfactory/components/shapes/L.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@gf.cell_with_module_name(tags=["shapes"])
def L(
    width: int | float = 1,
    size: tuple[int, int] = (10, 20),
    layer: LayerSpec = "MTOP",
    port_type: str = "electrical",
) -> Component:
    """Generates an 'L' geometry with ports on both ends.

    Based on phidl.

    Args:
        width: of the line.
        size: length and height of the base.
        layer: spec.
        port_type: for port.
    """
    D = Component()
    w = width / 2
    s1, s2 = size
    points = [(-w, -w), (s1, -w), (s1, w), (w, w), (w, s2), (-w, s2), (-w, -w)]
    D.add_polygon(points, layer=layer)
    D.add_port(
        name="e1",
        center=(0, s2),
        width=width,
        orientation=90,
        port_type=port_type,
        layer=layer,
    )
    D.add_port(
        name="e2",
        center=(s1, 0),
        width=width,
        orientation=0,
        port_type=port_type,
        layer=layer,
    )
    if port_type == "electrical":
        for port in D.ports:
            D.create_pin(ports=[port], name=port.name)
    return D

L

circle

circle

circle(
    radius: float = 10.0,
    angle_resolution: float = 2.5,
    layer: LayerSpec = "WG",
) -> Component

Generate a circle geometry.

Parameters:

Name Type Description Default
radius float

of the circle.

10.0
angle_resolution float

number of degrees per point.

2.5
layer LayerSpec

layer.

'WG'
Source code in gdsfactory/components/shapes/circle.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@gf.cell_with_module_name(tags=["shapes"])
def circle(
    radius: float = 10.0,
    angle_resolution: float = 2.5,
    layer: LayerSpec = "WG",
) -> Component:
    """Generate a circle geometry.

    Args:
        radius: of the circle.
        angle_resolution: number of degrees per point.
        layer: layer.
    """
    if radius <= 0:
        raise ValueError(f"radius={radius} must be > 0")
    c = Component()
    num_points = int(np.round(360.0 / angle_resolution)) + 1
    theta = np.deg2rad(np.linspace(0, 360, num_points, endpoint=True))
    points = np.stack((radius * np.cos(theta), radius * np.sin(theta)), axis=-1)
    c.add_polygon(points=points, layer=layer)
    return c

circle

circle_wave

circle_wave

circle_wave(
    radius: float = 10.0,
    amplitude: float = 1.0,
    n_oscillations: int = 8,
    angle_resolution: float = 1.0,
    layer: LayerSpec = "WG",
) -> Component

Returns a circle with a sinusoidal boundary variation.

The boundary radius varies as r(theta) = radius + amplitude * sin(n * theta).

Parameters:

Name Type Description Default
radius float

mean radius.

10.0
amplitude float

amplitude of sinusoidal modulation.

1.0
n_oscillations int

number of oscillations around the boundary.

8
angle_resolution float

degrees per point.

1.0
layer LayerSpec

layer spec.

'WG'
Source code in gdsfactory/components/shapes/circle_wave.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@gf.cell_with_module_name(tags=["shapes"])
def circle_wave(
    radius: float = 10.0,
    amplitude: float = 1.0,
    n_oscillations: int = 8,
    angle_resolution: float = 1.0,
    layer: LayerSpec = "WG",
) -> Component:
    """Returns a circle with a sinusoidal boundary variation.

    The boundary radius varies as r(theta) = radius + amplitude * sin(n * theta).

    Args:
        radius: mean radius.
        amplitude: amplitude of sinusoidal modulation.
        n_oscillations: number of oscillations around the boundary.
        angle_resolution: degrees per point.
        layer: layer spec.
    """
    c = Component()
    n_points = int(np.round(360.0 / angle_resolution)) + 1
    theta = np.linspace(0, 2 * np.pi, n_points, endpoint=True)
    r = radius + amplitude * np.sin(n_oscillations * theta)
    points = np.stack((r * np.cos(theta), r * np.sin(theta)), axis=-1)
    c.add_polygon(points=points, layer=layer)
    return c

circle_wave

compass

compass

compass(
    size: Size = (4.0, 2.0),
    layer: LayerSpec = "WG",
    port_type: str | None = "electrical",
    port_inclusion: float = 0.0,
    port_orientations: Ints | None = (180, 90, 0, -90),
    auto_rename_ports: bool = True,
) -> Component

Rectangle with ports on each edge (north, south, east, and west).

Parameters:

Name Type Description Default
size Size

rectangle size.

(4.0, 2.0)
layer LayerSpec

tuple (int, int).

'WG'
port_type str | None

optical, electrical.

'electrical'
port_inclusion float

from edge.

0.0
port_orientations Ints | None

list of port_orientations to add. None does not add ports.

(180, 90, 0, -90)
auto_rename_ports bool

auto rename ports.

True
Source code in gdsfactory/components/shapes/compass.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@gf.cell_with_module_name(tags=["shapes"])
def compass(
    size: Size = (4.0, 2.0),
    layer: LayerSpec = "WG",
    port_type: str | None = "electrical",
    port_inclusion: float = 0.0,
    port_orientations: Ints | None = (180, 90, 0, -90),
    auto_rename_ports: bool = True,
) -> Component:
    """Rectangle with ports on each edge (north, south, east, and west).

    Args:
        size: rectangle size.
        layer: tuple (int, int).
        port_type: optical, electrical.
        port_inclusion: from edge.
        port_orientations: list of port_orientations to add. None does not add ports.
        auto_rename_ports: auto rename ports.
    """
    c = gf.Component()
    _temp = snap_to_grid2x(size)
    dx = float(_temp[0])
    dy = float(_temp[1])
    port_orientations_list = port_orientations if port_orientations is not None else []

    if dx <= 0 or dy <= 0:
        raise ValueError(f"dx={dx} and dy={dy} must be > 0")

    points = [
        (-dx / 2.0, -dy / 2.0),
        (-dx / 2.0, dy / 2),
        (dx / 2, dy / 2),
        (dx / 2, -dy / 2.0),
    ]

    c.add_polygon(points, layer=layer)

    if port_type:
        for port_orientation in port_orientations_list:
            if port_orientation not in valid_port_orientations:
                raise ValueError(
                    f"{port_orientation=} must be in {valid_port_orientations}"
                )

        if 180 in port_orientations_list:
            c.add_port(
                name="e1",
                center=(-dx / 2 + port_inclusion, 0),
                width=dy,
                orientation=180,
                layer=layer,
                port_type=port_type,
            )
        if 90 in port_orientations_list:
            c.add_port(
                name="e2",
                center=(0, dy / 2 - port_inclusion),
                width=dx,
                orientation=90,
                layer=layer,
                port_type=port_type,
            )
        if 0 in port_orientations_list:
            c.add_port(
                name="e3",
                center=(dx / 2 - port_inclusion, 0),
                width=dy,
                orientation=0,
                layer=layer,
                port_type=port_type,
            )
        if -90 in port_orientations_list or 270 in port_orientations_list:
            c.add_port(
                name="e4",
                center=(0, -dy / 2 + port_inclusion),
                width=dx,
                orientation=-90,
                layer=layer,
                port_type=port_type,
            )

        if auto_rename_ports:
            c.auto_rename_ports()
        if port_type == "electrical":
            elec = [p for p in c.ports if p.port_type == "electrical"]
            if elec:
                c.create_pin(ports=elec, name="pad")
    return c

compass

cross

cross

cross(
    length: float = 10.0,
    width: float = 3.0,
    layer: LayerSpec = "WG",
    port_type: str | None = None,
) -> Component

Returns a cross from two rectangles of length and width.

Parameters:

Name Type Description Default
length float

float Length of the cross from one end to the other.

10.0
width float

float Width of the arms of the cross.

3.0
layer LayerSpec

layer for geometry.

'WG'
port_type str | None

None, optical, electrical.

None
Source code in gdsfactory/components/shapes/cross.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@gf.cell_with_module_name(tags=["shapes"])
def cross(
    length: float = 10.0,
    width: float = 3.0,
    layer: LayerSpec = "WG",
    port_type: str | None = None,
) -> Component:
    """Returns a cross from two rectangles of length and width.

    Args:
        length: float Length of the cross from one end to the other.
        width: float Width of the arms of the cross.
        layer: layer for geometry.
        port_type: None, optical, electrical.
    """
    layer = gf.get_layer(layer)
    c = gf.Component()
    R = gf.components.rectangle(size=(width, length), layer=layer)
    r1 = c.add_ref(R).rotate(90)
    r2 = c.add_ref(R)
    r1.center = (0, 0)
    r2.center = (0, 0)
    c.flatten()

    if port_type:
        prefix = "o" if port_type == "optical" else "e"
        c.add_port(
            f"{prefix}1",
            width=width,
            layer=layer,
            orientation=0,
            center=(+length / 2, 0),
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}2",
            width=width,
            layer=layer,
            orientation=180,
            center=(-length / 2, 0),
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}3",
            width=width,
            layer=layer,
            orientation=90,
            center=(0, length / 2),
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}4",
            width=width,
            layer=layer,
            orientation=270,
            center=(0, -length / 2),
            port_type=port_type,
        )
        c.auto_rename_ports()
    if port_type == "electrical":
        elec = [p for p in c.ports if p.port_type == "electrical"]
        if elec:
            c.create_pin(ports=elec, name="pad")
    return c

cross

dash

dash

dash(
    width: float = 10.0,
    width_end: float = 1.0,
    length: float = 20.0,
    taper_length: float = 5.0,
    tip_length: float = 2.0,
    n_bezier_points: int = 30,
    layer: LayerSpec = "WG",
) -> Component

Returns a dash shape with Bezier-curved tapered tips.

An elongated shape wider in the middle (width) that tapers via Bezier curves to a narrower tip (width_end) at each end. Based on the pyNISTtoolbox Dash pattern.

Parameters:

Name Type Description Default
width float

width at the center/body of the dash.

10.0
width_end float

width at the tips.

1.0
length float

total length of the straight body section.

20.0
taper_length float

length of each tapered transition.

5.0
tip_length float

length of each rounded tip beyond the taper.

2.0
n_bezier_points int

points per Bezier curve segment.

30
layer LayerSpec

layer spec.

'WG'
Source code in gdsfactory/components/shapes/dash.py
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@gf.cell_with_module_name(tags=["shapes"])
def dash(
    width: float = 10.0,
    width_end: float = 1.0,
    length: float = 20.0,
    taper_length: float = 5.0,
    tip_length: float = 2.0,
    n_bezier_points: int = 30,
    layer: LayerSpec = "WG",
) -> Component:
    """Returns a dash shape with Bezier-curved tapered tips.

    An elongated shape wider in the middle (width) that tapers via
    Bezier curves to a narrower tip (width_end) at each end. Based on
    the pyNISTtoolbox Dash pattern.

    Args:
        width: width at the center/body of the dash.
        width_end: width at the tips.
        length: total length of the straight body section.
        taper_length: length of each tapered transition.
        tip_length: length of each rounded tip beyond the taper.
        n_bezier_points: points per Bezier curve segment.
        layer: layer spec.
    """
    c = Component()
    W = width
    Wend = width_end
    L = length
    Ltap = taper_length
    Lt = tip_length

    alpha = np.pi / 2 - np.arctan((W - Wend) / (2 * Ltap))

    # Top tip
    pxup = Lt * np.cos(alpha)
    pyup = Lt * np.sin(alpha)
    px = [-Wend / 2, -Wend / 2 + pxup, Wend / 2 - pxup, Wend / 2]
    py = [L / 2 + Ltap, L / 2 + Ltap + pyup, L / 2 + Ltap + pyup, L / 2 + Ltap]
    xtip_t, ytip_t = _bezier3(px, py, n_bezier_points)

    # Left upper taper
    px = [-W / 2, -W / 2, -W / 2, -Wend / 2]
    py = [L / 3, L / 2, L / 2, L / 2 + Ltap]
    xangle_lu, yangle_lu = _bezier3(px, py, n_bezier_points)

    # Right upper taper
    px = [Wend / 2, W / 2, W / 2, W / 2]
    py = [L / 2 + Ltap, L / 2, L / 2, L / 3]
    xangle_ru, yangle_ru = _bezier3(px, py, n_bezier_points)

    # Right lower taper
    px = [W / 2, W / 2, W / 2, Wend / 2]
    py = [-L / 3, -L / 2, -L / 2, -L / 2 - Ltap]
    xangle_rb, yangle_rb = _bezier3(px, py, n_bezier_points)

    # Bottom tip
    px = [Wend / 2, Wend / 2 - pxup, -Wend / 2 + pxup, -Wend / 2]
    py = [-L / 2 - Ltap, -L / 2 - Ltap - pyup, -L / 2 - Ltap - pyup, -L / 2 - Ltap]
    xtip_b, ytip_b = _bezier3(px, py, n_bezier_points)

    # Left lower taper
    px = [-Wend / 2, -W / 2, -W / 2, -W / 2]
    py = [-L / 2 - Ltap, -L / 2, -L / 2, -L / 3]
    xangle_lb, yangle_lb = _bezier3(px, py, n_bezier_points)

    # Assemble outline
    xdash = xangle_lu + xtip_t + xangle_ru + xangle_rb + xtip_b + xangle_lb
    ydash = yangle_lu + ytip_t + yangle_ru + yangle_rb + ytip_b + yangle_lb

    points = list(zip(xdash, ydash, strict=False))
    c.add_polygon(points, layer=layer)
    return c

dash

ellipse

ellipse

ellipse(
    radii: tuple[float, float] = (10.0, 5.0),
    angle_resolution: float = 2.5,
    layer: LayerSpec = "WG",
) -> Component

Returns ellipse component.

Parameters:

Name Type Description Default
radii tuple[float, float]

Semimajor and semiminor axis lengths of the ellipse.

(10.0, 5.0)
angle_resolution float

number of degrees per point.

2.5
layer LayerSpec

Specific layer(s) to put polygon geometry on.

'WG'

The orientation of the ellipse is determined by the order of the radii variables; if the first element is larger, the ellipse will be horizontal and if the second element is larger, the ellipse will be vertical.

Source code in gdsfactory/components/shapes/ellipse.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@gf.cell_with_module_name(tags=["shapes"])
def ellipse(
    radii: tuple[float, float] = (10.0, 5.0),
    angle_resolution: float = 2.5,
    layer: LayerSpec = "WG",
) -> Component:
    """Returns ellipse component.

    Args:
        radii: Semimajor and semiminor axis lengths of the ellipse.
        angle_resolution: number of degrees per point.
        layer: Specific layer(s) to put polygon geometry on.

    The orientation of the ellipse is determined by the order of the radii variables;
    if the first element is larger, the ellipse will be horizontal and if the second
    element is larger, the ellipse will be vertical.
    """
    c = gf.Component()
    a = radii[0]
    b = radii[1]
    t = np.linspace(0, 360, int(360 / angle_resolution) + 1) * pi / 180
    r = a * b / (sqrt((b * cos(t)) ** 2 + (a * sin(t)) ** 2))
    xpts = r * cos(t)
    ypts = r * sin(t)
    c.add_polygon(points=list(zip(xpts, ypts, strict=False)), layer=layer)
    return c

ellipse

fiducial_squares

fiducial_squares

fiducial_squares(
    layers: LayerSpecs = ("WG",),
    size: Float2 = (5, 5),
    offset: float = 0.14,
) -> gf.Component

Returns fiducials with two squares.

Parameters:

Name Type Description Default
layers LayerSpecs

list of layers to draw the squares.

('WG',)
size Float2

size of each square in um.

(5, 5)
offset float

space between squares in x and y.

0.14
Source code in gdsfactory/components/shapes/fiducial_squares.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@gf.cell_with_module_name(tags=["shapes"])
def fiducial_squares(
    layers: LayerSpecs = ("WG",), size: Float2 = (5, 5), offset: float = 0.14
) -> gf.Component:
    """Returns fiducials with two squares.

    Args:
        layers: list of layers to draw the squares.
        size: size of each square in um.
        offset: space between squares in x and y.
    """
    c = gf.Component()

    dx, dy = (np.array(size) + np.array([offset, offset])) / 2

    for layer in layers:
        r = c << gf.c.rectangle(size=size, layer=layer, centered=True)
        r.move((dx, dy))

    for layer in layers:
        r = c << gf.c.rectangle(size=size, layer=layer, centered=True)
        r.move((-dx, -dy))

    return c

fiducial_squares

fractal

fractal

fractal(
    fractal_type: Literal[
        "sierpinski_triangle",
        "sierpinski_carpet",
        "vicsek_cross",
        "vicsek_saltire",
    ] = "sierpinski_triangle",
    depth: int = 4,
    size: float = 100.0,
    layer: LayerSpec = "WG",
) -> Component

Returns a fractal pattern.

Parameters:

Name Type Description Default
fractal_type Literal['sierpinski_triangle', 'sierpinski_carpet', 'vicsek_cross', 'vicsek_saltire']

type of fractal.

'sierpinski_triangle'
depth int

recursion depth (max recommended: 6).

4
size float

overall size of the fractal.

100.0
layer LayerSpec

layer spec.

'WG'
Source code in gdsfactory/components/shapes/fractal.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
@gf.cell_with_module_name(tags=["shapes"])
def fractal(
    fractal_type: Literal[
        "sierpinski_triangle",
        "sierpinski_carpet",
        "vicsek_cross",
        "vicsek_saltire",
    ] = "sierpinski_triangle",
    depth: int = 4,
    size: float = 100.0,
    layer: LayerSpec = "WG",
) -> Component:
    """Returns a fractal pattern.

    Args:
        fractal_type: type of fractal.
        depth: recursion depth (max recommended: 6).
        size: overall size of the fractal.
        layer: layer spec.
    """
    if depth > 6:
        raise ValueError(f"depth={depth} too large, max 6 recommended")

    generators = {
        "sierpinski_triangle": _sierpinski_triangle,
        "sierpinski_carpet": _sierpinski_carpet,
        "vicsek_cross": _vicsek_cross,
        "vicsek_saltire": _vicsek_saltire,
    }

    c = Component()
    polygons = generators[fractal_type](depth, size)
    for poly in polygons:
        c.add_polygon(poly, layer=layer)
    return c

fractal

hexagon module-attribute

hexagon = partial(regular_polygon, sides=6)

hexagon

marker_te module-attribute

marker_te = partial(
    rectangle,
    size=(fiber_size, fiber_size),
    layer="TE",
    centered=True,
)

marker_te

marker_tm module-attribute

marker_tm = partial(
    rectangle,
    size=(fiber_size, fiber_size),
    layer="TM",
    centered=True,
)

marker_tm

nxn

nxn

nxn(
    west: int = 1,
    east: int = 4,
    north: int = 0,
    south: int = 0,
    xsize: float = 8.0,
    ysize: float = 8.0,
    wg_width: float = 0.5,
    layer: LayerSpec = "WG",
    wg_margin: float = 1.0,
    **kwargs: Any
) -> Component

Returns a nxn component with nxn ports (west, east, north, south).

Parameters:

Name Type Description Default
west int

number of west ports.

1
east int

number of east ports.

4
north int

number of north ports.

0
south int

number of south ports.

0
xsize float

size in X.

8.0
ysize float

size in Y.

8.0
wg_width float

width of the straight ports.

0.5
layer LayerSpec

layer.

'WG'
wg_margin float

margin from straight to component edge.

1.0
kwargs Any

port_settings.

3 4 |___|_

{}
Source code in gdsfactory/components/shapes/nxn.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
@gf.cell_with_module_name(tags=["shapes"])
def nxn(
    west: int = 1,
    east: int = 4,
    north: int = 0,
    south: int = 0,
    xsize: float = 8.0,
    ysize: float = 8.0,
    wg_width: float = 0.5,
    layer: LayerSpec = "WG",
    wg_margin: float = 1.0,
    **kwargs: Any,
) -> Component:
    """Returns a nxn component with nxn ports (west, east, north, south).

    Args:
        west: number of west ports.
        east: number of east ports.
        north: number of north ports.
        south: number of south ports.
        xsize: size in X.
        ysize: size in Y.
        wg_width: width of the straight ports.
        layer: layer.
        wg_margin: margin from straight to component edge.
        kwargs: port_settings.

            3   4
            |___|_
        2 -|      |- 5
           |      |
        1 -|______|- 6
            |   |
            8   7
    """
    c = gf.Component()
    _ = c << gf.components.rectangle(size=(xsize, ysize), layer=layer)

    if west > 0:
        x_west = 0
        y_west = (
            [ysize / 2]
            if west == 1
            else list(
                np.linspace(
                    wg_margin + wg_width / 2, ysize - wg_margin - wg_width / 2, west
                )
            )
        )
        orientation = 180

        for i, yi in enumerate(y_west):
            c.add_port(
                f"W{i}",
                center=(float(x_west), float(yi)),
                width=wg_width,
                orientation=orientation,
                layer=layer,
                **kwargs,
            )

    if east > 0:
        x_east = xsize
        y_east = (
            [ysize / 2]
            if east == 1
            else list(
                np.linspace(
                    wg_margin + wg_width / 2, ysize - wg_margin - wg_width / 2, east
                )
            )
        )
        orientation = 0

        for i, yi in enumerate(y_east):
            c.add_port(
                f"E{i}",
                center=(float(x_east), float(yi)),
                width=wg_width,
                orientation=orientation,
                layer=layer,
                **kwargs,
            )

    if north > 0:
        y_north = ysize
        x_north = (
            [xsize / 2]
            if north == 1
            else list(
                np.linspace(
                    wg_margin + wg_width / 2, xsize - wg_margin - wg_width / 2, north
                )
            )
        )
        orientation = 90

        for i, xi in enumerate(x_north):
            c.add_port(
                f"N{i}",
                center=(float(xi), float(y_north)),
                width=wg_width,
                orientation=orientation,
                layer=layer,
                **kwargs,
            )
    if south > 0:
        y_south = 0
        x_south = (
            [xsize / 2]
            if south == 1
            else list(
                np.linspace(
                    wg_margin + wg_width / 2, xsize - wg_margin - wg_width / 2, south
                )
            )
        )
        orientation = 270

        for i, xi in enumerate(x_south):
            c.add_port(
                f"S{i}",
                center=(float(xi), float(y_south)),
                width=wg_width,
                orientation=orientation,
                layer=layer,
                **kwargs,
            )

    c.auto_rename_ports()
    return c

nxn

octagon module-attribute

octagon = partial(regular_polygon, sides=8)

octagon

pie_arc

pie_arc

pie_arc(
    radius: float = 10.0,
    radius_y: float | None = None,
    start_angle: float = 0.0,
    end_angle: float = 90.0,
    angle_resolution: float = 2.5,
    layer: LayerSpec = "WG",
) -> Component

Returns a pie-shaped arc (sector/wedge), centered at origin.

The shape is a closed polygon from the origin along two radial lines connected by an elliptical arc.

Parameters:

Name Type Description Default
radius float

x-radius (or uniform radius if radius_y is None).

10.0
radius_y float | None

y-radius for elliptical arc. Defaults to radius.

None
start_angle float

start angle in degrees.

0.0
end_angle float

end angle in degrees.

90.0
angle_resolution float

degrees per arc point.

2.5
layer LayerSpec

layer spec.

'WG'
Source code in gdsfactory/components/shapes/pie_arc.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@gf.cell_with_module_name(tags=["shapes"])
def pie_arc(
    radius: float = 10.0,
    radius_y: float | None = None,
    start_angle: float = 0.0,
    end_angle: float = 90.0,
    angle_resolution: float = 2.5,
    layer: LayerSpec = "WG",
) -> Component:
    """Returns a pie-shaped arc (sector/wedge), centered at origin.

    The shape is a closed polygon from the origin along two radial lines
    connected by an elliptical arc.

    Args:
        radius: x-radius (or uniform radius if radius_y is None).
        radius_y: y-radius for elliptical arc. Defaults to radius.
        start_angle: start angle in degrees.
        end_angle: end angle in degrees.
        angle_resolution: degrees per arc point.
        layer: layer spec.
    """
    if radius_y is None:
        radius_y = radius

    c = Component()
    sweep = end_angle - start_angle
    n_points = max(int(abs(sweep) / angle_resolution), 2)
    theta = np.deg2rad(np.linspace(start_angle, end_angle, n_points, endpoint=True))

    arc_points = list(
        zip(radius * np.cos(theta), radius_y * np.sin(theta), strict=False)
    )
    points = [(0, 0)] + arc_points
    c.add_polygon(points, layer=layer)
    return c

pie_arc

rect_su_shape

rect_su_shape

rect_su_shape(
    L1: float = 10.0,
    L2: float = 10.0,
    L3: float = 20.0,
    width: float = 1.0,
    layer: LayerSpec = "WG",
    port_type: str | None = "electrical",
) -> Component

Returns a rectangular S- or U-shaped routing structure.

The shape consists of three connected rectangular segments forming an S or U pattern. Positive and negative values of L1, L2, L3 produce different orientations (S-shape, U-shape, etc.).

Parameters:

Name Type Description Default
L1 float

length of first vertical segment.

10.0
L2 float

length of horizontal segment.

10.0
L3 float

length of second vertical segment.

20.0
width float

width of all segments.

1.0
layer LayerSpec

layer spec.

'WG'
port_type str | None

None, optical, or electrical.

'electrical'
Source code in gdsfactory/components/shapes/rect_su_shape.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
@gf.cell_with_module_name(tags=["shapes"])
def rect_su_shape(
    L1: float = 10.0,
    L2: float = 10.0,
    L3: float = 20.0,
    width: float = 1.0,
    layer: LayerSpec = "WG",
    port_type: str | None = "electrical",
) -> Component:
    """Returns a rectangular S- or U-shaped routing structure.

    The shape consists of three connected rectangular segments forming
    an S or U pattern. Positive and negative values of L1, L2, L3
    produce different orientations (S-shape, U-shape, etc.).

    Args:
        L1: length of first vertical segment.
        L2: length of horizontal segment.
        L3: length of second vertical segment.
        width: width of all segments.
        layer: layer spec.
        port_type: None, optical, or electrical.
    """
    c = Component()
    w = width / 2

    # Build as three connected rectangles
    # Segment 1: vertical from (0,0) going up by L1
    # Segment 2: horizontal at top of seg1, going right by L2
    # Segment 3: vertical from end of seg2, going up by L3
    # The overall shape is an S/U polygon

    if L1 >= 0 and L2 >= 0 and L3 >= 0:
        points = [
            (-w, 0),
            (w, 0),
            (w, L1),
            (L2 + w, L1),
            (L2 + w, L1 + L3),
            (L2 - w, L1 + L3),
            (L2 - w, L1 + width),
            (-w, L1 + width),
            (-w, 0),
        ]
    else:
        # General case: compose three rectangles
        r1 = gf.components.rectangle(size=(width, abs(L1)), layer=layer)
        r2 = gf.components.rectangle(size=(abs(L2), width), layer=layer)
        r3 = gf.components.rectangle(size=(width, abs(L3)), layer=layer)

        ref1 = c.add_ref(r1)
        ref1.center = (0, 0)

        ref2 = c.add_ref(r2)
        if L1 >= 0:
            ref2.xmin = -w
            ref2.ymin = abs(L1) / 2 - w
        else:
            ref2.xmin = -w
            ref2.ymax = -abs(L1) / 2 + w

        ref3 = c.add_ref(r3)
        if L2 >= 0:
            ref3.xmin = abs(L2) - w
        else:
            ref3.xmax = -abs(L2) + w
        if L3 >= 0:
            ref3.ymin = ref2.ymin
        else:
            ref3.ymax = ref2.ymax

        c.flatten()
        return c

    c.add_polygon(points, layer=layer)

    if port_type:
        prefix = "o" if port_type == "optical" else "e"
        c.add_port(
            f"{prefix}1",
            center=(0, 0),
            width=width,
            orientation=270,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}2",
            center=(L2, L1 + L3),
            width=width,
            orientation=90,
            layer=layer,
            port_type=port_type,
        )
        c.auto_rename_ports()
    if port_type == "electrical":
        for port in c.ports:
            c.create_pin(ports=[port], name=port.name)
    return c

rect_su_shape

rect_taper

rect_taper

rect_taper(
    rect_width: float = 1.0,
    rect_length: float = 10.0,
    taper_length: float = 5.0,
    taper_width: float = 4.0,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component

Returns a rectangle connected to a linear taper.

The rectangle of width rect_width and length rect_length is connected on the right to a taper that linearly expands from rect_width to taper_width over taper_length.

Parameters:

Name Type Description Default
rect_width float

width of the rectangular section.

1.0
rect_length float

length of the rectangular section.

10.0
taper_length float

length of the taper section.

5.0
taper_width float

end width of the taper.

4.0
layer LayerSpec

layer spec.

'WG'
port_type str | None

None, optical, or electrical.

'optical'
Source code in gdsfactory/components/shapes/rect_taper.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@gf.cell_with_module_name(tags=["shapes"])
def rect_taper(
    rect_width: float = 1.0,
    rect_length: float = 10.0,
    taper_length: float = 5.0,
    taper_width: float = 4.0,
    layer: LayerSpec = "WG",
    port_type: str | None = "optical",
) -> Component:
    """Returns a rectangle connected to a linear taper.

    The rectangle of width rect_width and length rect_length is connected
    on the right to a taper that linearly expands from rect_width to
    taper_width over taper_length.

    Args:
        rect_width: width of the rectangular section.
        rect_length: length of the rectangular section.
        taper_length: length of the taper section.
        taper_width: end width of the taper.
        layer: layer spec.
        port_type: None, optical, or electrical.
    """
    c = Component()
    hw = rect_width / 2
    tw = taper_width / 2
    total_length = rect_length + taper_length

    points = [
        (0, -hw),
        (rect_length, -hw),
        (total_length, -tw),
        (total_length, tw),
        (rect_length, hw),
        (0, hw),
    ]
    c.add_polygon(points, layer=layer)

    if port_type:
        prefix = "o" if port_type == "optical" else "e"
        c.add_port(
            f"{prefix}1",
            center=(0, 0),
            width=rect_width,
            orientation=180,
            layer=layer,
            port_type=port_type,
        )
        c.add_port(
            f"{prefix}2",
            center=(total_length, 0),
            width=taper_width,
            orientation=0,
            layer=layer,
            port_type=port_type,
        )
        c.auto_rename_ports()
    if port_type == "electrical":
        for port in c.ports:
            c.create_pin(ports=[port], name=port.name)
    return c

rect_taper

rectangle

rectangle

rectangle(
    size: Size = (4.0, 2.0),
    layer: LayerSpec = "WG",
    centered: bool = False,
    port_type: str | None = "electrical",
    port_orientations: Ints | None = (180, 90, 0, -90),
) -> Component

Returns a rectangle.

Parameters:

Name Type Description Default
size Size

(tuple) Width and height of rectangle.

(4.0, 2.0)
layer LayerSpec

Specific layer to put polygon geometry on.

'WG'
centered bool

True sets center to (0, 0), False sets south-west to (0, 0).

False
port_type str | None

optical, electrical.

'electrical'
port_orientations Ints | None

list of port_orientations to add. None adds no ports.

(180, 90, 0, -90)
Source code in gdsfactory/components/shapes/rectangle.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@gf.cell_with_module_name(tags=["shapes"])
def rectangle(
    size: Size = (4.0, 2.0),
    layer: LayerSpec = "WG",
    centered: bool = False,
    port_type: str | None = "electrical",
    port_orientations: Ints | None = (180, 90, 0, -90),
) -> Component:
    """Returns a 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: optical, electrical.
        port_orientations: list of port_orientations to add. None adds no ports.
    """
    c = Component()
    ref = c << gf.c.compass(
        size=size, layer=layer, port_type=port_type, port_orientations=port_orientations
    )
    if not centered:
        ref.move((size[0] / 2, size[1] / 2))
    if port_type:
        c.add_ports(ref.ports)
    c.flatten()
    if port_type == "electrical":
        elec = [p for p in c.ports if p.port_type == "electrical"]
        if elec:
            c.create_pin(ports=elec, name="pad")
    return c

rectangles

rectangles(
    size: Size = (4.0, 2.0),
    offsets: Sequence[float] | None = None,
    layers: LayerSpecs = ("WG", "SLAB150"),
    centered: bool = True,
    **kwargs: Any
) -> Component

Returns overimposed rectangles.

Parameters:

Name Type Description Default
size Size

(tuple) Width and height of rectangle.

(4.0, 2.0)
layers LayerSpecs

Specific layer to put polygon geometry on.

('WG', 'SLAB150')
offsets Sequence[float] | None

list of offsets. If None, all rectangles have a zero offset.

None
centered bool

True sets center to (0, 0), False sets south-west of first rectangle to (0, 0).

True
kwargs Any

additional arguments to pass to rectangle.

{}

Other Parameters:

Name Type Description
port_type

optical, electrical.

port_orientations

list of port_orientations to add.

┌──────────────┐ │ │ │ ┌──────┐ │ │ │ │ │ │ │ ├───► │ │ │offset │ └──────┘ │ │ │ └──────────────┘

Source code in gdsfactory/components/shapes/rectangle.py
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@gf.cell_with_module_name(tags=["shapes"])
def rectangles(
    size: Size = (4.0, 2.0),
    offsets: Sequence[float] | None = None,
    layers: LayerSpecs = ("WG", "SLAB150"),
    centered: bool = True,
    **kwargs: Any,
) -> Component:
    """Returns overimposed rectangles.

    Args:
        size: (tuple) Width and height of rectangle.
        layers: Specific layer to put polygon geometry on.
        offsets: list of offsets. If None, all rectangles have a zero offset.
        centered: True sets center to (0, 0), False sets south-west of first rectangle to (0, 0).
        kwargs: additional arguments to pass to rectangle.

    Keyword Args:
        port_type: optical, electrical.
        port_orientations: list of port_orientations to add.

            ┌──────────────┐
            │              │
            │   ┌──────┐   │
            │   │      │   │
            │   │      ├───►
            │   │      │offset
            │   └──────┘   │
            │              │
            └──────────────┘

    """
    c = Component()
    size_np = np.array(size, dtype=np.float64)
    ref0 = None
    offsets = offsets or [0] * len(layers)

    if len(offsets) != len(layers):
        raise ValueError(f"len(offsets) != len(layers) {len(offsets)} != {len(layers)}")
    for