Component#

A Component is like an empty canvas, where you can add polygons, references to other Components and ports (to connect to other components)

image0

In gdsfactory all dimensions are in microns

Lets add a polygon

[1]:
import gdsfactory as gf

# Create a blank component (essentially an empty GDS cell with some special features)
c = gf.Component("myComponent")

# Create and add a polygon from separate lists of x points and y points
# (Can also be added like [(x1,y1), (x2,y2), (x3,y3), ... ]
poly1 = c.add_polygon(
    [(-8, 6, 7, 9), (-6, 8, 17, 5)], layer=1
)  # GDS layers are tuples of ints (but if we use only one number it assumes the other number is 0)

c  # show it in matplotlib and klayout (you need to have klayout open and install gdsfactory from the git repo with make install)
2022-06-28 16:59:47.280 | INFO     | gdsfactory.config:<module>:52 - Load '/home/runner/work/gdsfactory/gdsfactory/gdsfactory' 5.11.4
../_images/notebooks_00_geometry_2_1.png
[1]:
myComponent: uid 0, ports [], aliases [], 1 polygons, 0 references

Exercise :

Make a component similar to the one above that has a second polygon in layer (1, 1)

[2]:
c = gf.Component("myComponent2")
# Create some new geometry from the functions available in the geometry library
t = gf.components.text("Hello!")
r = gf.components.rectangle(size=[5, 10], layer=(2, 0))

# Add references to the new geometry to c, our blank component
text1 = c.add_ref(t)  # Add the text we created as a reference
# Using the << operator (identical to add_ref()), add the same geometry a second time
text2 = c << t
r = c << r  # Add the rectangle we created

# Now that the geometry has been added to "c", we can move everything around:
text1.movey(25)
text2.move([5, 30])
text2.rotate(45)
r.movex(-15)
r.movex(-15)

print(c)
c
myComponent2: uid 1, ports [], aliases [], 0 polygons, 3 references
../_images/notebooks_00_geometry_4_1.png
[2]:
myComponent2: uid 1, ports [], aliases [], 0 polygons, 3 references

Connect ports#

Any Component can have “Port”s in it which allow you to snap geometry together like legos.

Below is an example where we write a simple function to make a rectangular straight, assign ports to the ends of the rectangle, and then snap those rectangles together

[3]:
@gf.cell
def straight(length=10, width=1, layer=(1, 0)):
    WG = gf.Component(f"straight_{length}_{width}_{layer}")
    WG.add_polygon([(0, 0), (length, 0), (length, width), (0, width)], layer=layer)
    WG.add_port(
        name="o1", midpoint=[0, width / 2], width=width, orientation=180, layer=layer
    )
    WG.add_port(
        name="o2", midpoint=[length, width / 2], width=width, orientation=0, layer=layer
    )
    return WG


c = gf.Component("straights")

wg1 = c << straight(length=6, width=2.5, layer=1)
wg2 = c << straight(length=11, width=2.5, layer=2)
wg3 = c << straight(length=15, width=2.5, layer=3)
wg2.movey(10).rotate(10)
wg3.movey(20).rotate(15)

c
../_images/notebooks_00_geometry_6_0.png
[3]:
straights: uid 6, ports [], aliases [], 0 polygons, 3 references
[4]:
# Now we can connect everything together using the ports:

# Each straight has two ports: 'W0' and 'E0'.  These are arbitrary
# names defined in our straight() function above

# Let's keep wg1 in place on the bottom, and connect the other straights to it.
# To do that, on wg2 we'll grab the "W0" port and connect it to the "E0" on wg1:
wg2.connect("o1", wg1.ports["o2"])
# Next, on wg3 let's grab the "W0" port and connect it to the "E0" on wg2:
wg3.connect("o1", wg2.ports["o2"])

c
../_images/notebooks_00_geometry_7_0.png
[4]:
straights: uid 6, ports [], aliases [], 0 polygons, 3 references
[5]:
c.add_port("o1", port=wg1.ports["o1"])
c.add_port("o2", port=wg3.ports["o2"])
c
../_images/notebooks_00_geometry_8_0.png
[5]:
straights: uid 6, ports ['o1', 'o2'], aliases [], 0 polygons, 3 references

As you can see the red labels are for the component ports while blue labels are for the sub-ports (children ports)

Move and rotate references#

