pyedb 0.6.0__py3-none-any.whl → 0.7.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.

Files changed (35) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/dotnet/clr_module.py +1 -1
  3. pyedb/dotnet/edb.py +15 -16
  4. pyedb/dotnet/edb_core/cell/hierarchy/model.py +17 -0
  5. pyedb/dotnet/edb_core/components.py +11 -11
  6. pyedb/dotnet/edb_core/configuration.py +346 -77
  7. pyedb/dotnet/edb_core/definition/component_def.py +9 -0
  8. pyedb/dotnet/edb_core/definition/package_def.py +27 -0
  9. pyedb/dotnet/edb_core/edb_data/components_data.py +8 -3
  10. pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +14 -13
  11. pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +2 -2
  12. pyedb/dotnet/edb_core/edb_data/layer_data.py +8 -3
  13. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +26 -5
  14. pyedb/dotnet/edb_core/edb_data/primitives_data.py +12 -11
  15. pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +1 -1
  16. pyedb/dotnet/edb_core/edb_data/sources.py +10 -0
  17. pyedb/dotnet/edb_core/hfss.py +1 -1
  18. pyedb/dotnet/edb_core/layout.py +94 -31
  19. pyedb/dotnet/edb_core/materials.py +637 -541
  20. pyedb/dotnet/edb_core/nets.py +5 -5
  21. pyedb/dotnet/edb_core/padstack.py +57 -6
  22. pyedb/dotnet/edb_core/siwave.py +9 -2
  23. pyedb/dotnet/edb_core/stackup.py +108 -94
  24. pyedb/dotnet/edb_core/utilities/__init__.py +3 -0
  25. pyedb/dotnet/edb_core/utilities/heatsink.py +69 -0
  26. pyedb/exceptions.py +6 -0
  27. pyedb/generic/filesystem.py +7 -3
  28. pyedb/generic/general_methods.py +4 -0
  29. pyedb/generic/process.py +4 -1
  30. pyedb/generic/settings.py +10 -0
  31. {pyedb-0.6.0.dist-info → pyedb-0.7.1.dist-info}/METADATA +31 -53
  32. {pyedb-0.6.0.dist-info → pyedb-0.7.1.dist-info}/RECORD +35 -32
  33. /pyedb/dotnet/edb_core/{edb_data → utilities}/simulation_setup.py +0 -0
  34. {pyedb-0.6.0.dist-info → pyedb-0.7.1.dist-info}/LICENSE +0 -0
  35. {pyedb-0.6.0.dist-info → pyedb-0.7.1.dist-info}/WHEEL +0 -0
@@ -1218,12 +1218,12 @@ class EdbNets(object):
1218
1218
  )
1219
1219
 
1220
1220
  @pyedb_function_handler()
1221
- def merge_nets_polygons(self, net_list):
1221
+ def merge_nets_polygons(self, net_names_list):
1222
1222
  """Convert paths from net into polygons, evaluate all connected polygons and perform the merge.
1223
1223
 
1224
1224
  Parameters
1225
1225
  ----------
1226
- net_list : str or list[str]
1226
+ net_names_list : str or list[str]
1227
1227
  Net name of list of net name.
1228
1228
 
1229
1229
  Returns
@@ -1232,6 +1232,6 @@ class EdbNets(object):
1232
1232
  ``True`` when successful, ``False`` when failed.
1233
1233
 
1234
1234
  """
1235
- if isinstance(net_list, str):
1236
- net_list = [net_list]
1237
- return self._pedb.modeler.unite_polygons_on_layer(net_list=net_list)
1235
+ if isinstance(net_names_list, str):
1236
+ net_names_list = [net_names_list]
1237
+ return self._pedb.modeler.unite_polygons_on_layer(net_names_list=net_names_list)
@@ -212,7 +212,7 @@ class EdbPadstacks(object):
212
212
 
213
213
  Returns
214
214
  -------
215
- dict[str, :class:`dotnet.edb_core.edb_data.padstacks_data.EDBPadstackInstance`]
215
+ dict[int, :class:`dotnet.edb_core.edb_data.padstacks_data.EDBPadstackInstance`]
216
216
  List of padstack instances.
