Components with hierarchy#

image0

You can define some components (waveguides, bends, couplers) as a stand alone components, with basic input parameters (width, length, radius …)

Then you can re-use those components in a more complex hiearchical components.

gdsfactory does this by passing the higher level component with the lower level functions to build the components.

You can customize any of the functions thanks to functools.partial

[1]:
import gdsfactory as gf

# gf.CONF.plotter = 'holoviews'
2022-06-28 17:01:04.093 | INFO     | gdsfactory.config:<module>:52 - Load '/home/runner/work/gdsfactory/gdsfactory/gdsfactory' 5.11.4
[2]:
@gf.cell
def bend_with_straight(
    bend=gf.components.bend_euler,
    straight=gf.components.straight,
) -> gf.Component:
    c = gf.Component()
    b = bend()
    s = straight()

    bref = c << b
    sref = c << s

    sref.connect("o2", bref.ports["o2"])
    c.info["length"] = b.info["length"] + s.info["length"]
    return c


c = bend_with_straight()
print(c.metadata.info.length)
c
26.637
../_images/notebooks_04_components_hierarchy_2_1.png
[2]:
bend_with_straight: uid 0, ports [], aliases [], 0 polygons, 2 references

Lets customize the functions that we pass. For example, we want to increase the radius of the bend from the default 10um to 20um.

[3]:
c = gf.components.bend_circular()
c
../_images/notebooks_04_components_hierarchy_4_0.png
[3]:
bend_circular: uid 5, ports ['o1', 'o2'], aliases [], 4 polygons, 0 references

functools.partial#

Partial lets you define different default parameters for a function, so you can modify the settings for the child cells.

[4]:
from functools import partial
[5]:
bend20 = partial(gf.components.bend_circular, radius=20)
b = bend20()
b
../_images/notebooks_04_components_hierarchy_7_0.png
[5]:
bend_circular_radius20: uid 7, ports ['o1', 'o2'], aliases [], 4 polygons, 0 references
[6]:
type(bend20)
[6]:
functools.partial
[7]:
bend20.func.__name__
[7]:
'bend_circular'
[8]:
bend20.keywords
[8]:
{'radius': 20}
[9]:
b = bend_with_straight(bend=bend20)
print(b.metadata.info.length)
b
41.416
../_images/notebooks_04_components_hierarchy_11_1.png
[9]:
bend_with_straight_e917218d: uid 9, ports [], aliases [], 0 polygons, 2 references
[10]:
# You can still modify the bend to have any bend radius
b3 = bend20(radius=10)
b3
../_images/notebooks_04_components_hierarchy_12_0.png
[10]:
bend_circular_radius10: uid 10, ports ['o1', 'o2'], aliases [], 4 polygons, 0 references

PDK custom fab#

You can define a new PDK by creating function that customize partial parameters of the generic functions.

Lets say that this PDK uses layer (41, 0) for the pads (instead of the layer used in the generic pad function).

You can also access functools.partial from gf.partial

[11]:
import gdsfactory as gf

pad = gf.partial(gf.components.pad, layer=(41, 0))
[12]:
c = pad()
c
../_images/notebooks_04_components_hierarchy_15_0.png
[12]:
pad_layer41__0: uid 11, ports ['e1', 'e2', 'e3', 'e4', 'pad'], aliases [], 0 polygons, 1 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.

[13]:
c1 = gf.components.straight()
c1
../_images/notebooks_04_components_hierarchy_17_0.png
[13]:
straight: uid 3, ports ['o1', 'o2'], aliases [], 4 polygons, 0 references
[14]:
straight_wide = gf.partial(gf.components.straight, width=3)
c3 = straight_wide()
c3
../_images/notebooks_04_components_hierarchy_18_0.png
[14]:
straight_width3: uid 13, ports ['o1', 'o2'], aliases [], 4 polygons, 0 references
[15]:
c1 = gf.components.straight(width=3)
c1
../_images/notebooks_04_components_hierarchy_19_0.png
[15]:
straight_width3: uid 13, ports ['o1', 'o2'], aliases [], 4 polygons, 0 references
[16]:
c2 = gf.add_tapers(c1)
c2
../_images/notebooks_04_components_hierarchy_20_0.png
[16]:
straight_width3_add_tap_df896968: uid 15, ports ['o1', 'o2'], aliases [], 0 polygons, 3 references
[17]:
c2.metadata_child.changed  # You can still access the child metadata
[17]:
{'width': 3}
[18]:
c3 = gf.routing.add_fiber_array(c2, with_loopback=False)
c3
../_images/notebooks_04_components_hierarchy_22_0.png
[18]:
straight_width3_add_tap_a215cee2: uid 19, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 9 references
[19]:
c3.metadata_child.changed  # You can still access the child metadata
[19]:
{'width': 3}

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

[20]:
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_04_components_hierarchy_25_0.png
[20]:
straight_width3_add_tap_a215cee2: uid 19, 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

For example:

[21]:
add_tapers_fiber_array = toolz.compose_left(add_tapers, add_fiber_array)
c4 = add_tapers_fiber_array(c1)
c4
../_images/notebooks_04_components_hierarchy_27_0.png
[21]:
straight_width3_add_tap_a215cee2: uid 19, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 9 references

is equivalent to

[22]:
c5 = add_fiber_array(add_tapers(c1))
c5
../_images/notebooks_04_components_hierarchy_29_0.png
[22]:
straight_width3_add_tap_a215cee2: uid 19, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 9 references

as well as equivalent to

[23]:
add_tapers_fiber_array = toolz.compose(add_fiber_array, add_tapers)
c6 = add_tapers_fiber_array(c1)
c6
../_images/notebooks_04_components_hierarchy_31_0.png
[23]:
straight_width3_add_tap_a215cee2: uid 19, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 9 references

or

[24]:
c7 = toolz.pipe(c1, add_tapers, add_fiber_array)
c7
../_images/notebooks_04_components_hierarchy_33_0.png
[24]:
straight_width3_add_tap_a215cee2: uid 19, ports ['vertical_te_00', 'vertical_te_01'], aliases [], 0 polygons, 9 references
[25]:
c7.metadata_child.changed  # You can still access the child metadata
[25]:
{'width': 3}
[26]:
c7.metadata.child.child.name, c7.metadata.child.child.function_name
[26]:
('straight_width3', 'straight')
[27]:
c7.metadata.child.name, c7.metadata.child.function_name
[27]:
('straight_width3_add_tap_df896968', 'add_tapers')
[28]:
c7.metadata.name, c7.metadata.function_name
[28]:
('straight_width3_add_tap_a215cee2', 'add_fiber_array')
[29]:
c7.metadata.changed.keys()
[29]:
dict_keys(['component', 'with_loopback'])