Components

gdsfactory provides a generic customizable components library in gf.components

Basic shapes

Rectangle

To create a simple rectangle, there are two functions:

gf.components.rectangle() can create a basic rectangle:

[1]:
import gdsfactory as gf

gf.components.rectangle(size=(4.5, 2), layer=(1, 0))
2021-09-25 16:19:19.544 | INFO     | gdsfactory.config:<module>:51 - 3.2.9
../_images/notebooks_04_components_3_1.png
[1]:
rectangle_layer1_0_size4p5_2: uid 1, ports [], aliases [], 1 polygons, 0 references

gf.components.bbox() can also create a rectangle based on a bounding box. This is useful if you want to create a rectangle which exactly surrounds a piece of existing geometry. For example, if we have an arc geometry and we want to define a box around it, we can use gf.components.bbox():

[2]:
D = gf.Component()
arc = D << gf.components.bend_circular(radius=10, width=0.5, angle=90, layer=(1, 0))
arc.rotate(90)
# Draw a rectangle around the arc we created by using the arc's bounding box
rect = D << gf.components.bbox(bbox=arc.bbox, layer=(0, 0))
D
../_images/notebooks_04_components_5_0.png
[2]:
Unnamed_06001b97: uid 2, ports [], aliases [], 0 polygons, 2 references

Cross

The gf.components.cross() function creates a cross structure:

[3]:
gf.components.cross(length=10, width=0.5, layer=(1, 0))
../_images/notebooks_04_components_7_0.png
[3]:
cross_layer1_0_length10_91787232: uid 5, ports ['o3', 'o1', 'o2', 'o4'], aliases [], 2 polygons, 0 references

Ellipse

The gf.components.ellipse() function creates an ellipse by defining the major and minor radii:

[4]:
gf.components.ellipse(radii=(10, 5), angle_resolution=2.5, layer=(1, 0))
../_images/notebooks_04_components_9_0.png
[4]:
ellipse_angle_resolutio_44057b02: uid 7, ports [], aliases [], 1 polygons, 0 references

Circle

The gf.components.circle() function creates a circle:

[5]:
gf.components.circle(radius=10, angle_resolution=2.5, layer=(1, 0))
../_images/notebooks_04_components_11_0.png
[5]:
circle_angle_resolution_dac0666b: uid 8, ports [], aliases [], 1 polygons, 0 references

Ring

The gf.components.ring() function creates a ring. The radius refers to the center radius of the ring structure (halfway between the inner and outer radius).

[6]:
gf.components.ring(radius=5, width=0.5, angle_resolution=2.5, layer=(1, 0))
../_images/notebooks_04_components_13_0.png
[6]:
ring_angle_resolution2p_845f9d9a: uid 9, ports [], aliases [], 1 polygons, 0 references
[7]:
gf.components.ring_single(
    width=0.5, gap=0.2, radius=10, length_x=4, length_y=2, layer=(1, 0)
)
../_images/notebooks_04_components_14_0.png
[7]:
ring_single_gap200n_lay_eddc165f: uid 10, ports ['o2', 'o1'], aliases [], 0 polygons, 6 references
[8]:
import gdsfactory as gf

gf.components.ring_double(
    width=0.5, gap=0.2, radius=10, length_x=4, length_y=2, layer=(1, 0)
)
../_images/notebooks_04_components_15_0.png
[8]:
ring_double_gap200n_lay_1cb49fce: uid 25, ports ['o1', 'o2', 'o3', 'o4'], aliases [], 0 polygons, 4 references
[9]:
gf.components.ring_double(
    width=0.5,
    gap=0.2,
    radius=10,
    length_x=4,
    length_y=2,
    layer=(1, 0),
    bend=gf.components.bend_circular,
)
../_images/notebooks_04_components_16_0.png
[9]:
ring_double_bendbend_ci_4127b690: uid 33, ports ['o1', 'o2', 'o3', 'o4'], aliases [], 0 polygons, 4 references

Bend circular

