Netlist extractor YAML#

Any component can extract its netlist with get_netlist

While gf.read.from_yaml converts a YAML Dict into a Component

get_netlist converts Component into a YAML Dict

import gdsfactory as gf
from omegaconf import OmegaConf
c = gf.components.mzi()
c.plot()
../_images/8f9f532047924f2565306ecb3a071b1022d96018db14fed8f2e5dd65e5235d3e.png
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b5358ef90>
../_images/74b326652571158d74c2e7938d8d7169a142de3c0b348b1c1c17e2c2774fa66b.png
c = gf.components.ring_single()
c.plot()
../_images/1375839967f22cd6dd75eed7618537188d08db4ff15e9fb6965c8c0c04b479a4.png
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b4e4e5110>
../_images/d2fb8a4e57769409f1418bef269804c08e47a819b8b5d7fc093ddf8e86849cd5.png
n = c.get_netlist()
c.write_netlist("ring.yml")
n = OmegaConf.load("ring.yml")
i = list(n["instances"].keys())
i
['bend_euler_1',
 'bend_euler_2',
 'coupler_ring_1',
 'straight_1',
 'straight_2',
 'straight_3']
instance_name0 = i[0]
n["instances"][instance_name0]["settings"]
{'angle': 90.0, 'cross_section': {'bbox_layers': None, 'bbox_offsets': None, 'components_along_path': [], 'radius': 10.0, 'radius_min': 5.0, 'sections': [{'hidden': False, 'insets': None, 'layer': 'WG', 'name': '_default', 'offset': 0.0, 'offset_function': None, 'port_names': ['o1', 'o2'], 'port_types': ['optical', 'optical'], 'simplify': None, 'width': 0.5, 'width_function': None}]}, 'direction': 'ccw', 'npoints': None, 'p': 0.5, 'radius': None, 'with_arc_floorplan': True}

Instance names#

By default get netlist names each instance with the name of the reference

@gf.cell
def mzi_with_bend_automatic_naming():
    c = gf.Component()
    mzi = c.add_ref(gf.components.mzi())
    bend = c.add_ref(gf.components.bend_euler())
    bend.connect("o1", mzi.ports["o2"])
    return c


c = mzi_with_bend_automatic_naming()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b4e57a790>
../_images/2d11114c7e4f663f43bfa6d42eb2295f9cc1eec01ed25304647b628961b64563.png
@gf.cell
def mzi_with_bend_deterministic_names_using_alias():
    c = gf.Component()
    mzi = c.add_ref(gf.components.mzi(), alias="my_mzi")
    bend = c.add_ref(gf.components.bend_euler(), alias="my_bend")
    bend.connect("o1", mzi.ports["o2"])
    return c


c = mzi_with_bend_deterministic_names_using_alias()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b53599390>
../_images/e1c974d40c226a2bead5f5f421dbf75092320ce6c498d9ed5c0a6dacdc0b5fb7.png
c = gf.components.mzi()
c.plot()
../_images/8f9f532047924f2565306ecb3a071b1022d96018db14fed8f2e5dd65e5235d3e.png
c = gf.components.mzi()
n = c.get_netlist()
print(c.get_netlist().keys())
dict_keys(['connections', 'instances', 'placements', 'ports', 'name'])
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b4e4e8a50>
../_images/74b326652571158d74c2e7938d8d7169a142de3c0b348b1c1c17e2c2774fa66b.png
n.keys()
dict_keys(['connections', 'instances', 'placements', 'ports', 'name'])

warnings#

Lets make a connectivity error, for example connecting ports on the wrong layer

@gf.cell
def mmi_with_bend():
    c = gf.Component()
    mmi = c.add_ref(gf.components.mmi1x2(), alias="mmi")
    bend = c.add_ref(gf.components.bend_euler(layer=(2, 0)), alias="bend")
    bend.connect("o1", mmi.ports["o2"], allow_layer_mismatch=True)
    return c


c = mmi_with_bend()
gf.remove_from_cache(c)
c.plot()
../_images/09fd754106561421ab819227d06fead94310293c5efbdb2e75c0cd04c806bef1.png
n = c.get_netlist()
print(n["warnings"])
{'optical': {'unconnected_ports': [{'ports': ['mmi,o1', 'mmi,o3', 'bend,o2'], 'values': [[-10.0, 0.0], [15.5, -0.625], [25.5, 10.625]], 'message': '3 unconnected optical ports!'}]}}
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b4e5c7090>
../_images/eae3fc0cd477eaadd7a79e05b08ab59f3601271a1cb6c0dbaa9d6f5a27e21474.png

