pyedb 0.23.0__py3-none-any.whl → 0.24.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 -1
- pyedb/dotnet/edb.py +35 -33
- pyedb/dotnet/edb_core/cell/hierarchy/hierarchy_obj.py +11 -0
- pyedb/dotnet/edb_core/cell/layout.py +30 -23
- pyedb/dotnet/edb_core/cell/layout_obj.py +0 -9
- pyedb/dotnet/edb_core/cell/primitive/__init__.py +3 -0
- pyedb/dotnet/edb_core/cell/{primitive.py → primitive/bondwire.py} +1 -146
- pyedb/dotnet/edb_core/cell/primitive/path.py +351 -0
- pyedb/dotnet/edb_core/cell/primitive/primitive.py +895 -0
- pyedb/dotnet/edb_core/cell/terminal/bundle_terminal.py +0 -4
- pyedb/dotnet/edb_core/cell/terminal/edge_terminal.py +1 -1
- pyedb/dotnet/edb_core/components.py +12 -5
- pyedb/dotnet/edb_core/dotnet/database.py +1 -23
- pyedb/dotnet/edb_core/dotnet/primitive.py +3 -139
- pyedb/dotnet/edb_core/edb_data/nets_data.py +1 -11
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +10 -24
- pyedb/dotnet/edb_core/edb_data/primitives_data.py +56 -868
- pyedb/dotnet/edb_core/geometry/polygon_data.py +43 -0
- pyedb/dotnet/edb_core/hfss.py +26 -22
- pyedb/dotnet/edb_core/layout_validation.py +3 -3
- pyedb/dotnet/edb_core/modeler.py +64 -81
- pyedb/dotnet/edb_core/nets.py +5 -4
- pyedb/dotnet/edb_core/padstack.py +12 -13
- pyedb/dotnet/edb_core/siwave.py +1 -1
- pyedb/dotnet/edb_core/stackup.py +3 -3
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +1 -1
- pyedb/ipc2581/ecad/cad_data/polygon.py +2 -2
- pyedb/ipc2581/ecad/cad_data/step.py +2 -2
- pyedb/siwave.py +99 -0
- {pyedb-0.23.0.dist-info → pyedb-0.24.0.dist-info}/METADATA +2 -2
- {pyedb-0.23.0.dist-info → pyedb-0.24.0.dist-info}/RECORD +33 -30
- {pyedb-0.23.0.dist-info → pyedb-0.24.0.dist-info}/LICENSE +0 -0
- {pyedb-0.23.0.dist-info → pyedb-0.24.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
import math
|
|
23
|
+
|
|
24
|
+
from pyedb.dotnet.edb_core.cell.primitive.primitive import Primitive
|
|
25
|
+
from pyedb.dotnet.edb_core.general import convert_py_list_to_net_list
|
|
26
|
+
from pyedb.dotnet.edb_core.geometry.point_data import PointData
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Path(Primitive):
|
|
30
|
+
def __init__(self, pedb, edb_object=None):
|
|
31
|
+
super().__init__(pedb, edb_object)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def width(self):
|
|
35
|
+
"""Path width.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
float
|
|
40
|
+
Path width or None.
|
|
41
|
+
"""
|
|
42
|
+
return self._edb_object.GetWidth()
|
|
43
|
+
|
|
44
|
+
@width.setter
|
|
45
|
+
def width(self, value):
|
|
46
|
+
self.primitive_object.SetWidth(self._pedb.edb_value(value))
|
|
47
|
+
|
|
48
|
+
def get_end_cap_style(self):
|
|
49
|
+
"""Get path end cap styles.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
tuple[
|
|
54
|
+
:class:`PathEndCapType`,
|
|
55
|
+
:class:`PathEndCapType`
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
Returns a tuple of the following format:
|
|
59
|
+
|
|
60
|
+
**(end_cap1, end_cap2)**
|
|
61
|
+
|
|
62
|
+
**end_cap1** : End cap style of path start end cap.
|
|
63
|
+
|
|
64
|
+
**end_cap2** : End cap style of path end cap.
|
|
65
|
+
"""
|
|
66
|
+
return self._edb_object.GetEndCapStyle()
|
|
67
|
+
|
|
68
|
+
def set_end_cap_style(self, end_cap1, end_cap2):
|
|
69
|
+
"""Set path end cap styles.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
end_cap1: :class:`PathEndCapType`
|
|
74
|
+
End cap style of path start end cap.
|
|
75
|
+
end_cap2: :class:`PathEndCapType`
|
|
76
|
+
End cap style of path end cap.
|
|
77
|
+
"""
|
|
78
|
+
self._edb_object.SetEndCapStyle(end_cap1, end_cap2)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def length(self):
|
|
82
|
+
"""Path length in meters.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
float
|
|
87
|
+
Path length in meters.
|
|
88
|
+
"""
|
|
89
|
+
center_line_arcs = list(self._edb_object.GetCenterLine().GetArcData())
|
|
90
|
+
path_length = 0.0
|
|
91
|
+
for arc in center_line_arcs:
|
|
92
|
+
path_length += arc.GetLength()
|
|
93
|
+
if self.get_end_cap_style()[0]:
|
|
94
|
+
if not self.get_end_cap_style()[1].value__ == 1:
|
|
95
|
+
path_length += self.width / 2
|
|
96
|
+
if not self.get_end_cap_style()[2].value__ == 1:
|
|
97
|
+
path_length += self.width / 2
|
|
98
|
+
return path_length
|
|
99
|
+
|
|
100
|
+
def add_point(self, x, y, incremental=False):
|
|
101
|
+
"""Add a point at the end of the path.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
x: str, int, float
|
|
106
|
+
X coordinate.
|
|
107
|
+
y: str, in, float
|
|
108
|
+
Y coordinate.
|
|
109
|
+
incremental: bool
|
|
110
|
+
Add point incrementally. If True, coordinates of the added point is incremental to the last point.
|
|
111
|
+
The default value is ``False``.
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
bool
|
|
116
|
+
"""
|
|
117
|
+
center_line = self._edb_object.GetCenterLine()
|
|
118
|
+
|
|
119
|
+
if incremental:
|
|
120
|
+
x = self._pedb.edb_value(x)
|
|
121
|
+
y = self._pedb.edb_value(y)
|
|
122
|
+
last_point = list(center_line.Points)[-1]
|
|
123
|
+
x = "({})+({})".format(x, last_point.X.ToString())
|
|
124
|
+
y = "({})+({})".format(y, last_point.Y.ToString())
|
|
125
|
+
center_line.AddPoint(PointData(self._pedb, x=x, y=y)._edb_object)
|
|
126
|
+
return self._edb_object.SetCenterLine(center_line)
|
|
127
|
+
|
|
128
|
+
def get_center_line(self, to_string=False):
|
|
129
|
+
"""Get the center line of the trace.
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
to_string : bool, optional
|
|
134
|
+
Type of return. The default is ``"False"``.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
list
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
if to_string:
|
|
142
|
+
return [[p.X.ToString(), p.Y.ToString()] for p in list(self._edb_object.GetCenterLine().Points)]
|
|
143
|
+
else:
|
|
144
|
+
return [[p.X.ToDouble(), p.Y.ToDouble()] for p in list(self._edb_object.GetCenterLine().Points)]
|
|
145
|
+
|
|
146
|
+
def clone(self):
|
|
147
|
+
"""Clone a primitive object with keeping same definition and location.
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
bool
|
|
152
|
+
``True`` when successful, ``False`` when failed.
|
|
153
|
+
"""
|
|
154
|
+
center_line = self.center_line
|
|
155
|
+
width = self.width
|
|
156
|
+
corner_style = self.corner_style
|
|
157
|
+
end_cap_style = self.get_end_cap_style()
|
|
158
|
+
cloned_path = self._app.edb_api.cell.primitive.path.create(
|
|
159
|
+
self._app.active_layout,
|
|
160
|
+
self.layer_name,
|
|
161
|
+
self.net,
|
|
162
|
+
width,
|
|
163
|
+
end_cap_style[1],
|
|
164
|
+
end_cap_style[2],
|
|
165
|
+
corner_style,
|
|
166
|
+
center_line,
|
|
167
|
+
)
|
|
168
|
+
if cloned_path:
|
|
169
|
+
return cloned_path
|
|
170
|
+
|
|
171
|
+
#
|
|
172
|
+
|
|
173
|
+
def create_edge_port(
|
|
174
|
+
self,
|
|
175
|
+
name,
|
|
176
|
+
position="End",
|
|
177
|
+
port_type="Wave",
|
|
178
|
+
reference_layer=None,
|
|
179
|
+
horizontal_extent_factor=5,
|
|
180
|
+
vertical_extent_factor=3,
|
|
181
|
+
pec_launch_width="0.01mm",
|
|
182
|
+
):
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
name : str
|
|
188
|
+
Name of the port.
|
|
189
|
+
position : str, optional
|
|
190
|
+
Position of the port. The default is ``"End"``, in which case the port is created at the end of the trace.
|
|
191
|
+
Options are ``"Start"`` and ``"End"``.
|
|
192
|
+
port_type : str, optional
|
|
193
|
+
Type of the port. The default is ``"Wave"``, in which case a wave port is created. Options are ``"Wave"``
|
|
194
|
+
and ``"Gap"``.
|
|
195
|
+
reference_layer : str, optional
|
|
196
|
+
Name of the references layer. The default is ``None``. Only available for gap port.
|
|
197
|
+
horizontal_extent_factor : int, optional
|
|
198
|
+
Horizontal extent factor of the wave port. The default is ``5``.
|
|
199
|
+
vertical_extent_factor : int, optional
|
|
200
|
+
Vertical extent factor of the wave port. The default is ``3``.
|
|
201
|
+
pec_launch_width : float, str, optional
|
|
202
|
+
Perfect electrical conductor width of the wave port. The default is ``"0.01mm"``.
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
:class:`dotnet.edb_core.edb_data.sources.ExcitationPorts`
|
|
207
|
+
|
|
208
|
+
Examples
|
|
209
|
+
--------
|
|
210
|
+
>>> edbapp = pyedb.dotnet.Edb("myproject.aedb")
|
|
211
|
+
>>> sig = appedb.modeler.create_trace([[0, 0], ["9mm", 0]], "TOP", "1mm", "SIG", "Flat", "Flat")
|
|
212
|
+
>>> sig.create_edge_port("pcb_port", "end", "Wave", None, 8, 8)
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
center_line = self.get_center_line()
|
|
216
|
+
pos = center_line[-1] if position.lower() == "end" else center_line[0]
|
|
217
|
+
|
|
218
|
+
if port_type.lower() == "wave":
|
|
219
|
+
return self._app.hfss.create_wave_port(
|
|
220
|
+
self.id, pos, name, 50, horizontal_extent_factor, vertical_extent_factor, pec_launch_width
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
return self._app.hfss.create_edge_port_vertical(self.id, pos, name, 50, reference_layer)
|
|
224
|
+
|
|
225
|
+
def create_via_fence(self, distance, gap, padstack_name, net_name="GND"):
|
|
226
|
+
"""Create via fences on both sides of the trace.
|
|
227
|
+
|
|
228
|
+
Parameters
|
|
229
|
+
----------
|
|
230
|
+
distance: str, float
|
|
231
|
+
Distance between via fence and trace center line.
|
|
232
|
+
gap: str, float
|
|
233
|
+
Gap between vias.
|
|
234
|
+
padstack_name: str
|
|
235
|
+
Name of the via padstack.
|
|
236
|
+
net_name: str, optional
|
|
237
|
+
Name of the net.
|
|
238
|
+
|
|
239
|
+
Returns
|
|
240
|
+
-------
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
def getAngle(v1, v2): # pragma: no cover
|
|
244
|
+
v1_mag = math.sqrt(v1[0] ** 2 + v1[1] ** 2)
|
|
245
|
+
v2_mag = math.sqrt(v2[0] ** 2 + v2[1] ** 2)
|
|
246
|
+
dotsum = v1[0] * v2[0] + v1[1] * v2[1]
|
|
247
|
+
if v1[0] * v2[1] - v1[1] * v2[0] > 0:
|
|
248
|
+
scale = 1
|
|
249
|
+
else:
|
|
250
|
+
scale = -1
|
|
251
|
+
dtheta = scale * math.acos(dotsum / (v1_mag * v2_mag))
|
|
252
|
+
|
|
253
|
+
return dtheta
|
|
254
|
+
|
|
255
|
+
def getLocations(line, gap): # pragma: no cover
|
|
256
|
+
location = [line[0]]
|
|
257
|
+
residual = 0
|
|
258
|
+
|
|
259
|
+
for n in range(len(line) - 1):
|
|
260
|
+
x0, y0 = line[n]
|
|
261
|
+
x1, y1 = line[n + 1]
|
|
262
|
+
length = math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2)
|
|
263
|
+
dx, dy = (x1 - x0) / length, (y1 - y0) / length
|
|
264
|
+
x = x0 - dx * residual
|
|
265
|
+
y = y0 - dy * residual
|
|
266
|
+
length = length + residual
|
|
267
|
+
while length >= gap:
|
|
268
|
+
x += gap * dx
|
|
269
|
+
y += gap * dy
|
|
270
|
+
location.append((x, y))
|
|
271
|
+
length -= gap
|
|
272
|
+
|
|
273
|
+
residual = length
|
|
274
|
+
return location
|
|
275
|
+
|
|
276
|
+
def getParalletLines(pts, distance): # pragma: no cover
|
|
277
|
+
leftline = []
|
|
278
|
+
rightline = []
|
|
279
|
+
|
|
280
|
+
x0, y0 = pts[0]
|
|
281
|
+
x1, y1 = pts[1]
|
|
282
|
+
vector = (x1 - x0, y1 - y0)
|
|
283
|
+
orientation1 = getAngle((1, 0), vector)
|
|
284
|
+
|
|
285
|
+
leftturn = orientation1 + math.pi / 2
|
|
286
|
+
righrturn = orientation1 - math.pi / 2
|
|
287
|
+
leftPt = (x0 + distance * math.cos(leftturn), y0 + distance * math.sin(leftturn))
|
|
288
|
+
leftline.append(leftPt)
|
|
289
|
+
rightPt = (x0 + distance * math.cos(righrturn), y0 + distance * math.sin(righrturn))
|
|
290
|
+
rightline.append(rightPt)
|
|
291
|
+
|
|
292
|
+
for n in range(1, len(pts) - 1):
|
|
293
|
+
x0, y0 = pts[n - 1]
|
|
294
|
+
x1, y1 = pts[n]
|
|
295
|
+
x2, y2 = pts[n + 1]
|
|
296
|
+
|
|
297
|
+
v1 = (x1 - x0, y1 - y0)
|
|
298
|
+
v2 = (x2 - x1, y2 - y1)
|
|
299
|
+
dtheta = getAngle(v1, v2)
|
|
300
|
+
orientation1 = getAngle((1, 0), v1)
|
|
301
|
+
|
|
302
|
+
leftturn = orientation1 + dtheta / 2 + math.pi / 2
|
|
303
|
+
righrturn = orientation1 + dtheta / 2 - math.pi / 2
|
|
304
|
+
|
|
305
|
+
distance2 = distance / math.sin((math.pi - dtheta) / 2)
|
|
306
|
+
leftPt = (x1 + distance2 * math.cos(leftturn), y1 + distance2 * math.sin(leftturn))
|
|
307
|
+
leftline.append(leftPt)
|
|
308
|
+
rightPt = (x1 + distance2 * math.cos(righrturn), y1 + distance2 * math.sin(righrturn))
|
|
309
|
+
rightline.append(rightPt)
|
|
310
|
+
|
|
311
|
+
x0, y0 = pts[-2]
|
|
312
|
+
x1, y1 = pts[-1]
|
|
313
|
+
|
|
314
|
+
vector = (x1 - x0, y1 - y0)
|
|
315
|
+
orientation1 = getAngle((1, 0), vector)
|
|
316
|
+
leftturn = orientation1 + math.pi / 2
|
|
317
|
+
righrturn = orientation1 - math.pi / 2
|
|
318
|
+
leftPt = (x1 + distance * math.cos(leftturn), y1 + distance * math.sin(leftturn))
|
|
319
|
+
leftline.append(leftPt)
|
|
320
|
+
rightPt = (x1 + distance * math.cos(righrturn), y1 + distance * math.sin(righrturn))
|
|
321
|
+
rightline.append(rightPt)
|
|
322
|
+
return leftline, rightline
|
|
323
|
+
|
|
324
|
+
distance = self._pedb.edb_value(distance).ToDouble()
|
|
325
|
+
gap = self._pedb.edb_value(gap).ToDouble()
|
|
326
|
+
center_line = self.get_center_line()
|
|
327
|
+
leftline, rightline = getParalletLines(center_line, distance)
|
|
328
|
+
for x, y in getLocations(rightline, gap) + getLocations(leftline, gap):
|
|
329
|
+
self._pedb.padstacks.place([x, y], padstack_name, net_name=net_name)
|
|
330
|
+
|
|
331
|
+
@property
|
|
332
|
+
def center_line(self):
|
|
333
|
+
""":class:`PolygonData <ansys.edb.geometry.PolygonData>`: Center line for this Path."""
|
|
334
|
+
edb_center_line = self._edb_object.GetCenterLine()
|
|
335
|
+
return [[pt.X.ToDouble(), pt.Y.ToDouble()] for pt in list(edb_center_line.Points)]
|
|
336
|
+
|
|
337
|
+
@center_line.setter
|
|
338
|
+
def center_line(self, value):
|
|
339
|
+
if isinstance(value, list):
|
|
340
|
+
points = [self._pedb.point_data(i[0], i[1]) for i in value]
|
|
341
|
+
polygon_data = self._edb.geometry.polygon_data.dotnetobj(convert_py_list_to_net_list(points), False)
|
|
342
|
+
self._edb_object.SetCenterLine(polygon_data)
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def corner_style(self):
|
|
346
|
+
""":class:`PathCornerType`: Path's corner style."""
|
|
347
|
+
return self._edb_object.GetCornerStyle()
|
|
348
|
+
|
|
349
|
+
@corner_style.setter
|
|
350
|
+
def corner_style(self, corner_type):
|
|
351
|
+
self._edb_object.SetCornerStyle(corner_type)
|