The gf.components.bend_circular() function creates an arc. The radius refers to the center radius of the arc (halfway between the inner and outer radius).

[10]:
gf.components.bend_circular(radius=2.0, width=0.5, angle=90, npoints=720, layer=(1, 0))
../_images/notebooks_04_components_18_0.png
[10]:
bend_circular_angle90_l_5ebc659d: uid 34, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references

Bend euler

The gf.components.bend_euler() function creates an adiabatic bend in which the bend radius changes gradually. Euler bends have lower loss than circular bends.

[11]:
gf.components.bend_euler(radius=2.0, width=0.5, angle=90, npoints=720, layer=(1, 0))
../_images/notebooks_04_components_20_0.png
[11]:
bend_euler_angle90_laye_c1b4eebf: uid 35, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references

Tapers

gf.components.taper()is defined by setting its length and its start and end length. It has two ports, 1 and 2, on either end, allowing you to easily connect it to other structures.

[12]:
gf.components.taper(length=10, width1=6, width2=4, port=None, layer=(1, 0))
../_images/notebooks_04_components_22_0.png
[12]:
taper_layer1_0_length10_8f785b8b: uid 36, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references

gf.components.ramp() is a structure is similar to taper() except it is asymmetric. It also has two ports, 1 and 2, on either end.

[13]:
gf.components.ramp(length=10, width1=4, width2=8, layer=(1, 0))
../_images/notebooks_04_components_24_0.png
[13]:
ramp_layer1_0_length10__4acf6857: uid 37, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references

Common compound shapes

The gf.components.L() function creates a “L” shape with ports on either end named 1 and 2.

[14]:
gf.components.L(width=7, size=(10, 20), layer=(1, 0))
../_images/notebooks_04_components_27_0.png
[14]:
L_layer1_0_size10_20_width7: uid 38, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references

The gf.components.C() function creates a “C” shape with ports on either end named 1 and 2.

[15]:
gf.components.C(width=7, size=(10, 20), layer=(1, 0))
../_images/notebooks_04_components_29_0.png
[15]:
C_layer1_0_size10_20_width7: uid 39, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references

Text

Gdsfactory has an implementation of the DEPLOF font with the majority of english ASCII characters represented (thanks to phidl)

[16]:
gf.components.text(
    text="Hello world!\nMultiline text\nLeft-justified",
    size=10,
    justify="left",
    layer=(1, 0),
)
# `justify` should be either 'left', 'center', or 'right'
../_images/notebooks_04_components_31_0.png
[16]:
Hello_world Multiline_text Leftmjustified_0_0: uid 40, ports [], aliases [], 0 polygons, 3 references

Grid / packer / align / distribute

Grid

The gf.components.grid() function can take a list (or 2D array) of objects and arrange them along a grid. This is often useful for making parameter sweeps. If the separation argument is true, grid is arranged such that the elements are guaranteed not to touch, with a spacing distance between them. If separation is false, elements are spaced evenly along a grid. The align_x/align_y arguments specify intra-row/intra-column alignment. Theedge_x/edge_y arguments specify inter-row/inter-column alignment (unused if separation = True).

[17]:
import gdsfactory as gf

components_list = []
for width1 in [1, 6, 9]:
    for width2 in [1, 2, 4, 8]:
        D = gf.components.taper(length=10, width1=width1, width2=width2, layer=(1, 0))
        components_list.append(D)

c = gf.grid(
    components_list,
    spacing=(5, 1),
    separation=True,
    shape=(3, 4),
    align_x="x",
    align_y="y",
    edge_x="x",
    edge_y="ymax",
)
c
../_images/notebooks_04_components_34_0.png
[17]:
grid: uid 56, ports [], aliases [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)], 0 polygons, 12 references

Packer

The gf.pack() function packs geometries together into rectangular bins. If a max_size is specified, the function will create as many bins as is necessary to pack all the geometries and then return a list of the filled-bin Devices.