There are several actions we can take to move and rotate the geometry.

These actions include movement, rotation, and reflection.

[6]:
c = gf.Component("straights")

wg1 = c << straight(length=1, layer=(1, 0))
wg2 = c << straight(length=2, layer=(2, 0))
wg3 = c << straight(length=3, layer=(3, 0))

# Create and add a polygon from separate lists of x points and y points
# e.g. [(x1, x2, x3, ...), (y1, y2, y3, ...)]
poly1 = c.add_polygon([(8, 6, 7, 9), (6, 8, 9, 5)], layer=(4, 0))

# Alternatively, create and add a polygon from a list of points
# e.g. [(x1,y1), (x2,y2), (x3,y3), ...] using the same function
poly2 = c.add_polygon([(0, 0), (1, 1), (1, 3), (-3, 3)], layer=(5, 0))


# Shift the first straight we created over by dx = 10, dy = 5
wg1.move([10, 5])

# Shift the second straight over by dx = 10, dy = 0
wg2.move(origin=[0, 0], destination=[10, 0])

# Shift the third straight over by dx = 0, dy = 4
wg3.move([1, 1], [5, 5], axis="y")

poly1.movey(4)  # Same as specifying axis='y' in the move() command
poly2.movex(4)  # Same as specifying axis='x'' in the move() command
wg3.movex(0, 10)
# Moves "from" x=0 "to" x=10 (dx=10)

# wg1.rotate(45) # Rotate the first straight by 45 degrees around (0,0)
# wg2.rotate(30, center = [1,1]) # Rotate the second straight by 30 degrees around (1,1)
# wg1.reflect(p1 = [1,1], p2 = [1,3]) # Reflects wg3 across the line formed by p1 and p2
c
../_images/notebooks_00_geometry_11_0.png
[6]:
straights: uid 14, ports [], aliases [], 2 polygons, 3 references

Ports#

Although our straights wg1/wg2/wg3 have ports, they’re only references of the component c we’re working in, and c itself does not – it only draws the subports (ports of wg1, wg2, wg3) as a convenience. We need to add ports that we specifically want in our new component c. add_port() can take a port argument which allows you to pass it an underlying reference port to copy. You can also rename the port if you desire:

You can access the ports of a Component or ComponentReference

[7]:
wg2.ports
[7]:
{'o1': Port (name o1, midpoint [10.   0.5], width 1.0, orientation 180, layer (2, 0), port_type optical),
 'o2': Port (name o2, midpoint [12.   0.5], width 1.0, orientation 0, layer (2, 0), port_type optical)}

References#

Now that we have our component c which is a multi-straight component, we can add references to that component in a new blank canvas we’ll call c2. We’ll add two copies of c to c2, and shift one so we can see them both

[8]:
c2 = gf.Component("MultiMultiWaveguide")
wg1 = straight()
wg2 = straight(layer=(2, 0))
mwg1_ref = c2.add_ref(wg1)
mwg2_ref = c2.add_ref(wg2)
mwg2_ref.move(destination=[10, 10])
c2
../_images/notebooks_00_geometry_16_0.png
[8]:
MultiMultiWaveguide: uid 18, ports [], aliases [], 0 polygons, 2 references
[9]:
# Like before, let's connect mwg1 and mwg2 together
mwg1_ref.connect(port="o2", destination=mwg2_ref.ports["o1"])
c2
../_images/notebooks_00_geometry_17_0.png
[9]:
MultiMultiWaveguide: uid 18, ports [], aliases [], 0 polygons, 2 references

Labels#

We can also label (annotate) our devices, in order to record information directly into the final GDS file without putting any extra geometry onto any layer This label will display in a GDS viewer, but will not be rendered or printed like the polygons created by gf.components.text(). You can use for example gf.show() to see the labels in Klayout

[10]:
c2.add_label(text="First label", position=mwg1_ref.center)
c2.add_label(text="Second label", position=mwg2_ref.center)

# It's very useful for recording information about the devices or layout
c2.add_label(
    text=f"The x size of this\nlayout is {c2.xsize}",
    position=(c2.xmax, c2.ymax),
    layer=(10, 0),
)