217
217
 
218
218
  """
@@ -223,6 +223,24 @@ class EdbPadstacks(object):
223
223
  padstack_instances[edb_padstack_instance.GetId()] = EDBPadstackInstance(edb_padstack_instance, self._pedb)
224
224
  return padstack_instances
225
225
 
226
+ @property
227
+ def instances_by_name(self):
228
+ """Dictionary of all padstack instances (vias and pins) by name.
229
+
230
+ Returns
231
+ -------
232
+ dict[str, :class:`dotnet.edb_core.edb_data.padstacks_data.EDBPadstackInstance`]
233
+ List of padstack instances.
234
+
235
+ """
236
+ padstack_instances = {}
237
+ for _, edb_padstack_instance in self._pedb.padstacks.instances.items():
238
+ if edb_padstack_instance.aedt_name:
239
+ padstack_instances[edb_padstack_instance.aedt_name] = EDBPadstackInstance(
240
+ edb_padstack_instance, self._pedb
241
+ )
242
+ return padstack_instances
243
+
226
244
  @property
227
245
  def pins(self):
228
246
  """Dictionary of all pins instances (belonging to component).
@@ -1332,6 +1350,42 @@ class EdbPadstacks(object):
1332
1350
  self.definitions[padstack_name].edb_padstack.SetData(new_padstack_def)
1333
1351
  return True
1334
1352
 
1353
+ @pyedb_function_handler()
1354
+ def get_instances(self, name=None, pid=None, definition_name=None, net_name=None):
1355
+ """Get padstack instances by conditions.
1356
+
1357
+ Parameters
1358
+ ----------
1359
+ name : str, optional
1360
+ Name of the padstack.
1361
+ pid : int, optional
1362
+ Id of the padstack.
1363
+ definition_name : str, list, optional
1364
+ Name of the padstack definition.
1365
+ net_name : str, optional
1366
+ The net name to be used for filtering padstack instances.
1367
+
1368
+ Returns
1369
+ -------
1370
+ list
1371
+ List of :class:`dotnet.edb_core.edb_data.padstacks_data.EDBPadstackInstance`.
1372
+ """
1373
+
1374
+ instances_by_id = self.instances
1375
+ if pid:
1376
+ return instances_by_id[pid]
1377
+ elif name:
1378
+ return self.instances_by_name[name]
1379
+ else:
1380
+ instances = list(instances_by_id.values())
1381
+ if definition_name:
1382
+ definition_name = definition_name if isinstance(definition_name, list) else [definition_name]
1383
+ instances = [inst for inst in instances if inst.padstack_definition in definition_name]
1384
+ if net_name:
1385
+ net_name = net_name if isinstance(net_name, list) else [net_name]
1386
+ instances = [inst for inst in instances if inst.net_name in net_name]
1387
+ return instances
1388
+
1335
1389
  @pyedb_function_handler()
1336
1390
  def get_padstack_instance_by_net_name(self, net_name):
1337
1391
  """Get a list of padstack instances by net name.
@@ -1346,11 +1400,8 @@ class EdbPadstacks(object):
1346
1400
  list
1347
1401
  List of :class:`dotnet.edb_core.edb_data.padstacks_data.EDBPadstackInstance`.
1348
1402
  """
1349
- padstack_instances = []
1350
- for inst_id, inst in self.instances.items():
1351
- if inst.net_name == net_name:
1352
- padstack_instances.append(inst)
1353
- return padstack_instances
1403
+ warnings.warn("Use new property :func:`get_padstack_instance` instead.", DeprecationWarning)
1404
+ return self.get_instances(net_name=net_name)
1354
1405
 
1355
1406
  @pyedb_function_handler()