Here we generate several random shapes then pack them together automatically. We allow the bin to be as large as needed to fit all the Devices by specifying max_size = (None, None). By setting aspect_ratio = (2,1), we specify the rectangular bin it tries to pack them into should be twice as wide as it is tall:

[18]:
import numpy as np
import gdsfactory as gf

np.random.seed(5)
D_list = [
    gf.components.ellipse(radii=tuple(np.random.rand(2) * n + 2)) for n in range(50)
]
D_list += [
    gf.components.rectangle(size=tuple(np.random.rand(2) * n + 2)) for n in range(50)
]

D_packed_list = gf.pack(
    D_list,  # Must be a list or tuple of Devices
    spacing=1.25,  # Minimum distance between adjacent shapes
    aspect_ratio=(2, 1),  # (width, height) ratio of the rectangular bin
    max_size=(None, None),  # Limits the size into which the shapes will be packed
    density=1.05,  # Values closer to 1 pack tighter but require more computation
    sort_by_area=True,  # Pre-sorts the shapes by area
)
D = D_packed_list[0]  # Only one bin was created, so we plot that
D
../_images/notebooks_04_components_36_0.png
[18]:
Unnamed_c6a50e1d: uid 158, ports [], aliases [], 0 polygons, 100 references

Say we need to pack many shapes into multiple 500x500 unit die. If we set max_size = (500,500) the shapes will be packed into as many 500x500 unit die as required to fit them all:

[19]:
np.random.seed(1)
D_list = [
    gf.components.ellipse(radii=tuple(np.random.rand(2) * n + 2)) for n in range(120)
]
D_list += [
    gf.components.rectangle(size=tuple(np.random.rand(2) * n + 2)) for n in range(120)
]

D_packed_list = gf.pack(
    D_list,  # Must be a list or tuple of Devices
    spacing=4,  # Minimum distance between adjacent shapes
    aspect_ratio=(1, 1),  # Shape of the box
    max_size=(500, 500),  # Limits the size into which the shapes will be packed
    density=1.05,  # Values closer to 1 pack tighter but require more computation
    sort_by_area=True,  # Pre-sorts the shapes by area
)

# Put all packed bins into a single device and spread them out with distribute()
F = gf.Component()
[F.add_ref(D) for D in D_packed_list]
F.distribute(elements="all", direction="x", spacing=100, separation=True)
F
/home/docs/checkouts/readthedocs.org/user_builds/gdsfactory/checkouts/latest/gdsfactory/pack.py:168: UserWarning: created 3 groups of components
  warnings.warn(f"created {len(components_packed_list)-1} groups of components")
../_images/notebooks_04_components_38_1.png
[19]:
Unnamed_3ecd2a40: uid 743, ports [], aliases [], 0 polygons, 4 references

Note that the packing problem is an NP-complete problem, so gf.components.packer() may be slow if there are more than a few hundred Devices to pack (in that case, try pre-packing a few dozen at a time then packing the resulting bins). Requires the rectpack python package.

Distribute

The distribute() function allows you to space out elements within a Device evenly in the x or y direction. It is meant to duplicate the distribute functionality present in Inkscape / Adobe Illustrator:

image0

Say we start out with a few random-sized rectangles we want to space out:

[20]:
c = gf.Component()
# Create different-sized rectangles and add them to D
[
    c.add_ref(
        gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20], layer=(0, 0))
    ).move([n, n * 4])
    for n in [0, 2, 3, 1, 2]
]
c
../_images/notebooks_04_components_43_0.png
[20]:
Unnamed_6b0222e6: uid 744, ports [], aliases [], 0 polygons, 5 references

Oftentimes, we want to guarantee some distance between the objects. By setting separation = True we move each object such that there is spacing distance between them:

[21]:
D = gf.Component()
# Create different-sized rectangles and add them to D
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
# Distribute all the rectangles in D along the x-direction with a separation of 5
D.distribute(
    elements="all",  # either 'all' or a list of objects
    direction="x",  # 'x' or 'y'
    spacing=5,
    separation=True,
)
D
../_images/notebooks_04_components_45_0.png
[21]:
Unnamed_32515d53: uid 749, ports [], aliases [], 0 polygons, 5 references

