Routing with different CrossSections#
When working in a technologies with multiple waveguide cross-sections, it is useful to differentiate intent layers for the different waveguide types and assign default transitions between those layers. In this way, you can easily autotransition between the different cross-section types.
Setting up your PDK#
Let’s first set up a sample PDK with the following key features:
Rib and strip cross-sections with differentiated intent layers.
Default transitions for each individual cross-section type (width tapers), and also a rib-to-strip transition component to switch between them.
Preferred routing cross-sections defined for the all-angle router.
from functools import partial
import gdsfactory as gf
from gdsfactory.cross_section import xs_rc, strip, rib
from gdsfactory.generic_tech import get_generic_pdk
from gdsfactory.read import cell_from_yaml_template
from gdsfactory.routing import all_angle
from gdsfactory.typings import CrossSectionSpec
gf.config.enable_offgrid_ports()
gf.CONF.display_type = "klayout"
generic_pdk = get_generic_pdk()
# define our rib and strip waveguide intent layers
RIB_INTENT_LAYER = (2000, 11)
STRIP_INTENT_LAYER = (2001, 11)
generic_pdk.layers.update(
RIB_INTENT_LAYER=RIB_INTENT_LAYER, STRIP_INTENT_LAYER=STRIP_INTENT_LAYER
)
# create strip and rib cross-sections, with differentiated intent layers
strip_with_intent = partial(
strip,
cladding_layers=[
"STRIP_INTENT_LAYER"
], # keeping WG layer is nice for compatibility
cladding_offsets=[0],
gap=2,
)
rib_with_intent = partial(
rib,
cladding_layers=["RIB_INTENT_LAYER"], # keeping WG layer is nice for compatibility
cladding_offsets=[0],
gap=5,
)
# create strip->rib transition component
@gf.cell
def strip_to_rib(width1: float = 0.5, width2: float = 0.5) -> gf.Component:
c = gf.Component()
taper = c << gf.c.taper_strip_to_ridge(width1=width1, width2=width2)
c.add_port(
"o1",
port=taper.ports["o1"],
layer=(1, 0),
cross_section=strip_with_intent(width=width1),
width=width1,
)
c.add_port(
"o2",
port=taper.ports["o2"],
layer=(1, 0),
cross_section=rib_with_intent(width=width2),
width=width2,
)
c.absorb(taper)
c.info.update(taper.info)
c.add_route_info(cross_section="r2s", length=c.info["length"])
return c
# also define a rib->strip component for transitioning the other way
@gf.cell
def rib_to_strip(width1: float = 0.5, width2: float = 0.5) -> gf.Component:
c = gf.Component()
taper = c << strip_to_rib(width1=width2, width2=width1)
c.add_port("o1", port=taper.ports["o2"])
c.add_port("o2", port=taper.ports["o1"])
c.info.update(taper.info)
return c
@gf.cell
def taper_single_cross_section(
cross_section: CrossSectionSpec = "xs_sc", width1: float = 0.5, width2: float = 1.0
) -> gf.Component:
"""Single-layer taper components."""
cs1 = gf.get_cross_section(cross_section, width=width1)
cs2 = gf.get_cross_section(cross_section, width=width2)
length = abs(width1 - width2) * 10
c = gf.Component()
ref = c << gf.components.taper_cross_section_linear(cs1, cs2, length=length)
c.add_ports(ref.ports)
c.info["length"] = length
return c
taper_strip = partial(taper_single_cross_section, cross_section="xs_sc")
taper_rib = partial(taper_single_cross_section, cross_section="xs_rc")
# make a new PDK with our required layers, cross-sections, and default transitions
multi_wg_pdk = gf.Pdk(
base_pdk=generic_pdk,
name="multi_wg_demo",
layers={
"RIB_INTENT": RIB_INTENT_LAYER,
"STRIP_INTENT": STRIP_INTENT_LAYER,
},
cross_sections={
"xs_rc": rib_with_intent,
"xs_sc": strip_with_intent,
},
layer_transitions={
RIB_INTENT_LAYER: taper_rib,
STRIP_INTENT_LAYER: taper_strip,
(RIB_INTENT_LAYER, STRIP_INTENT_LAYER): rib_to_strip,
(STRIP_INTENT_LAYER, RIB_INTENT_LAYER): strip_to_rib,
},
layer_views=generic_pdk.layer_views,
)
# activate our new PDK
multi_wg_pdk.activate()
# set to prefer rib routing when there is enough space
all_angle.LOW_LOSS_CROSS_SECTIONS.insert(0, "xs_rc")
2024-10-31 18:40:37.027 | WARNING | gdsfactory.pdk:activate:289 - UserWarning: base_pdk is deprecated. Use base_pdks instead
Let’s quickly demonstrate our new cross-sections and transition component.
# demonstrate rib and strip waveguides in our new PDK
strip_width = 1
rib_width = 0.7
c = gf.Component()
strip_wg = c << gf.c.straight(cross_section=strip_with_intent(width=strip_width))
rib_wg = c << gf.c.straight(cross_section=rib_with_intent(width=rib_width))
taper = c << strip_to_rib(width1=strip_width, width2=rib_width)
taper.connect("o1", strip_wg.ports["o2"])
rib_wg.connect("o1", taper.ports["o2"])
c.show()
c.plot()
2024-10-31 18:40:37.035 | WARNING | __main__:<module>:6 - UserWarning: gap is deprecated. Pass this parameter to the routing function instead.
2024-10-31 18:40:37.036 | WARNING | __main__:<module>:6 - UserWarning: gap is deprecated.
2024-10-31 18:40:37.041 | WARNING | __main__:<module>:7 - UserWarning: gap is deprecated. Pass this parameter to the routing function instead.
2024-10-31 18:40:37.042 | WARNING | __main__:<module>:7 - UserWarning: gap is deprecated.
2024-10-31 18:40:37.049 | WARNING | gdsfactory.pdk:get_cross_section:519 - UserWarning: gap is deprecated. Pass this parameter to the routing function instead.
2024-10-31 18:40:37.050 | WARNING | gdsfactory.pdk:get_cross_section:519 - UserWarning: gap is deprecated.
2024-10-31 18:40:37.052 | WARNING | gdsfactory.pdk:get_cross_section:511 - UserWarning: CrossSection.copy() only modifies the attributes of the first section.
2024-10-31 18:40:37.056 | WARNING | __main__:strip_to_rib:49 - UserWarning: gap is deprecated. Pass this parameter to the routing function instead.
2024-10-31 18:40:37.057 | WARNING | __main__:strip_to_rib:49 - UserWarning: gap is deprecated.
2024-10-31 18:40:37.058 | WARNING | __main__:strip_to_rib:56 - UserWarning: gap is deprecated. Pass this parameter to the routing function instead.
2024-10-31 18:40:37.059 | WARNING | __main__:strip_to_rib:56 - UserWarning: gap is deprecated.
2024-10-31 18:40:37.062 | WARNING | gdsfactory.show:show:47 - UserWarning: Unnamed cells, 1 in 'Unnamed_364b5fe1'
2024-10-31 18:40:37.063 | WARNING | gdsfactory.klive:show:49 - UserWarning: Could not connect to klive server. Is klayout open and klive plugin installed?
2024-10-31 18:40:37.395 | WARNING | gdsfactory.component:plot_klayout:1646 - UserWarning: Unnamed cells, 1 in 'Unnamed_364b5fe1'
Autotransitioning with the All-Angle Router#
Now that our PDK and settings are all configured, we can see how the all-angle router will auto-transition for us between different cross sections.
Because we are using the low-loss connector by default, and the highest priority cross section is rib, we will see rib routing anywhere there is enough space to transition.
from pathlib import Path
from IPython.display import Code, display
from gdsfactory.read import cell_from_yaml_template
def show_yaml_pic(filepath):
gf.clear_cache()
cell_name = filepath.stem
return display(
Code(filename=filepath, language="yaml+jinja"),
cell_from_yaml_template(filepath, name=cell_name)(),
)
# load a yaml PIC, and see how it looks with our new technology
sample_dir = Path("yaml_pics")
basic_sample_fn = sample_dir / "aar_indirect.pic.yml"
show_yaml_pic(basic_sample_fn)
2024-10-31 18:40:37.563 | WARNING | gdsfactory.read.from_yaml:from_yaml:692 - UserWarning: prefix is deprecated and will be removed soon. _from_yaml
2024-10-31 18:40:37.566 | WARNING | gdsfactory.pdk:get_cross_section:519 - UserWarning: gap is deprecated. Pass this parameter to the routing function instead.
2024-10-31 18:40:37.567 | WARNING | gdsfactory.pdk:get_cross_section:519 - UserWarning: gap is deprecated.
2024-10-31 18:40:37.570 | WARNING | gdsfactory.pdk:get_cross_section:511 - UserWarning: CrossSection.copy() only modifies the attributes of the first section.
instances:
mmi_long:
component: mmi1x2
settings:
width_mmi: 4.5
length_mmi: 10
mmi_short:
component: mmi1x2
settings:
width_mmi: 4.5
length_mmi: 5
placements:
mmi_short:
rotation: 180
mmi_long:
port: o1
rotation: -10 # port vectors no longer intersect
x: mmi_short,o1
y: mmi_short,o1
dx: 50
dy: 20
routes:
optical:
routing_strategy: get_bundle_all_angle
links:
mmi_short,o1: mmi_long,o1
ports:
o1: mmi_short,o2
o2: mmi_short,o3
2024-10-31 18:40:37.813 | WARNING | gdsfactory.component:_write_library:1934 - UserWarning: Component aar_indirect.pic has invalid transformations. Try component.flatten_offgrid_references() first.
2024-10-31 18:40:37.815 | WARNING | gdsfactory.klive:show:49 - UserWarning: Could not connect to klive server. Is klayout open and klive plugin installed?
aar_indirect.pic: uid 59d8273a, ports ['o1', 'o2'], references ['mmi_long', 'mmi_short', 'bend_euler_1', 'bend_euler_2', 'straight_1'], 0 polygons
c = gf.read.from_yaml(yaml_str=basic_sample_fn.read_text())
c.plot()
2024-10-31 18:40:37.961 | WARNING | gdsfactory.component:_write_library:1934 - UserWarning: Component Unnamed_c0708b5d has invalid transformations. Try component.flatten_offgrid_references() first.
2024-10-31 18:40:37.963 | WARNING | gdsfactory.component:plot_klayout:1646 - UserWarning: Unnamed cells, 1 in 'Unnamed_c0708b5d'
You can see that since gap
is defined in our cross-sections, the bundle router also intelligently picks the appropriate bundle spacing for the cross section used.
Notice how the strip waveguide bundles are much more tightly packed than the rib waveguide bundles in the example below.
basic_sample_fn2 = sample_dir / "aar_bundles03.pic.yml"
show_yaml_pic(basic_sample_fn2)
2024-10-31 18:40:38.197 | WARNING | gdsfactory.routing.auto_taper:taper_to_cross_section:63 - UserWarning: No registered width taper for layer (1, 0). Skipping.
default_settings:
n_bundle:
value: 3
description: "The number of routes in the bundle"
instances:
{% for i in range(n_bundle) %}
wg_start_{{ i }}:
component: straight
wg_end_{{ i }}:
component: straight
{% endfor %}
placements:
{% for i in range(n_bundle) %}
wg_end_{{ i }}:
rotation: 180
y: {{ i * 4 }}
wg_start_{{ i }}:
port: o1
rotation: {{ 20 - i * 5 }} # ports need not be aligned
x: wg_end_0,o1
y: wg_end_0,o1
dx: 120
dy: {{ 80 + i * 4 }}
{% endfor %}
routes:
optical:
routing_strategy: get_bundle_all_angle
settings:
steps:
- ds: 65
exit_angle: 90
- ds: 110
exit_angle: 0
- ds: 81.5
exit_angle: -65
- ds: 52
exit_angle: -158
cross_section: xs_sc # explicitly using xs_sc routing
- ds: 67.5
exit_angle: 100
cross_section: # explicitly using xs_sc routing with custom width
cross_section: xs_sc
settings:
width: 1.2
links:
{% for i in [2, 1, 0] %}
wg_end_{{ i }},o1: wg_start_{{ i }},o1
{% endfor %}
ports:
o1: wg_start_0,o2
o2: wg_end_0,o2
o3: wg_start_1,o2
o4: wg_end_1,o2
o5: wg_start_2,o2
o6: wg_end_2,o2
2024-10-31 18:40:38.291 | WARNING | gdsfactory.component:_write_library:1934 - UserWarning: Component aar_bundles03.pic has invalid transformations. Try component.flatten_offgrid_references() first.
aar_bundles03.pic: uid fe2bafe2, ports ['o1', 'o2', 'o3', 'o4', 'o5', 'o6'], references ['wg_start_0', 'wg_end_0', 'wg_start_1', 'wg_end_1', 'wg_start_2', 'wg_end_2', 'straight_1', 'bend_euler_1', 'straight_2', 'bend_euler_2', 'straight_3', 'bend_euler_3', 'straight_4', 'bend_euler_4', 'straight_5', 'bend_euler_5', 'straight_6', 'bend_euler_6', 'straight_7', 'straight_8', 'bend_euler_7', 'straight_9', 'bend_euler_8', 'straight_10', 'bend_euler_9', 'straight_11', 'bend_euler_10', 'straight_12', 'bend_euler_11', 'straight_13', 'bend_euler_12', 'straight_14', 'straight_15', 'bend_euler_13', 'straight_16', 'bend_euler_14', 'straight_17', 'bend_euler_15', 'straight_18', 'bend_euler_16', 'straight_19', 'bend_euler_17', 'straight_20', 'bend_euler_18', 'straight_21'], 0 polygons
f = cell_from_yaml_template(basic_sample_fn2, name="sample_transition")
c = f()
c.plot()
2024-10-31 18:40:38.439 | WARNING | gdsfactory.component:_write_library:1934 - UserWarning: Component sample_transition has invalid transformations. Try component.flatten_offgrid_references() first.