pyedb 0.56.0__py3-none-any.whl → 0.58.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/configuration/cfg_data.py +3 -0
- pyedb/configuration/cfg_pin_groups.py +2 -0
- pyedb/configuration/cfg_terminals.py +232 -0
- pyedb/configuration/configuration.py +146 -3
- pyedb/dotnet/clr_module.py +1 -2
- pyedb/dotnet/database/Variables.py +30 -22
- pyedb/dotnet/database/cell/hierarchy/component.py +2 -8
- pyedb/dotnet/database/cell/layout.py +5 -1
- pyedb/dotnet/database/cell/primitive/primitive.py +2 -2
- pyedb/dotnet/database/cell/terminal/bundle_terminal.py +12 -0
- pyedb/dotnet/database/cell/terminal/pingroup_terminal.py +1 -1
- pyedb/dotnet/database/cell/terminal/terminal.py +38 -0
- pyedb/dotnet/database/components.py +15 -19
- pyedb/dotnet/database/dotnet/database.py +1 -0
- pyedb/dotnet/database/edb_data/control_file.py +19 -8
- pyedb/dotnet/database/edb_data/nets_data.py +3 -3
- pyedb/dotnet/database/edb_data/padstacks_data.py +39 -14
- pyedb/dotnet/database/edb_data/ports.py +0 -25
- pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
- pyedb/dotnet/database/edb_data/raptor_x_simulation_setup_data.py +18 -19
- pyedb/dotnet/database/edb_data/simulation_configuration.py +3 -3
- pyedb/dotnet/database/edb_data/sources.py +21 -2
- pyedb/dotnet/database/general.py +1 -6
- pyedb/dotnet/database/hfss.py +9 -8
- pyedb/dotnet/database/layout_validation.py +14 -3
- pyedb/dotnet/database/materials.py +1 -3
- pyedb/dotnet/database/modeler.py +7 -3
- pyedb/dotnet/database/nets.py +27 -19
- pyedb/dotnet/database/padstack.py +4 -2
- pyedb/dotnet/database/sim_setup_data/io/siwave.py +54 -1
- pyedb/dotnet/database/siwave.py +4 -3
- pyedb/dotnet/database/stackup.py +55 -58
- pyedb/dotnet/database/utilities/heatsink.py +0 -1
- pyedb/dotnet/database/utilities/hfss_simulation_setup.py +81 -0
- pyedb/dotnet/database/utilities/simulation_setup.py +7 -5
- pyedb/dotnet/database/utilities/siwave_cpa_simulation_setup.py +1 -0
- pyedb/dotnet/database/utilities/siwave_simulation_setup.py +264 -13
- pyedb/dotnet/edb.py +65 -47
- pyedb/exceptions.py +1 -2
- pyedb/extensions/create_cell_array.py +67 -49
- pyedb/generic/data_handlers.py +13 -23
- pyedb/generic/design_types.py +9 -35
- pyedb/generic/filesystem.py +4 -2
- pyedb/generic/general_methods.py +28 -41
- pyedb/generic/plot.py +8 -23
- pyedb/generic/process.py +78 -10
- pyedb/grpc/database/_typing.py +0 -0
- pyedb/grpc/database/components.py +14 -13
- pyedb/grpc/database/control_file.py +27 -40
- pyedb/grpc/database/definition/materials.py +1 -1
- pyedb/grpc/database/definition/package_def.py +6 -3
- pyedb/grpc/database/definition/padstack_def.py +14 -12
- pyedb/grpc/database/hfss.py +1 -4
- pyedb/grpc/database/hierarchy/component.py +5 -13
- pyedb/grpc/database/hierarchy/pingroup.py +16 -3
- pyedb/grpc/database/layers/layer.py +1 -2
- pyedb/grpc/database/layers/stackup_layer.py +42 -19
- pyedb/grpc/database/layout/layout.py +43 -27
- pyedb/grpc/database/layout/voltage_regulator.py +6 -1
- pyedb/grpc/database/layout_validation.py +5 -2
- pyedb/grpc/database/modeler.py +254 -252
- pyedb/grpc/database/net/differential_pair.py +9 -2
- pyedb/grpc/database/net/extended_net.py +24 -9
- pyedb/grpc/database/net/net.py +14 -5
- pyedb/grpc/database/net/net_class.py +24 -7
- pyedb/grpc/database/nets.py +11 -43
- pyedb/grpc/database/padstacks.py +67 -119
- pyedb/grpc/database/primitive/bondwire.py +3 -67
- pyedb/grpc/database/primitive/circle.py +42 -3
- pyedb/grpc/database/primitive/padstack_instance.py +58 -31
- pyedb/grpc/database/primitive/path.py +160 -11
- pyedb/grpc/database/primitive/polygon.py +73 -7
- pyedb/grpc/database/primitive/primitive.py +2 -2
- pyedb/grpc/database/primitive/rectangle.py +105 -4
- pyedb/grpc/database/simulation_setup/hfss_general_settings.py +0 -2
- pyedb/grpc/database/simulation_setup/hfss_settings_options.py +0 -4
- pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +79 -0
- pyedb/grpc/database/simulation_setup/siwave_cpa_simulation_setup.py +1 -0
- pyedb/grpc/database/simulation_setup/sweep_data.py +1 -3
- pyedb/grpc/database/siwave.py +6 -13
- pyedb/grpc/database/source_excitations.py +46 -63
- pyedb/grpc/database/stackup.py +55 -60
- pyedb/grpc/database/terminal/bundle_terminal.py +10 -3
- pyedb/grpc/database/terminal/padstack_instance_terminal.py +9 -11
- pyedb/grpc/database/terminal/pingroup_terminal.py +8 -1
- pyedb/grpc/database/terminal/point_terminal.py +30 -0
- pyedb/grpc/database/terminal/terminal.py +35 -10
- pyedb/grpc/database/utility/heat_sink.py +0 -1
- pyedb/grpc/database/utility/hfss_extent_info.py +2 -2
- pyedb/grpc/database/utility/xml_control_file.py +19 -8
- pyedb/grpc/edb.py +63 -32
- pyedb/grpc/edb_init.py +1 -0
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +6 -2
- pyedb/ipc2581/ecad/cad_data/step.py +1 -1
- pyedb/ipc2581/ipc2581.py +8 -7
- pyedb/libraries/common.py +3 -4
- pyedb/libraries/rf_libraries/base_functions.py +7 -16
- pyedb/libraries/rf_libraries/planar_antennas.py +3 -21
- pyedb/misc/aedtlib_personalib_install.py +2 -2
- pyedb/misc/downloads.py +19 -3
- pyedb/misc/misc.py +5 -2
- pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +3 -2
- pyedb/misc/siw_feature_config/xtalk_scan/scan_config.py +0 -1
- pyedb/misc/utilities.py +0 -1
- pyedb/modeler/geometry_operators.py +3 -2
- {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/METADATA +6 -7
- {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/RECORD +110 -108
- {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/WHEEL +0 -0
- {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -23,21 +23,25 @@
|
|
|
23
23
|
"""
|
|
24
24
|
This module contains the array building feature from unit cell.
|
|
25
25
|
"""
|
|
26
|
+
|
|
26
27
|
import itertools
|
|
27
28
|
from typing import Optional, Union
|
|
29
|
+
import warnings
|
|
28
30
|
|
|
29
31
|
from pyedb import Edb
|
|
32
|
+
from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
|
|
33
|
+
from pyedb.misc.decorators import execution_timer
|
|
30
34
|
|
|
31
35
|
|
|
32
36
|
# ----------------------
|
|
33
|
-
# Public
|
|
37
|
+
# Public api
|
|
34
38
|
# ----------------------
|
|
35
39
|
def create_array_from_unit_cell(
|
|
36
40
|
edb: Edb,
|
|
37
41
|
x_number: int = 2,
|
|
38
42
|
y_number: int = 2,
|
|
39
|
-
offset_x: Optional[Union[int, float]] = None,
|
|
40
|
-
offset_y: Optional[Union[int, float]] = None,
|
|
43
|
+
offset_x: Optional[Union[int, float, str]] = None,
|
|
44
|
+
offset_y: Optional[Union[int, float, str]] = None,
|
|
41
45
|
) -> bool:
|
|
42
46
|
"""
|
|
43
47
|
Create a 2-D rectangular array from the current EDB unit cell.
|
|
@@ -56,10 +60,10 @@ def create_array_from_unit_cell(
|
|
|
56
60
|
Number of columns (X-direction). Must be > 0. Defaults to 2.
|
|
57
61
|
y_number : int, optional
|
|
58
62
|
Number of rows (Y-direction). Must be > 0. Defaults to 2.
|
|
59
|
-
offset_x : int | float | None, optional
|
|
63
|
+
offset_x : int | float | str, None, optional
|
|
60
64
|
Horizontal pitch (distance between cell origins). When *None* the
|
|
61
65
|
value is derived from the outline geometry.
|
|
62
|
-
offset_y : int | float | None, optional
|
|
66
|
+
offset_y : int | float | str, None, optional
|
|
63
67
|
Vertical pitch (distance between cell origins). When *None* the
|
|
64
68
|
value is derived from the outline geometry.
|
|
65
69
|
|
|
@@ -93,12 +97,15 @@ def create_array_from_unit_cell(
|
|
|
93
97
|
adapter = _GrpcAdapter(edb)
|
|
94
98
|
else:
|
|
95
99
|
adapter = _DotNetAdapter(edb)
|
|
100
|
+
warnings.warn(".NET back-end is deprecated and will be removed in future releases.", UserWarning)
|
|
101
|
+
warnings.warn("Consider moving to PyEDB gRPC (ANSYS 2025R2) for better performances", UserWarning)
|
|
96
102
|
return __create_array_from_unit_cell_impl(edb, adapter, x_number, y_number, offset_x, offset_y)
|
|
97
103
|
|
|
98
104
|
|
|
99
105
|
# ------------------------------------------------------------------
|
|
100
106
|
# Implementation (technology-agnostic)
|
|
101
107
|
# ------------------------------------------------------------------
|
|
108
|
+
@execution_timer("create_array_from_unit_cell")
|
|
102
109
|
def __create_array_from_unit_cell_impl(
|
|
103
110
|
edb: Edb,
|
|
104
111
|
adapter: "_BaseAdapter",
|
|
@@ -133,8 +140,12 @@ def __create_array_from_unit_cell_impl(
|
|
|
133
140
|
# ---------- Sanity & auto-pitch detection ----------
|
|
134
141
|
if x_number <= 0 or y_number <= 0:
|
|
135
142
|
raise ValueError("x_number and y_number must be positive integers")
|
|
143
|
+
if offset_x and not offset_y:
|
|
144
|
+
raise ValueError("If offset_x is provided, offset_y must be provided as well")
|
|
145
|
+
if offset_y and not offset_x:
|
|
146
|
+
raise ValueError("If offset_y is provided, offset_x must be provided as well")
|
|
136
147
|
|
|
137
|
-
if offset_x
|
|
148
|
+
if not offset_x and not offset_y:
|
|
138
149
|
edb.logger.info("Auto-detecting outline extents")
|
|
139
150
|
outline_prims = [p for p in edb.modeler.primitives if p.layer_name.lower() == "outline"]
|
|
140
151
|
if not outline_prims:
|
|
@@ -143,40 +154,52 @@ def __create_array_from_unit_cell_impl(
|
|
|
143
154
|
if not adapter.is_supported_outline(outline):
|
|
144
155
|
raise RuntimeError("Outline primitive is not a polygon/rectangle. Provide offset_x / offset_y.")
|
|
145
156
|
offset_x, offset_y = adapter.pitch_from_outline(outline)
|
|
157
|
+
offset_x = edb.value(offset_x)
|
|
158
|
+
offset_y = edb.value(offset_y)
|
|
146
159
|
|
|
147
160
|
# ---------- Collect everything we have to replicate ----------
|
|
148
161
|
primitives = [p for p in edb.modeler.primitives if adapter.is_primitive_to_copy(p)]
|
|
149
162
|
paths = list(edb.modeler.paths)
|
|
150
163
|
vias = list(edb.padstacks.vias.values())
|
|
151
164
|
components = list(edb.components.instances.values())
|
|
165
|
+
pingroups = edb.layout.pin_groups
|
|
166
|
+
if edb.grpc:
|
|
167
|
+
pg_dict = {
|
|
168
|
+
pad.edb_uid: pg.name # edb_uid → PinGroup.name
|
|
169
|
+
for pg in pingroups # for every PinGroup
|
|
170
|
+
for pad in pg.pins.values() # for every PadstackInstance in its pin-dict
|
|
171
|
+
}
|
|
172
|
+
else:
|
|
173
|
+
pg_dict = {
|
|
174
|
+
pad.id: pg.name # edb_uid → PinGroup.name
|
|
175
|
+
for pg in pingroups # for every PinGroup
|
|
176
|
+
for pad in pg.pins.values() # for every PadstackInstance in its pin-dict
|
|
177
|
+
}
|
|
152
178
|
|
|
153
179
|
# ---------- Replication loops ----------
|
|
154
180
|
edb.logger.info(f"Starting array replication {x_number}×{y_number}")
|
|
181
|
+
total_number = x_number * y_number - 1 # minus original
|
|
182
|
+
cell_count = 0
|
|
155
183
|
for i, j in itertools.product(range(x_number), range(y_number)):
|
|
156
184
|
if i == 0 and j == 0:
|
|
157
185
|
continue # original already exists
|
|
158
|
-
|
|
159
186
|
dx = edb.value(offset_x * i)
|
|
160
187
|
dy = edb.value(offset_y * j)
|
|
161
|
-
|
|
188
|
+
# Components
|
|
189
|
+
for comp in components:
|
|
190
|
+
adapter.duplicate_component(comp, dx, dy, i, j, pin_groups=pg_dict)
|
|
162
191
|
# Primitives & voids
|
|
163
192
|
for prim in primitives:
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
adapter.duplicate_void(new_poly, void, dx, dy)
|
|
167
|
-
|
|
193
|
+
if not prim.is_void:
|
|
194
|
+
adapter.duplicate_primitive(prim, dx, dy, i, j)
|
|
168
195
|
# Paths
|
|
169
196
|
for path in paths:
|
|
170
197
|
adapter.duplicate_path(path, dx, dy, i, j)
|
|
171
|
-
|
|
172
198
|
# Stand-alone vias
|
|
173
199
|
for via in (v for v in vias if not v.component):
|
|
174
200
|
adapter.duplicate_standalone_via(via, dx, dy, i, j)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
for comp in components:
|
|
178
|
-
adapter.duplicate_component(comp, dx, dy, i, j)
|
|
179
|
-
|
|
201
|
+
cell_count += 1
|
|
202
|
+
edb.logger.info(f"Replicated cell {cell_count} of {total_number} ({(cell_count / total_number) * 100:.1f}%)")
|
|
180
203
|
edb.logger.info("Array replication finished successfully")
|
|
181
204
|
return True
|
|
182
205
|
|
|
@@ -189,6 +212,8 @@ class _BaseAdapter:
|
|
|
189
212
|
|
|
190
213
|
def __init__(self, edb: Edb):
|
|
191
214
|
self.edb = edb
|
|
215
|
+
self.layers = edb.stackup.layers
|
|
216
|
+
self.active_layout = edb.active_layout
|
|
192
217
|
|
|
193
218
|
# ---- Outline helpers ----
|
|
194
219
|
def is_supported_outline(self, outline) -> bool:
|
|
@@ -215,10 +240,6 @@ class _BaseAdapter:
|
|
|
215
240
|
"""Return a new primitive translated by (dx, dy)."""
|
|
216
241
|
raise NotImplementedError
|
|
217
242
|
|
|
218
|
-
def duplicate_void(self, new_poly, void, dx, dy):
|
|
219
|
-
"""Add a translated copy of *void* to *new_poly*."""
|
|
220
|
-
raise NotImplementedError
|
|
221
|
-
|
|
222
243
|
def duplicate_path(self, path, dx, dy, i, j):
|
|
223
244
|
"""Create a translated copy of *path*."""
|
|
224
245
|
raise NotImplementedError
|
|
@@ -227,7 +248,7 @@ class _BaseAdapter:
|
|
|
227
248
|
"""Create a translated copy of a stand-alone via."""
|
|
228
249
|
raise NotImplementedError
|
|
229
250
|
|
|
230
|
-
def duplicate_component(self, comp, dx, dy, i, j):
|
|
251
|
+
def duplicate_component(self, comp, dx, dy, i, j, pin_groups=None):
|
|
231
252
|
"""Create a translated copy of *comp* (including its pins)."""
|
|
232
253
|
raise NotImplementedError
|
|
233
254
|
|
|
@@ -247,15 +268,11 @@ class _GrpcAdapter(_BaseAdapter):
|
|
|
247
268
|
|
|
248
269
|
def duplicate_primitive(self, prim, dx, dy, i, j):
|
|
249
270
|
moved_pd = prim.polygon_data.move((dx, dy))
|
|
271
|
+
voids = [voids.polygon_data.move((dx, dy)) for voids in prim.voids]
|
|
250
272
|
return self.edb.modeler.create_polygon(
|
|
251
|
-
moved_pd,
|
|
252
|
-
layer_name=prim.layer.name,
|
|
253
|
-
net_name=prim.net.name,
|
|
273
|
+
moved_pd, layer_name=prim.layer.name, net_name=prim.net.name, voids=voids
|
|
254
274
|
)
|
|
255
275
|
|
|
256
|
-
def duplicate_void(self, new_poly, void, dx, dy):
|
|
257
|
-
new_poly.add_void(void.polygon_data.move((dx, dy)))
|
|
258
|
-
|
|
259
276
|
def duplicate_path(self, path, dx, dy, i, j):
|
|
260
277
|
moved_line = path.cast().center_line.move((dx, dy))
|
|
261
278
|
self.edb.modeler.create_trace(
|
|
@@ -263,29 +280,30 @@ class _GrpcAdapter(_BaseAdapter):
|
|
|
263
280
|
width=path.width,
|
|
264
281
|
layer_name=path.layer.name,
|
|
265
282
|
net_name=path.net.name,
|
|
283
|
+
corner_style=path.corner_style,
|
|
284
|
+
start_cap_style=path.end_cap1,
|
|
285
|
+
end_cap_style=path.end_cap2,
|
|
266
286
|
)
|
|
267
287
|
|
|
268
288
|
def duplicate_standalone_via(self, via, dx, dy, i, j):
|
|
269
|
-
from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
|
|
270
|
-
|
|
271
289
|
pos = via.position
|
|
272
290
|
PadstackInstance.create(
|
|
273
|
-
self.
|
|
291
|
+
self.active_layout,
|
|
274
292
|
net=via.net,
|
|
275
|
-
name=f"{via.name}
|
|
276
|
-
padstack_def=
|
|
293
|
+
name=f"{via.name}_X{i}_Y{j}",
|
|
294
|
+
padstack_def=via.definition,
|
|
277
295
|
position_x=pos[0] + dx,
|
|
278
296
|
position_y=pos[1] + dy,
|
|
279
297
|
rotation=0.0,
|
|
280
|
-
top_layer=self.
|
|
281
|
-
bottom_layer=self.
|
|
298
|
+
top_layer=self.layers[via.start_layer],
|
|
299
|
+
bottom_layer=self.layers[via.stop_layer],
|
|
282
300
|
)
|
|
283
301
|
|
|
284
|
-
def duplicate_component(self, comp, dx, dy, i, j):
|
|
285
|
-
from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
|
|
286
|
-
|
|
302
|
+
def duplicate_component(self, comp, dx, dy, i, j, pin_groups=None):
|
|
287
303
|
new_pins = []
|
|
304
|
+
_pg = {}
|
|
288
305
|
for pin in comp.pins.values():
|
|
306
|
+
pg_name = pin_groups.get(pin.edb_uid, None) if pin_groups else None
|
|
289
307
|
pos = pin.position
|
|
290
308
|
new_pin = PadstackInstance.create(
|
|
291
309
|
self.edb.active_layout,
|
|
@@ -299,6 +317,8 @@ class _GrpcAdapter(_BaseAdapter):
|
|
|
299
317
|
bottom_layer=self.edb.stackup.layers[pin.stop_layer],
|
|
300
318
|
)
|
|
301
319
|
new_pins.append(new_pin)
|
|
320
|
+
if pg_name:
|
|
321
|
+
_pg.setdefault(pg_name, []).append(new_pin)
|
|
302
322
|
|
|
303
323
|
if new_pins:
|
|
304
324
|
res = self.edb.value(comp.res_value) if hasattr(comp, "res_value") and comp.res_value else None
|
|
@@ -313,8 +333,11 @@ class _GrpcAdapter(_BaseAdapter):
|
|
|
313
333
|
l_value=ind,
|
|
314
334
|
c_value=cap,
|
|
315
335
|
)
|
|
336
|
+
new_comp.type = comp.type
|
|
316
337
|
if hasattr(comp, "component_property") and comp.component_property:
|
|
317
338
|
new_comp.component_property = comp.component_property
|
|
339
|
+
for pg_name, pins in _pg.items():
|
|
340
|
+
self.edb.components.create_pingroup_from_pins(pins=pins, group_name=f"{pg_name}_{i}_{j}")
|
|
318
341
|
|
|
319
342
|
|
|
320
343
|
class _DotNetAdapter(_BaseAdapter):
|
|
@@ -342,14 +365,6 @@ class _DotNetAdapter(_BaseAdapter):
|
|
|
342
365
|
net_name=prim.net.name,
|
|
343
366
|
)
|
|
344
367
|
|
|
345
|
-
def duplicate_void(self, new_poly, void, dx, dy):
|
|
346
|
-
from pyedb.dotnet.database.geometry.point_data import PointData
|
|
347
|
-
|
|
348
|
-
vector = PointData.create_from_xy(self.edb, x=dx, y=dy)
|
|
349
|
-
void_polygon_data = void.polygon_data
|
|
350
|
-
void_polygon_data._edb_object.Move(vector._edb_object)
|
|
351
|
-
new_poly.add_void(void_polygon_data.points)
|
|
352
|
-
|
|
353
368
|
def duplicate_path(self, path, dx, dy, i, j):
|
|
354
369
|
from pyedb.dotnet.database.geometry.point_data import PointData
|
|
355
370
|
|
|
@@ -357,11 +372,15 @@ class _DotNetAdapter(_BaseAdapter):
|
|
|
357
372
|
moved_path = path._edb_object.GetCenterLine()
|
|
358
373
|
moved_path.Move(vector._edb_object)
|
|
359
374
|
moved_path = [[pt.X.ToDouble(), pt.Y.ToDouble()] for pt in list(moved_path.Points)]
|
|
375
|
+
end_caps = path._edb_object.GetEndCapStyle()
|
|
376
|
+
|
|
360
377
|
self.edb.modeler.create_trace(
|
|
361
378
|
path_list=list(moved_path),
|
|
362
379
|
width=path.width,
|
|
363
380
|
layer_name=path.layer.name,
|
|
364
381
|
net_name=path.net.name,
|
|
382
|
+
start_cap_style=str(end_caps[1]),
|
|
383
|
+
end_cap_style=str(end_caps[2]),
|
|
365
384
|
)
|
|
366
385
|
|
|
367
386
|
def duplicate_standalone_via(self, via, dx, dy, i, j):
|
|
@@ -372,7 +391,7 @@ class _DotNetAdapter(_BaseAdapter):
|
|
|
372
391
|
via_name=f"{via.aedt_name}_i{i}_j{j}",
|
|
373
392
|
)
|
|
374
393
|
|
|
375
|
-
def duplicate_component(self, comp, dx, dy, i, j):
|
|
394
|
+
def duplicate_component(self, comp, dx, dy, i, j, pin_groups=None):
|
|
376
395
|
new_pins = []
|
|
377
396
|
for pin in comp.pins.values():
|
|
378
397
|
pos = pin.position
|
|
@@ -382,7 +401,6 @@ class _DotNetAdapter(_BaseAdapter):
|
|
|
382
401
|
via_name=f"{pin.aedt_name}_i{i}_j{j}",
|
|
383
402
|
)
|
|
384
403
|
new_pins.append(new_pin)
|
|
385
|
-
|
|
386
404
|
if new_pins:
|
|
387
405
|
new_comp = self.edb.components.create(
|
|
388
406
|
pins=new_pins,
|
pyedb/generic/data_handlers.py
CHANGED
|
@@ -72,23 +72,13 @@ def unique_string_list(element_list, only_string=True): # pragma: no cover
|
|
|
72
72
|
-------
|
|
73
73
|
|
|
74
74
|
"""
|
|
75
|
-
if element_list:
|
|
76
|
-
|
|
77
|
-
element_list = set(element_list)
|
|
78
|
-
elif isinstance(element_list, str):
|
|
79
|
-
element_list = [element_list]
|
|
80
|
-
else:
|
|
81
|
-
error_message = "Invalid list data"
|
|
82
|
-
try:
|
|
83
|
-
error_message += " {}".format(element_list)
|
|
84
|
-
except:
|
|
85
|
-
pass
|
|
86
|
-
raise Exception(error_message)
|
|
75
|
+
if isinstance(element_list, str):
|
|
76
|
+
element_list = [element_list]
|
|
87
77
|
|
|
88
|
-
|
|
89
|
-
|
|
78
|
+
if only_string and any(not isinstance(x, str) for x in element_list):
|
|
79
|
+
raise TypeError("Invalid list entries, some elements are not of type string.")
|
|
90
80
|
|
|
91
|
-
return element_list
|
|
81
|
+
return list(set(element_list))
|
|
92
82
|
|
|
93
83
|
|
|
94
84
|
def string_list(element_list): # pragma: no cover
|
|
@@ -142,28 +132,28 @@ def from_rkm(code): # pragma: no cover
|
|
|
142
132
|
|
|
143
133
|
Examples
|
|
144
134
|
--------
|
|
145
|
-
>>> from_rkm(
|
|
135
|
+
>>> from_rkm("R47")
|
|
146
136
|
'0.47'
|
|
147
137
|
|
|
148
|
-
>>> from_rkm(
|
|
138
|
+
>>> from_rkm("4R7")
|
|
149
139
|
'4.7'
|
|
150
140
|
|
|
151
|
-
>>> from_rkm(
|
|
141
|
+
>>> from_rkm("470R")
|
|
152
142
|
'470'
|
|
153
143
|
|
|
154
|
-
>>> from_rkm(
|
|
144
|
+
>>> from_rkm("4K7")
|
|
155
145
|
'4.7k'
|
|
156
146
|
|
|
157
|
-
>>> from_rkm(
|
|
147
|
+
>>> from_rkm("47K")
|
|
158
148
|
'47k'
|
|
159
149
|
|
|
160
|
-
>>> from_rkm(
|
|
150
|
+
>>> from_rkm("47K3")
|
|
161
151
|
'47.3k'
|
|
162
152
|
|
|
163
|
-
>>> from_rkm(
|
|
153
|
+
>>> from_rkm("470K")
|
|
164
154
|
'470k'
|
|
165
155
|
|
|
166
|
-
>>> from_rkm(
|
|
156
|
+
>>> from_rkm("4M7")
|
|
167
157
|
'4.7M'
|
|
168
158
|
|
|
169
159
|
"""
|
pyedb/generic/design_types.py
CHANGED
|
@@ -32,18 +32,15 @@ if TYPE_CHECKING:
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@overload
|
|
35
|
-
def Edb(*, grpc: Literal[True], **kwargs) -> "EdbGrpc":
|
|
36
|
-
...
|
|
35
|
+
def Edb(*, grpc: Literal[True], **kwargs) -> "EdbGrpc": ...
|
|
37
36
|
|
|
38
37
|
|
|
39
38
|
@overload
|
|
40
|
-
def Edb(*, grpc: Literal[False] = False, **kwargs) -> "EdbDotnet":
|
|
41
|
-
...
|
|
39
|
+
def Edb(*, grpc: Literal[False] = False, **kwargs) -> "EdbDotnet": ...
|
|
42
40
|
|
|
43
41
|
|
|
44
42
|
@overload
|
|
45
|
-
def Edb(*, grpc: bool, **kwargs) -> Union["EdbGrpc", "EdbDotnet"]:
|
|
46
|
-
...
|
|
43
|
+
def Edb(*, grpc: bool, **kwargs) -> Union["EdbGrpc", "EdbDotnet"]: ...
|
|
47
44
|
|
|
48
45
|
|
|
49
46
|
# lazy imports
|
|
@@ -167,17 +164,10 @@ def Edb(
|
|
|
167
164
|
4. Simulation Setup
|
|
168
165
|
|
|
169
166
|
# Create SIwave SYZ setup
|
|
170
|
-
>>> syz_setup = edb.create_siwave_syz_setup(
|
|
171
|
-
>>> name="GHz_Setup",
|
|
172
|
-
>>> start_freq="1GHz",
|
|
173
|
-
>>> stop_freq="10GHz"
|
|
174
|
-
>>> )
|
|
167
|
+
>>> syz_setup = edb.create_siwave_syz_setup(name="GHz_Setup", start_freq="1GHz", stop_freq="10GHz")
|
|
175
168
|
|
|
176
169
|
# Create SIwave DC setup
|
|
177
|
-
>>> dc_setup = edb.create_siwave_dc_setup(
|
|
178
|
-
>>> name="DC_Analysis",
|
|
179
|
-
>>> use_dc_point=True
|
|
180
|
-
>>> )
|
|
170
|
+
>>> dc_setup = edb.create_siwave_dc_setup(name="DC_Analysis", use_dc_point=True)
|
|
181
171
|
|
|
182
172
|
# Solve with SIwave
|
|
183
173
|
>>> edb.solve_siwave()
|
|
@@ -208,17 +198,10 @@ def Edb(
|
|
|
208
198
|
7. Port Creation
|
|
209
199
|
|
|
210
200
|
# Create wave port between two pins
|
|
211
|
-
>>> wave_port = edb.source_excitation.create_port(
|
|
212
|
-
>>> positive_terminal=pin1,
|
|
213
|
-
>>> negative_terminal=pin2,
|
|
214
|
-
>>> port_type="Wave"
|
|
215
|
-
>>> )
|
|
201
|
+
>>> wave_port = edb.source_excitation.create_port(positive_terminal=pin1, negative_terminal=pin2, port_type="Wave")
|
|
216
202
|
|
|
217
203
|
# Create lumped port
|
|
218
|
-
>>> lumped_port = edb.source_excitation.create_port(
|
|
219
|
-
>>> positive_terminal=via_terminal,
|
|
220
|
-
>>> port_type="Lumped"
|
|
221
|
-
>>> )
|
|
204
|
+
>>> lumped_port = edb.source_excitation.create_port(positive_terminal=via_terminal, port_type="Lumped")
|
|
222
205
|
|
|
223
206
|
8. Component Management
|
|
224
207
|
|
|
@@ -231,12 +214,7 @@ def Edb(
|
|
|
231
214
|
9. Parametrization
|
|
232
215
|
|
|
233
216
|
# Auto-parametrize design elements
|
|
234
|
-
>>> params = edb.auto_parametrize_design(
|
|
235
|
-
>>> traces=True,
|
|
236
|
-
>>> pads=True,
|
|
237
|
-
>>> antipads=True,
|
|
238
|
-
>>> use_relative_variables=True
|
|
239
|
-
>>> )
|
|
217
|
+
>>> params = edb.auto_parametrize_design(traces=True, pads=True, antipads=True, use_relative_variables=True)
|
|
240
218
|
>>> print("Created parameters:", params)
|
|
241
219
|
|
|
242
220
|
10. Design Statistics
|
|
@@ -255,11 +233,7 @@ def Edb(
|
|
|
255
233
|
12. Differential Pairs
|
|
256
234
|
|
|
257
235
|
# Create differential pair
|
|
258
|
-
>>> edb.differential_pairs.create(
|
|
259
|
-
>>> positive_net="USB_P",
|
|
260
|
-
>>> negative_net="USB_N",
|
|
261
|
-
>>> name="USB_DP"
|
|
262
|
-
>>> )
|
|
236
|
+
>>> edb.differential_pairs.create(positive_net="USB_P", negative_net="USB_N", name="USB_DP")
|
|
263
237
|
|
|
264
238
|
13. Workflow Automation
|
|
265
239
|
|
pyedb/generic/filesystem.py
CHANGED
|
@@ -3,6 +3,8 @@ import secrets
|
|
|
3
3
|
import shutil
|
|
4
4
|
import string
|
|
5
5
|
|
|
6
|
+
from pyedb.generic.settings import settings
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
def search_files(dirname, pattern="*"):
|
|
8
10
|
"""Search for files inside a directory given a specific pattern.
|
|
@@ -65,8 +67,8 @@ class Scratch:
|
|
|
65
67
|
try:
|
|
66
68
|
# TODO check why on Anaconda 3.7 get errors with os.path.exists
|
|
67
69
|
shutil.rmtree(self._scratch_path, ignore_errors=True)
|
|
68
|
-
except:
|
|
69
|
-
|
|
70
|
+
except Exception:
|
|
71
|
+
settings.logger.error(f"An error occurred while removing {self._scratch_path}")
|
|
70
72
|
|
|
71
73
|
def copyfile(self, src_file, dst_filename=None):
|
|
72
74
|
"""
|
pyedb/generic/general_methods.py
CHANGED
|
@@ -50,13 +50,9 @@ is_linux = os.name == "posix"
|
|
|
50
50
|
is_windows = not is_linux
|
|
51
51
|
_pythonver = sys.version_info[0]
|
|
52
52
|
|
|
53
|
+
import xml.etree.cElementTree as ET # nosec B405
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
import xml.etree.cElementTree as ET
|
|
56
|
-
|
|
57
|
-
ET.VERSION
|
|
58
|
-
except ImportError:
|
|
59
|
-
ET = None
|
|
55
|
+
ET.VERSION
|
|
60
56
|
|
|
61
57
|
|
|
62
58
|
class GrpcApiError(Exception):
|
|
@@ -118,7 +114,6 @@ def _exception(ex_info, func, args, kwargs, message="Type Error"):
|
|
|
118
114
|
if message_to_print:
|
|
119
115
|
_write_mes("Last Electronics Desktop Message - " + message_to_print)
|
|
120
116
|
|
|
121
|
-
args_name = []
|
|
122
117
|
try:
|
|
123
118
|
args_dict = _get_args_dicts(func, args, kwargs)
|
|
124
119
|
first_time_log = True
|
|
@@ -129,13 +124,13 @@ def _exception(ex_info, func, args, kwargs, message="Type Error"):
|
|
|
129
124
|
_write_mes("Method arguments: ")
|
|
130
125
|
first_time_log = False
|
|
131
126
|
_write_mes(" {} = {} ".format(el, args_dict[el]))
|
|
132
|
-
except:
|
|
133
|
-
|
|
134
|
-
|
|
127
|
+
except Exception:
|
|
128
|
+
settings.logger.error(f"An error occurred while parsing and logging an error with method {func.__name__}.")
|
|
129
|
+
|
|
135
130
|
if not func.__name__.startswith("_"):
|
|
136
131
|
_write_mes(
|
|
137
132
|
"Check Online documentation on: https://edb.docs.pyansys.com/version/stable/search.html?q={}".format(
|
|
138
|
-
|
|
133
|
+
func.__name__
|
|
139
134
|
)
|
|
140
135
|
)
|
|
141
136
|
|
|
@@ -695,13 +690,9 @@ def read_csv_pandas(filename, encoding="utf-8"): # pragma: no cover
|
|
|
695
690
|
:class:`pandas.DataFrame`
|
|
696
691
|
|
|
697
692
|
"""
|
|
698
|
-
|
|
699
|
-
import pandas as pd
|
|
693
|
+
import pandas as pd
|
|
700
694
|
|
|
701
|
-
|
|
702
|
-
except ImportError:
|
|
703
|
-
logging.error("Pandas is not available. Install it.")
|
|
704
|
-
return None
|
|
695
|
+
return pd.read_csv(filename, encoding=encoding, header=0, na_values=".")
|
|
705
696
|
|
|
706
697
|
|
|
707
698
|
def read_tab(filename): # pragma: no cover
|
|
@@ -735,14 +726,10 @@ def read_xlsx(filename): # pragma: no cover
|
|
|
735
726
|
list
|
|
736
727
|
|
|
737
728
|
"""
|
|
738
|
-
|
|
739
|
-
import pandas as pd
|
|
729
|
+
import pandas as pd
|
|
740
730
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
except ImportError:
|
|
744
|
-
lines = []
|
|
745
|
-
return lines
|
|
731
|
+
lines = pd.read_excel(filename)
|
|
732
|
+
return lines
|
|
746
733
|
|
|
747
734
|
|
|
748
735
|
def write_csv(output, list_data, delimiter=",", quotechar="|", quoting=csv.QUOTE_MINIMAL): # pragma: no cover
|
|
@@ -872,11 +859,7 @@ def compute_fft(time_vals, value): # pragma: no cover
|
|
|
872
859
|
tuple
|
|
873
860
|
Frequency and Values.
|
|
874
861
|
"""
|
|
875
|
-
|
|
876
|
-
import numpy as np
|
|
877
|
-
except ImportError:
|
|
878
|
-
logging.error("NumPy is not available. Install it.")
|
|
879
|
-
return False
|
|
862
|
+
import numpy as np
|
|
880
863
|
|
|
881
864
|
deltaT = time_vals[-1] - time_vals[0]
|
|
882
865
|
num_points = len(time_vals)
|
|
@@ -925,11 +908,8 @@ def parse_excitation_file(
|
|
|
925
908
|
tuple
|
|
926
909
|
Frequency, magnitude and phase.
|
|
927
910
|
"""
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
except ImportError:
|
|
931
|
-
logging.error("NumPy is not available. Install it.")
|
|
932
|
-
return False
|
|
911
|
+
import numpy as np
|
|
912
|
+
|
|
933
913
|
df = read_csv_pandas(file_name, encoding=encoding)
|
|
934
914
|
if is_time_domain:
|
|
935
915
|
time = df[df.keys()[0]].values * x_scale
|
|
@@ -1166,6 +1146,11 @@ def install_with_pip(package_name, package_path=None, upgrade=False, uninstall=F
|
|
|
1166
1146
|
"""Install a new package using pip.
|
|
1167
1147
|
This method is useful for installing a package from the AEDT Console without launching the Python environment.
|
|
1168
1148
|
|
|
1149
|
+
.. warning::
|
|
1150
|
+
Do not execute this function with untrusted function argument, environment
|
|
1151
|
+
variables or pyedb global settings.
|
|
1152
|
+
See the :ref:`security guide<ref_security_consideration>` for details.
|
|
1153
|
+
|
|
1169
1154
|
Parameters
|
|
1170
1155
|
----------
|
|
1171
1156
|
package_name : str
|
|
@@ -1178,10 +1163,12 @@ def install_with_pip(package_name, package_path=None, upgrade=False, uninstall=F
|
|
|
1178
1163
|
Whether to install the package or uninstall the package.
|
|
1179
1164
|
"""
|
|
1180
1165
|
|
|
1181
|
-
import subprocess
|
|
1166
|
+
import subprocess # nosec B404
|
|
1182
1167
|
|
|
1183
|
-
|
|
1168
|
+
if not package_name or not isinstance(package_name, str):
|
|
1169
|
+
raise ValueError("A valid package name must be provided.")
|
|
1184
1170
|
|
|
1171
|
+
executable = sys.executable
|
|
1185
1172
|
commands = []
|
|
1186
1173
|
if uninstall:
|
|
1187
1174
|
commands.append([executable, "-m", "pip", "uninstall", "--yes", package_name])
|
|
@@ -1195,12 +1182,12 @@ def install_with_pip(package_name, package_path=None, upgrade=False, uninstall=F
|
|
|
1195
1182
|
command.append("-U")
|
|
1196
1183
|
|
|
1197
1184
|
commands.append(command)
|
|
1185
|
+
|
|
1198
1186
|
for command in commands:
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
p.wait()
|
|
1187
|
+
try:
|
|
1188
|
+
subprocess.run(command, check=True) # nosec
|
|
1189
|
+
except subprocess.CalledProcessError as e: # nosec
|
|
1190
|
+
raise RuntimeError("An error occurred while installing with pip") from e
|
|
1204
1191
|
|
|
1205
1192
|
|
|
1206
1193
|
class Help: # pragma: no cover
|
pyedb/generic/plot.py
CHANGED
|
@@ -1,31 +1,16 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import warnings
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
warnings.warn(
|
|
8
|
-
"The NumPy module is required to run some functionalities of PostProcess.\n"
|
|
9
|
-
"Install with \n\npip install numpy\n\nRequires CPython."
|
|
10
|
-
)
|
|
11
|
-
try:
|
|
12
|
-
from matplotlib.patches import PathPatch
|
|
13
|
-
from matplotlib.path import Path
|
|
3
|
+
from matplotlib.patches import PathPatch
|
|
4
|
+
from matplotlib.path import Path
|
|
5
|
+
import numpy # noqa: F401
|
|
14
6
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
# Use matplotlib agg backend (non-interactive) when the CI is running.
|
|
8
|
+
if bool(int(os.getenv("PYEDB_CI_NO_DISPLAY", "0"))): # pragma: no cover
|
|
9
|
+
import matplotlib
|
|
18
10
|
|
|
19
|
-
|
|
20
|
-
import matplotlib.pyplot as plt
|
|
11
|
+
matplotlib.use("Agg")
|
|
21
12
|
|
|
22
|
-
|
|
23
|
-
warnings.warn(
|
|
24
|
-
"The Matplotlib module is required to run some functionalities of PostProcess.\n"
|
|
25
|
-
"Install with \n\npip install matplotlib\n\nRequires CPython."
|
|
26
|
-
)
|
|
27
|
-
except:
|
|
28
|
-
pass
|
|
13
|
+
import matplotlib.pyplot as plt
|
|
29
14
|
|
|
30
15
|
|
|
31
16
|
def plot_matplotlib(
|