# Again, note we have to write the GDS for it to be visible (view in KLayout)
c2.write_gds("MultiMultiWaveguideWithLabels.gds")
c2
2022-06-28 16:59:49.287 | INFO     | gdsfactory.component:write_gds:1062 - Write GDS to 'MultiMultiWaveguideWithLabels.gds'
../_images/notebooks_00_geometry_19_1.png
[10]:
MultiMultiWaveguide: uid 18, ports [], aliases [], 0 polygons, 2 references

Boolean shapes#

If you want to subtract one shape from another, merge two shapes, or perform an XOR on them, you can do that with the boolean() function.

The operation argument should be {not, and, or, xor, ‘A-B’, ‘B-A’, ‘A+B’}. Note that ‘A+B’ is equivalent to ‘or’, ‘A-B’ is equivalent to ‘not’, and ‘B-A’ is equivalent to ‘not’ with the operands switched

[11]:
import gdsfactory as gf

c = gf.Component()
e1 = c.add_ref(gf.components.ellipse(layer=(2, 0)))
e2 = c.add_ref(gf.components.ellipse(radii=(10, 6), layer=(2, 0))).movex(2)
e3 = c.add_ref(gf.components.ellipse(radii=(10, 4), layer=(2, 0))).movex(5)
c
/home/runner/work/gdsfactory/gdsfactory/gdsfactory/component.py:1054: UserWarning: Component 'Unnamed_64d54247' contains 1 Unnamed cells
  warnings.warn(
../_images/notebooks_00_geometry_21_1.png
[11]:
Unnamed_64d54247: uid 21, ports [], aliases [], 0 polygons, 3 references
[12]:
c2 = gf.geometry.boolean(A=[e1, e3], B=e2, operation="A-B", layer=(2, 0))
c2
../_images/notebooks_00_geometry_22_0.png
[12]:
boolean_3b7ce84b: uid 26, ports [], aliases [], 2 polygons, 0 references
[13]:
import gdsfactory as gf

c = gf.Component("rectangle_with_label")
r = c << gf.components.rectangle(size=(1, 1))
r.x = 0
r.y = 0
c.add_label(
    text="Demo label",
    position=(0, 0),
    layer=(1, 0),
)
c
../_images/notebooks_00_geometry_23_0.png
[13]:
rectangle_with_label: uid 27, ports [], aliases [], 0 polygons, 1 references

Move Reference by port#

[14]:
c = gf.Component("ref_port_sample")
mmi = c.add_ref(gf.components.mmi1x2())
bend = c.add_ref(gf.components.bend_circular(layer=(2, 0)))
c
../_images/notebooks_00_geometry_25_0.png
[14]:
ref_port_sample: uid 30, ports [], aliases [], 0 polygons, 2 references
[15]:
bend.connect("o1", mmi.ports["o2"])  # connects follow Source, destination syntax
c
../_images/notebooks_00_geometry_26_0.png
[15]:
ref_port_sample: uid 30, ports [], aliases [], 0 polygons, 2 references

Mirror reference#

By default the mirror works along the x=0 axis.

[16]:
c = gf.Component("ref_mirror")
mmi = c.add_ref(gf.components.mmi1x2())
bend = c.add_ref(gf.components.bend_circular(layer=(2, 0)))
c
../_images/notebooks_00_geometry_28_0.png
[16]:
ref_mirror: uid 37, ports [], aliases [], 0 polygons, 2 references
[17]:
mmi.mirror()
c
../_images/notebooks_00_geometry_29_0.png
[17]:
ref_mirror: uid 37, ports [], aliases [], 0 polygons, 2 references

Write GDS#

GDSII is the Standard format for exchanging CMOS and Photonic circuits.

You can write your Component to GDS file.

[18]:
c.write_gds("demo.gds")
2022-06-28 16:59:50.430 | INFO     | gdsfactory.component:write_gds:1062 - Write GDS to 'demo.gds'
[18]:
PosixPath('demo.gds')

You can see the GDS file in Klayout viewer.

Sometimes you also want to save the GDS together with metadata (settings, port names, widths, locations …) in YAML

[19]:
c.write_gds_with_metadata("demo.gds")
2022-06-28 16:59:50.440 | INFO     | gdsfactory.component:write_gds:1062 - Write GDS to 'demo.gds'
2022-06-28 16:59:50.454 | INFO     | gdsfactory.component:write_gds_with_metadata:1070 - Write YAML metadata to 'demo.yml'
[19]:
PosixPath('demo.gds')

based on phidl tutorial