Fix order-dependent port renaming; close gdstk backend gaps - #102
Fix order-dependent port renaming; close gdstk backend gaps#102carloscl03 wants to merge 2 commits into
Conversation
rename_component_ports renamed in place: pop the old key, insert the new one
on the same dict it was iterating. When a port renames onto a name another
port still holds, the guard
if namepair[0] in custom_comp.ports.keys():
silently skips it, so one of the two is dropped -- and which one survives
depends on dict insertion order.
rename_ports_by_orientation triggers this routinely: a mirrored port has
orientation 90 and legitimately renames _S -> _N onto a name that is still
queued. In diff_pair(gf180, width=3, fingers=4) that is 334 ports.
The result differs per backend. Same component, same port, orientation 90.0:
gdsfactory keeps bl_multiplier_0_source_S (90 deg is N, so this is wrong)
gdstk gives bl_multiplier_0_source_N (correct)
and add_df_labels then raises KeyError on the latter. It works under
gdsfactory by accident of ordering, not by construction.
Build the renamed mapping first, then swap it in. Order-independent.
diff_pair(gf180, width=3, fingers=4):
before 4530 ports gdstk: KeyError 'bl_multiplier_0_source_S'
after 4864 ports both backends identical, lookup succeeds
The 334 extra ports are the ones that were being dropped.
With GLAYOUT_BACKEND=gdstk the opamp failed on gf180. Four distinct gaps,
found by fixing one and hitting the next:
1. Component.add() rejected a bare Component. gdsfactory accepts one and
references it implicitly; several composite cells rely on that.
2. diff_pair_stackedcmirror imported rectangle straight from gdsfactory
(`from gdsfactory.components.rectangle import rectangle as _rect`),
bypassing the backend abstraction, so the active backend could not
reference the result. The module already imports the backend's rectangle
at module level.
3. ComponentReference.info did not reach the referenced component. mimcap_array
writes info['netlist'] on the Component while opamp_twostage reads it back
off the reference returned by '<<' -- gdsfactory exposes the parent's info
through the reference, so delegate instead of holding an empty dict.
4. Component.ref() took no arguments. opamp.py calls ref(position=...);
added position/rotation/x_reflection to match gdsfactory.
opamp(gf180, ...) with GLAYOUT_BACKEND=gdstk, same parameters as
GLayout_Cells.ipynb cell 14:
gdsfactory backend 383.0 s
gdstk backend 55.5 s 99090 ports
Requires the port rename fix in the previous commit; without it the run stops
earlier with KeyError: 'bl_multiplier_0_source_S'.
|
Context on why this came up: #100's notebook check fails on Chasing that led here. With So this does not fix #100's check directly, but it makes switching the notebook |
Two commits. The first is a real bug in
port_utilsthat affects the defaultbackend too; the second unblocks
GLAYOUT_BACKEND=gdstkfor the opamp.Renaming ports depends on dict order
rename_component_portsrenames in place — pops the old key and inserts thenew one on the same dict it just iterated:
When a port renames onto a name another port still holds, that guard silently
skips it and one of the two is dropped. Which one survives depends on insertion
order.
rename_ports_by_orientationtriggers this routinely: a mirrored port hasorientation 90 and legitimately renames
_S->_Nonto a name still queued.In
diff_pair(gf180, width=3, fingers=4)that is 334 ports.Same component, same port, orientation 90.0:
bl_multiplier_0_source_S(90° is N, so this is wrong)bl_multiplier_0_source_N(correct)add_df_labelsthen raisesKeyErroron the latter. It works under gdsfactoryby accident of ordering, not by construction.
Fix builds the renamed mapping first, then swaps it in.
The 334 extra ports are the ones that were being dropped.
gdstk backend gaps
With the rename fixed, the opamp got further and hit four more gaps, found one
at a time:
Component.add()rejected a bareComponent. gdsfactory accepts one andreferences it implicitly; several composite cells rely on that.
diff_pair_stackedcmirrorimportedrectanglestraight from gdsfactory,bypassing the backend abstraction — the module already imports the backend's
rectangleat module level.ComponentReference.infoheld its own empty dict.mimcap_arraywritesinfo['netlist']on the Component whileopamp_twostagereads it back offthe reference returned by
<<; gdsfactory exposes the parent's info throughthe reference, so delegate.
Component.ref()took no arguments.opamp.py:155callsref(position=...).Results
opamp(gf180, ...), same parameters asGLayout_Cells.ipynbcell 14:No regression: under
gdsfactory,nmos(gf180, width=3, fingers=4)produces3328 ports and 875 polygons before and after.
Note
This makes the fast backend usable; it does not address why the slow one is
slow. Profiling the opamp under
gdsfactoryshows 96% of runtime insidepydantic validation, 6.3M
serialization.pycalls, and 1.85MPortobjectscreated — 93% of a transistor's ports belong to individual vias that are never
routed to. That is a port-model question, not something a compatibility fix
reaches.