get_netlist_recursive#

When you do get_netlist() for a component it will only show connections for the instances that belong to that component. So despite having a lot of connections, it will show only the meaningful connections for that component. For example, a ring has a ring_coupler. If you want to dig deeper, the connections that made that ring coupler are still available.

get_netlist_recursive() returns a recursive netlist.

c = gf.components.ring_single()
c.plot()
../_images/1375839967f22cd6dd75eed7618537188d08db4ff15e9fb6965c8c0c04b479a4.png
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b535645d0>
../_images/d2fb8a4e57769409f1418bef269804c08e47a819b8b5d7fc093ddf8e86849cd5.png
c = gf.components.ring_double()
c.plot()
../_images/120a7336056722cfe53cd7bbd9a3b10abdcb4377b615cbbce07669bc3cbe5a67.png
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b4e2eb950>
../_images/86b791c30a189b135d8821816903f1d63ea56ec1a86f6683f9dd2d7abd7fff9a.png
c = gf.components.mzit()
c.plot()
../_images/b32ed42c21637b7f6ae9595efe95957960b1dd66bc98be7c03808c0f70d12264.png
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b5bcf66d0>
../_images/98da5b6c2884bff564a1ae9b1b58b9fbca1111ba4134317dde7109d482ac8cad.png
coupler_lengths = [10, 20, 30]
coupler_gaps = [0.1, 0.2, 0.3]
delta_lengths = [10, 100]

c = gf.components.mzi_lattice(
    coupler_lengths=coupler_lengths,
    coupler_gaps=coupler_gaps,
    delta_lengths=delta_lengths,
)
c.plot()
../_images/3145f7728204db098473d6adb2256cad40662bd7f39769aea1071449d864ed46.png
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b4e5d3450>
../_images/86413b29082f99106ff0161b9c55cafefb6a0ec4adc1ebcb5c0159df7e1298ff.png
coupler_lengths = [10, 20, 30, 40]
coupler_gaps = [0.1, 0.2, 0.4, 0.5]
delta_lengths = [10, 100, 200]

c = gf.components.mzi_lattice(
    coupler_lengths=coupler_lengths,
    coupler_gaps=coupler_gaps,
    delta_lengths=delta_lengths,
)
c.plot()
../_images/1ac0512b7aaf964e711863161cc41188d2c19b7e81d90206246b9d58d2d8f341.png
n = c.get_netlist()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7f6b4deb08d0>
../_images/e768d6d55143c2502a0154812a966db95d4731b926e4698b6c3764010a2f5536.png
n_recursive = c.get_netlist_recursive()
n_recursive.keys()
dict_keys(['mzi_lattice_d52c1fad', 'mzi_d52b8f87', 'mzi_cd076e56', 'mzi_48422baa'])

get_netlist_flat#

You can also flatten the recursive netlist

flat_netlist = c.get_netlist_flat()

The flat netlist contains the same keys as a regular netlist:

flat_netlist.keys()
dict_keys(['connections', 'placements', 'instances', 'ports', 'name'])

However, its instances are flattened and uniquely renamed according to hierarchy:

flat_netlist["instances"].keys()
dict_keys(['mzi_lattice_d52c1fad~mzi_1~bend_euler_1', 'mzi_lattice_d52c1fad~mzi_1~bend_euler_2', 'mzi_lattice_d52c1fad~mzi_1~bend_euler_3', 'mzi_lattice_d52c1fad~mzi_1~bend_euler_4', 'mzi_lattice_d52c1fad~mzi_1~bend_euler_5', 'mzi_lattice_d52c1fad~mzi_1~bend_euler_6', 'mzi_lattice_d52c1fad~mzi_1~bend_euler_7', 'mzi_lattice_d52c1fad~mzi_1~bend_euler_8', 'mzi_lattice_d52c1fad~mzi_1~cp1', 'mzi_lattice_d52c1fad~mzi_1~cp2', 'mzi_lattice_d52c1fad~mzi_1~straight_10', 'mzi_lattice_d52c1fad~mzi_1~straight_5', 'mzi_lattice_d52c1fad~mzi_1~straight_6', 'mzi_lattice_d52c1fad~mzi_1~straight_7', 'mzi_lattice_d52c1fad~mzi_1~straight_8', 'mzi_lattice_d52c1fad~mzi_1~straight_9', 'mzi_lattice_d52c1fad~mzi_1~sxb', 'mzi_lattice_d52c1fad~mzi_1~sxt', 'mzi_lattice_d52c1fad~mzi_1~syl', 'mzi_lattice_d52c1fad~mzi_1~sytl', 'mzi_lattice_d52c1fad~mzi_2~bend_euler_1', 'mzi_lattice_d52c1fad~mzi_2~bend_euler_2', 'mzi_lattice_d52c1fad~mzi_2~bend_euler_3', 'mzi_lattice_d52c1fad~mzi_2~bend_euler_4', 'mzi_lattice_d52c1fad~mzi_2~bend_euler_5', 'mzi_lattice_d52c1fad~mzi_2~bend_euler_6', 'mzi_lattice_d52c1fad~mzi_2~bend_euler_7', 'mzi_lattice_d52c1fad~mzi_2~bend_euler_8', 'mzi_lattice_d52c1fad~mzi_2~cp2', 'mzi_lattice_d52c1fad~mzi_2~straight_10', 'mzi_lattice_d52c1fad~mzi_2~straight_5', 'mzi_lattice_d52c1fad~mzi_2~straight_6', 'mzi_lattice_d52c1fad~mzi_2~straight_7', 'mzi_lattice_d52c1fad~mzi_2~straight_8', 'mzi_lattice_d52c1fad~mzi_2~straight_9', 'mzi_lattice_d52c1fad~mzi_2~sxb', 'mzi_lattice_d52c1fad~mzi_2~sxt', 'mzi_lattice_d52c1fad~mzi_2~syl', 'mzi_lattice_d52c1fad~mzi_2~sytl', 'mzi_lattice_d52c1fad~mzi_3~bend_euler_1', 'mzi_lattice_d52c1fad~mzi_3~bend_euler_2', 'mzi_lattice_d52c1fad~mzi_3~bend_euler_3', 'mzi_lattice_d52c1fad~mzi_3~bend_euler_4', 'mzi_lattice_d52c1fad~mzi_3~bend_euler_5', 'mzi_lattice_d52c1fad~mzi_3~bend_euler_6', 'mzi_lattice_d52c1fad~mzi_3~bend_euler_7', 'mzi_lattice_d52c1fad~mzi_3~bend_euler_8', 'mzi_lattice_d52c1fad~mzi_3~cp2', 'mzi_lattice_d52c1fad~mzi_3~straight_10', 'mzi_lattice_d52c1fad~mzi_3~straight_5', 'mzi_lattice_d52c1fad~mzi_3~straight_6', 'mzi_lattice_d52c1fad~mzi_3~straight_7', 'mzi_lattice_d52c1fad~mzi_3~straight_8', 'mzi_lattice_d52c1fad~mzi_3~straight_9', 'mzi_lattice_d52c1fad~mzi_3~sxb', 'mzi_lattice_d52c1fad~mzi_3~sxt', 'mzi_lattice_d52c1fad~mzi_3~syl', 'mzi_lattice_d52c1fad~mzi_3~sytl'])

Placement information is accumulated, and connections and ports are mapped, respectively, to the ports of the unique instances or the component top level ports. This can be plotted:

c.plot_netlist_flat(with_labels=False)  # labels get cluttered
<networkx.classes.graph.Graph at 0x7f6b4e600610>
../_images/24046a2c254d0ce7c23f51b8811610b4163ac1072483952ac2e4f501e44e4f40.png

allow_multiple_connections#

The default get_netlist function (also used by default by get_netlist_recurse and get_netlist_flat) can identify more than two ports sharing the same connection through the allow_multiple flag.

For instance, consider a resistor network with one shared node:

vdiv = gf.Component("voltageDivider")
r1 = vdiv << gf.components.resistance_sheet()
r2 = vdiv << gf.components.resistance_sheet()
r3 = vdiv << gf.get_component(gf.components.resistance_sheet).rotate()
r4 = vdiv << gf.get_component(gf.components.resistance_sheet).rotate()

r1.connect("pad2", r2.ports["pad1"])
r3.connect("pad1", r2.ports["pad1"], preserve_orientation=True)
r4.connect("pad2", r2.ports["pad1"], preserve_orientation=True)

vdiv.plot()
../_images/587330d578b1b29bba3c94a941ec624b74b6b42f80fbcaa7bf74b29a960874e6.png
try:
    vdiv.get_netlist_flat()
except Exception as exc:
    print(exc)