Alternatively, we can spread them out on a fixed grid by setting separation = False. Here we align the left edge (edge = 'min') of each object along a grid spacing of 100:

[22]:
D = gf.Component()
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
D.distribute(
    elements="all", direction="x", spacing=100, separation=False, edge="xmin"
)  # edge must be either 'xmin' (left), 'xmax' (right), or 'x' (center)
D
../_images/notebooks_04_components_47_0.png
[22]:
Unnamed_cc63e5c9: uid 754, ports [], aliases [], 0 polygons, 5 references

The alignment can be done along the right edge as well by setting edge = 'max', or along the center by setting edge = 'center' like in the following:

[23]:
D = gf.Component()
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move(
        (n - 10, n * 4)
    )
    for n in [0, 2, 3, 1, 2]
]
D.distribute(
    elements="all", direction="x", spacing=100, separation=False, edge="x"
)  # edge must be either 'xmin' (left), 'xmax' (right), or 'x' (center)
D
../_images/notebooks_04_components_49_0.png
[23]:
Unnamed_0bab2d93: uid 759, ports [], aliases [], 0 polygons, 5 references

Align

The align() function allows you to elements within a Device horizontally or vertically. It is meant to duplicate the alignment functionality present in Inkscape / Adobe Illustrator:

image0

Say we distribute() a few objects, but they’re all misaligned:

[24]:
D = gf.Component()
# Create different-sized rectangles and add them to D then distribute them
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
D.distribute(elements="all", direction="x", spacing=5, separation=True)
D
../_images/notebooks_04_components_53_0.png
[24]:
Unnamed_8177db20: uid 764, ports [], aliases [], 0 polygons, 5 references

we can use the align() function to align their top edges (``alignment = ‘ymax’):

[25]:
D = gf.Component()
# Create different-sized rectangles and add them to D then distribute them
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
D.distribute(elements="all", direction="x", spacing=5, separation=True)

# Align top edges
D.align(elements="all", alignment="ymax")
D
../_images/notebooks_04_components_55_0.png
[25]:
Unnamed_643b8374: uid 769, ports [], aliases [], 0 polygons, 5 references

or align their centers (``alignment = ‘y’):

[26]:
D = gf.Component()
# Create different-sized rectangles and add them to D then distribute them
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
D.distribute(elements="all", direction="x", spacing=5, separation=True)

# Align top edges
D.align(elements="all", alignment="y")
D
../_images/notebooks_04_components_57_0.png
[26]:
Unnamed_7e69e759: uid 774, ports [], aliases [], 0 polygons, 5 references

other valid alignment options include 'xmin', 'x', 'xmax', 'ymin', 'y', and 'ymax'

Boolean / outline / offset / invert

There are several common boolean-type operations available in the geometry library. These include typical boolean operations (and/or/not/xor), offsetting (expanding/shrinking polygons), outlining, and inverting.

Boolean

The gf.boolean() function can perform AND/OR/NOT/XOR operations, and will return a new geometry with the result of that operation.

Speedup note: The num_divisions argument can be used to divide up the geometry into multiple rectangular regions and process each region sequentially (which is more computationally efficient). If you have a large geometry that takes a long time to process, try using num_divisions = [10,10] to optimize the operation.

[27]:
E = gf.components.ellipse(radii=(10, 5), layer=(1, 0))
R = gf.components.rectangle(size=[15, 5], layer=(2, 0))
C = gf.boolean(A=E, B=R, operation="not", precision=1e-6, num_divisions=[1, 1], layer=0)
# Other operations include 'and', 'or', 'xor', or equivalently 'A-B', 'B-A', 'A+B'

# Plot the originals and the result
D = gf.Component()
D.add_ref(E)
D.add_ref(R).movey(-1.5)
D.add_ref(C).movex(30)
D
../_images/notebooks_04_components_61_0.png
[27]:
Unnamed_b69c32f6: uid 783, ports [], aliases [], 0 polygons, 3 references

