pyedb 0.21.1__py3-none-any.whl → 0.22.1__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/component_libraries/ansys_components.py +85 -0
- pyedb/dotnet/edb.py +28 -3
- pyedb/dotnet/edb_core/components.py +82 -14
- pyedb/dotnet/edb_core/edb_data/layer_data.py +3 -2
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +4 -4
- pyedb/dotnet/edb_core/edb_data/simulation_configuration.py +1 -2
- pyedb/dotnet/edb_core/materials.py +6 -4
- pyedb/dotnet/edb_core/padstack.py +9 -1
- pyedb/dotnet/edb_core/stackup.py +2 -1
- pyedb/modeler/geometry_operators.py +29 -3
- {pyedb-0.21.1.dist-info → pyedb-0.22.1.dist-info}/METADATA +3 -1
- {pyedb-0.21.1.dist-info → pyedb-0.22.1.dist-info}/RECORD +15 -15
- pyedb/dotnet/edb_core/edb_data/hfss_pi_simulation_setup_data.py +0 -0
- {pyedb-0.21.1.dist-info → pyedb-0.22.1.dist-info}/LICENSE +0 -0
- {pyedb-0.21.1.dist-info → pyedb-0.22.1.dist-info}/WHEEL +0 -0
pyedb/__init__.py
CHANGED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import struct
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import skrf as rf
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ComponentLib:
|
|
8
|
+
"""Handle component libraries."""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.capacitors = {}
|
|
12
|
+
self.inductors = {}
|
|
13
|
+
self.path = ""
|
|
14
|
+
self.series = {}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Series:
|
|
18
|
+
"""Handle component series."""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self._index_file = ""
|
|
22
|
+
self._sbin_file = ""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ComponentPart:
|
|
26
|
+
"""Handle component part definition."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, name, index, sbin_file):
|
|
29
|
+
self.name = name
|
|
30
|
+
self._index = index
|
|
31
|
+
self._sbin_file = sbin_file
|
|
32
|
+
self.nb_ports = 2
|
|
33
|
+
self.nb_freq = 0
|
|
34
|
+
self.ref_impedance = 50.0
|
|
35
|
+
self._s_parameters = None
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def s_parameters(self):
|
|
39
|
+
"""Return skrf.network.Network object
|
|
40
|
+
See `scikit-rf documentation <https://scikit-rf.readthedocs.io/en/latest/api/network.html#network-class>`
|
|
41
|
+
"""
|
|
42
|
+
if not self._s_parameters:
|
|
43
|
+
self._extract_impedance()
|
|
44
|
+
return self._s_parameters
|
|
45
|
+
|
|
46
|
+
def _extract_impedance(self):
|
|
47
|
+
with open(self._sbin_file, mode="rb") as file:
|
|
48
|
+
file_content = file.read()
|
|
49
|
+
file.seek(self._index)
|
|
50
|
+
self.nb_ports = struct.unpack("i", file_content[self._index : self._index + 4])[0]
|
|
51
|
+
self._index += 4
|
|
52
|
+
self.nb_freq = struct.unpack("i", file_content[self._index : self._index + 4])[0]
|
|
53
|
+
self._index += 4
|
|
54
|
+
self.ref_impedance = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
55
|
+
self._index += 4
|
|
56
|
+
s_matrix = np.zeros((self.nb_freq, 2, 2), dtype=complex)
|
|
57
|
+
frequencies = []
|
|
58
|
+
for f in range(self.nb_freq):
|
|
59
|
+
freq = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
60
|
+
self._index += 4
|
|
61
|
+
s11_re_imp = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
62
|
+
self._index += 4
|
|
63
|
+
s11_im_imp = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
64
|
+
self._index += 4
|
|
65
|
+
s12_re_imp = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
66
|
+
self._index += 4
|
|
67
|
+
s12_im_imp = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
68
|
+
self._index += 4
|
|
69
|
+
s21_re_imp = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
70
|
+
self._index += 4
|
|
71
|
+
s21_im_imp = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
72
|
+
self._index += 4
|
|
73
|
+
s22_re_imp = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
74
|
+
self._index += 4
|
|
75
|
+
s22_im_imp = struct.unpack("f", file_content[self._index : self._index + 4])[0]
|
|
76
|
+
self._index += 4
|
|
77
|
+
s_matrix[f, 0, 0] = s11_re_imp + s11_im_imp * 1j
|
|
78
|
+
s_matrix[f, 0, 1] = s12_re_imp + s12_im_imp * 1j
|
|
79
|
+
s_matrix[f, 1, 0] = s21_re_imp + s21_im_imp * 1j
|
|
80
|
+
s_matrix[f, 1, 1] = s22_re_imp + s22_im_imp * 1j
|
|
81
|
+
frequencies.append(freq)
|
|
82
|
+
self._s_parameters = rf.Network()
|
|
83
|
+
self._s_parameters.frequency = tuple(frequencies)
|
|
84
|
+
self._s_parameters.s = s_matrix
|
|
85
|
+
file.close()
|
pyedb/dotnet/edb.py
CHANGED
|
@@ -37,6 +37,7 @@ import time
|
|
|
37
37
|
import traceback
|
|
38
38
|
from typing import Union
|
|
39
39
|
import warnings
|
|
40
|
+
from zipfile import ZipFile as zpf
|
|
40
41
|
|
|
41
42
|
import rtree
|
|
42
43
|
|
|
@@ -118,7 +119,7 @@ class Edb(Database):
|
|
|
118
119
|
edbpath : str, optional
|
|
119
120
|
Full path to the ``aedb`` folder. The variable can also contain
|
|
120
121
|
the path to a layout to import. Allowed formats are BRD, MCM,
|
|
121
|
-
XML (IPC2581), GDS, and DXF. The default is ``None``.
|
|
122
|
+
XML (IPC2581), GDS, ODB++(TGZ and ZIP) and DXF. The default is ``None``.
|
|
122
123
|
For GDS import, the Ansys control file (also XML) should have the same
|
|
123
124
|
name as the GDS file. Only the file extension differs.
|
|
124
125
|
cellname : str, optional
|
|
@@ -221,7 +222,31 @@ class Edb(Database):
|
|
|
221
222
|
if not isreadonly:
|
|
222
223
|
self._check_remove_project_files(edbpath, remove_existing_aedt)
|
|
223
224
|
|
|
224
|
-
if edbpath[-3:]
|
|
225
|
+
if edbpath[-3:] == "zip":
|
|
226
|
+
self.edbpath = edbpath[:-4] + ".aedb"
|
|
227
|
+
working_dir = os.path.dirname(edbpath)
|
|
228
|
+
zipped_file = zpf(edbpath, "r")
|
|
229
|
+
top_level_folders = {item.split("/")[0] for item in zipped_file.namelist()}
|
|
230
|
+
if len(top_level_folders) == 1:
|
|
231
|
+
self.logger.info("Unzipping ODB++...")
|
|
232
|
+
zipped_file.extractall(working_dir)
|
|
233
|
+
else:
|
|
234
|
+
self.logger.info("Unzipping ODB++ before translating to EDB...")
|
|
235
|
+
zipped_file.extractall(edbpath[:-4])
|
|
236
|
+
self.logger.info("ODB++ unzipped successfully.")
|
|
237
|
+
zipped_file.close()
|
|
238
|
+
control_file = None
|
|
239
|
+
if technology_file:
|
|
240
|
+
if os.path.splitext(technology_file)[1] == ".xml":
|
|
241
|
+
control_file = technology_file
|
|
242
|
+
else:
|
|
243
|
+
control_file = convert_technology_file(technology_file, edbversion=edbversion)
|
|
244
|
+
self.logger.info("Translating ODB++ to EDB...")
|
|
245
|
+
self.import_layout_pcb(edbpath[:-4], working_dir, use_ppe=use_ppe, control_file=control_file)
|
|
246
|
+
if settings.enable_local_log_file and self.log_name:
|
|
247
|
+
self._logger.add_file_logger(self.log_name, "Edb")
|
|
248
|
+
self.logger.info("EDB %s was created correctly from %s file.", self.edbpath, edbpath)
|
|
249
|
+
elif edbpath[-3:] in ["brd", "mcm", "sip", "gds", "xml", "dxf", "tgz", "anf"]:
|
|
225
250
|
self.edbpath = edbpath[:-4] + ".aedb"
|
|
226
251
|
working_dir = os.path.dirname(edbpath)
|
|
227
252
|
control_file = None
|
|
@@ -581,7 +606,7 @@ class Edb(Database):
|
|
|
581
606
|
):
|
|
582
607
|
"""Import a board file and generate an ``edb.def`` file in the working directory.
|
|
583
608
|
|
|
584
|
-
This function supports all AEDT formats, including DXF, GDS, SML (IPC2581), BRD, MCM and TGZ.
|
|
609
|
+
This function supports all AEDT formats, including DXF, GDS, SML (IPC2581), BRD, MCM, SIP, ZIP and TGZ.
|
|
585
610
|
|
|
586
611
|
Parameters
|
|
587
612
|
----------
|
|
@@ -26,9 +26,15 @@
|
|
|
26
26
|
import codecs
|
|
27
27
|
import json
|
|
28
28
|
import math
|
|
29
|
+
import os
|
|
29
30
|
import re
|
|
30
31
|
import warnings
|
|
31
32
|
|
|
33
|
+
from pyedb.component_libraries.ansys_components import (
|
|
34
|
+
ComponentLib,
|
|
35
|
+
ComponentPart,
|
|
36
|
+
Series,
|
|
37
|
+
)
|
|
32
38
|
from pyedb.dotnet.clr_module import String
|
|
33
39
|
from pyedb.dotnet.edb_core.cell.hierarchy.component import EDBComponent
|
|
34
40
|
from pyedb.dotnet.edb_core.definition.component_def import EDBComponentDef
|
|
@@ -640,6 +646,50 @@ class Components(object):
|
|
|
640
646
|
return cmp_prop.GetSolderBallProperty().GetHeight()
|
|
641
647
|
return False
|
|
642
648
|
|
|
649
|
+
def get_vendor_libraries(self):
|
|
650
|
+
"""Retrieve all capacitors and inductors libraries from ANSYS installation (used by Siwave).
|
|
651
|
+
|
|
652
|
+
Returns
|
|
653
|
+
-------
|
|
654
|
+
pyedb.component_libraries.ansys_components import ComponentLib object. ComponentLib object contains nested
|
|
655
|
+
dictionaries to navigate through [component tpe][vendors][series]
|
|
656
|
+
[class: `pyedb.component_libraries.ansys_components.ComponentPart`]
|
|
657
|
+
|
|
658
|
+
Examples
|
|
659
|
+
--------
|
|
660
|
+
>>> edbapp = Edb()
|
|
661
|
+
>>> comp_lib = edbapp.components.get_vendor_libraries()
|
|
662
|
+
>>> network = comp_lib.capacitors["AVX"]["AccuP01005"]["C005YJ0R1ABSTR"].s_parameters
|
|
663
|
+
>>> network.write_touchstone(os.path.join(edbapp.directory, "test_export.s2p"))
|
|
664
|
+
|
|
665
|
+
"""
|
|
666
|
+
comp_lib_path = os.path.join(self._pedb.base_path, "complib", "Locked")
|
|
667
|
+
comp_types = ["Capacitors", "Inductors"]
|
|
668
|
+
comp_lib = ComponentLib()
|
|
669
|
+
comp_lib.path = comp_lib_path
|
|
670
|
+
for cmp_type in comp_types:
|
|
671
|
+
folder = os.path.join(comp_lib_path, cmp_type)
|
|
672
|
+
vendors = {f.name: "" for f in os.scandir(folder) if f.is_dir()}
|
|
673
|
+
for vendor in list(vendors.keys()):
|
|
674
|
+
series = {f.name: Series() for f in os.scandir(os.path.join(folder, vendor)) if f.is_dir()}
|
|
675
|
+
for serie_name, _ in series.items():
|
|
676
|
+
_serie = {}
|
|
677
|
+
index_file = os.path.join(folder, vendor, serie_name, "index.txt")
|
|
678
|
+
sbin_file = os.path.join(folder, vendor, serie_name, "sdata.bin")
|
|
679
|
+
if os.path.isfile(index_file):
|
|
680
|
+
with open(index_file, "r") as f:
|
|
681
|
+
for line in f.readlines():
|
|
682
|
+
part_name, index = line.split()
|
|
683
|
+
_serie[part_name] = ComponentPart(part_name, int(index), sbin_file)
|
|
684
|
+
f.close()
|
|
685
|
+
series[serie_name] = _serie
|
|
686
|
+
vendors[vendor] = series
|
|
687
|
+
if cmp_type == "Capacitors":
|
|
688
|
+
comp_lib.capacitors = vendors
|
|
689
|
+
elif cmp_type == "Inductors":
|
|
690
|
+
comp_lib.inductors = vendors
|
|
691
|
+
return comp_lib
|
|
692
|
+
|
|
643
693
|
def create_source_on_component(self, sources=None):
|
|
644
694
|
"""Create voltage, current source, or resistor on component.
|
|
645
695
|
|
|
@@ -876,6 +926,7 @@ class Components(object):
|
|
|
876
926
|
solder_balls_height=None,
|
|
877
927
|
solder_balls_size=None,
|
|
878
928
|
solder_balls_mid_size=None,
|
|
929
|
+
extend_reference_pins_outside_component=False,
|
|
879
930
|
):
|
|
880
931
|
"""Create ports on a component.
|
|
881
932
|
|
|
@@ -908,6 +959,9 @@ class Components(object):
|
|
|
908
959
|
solder_balls_mid_size : float, optional
|
|
909
960
|
Solder balls mid-diameter. When provided if value is different than solder balls size, spheroid shape will
|
|
910
961
|
be switched.
|
|
962
|
+
extend_reference_pins_outside_component : bool
|
|
963
|
+
When no reference pins are found on the component extend the pins search with taking the closest one. If
|
|
964
|
+
`do_pingroup` is `True` will be set to `False`. Default value is `False`.
|
|
911
965
|
|
|
912
966
|
Returns
|
|
913
967
|
-------
|
|
@@ -966,7 +1020,7 @@ class Components(object):
|
|
|
966
1020
|
self._logger.error(
|
|
967
1021
|
"No reference pins found on component. You might consider"
|
|
968
1022
|
"using Circuit port instead since reference pins can be extended"
|
|
969
|
-
"outside the component
|
|
1023
|
+
"outside the component when not found if argument extend_reference_pins_outside_component is True."
|
|
970
1024
|
)
|
|
971
1025
|
return False
|
|
972
1026
|
pad_params = self._padstack.get_pad_parameters(pin=cmp_pins[0], layername=pin_layers[0], pad_type=0)
|
|
@@ -1017,8 +1071,14 @@ class Components(object):
|
|
|
1017
1071
|
if not p.IsLayoutPin():
|
|
1018
1072
|
p.SetIsLayoutPin(True)
|
|
1019
1073
|
if not ref_pins:
|
|
1020
|
-
self._logger.warning("No reference pins found on component
|
|
1021
|
-
|
|
1074
|
+
self._logger.warning("No reference pins found on component")
|
|
1075
|
+
if not extend_reference_pins_outside_component:
|
|
1076
|
+
self._logger.warning(
|
|
1077
|
+
"argument extend_reference_pins_outside_component is False. You might want "
|
|
1078
|
+
"setting to True to extend the reference pin search outside the component"
|
|
1079
|
+
)
|
|
1080
|
+
else:
|
|
1081
|
+
do_pingroup = False
|
|
1022
1082
|
if do_pingroup:
|
|
1023
1083
|
if len(ref_pins) == 1:
|
|
1024
1084
|
ref_pins.is_pin = True
|
|
@@ -1061,16 +1121,22 @@ class Components(object):
|
|
|
1061
1121
|
if ref_pins:
|
|
1062
1122
|
self.create_port_on_pins(component, pin, ref_pins)
|
|
1063
1123
|
else:
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
[EDBPadstackInstance(pin, self._pedb).name],
|
|
1072
|
-
[EDBPadstackInstance(ref_pin[0], self._pedb).id],
|
|
1124
|
+
if extend_reference_pins_outside_component:
|
|
1125
|
+
_pin = EDBPadstackInstance(pin, self._pedb)
|
|
1126
|
+
ref_pin = _pin.get_reference_pins(
|
|
1127
|
+
reference_net=reference_net[0],
|
|
1128
|
+
max_limit=1,
|
|
1129
|
+
component_only=False,
|
|
1130
|
+
search_radius=3e-3,
|
|
1073
1131
|
)
|
|
1132
|
+
if ref_pin:
|
|
1133
|
+
self.create_port_on_pins(
|
|
1134
|
+
component,
|
|
1135
|
+
[EDBPadstackInstance(pin, self._pedb).name],
|
|
1136
|
+
[EDBPadstackInstance(ref_pin[0], self._pedb).id],
|
|
1137
|
+
)
|
|
1138
|
+
else:
|
|
1139
|
+
self._logger.error("Skipping port creation no reference pin found.")
|
|
1074
1140
|
return True
|
|
1075
1141
|
|
|
1076
1142
|
def _create_terminal(self, pin, term_name=None):
|
|
@@ -1391,14 +1457,16 @@ class Components(object):
|
|
|
1391
1457
|
-------
|
|
1392
1458
|
Edb pin group terminal.
|
|
1393
1459
|
"""
|
|
1394
|
-
|
|
1460
|
+
if not "Cell.Hierarchy.PinGroup" in str(pingroup):
|
|
1461
|
+
pingroup = pingroup._edb_object
|
|
1462
|
+
pin = list(pingroup.GetPins())[0]
|
|
1395
1463
|
if term_name is None:
|
|
1396
1464
|
term_name = "{}.{}.{}".format(pin.GetComponent().GetName(), pin.GetName(), pin.GetNet().GetName())
|
|
1397
1465
|
for t in list(self._pedb.active_layout.Terminals):
|
|
1398
1466
|
if t.GetName() == term_name:
|
|
1399
1467
|
return t
|
|
1400
1468
|
pingroup_term = self._edb.cell.terminal.PinGroupTerminal.Create(
|
|
1401
|
-
self._active_layout, pingroup.
|
|
1469
|
+
self._active_layout, pingroup.GetNet(), term_name, pingroup, isref
|
|
1402
1470
|
)
|
|
1403
1471
|
if term_type == "circuit":
|
|
1404
1472
|
pingroup_term.SetIsCircuitPort(True)
|
|
@@ -214,8 +214,9 @@ class LayerEdbClass(object):
|
|
|
214
214
|
layer_clone.SetName(name)
|
|
215
215
|
self._pedb.stackup._set_layout_stackup(layer_clone, "change_name", self._name)
|
|
216
216
|
self._name = name
|
|
217
|
-
|
|
218
|
-
padstack_def.
|
|
217
|
+
if self.type == "signal":
|
|
218
|
+
for padstack_def in list(self._pedb.padstacks.definitions.values()):
|
|
219
|
+
padstack_def._update_layer_names(old_name=old_name, updated_name=name)
|
|
219
220
|
|
|
220
221
|
@property
|
|
221
222
|
def type(self):
|
|
@@ -777,10 +777,10 @@ class EDBPadstack(object):
|
|
|
777
777
|
convert_only_signal_vias : bool, optional
|
|
778
778
|
Either to convert only vias belonging to signal nets or all vias. Defaults is ``True``.
|
|
779
779
|
hole_wall_angle : float, optional
|
|
780
|
-
Angle of laser penetration in degrees. The angle defines the
|
|
780
|
+
Angle of laser penetration in degrees. The angle defines the lowest hole diameter with this formula:
|
|
781
781
|
HoleDiameter -2*tan(laser_angle* Hole depth). Hole depth is the height of the via (dielectric thickness).
|
|
782
782
|
The default is ``15``.
|
|
783
|
-
The
|
|
783
|
+
The lowest hole is ``0.75*HoleDepth/HoleDiam``.
|
|
784
784
|
delete_padstack_def : bool, optional
|
|
785
785
|
Whether to delete the padstack definition. The default is ``True``.
|
|
786
786
|
If ``False``, the padstack definition is not deleted and the hole size is set to zero.
|
|
@@ -812,8 +812,8 @@ class EDBPadstack(object):
|
|
|
812
812
|
stop_elevation = layers[self.instances[0].stop_layer].upper_elevation
|
|
813
813
|
|
|
814
814
|
diel_thick = abs(start_elevation - stop_elevation)
|
|
815
|
-
rad1 = self.hole_properties[0] / 2
|
|
816
|
-
rad2 = self.hole_properties[0] / 2
|
|
815
|
+
rad1 = self.hole_properties[0] / 2 - math.tan(hole_wall_angle * diel_thick * math.pi / 180)
|
|
816
|
+
rad2 = self.hole_properties[0] / 2
|
|
817
817
|
|
|
818
818
|
if start_elevation < (topz + bottomz) / 2:
|
|
819
819
|
rad1, rad2 = rad2, rad1
|
|
@@ -2459,7 +2459,6 @@ class SimulationConfiguration(object):
|
|
|
2459
2459
|
--------
|
|
2460
2460
|
|
|
2461
2461
|
>>> from pyedb import Edb
|
|
2462
|
-
>>> from dotnet.edb_core.edb_data.simulation_configuration import SimulationConfiguration
|
|
2463
2462
|
>>> config_file = path_configuration_file
|
|
2464
2463
|
>>> source_file = path_to_edb_folder
|
|
2465
2464
|
>>> edb = Edb(source_file)
|
|
@@ -2478,7 +2477,7 @@ class SimulationConfiguration(object):
|
|
|
2478
2477
|
cfg_lines = cfg_file.read().split("\n")
|
|
2479
2478
|
for line in cfg_lines:
|
|
2480
2479
|
if line.strip() != "":
|
|
2481
|
-
if line.find("="):
|
|
2480
|
+
if line.find("=") > 0:
|
|
2482
2481
|
i, prop_value = line.strip().split("=")
|
|
2483
2482
|
value = prop_value.replace("'", "").strip()
|
|
2484
2483
|
if i.lower().startswith("generatesolderballs"):
|
|
@@ -212,10 +212,10 @@ class Material(object):
|
|
|
212
212
|
@dc_conductivity.setter
|
|
213
213
|
def dc_conductivity(self, value: Union[int, float]):
|
|
214
214
|
"""Set material dielectric conductivity."""
|
|
215
|
-
if self.__dc_model:
|
|
215
|
+
if self.__dc_model and value:
|
|
216
216
|
self.__dc_model.SetDCConductivity(value)
|
|
217
217
|
else:
|
|
218
|
-
self.__edb.logger.error(f"DC conductivity cannot be updated in material without DC model.")
|
|
218
|
+
self.__edb.logger.error(f"DC conductivity cannot be updated in material without DC model or value {value}.")
|
|
219
219
|
|
|
220
220
|
@property
|
|
221
221
|
def dc_permittivity(self):
|
|
@@ -227,10 +227,12 @@ class Material(object):
|
|
|
227
227
|
@dc_permittivity.setter
|
|
228
228
|
def dc_permittivity(self, value: Union[int, float]):
|
|
229
229
|
"""Set material dielectric relative permittivity"""
|
|
230
|
-
if self.__dc_model:
|
|
230
|
+
if self.__dc_model and value:
|
|
231
231
|
self.__dc_model.SetDCRelativePermitivity(value)
|
|
232
232
|
else:
|
|
233
|
-
self.__edb.logger.error(
|
|
233
|
+
self.__edb.logger.error(
|
|
234
|
+
f"DC permittivity cannot be updated in material without DC model or value {value}." f""
|
|
235
|
+
)
|
|
234
236
|
|
|
235
237
|
@property
|
|
236
238
|
def dielectric_model_frequency(self):
|
|
@@ -1579,7 +1579,9 @@ class EdbPadstacks(object):
|
|
|
1579
1579
|
bounding_box = tuple(bounding_box)
|
|
1580
1580
|
return list(index.intersection(bounding_box))
|
|
1581
1581
|
|
|
1582
|
-
def merge_via_along_lines(
|
|
1582
|
+
def merge_via_along_lines(
|
|
1583
|
+
self, net_name="GND", distance_threshold=5e-3, minimum_via_number=6, selected_angles=None
|
|
1584
|
+
):
|
|
1583
1585
|
"""Replace padstack instances along lines into a single polygon.
|
|
1584
1586
|
|
|
1585
1587
|
Detect all padstack instances that are placed along lines and replace them by a single polygon based one
|
|
@@ -1598,6 +1600,11 @@ class EdbPadstacks(object):
|
|
|
1598
1600
|
minimum_via_number : int, optional
|
|
1599
1601
|
The minimum number of points that a line must contain. Default is ``6``.
|
|
1600
1602
|
|
|
1603
|
+
selected_angles : list[int, float]
|
|
1604
|
+
Specify angle in degrees to detected, for instance [0, 180] is only detecting horizontal and vertical lines.
|
|
1605
|
+
Other values can be assigned like 45 degrees. When `None` is provided all lines are detected. Default value
|
|
1606
|
+
is `None`.
|
|
1607
|
+
|
|
1601
1608
|
Returns
|
|
1602
1609
|
-------
|
|
1603
1610
|
bool
|
|
@@ -1622,6 +1629,7 @@ class EdbPadstacks(object):
|
|
|
1622
1629
|
points=instances_location,
|
|
1623
1630
|
minimum_number_of_points=minimum_via_number,
|
|
1624
1631
|
distance_threshold=distance_threshold,
|
|
1632
|
+
selected_angles=selected_angles,
|
|
1625
1633
|
)
|
|
1626
1634
|
for line in line_indexes:
|
|
1627
1635
|
[_instances_to_delete.append(pdstk_series[ind]) for ind in line]
|
pyedb/dotnet/edb_core/stackup.py
CHANGED
|
@@ -1838,7 +1838,8 @@ class Stackup(LayerCollection):
|
|
|
1838
1838
|
for lay_ind in range(len(list(temp.keys()))):
|
|
1839
1839
|
if not config_file_layers[lay_ind] == layout_layers[lay_ind]:
|
|
1840
1840
|
renamed_layers[layout_layers[lay_ind]] = config_file_layers[lay_ind]
|
|
1841
|
-
|
|
1841
|
+
layers_names = list(self.stackup_layers.keys())[::]
|
|
1842
|
+
for name in layers_names:
|
|
1842
1843
|
layer = None
|
|
1843
1844
|
if name in temp:
|
|
1844
1845
|
layer = temp[name]
|
|
@@ -2019,7 +2019,7 @@ class GeometryOperators(object):
|
|
|
2019
2019
|
|
|
2020
2020
|
@staticmethod
|
|
2021
2021
|
def find_points_along_lines(
|
|
2022
|
-
points, minimum_number_of_points=3, distance_threshold=None, return_additional_info=False
|
|
2022
|
+
points, minimum_number_of_points=3, distance_threshold=None, selected_angles=None, return_additional_info=False
|
|
2023
2023
|
):
|
|
2024
2024
|
"""Detect all points that are placed along lines.
|
|
2025
2025
|
|
|
@@ -2040,6 +2040,15 @@ class GeometryOperators(object):
|
|
|
2040
2040
|
distance_threshold : float, None, optional
|
|
2041
2041
|
If two points in a line are separated by a distance larger than `distance_threshold`,
|
|
2042
2042
|
the line is divided in two parts. Default is ``None``, in which case the control is not performed.
|
|
2043
|
+
selected_angles : list, None, optional
|
|
2044
|
+
Specify a list of angles in degrees. If specified, the method returns only the lines which
|
|
2045
|
+
have one of the specified angles.
|
|
2046
|
+
The angle is defined as the positive angle of the infinite line with respect to the x-axis
|
|
2047
|
+
It is positive, and between 0 and 180 degrees.
|
|
2048
|
+
For example, ``[90]`` indicated vertical lines parallel to the y-axis,
|
|
2049
|
+
``[0]`` or ``[180]`` identifies horizontal lines parallel to the x-axis,
|
|
2050
|
+
``45`` identifies lines parallel to the first quadrant bisector.
|
|
2051
|
+
Default is ``None``, in which case all lines are returned.
|
|
2043
2052
|
return_additional_info : bool, optional
|
|
2044
2053
|
Whether to return additional information about the number of elements processed.
|
|
2045
2054
|
The default is ``True``.
|
|
@@ -2062,6 +2071,11 @@ class GeometryOperators(object):
|
|
|
2062
2071
|
# Parameters
|
|
2063
2072
|
min_num_points = max(3, int(minimum_number_of_points))
|
|
2064
2073
|
cluster_lines_flag = False if distance_threshold is None else True
|
|
2074
|
+
if selected_angles is not None:
|
|
2075
|
+
if 0 in selected_angles or 180 in selected_angles:
|
|
2076
|
+
selected_angles.extend([0, 180])
|
|
2077
|
+
selected_angles = set(selected_angles)
|
|
2078
|
+
selected_angles = np.deg2rad(np.array(list(selected_angles)))
|
|
2065
2079
|
tol_rad = 1e-10
|
|
2066
2080
|
|
|
2067
2081
|
# Converts the input
|
|
@@ -2087,7 +2101,9 @@ class GeometryOperators(object):
|
|
|
2087
2101
|
|
|
2088
2102
|
# Calculate the angle in radians with the x-axis
|
|
2089
2103
|
angle_rad = np.arctan2(dy, dx)
|
|
2090
|
-
if angle_rad <
|
|
2104
|
+
if abs(angle_rad - np.pi) < tol_rad:
|
|
2105
|
+
angles[i].append(0.0)
|
|
2106
|
+
elif angle_rad < 0:
|
|
2091
2107
|
angles[i].append(angle_rad + np.pi)
|
|
2092
2108
|
else:
|
|
2093
2109
|
angles[i].append(angle_rad)
|
|
@@ -2118,8 +2134,18 @@ class GeometryOperators(object):
|
|
|
2118
2134
|
# this two points already belong to a line, no need to store them again
|
|
2119
2135
|
continue
|
|
2120
2136
|
new_lines[angle].update((i, j))
|
|
2137
|
+
|
|
2138
|
+
# Depending on user choice, it checks if the angle is among the desired angles
|
|
2139
|
+
if selected_angles is not None:
|
|
2140
|
+
selected_lines = []
|
|
2141
|
+
for ang, v in new_lines.items():
|
|
2142
|
+
if any(abs(sa - ang) < tol_rad for sa in selected_angles):
|
|
2143
|
+
selected_lines.append(v)
|
|
2144
|
+
else:
|
|
2145
|
+
selected_lines = [l for l in new_lines.values()]
|
|
2146
|
+
|
|
2121
2147
|
# considering only lines with at least 3 points
|
|
2122
|
-
lines_to_add = [l for l in
|
|
2148
|
+
lines_to_add = [l for l in selected_lines if len(l) >= 3]
|
|
2123
2149
|
detected_lines_idx.extend(lines_to_add)
|
|
2124
2150
|
|
|
2125
2151
|
# Discard the lines with less than min_num_points
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyedb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.22.1
|
|
4
4
|
Summary: Higher-Level Pythonic Ansys Electronics Data Base
|
|
5
5
|
Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
|
|
6
6
|
Maintainer-email: PyEDB developers <simon.vandenbrouck@ansys.com>
|
|
@@ -24,6 +24,7 @@ Requires-Dist: pandas>=1.1.0,<2.3
|
|
|
24
24
|
Requires-Dist: pydantic>=2.6.4,<2.9
|
|
25
25
|
Requires-Dist: Rtree >= 1.2.0
|
|
26
26
|
Requires-Dist: toml == 0.10.2
|
|
27
|
+
Requires-Dist: scikit-rf
|
|
27
28
|
Requires-Dist: ansys-sphinx-theme>=0.10.0,<0.17 ; extra == "doc"
|
|
28
29
|
Requires-Dist: imageio>=2.30.0,<2.35 ; extra == "doc"
|
|
29
30
|
Requires-Dist: ipython>=8.13.0,<8.27 ; extra == "doc"
|
|
@@ -47,6 +48,7 @@ Requires-Dist: mock>=5.1.0,<5.2 ; extra == "tests"
|
|
|
47
48
|
Requires-Dist: pytest>=7.4.0,<8.3 ; extra == "tests"
|
|
48
49
|
Requires-Dist: pytest-cov>=4.0.0,<5.1 ; extra == "tests"
|
|
49
50
|
Requires-Dist: pytest-xdist>=3.5.0,<3.7 ; extra == "tests"
|
|
51
|
+
Requires-Dist: scikit-rf ; extra == "tests"
|
|
50
52
|
Project-URL: Bugs, https://github.com/ansys/pyedb/issues
|
|
51
53
|
Project-URL: Discussions, https://github.com/ansys/pyedb/discussions
|
|
52
54
|
Project-URL: Documentation, https://edb.docs.pyansys.com
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
pyedb/__init__.py,sha256=
|
|
1
|
+
pyedb/__init__.py,sha256=aGJoyShOw-T16kkEdjWiiAZz944O0yLK-PpwX5lBoBc,1521
|
|
2
2
|
pyedb/edb_logger.py,sha256=yNkXnoL2me7ubLT6O6r6ElVnkZ1g8fmfFYC_2XJZ1Sw,14950
|
|
3
3
|
pyedb/exceptions.py,sha256=n94xluzUks6BA24vd_L6HkrvoP_H_l6__hQmqzdCyPo,111
|
|
4
4
|
pyedb/siwave.py,sha256=_AxkSZ-7P9aVmizWUWmwcqI07rpGsIZ_fYsd1WNjR6w,12986
|
|
5
5
|
pyedb/workflow.py,sha256=Y0ya4FUHwlSmoLP45zjdYLsSpyKduHUSpT9GGK9MGd8,814
|
|
6
|
+
pyedb/component_libraries/ansys_components.py,sha256=afelGEPmQzbFOIkHnvnWosSs3CqDT5j_kr_1bhrdfnI,3336
|
|
6
7
|
pyedb/configuration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
8
|
pyedb/configuration/cfg_boundaries.py,sha256=ckb-OfaObItwy-xc0LqkHJyeCfKC5vg668olPjZbaKo,6647
|
|
8
9
|
pyedb/configuration/cfg_common.py,sha256=5ne78TTA0wHpbi804nsUd9SPxNKZvut_X_Miu-xDRgk,1982
|
|
@@ -22,22 +23,22 @@ pyedb/configuration/cfg_stackup.py,sha256=CX7uNN5QRoYW_MOObknP8003YchTS7PH9Oee7F
|
|
|
22
23
|
pyedb/configuration/configuration.py,sha256=coJU6y-y7VOGmH_NPXdEdoBdD_aZzYFx7sgvLKNm02E,12629
|
|
23
24
|
pyedb/dotnet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
25
|
pyedb/dotnet/clr_module.py,sha256=Mo13Of3DVSA5HR-5xZEXOiHApIKy52CUxtJ2gPkEu1A,3406
|
|
25
|
-
pyedb/dotnet/edb.py,sha256=
|
|
26
|
+
pyedb/dotnet/edb.py,sha256=ps_zASr7shSBISTB2fsfayCWmTDJ9FTYhDL3T-2pAUg,180752
|
|
26
27
|
pyedb/dotnet/application/Variables.py,sha256=v_fxFJ6xjyyhk4uaMzWAbP-1FhXGuKsVNuyV1huaPME,77867
|
|
27
28
|
pyedb/dotnet/application/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
29
|
pyedb/dotnet/edb_core/__init__.py,sha256=nIRLJ8VZLcMAp12zmGsnZ5x2BEEl7q_Kj_KAOXxVjpQ,52
|
|
29
|
-
pyedb/dotnet/edb_core/components.py,sha256=
|
|
30
|
+
pyedb/dotnet/edb_core/components.py,sha256=lHpLiZj6uEsnZF-i-aUoLZNpvBBlYCk5cYFTGnGcSUA,110215
|
|
30
31
|
pyedb/dotnet/edb_core/general.py,sha256=1g2bUekyUbu8p8zCinT4eI7uqRGIK8tY8mfuFePGRGg,4415
|
|
31
32
|
pyedb/dotnet/edb_core/hfss.py,sha256=Sh417lko4COeeod1C8sbBp6GkoTENEbALdYFOePnOcg,68535
|
|
32
33
|
pyedb/dotnet/edb_core/layout_obj_instance.py,sha256=Pd8rfdO3b6HLFGwXBMw-tfE4LPIcW_9_X5KEdFaiito,1407
|
|
33
34
|
pyedb/dotnet/edb_core/layout_validation.py,sha256=aOYgttlJ007uGG94NnhZR_iNHTuCI5CHkHWWTcm9n-E,12617
|
|
34
|
-
pyedb/dotnet/edb_core/materials.py,sha256=
|
|
35
|
+
pyedb/dotnet/edb_core/materials.py,sha256=PvuxVmm7XGHZjfSy-gsuMKVwthfsW_LLQ3LTRz4YE6c,43167
|
|
35
36
|
pyedb/dotnet/edb_core/modeler.py,sha256=8y6N4HyfJjBrbfac_mggO3qta4GH_dwmt9J4fdqN5WU,55920
|
|
36
37
|
pyedb/dotnet/edb_core/net_class.py,sha256=4U6Cc1Gn7ZJ_ub9uKmtrsoz5wD1XS42afci3Y3ewRp0,11354
|
|
37
38
|
pyedb/dotnet/edb_core/nets.py,sha256=CnZxnK_-WwOMzmJAEalxj_Xo3KlTSJNtf7Fn9x093Ak,43147
|
|
38
|
-
pyedb/dotnet/edb_core/padstack.py,sha256=
|
|
39
|
+
pyedb/dotnet/edb_core/padstack.py,sha256=SO7rIfPYwLnbz0ddLPcgM9CVfhWAY1DfSQVixFJeRNc,63571
|
|
39
40
|
pyedb/dotnet/edb_core/siwave.py,sha256=qhPTgFHaBThvlvch1po8uGsZdmOi1rVzEFHYv19DHPU,64317
|
|
40
|
-
pyedb/dotnet/edb_core/stackup.py,sha256=
|
|
41
|
+
pyedb/dotnet/edb_core/stackup.py,sha256=o3mxqmYQptcLw-WiSo6QOL5sUZKHaDIXYMtU98pL2sU,120550
|
|
41
42
|
pyedb/dotnet/edb_core/cell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
43
|
pyedb/dotnet/edb_core/cell/connectable.py,sha256=rKWPATg0GCHi4d1ftu2V7WWhkDONjloCSPujssie5a8,2310
|
|
43
44
|
pyedb/dotnet/edb_core/cell/layout.py,sha256=g9vMzja9VT9iLFJDz75zYWuE5FfdjquYWysoaIgu1kY,11877
|
|
@@ -73,14 +74,13 @@ pyedb/dotnet/edb_core/edb_data/control_file.py,sha256=_W5DDBFvm4gTq8yCvhzfiWUsfp
|
|
|
73
74
|
pyedb/dotnet/edb_core/edb_data/design_options.py,sha256=RO9ip-T5Bfxpsl97_QEk0qDZsza3tLzIX2t25XLutys,2057
|
|
74
75
|
pyedb/dotnet/edb_core/edb_data/edbvalue.py,sha256=Vj_11HXsQUNavizKp5FicORm6cjhXRh9uvxhv_D_RJc,1977
|
|
75
76
|
pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py,sha256=hKFHWUl0_OCMEZJbQn5c8Y1a-BYKr8nAycIlrCoeufk,13005
|
|
76
|
-
pyedb/dotnet/edb_core/edb_data/
|
|
77
|
-
pyedb/dotnet/edb_core/edb_data/layer_data.py,sha256=SBx2IKtbSn4AvySw4b6IGu3rbb6F-nEoaiqhUwarpLY,25706
|
|
77
|
+
pyedb/dotnet/edb_core/edb_data/layer_data.py,sha256=B6cClg5KTcKUn33jYccTN0uNZZ5dQ037WHCsI-vg3Us,25748
|
|
78
78
|
pyedb/dotnet/edb_core/edb_data/nets_data.py,sha256=ICF61kgST9Rm4hUqU3KUh8FMNlrZEVQO1LqZR1VADkA,9979
|
|
79
|
-
pyedb/dotnet/edb_core/edb_data/padstacks_data.py,sha256=
|
|
79
|
+
pyedb/dotnet/edb_core/edb_data/padstacks_data.py,sha256=atUTmNcR7qZ9GASM7_j8sQyjUM-IWePMbDBJ1A9kDZc,78359
|
|
80
80
|
pyedb/dotnet/edb_core/edb_data/ports.py,sha256=wr2RQi8VExuNIVmnp7c4VpTIhODgthmJmHr01zO4ueo,8873
|
|
81
81
|
pyedb/dotnet/edb_core/edb_data/primitives_data.py,sha256=i6mC-y19iiO9Tr4w3A4Usrd_flyeUSPnxLCz980qFyA,43442
|
|
82
82
|
pyedb/dotnet/edb_core/edb_data/raptor_x_simulation_setup_data.py,sha256=P37-OIsc8TuTC_s3CXRmvZcJqxAftHA7SATfEyoAnMM,20953
|
|
83
|
-
pyedb/dotnet/edb_core/edb_data/simulation_configuration.py,sha256=
|
|
83
|
+
pyedb/dotnet/edb_core/edb_data/simulation_configuration.py,sha256=3qx-YHiA4oC9TJboXb7fKQSzCn5SJRwVOe1OmuVZbmc,100927
|
|
84
84
|
pyedb/dotnet/edb_core/edb_data/sources.py,sha256=jzC6p-fiuPEvZn3b9z1-X5UexW5jd48jZRamXillnXI,15700
|
|
85
85
|
pyedb/dotnet/edb_core/edb_data/utilities.py,sha256=3wZqOJ35eisOwOPKOs-bvJ8kmd62e46EiFuiFtsroB4,4928
|
|
86
86
|
pyedb/dotnet/edb_core/edb_data/variables.py,sha256=LS1jZPOYgRvf4cyKf_x8hI9Brs-qbh4wrHu_QGLElrg,3501
|
|
@@ -180,9 +180,9 @@ pyedb/misc/siw_feature_config/xtalk_scan/net.py,sha256=iQBB2iIyvymLzJP4MTiyo_HTf
|
|
|
180
180
|
pyedb/misc/siw_feature_config/xtalk_scan/pins.py,sha256=NBZLWFoDLGfBj-zGCcZGHzcuANrlfDu4XSbTeC5F8tU,2237
|
|
181
181
|
pyedb/misc/siw_feature_config/xtalk_scan/scan_config.py,sha256=YmYI6WTQulL5Uf8WxeUI_sfpcVVPnFAjjygUNNhGMdo,3599
|
|
182
182
|
pyedb/misc/siw_feature_config/xtalk_scan/td_xtalk_config.py,sha256=KHa-UqcXuabiVfT2CV-UvWl5Q2qGYHF2Ye9azcAlnXc,3966
|
|
183
|
-
pyedb/modeler/geometry_operators.py,sha256=
|
|
183
|
+
pyedb/modeler/geometry_operators.py,sha256=g_Sy7a6R23sP6RtboJn1rl8uTuo8oeLmMF21rNkzwjk,74198
|
|
184
184
|
pyedb/siwave_core/icepak.py,sha256=WnZ-t8mik7LDY06V8hZFV-TxRZJQWK7bu_8Ichx-oBs,5206
|
|
185
|
-
pyedb-0.
|
|
186
|
-
pyedb-0.
|
|
187
|
-
pyedb-0.
|
|
188
|
-
pyedb-0.
|
|
185
|
+
pyedb-0.22.1.dist-info/LICENSE,sha256=qQWivZ12ETN5l3QxvTARY-QI5eoRRlyHdwLlAj0Bg5I,1089
|
|
186
|
+
pyedb-0.22.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
|
187
|
+
pyedb-0.22.1.dist-info/METADATA,sha256=ggDIgzAO9DRVSItoBXoaskYZIA0xzSSv8hSosVGDOS4,8387
|
|
188
|
+
pyedb-0.22.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|