Found multiple connections at (-50.0, 0.0):['resistance_sheet_1,pad2', 'resistance_sheet_2,pad1', 'rotate_1,pad1', 'rotate_2,pad2']
vdiv.get_netlist_flat(allow_multiple=True)
{'connections': {'voltageDivider~resistance_sheet_1,pad2': ['voltageDivider~resistance_sheet_2,pad1'],
  'voltageDivider~resistance_sheet_2,pad1': ['voltageDivider~resistance_sheet_1,pad2',
   'voltageDivider~rotate_1~resistance_sheet_1,pad1'],
  'voltageDivider~rotate_1~resistance_sheet_1,pad1': ['voltageDivider~resistance_sheet_2,pad1',
   'voltageDivider~rotate_2~resistance_sheet_1,pad2'],
  'voltageDivider~rotate_2~resistance_sheet_1,pad2': ['voltageDivider~rotate_1~resistance_sheet_1,pad1']},
 'placements': {'voltageDivider~resistance_sheet_1': {'x': -100.0,
   'y': 0.0,
   'mirror': 0,
   'rotation': 0},
  'voltageDivider~resistance_sheet_2': {'x': 0.0,
   'y': 0.0,
   'mirror': 0,
   'rotation': 0},
  'voltageDivider~rotate_1~resistance_sheet_1': {'x': -50.0,
   'y': 50.0,
   'mirror': 0,
   'rotation': 90},
  'voltageDivider~rotate_2~resistance_sheet_1': {'x': -50.0,
   'y': -50.0,
   'mirror': 0,
   'rotation': 90}},
 'instances': {'voltageDivider~resistance_sheet_1': {'component': 'resistance_sheet',
   'info': {'resistance': 0},
   'settings': {'width': 10,
    'layers': ['SLAB90', 'NPP'],
    'layer_offsets': [0, 0.2],
    'pad': {'function': 'via_stack',
     'settings': {'layers': ['SLAB90', 'NPP', 'M1'],
      'vias': [None,
       None,
       {'function': 'via',
        'settings': {'layer': 'VIAC'},
        'module': 'gdsfactory.components.via'}],
      'size': [80, 80]},
     'module': 'gdsfactory.components.via_stack'},
    'pad_pitch': 100.0,
    'ohms_per_square': None,
    'port_orientation1': 180,
    'port_orientation2': 0}},
  'voltageDivider~resistance_sheet_2': {'component': 'resistance_sheet',
   'info': {'resistance': 0},
   'settings': {'width': 10,
    'layers': ['SLAB90', 'NPP'],
    'layer_offsets': [0, 0.2],
    'pad': {'function': 'via_stack',
     'settings': {'layers': ['SLAB90', 'NPP', 'M1'],
      'vias': [None,
       None,
       {'function': 'via',
        'settings': {'layer': 'VIAC'},
        'module': 'gdsfactory.components.via'}],
      'size': [80, 80]},
     'module': 'gdsfactory.components.via_stack'},
    'pad_pitch': 100.0,
    'ohms_per_square': None,
    'port_orientation1': 180,
    'port_orientation2': 0}},
  'voltageDivider~rotate_1~resistance_sheet_1': {'component': 'resistance_sheet',
   'info': {'resistance': 0},
   'settings': {'width': 10,
    'layers': ['SLAB90', 'NPP'],
    'layer_offsets': [0, 0.2],
    'pad': {'function': 'via_stack',
     'settings': {'layers': ['SLAB90', 'NPP', 'M1'],
      'vias': [None,
       None,
       {'function': 'via',
        'settings': {'layer': 'VIAC'},
        'module': 'gdsfactory.components.via'}],
      'size': [80, 80]},
     'module': 'gdsfactory.components.via_stack'},
    'pad_pitch': 100.0,
    'ohms_per_square': None,
    'port_orientation1': 180,
    'port_orientation2': 0}},
  'voltageDivider~rotate_2~resistance_sheet_1': {'component': 'resistance_sheet',
   'info': {'resistance': 0},
   'settings': {'width': 10,
    'layers': ['SLAB90', 'NPP'],
    'layer_offsets': [0, 0.2],
    'pad': {'function': 'via_stack',
     'settings': {'layers': ['SLAB90', 'NPP', 'M1'],
      'vias': [None,
       None,
       {'function': 'via',
        'settings': {'layer': 'VIAC'},
        'module': 'gdsfactory.components.via'}],
      'size': [80, 80]},
     'module': 'gdsfactory.components.via_stack'},
    'pad_pitch': 100.0,
    'ohms_per_square': None,
    'port_orientation1': 180,
    'port_orientation2': 0}}},
 'ports': {},
 'name': 'voltageDivider'}
from gplugins.gfviz import show
import gdsfactory as gf
show(gf.components.mzi())
gfviz
show(gf.components.ring_single())
gfviz