Offset

The gf.offset() function takes the polygons of the input geometry, combines them together, and expands/contracts them. The function returns polygons on a single layer – it does not respect layers.

Speedup note: The num_divisions argument can be used to divide up the geometry into multiple rectangular regions and process each region sequentially (which is more computationally efficient). If you have a large geometry that takes a long time to process, try using num_divisions = [10,10] to optimize the operation.

[28]:
import gdsfactory as gf

# Create `T`, an ellipse and rectangle which will be offset (expanded / contracted)
T = gf.Component("ellipse_and_rectangle")
e = T << gf.components.ellipse(radii=(10, 5), layer=(1, 0))
r = T << gf.components.rectangle(size=[15, 5], layer=(2, 0))
r.move([3, -2.5])

Texpanded = gf.offset(
    T, distance=2, join_first=True, precision=1e-6, num_divisions=[1, 1], layer=(0, 0)
)
Texpanded.name = "expanded"
Tshrink = gf.offset(
    T,
    distance=-1.5,
    join_first=True,
    precision=1e-6,
    num_divisions=[1, 1],
    layer=(0, 0),
)
Tshrink.name = "shrink"

# Plot the original geometry, the expanded, and the shrunk versions
D = gf.Component("top")
t1 = D.add_ref(T)
t2 = D.add_ref(Texpanded)
t3 = D.add_ref(Tshrink)
D.distribute([t1, t2, t3], direction="x", spacing=5)
D
../_images/notebooks_04_components_63_0.png
[28]:
top: uid 789, ports [], aliases [], 0 polygons, 3 references

Outline

The gf.components.outline() function takes the polygons of the input geometry then performs an offset and “not” boolean operation to create an outline. The function returns polygons on a single layer – it does not respect layers.

Speedup note: The num_divisions argument can be used to divide up the geometry into multiple rectangular regions and process each region sequentially (which is more computationally efficient). If you have a large geometry that takes a long time to process, try using num_divisions = [10,10] to optimize the operation.

[29]:
import phidl.geometry as pg

# Create a blank device and add two shapes
X = gf.Component()
X.add_ref(gf.components.cross(length=25, width=1, layer=(1, 0)))
X.add_ref(gf.components.ellipse(radii=[10, 5], layer=(2, 0)))

O = pg.outline(X, distance=1.5, precision=1e-6, layer=(0, 0))

# Plot the original geometry and the result
D = gf.Component()
D.add_ref(X)
D.add_ref(O).movex(30)
D
../_images/notebooks_04_components_65_0.png
[29]:
Unnamed_0c45b6a9: uid 798, ports [], aliases [], 0 polygons, 2 references

The open_ports argument opens holes in the outlined geometry at each Port location.

  • If not False, holes will be cut in the outline such that the Ports are not covered.

  • If True, the holes will have the same width as the Ports.

  • If a float, the holes will be widened by that value.

  • If a float equal to the outline distance, the outline will be flush with the port (useful positive-tone processes).

[30]:
D = gf.components.L(width=7, size=(10, 20), layer=(1, 0))
D
../_images/notebooks_04_components_67_0.png
[30]:
L_layer1_0_size10_20_width7: uid 799, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references
[31]:
# Outline the geometry and open a hole at each port
N = pg.outline(D, distance=5, open_ports=False)  # No holes
gf.plot(N)
../_images/notebooks_04_components_68_0.png
[32]:
O = pg.outline(D, distance=5, open_ports=True)  # Hole is the same width as the port
gf.plot(O)
../_images/notebooks_04_components_69_0.png
[33]:
P = pg.outline(
    D, distance=5, open_ports=2.9
)  # Change the hole size by entering a float
gf.plot(P)
../_images/notebooks_04_components_70_0.png
[34]:
Q = pg.outline(
    D, distance=5, open_ports=5
)  # Creates flush opening (open_ports > distance)
gf.plot(Q)
../_images/notebooks_04_components_71_0.png

