YAML Place and AutoRoute#
You have two options for working with gdsfactory:
python flow: you define your layout using python functions (Parametric Cells), and connect them with routing functions.
YAML Place and AutoRoute: you define your Component as Place and Route in YAML. From the netlist you can simulate the Component or generate the layout.
The YAML format contains the schematic together with placement information.
YAML is a human readable version of JSON that you can use to define placements and routes
to define a a YAML Component you need to define:
instances: with each instance setting
placements: with X and Y
And optionally:
routes: between instance ports
connections: to connect instance ports to other ports (without routes)
ports: define input and output ports for the top level Component.
gdsfactory VSCode extension has a filewatcher for *.pic.yml
files that will show them live in klayout as you edit them.
The extension provides you with useful code snippets and filewatcher extension to see live modifications of *pic.yml
or *.py
files. Look for the telescope button on the top right of VSCode ðŸ”.
import gdsfactory as gf
from IPython.display import Code
filepath = "yaml_pics/pads.pic.yml"
Code(filepath, language="yaml+jinja")
instances:
bl:
component: pad
tl:
component: pad
br:
component: pad
tr:
component: pad
placements:
tl:
x: -200
y: 500
br:
x: 400
y: 400
tr:
x: 400
y: 600
routes:
electrical:
settings:
separation: 20
cross_section: metal_routing
links:
tl,e3: tr,e1
bl,e3: br,e1
optical:
settings:
radius: 100
port_type: optical
links:
bl,e4: br,e3
c = gf.read.from_yaml(filepath)
c
![../_images/7528e709584117020cc13acbfc7d2713123601aa26f42ccd9ac5cf5bf9350624.png](../_images/7528e709584117020cc13acbfc7d2713123601aa26f42ccd9ac5cf5bf9350624.png)
Lets start by defining the instances
and placements
section in YAML
Lets place an mmi_long
where you can place the o1
port at x=20, y=10
filepath = "yaml_pics/mmis.pic.yml"
Code(filepath, language="yaml+jinja")
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_long:
port: o1
x: 20
y: 10
mirror: False
c = gf.read.from_yaml(filepath)
c.plot()
![../_images/a5d08f778eeabf0d77c8305ac58538d0e1f598290764a4ba2fd9e98271f833a8.png](../_images/a5d08f778eeabf0d77c8305ac58538d0e1f598290764a4ba2fd9e98271f833a8.png)
ports#
You can expose any ports of any instance to the new Component with a ports
section in YAML
Lets expose all the ports from mmi_long
into the new component.
Ports are exposed as new_port_name: instance_name, port_name
filepath = "yaml_pics/ports_demo.pic.yml"
Code(filepath, language="yaml+jinja")
name: ports_demo
instances:
mmi_long:
component: mmi1x2
settings:
width_mmi: 4.5
length_mmi: 5
placements:
mmi_long:
port: o1
x: 20
y: 10
mirror: True
ports:
o3: mmi_long,o3
o2: mmi_long,o2
o1: mmi_long,o1
c = gf.read.from_yaml(filepath)
c.plot()
![../_images/b42d2530a26259a603a1d406f2bad06fc0917a6ba26ba2dc7d00ae8d835efde0.png](../_images/b42d2530a26259a603a1d406f2bad06fc0917a6ba26ba2dc7d00ae8d835efde0.png)
You can also define a mirror placement using a port
Try mirroring with other ports o2
, o3
or with a number as well as with a rotation 90
, 180
, 270
filepath = "yaml_pics/mirror_demo.pic.yml"
Code(filepath, language="yaml+jinja")
name: mirror_demo
instances:
mmi_long:
component: mmi1x2
settings:
width_mmi: 4.5
length_mmi: 5
placements:
mmi_long:
x: 0
y: 0
mirror: o1
rotation: 0
c = gf.read.from_yaml(filepath)
c.plot()
![../_images/1fd1903189c3c7b0dad75a118c0d2d51427f457b8162033deb7790e213c5f4bb.png](../_images/1fd1903189c3c7b0dad75a118c0d2d51427f457b8162033deb7790e213c5f4bb.png)
connections#
You can connect any two instances by defining a connections
section in the YAML file.
it follows the syntax instance_source,port : instance_destination,port
filepath = "yaml_pics/connections_demo.pic.yml"
Code(filepath, language="yaml+jinja")
name: connections_demo
instances:
b:
component: bend_circular
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:
port: o1
x: 10
y: 20
connections:
b,o1 : mmi_short,o2
mmi_long,o1: b,o2
ports:
o1: mmi_short,o1
o2: mmi_long,o2
o3: mmi_long,o3
c = gf.read.from_yaml(filepath)
c.plot()
![../_images/cdc491cab0571e1aaf5974bf68ddd3fa81c45a1accfb6af2ae1d06fb8c89d538.png](../_images/cdc491cab0571e1aaf5974bf68ddd3fa81c45a1accfb6af2ae1d06fb8c89d538.png)
Relative port placing
You can also place a component with respect to another instance port
You can also define an x and y offset with dx
and dy
filepath = "yaml_pics/relative_port_placing.pic.yml"
Code(filepath, language="yaml+jinja")
name: relative_port_placing
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:
port: o1
x: 0
y: 0
mmi_long:
port: o1
x: mmi_short,o2
y: mmi_short,o2
dx : 10
dy: -10
c = gf.read.from_yaml(filepath)
c.plot()
![../_images/d48805e5552ee924bb8376ea3b4575d7ec2d507a728d27ecfdce2ddeecb2b45e.png](../_images/d48805e5552ee924bb8376ea3b4575d7ec2d507a728d27ecfdce2ddeecb2b45e.png)
routes#
You can define routes between two instances by defining a routes
section in YAML
it follows the syntax
routes:
route_name:
links:
instance_source,port: instance_destination,port
settings: # for the route (optional)
waveguide: strip
width: 1.2
filepath = "yaml_pics/routes.pic.yml"
Code(filepath, language="yaml+jinja")
name: with_routes
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_long:
x: 100
y: 100
routes:
optical:
links:
mmi_short,o2: mmi_long,o1
settings:
cross_section:
cross_section: strip
c = gf.read.from_yaml(filepath)
c.plot()
![../_images/7608e00e984a5b3fe7f85de196586ef6fb556f83d2cb901771f0ce1a9c240f61.png](../_images/7608e00e984a5b3fe7f85de196586ef6fb556f83d2cb901771f0ce1a9c240f61.png)
instances, placements, connections, ports, routes#
Lets combine all you learned so far.
You can define the netlist connections of a component by a netlist in YAML format
Note that you define the connections as instance_source.port -> instance_destination.port
so the order is important and therefore you can only
change the position of the instance_destination
You can define several routes that will be connected using gf.routing.route_bundle
filepath = "yaml_pics/routes_mmi.pic.yml"
Code(filepath, language="yaml+jinja")
name: routes_mmi
instances:
mmi_bottom:
component: mmi2x2
mmi_top:
component: mmi2x2
placements:
mmi_top:
x: 100
y: 100
routes:
optical:
links:
mmi_bottom,o4: mmi_top,o1
mmi_bottom,o3: mmi_top,o2
c = gf.read.from_yaml(filepath)
c.plot()
![../_images/6a5b5ec47041a34dcf5a64dd42a4959030e25eb564de6137c849ca9659c3c05d.png](../_images/6a5b5ec47041a34dcf5a64dd42a4959030e25eb564de6137c849ca9659c3c05d.png)
You can also add custom component_factories to gf.read.from_yaml
@gf.cell
def pad_new(size=(100, 100), layer=(1, 0)):
c = gf.Component()
compass = c << gf.components.compass(size=size, layer=layer)
c.ports = compass.ports
return c
gf.get_active_pdk().register_cells(pad_new=pad_new)
c = pad_new()
c.plot()
![../_images/26e7983483ed62b3e30097706c82096a1e8c1b6d6bbd50f7767860289a1a4d3c.png](../_images/26e7983483ed62b3e30097706c82096a1e8c1b6d6bbd50f7767860289a1a4d3c.png)
filepath = "yaml_pics/new_factories.pic.yml"
Code(filepath, language="yaml+jinja")
name: new_factories
instances:
bot:
component: pad_new
top:
component: pad_new
placements:
top:
x: 0
y: 200
c = gf.read.from_yaml(filepath)
c.plot()
![../_images/f15d409b1684b6bcd3e1460ff5900da109face4c7f8d03c18851edcbdacf8ef1.png](../_images/f15d409b1684b6bcd3e1460ff5900da109face4c7f8d03c18851edcbdacf8ef1.png)
filepath = "yaml_pics/routes_custom.pic.yml"
Code(filepath, language="yaml+jinja")
instances:
t:
component: pad_array
settings:
orientation: 270
columns: 3
b:
component: pad_array
settings:
orientation: 90
columns: 3
placements:
t:
x: 200
y: 400
routes:
electrical:
settings:
start_straight_length: 150
end_straight_length: 150
cross_section: metal_routing
links:
t,e11: b,e11
t,e13: b,e13
c = gf.read.from_yaml(filepath)
c.plot()
/home/runner/work/gdsfactory/gdsfactory/gdsfactory/components/pad.py:101: UserWarning: orientation is deprecated, use port_orientation
warnings.warn("orientation is deprecated, use port_orientation")
![../_images/5a2cbf05cb5890e43aaad9a993c233412bf4eae109d225b07b13968bab4fe6f1.png](../_images/5a2cbf05cb5890e43aaad9a993c233412bf4eae109d225b07b13968bab4fe6f1.png)
Also, you can define route bundles with different settings and specify the route factory
as a parameter as well as the settings
for that particular route alias.
Arrays#
You can also define arrays and connect to the array ports.
sample_array = """
instances:
sa:
component: straight
na: 2
dax: 50
nb: 3
dby: 10
b:
component: bend_euler
connections:
b,o1: sa<0.2>,o2
routes:
b1:
links:
sa<0.0>,o2: sa<1.0>,o1
sa<0.1>,o2: sa<1.1>,o1
ports:
o1: b,o2
o2: sa<0.0>,o1
"""
gf.read.from_yaml(sample_array)
![../_images/247b2220d2d2168b54ea5c281c56ca02c7d815cefd313249949c1c49bbf12d3d.png](../_images/247b2220d2d2168b54ea5c281c56ca02c7d815cefd313249949c1c49bbf12d3d.png)
Jinja Pcells#
You use jinja templates in YAML cells to define Pcells.
from IPython.display import Code
import gdsfactory as gf
from gdsfactory.read import cell_from_yaml_template
jinja_yaml = """
default_settings:
length_mmi:
value: 10
description: "The length of the long MMI"
width_mmi:
value: 5
description: "The width of both MMIs"
instances:
mmi_long:
component: mmi1x2
settings:
width_mmi: {{ width_mmi }}
length_mmi: {{ length_mmi }}
mmi_short:
component: mmi1x2
settings:
width_mmi: {{ width_mmi }}
length_mmi: 5
connections:
mmi_long,o2: mmi_short,o1
ports:
o1: mmi_long,o1
o2: mmi_short,o2
o3: mmi_short,o3
"""
pic_filename = "demo_jinja.pic.yml"
with open(pic_filename, mode="w") as f:
f.write(jinja_yaml)
pic_cell = cell_from_yaml_template(pic_filename, name="demo_jinja")
gf.get_active_pdk().register_cells(
demo_jinja=pic_cell
) # let's register this cell so we can use it later
Code(filename=pic_filename, language="yaml+jinja")
default_settings:
length_mmi:
value: 10
description: "The length of the long MMI"
width_mmi:
value: 5
description: "The width of both MMIs"
instances:
mmi_long:
component: mmi1x2
settings:
width_mmi: {{ width_mmi }}
length_mmi: {{ length_mmi }}
mmi_short:
component: mmi1x2
settings:
width_mmi: {{ width_mmi }}
length_mmi: 5
connections:
mmi_long,o2: mmi_short,o1
ports:
o1: mmi_long,o1
o2: mmi_short,o2
o3: mmi_short,o3
You’ll see that this generated a python function, with a real signature, default arguments, docstring and all!
help(pic_cell)
Help on function demo_jinja:
demo_jinja(*, length_mmi=10, width_mmi=5)
demo_jinja: a templated yaml cell. This cell accepts keyword arguments only
Keyword Args:
length_mmi: The length of the long MMI
width_mmi: The width of both MMIs
You can invoke this cell without arguments to see the default implementation
c = pic_cell()
c.plot()
![../_images/b49745ec9c0bf30adc85e5bbeb2380ebf7c222d8e29cc07bc942c9a131e60e62.png](../_images/b49745ec9c0bf30adc85e5bbeb2380ebf7c222d8e29cc07bc942c9a131e60e62.png)
Or you can provide arguments explicitly, like a normal cell. Note however that yaml-based cells only accept keyword arguments, since yaml dictionaries are inherently unordered.
c = pic_cell(length_mmi=100)
c.plot()
![../_images/6c5d158df38951fe78a71bb2a1f56982b49c05819b310adc4393ccb3a8a15bf2.png](../_images/6c5d158df38951fe78a71bb2a1f56982b49c05819b310adc4393ccb3a8a15bf2.png)