Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement/edb plot #669

Merged
merged 6 commits into from
Dec 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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