Invert

The pg.invert() function creates an inverted version of the input geometry. The function creates a rectangle around the geometry (with extra padding of distance border), then subtract all polygons from all layers from that rectangle, resulting in an inverted version of the geometry.

Speedup note: The num_divisions argument can be used to divide up the geometry into multiple rectangular regions and process each region sequentially (which is more computationally efficient). If you have a large geometry that takes a long time to process, try using num_divisions = [10,10] to optimize the operation.

[35]:
import phidl.geometry as pg

E = gf.components.ellipse(radii=(10, 5))
D = pg.invert(E, border=0.5, precision=1e-6, layer=(0, 0))
gf.plot(D)
../_images/notebooks_04_components_73_0.png

Union

The pg.union() function is a “join” function, and is functionally identical to the “OR” operation of gf.boolean(). The one difference is it’s able to perform this function layer-wise, so each layer can be individually combined.

[36]:
import gdsfactory as gf
import phidl.geometry as pg

D = gf.Component()
e0 = D << gf.components.ellipse(layer=(0, 0))
e1 = D << gf.components.ellipse(layer=(0, 0))
e2 = D << gf.components.ellipse(layer=(0, 0))
e3 = D << gf.components.ellipse(layer=(0, 0))
e4 = D << gf.components.ellipse(layer=(0, 0))
e5 = D << gf.components.ellipse(layer=(0, 0))

e1.rotate(15 * 1)
e2.rotate(15 * 2)
e3.rotate(15 * 3)
e4.rotate(15 * 4)
e5.rotate(15 * 5)

D
../_images/notebooks_04_components_75_0.png
[36]:
Unnamed_b74b9086: uid 832, ports [], aliases [], 0 polygons, 6 references
[37]:
# We have two options to unioning - take all polygons, regardless of
# layer, and join them together (in this case on layer (0,0) like so:
D_joined = pg.union(D, by_layer=False, layer=(0, 0))
gf.plot(D_joined)
../_images/notebooks_04_components_76_0.png
[38]:
# Or we can perform the union operate by-layer
D_joined_by_layer = pg.union(D, by_layer=True)
gf.plot(D_joined_by_layer)
../_images/notebooks_04_components_77_0.png

XOR / diff

The pg.xor_diff() function can be used to compare two geometries and identify where they are different. Specifically, it performs a layer-wise XOR operation. If two geometries are identical, the result will be an empty Device. If they are not identical, any areas not shared by the two geometries will remain.

[39]:
import phidl.geometry as pg

A = gf.Component()
A.add_ref(gf.components.ellipse(radii=[10, 5], layer=(1, 0)))
A.add_ref(gf.components.text("A")).move([3, 0])

B = gf.Component()
B.add_ref(gf.components.ellipse(radii=[11, 4], layer=(1, 0))).movex(4)
B.add_ref(gf.components.text("B")).move([3.2, 0])
X = pg.xor_diff(A=A, B=B, precision=1e-6)

# Plot the original geometry and the result
# Upper left: A / Upper right: B
# Lower left: A and B / Lower right: A xor B "diff" comparison
D = gf.Component()
D.add_ref(A).move([-15, 25])
D.add_ref(B).move([15, 25])
D.add_ref(A).movex(-15)
D.add_ref(B).movex(-15)
D.add_ref(X).movex(15)
D
../_images/notebooks_04_components_79_0.png
[39]:
Unnamed_402e7977: uid 845, ports [], aliases [], 0 polygons, 5 references

Lithography structures

Step-resolution

The gf.components.litho_steps() function creates lithographic test structure that is useful for measuring resolution of photoresist or electron-beam resists. It provides both positive-tone and negative-tone resolution tests.

[40]:
D = gf.components.litho_steps(
    line_widths=[1, 2, 4, 8, 16], line_spacing=10, height=100, layer=(1, 0)
)
D
../_images/notebooks_04_components_81_0.png
[40]:
litho_steps_height100_l_4f03783f: uid 846, ports [], aliases [], 0 polygons, 12 references

