From 4b41fcaa2058c341a85b4d247c024f1fbaac67ab Mon Sep 17 00:00:00 2001 From: Niko Savola Date: Thu, 9 Nov 2023 13:54:07 -0800 Subject: [PATCH 1/2] Ensure unique SPICE netlist elements with a counter --- gplugins/klayout/netlist_spice_reader.py | 9 +++++++-- gplugins/klayout/tests/test_netlist_spice_reader.py | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/gplugins/klayout/netlist_spice_reader.py b/gplugins/klayout/netlist_spice_reader.py index f524b208..9a4f16c6 100644 --- a/gplugins/klayout/netlist_spice_reader.py +++ b/gplugins/klayout/netlist_spice_reader.py @@ -2,10 +2,15 @@ class NoCommentReader(kdb.NetlistSpiceReaderDelegate): - """KLayout Spice reader without comments after $. This allows checking the netlist for HSPICE""" + """KLayout Spice reader without comments after $. This allows checking the netlist for HSPICE.""" + + n_nodes: int = 0 def parse_element(self, s: str, element: str) -> kdb.ParseElementData: if "$" in s: - s = s.split("$")[0] # Don't take comments into account + s, *_ = s.split("$") # Don't take comments into account parsed = super().parse_element(s, element) + # ensure uniqueness + parsed.model_name = parsed.model_name + f"_{self.n_nodes}" + self.n_nodes += 1 return parsed diff --git a/gplugins/klayout/tests/test_netlist_spice_reader.py b/gplugins/klayout/tests/test_netlist_spice_reader.py index a4f40204..868df339 100644 --- a/gplugins/klayout/tests/test_netlist_spice_reader.py +++ b/gplugins/klayout/tests/test_netlist_spice_reader.py @@ -6,12 +6,12 @@ @pytest.mark.parametrize( "s,element,expected_name,expected_nets", [ - ("1 2 POS", "X", "POS", {"1", "2"}), - ("2 3 NEG $ This is a comment", "X", "NEG", {"2", "3"}), + ("1 2 POS", "X", "POS_0", {"1", "2"}), + ("2 3 NEG $ This is a comment", "X", "NEG_0", {"2", "3"}), ( "5 4 some_elem some_variable=1 $ This is a comment", "X", - "some_elem", + "some_elem_0", {"5", "4"}, ), ], From 78876190a3093ea9757b25902b2b7eedf70fddfa Mon Sep 17 00:00:00 2001 From: Niko Savola Date: Thu, 9 Nov 2023 14:11:06 -0800 Subject: [PATCH 2/2] Add option to plot only most complex circuit in `plot_nets` --- gplugins/klayout/plot_nets.py | 26 +++++++++++++++++--- gplugins/klayout/tests/test_plot_nets.py | 31 ++++++++++++------------ 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/gplugins/klayout/plot_nets.py b/gplugins/klayout/plot_nets.py index 6ea21ad7..65a87a51 100644 --- a/gplugins/klayout/plot_nets.py +++ b/gplugins/klayout/plot_nets.py @@ -15,13 +15,23 @@ def _get_subcircuit_name(subcircuit: kdb.SubCircuit) -> str: def netlist_to_networkx( - netlist: kdb.Netlist, fully_connected: bool = False, include_labels: bool = True + netlist: kdb.Netlist, + fully_connected: bool = False, + include_labels: bool = True, + only_most_complex: bool = False, ) -> nx.Graph: """Convert a KLayout DB `Netlist` to a networkx graph. Args: - kdbnet: The KLayout DB `Netlist` to convert to a networkx `Graph`. + netlist: The KLayout DB `Netlist` to convert to a networkx `Graph`. + fully_connected: Whether to plot the graph as elements fully connected to all other ones (True) or + going through other elements (False). include_labels: Whether to include labels in the graph connected to corresponding cells. + only_most_complex: Whether to plot only the circuit with most connections or not. + Helpful for not plotting subcircuits separately. + + Returns: + A networkx `Graph` representing the connectivity of the `Netlist`. """ G = nx.Graph() @@ -29,6 +39,9 @@ def netlist_to_networkx( itertools.islice(netlist.each_circuit_top_down(), netlist.top_circuit_count()) ) + if only_most_complex: + top_circuits = (max(top_circuits, key=lambda x: x.pin_count()),) + for circuit in top_circuits: # first flatten components that won't be kept for subcircuit in circuit.each_subcircuit(): @@ -65,6 +78,7 @@ def plot_nets( fully_connected: bool = False, interactive: bool = False, include_labels: bool = True, + only_most_complex: bool = False, ) -> None: """Plots the connectivity between the components in the KLayout LayoutToNetlist file from :func:`~get_l2n`. @@ -72,10 +86,11 @@ def plot_nets( filepath: Path to the KLayout LayoutToNetlist file or a SPICE netlist. File extensions should be `.l2n` and `.spice`, respectively. fully_connected: Whether to plot the graph as elements fully connected to all other ones (True) or - going through other elements (False). interactive: Whether to plot an interactive graph with `pyvis` or not. include_labels: Whether to include labels in the graph connected to corresponding cells. + only_most_complex: Whether to plot only the circuit with most connections or not. + Helpful for not plotting subcircuits separately. """ match Path(filepath).suffix: case ".l2n" | ".txt": @@ -94,7 +109,10 @@ def plot_nets( # Creating a graph for the connectivity G_connectivity = netlist_to_networkx( - netlist, fully_connected=fully_connected, include_labels=include_labels + netlist, + fully_connected=fully_connected, + include_labels=include_labels, + only_most_complex=only_most_complex, ) # Plotting the graph diff --git a/gplugins/klayout/tests/test_plot_nets.py b/gplugins/klayout/tests/test_plot_nets.py index 4995e387..18b60321 100644 --- a/gplugins/klayout/tests/test_plot_nets.py +++ b/gplugins/klayout/tests/test_plot_nets.py @@ -32,21 +32,22 @@ def spice_netlist(tmpdir_factory) -> str: return netlist_path -def test_plot_nets(klayout_netlist): - plot_nets(klayout_netlist) - - -def test_plot_nets_interactive(klayout_netlist): - plot_nets(klayout_netlist, interactive=True) - assert Path("connectivity.html").exists() - - -def test_plot_nets_not_fully_connected(klayout_netlist): - plot_nets(klayout_netlist, fully_connected=False) - - -def test_plot_nets_no_labels(klayout_netlist): - plot_nets(klayout_netlist, include_labels=False) +@pytest.mark.parametrize("fully_connected", [True, False]) +@pytest.mark.parametrize("interactive", [True, False]) +@pytest.mark.parametrize("include_labels", [True, False]) +@pytest.mark.parametrize("only_most_complex", [True, False]) +def test_plot_nets( + klayout_netlist, fully_connected, interactive, include_labels, only_most_complex +): + plot_nets( + klayout_netlist, + fully_connected=fully_connected, + interactive=interactive, + include_labels=include_labels, + only_most_complex=only_most_complex, + ) + if interactive: + assert Path("connectivity.html").exists() def test_plot_nets_spice(spice_netlist):