Skip to content

Commit

Permalink
Enhancement/edb plot (#669)
Browse files Browse the repository at this point in the history
* Added plot methods to nets. It requires matplotlib.

* Added Plot methods to edb

* Added example and UnitTest

* Added example and UnitTest

* Added example and UnitTest

* Added example and UnitTest
  • Loading branch information
maxcapodi78 authored Dec 21, 2021
1 parent c538405 commit b36d772
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 2 deletions.
8 changes: 7 additions & 1 deletion _unittest/test_00_EDB.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

test_project_name = "Galileo_edb"
bom_example = "bom_example.csv"
from _unittest.conftest import config, desktop_version, local_path, scratch_path
from _unittest.conftest import config, desktop_version, local_path, scratch_path, is_ironpython

try:
import pytest
Expand Down Expand Up @@ -532,3 +532,9 @@ def test_68_flip_layer_stackup(self):

def test_69_create_solder_balls_on_component(self):
assert self.edbapp.core_components.set_solder_ball("U2A5")

@pytest.mark.skipif(is_ironpython, reason="This Test uses Ironpython")
def test_70_plot_on_matplotlib(self):
local_png = os.path.join(self.local_scratch.path, "test.png")
self.edbapp.core_nets.plot(None, None, save_plot=local_png)
assert os.path.exists(local_png)
56 changes: 56 additions & 0 deletions examples/00-EDB/05_Plot_nets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
Plot Nets with Matplotlib
-------------------------
This example shows how to use EDB Class to plot a net or a layout.
"""

###############################################################################
# Import Section
# ~~~~~~~~~~~~~~
import shutil
import os
import tempfile
from pyaedt import generate_unique_name, examples, Edb

###############################################################################
# File download
# ~~~~~~~~~~~~~
# In this section the aedb file will be downloaded and copied in Temporary Folder.

tmpfold = tempfile.gettempdir()
temp_folder = os.path.join(tmpfold, generate_unique_name("Example"))
if not os.path.exists(temp_folder):
os.makedirs(temp_folder)
example_path = examples.download_aedb()
targetfolder = os.path.join(temp_folder, "Galileo.aedb")
if os.path.exists(targetfolder):
shutil.rmtree(targetfolder)
shutil.copytree(example_path[:-8], targetfolder)

###############################################################################
# Launch EDB
# ~~~~~~~~~~
# This example launches the :class:`pyaedt.Edb` class.
# This example uses EDB 2021.2 and uses SI units.

edb = Edb(edbpath=targetfolder, edbversion="2021.2")

###############################################################################
# Plot a custom set of nets colored by Nets.

edb.core_nets.plot(["VREF", "V3P3_S0"], color_by_layer=False)

###############################################################################
# Plot a custom set of nets colored by Layer.

edb.core_nets.plot("V3P3_S0", color_by_layer=True)

###############################################################################
# Plot all nets on a layer colored by Nets.

edb.core_nets.plot(None, ["TOP"], color_by_layer=False)

###############################################################################
# Close Db

edb.close_edb()
2 changes: 1 addition & 1 deletion examples/02-Maxwell/Maxwell3DTeam3.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,4 @@

###############################################################################
# The electronics desktop is released from the script engine, we leave the desktop and project open.
M3D.release_desktop(False, False)
M3D.release_desktop(True, True)
152 changes: 152 additions & 0 deletions pyaedt/edb_core/nets.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
from __future__ import absolute_import
import warnings
import random
import time

from pyaedt.generic.general_methods import is_ironpython
from pyaedt.generic.general_methods import aedt_exception_handler, generate_unique_name

try:
from matplotlib import pyplot as plt
except ImportError:
if not is_ironpython:
warnings.warn(
"The Matplotlib module is required to run some functionalities.\n" "Install with \npip install matplotlib"
)
pass


class EdbNets(object):
"""Manages EDB functionalities for nets.
Expand Down Expand Up @@ -100,6 +113,145 @@ def power_nets(self):
nets[net] = value
return nets

@aedt_exception_handler
def plot(self, nets, layers=None, color_by_layer=True, save_plot=None, outline=None):
"""Plot a Net to Matplotlib 2D Chart.
Parameters
----------
nets : str, list
Name of the net or list of nets to plot. If `None` all nets will be plotted.
layers : str, list
Name of the layers on which plot. If `None` all the signal layers will be considered.
color_by_layer : bool
If `True` then the plot will be colored by layer.
If `False` the plot will be colored by net.
save_plot : str, optional
If `None` the plot will be shown.
If a path is entered the plot will be saved to such path.
outline : list, optional
List of points of the outline to plot.
"""
start_time = time.time()
if is_ironpython:
self._logger.warning("Plot functionalities are enabled only in CPython.")
return False
if not layers:
layers = list(self._pedb.core_stackup.signal_layers.keys())
if not nets:
nets = list(self.nets.keys())
labels = {}
fig, ax = plt.subplots(figsize=(20, 10))
if outline:
x1 = [i[0] for i in outline]
y1 = [i[1] for i in outline]
plt.fill(x1, y1, c="b", label="Outline", alpha=0.3)

if isinstance(nets, str):
nets = [nets]
for path in self._pedb.core_primitives.paths:
net_name = path.GetNet().GetName()
layer_name = path.GetLayer().GetName()
if net_name in nets and layer_name in layers:
my_net_points = path.GetPolygonData().Points
x = []
y = []
for point in list(my_net_points):
if point.Y.ToDouble() < 1e100:
x.append(point.X.ToDouble())
y.append(point.Y.ToDouble())
if not x:
continue
if color_by_layer:
label = "Layer " + layer_name
if label not in labels:
color = path.GetLayer().GetColor()
try:
c = tuple([color.Item1 / 255, color.Item2 / 255, color.Item3 / 255])
except:
c = "b"
labels[label] = c
plt.fill(x, y, c=labels[label], label=label, alpha=0.3)
else:
plt.fill(x, y, c=labels[label], alpha=0.3)
else:
label = "Net " + net_name
if label not in labels:
labels[label] = tuple(
[
round(random.uniform(0, 1), 3),
round(random.uniform(0, 1), 3),
round(random.uniform(0, 1), 3),
]
)
plt.fill(x, y, c=labels[label], label=label, alpha=0.3)
else:
plt.fill(x, y, c=labels[label], alpha=0.3)

for poly in self._pedb.core_primitives.polygons:
net_name = poly.GetNet().GetName()
layer_name = poly.GetLayer().GetName()
if net_name in nets and layer_name in layers:

my_net_points = poly.GetPolygonData().Points
x = []
y = []
for point in list(my_net_points):
if point.Y.ToDouble() < 1e100:
x.append(point.X.ToDouble())
y.append(point.Y.ToDouble())
if not x:
continue
if color_by_layer:
label = "Layer " + layer_name
if label not in labels:
color = poly.GetLayer().GetColor()
try:
c = tuple([color.Item1 / 255, color.Item2 / 255, color.Item3 / 255])
except:
c = "b"
labels[label] = c
plt.fill(x, y, c=labels[label], label=label, alpha=0.3)
else:
plt.fill(x, y, c=labels[label], alpha=0.3)
else:
label = "Net " + net_name
if label not in labels:
labels[label] = tuple(
[
round(random.uniform(0, 1), 3),
round(random.uniform(0, 1), 3),
round(random.uniform(0, 1), 3),
]
)
plt.fill(x, y, c=labels[label], label=label, alpha=0.3)
else:
plt.fill(x, y, c=labels[label], alpha=0.3)

for void in poly.Voids:
v_points = void.GetPolygonData().Points
x1 = []
y1 = []
for point in list(v_points):
if point.Y.ToDouble() < 1e100:
x1.append(point.X.ToDouble())
y1.append(point.Y.ToDouble())
if x1:
if "Voids" not in labels:
labels["Voids"] = "black"
plt.fill(x1, y1, c="black", alpha=1, label="Voids")
else:
plt.fill(x1, y1, c="black", alpha=1)

ax.set(xlabel="X (m)", ylabel="Y (m)", title=self._pedb.active_cell.GetName())
ax.legend()
end_time = time.time() - start_time
self._logger.info("Plot Generation time %s seconds", round(end_time, 3))
if save_plot:
plt.savefig(save_plot)
else:
plt.show()

@aedt_exception_handler
def is_power_gound_net(self, netname_list):
"""Determine if one of the nets in a list is power or ground.
Expand Down

0 comments on commit b36d772

Please sign in to comment.