Calipers (inter-layer alignment)

The gf.components.litho_calipers() function is used to detect offsets in multilayer fabrication. It creates a two sets of notches on different layers. When an fabrication error/offset occurs, it is easy to detect how much the offset is because both center-notches are no longer aligned.

[41]:
D = gf.components.litho_calipers(
    notch_size=[1, 5],
    notch_spacing=2,
    num_notches=7,
    offset_per_notch=0.1,
    row_spacing=0,
    layer1=(1, 0),
    layer2=(2, 0),
)
D
../_images/notebooks_04_components_84_0.png
[41]:
litho_calipers_layer11__460508bd: uid 855, ports [], aliases [], 0 polygons, 32 references

Paths / straights

See the Path tutorial for more details – this is just an enumeration of the available built-in Path functions

Circular arc

[42]:
P = gf.path.arc(radius=10, angle=135, npoints=720)
gf.plot(P)
../_images/notebooks_04_components_87_0.png

Straight

[43]:
import gdsfactory as gf

P = gf.path.straight(length=5, npoints=100)
gf.plot(P)
../_images/notebooks_04_components_89_0.png

Euler curve

Also known as a straight-to-bend, clothoid, racetrack, or track transition, this Path tapers adiabatically from straight to curved. Often used to minimize losses in photonic straights. If p < 1.0, will create a “partial euler” curve as described in Vogelbacher et. al. https://dx.doi.org/10.1364/oe.27.031394. If the use_eff argument is false, radius corresponds to minimum radius of curvature of the bend. If use_eff is true, radius corresponds to the “effective” radius of the bend– The curve will be scaled such that the endpoints match an arc with parameters radius and angle.

[44]:
P = gf.path.euler(radius=3, angle=90, p=1.0, use_eff=False, npoints=720)
gf.plot(P)
../_images/notebooks_04_components_91_0.png
[45]:
P
[45]:
<phidl.device_layout.Path at 0x7fefd18c5190>

Smooth path from waypoints

[46]:
import numpy as np
import gdsfactory as gf

points = np.array([(20, 10), (40, 10), (20, 40), (50, 40), (50, 20), (70, 20)])

P = gf.path.smooth(
    points=points,
    radius=2,
    bend_path_function=gf.path.euler,
    use_eff=False,
)
gf.plot(P)
../_images/notebooks_04_components_94_0.png

Delay spiral

[47]:
gf.components.spiral()
../_images/notebooks_04_components_96_0.png
[47]:
spiral: uid 859, ports ['o2', 'o1'], aliases [], 242 polygons, 0 references

Importing GDS files

gf.import_gds() allows you to easily import external GDSII files. It imports a single cell from the external GDS file and converts it into a gdsfactory component.

[48]:
D = gf.components.ellipse()
D.write_gds("myoutput.gds")
D = gf.import_gds(gdspath="myoutput.gds", cellname=None, flatten=False)
D
../_images/notebooks_04_components_99_0.png
[48]:
ellipse: uid 861, ports [], aliases [], 1 polygons, 0 references

LayerSet

The LayerSet class allows you to predefine a collection of layers and specify their properties including: gds layer/datatype, name, and color. It also comes with a handy preview function called gf.layers.preview_layerset()

[49]:
import gdsfactory as gf

lys = gf.layers.LayerSet()
lys.add_layer("p", color="lightblue", gds_layer=1, gds_datatype=0)
lys.add_layer("p+", color="blue", gds_layer=2, gds_datatype=0)
lys.add_layer("p++", color="darkblue", gds_layer=3, gds_datatype=0)
lys.add_layer("n", color="lightgreen", gds_layer=4, gds_datatype=0)
lys.add_layer("n+", color="green", gds_layer=4, gds_datatype=98)
lys.add_layer("n++", color="darkgreen", gds_layer=5, gds_datatype=99)
D = gf.layers.preview_layerset(lys, size=100, spacing=100)
D
../_images/notebooks_04_components_101_0.png
[49]:
layerset: uid 862, ports [], aliases [], 0 polygons, 12 references

