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
import yaml
c = gf.components.mzi()
c.plot()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7ff4a9568650>
c = gf.components.ring_single()
c.plot()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7ff4a940c650>
n = c.get_netlist()
netlist_string = c.write_netlist(n)
n = yaml.safe_load(netlist_string)
i = list(n["instances"].keys())
i
['bend_euler_R10_A90_P0p5_2f1f5c6d_10000_11300',
'bend_euler_R10_A90_P0p5_2f1f5c6d_m4000_21300',
'coupler_ring_G0p2_R10_L_9871ac59_0_0',
'straight_L0p6_N2_CSstrip_WNone_10000_11300',
'straight_L0p6_N2_CSstrip_WNone_m14000_10700',
'straight_L4_N2_CSstrip_WNone_0_21300']
instance_name0 = i[0]
n["instances"][instance_name0]["settings"]
{'allow_min_radius_violation': False,
'angle': 90,
'cross_section': 'strip',
'layer': None,
'npoints': None,
'p': 0.5,
'radius': 10,
'width': 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 0x7ff4a9407a90>
@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()
/home/runner/work/gplugins/gplugins/.venv/lib/python3.11/site-packages/gdsfactory/component.py:679: UserWarning: alias is deprecated, use name instead
warnings.warn("alias is deprecated, use name instead")
<networkx.classes.graph.Graph at 0x7ff4a93858d0>
c = gf.components.mzi()
c.plot()
c = gf.components.mzi()
n = c.get_netlist()
print(c.get_netlist().keys())
dict_keys(['nets', 'instances', 'placements', 'ports', 'name'])
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7ff4a9363d50>
n.keys()
dict_keys(['nets', '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()
c.plot()
n = c.get_netlist()
print(n["warnings"])
{'optical': {'unconnected_ports': ({'ports': ('mmi,o1', 'mmi,o3', 'bend,o2'), 'values': ((-10000, 0), (15500, -625), (25500, 10625)), 'message': '3 unconnected optical ports!'},)}}
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7ff4aad59550>
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()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7ff4a90d3a10>
c = gf.components.ring_double()
c.plot()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7ff4a912fe90>
c = gf.components.mzit()
c.plot()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7ff4a9385ed0>
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()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7ff4a8e50f50>
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()
n = c.get_netlist()
c.plot_netlist()
<networkx.classes.graph.Graph at 0x7ff4a8e4aa50>
n_recursive = c.get_netlist(recursive=True)
n_recursive.keys()
dict_keys(['mzi_lattice_CL10_20_30__8514709c', 'mzi_DL10_LY2_LXNone_Bbe_7eee11eb', 'mzi_DL100_LY2_LXNone_Bb_4ce1f1e6', 'mzi_DL200_LY2_LXNone_Bb_dce7e6c1'])
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(with_labels=False) # labels get cluttered
<networkx.classes.graph.Graph at 0x7ff4a8e9a050>
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:
import gdsfactory as gf
vdiv = gf.Component()
r1 = vdiv << gf.components.resistance_sheet()
r2 = vdiv << gf.components.resistance_sheet()
r3 = vdiv << gf.get_component(gf.components.resistance_sheet)
r4 = vdiv << gf.get_component(gf.components.resistance_sheet)
r1.connect("pad2", r2.ports["pad1"])
r3.connect("pad1", r2.ports["pad1"])
r4.connect("pad2", r2.ports["pad1"])
r4.drotate(90)
vdiv.plot()
try:
vdiv.get_netlist()
except Exception as exc:
print(exc)
vdiv.get_netlist(allow_multiple=True)
{'nets': ({'p1': 'resistance_sheet_W10_LH_1b3e3b4b_0_0,pad1',
'p2': 'resistance_sheet_W10_LH_1b3e3b4b_0_m50000,pad2'},
{'p1': 'resistance_sheet_W10_LH_1b3e3b4b_0_0,pad1',
'p2': 'resistance_sheet_W10_LH_1b3e3b4b_m100000_m50000,pad1'},
{'p1': 'resistance_sheet_W10_LH_1b3e3b4b_0_0,pad2',
'p2': 'resistance_sheet_W10_LH_1b3e3b4b_0_m50000,pad1'},
{'p1': 'resistance_sheet_W10_LH_1b3e3b4b_0_m50000,pad2',
'p2': 'resistance_sheet_W10_LH_1b3e3b4b_m100000_m50000,pad1'}),
'instances': {'resistance_sheet_W10_LH_1b3e3b4b_0_0': {'component': 'resistance_sheet',
'info': {'resistance': 0},
'settings': {'width': 10,
'layers': ('HEATER',),
'layer_offsets': (0, 0.2),
'pad': 'via_stack_heater_mtop',
'pad_size': (50, 50),
'pad_pitch': 100,
'ohms_per_square': None,
'pad_port_name': 'e4'}},
'resistance_sheet_W10_LH_1b3e3b4b_0_m50000': {'component': 'resistance_sheet',
'info': {'resistance': 0},
'settings': {'width': 10,
'layers': ('HEATER',),
'layer_offsets': (0, 0.2),
'pad': 'via_stack_heater_mtop',
'pad_size': (50, 50),
'pad_pitch': 100,
'ohms_per_square': None,
'pad_port_name': 'e4'}},
'resistance_sheet_W10_LH_1b3e3b4b_50000_0': {'component': 'resistance_sheet',
'info': {'resistance': 0},
'settings': {'width': 10,
'layers': ('HEATER',),
'layer_offsets': (0, 0.2),
'pad': 'via_stack_heater_mtop',
'pad_size': (50, 50),
'pad_pitch': 100,
'ohms_per_square': None,
'pad_port_name': 'e4'}},
'resistance_sheet_W10_LH_1b3e3b4b_m100000_m50000': {'component': 'resistance_sheet',
'info': {'resistance': 0},
'settings': {'width': 10,
'layers': ('HEATER',),
'layer_offsets': (0, 0.2),
'pad': 'via_stack_heater_mtop',
'pad_size': (50, 50),
'pad_pitch': 100,
'ohms_per_square': None,
'pad_port_name': 'e4'}}},
'placements': {'resistance_sheet_W10_LH_1b3e3b4b_0_0': {'x': 0,
'y': 0,
'rotation': 0,
'mirror': False},
'resistance_sheet_W10_LH_1b3e3b4b_0_m50000': {'x': 0,
'y': -50,
'rotation': 180,
'mirror': False},
'resistance_sheet_W10_LH_1b3e3b4b_50000_0': {'x': 50,
'y': 0,
'rotation': 270,
'mirror': False},
'resistance_sheet_W10_LH_1b3e3b4b_m100000_m50000': {'x': -100,
'y': -50,
'rotation': 180,
'mirror': False}},
'ports': {},
'name': 'Unnamed_85',
'warnings': {'electrical': {'unconnected_ports': ({'ports': ('resistance_sheet_W10_LH_1b3e3b4b_m100000_m50000,pad2',
'resistance_sheet_W10_LH_1b3e3b4b_50000_0,pad1',
'resistance_sheet_W10_LH_1b3e3b4b_50000_0,pad2'),
'values': ((-150000, -25000), (25000, 50000), (25000, -50000)),
'message': '3 unconnected electrical ports!'},)}}}