1356
1407
  def get_reference_pins(
@@ -27,6 +27,7 @@ This module contains these classes: ``CircuitPort``, ``CurrentSource``, ``EdbSiw
27
27
  import os
28
28
  import time
29
29
 
30
+ from pyedb.dotnet.edb_core.edb_data.padstacks_data import EDBPadstackInstance
30
31
  from pyedb.dotnet.edb_core.edb_data.simulation_configuration import (
31
32
  SimulationConfiguration,
32
33
  SourceType,
@@ -140,8 +141,14 @@ class EdbSiwave(object):
140
141
  Name of the source.
141
142
 
142
143
  """
143
- pos_pin = source.positive_node.node_pins
144
- neg_pin = source.negative_node.node_pins
144
+ if isinstance(source.positive_node.node_pins, EDBPadstackInstance):
145
+ pos_pin = source.positive_node.node_pins._edb_padstackinstance
146
+ else:
147
+ pos_pin = source.positive_node.node_pins
148
+ if isinstance(source.negative_node.node_pins, EDBPadstackInstance):
149
+ neg_pin = source.negative_node.node_pins._edb_padstackinstance
150
+ else:
151
+ neg_pin = source.negative_node.node_pins
145
152
 
146
153
  res, fromLayer_pos, toLayer_pos = pos_pin.GetLayerRange()
147
154
  res, fromLayer_neg, toLayer_neg = neg_pin.GetLayerRange()
@@ -31,9 +31,10 @@ 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
 
36
+ import matplotlib.colors
37
+
37
38
  from pyedb.dotnet.edb_core.edb_data.layer_data import (
38
39
  LayerEdbClass,
39
40
  StackupLayerEdbClass,
@@ -878,6 +879,7 @@ class Stackup(object):
878
879
  else:
879
880
  return False
880
881
 
882
+ # TODO: This method might need some refactoring
881
883
  @pyedb_function_handler()
882
884
  def _import_layer_stackup(self, input_file=None):
883
885
  if input_file:
@@ -886,7 +888,12 @@ class Stackup(object):
886
888
  for k, v in json_dict.items():
887
889
  if k == "materials":
888
890
  for material in v.values():
889
- self._pedb.materials._load_materials(material)
891
+ material_name = material["name"]
892
+ del material["name"]
893
+ if material_name not in self._pedb.materials:
894
+ self._pedb.materials.add_material(material_name, **material)
895
+ else:
896
+ self._pedb.materials.update_material(material_name, material)
890
897
  if k == "layers":
891
898
  if len(list(v.values())) == len(list(self.stackup_layers.values())):
892
899
  imported_layers_list = [l_dict["name"] for l_dict in list(v.values())]
@@ -1667,36 +1674,63 @@ class Stackup(object):
1667
1674
  temp_data = {name: area / outline_area * 100 for name, area in temp_data.items()}
1668
1675
  return temp_data
1669
1676
 
1677
+ # TODO: This method might need some refactoring
1670
1678
  @pyedb_function_handler()
1671
- def _import_dict(self, json_dict):
1679
+ def _import_dict(self, json_dict, rename=False):
1672
1680
  """Import stackup from a dictionary."""
1673
- mats = json_dict["materials"]
1674
- for material in mats.values():
1675
- self._pedb.materials._load_materials(material)
1676
-
1681
+ if not "materials" in json_dict:
1682
+ self._logger.warning("Configuration file does not have material definition")
1683
+ self._logger.warning(
1684
+ "Please check your json or xml file, if no material are defined your project will"
1685
+ "likely fail to simulate"
1686
+ )
1687
+ else:
1688
+ mats = json_dict["materials"]
1689
+ for material in mats.values():
1690
+ material_name = material["name"]
1691
+ del material["name"]
1692
+ if material_name not in self._pedb.materials:
1693
+ self._pedb.materials.add_material(material_name, **material)
1694
+ else:
1695
+ self._pedb.materials.update_material(material_name, material)
1677
1696
  temp = {i: j for i, j in json_dict["layers"].items() if j["type"] in ["signal", "dielectric"]}
1697
+ config_file_layers = list(temp.keys())
1698
+ layout_layers = list(self.stackup_layers.keys())
1699
+ renamed_layers = {}
1700
+ if rename and len(config_file_layers) == len(layout_layers):
1701
+ for lay_ind in range(len(list(temp.keys()))):
1702
+ if not config_file_layers[lay_ind] == layout_layers[lay_ind]:
1703
+ renamed_layers[layout_layers[lay_ind]] = config_file_layers[lay_ind]
1678
1704
  for name in list(self.stackup_layers.keys()):
1705
+ layer = None
1679
1706
  if name in temp:
1680
1707
  layer = temp[name]
1681
- default_layer = {
1682
- "name": "default",
1683
- "type": "signal",
1684
- "material": "copper",
1685
- "dielectric_fill": "fr4_epoxy",
1686
- "thickness": 3.5e-05,
1687
- "etch_factor": 0.0,
1688
- "roughness_enabled": False,
1689
- "top_hallhuray_nodule_radius": 0.0,
1690
- "top_hallhuray_surface_ratio": 0.0,
1691
- "bottom_hallhuray_nodule_radius": 0.0,
1692
- "bottom_hallhuray_surface_ratio": 0.0,
1693
- "side_hallhuray_nodule_radius": 0.0,
1694
- "side_hallhuray_surface_ratio": 0.0,
1695
- "upper_elevation": 0.0,
1696
- "lower_elevation": 0.0,
1697
- "color": [242, 140, 102],
1698
- }
1699
-
1708
+ elif name in renamed_layers:
1709
+ layer = temp[renamed_layers[name]]
1710
+ self.stackup_layers[name].name = renamed_layers[name]
1711
+ name = renamed_layers[name]
1712
+ else: # Remove layers not in config file.
1713
+ self.remove_layer(name)
1714
+ self._logger.warning(f"Layer {name} were not found in configuration file, removing layer")
1715
+ default_layer = {
1716
+ "name": "default",
1717
+ "type": "signal",
1718
+ "material": "copper",
1719
+ "dielectric_fill": "fr4_epoxy",
1720
+ "thickness": 3.5e-05,
1721
+ "etch_factor": 0.0,
1722
+ "roughness_enabled": False,
1723
+ "top_hallhuray_nodule_radius": 0.0,
1724
+ "top_hallhuray_surface_ratio": 0.0,
1725
+ "bottom_hallhuray_nodule_radius": 0.0,
1726
+ "bottom_hallhuray_surface_ratio": 0.0,
1727
+ "side_hallhuray_nodule_radius": 0.0,
1728
+ "side_hallhuray_surface_ratio": 0.0,
1729
+ "upper_elevation": 0.0,
1730
+ "lower_elevation": 0.0,
1731
+ "color": [242, 140, 102],
1732
+ }
1733
+ if layer:
1700
1734
  if "color" in layer:
1701
1735
  default_layer["color"] = layer["color"]
1702
1736
  elif not layer["type"] == "signal":
@@ -1704,15 +1738,11 @@ class Stackup(object):
1704
1738
 
1705
1739
  for k, v in layer.items():
1706
1740
  default_layer[k] = v
1707
-
1708
1741
  self.stackup_layers[name]._load_layer(default_layer)
1709
- else: # Remove layers not in config file.
1710
- self.remove_layer(name)
1711
-
1712
- for layer_name, layer in temp.items():
1742
+ for layer_name, layer in temp.items(): # looping over potential new layers to add
1713
1743
  if layer_name in self.stackup_layers:
1714
1744
  continue # if layer exist, skip
1715
-
1745
+ # adding layer
1716
1746
  default_layer = {
1717
1747
  "name": "default",
1718
1748
  "type": "signal",
@@ -1786,12 +1816,12 @@ class Stackup(object):
1786
1816
  return True
1787
1817
 
1788
1818
  @pyedb_function_handler()
1789
- def _import_json(self, file_path):
1819
+ def _import_json(self, file_path, rename=False):
1790
1820
  """Import stackup from a json file."""
1791
1821
  if file_path:
1792
1822
  f = open(file_path)
1793
1823
  json_dict = json.load(f) # pragma: no cover
1794
- return self._import_dict(json_dict)
1824
+ return self._import_dict(json_dict, rename)
1795
1825
 
1796
1826
  @pyedb_function_handler()
1797
1827
  def _import_csv(self, file_path):
@@ -2035,7 +2065,7 @@ class Stackup(object):
2035
2065
  material["Conductivity"] = val.conductivity
2036
2066
  else:
2037
2067
  material["Permittivity"] = val.permittivity
2038
- material["DielectricLossTangent"] = val.loss_tangent
2068
+ material["DielectricLossTangent"] = val.dielectric_loss_tangent
2039
2069
  materials[name] = material
2040
2070
 
2041
2071
  return layers, materials, roughness_models, non_stackup_layers
@@ -2064,11 +2094,11 @@ class Stackup(object):
2064
2094
  return True
2065
2095
 
2066
2096
  @pyedb_function_handler()
2067
- def _import_xml(self, file_path):
2068
- """Read external xml file and update stackup.
2069
- 1, all existing layers must exist in xml file.
2070
- 2, xml can have more layers than the existing stackup.
2071
- 3, if xml has different layer order, reorder the layers according to xml definition.
2097
+ def _import_xml(self, file_path, rename=False):
2098
+ """Read external xml file and convert into json file.
2099
+ You can use xml file to import layer stackup but using json file is recommended.
2100
+ see :class:`pyedb.dotnet.edb_core.edb_data.simulation_configuration.SimulationConfiguration´ class to
2101
+ generate files`.
2072
2102
 
2073
2103
  Parameters
2074
2104
  ----------
@@ -2081,59 +2111,38 @@ class Stackup(object):
2081
2111
  ``True`` when successful, ``False`` when failed.
2082
2112
  """
2083
2113
  tree = ET.parse(file_path)
2084
- material_dict = {}
2085
2114
  root = tree.getroot()
2086
2115
  stackup = root.find("Stackup")
2087
- for m in stackup.find("Materials").findall("Material"):
2088
- material = {}
2089
- for i in list(m):
2090
- material[i.tag] = list(i)[0].text
2091
- material_dict[m.attrib["Name"]] = material
2092
-
2093
- self._add_materials_from_dictionary(material_dict)
2094
-
2095
- lc_import = self._pedb.edb_api.Cell.LayerCollection()
2096
-
2097
- if not lc_import.ImportFromControlFile(file_path): # pragma: no cover
2098
- logger.error("Import xml failed. Please check xml content.")
2099
- return False
2100
-
2101
- if not len(self.stackup_layers):
2102
- self._pedb.layout.layer_collection = lc_import
2103
- self.refresh_layer_collection()
2104
- return True
2105
-
2106
- dumy_layers = OrderedDict()
2107
- for i in list(lc_import.Layers(self._pedb.edb_api.cell.layer_type_set.AllLayerSet)):
2108
- dumy_layers[i.GetName()] = i.Clone()
2109
-
2110
- for name in self.layers.keys():
2111
- if not name in dumy_layers:
2112
- logger.error("{} doesn't exist in xml".format(name))
2113
- return False
2114
-
2115
- for name, l in dumy_layers.items():
2116
- layer_type = re.sub(r"Layer$", "", l.GetLayerType().ToString()).lower()
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
2116
+ stackup_dict = {}
2117
+ if stackup.find("Materials"):
2118
+ stackup_dict["materials"] = {}
2119
+ for m in stackup.find("Materials").findall("Material"):
2120
+ material = {"name": m.attrib["Name"]}
2121
+ for i in list(m):
2122
+ material[i.tag.lower()] = float(list(i)[0].text)
2123
+ if material:
2124
+ stackup_dict["materials"][material["name"]] = material
2125
+ stackup_section = stackup.find("Layers")
2126
+ if stackup_section:
2127
+ length_unit = stackup_section.attrib["LengthUnit"]
2128
+ stackup_dict["layers"] = {}
2129
+ for l in stackup.find("Layers").findall("Layer"):
2130
+ layer = {"name": l.attrib["Name"]}
2131
+ for k, v in l.attrib.items():
2132
+ if k == "Color":
2133
+ layer[k.lower()] = [int(x * 255) for x in list(matplotlib.colors.to_rgb(v))]
2134
+ elif k == "Thickness":
2135
+ layer[k.lower()] = v + length_unit
2136
+ elif v == "conductor":
2137
+ layer[k.lower()] = "signal"
2138
+ elif k == "FillMaterial":
2139
+ layer["dielectric_fill"] = v
2140
+ else:
2141
+ layer[k.lower()] = v
2142
+ if layer:
2143
+ if layer["type"] == "signal" or layer["type"] == "dielectric":
2144
+ stackup_dict["layers"][layer["name"]] = layer
2145
+ return self._import_dict(stackup_dict, rename=rename)
2137
2146
 
2138
2147
  @pyedb_function_handler()
2139
2148
  def _export_xml(self, file_path):
@@ -2187,7 +2196,7 @@ class Stackup(object):
2187
2196
  return True
2188
2197
 
2189
2198
  @pyedb_function_handler()
2190
- def load(self, file_path):
2199
+ def load(self, file_path, rename=False):
2191
2200
  """Import stackup from a file. The file format can be XML, CSV, or JSON.
2192
2201
 
2193
2202
 
@@ -2195,6 +2204,11 @@ class Stackup(object):
2195
2204
  ----------
2196
2205
  file_path : str
2197
2206
  Path to stackup file.
2207
+ rename : bool
2208
+ If rename is ``False`` then layer in layout not found in the stackup file are deleted.
2209
+ Otherwise, if the number of layer in the stackup file equals the number of stackup layer
2210
+ in the layout, layers are renamed according the the file.
2211
+ Note that layer order matters, and has to be writtent from top to bottom layer in the file.
2198
2212
 
2199
2213
  Returns
2200
2214
  -------
@@ -2213,9 +2227,9 @@ class Stackup(object):
2213
2227
  elif file_path.endswith(".csv"):
2214
2228
  return self._import_csv(file_path)
2215
2229
  elif file_path.endswith(".json"):
2216
- return self._import_json(file_path)
2230
+ return self._import_json(file_path, rename=rename)
2217
2231
  elif file_path.endswith(".xml"):
2218
- return self._import_xml(file_path)
2232
+ return self._import_xml(file_path, rename=rename)
2219
2233
  else:
2220
2234
  return False
2221
2235
 
@@ -0,0 +1,3 @@
1
+ from pathlib import Path
2
+
3
+ workdir = Path(__file__).parent
@@ -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)
pyedb/exceptions.py ADDED
@@ -0,0 +1,6 @@
1
+ """
2
+ """
3
+
4
+
5
+ class MaterialModelException(Exception):
6
+ """Exception triggered when handling material model."""
@@ -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
- copy_tree(src_folder, destfolder)
128
- return True
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
@@ -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
 
pyedb/generic/process.py CHANGED
@@ -95,7 +95,10 @@ class SiwaveSolve(object):
95
95
  command.append(self._project_path)
96
96
  command.append(exec_file)
97
97
  command.append("-formatOutput -useSubdir")
98
- p = subprocess.Popen(" ".join(command))
98
+ if os.name == "posix":
99
+ p = subprocess.Popen(command)
100
+ else:
101
+ p = subprocess.Popen(" ".join(command))
99
102
  p.wait()
100
103
 
101
104
  def export_3d_cad(
pyedb/generic/settings.py CHANGED
@@ -52,6 +52,7 @@ class Settings(object):
52
52
  self._force_error_on_missing_project = False
53
53
  self._enable_pandas_output = False
54
54
  self.time_tick = time.time()
55
+ self.retry_n_times_time_interval = 0.1
55
56
  self._global_log_file_name = "pyedb_{}.log".format(os.path.split(os.path.expanduser("~"))[-1])
56
57
  self._enable_global_log_file = True
57
58
  self._enable_local_log_file = False
@@ -240,5 +241,14 @@ class Settings(object):
240
241
  if os.path.exists(value):
241
242
  self._edb_dll_path = value
242
243
 
244
+ @property
245
+ def retry_n_times_time_interval(self):
246
+ """Time interval between the retries by the ``_retry_n_times`` method."""
247
+ return self._retry_n_times_time_interval
248
+
249
+ @retry_n_times_time_interval.setter
250
+ def retry_n_times_time_interval(self, value):
251
+ self._retry_n_times_time_interval = float(value)
252
+
243
253
 
244
254
  settings = Settings()