Cells inside cells (containers)

Cells must have a unique name. If you change something in the cell, such as add a label, a grating coupler or just a pin and keep the same name it is very likely that you will have a name conflict when you combine two cells with the same name but different geometry.

To solve this name collision, you can create a new component and add a reference of the old cell into the new cell. For example, if you can add padding to a Component called ‘straight_L3’ and you can define a function that returns a new cell that also contains a reference to the original cell.

Cells that have a component argument automatically will copy the component settings into the new cell.

[1]:
from typing import Tuple, Optional, List
import gdsfactory as gf


@gf.cell
def add_padding(
    component: gf.Component,
    layers: List[Tuple[int, int]] = [gf.LAYER.DEVREC],
    default: float = 5.0,
    top: Optional[float] = None,
    bottom: Optional[float] = None,
    right: Optional[float] = None,
    left: Optional[float] = None,
) -> gf.Component:
    """Adds padding layers to a container.

    Args:
        component
        layers: list of layers
        suffix for name
        default: default padding
        top: north padding
        bottom: south padding
        right: east padding
        left: west padding
    """

    container = gf.Component()
    container << component

    c = component
    top = top if top else default
    bottom = bottom if bottom else default
    right = right if right else default
    left = left if left else default

    points = [
        [c.xmin - left, c.ymin - bottom],
        [c.xmax + right, c.ymin - bottom],
        [c.xmax + right, c.ymax + top],
        [c.xmin - left, c.ymax + top],
    ]

    for layer in layers:
        container.add_polygon(points, layer=layer)
    return container


wg = gf.components.straight()
wg
2021-09-25 16:20:02.789 | INFO     | gdsfactory.config:<module>:51 - 3.2.9
../_images/notebooks_05_container_1_1.png
[1]:
straight: uid 1, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references
[2]:
wg_padding = add_padding(component=wg)
wg_padding
../_images/notebooks_05_container_2_0.png
[2]:
add_padding_componentstraight: uid 2, ports [], aliases [], 1 polygons, 1 references
[3]:
wg.settings
[3]:
{'length': 10.0, 'npoints': 2, 'with_cladding_box': True}
[4]:
wg_padding.settings
[4]:
{'layers': [(68, 0)],
 'default': 5.0,
 'top': None,
 'bottom': None,
 'right': None,
 'left': None,
 'component': straight: uid 1, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references}

You can use many containers from gf. Also note that many functions have a container version that creates a new cell and a non container version that operates over the cell.

Make sure you only use the function that operate over the cell if you plan to only use that new version of the cell (to avoid name conflicts)

[5]:
import gdsfactory as gf

c = gf.components.straight()
c
../_images/notebooks_05_container_6_0.png
[5]:
straight: uid 3, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references
[6]:
c = gf.components.straight()
cc = gf.add_padding(component=c, default=5)
cc
../_images/notebooks_05_container_7_0.png
[6]:
straight: uid 4, ports ['o1', 'o2'], aliases [], 2 polygons, 0 references
[7]:
print(cc.name)  # matches original name
straight
[8]:
c = gf.components.straight()
cc = gf.add_padding_container(component=c, default=5)
cc
../_images/notebooks_05_container_9_0.png
[8]:
add_padding_container_c_bcada407: uid 6, ports ['o1', 'o2'], aliases [], 1 polygons, 1 references
[9]:
print(cc.name)  # new name
add_padding_container_c_bcada407
[10]:
c = gf.components.straight()
cc = gf.components.extension.extend_ports(component=c)
cc
../_images/notebooks_05_container_11_0.png
[10]:
extend_ports_componentstraight: uid 9, ports ['o1', 'o2'], aliases [], 2 polygons, 1 references
[11]:
c = gf.components.spiral_inner_io()
cc = gf.add_grating_couplers.add_grating_couplers_with_loopback_fiber_array(component=c, with_loopback=False)
cc
../_images/notebooks_05_container_12_0.png
[11]:
add_grating_couplers_wi_dede53e3: uid 48, ports [], aliases [], 0 polygons, 3 references
[12]:
c = gf.components.straight()
cc = gf.routing.add_fiber_array(component=c)
cc
../_images/notebooks_05_container_13_0.png
[12]:
add_fiber_array_compone_ae68013b: uid 53, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 22 references
[13]:
c = gf.components.straight()
cc = gf.routing.add_fiber_single(component=c)
cc
../_images/notebooks_05_container_14_0.png
[13]:
add_fiber_single_compon_85a6a586: uid 66, ports ['vertical_te_00', 'vertical_te_1'], aliases [], 0 polygons, 8 references

Composing functions

You can combine more complex functions out of smaller functions.

Lets say that we want to add tapers and grating couplers to a wide waveguide.

[14]:
c1 = gf.c.straight()
c1
../_images/notebooks_05_container_16_0.png
[14]:
straight: uid 77, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references
[15]:
straight_wide = gf.partial(gf.components.straight, width=3)
c3 = straight_wide()
c3
../_images/notebooks_05_container_17_0.png
[15]:
straight_width3: uid 78, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references
[16]:
c1 = gf.c.straight(width=3)
c1
../_images/notebooks_05_container_18_0.png
[16]:
straight_width3: uid 79, ports ['o1', 'o2'], aliases [], 1 polygons, 0 references
[17]:
c2 = gf.add_tapers(c1)
c2
../_images/notebooks_05_container_19_0.png
[17]:
add_tapers_componentstr_950ec075: uid 80, ports ['o1', 'o2'], aliases [], 0 polygons, 3 references
[18]:
c3 = gf.routing.add_fiber_array(c2, with_loopback=False)
c3
../_images/notebooks_05_container_20_0.png
[18]:
add_fiber_array_compone_e612a1d1: uid 85, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 9 references

Lets do it with a single step thanks to toolz.pipe

[19]:
import toolz

add_fiber_array = gf.partial(gf.routing.add_fiber_array, with_loopback=False)
add_tapers = gf.add_tapers

# pipe is more readable than the equivalent add_fiber_array(add_tapers(c1))
c3 = toolz.pipe(c1, add_tapers, add_fiber_array)
c3
../_images/notebooks_05_container_22_0.png
[19]:
add_fiber_array_compone_e612a1d1: uid 96, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 9 references

we can even combine add_tapers and add_fiber_array thanks to toolz.compose or toolz.compose

[20]:
add_tapers_fiber_array = toolz.compose_left(add_tapers, add_fiber_array)
add_tapers_fiber_array(c1)
../_images/notebooks_05_container_24_0.png
[20]:
add_fiber_array_compone_e612a1d1: uid 107, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 9 references
[21]:
add_tapers_fiber_array = toolz.compose(add_fiber_array, add_tapers)
add_tapers_fiber_array(c1)
../_images/notebooks_05_container_25_0.png
[21]:
add_fiber_array_compone_e612a1d1: uid 118, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 9 references
[22]:
toolz.pipe?
[23]:
toolz.compose?