pyedb 0.7.0__py3-none-any.whl → 0.8.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/clr_module.py +1 -1
- pyedb/dotnet/edb.py +7 -7
- pyedb/dotnet/edb_core/cell/hierarchy/model.py +1 -1
- pyedb/dotnet/edb_core/components.py +15 -12
- pyedb/dotnet/edb_core/configuration.py +232 -25
- pyedb/dotnet/edb_core/definition/component_def.py +10 -1
- pyedb/dotnet/edb_core/definition/component_model.py +1 -1
- pyedb/dotnet/edb_core/definition/definition_obj.py +1 -1
- pyedb/dotnet/edb_core/definition/definitions.py +8 -2
- pyedb/dotnet/edb_core/definition/package_def.py +61 -15
- pyedb/dotnet/edb_core/dotnet/database.py +5 -4
- pyedb/dotnet/edb_core/edb_data/components_data.py +3 -2
- pyedb/dotnet/edb_core/edb_data/connectable.py +1 -1
- pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +14 -13
- pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +2 -2
- pyedb/dotnet/edb_core/edb_data/layer_data.py +9 -3
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +6 -5
- pyedb/dotnet/edb_core/edb_data/primitives_data.py +16 -13
- pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +7 -1
- pyedb/dotnet/edb_core/edb_data/sources.py +10 -0
- pyedb/dotnet/edb_core/geometry/__init__.py +0 -0
- pyedb/dotnet/edb_core/{edb_data/obj_base.py → geometry/point_data.py} +12 -26
- pyedb/dotnet/edb_core/geometry/polygon_data.py +77 -0
- pyedb/dotnet/edb_core/layout.py +59 -0
- pyedb/dotnet/edb_core/materials.py +715 -597
- pyedb/dotnet/edb_core/nets.py +3 -3
- pyedb/dotnet/edb_core/obj_base.py +94 -0
- pyedb/dotnet/edb_core/padstack.py +57 -6
- pyedb/dotnet/edb_core/siwave.py +11 -4
- pyedb/dotnet/edb_core/stackup.py +152 -131
- pyedb/dotnet/edb_core/utilities/__init__.py +3 -0
- pyedb/dotnet/edb_core/utilities/heatsink.py +69 -0
- pyedb/dotnet/sim_setup_data/data/siw_dc_ir_settings.py +46 -0
- pyedb/edb_logger.py +15 -1
- pyedb/exceptions.py +6 -0
- pyedb/generic/filesystem.py +7 -3
- pyedb/generic/general_methods.py +4 -0
- pyedb/generic/process.py +4 -1
- pyedb/generic/settings.py +30 -8
- pyedb/siwave.py +50 -1
- {pyedb-0.7.0.dist-info → pyedb-0.8.0.dist-info}/METADATA +31 -53
- {pyedb-0.7.0.dist-info → pyedb-0.8.0.dist-info}/RECORD +46 -39
- /pyedb/dotnet/edb_core/{edb_data → utilities}/simulation_setup.py +0 -0
- {pyedb-0.7.0.dist-info → pyedb-0.8.0.dist-info}/LICENSE +0 -0
- {pyedb-0.7.0.dist-info → pyedb-0.8.0.dist-info}/WHEEL +0 -0
pyedb/dotnet/edb_core/stackup.py
CHANGED
|
@@ -31,7 +31,6 @@ from collections import OrderedDict
|
|
|
31
31
|
import json
|
|
32
32
|
import logging
|
|
33
33
|
import math
|
|
34
|
-
import re
|
|
35
34
|
import warnings
|
|
36
35
|
|
|
37
36
|
from pyedb.dotnet.edb_core.edb_data.layer_data import (
|
|
@@ -47,9 +46,15 @@ from pyedb.generic.general_methods import (
|
|
|
47
46
|
)
|
|
48
47
|
from pyedb.misc.aedtlib_personalib_install import write_pretty_xml
|
|
49
48
|
|
|
49
|
+
colors = None
|
|
50
50
|
pd = None
|
|
51
51
|
np = None
|
|
52
52
|
if not is_ironpython:
|
|
53
|
+
try:
|
|
54
|
+
import matplotlib.colors as colors
|
|
55
|
+
except ImportError:
|
|
56
|
+
colors = None
|
|
57
|
+
|
|
53
58
|
try:
|
|
54
59
|
import numpy as np
|
|
55
60
|
except ImportError:
|
|
@@ -199,7 +204,7 @@ class Stackup(object):
|
|
|
199
204
|
inner_layer_thickness="17um",
|
|
200
205
|
outer_layer_thickness="50um",
|
|
201
206
|
dielectric_thickness="100um",
|
|
202
|
-
dielectric_material="
|
|
207
|
+
dielectric_material="FR4_epoxy",
|
|
203
208
|
soldermask=True,
|
|
204
209
|
soldermask_thickness="20um",
|
|
205
210
|
): # pragma: no cover
|
|
@@ -242,7 +247,7 @@ class Stackup(object):
|
|
|
242
247
|
self.add_layer(
|
|
243
248
|
"D" + str(int(layer_count / 2)),
|
|
244
249
|
None,
|
|
245
|
-
material="
|
|
250
|
+
material="FR4_epoxy",
|
|
246
251
|
thickness=dielectric_thickness,
|
|
247
252
|
layer_type="dielectric",
|
|
248
253
|
fillMaterial=dielectric_material,
|
|
@@ -258,7 +263,7 @@ class Stackup(object):
|
|
|
258
263
|
self.add_layer(
|
|
259
264
|
"SMT",
|
|
260
265
|
None,
|
|
261
|
-
material="
|
|
266
|
+
material="SolderMask",
|
|
262
267
|
thickness=soldermask_thickness,
|
|
263
268
|
layer_type="dielectric",
|
|
264
269
|
fillMaterial=dielectric_material,
|
|
@@ -266,14 +271,14 @@ class Stackup(object):
|
|
|
266
271
|
self.add_layer(
|
|
267
272
|
"SMB",
|
|
268
273
|
None,
|
|
269
|
-
material="
|
|
274
|
+
material="SolderMask",
|
|
270
275
|
thickness=soldermask_thickness,
|
|
271
276
|
layer_type="dielectric",
|
|
272
277
|
fillMaterial=dielectric_material,
|
|
273
278
|
method="add_on_bottom",
|
|
274
279
|
)
|
|
275
|
-
self.stackup_layers["TOP"].dielectric_fill = "
|
|
276
|
-
self.stackup_layers["BOT"].dielectric_fill = "
|
|
280
|
+
self.stackup_layers["TOP"].dielectric_fill = "SolderMask"
|
|
281
|
+
self.stackup_layers["BOT"].dielectric_fill = "SolderMask"
|
|
277
282
|
|
|
278
283
|
for layer_num in np.arange(int(layer_count / 2), 1, -1):
|
|
279
284
|
# Generate upper half
|
|
@@ -633,6 +638,7 @@ class Stackup(object):
|
|
|
633
638
|
else:
|
|
634
639
|
return False
|
|
635
640
|
|
|
641
|
+
# TODO: Update optional argument material into material_name and fillMaterial into fill_material_name
|
|
636
642
|
@pyedb_function_handler()
|
|
637
643
|
def add_layer(
|
|
638
644
|
self,
|
|
@@ -641,7 +647,7 @@ class Stackup(object):
|
|
|
641
647
|
method="add_on_top",
|
|
642
648
|
layer_type="signal",
|
|
643
649
|
material="copper",
|
|
644
|
-
fillMaterial="
|
|
650
|
+
fillMaterial="FR4_epoxy",
|
|
645
651
|
thickness="35um",
|
|
646
652
|
etch_factor=None,
|
|
647
653
|
is_negative=False,
|
|
@@ -685,25 +691,25 @@ class Stackup(object):
|
|
|
685
691
|
if layer_name in self.layers:
|
|
686
692
|
logger.error("layer {} exists.".format(layer_name))
|
|
687
693
|
return False
|
|
688
|
-
materials_lower = {m.lower(): m for m in list(self._pedb.materials.materials.keys())}
|
|
689
694
|
if not material:
|
|
690
|
-
if layer_type == "signal"
|
|
691
|
-
material = "copper"
|
|
692
|
-
else:
|
|
693
|
-
material = "fr4_epoxy"
|
|
695
|
+
material = "copper" if layer_type == "signal" else "FR4_epoxy"
|
|
694
696
|
if not fillMaterial:
|
|
695
|
-
fillMaterial = "
|
|
697
|
+
fillMaterial = "FR4_epoxy"
|
|
696
698
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
699
|
+
materials = self._pedb.materials
|
|
700
|
+
if material not in materials:
|
|
701
|
+
logger.warning(
|
|
702
|
+
f"Material '{material}' does not exist in material library. Intempt to create it from syslib."
|
|
703
|
+
)
|
|
704
|
+
material_properties = self._pedb.materials.read_syslib_material(material)
|
|
705
|
+
materials.add_material(material, **material_properties)
|
|
701
706
|
|
|
702
|
-
if layer_type != "dielectric":
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
+
if layer_type != "dielectric" and fillMaterial not in materials:
|
|
708
|
+
logger.warning(
|
|
709
|
+
f"Material '{fillMaterial}' does not exist in material library. Intempt to create it from syslib."
|
|
710
|
+
)
|
|
711
|
+
material_properties = self._pedb.materials.read_syslib_material(fillMaterial)
|
|
712
|
+
materials.add_material(fillMaterial, **material_properties)
|
|
707
713
|
|
|
708
714
|
if layer_type in ["signal", "dielectric"]:
|
|
709
715
|
new_layer = self._create_stackup_layer(layer_name, thickness, layer_type)
|
|
@@ -878,6 +884,7 @@ class Stackup(object):
|
|
|
878
884
|
else:
|
|
879
885
|
return False
|
|
880
886
|
|
|
887
|
+
# TODO: This method might need some refactoring
|
|
881
888
|
@pyedb_function_handler()
|
|
882
889
|
def _import_layer_stackup(self, input_file=None):
|
|
883
890
|
if input_file:
|
|
@@ -886,7 +893,12 @@ class Stackup(object):
|
|
|
886
893
|
for k, v in json_dict.items():
|
|
887
894
|
if k == "materials":
|
|
888
895
|
for material in v.values():
|
|
889
|
-
|
|
896
|
+
material_name = material["name"]
|
|
897
|
+
del material["name"]
|
|
898
|
+
if material_name not in self._pedb.materials:
|
|
899
|
+
self._pedb.materials.add_material(material_name, **material)
|
|
900
|
+
else:
|
|
901
|
+
self._pedb.materials.update_material(material_name, material)
|
|
890
902
|
if k == "layers":
|
|
891
903
|
if len(list(v.values())) == len(list(self.stackup_layers.values())):
|
|
892
904
|
imported_layers_list = [l_dict["name"] for l_dict in list(v.values())]
|
|
@@ -1667,36 +1679,63 @@ class Stackup(object):
|
|
|
1667
1679
|
temp_data = {name: area / outline_area * 100 for name, area in temp_data.items()}
|
|
1668
1680
|
return temp_data
|
|
1669
1681
|
|
|
1682
|
+
# TODO: This method might need some refactoring
|
|
1670
1683
|
@pyedb_function_handler()
|
|
1671
|
-
def _import_dict(self, json_dict):
|
|
1684
|
+
def _import_dict(self, json_dict, rename=False):
|
|
1672
1685
|
"""Import stackup from a dictionary."""
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
self.
|
|
1676
|
-
|
|
1686
|
+
if not "materials" in json_dict:
|
|
1687
|
+
self._logger.warning("Configuration file does not have material definition")
|
|
1688
|
+
self._logger.warning(
|
|
1689
|
+
"Please check your json or xml file, if no material are defined your project will"
|
|
1690
|
+
"likely fail to simulate"
|
|
1691
|
+
)
|
|
1692
|
+
else:
|
|
1693
|
+
mats = json_dict["materials"]
|
|
1694
|
+
for material in mats.values():
|
|
1695
|
+
material_name = material["name"]
|
|
1696
|
+
del material["name"]
|
|
1697
|
+
if material_name not in self._pedb.materials:
|
|
1698
|
+
self._pedb.materials.add_material(material_name, **material)
|
|
1699
|
+
else:
|
|
1700
|
+
self._pedb.materials.update_material(material_name, material)
|
|
1677
1701
|
temp = {i: j for i, j in json_dict["layers"].items() if j["type"] in ["signal", "dielectric"]}
|
|
1702
|
+
config_file_layers = list(temp.keys())
|
|
1703
|
+
layout_layers = list(self.stackup_layers.keys())
|
|
1704
|
+
renamed_layers = {}
|
|
1705
|
+
if rename and len(config_file_layers) == len(layout_layers):
|
|
1706
|
+
for lay_ind in range(len(list(temp.keys()))):
|
|
1707
|
+
if not config_file_layers[lay_ind] == layout_layers[lay_ind]:
|
|
1708
|
+
renamed_layers[layout_layers[lay_ind]] = config_file_layers[lay_ind]
|
|
1678
1709
|
for name in list(self.stackup_layers.keys()):
|
|
1710
|
+
layer = None
|
|
1679
1711
|
if name in temp:
|
|
1680
1712
|
layer = temp[name]
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1713
|
+
elif name in renamed_layers:
|
|
1714
|
+
layer = temp[renamed_layers[name]]
|
|
1715
|
+
self.stackup_layers[name].name = renamed_layers[name]
|
|
1716
|
+
name = renamed_layers[name]
|
|
1717
|
+
else: # Remove layers not in config file.
|
|
1718
|
+
self.remove_layer(name)
|
|
1719
|
+
self._logger.warning(f"Layer {name} were not found in configuration file, removing layer")
|
|
1720
|
+
default_layer = {
|
|
1721
|
+
"name": "default",
|
|
1722
|
+
"type": "signal",
|
|
1723
|
+
"material": "copper",
|
|
1724
|
+
"dielectric_fill": "FR4_epoxy",
|
|
1725
|
+
"thickness": 3.5e-05,
|
|
1726
|
+
"etch_factor": 0.0,
|
|
1727
|
+
"roughness_enabled": False,
|
|
1728
|
+
"top_hallhuray_nodule_radius": 0.0,
|
|
1729
|
+
"top_hallhuray_surface_ratio": 0.0,
|
|
1730
|
+
"bottom_hallhuray_nodule_radius": 0.0,
|
|
1731
|
+
"bottom_hallhuray_surface_ratio": 0.0,
|
|
1732
|
+
"side_hallhuray_nodule_radius": 0.0,
|
|
1733
|
+
"side_hallhuray_surface_ratio": 0.0,
|
|
1734
|
+
"upper_elevation": 0.0,
|
|
1735
|
+
"lower_elevation": 0.0,
|
|
1736
|
+
"color": [242, 140, 102],
|
|
1737
|
+
}
|
|
1738
|
+
if layer:
|
|
1700
1739
|
if "color" in layer:
|
|
1701
1740
|
default_layer["color"] = layer["color"]
|
|
1702
1741
|
elif not layer["type"] == "signal":
|
|
@@ -1704,20 +1743,16 @@ class Stackup(object):
|
|
|
1704
1743
|
|
|
1705
1744
|
for k, v in layer.items():
|
|
1706
1745
|
default_layer[k] = v
|
|
1707
|
-
|
|
1708
1746
|
self.stackup_layers[name]._load_layer(default_layer)
|
|
1709
|
-
|
|
1710
|
-
self.remove_layer(name)
|
|
1711
|
-
|
|
1712
|
-
for layer_name, layer in temp.items():
|
|
1747
|
+
for layer_name, layer in temp.items(): # looping over potential new layers to add
|
|
1713
1748
|
if layer_name in self.stackup_layers:
|
|
1714
1749
|
continue # if layer exist, skip
|
|
1715
|
-
|
|
1750
|
+
# adding layer
|
|
1716
1751
|
default_layer = {
|
|
1717
1752
|
"name": "default",
|
|
1718
1753
|
"type": "signal",
|
|
1719
1754
|
"material": "copper",
|
|
1720
|
-
"dielectric_fill": "
|
|
1755
|
+
"dielectric_fill": "FR4_epoxy",
|
|
1721
1756
|
"thickness": 3.5e-05,
|
|
1722
1757
|
"etch_factor": 0.0,
|
|
1723
1758
|
"roughness_enabled": False,
|
|
@@ -1786,12 +1821,12 @@ class Stackup(object):
|
|
|
1786
1821
|
return True
|
|
1787
1822
|
|
|
1788
1823
|
@pyedb_function_handler()
|
|
1789
|
-
def _import_json(self, file_path):
|
|
1824
|
+
def _import_json(self, file_path, rename=False):
|
|
1790
1825
|
"""Import stackup from a json file."""
|
|
1791
1826
|
if file_path:
|
|
1792
1827
|
f = open(file_path)
|
|
1793
1828
|
json_dict = json.load(f) # pragma: no cover
|
|
1794
|
-
return self._import_dict(json_dict)
|
|
1829
|
+
return self._import_dict(json_dict, rename)
|
|
1795
1830
|
|
|
1796
1831
|
@pyedb_function_handler()
|
|
1797
1832
|
def _import_csv(self, file_path):
|
|
@@ -2035,40 +2070,39 @@ class Stackup(object):
|
|
|
2035
2070
|
material["Conductivity"] = val.conductivity
|
|
2036
2071
|
else:
|
|
2037
2072
|
material["Permittivity"] = val.permittivity
|
|
2038
|
-
material["DielectricLossTangent"] = val.
|
|
2073
|
+
material["DielectricLossTangent"] = val.dielectric_loss_tangent
|
|
2039
2074
|
materials[name] = material
|
|
2040
2075
|
|
|
2041
2076
|
return layers, materials, roughness_models, non_stackup_layers
|
|
2042
2077
|
|
|
2043
2078
|
@pyedb_function_handler()
|
|
2044
2079
|
def _add_materials_from_dictionary(self, material_dict):
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
self._pedb.materials.add_conductor_material(name, attr["Conductivity"])
|
|
2080
|
+
materials = self.self._pedb.materials.materials
|
|
2081
|
+
for name, material_properties in material_dict.items():
|
|
2082
|
+
if not name in materials:
|
|
2083
|
+
if "Conductivity" in material_properties:
|
|
2084
|
+
materials.add_conductor_material(name, material_properties["Conductivity"])
|
|
2051
2085
|
else:
|
|
2052
|
-
|
|
2086
|
+
materials.add_dielectric_material(
|
|
2053
2087
|
name,
|
|
2054
|
-
|
|
2055
|
-
|
|
2088
|
+
material_properties["Permittivity"],
|
|
2089
|
+
material_properties["DielectricLossTangent"],
|
|
2056
2090
|
)
|
|
2057
2091
|
else:
|
|
2058
|
-
|
|
2059
|
-
if "Conductivity" in
|
|
2060
|
-
|
|
2092
|
+
material = materials[name]
|
|
2093
|
+
if "Conductivity" in material_properties:
|
|
2094
|
+
material.conductivity = material_properties["Conductivity"]
|
|
2061
2095
|
else:
|
|
2062
|
-
|
|
2063
|
-
|
|
2096
|
+
material.permittivity = material_properties["Permittivity"]
|
|
2097
|
+
material.loss_tanget = material_properties["DielectricLossTangent"]
|
|
2064
2098
|
return True
|
|
2065
2099
|
|
|
2066
2100
|
@pyedb_function_handler()
|
|
2067
|
-
def _import_xml(self, file_path):
|
|
2068
|
-
"""Read external xml file and
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2101
|
+
def _import_xml(self, file_path, rename=False):
|
|
2102
|
+
"""Read external xml file and convert into json file.
|
|
2103
|
+
You can use xml file to import layer stackup but using json file is recommended.
|
|
2104
|
+
see :class:`pyedb.dotnet.edb_core.edb_data.simulation_configuration.SimulationConfiguration´ class to
|
|
2105
|
+
generate files`.
|
|
2072
2106
|
|
|
2073
2107
|
Parameters
|
|
2074
2108
|
----------
|
|
@@ -2080,60 +2114,42 @@ class Stackup(object):
|
|
|
2080
2114
|
bool
|
|
2081
2115
|
``True`` when successful, ``False`` when failed.
|
|
2082
2116
|
"""
|
|
2117
|
+
if not colors:
|
|
2118
|
+
self._pedb.logger.error("Matplotlib is needed. Please, install it first.")
|
|
2119
|
+
return False
|
|
2083
2120
|
tree = ET.parse(file_path)
|
|
2084
|
-
material_dict = {}
|
|
2085
2121
|
root = tree.getroot()
|
|
2086
2122
|
stackup = root.find("Stackup")
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
if
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
if name in self.layers:
|
|
2118
|
-
layer = self.layers[name]
|
|
2119
|
-
layer.type = layer_type
|
|
2120
|
-
else:
|
|
2121
|
-
layer = self.add_layer(name, layer_type=layer_type, material="copper", fillMaterial="copper")
|
|
2122
|
-
|
|
2123
|
-
if l.IsStackupLayer():
|
|
2124
|
-
layer.material = l.GetMaterial()
|
|
2125
|
-
layer.thickness = l.GetThicknessValue().ToDouble()
|
|
2126
|
-
layer.dielectric_fill = l.GetFillMaterial()
|
|
2127
|
-
layer.etch_factor = l.GetEtchFactor().ToDouble()
|
|
2128
|
-
|
|
2129
|
-
lc_new = self._pedb.edb_api.Cell.LayerCollection()
|
|
2130
|
-
for name, _ in dumy_layers.items():
|
|
2131
|
-
layer = self.layers[name]
|
|
2132
|
-
lc_new.AddLayerBottom(layer._edb_layer)
|
|
2133
|
-
|
|
2134
|
-
self._pedb.layout.layer_collection = lc_new
|
|
2135
|
-
self.refresh_layer_collection()
|
|
2136
|
-
return True
|
|
2123
|
+
stackup_dict = {}
|
|
2124
|
+
if stackup.find("Materials"):
|
|
2125
|
+
stackup_dict["materials"] = {}
|
|
2126
|
+
for m in stackup.find("Materials").findall("Material"):
|
|
2127
|
+
material = {"name": m.attrib["Name"]}
|
|
2128
|
+
for i in list(m):
|
|
2129
|
+
material[i.tag.lower()] = float(list(i)[0].text)
|
|
2130
|
+
if material:
|
|
2131
|
+
stackup_dict["materials"][material["name"]] = material
|
|
2132
|
+
stackup_section = stackup.find("Layers")
|
|
2133
|
+
if stackup_section:
|
|
2134
|
+
length_unit = stackup_section.attrib["LengthUnit"]
|
|
2135
|
+
stackup_dict["layers"] = {}
|
|
2136
|
+
for l in stackup.find("Layers").findall("Layer"):
|
|
2137
|
+
layer = {"name": l.attrib["Name"]}
|
|
2138
|
+
for k, v in l.attrib.items():
|
|
2139
|
+
if k == "Color":
|
|
2140
|
+
layer[k.lower()] = [int(x * 255) for x in list(colors.to_rgb(v))]
|
|
2141
|
+
elif k == "Thickness":
|
|
2142
|
+
layer[k.lower()] = v + length_unit
|
|
2143
|
+
elif v == "conductor":
|
|
2144
|
+
layer[k.lower()] = "signal"
|
|
2145
|
+
elif k == "FillMaterial":
|
|
2146
|
+
layer["dielectric_fill"] = v
|
|
2147
|
+
else:
|
|
2148
|
+
layer[k.lower()] = v
|
|
2149
|
+
if layer:
|
|
2150
|
+
if layer["type"] == "signal" or layer["type"] == "dielectric":
|
|
2151
|
+
stackup_dict["layers"][layer["name"]] = layer
|
|
2152
|
+
return self._import_dict(stackup_dict, rename=rename)
|
|
2137
2153
|
|
|
2138
2154
|
@pyedb_function_handler()
|
|
2139
2155
|
def _export_xml(self, file_path):
|
|
@@ -2187,7 +2203,7 @@ class Stackup(object):
|
|
|
2187
2203
|
return True
|
|
2188
2204
|
|
|
2189
2205
|
@pyedb_function_handler()
|
|
2190
|
-
def load(self, file_path):
|
|
2206
|
+
def load(self, file_path, rename=False):
|
|
2191
2207
|
"""Import stackup from a file. The file format can be XML, CSV, or JSON.
|
|
2192
2208
|
|
|
2193
2209
|
|
|
@@ -2195,6 +2211,11 @@ class Stackup(object):
|
|
|
2195
2211
|
----------
|
|
2196
2212
|
file_path : str
|
|
2197
2213
|
Path to stackup file.
|
|
2214
|
+
rename : bool
|
|
2215
|
+
If rename is ``False`` then layer in layout not found in the stackup file are deleted.
|
|
2216
|
+
Otherwise, if the number of layer in the stackup file equals the number of stackup layer
|
|
2217
|
+
in the layout, layers are renamed according the the file.
|
|
2218
|
+
Note that layer order matters, and has to be writtent from top to bottom layer in the file.
|
|
2198
2219
|
|
|
2199
2220
|
Returns
|
|
2200
2221
|
-------
|
|
@@ -2213,9 +2234,9 @@ class Stackup(object):
|
|
|
2213
2234
|
elif file_path.endswith(".csv"):
|
|
2214
2235
|
return self._import_csv(file_path)
|
|
2215
2236
|
elif file_path.endswith(".json"):
|
|
2216
|
-
return self._import_json(file_path)
|
|
2237
|
+
return self._import_json(file_path, rename=rename)
|
|
2217
2238
|
elif file_path.endswith(".xml"):
|
|
2218
|
-
return self._import_xml(file_path)
|
|
2239
|
+
return self._import_xml(file_path, rename=rename)
|
|
2219
2240
|
else:
|
|
2220
2241
|
return False
|
|
2221
2242
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
class HeatSink:
|
|
2
|
+
|
|
3
|
+
"""Heatsink model description.
|
|
4
|
+
|
|
5
|
+
Parameters
|
|
6
|
+
----------
|
|
7
|
+
pedb : :class:`pyedb.dotnet.edb.Edb`
|
|
8
|
+
Inherited object.
|
|
9
|
+
edb_object : :class:`Ansys.Ansoft.Edb.Utility.HeatSink`,
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, pedb, edb_object=None):
|
|
13
|
+
self._pedb = pedb
|
|
14
|
+
self._fin_orientation_type = {
|
|
15
|
+
"x_oriented": self._pedb.edb_api.utility.utility.HeatSinkFinOrientation.XOriented,
|
|
16
|
+
"y_oriented": self._pedb.edb_api.utility.utility.HeatSinkFinOrientation.YOriented,
|
|
17
|
+
"other_oriented": self._pedb.edb_api.utility.utility.HeatSinkFinOrientation.OtherOriented,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if edb_object:
|
|
21
|
+
self._edb_object = edb_object
|
|
22
|
+
else:
|
|
23
|
+
self._edb_object = self._pedb.edb_api.utility.utility.HeatSink()
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def fin_base_height(self):
|
|
27
|
+
"""The base elevation of the fins."""
|
|
28
|
+
return self._edb_object.FinBaseHeight.ToDouble()
|
|
29
|
+
|
|
30
|
+
@fin_base_height.setter
|
|
31
|
+
def fin_base_height(self, value):
|
|
32
|
+
self._edb_object.FinBaseHeight = self._pedb.edb_value(value)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def fin_height(self):
|
|
36
|
+
"""The fin height."""
|
|
37
|
+
return self._edb_object.FinHeight.ToDouble()
|
|
38
|
+
|
|
39
|
+
@fin_height.setter
|
|
40
|
+
def fin_height(self, value):
|
|
41
|
+
self._edb_object.FinHeight = self._pedb.edb_value(value)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def fin_orientation(self):
|
|
45
|
+
"""The fin orientation."""
|
|
46
|
+
temp = self._edb_object.FinOrientation
|
|
47
|
+
return list(self._fin_orientation_type.keys())[list(self._fin_orientation_type.values()).index(temp)]
|
|
48
|
+
|
|
49
|
+
@fin_orientation.setter
|
|
50
|
+
def fin_orientation(self, value):
|
|
51
|
+
self._edb_object.FinOrientation = self._fin_orientation_type[value]
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def fin_spacing(self):
|
|
55
|
+
"""The fin spacing."""
|
|
56
|
+
return self._edb_object.FinSpacing.ToDouble()
|
|
57
|
+
|
|
58
|
+
@fin_spacing.setter
|
|
59
|
+
def fin_spacing(self, value):
|
|
60
|
+
self._edb_object.FinSpacing = self._pedb.edb_value(value)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def fin_thickness(self):
|
|
64
|
+
"""The fin thickness."""
|
|
65
|
+
return self._edb_object.FinThickness.ToDouble()
|
|
66
|
+
|
|
67
|
+
@fin_thickness.setter
|
|
68
|
+
def fin_thickness(self, value):
|
|
69
|
+
self._edb_object.FinThickness = self._pedb.edb_value(value)
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
|
|
23
|
+
|
|
24
|
+
class SiwaveDCIRSettings:
|
|
25
|
+
"""Class for DC IR settings."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, parent):
|
|
28
|
+
self._parent = parent
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def export_dc_thermal_data(self):
|
|
32
|
+
"""Export DC Thermal Data.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
bool
|
|
37
|
+
``True`` when activated, ``False`` deactivated.
|
|
38
|
+
"""
|
|
39
|
+
return self._parent.get_sim_setup_info.SimulationSettings.DCIRSettings.ExportDCThermalData
|
|
40
|
+
|
|
41
|
+
@export_dc_thermal_data.setter
|
|
42
|
+
def export_dc_thermal_data(self, value):
|
|
43
|
+
edb_setup_info = self._parent.get_sim_setup_info
|
|
44
|
+
edb_setup_info.SimulationSettings.DCIRSettings.ExportDCThermalData = value
|
|
45
|
+
self._parent._edb_object = self._parent._set_edb_setup_info(edb_setup_info)
|
|
46
|
+
self._parent._update_setup()
|
pyedb/edb_logger.py
CHANGED
|
@@ -415,4 +415,18 @@ class EdbLogger(object):
|
|
|
415
415
|
return self._global
|
|
416
416
|
|
|
417
417
|
|
|
418
|
-
|
|
418
|
+
logger = logging.getLogger("Global")
|
|
419
|
+
if any("aedt_logger" in str(i) for i in logger.filters):
|
|
420
|
+
from pyaedt.generic.settings import settings as pyaedt_settings
|
|
421
|
+
|
|
422
|
+
from pyedb.generic.settings import settings as pyaedb_settings
|
|
423
|
+
|
|
424
|
+
pyedb_logger = pyaedt_settings.logger
|
|
425
|
+
pyaedb_settings.use_pyaedt_log = True
|
|
426
|
+
pyaedb_settings.logger = pyedb_logger
|
|
427
|
+
|
|
428
|
+
else:
|
|
429
|
+
pyedb_logger = EdbLogger(to_stdout=settings.enable_screen_logs)
|
|
430
|
+
from pyedb.generic.settings import settings as pyaedb_settings
|
|
431
|
+
|
|
432
|
+
pyaedb_settings.logger = pyedb_logger
|
pyedb/exceptions.py
ADDED
pyedb/generic/filesystem.py
CHANGED
|
@@ -108,7 +108,7 @@ class Scratch:
|
|
|
108
108
|
|
|
109
109
|
return dst_file
|
|
110
110
|
|
|
111
|
-
def copyfolder(self, src_folder, destfolder):
|
|
111
|
+
def copyfolder(self, src_folder, destfolder=None):
|
|
112
112
|
"""
|
|
113
113
|
|
|
114
114
|
Parameters
|
|
@@ -124,8 +124,12 @@ class Scratch:
|
|
|
124
124
|
"""
|
|
125
125
|
from distutils.dir_util import copy_tree
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
if destfolder:
|
|
128
|
+
copy_tree(src_folder, destfolder)
|
|
129
|
+
else:
|
|
130
|
+
destfolder = os.path.join(self.path, os.path.split(src_folder)[-1])
|
|
131
|
+
copy_tree(src_folder, destfolder)
|
|
132
|
+
return destfolder
|
|
129
133
|
|
|
130
134
|
def __enter__(self):
|
|
131
135
|
return self
|
pyedb/generic/general_methods.py
CHANGED
|
@@ -44,6 +44,7 @@ import tempfile
|
|
|
44
44
|
import time
|
|
45
45
|
import traceback
|
|
46
46
|
|
|
47
|
+
from pyedb.exceptions import MaterialModelException
|
|
47
48
|
from pyedb.generic.constants import CSS4_COLORS
|
|
48
49
|
from pyedb.generic.settings import settings
|
|
49
50
|
|
|
@@ -179,6 +180,9 @@ def _function_handler_wrapper(user_function): # pragma: no cover
|
|
|
179
180
|
except IOError:
|
|
180
181
|
_exception(sys.exc_info(), user_function, args, kwargs, "IO Error")
|
|
181
182
|
return False
|
|
183
|
+
except MaterialModelException:
|
|
184
|
+
_exception(sys.exc_info(), user_function, args, kwargs, "Material Model")
|
|
185
|
+
return False
|
|
182
186
|
|
|
183
187
|
return wrapper
|
|
184
188
|
|