Useful contact pads / connectors

These functions are common shapes with ports, often used to make contact pads

[50]:
D = gf.components.compass(size=(4, 2), layer=(1, 0))
D
../_images/notebooks_04_components_103_0.png
[50]:
compass_layer1_0_size4_2: uid 887, ports ['e1', 'e2', 'e3', 'e4'], aliases [], 0 polygons, 1 references
[51]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.compass_multi(size=(4, 2), ports={"N": 3, "S": 4}, layer=(1, 0))
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_104_0.png
[52]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.flagpole(
    size=(50, 25), stub_size=(4, 8), shape="p", taper_type="fillet", layer=(1, 0)
)
# taper_type should be None, 'fillet', or 'straight'

plot(D)  # quickplot the geometry
../_images/notebooks_04_components_105_0.png
[53]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.straight(size=(4, 2), layer=(1, 0))
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_106_0.png
[54]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.connector(midpoint=(0, 0), width=1, orientation=0)
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_107_0.png
[55]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.tee(size=(8, 4), stub_size=(1, 2), taper_type="fillet", layer=(1, 0))
# taper_type should be None, 'fillet', or 'straight'
plot(D)
../_images/notebooks_04_components_108_0.png

Chip / die template

[56]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.basic_die(
    size=(10000, 5000),  # Size of die
    street_width=100,  # Width of corner marks for die-sawing
    street_length=1000,  # Length of corner marks for die-sawing
    die_name="chip99",  # Label text
    text_size=500,  # Label text size
    text_location="SW",  # Label text compass location e.g. 'S', 'SE', 'SW'
    layer=0,
    draw_bbox=False,
    bbox_layer=99,
)
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_110_0.png

Optimal superconducting curves

The following structures are meant to reduce “current crowding” in superconducting thin-film structures (such as superconducting nanowires). They are the result of conformal mapping equations derived in Clem, J. & Berggren, K. “Geometry-dependent critical currents in superconducting nanocircuits.” Phys. Rev. B 84, 1–27 (2011).

[57]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.optimal_hairpin(
    width=0.2, pitch=0.6, length=10, turn_ratio=4, num_pts=50, layer=0
)
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_112_0.png
[58]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.optimal_step(
    start_width=10,
    end_width=22,
    num_pts=50,
    width_tol=1e-3,
    anticrowding_factor=1.2,
    symmetric=False,
    layer=0,
)
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_113_0.png
[59]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.optimal_90deg(width=100.0, num_pts=15, length_adjust=1, layer=0)
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_114_0.png
[60]:
import phidl.geometry as pg
from phidl import quickplot as plot

D = pg.snspd(
    wire_width=0.2,
    wire_pitch=0.6,
    size=(10, 8),
    num_squares=None,
    turn_ratio=4,
    terminals_same_side=False,
    layer=0,
)
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_115_0.png
[61]:
D = pg.snspd_expanded(
    wire_width=0.3,
    wire_pitch=0.6,
    size=(10, 8),
    num_squares=None,
    connector_width=1,
    connector_symmetric=False,
    turn_ratio=4,
    terminals_same_side=False,
    layer=0,
)
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_116_0.png

Copying and extracting geometry

[62]:
E = gf.Component()
E.add_ref(gf.components.ellipse(layer=(1, 0)))
# X = gf.components.ellipse(layer = {0,1})
D = pg.extract(E, layers=[(1, 0)])
plot(D)  # quickplot the geometry
../_images/notebooks_04_components_118_0.png
[63]:
import gdsfactory as gf

X = gf.components.ellipse(layer=(2, 0))
c = X.copy()
c
../_images/notebooks_04_components_119_0.png
[63]:
ellipse_layer2_0: uid 919, ports [], aliases [], 1 polygons, 0 references
[ ]:

[ ]: