Source code for photonforge.netlist

from .extension import Component, Reference
from .parametric import route, route_manhattan

from typing import Any


[docs] def component_from_netlist(netlist: dict[str, Any]) -> Component: """Create a component from a netlist description. Args: netlist: Dictionary with the component description. The only required key is ``'instances'``, which describes the references to all sub-components. See other keys in the example below. Examples: >>> coupler = parametric.dual_ring_coupler( ... port_spec="Strip", ... coupling_distance=0.6, ... radius=4, ... ) ... bus = parametric.ring_coupler( ... port_spec="Strip", ... coupling_distance=0.6, ... radius=4, ... bus_length=5, ... ) >>> netlist1 = { ... "name": "RING", ... "instances": {"COUPLER": coupler, "BUS_0": bus, "BUS_1": bus}, ... "instance_models": [ ... ("COUPLER", DirectionalCouplerModel(0.8, -0.5j)), ... ], ... "connections": [ ... (("COUPLER", "P0"), ("BUS_0", "P1")), ... (("BUS_1", "P1"), ("COUPLER", "P3")), ... ], ... "ports": [ ... ("BUS_0", "P0"), ... ("BUS_0", "P2"), ... ("BUS_1", "P2"), ... ("BUS_1", "P0"), ... ], ... "models": [CircuitModel()], ... } >>> component1 = component_from_netlist(netlist1) >>> netlist2 = { ... "instances": [ ... coupler, ... {"component": bus, "origin": (0, -12)}, ... {"component": bus, "origin": (3, 7), "rotation": 180}, ... ], ... "virtual connections": [ ... ((0, "P0"), (1, "P1")), ... ((0, "P2"), (1, "P3")), ... ((2, "P3"), (0, "P1")), ... ], ... "routes": [ ... ((1, "P2"), (2, "P0"), {"radius": 6}), ... ((2, "P1"), (0, "P3"), parametric.route_s_bend), ... ], ... "ports": [ ... (1, "P0", "In"), ... (2, "P2", "Add"), ... ], ... "models": [(CircuitModel(), "Circuit")], ... } >>> component2 = component_from_netlist(netlist2) >>> spec = cpw_spec("METAL", 3, 1) ... tl = parametric.straight(port_spec=spec, length=20) ... terminal = Terminal( ... "METAL", Rectangle(center=(-10, 20), size=(2, 2)) ... ) ... tl.add_terminal(terminal, "T0") ... netlist3 = { ... "instances": [tl], ... "terminal routes": [ ... ((0, "T0"), (0, ("E0", "gnd0"))), ... ], ... "terminals": [ ... (0, "T0", "GND0"), ... (0, ("E1", "gnd0"), "GND1"), ... ], ... } >>> component3 = component_from_netlist(netlist3) The value in ``"instances"`` can be a dictionary or a list, in which case, index numbers are used in place of the keys. Each value is can be a :class:`Component` or another dictionary with keyword arguments to create a :class:`Reference`. Sub-components can receive extra models from ``"instance_models"``. The last added model for each sub-component will be active. The ``"connections"`` list specifies connections between instances. Each item is of the form ``((key1, port1), (key2, port2))``, indicating that the reference ``key1`` must be transformed to have its ``port1`` connected to ``port2`` from the reference ``key2``. Items in the ``"routes"`` list contain 2 reference ports, similarly to ``"connections"``, plus an optional routing function and a dictionary of keyword arguments to the function: ``((key1, port1), (key2, port2), route_function, kwargs_dict)``. If ``route_function`` is not provided, :func:`photonforge.parametric.route` is used. A list of ``"terminal routes"`` can be also be specified analogously to ``"routes"``, with the difference that only terminal routing functions can be used and :func:`photonforge.parametric.route_manhattan` is the default. Terminals within ports can also be used by replacing the terminal name string with a tuple ``(port_name, terminal_name)``. The ``"ports"`` list specify the top-level component ports derived from instance ports from ``(key, port)`` or ``(key, port, new_name)``. The same goes for the ``"terminals"`` lists, except that terminal names can be replaced by a ``(port_name, terminal_name)`` tuple to indicate a terminals within a port. """ component = Component(netlist.get("name", "")) references = {} instances = netlist["instances"] instances_items = instances.items() if isinstance(instances, dict) else enumerate(instances) for key, instance in instances_items: reference = Reference(**instance) if isinstance(instance, dict) else Reference(instance) component.add(reference) references[key] = reference # Order matters here for connection in netlist.get("connections", ()): key1, port1 = connection[0] key2, port2 = connection[1] references[key1].connect(port1, references[key2][port2]) for connection in netlist.get("virtual connections", ()): key1, port1 = connection[0] key2, port2 = connection[1] component.add_virtual_connection(references[key1], port1, references[key2], port2) for connection in netlist.get("routes", ()): key1, port1 = connection[0] key2, port2 = connection[1] route_fn = connection[2] if len(connection) > 2 and callable(connection[2]) else route kwargs = connection[-1] if isinstance(connection[-1], dict) else {} component.add( route_fn(port1=references[key1][port1], port2=references[key2][port2], **kwargs) ) for connection in netlist.get("terminal routes", ()): terminals = [ references[key][terminal] if isinstance(terminal, str) else references[key][terminal[0]].terminals(terminal[1]) for key, terminal in connection[:2] ] if terminals[0] is None or terminals[1] is None: i = 0 if terminals[0] is None else 1 raise RuntimeError( f"Terminal specification {connection[i]} from 'terminal routes' does not exist." ) route_fn = ( connection[2] if len(connection) > 2 and callable(connection[2]) else route_manhattan ) kwargs = connection[-1] if isinstance(connection[-1], dict) else {} component.add(route_fn(terminal1=terminals[0], terminal2=terminals[1], **kwargs)) for item in netlist.get("ports", ()): if len(item) == 3: key, port, name = item else: key, port = item name = None component.add_port(references[key][port], name) for item in netlist.get("terminals", ()): if len(item) == 3: key, terminal, name = item else: key, terminal = item name = None terminal = ( references[key][terminal] if isinstance(terminal, str) else references[key][terminal[0]].terminals(terminal[1]) ) if terminal is None: raise RuntimeError( f"Terminal specification {item[:2]} from 'terminals' does not exist." ) component.add_terminal(terminal, name) for item in netlist.get("models", ()): if isinstance(item, tuple): model, name = item else: model = item name = None component.add_model(model, name) for item in netlist.get("instance_models", ()): if len(item) == 3: key, model, name = item else: key, model = item name = None references[key].component.add_model(model, name) return component