pyedb 0.31.0__py3-none-any.whl → 0.34.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyedb might be problematic. Click here for more details.
- pyedb/__init__.py +1 -27
- pyedb/common/__init__.py +0 -0
- pyedb/common/nets.py +488 -0
- pyedb/configuration/cfg_common.py +20 -0
- pyedb/configuration/cfg_components.py +218 -57
- pyedb/configuration/cfg_data.py +6 -0
- pyedb/configuration/cfg_modeler.py +139 -0
- pyedb/configuration/cfg_operations.py +5 -4
- pyedb/configuration/cfg_padstacks.py +319 -55
- pyedb/configuration/cfg_ports_sources.py +99 -7
- pyedb/configuration/cfg_s_parameter_models.py +6 -6
- pyedb/configuration/configuration.py +31 -9
- pyedb/dotnet/clr_module.py +92 -32
- pyedb/dotnet/edb.py +54 -5
- pyedb/dotnet/edb_core/cell/hierarchy/component.py +0 -202
- pyedb/dotnet/edb_core/cell/layout.py +1 -1
- pyedb/dotnet/edb_core/cell/primitive/primitive.py +5 -27
- pyedb/dotnet/edb_core/edb_data/control_file.py +21 -0
- pyedb/dotnet/edb_core/edb_data/nets_data.py +6 -1
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +16 -222
- pyedb/dotnet/edb_core/edb_data/primitives_data.py +31 -0
- pyedb/dotnet/edb_core/hfss.py +2 -2
- pyedb/dotnet/edb_core/layout_validation.py +1 -3
- pyedb/dotnet/edb_core/materials.py +38 -38
- pyedb/dotnet/edb_core/modeler.py +4 -1
- pyedb/dotnet/edb_core/nets.py +2 -373
- pyedb/generic/filesystem.py +2 -5
- {pyedb-0.31.0.dist-info → pyedb-0.34.0.dist-info}/METADATA +12 -9
- {pyedb-0.31.0.dist-info → pyedb-0.34.0.dist-info}/RECORD +31 -28
- {pyedb-0.31.0.dist-info → pyedb-0.34.0.dist-info}/WHEEL +1 -1
- {pyedb-0.31.0.dist-info → pyedb-0.34.0.dist-info}/LICENSE +0 -0
pyedb/__init__.py
CHANGED
|
@@ -11,13 +11,6 @@ if "PYEDB_USE_DOTNET" not in os.environ:
|
|
|
11
11
|
os.environ["PYEDB_USE_DOTNET"] = "0"
|
|
12
12
|
|
|
13
13
|
LATEST_DEPRECATED_PYTHON_VERSION = (3, 7)
|
|
14
|
-
LINUX_WARNING = (
|
|
15
|
-
"Due to compatibility issues between .NET Core and libssl on some Linux versions, "
|
|
16
|
-
"for example Ubuntu 22.04, we are going to stop depending on `dotnetcore2`."
|
|
17
|
-
"Instead of using this package which embeds .NET Core 3, users will be required to "
|
|
18
|
-
"install .NET themselves. For more information, see "
|
|
19
|
-
"https://edb.docs.pyansys.com/version/stable/build_breaking_change.html"
|
|
20
|
-
)
|
|
21
14
|
|
|
22
15
|
|
|
23
16
|
def deprecation_warning():
|
|
@@ -46,31 +39,12 @@ def deprecation_warning():
|
|
|
46
39
|
warnings.showwarning = existing_showwarning
|
|
47
40
|
|
|
48
41
|
|
|
49
|
-
def linux_warning():
|
|
50
|
-
"""Warning message informing Linux users a future breaking change is coming."""
|
|
51
|
-
# Store warnings showwarning
|
|
52
|
-
existing_showwarning = warnings.showwarning
|
|
53
|
-
|
|
54
|
-
# Define and use custom showwarning
|
|
55
|
-
def custom_show_warning(message, category, filename, lineno, file=None, line=None):
|
|
56
|
-
"""Custom warning used to remove <stdin>:loc: pattern."""
|
|
57
|
-
print("{}: {}".format(category.__name__, message))
|
|
58
|
-
|
|
59
|
-
warnings.showwarning = custom_show_warning
|
|
60
|
-
|
|
61
|
-
if os.name == "posix":
|
|
62
|
-
warnings.warn(LINUX_WARNING, FutureWarning)
|
|
63
|
-
|
|
64
|
-
# Restore warnings showwarning
|
|
65
|
-
warnings.showwarning = existing_showwarning
|
|
66
|
-
|
|
67
|
-
|
|
68
42
|
deprecation_warning()
|
|
69
43
|
|
|
70
44
|
#
|
|
71
45
|
|
|
72
46
|
pyedb_path = os.path.dirname(__file__)
|
|
73
|
-
__version__ = "0.
|
|
47
|
+
__version__ = "0.34.0"
|
|
74
48
|
version = __version__
|
|
75
49
|
|
|
76
50
|
#
|
pyedb/common/__init__.py
ADDED
|
File without changes
|
pyedb/common/nets.py
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
import shapely
|
|
6
|
+
|
|
7
|
+
from pyedb.generic.constants import CSS4_COLORS
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_notebook():
|
|
11
|
+
"""Check if pyaedt is running in Jupyter or not.
|
|
12
|
+
|
|
13
|
+
Returns
|
|
14
|
+
-------
|
|
15
|
+
bool
|
|
16
|
+
"""
|
|
17
|
+
try:
|
|
18
|
+
shell = get_ipython().__class__.__name__
|
|
19
|
+
if shell in ["ZMQInteractiveShell"]: # pragma: no cover
|
|
20
|
+
return True # Jupyter notebook or qtconsole
|
|
21
|
+
else:
|
|
22
|
+
return False
|
|
23
|
+
except NameError:
|
|
24
|
+
return False # Probably standard Python interpreter
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def is_ipython():
|
|
28
|
+
"""Check if pyaedt is running in Jupyter or not.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
bool
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
shell = get_ipython().__class__.__name__
|
|
36
|
+
if shell in ["TerminalInteractiveShell", "SpyderShell"]:
|
|
37
|
+
return True # Jupyter notebook or qtconsole
|
|
38
|
+
else: # pragma: no cover
|
|
39
|
+
return False
|
|
40
|
+
except NameError:
|
|
41
|
+
return False # Probably standard Python interpreter
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class CommonNets:
|
|
45
|
+
def __init__(self, _pedb):
|
|
46
|
+
self._pedb = _pedb
|
|
47
|
+
|
|
48
|
+
def plot(
|
|
49
|
+
self,
|
|
50
|
+
nets=None,
|
|
51
|
+
layers=None,
|
|
52
|
+
color_by_net=False,
|
|
53
|
+
show_legend=True,
|
|
54
|
+
save_plot=None,
|
|
55
|
+
outline=None,
|
|
56
|
+
size=(6000, 3000),
|
|
57
|
+
plot_components=True,
|
|
58
|
+
top_view=True,
|
|
59
|
+
show=True,
|
|
60
|
+
annotate_component_names=True,
|
|
61
|
+
plot_vias=False,
|
|
62
|
+
include_outline=True,
|
|
63
|
+
plot_edges=True,
|
|
64
|
+
**kwargs,
|
|
65
|
+
):
|
|
66
|
+
"""Plot a Net to Matplotlib 2D Chart.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
nets : str, list, optional
|
|
71
|
+
Name of the net or list of nets to plot. If ``None`` all nets will be plotted.
|
|
72
|
+
layers : str, list, optional
|
|
73
|
+
Name of the layers to include in the plot. If ``None`` all the signal layers will be considered.
|
|
74
|
+
color_by_net : bool, optional
|
|
75
|
+
If ``True`` the plot will be colored by net.
|
|
76
|
+
If ``False`` the plot will be colored by layer. (default)
|
|
77
|
+
show_legend : bool, optional
|
|
78
|
+
If ``True`` the legend is shown in the plot. (default)
|
|
79
|
+
If ``False`` the legend is not shown.
|
|
80
|
+
save_plot : str, optional
|
|
81
|
+
If a path is specified the plot will be saved in this location.
|
|
82
|
+
If ``save_plot`` is provided, the ``show`` parameter is ignored.
|
|
83
|
+
outline : list, optional
|
|
84
|
+
Add a customer outline from a list of points of the outline to plot.
|
|
85
|
+
size : tuple, int, optional
|
|
86
|
+
Image size in pixel (width, height). Default value is ``(6000, 3000)``
|
|
87
|
+
top_view : bool, optional
|
|
88
|
+
Whether if use top view or bottom view. Components will be visible only for the highest layer in the view.
|
|
89
|
+
plot_components : bool, optional
|
|
90
|
+
If ``True`` the components placed on top layer are plotted.
|
|
91
|
+
If ``False`` the components are not plotted. (default).
|
|
92
|
+
This may impact in the plot computation time.
|
|
93
|
+
If nets and/or layers is specified, only the components belonging to the specified nets/layers are plotted.
|
|
94
|
+
annotate_component_names: bool, optional
|
|
95
|
+
Whether to add the component names to the plot or not. Default is ``True``.
|
|
96
|
+
plot_vias : bool, optional
|
|
97
|
+
Whether to plot vias (circular and rectangular) or not. This may impact in the plot computation time.
|
|
98
|
+
Default is ``False``.
|
|
99
|
+
show : bool, optional
|
|
100
|
+
Whether to show the plot or not. Default is `True`.
|
|
101
|
+
include_outline : bool, optional
|
|
102
|
+
Whether to include the internal layout outline or not. Default is `True`.
|
|
103
|
+
plot_edges : bool, optional
|
|
104
|
+
Whether to plot polygon edges or not. Default is `True`.
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
(ax, fig)
|
|
109
|
+
Matplotlib ax and figures.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
if "plot_components_on_top" in kwargs and top_view:
|
|
113
|
+
plot_components = kwargs["plot_components_on_top"]
|
|
114
|
+
if "plot_components_on_bottom" in kwargs and not top_view:
|
|
115
|
+
plot_components = kwargs["plot_components_on_bottom"]
|
|
116
|
+
|
|
117
|
+
def mirror_poly(poly):
|
|
118
|
+
sign = 1
|
|
119
|
+
if not top_view:
|
|
120
|
+
sign = -1
|
|
121
|
+
return [[sign * i[0], i[1]] for i in poly]
|
|
122
|
+
|
|
123
|
+
import matplotlib.pyplot as plt
|
|
124
|
+
|
|
125
|
+
dpi = 100.0
|
|
126
|
+
figsize = (size[0] / dpi, size[1] / dpi)
|
|
127
|
+
|
|
128
|
+
fig = plt.figure(figsize=figsize)
|
|
129
|
+
ax = fig.add_subplot(1, 1, 1)
|
|
130
|
+
from shapely import affinity
|
|
131
|
+
from shapely.geometry import (
|
|
132
|
+
LinearRing,
|
|
133
|
+
MultiLineString,
|
|
134
|
+
MultiPolygon,
|
|
135
|
+
Point,
|
|
136
|
+
Polygon,
|
|
137
|
+
)
|
|
138
|
+
from shapely.plotting import plot_line, plot_polygon
|
|
139
|
+
|
|
140
|
+
start_time = time.time()
|
|
141
|
+
if not nets:
|
|
142
|
+
nets = list(self.nets.keys())
|
|
143
|
+
if isinstance(nets, str):
|
|
144
|
+
nets = [nets]
|
|
145
|
+
if not layers:
|
|
146
|
+
layers = list(self._pedb.stackup.signal_layers.keys())
|
|
147
|
+
if isinstance(layers, str):
|
|
148
|
+
layers = [layers]
|
|
149
|
+
color_index = 0
|
|
150
|
+
label_colors = {}
|
|
151
|
+
edge_colors = {}
|
|
152
|
+
if outline:
|
|
153
|
+
poly = Polygon(outline)
|
|
154
|
+
plot_line(poly.boundary, add_points=False, color=(0.7, 0, 0), linewidth=4)
|
|
155
|
+
elif include_outline:
|
|
156
|
+
prims = self._pedb.modeler.primitives_by_layer.get("Outline", [])
|
|
157
|
+
if prims:
|
|
158
|
+
for prim in prims:
|
|
159
|
+
if prim.is_void:
|
|
160
|
+
continue
|
|
161
|
+
xt, yt = prim.points()
|
|
162
|
+
p1 = [(i, j) for i, j in zip(xt[::-1], yt[::-1])]
|
|
163
|
+
p1 = mirror_poly(p1)
|
|
164
|
+
poly = LinearRing(p1)
|
|
165
|
+
plot_line(poly, add_points=False, color=(0.7, 0, 0), linewidth=4)
|
|
166
|
+
else:
|
|
167
|
+
bbox = self._pedb.hfss.get_layout_bounding_box()
|
|
168
|
+
if not bbox:
|
|
169
|
+
return False, False
|
|
170
|
+
x1 = bbox[0]
|
|
171
|
+
x2 = bbox[2]
|
|
172
|
+
y1 = bbox[1]
|
|
173
|
+
y2 = bbox[3]
|
|
174
|
+
p = [(x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1)]
|
|
175
|
+
p = mirror_poly(p)
|
|
176
|
+
poly = LinearRing(p)
|
|
177
|
+
plot_line(poly, add_points=False, color=(0.7, 0, 0), linewidth=4)
|
|
178
|
+
layer_colors = {i: k.color for i, k in self._pedb.stackup.layers.items()}
|
|
179
|
+
top_layer = list(self._pedb.stackup.signal_layers.keys())[0]
|
|
180
|
+
bottom_layer = list(self._pedb.stackup.signal_layers.keys())[-1]
|
|
181
|
+
lines = []
|
|
182
|
+
top_comps = []
|
|
183
|
+
bottom_comps = []
|
|
184
|
+
if plot_components:
|
|
185
|
+
nc = 0
|
|
186
|
+
|
|
187
|
+
for comp in self._pedb.components.instances.values():
|
|
188
|
+
if not comp.is_enabled:
|
|
189
|
+
continue
|
|
190
|
+
net_names = comp.nets
|
|
191
|
+
if nets and not any([i in nets for i in net_names]):
|
|
192
|
+
continue
|
|
193
|
+
layer_name = comp.placement_layer
|
|
194
|
+
if layer_name not in layers:
|
|
195
|
+
continue
|
|
196
|
+
if plot_components and top_view and layer_name == top_layer:
|
|
197
|
+
component_color = (0 / 255, 0 / 255, 0 / 255) # this is the color used in AEDT
|
|
198
|
+
label = "Component on top layer"
|
|
199
|
+
label_colors[label] = component_color
|
|
200
|
+
elif plot_components and not top_view and layer_name == bottom_layer:
|
|
201
|
+
component_color = (41 / 255, 41 / 255, 41 / 255) # 41, 171, 135
|
|
202
|
+
label = "Component on bottom layer"
|
|
203
|
+
label_colors[label] = component_color
|
|
204
|
+
else:
|
|
205
|
+
continue
|
|
206
|
+
for pinst in comp.pins.values():
|
|
207
|
+
pdef = pinst.definition
|
|
208
|
+
p_b_l = {i: j for i, j in pdef.pad_by_layer.items()}
|
|
209
|
+
pinst_net_name = pinst.net_name
|
|
210
|
+
if top_view and top_layer in p_b_l and pinst_net_name in nets:
|
|
211
|
+
try:
|
|
212
|
+
shape = p_b_l[top_layer].shape
|
|
213
|
+
if shape.lower() == "circle":
|
|
214
|
+
poly = Point(pinst.position)
|
|
215
|
+
top_comps.append(poly.buffer(p_b_l[top_layer].parameters_values[0] / 2))
|
|
216
|
+
elif shape.lower() == "rectangle":
|
|
217
|
+
px, py = pinst.position
|
|
218
|
+
w, h = p_b_l[top_layer].parameters_values
|
|
219
|
+
poly = [
|
|
220
|
+
[px - w / 2, py - h / 2],
|
|
221
|
+
[px - w / 2, py + h / 2],
|
|
222
|
+
[px + w / 2, py + h / 2],
|
|
223
|
+
[px + w / 2, py - h / 2],
|
|
224
|
+
]
|
|
225
|
+
poly = Polygon(poly)
|
|
226
|
+
top_comps.append(
|
|
227
|
+
affinity.rotate(
|
|
228
|
+
poly,
|
|
229
|
+
(float(p_b_l[top_layer].rotation) + pinst.rotation + comp.rotation)
|
|
230
|
+
/ math.pi
|
|
231
|
+
* 180,
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
except KeyError:
|
|
235
|
+
pass
|
|
236
|
+
elif not top_view and bottom_layer in p_b_l and pinst.net_name in nets:
|
|
237
|
+
try:
|
|
238
|
+
shape = p_b_l[bottom_layer].shape
|
|
239
|
+
if shape == "Circle":
|
|
240
|
+
x, y = pinst.position
|
|
241
|
+
poly = Point(-x, y)
|
|
242
|
+
bottom_comps.append(poly.buffer(p_b_l[bottom_layer].parameters_values[0] / 2))
|
|
243
|
+
elif shape == "Rectangle":
|
|
244
|
+
px, py = pinst.position
|
|
245
|
+
w, h = p_b_l[bottom_layer].parameters_values
|
|
246
|
+
poly = [
|
|
247
|
+
[px - w / 2, py - h / 2],
|
|
248
|
+
[px - w / 2, py + h / 2],
|
|
249
|
+
[px + w / 2, py + h / 2],
|
|
250
|
+
[px + w / 2, py - h / 2],
|
|
251
|
+
]
|
|
252
|
+
poly = Polygon(mirror_poly(poly))
|
|
253
|
+
bottom_comps.append(
|
|
254
|
+
affinity.rotate(
|
|
255
|
+
poly,
|
|
256
|
+
-(float(p_b_l[bottom_layer].rotation) + pinst.rotation + comp.rotation)
|
|
257
|
+
/ math.pi
|
|
258
|
+
* 180,
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
except KeyError:
|
|
262
|
+
pass
|
|
263
|
+
cbb = comp.bounding_box
|
|
264
|
+
x = [cbb[0], cbb[0], cbb[2], cbb[2]]
|
|
265
|
+
y = [cbb[1], cbb[3], cbb[3], cbb[1]]
|
|
266
|
+
vertices = [(i, j) for i, j in zip(x, y)]
|
|
267
|
+
vertices = mirror_poly(vertices)
|
|
268
|
+
poly = Polygon(vertices)
|
|
269
|
+
lines.append(poly.boundary)
|
|
270
|
+
if annotate_component_names:
|
|
271
|
+
font_size = 6 if poly.area < 6e-6 else 10
|
|
272
|
+
ax.annotate(
|
|
273
|
+
comp.name,
|
|
274
|
+
(poly.centroid.x, poly.centroid.y),
|
|
275
|
+
va="center",
|
|
276
|
+
ha="center",
|
|
277
|
+
color=component_color,
|
|
278
|
+
size=font_size,
|
|
279
|
+
rotation=comp.rotation * 180 / math.pi,
|
|
280
|
+
)
|
|
281
|
+
self._logger.debug("Plotted {} component(s)".format(nc))
|
|
282
|
+
|
|
283
|
+
if top_comps:
|
|
284
|
+
ob = MultiPolygon(top_comps)
|
|
285
|
+
plot_polygon(ob, add_points=False, ax=ax)
|
|
286
|
+
if bottom_comps:
|
|
287
|
+
ob = MultiPolygon(bottom_comps)
|
|
288
|
+
plot_polygon(ob, add_points=False, ax=ax)
|
|
289
|
+
|
|
290
|
+
if lines:
|
|
291
|
+
ob = MultiLineString(lines)
|
|
292
|
+
plot_line(ob, ax=ax, add_points=False, color=(1, 0, 0), linewidth=1)
|
|
293
|
+
|
|
294
|
+
def create_poly(prim, polys, lines):
|
|
295
|
+
if prim.is_void:
|
|
296
|
+
return
|
|
297
|
+
net_name = prim.net_name
|
|
298
|
+
layer_name = prim.layer_name
|
|
299
|
+
if nets and (net_name not in nets or layer_name not in layers):
|
|
300
|
+
return
|
|
301
|
+
# if prim.primitive_type == "path":
|
|
302
|
+
# line = prim.center_line
|
|
303
|
+
# line = mirror_poly(line)
|
|
304
|
+
# poly = LineString(line).buffer(prim.width / 2)
|
|
305
|
+
# else:
|
|
306
|
+
xt, yt = prim.points()
|
|
307
|
+
if len(xt) < 3:
|
|
308
|
+
return
|
|
309
|
+
p1 = [(i, j) for i, j in zip(xt[::-1], yt[::-1])]
|
|
310
|
+
p1 = mirror_poly(p1)
|
|
311
|
+
|
|
312
|
+
holes = []
|
|
313
|
+
for void in prim.voids:
|
|
314
|
+
xvt, yvt = void.points(arc_segments=3)
|
|
315
|
+
if len(xvt) < 3:
|
|
316
|
+
continue
|
|
317
|
+
h1 = mirror_poly([(i, j) for i, j in zip(xvt, yvt)])
|
|
318
|
+
holes.append(h1)
|
|
319
|
+
if len(holes) > 1:
|
|
320
|
+
holes = shapely.union_all([Polygon(i) for i in holes])
|
|
321
|
+
if isinstance(holes, MultiPolygon):
|
|
322
|
+
holes = [i.boundary for i in list(holes.geoms)]
|
|
323
|
+
else:
|
|
324
|
+
holes = [holes.boundary]
|
|
325
|
+
poly = Polygon(p1, holes)
|
|
326
|
+
if layer_name == "Outline":
|
|
327
|
+
if label_colors[label] in lines:
|
|
328
|
+
lines.append(poly.boundary)
|
|
329
|
+
elif poly:
|
|
330
|
+
polys.append(poly)
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
if color_by_net:
|
|
334
|
+
for net in nets:
|
|
335
|
+
prims = self._pedb.nets.nets[net].primitives
|
|
336
|
+
polys = []
|
|
337
|
+
lines = []
|
|
338
|
+
if net not in nets:
|
|
339
|
+
continue
|
|
340
|
+
label = "Net " + net
|
|
341
|
+
label_colors[label] = list(CSS4_COLORS.keys())[color_index]
|
|
342
|
+
try:
|
|
343
|
+
edge_colors[label] = [i * 0.5 for i in label_colors[label]]
|
|
344
|
+
except TypeError:
|
|
345
|
+
edge_colors[label] = label_colors[label]
|
|
346
|
+
color_index += 1
|
|
347
|
+
if color_index >= len(CSS4_COLORS):
|
|
348
|
+
color_index = 0
|
|
349
|
+
for prim in prims:
|
|
350
|
+
create_poly(prim, polys, lines)
|
|
351
|
+
if polys:
|
|
352
|
+
ob = MultiPolygon(polys)
|
|
353
|
+
plot_polygon(
|
|
354
|
+
ob,
|
|
355
|
+
ax=ax,
|
|
356
|
+
color=label_colors[label],
|
|
357
|
+
add_points=False,
|
|
358
|
+
alpha=0.7,
|
|
359
|
+
label=label,
|
|
360
|
+
edgecolor="none" if not plot_edges else edge_colors[label],
|
|
361
|
+
)
|
|
362
|
+
if lines:
|
|
363
|
+
ob = MultiLineString(p)
|
|
364
|
+
plot_line(ob, ax=ax, add_points=False, color=label_colors[label], linewidth=1, label=label)
|
|
365
|
+
else:
|
|
366
|
+
prims_by_layers_dict = {i: j for i, j in self._pedb.modeler.primitives_by_layer.items()}
|
|
367
|
+
if not top_view:
|
|
368
|
+
prims_by_layers_dict = {
|
|
369
|
+
i: prims_by_layers_dict[i] for i in reversed(self._pedb.modeler.primitives_by_layer.keys())
|
|
370
|
+
}
|
|
371
|
+
num_layers = len(layers)
|
|
372
|
+
delta_alpha = 0.7 / num_layers
|
|
373
|
+
alpha = 0.3
|
|
374
|
+
for layer, prims in prims_by_layers_dict.items():
|
|
375
|
+
polys = []
|
|
376
|
+
lines = []
|
|
377
|
+
if layer not in layers:
|
|
378
|
+
continue
|
|
379
|
+
label = "Layer " + layer
|
|
380
|
+
if label not in label_colors:
|
|
381
|
+
try:
|
|
382
|
+
color = layer_colors[layer]
|
|
383
|
+
c = (
|
|
384
|
+
float(color[0] / 255),
|
|
385
|
+
float(color[1] / 255),
|
|
386
|
+
float(color[2] / 255),
|
|
387
|
+
)
|
|
388
|
+
except:
|
|
389
|
+
c = list(CSS4_COLORS.keys())[color_index]
|
|
390
|
+
color_index += 1
|
|
391
|
+
if color_index >= len(CSS4_COLORS):
|
|
392
|
+
color_index = 0
|
|
393
|
+
label_colors[label] = c
|
|
394
|
+
try:
|
|
395
|
+
edge_colors[label] = [i * 0.5 for i in c]
|
|
396
|
+
except TypeError:
|
|
397
|
+
edge_colors[label] = label_colors[label]
|
|
398
|
+
for prim in prims:
|
|
399
|
+
create_poly(prim, polys, lines)
|
|
400
|
+
if polys:
|
|
401
|
+
ob = MultiPolygon(polys)
|
|
402
|
+
plot_polygon(
|
|
403
|
+
ob,
|
|
404
|
+
ax=ax,
|
|
405
|
+
color=label_colors[label],
|
|
406
|
+
add_points=False,
|
|
407
|
+
alpha=alpha,
|
|
408
|
+
label=label,
|
|
409
|
+
edgecolor="none" if not plot_edges else edge_colors[label],
|
|
410
|
+
)
|
|
411
|
+
if lines:
|
|
412
|
+
ob = MultiLineString(p)
|
|
413
|
+
plot_line(ob, ax=ax, add_points=False, color=label_colors[label], linewidth=1, label=label)
|
|
414
|
+
alpha = alpha + delta_alpha
|
|
415
|
+
|
|
416
|
+
if plot_vias:
|
|
417
|
+
polys = []
|
|
418
|
+
|
|
419
|
+
for pinst in self._pedb.padstacks.instances.values():
|
|
420
|
+
if pinst.is_pin:
|
|
421
|
+
continue
|
|
422
|
+
pdef = pinst.definition
|
|
423
|
+
p_b_l = {i: j for i, j in pdef.pad_by_layer.items()}
|
|
424
|
+
pinst_net_name = pinst.net_name
|
|
425
|
+
if top_view and pinst_net_name in nets:
|
|
426
|
+
for k in range(len(layers)):
|
|
427
|
+
if layers[k] in p_b_l.keys():
|
|
428
|
+
pad_value = p_b_l[layers[k]]
|
|
429
|
+
break
|
|
430
|
+
elif not top_view and pinst_net_name in nets:
|
|
431
|
+
rev_layers = list(reversed(layers))
|
|
432
|
+
for k in range(len(rev_layers)):
|
|
433
|
+
if rev_layers[k] in p_b_l.keys():
|
|
434
|
+
pad_value = p_b_l[rev_layers[k]]
|
|
435
|
+
break
|
|
436
|
+
else:
|
|
437
|
+
continue
|
|
438
|
+
try:
|
|
439
|
+
shape = pad_value.shape
|
|
440
|
+
if shape.lower() == "circle":
|
|
441
|
+
x, y = pinst.position
|
|
442
|
+
if top_view:
|
|
443
|
+
poly = Point(pinst.position)
|
|
444
|
+
else:
|
|
445
|
+
poly = Point(-x, y)
|
|
446
|
+
polys.append(poly.buffer(p_b_l[top_layer].parameters_values[0] / 2))
|
|
447
|
+
elif shape.lower() == "rectangle":
|
|
448
|
+
px, py = pinst.position
|
|
449
|
+
w, h = pad_value.parameters_values
|
|
450
|
+
poly = [
|
|
451
|
+
[px - w / 2, py - h / 2],
|
|
452
|
+
[px - w / 2, py + h / 2],
|
|
453
|
+
[px + w / 2, py + h / 2],
|
|
454
|
+
[px + w / 2, py - h / 2],
|
|
455
|
+
]
|
|
456
|
+
poly = Polygon(mirror_poly(poly))
|
|
457
|
+
polys.append(
|
|
458
|
+
affinity.rotate(
|
|
459
|
+
poly, (float(pad_value.rotation) + pinst.rotation + comp.rotation) / math.pi * 180
|
|
460
|
+
)
|
|
461
|
+
)
|
|
462
|
+
except KeyError:
|
|
463
|
+
pass
|
|
464
|
+
if polys:
|
|
465
|
+
ob = MultiPolygon(polys)
|
|
466
|
+
plot_polygon(ob, add_points=False, ax=ax, edgecolor="none")
|
|
467
|
+
# Hide grid lines
|
|
468
|
+
ax.grid(False)
|
|
469
|
+
ax.set_axis_off()
|
|
470
|
+
# Hide axes ticks
|
|
471
|
+
ax.set_xticks([])
|
|
472
|
+
ax.set_yticks([])
|
|
473
|
+
message = "Edb Top View" if top_view else "Edb Bottom View"
|
|
474
|
+
plt.title(message, size=20)
|
|
475
|
+
if show_legend:
|
|
476
|
+
plt.legend(loc="upper left", fontsize="x-large")
|
|
477
|
+
end_time = time.time() - start_time
|
|
478
|
+
self._logger.info(f"Plot Generation time {round(end_time, 3)}")
|
|
479
|
+
if save_plot:
|
|
480
|
+
plt.savefig(save_plot)
|
|
481
|
+
if show: # pragma: no cover
|
|
482
|
+
if is_notebook():
|
|
483
|
+
pass
|
|
484
|
+
elif is_ipython() or "PYTEST_CURRENT_TEST" in os.environ:
|
|
485
|
+
fig.show()
|
|
486
|
+
else:
|
|
487
|
+
plt.show()
|
|
488
|
+
return fig, ax
|
|
@@ -39,3 +39,23 @@ class CfgBase:
|
|
|
39
39
|
if attr not in dir(pedb_object):
|
|
40
40
|
raise AttributeError(f"Invalid attribute '{attr}' in '{pedb_object}'")
|
|
41
41
|
setattr(pedb_object, attr, value)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class CfgVar:
|
|
45
|
+
def __init__(self, **kwargs):
|
|
46
|
+
self.name = kwargs["name"]
|
|
47
|
+
self.value = kwargs["value"]
|
|
48
|
+
self.description = kwargs.get("description", "")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class CfgVariables:
|
|
52
|
+
def __init__(self, pedb, data):
|
|
53
|
+
self._pedb = pedb
|
|
54
|
+
self.variables = [CfgVar(**i) for i in data]
|
|
55
|
+
|
|
56
|
+
def apply(self):
|
|
57
|
+
for i in self.variables:
|
|
58
|
+
if i.name.startswith("$"):
|
|
59
|
+
self._pedb.add_project_variable(i.name, i.value)
|
|
60
|
+
else:
|
|
61
|
+
self._pedb.add_design_variable(i.name, i.value)
|