pyedb 0.7.1__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.

Files changed (32) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/dotnet/edb.py +1 -0
  3. pyedb/dotnet/edb_core/cell/hierarchy/model.py +1 -1
  4. pyedb/dotnet/edb_core/components.py +4 -1
  5. pyedb/dotnet/edb_core/configuration.py +37 -5
  6. pyedb/dotnet/edb_core/definition/component_def.py +1 -1
  7. pyedb/dotnet/edb_core/definition/component_model.py +1 -1
  8. pyedb/dotnet/edb_core/definition/definition_obj.py +1 -1
  9. pyedb/dotnet/edb_core/definition/definitions.py +8 -2
  10. pyedb/dotnet/edb_core/definition/package_def.py +34 -15
  11. pyedb/dotnet/edb_core/dotnet/database.py +5 -4
  12. pyedb/dotnet/edb_core/edb_data/components_data.py +1 -1
  13. pyedb/dotnet/edb_core/edb_data/connectable.py +1 -1
  14. pyedb/dotnet/edb_core/edb_data/layer_data.py +3 -2
  15. pyedb/dotnet/edb_core/edb_data/primitives_data.py +4 -2
  16. pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +6 -0
  17. pyedb/dotnet/edb_core/geometry/__init__.py +0 -0
  18. pyedb/dotnet/edb_core/{edb_data/obj_base.py → geometry/point_data.py} +12 -26
  19. pyedb/dotnet/edb_core/geometry/polygon_data.py +77 -0
  20. pyedb/dotnet/edb_core/materials.py +128 -106
  21. pyedb/dotnet/edb_core/nets.py +3 -3
  22. pyedb/dotnet/edb_core/obj_base.py +94 -0
  23. pyedb/dotnet/edb_core/siwave.py +2 -2
  24. pyedb/dotnet/edb_core/stackup.py +48 -41
  25. pyedb/dotnet/sim_setup_data/data/siw_dc_ir_settings.py +46 -0
  26. pyedb/edb_logger.py +15 -1
  27. pyedb/generic/settings.py +20 -8
  28. pyedb/siwave.py +50 -1
  29. {pyedb-0.7.1.dist-info → pyedb-0.8.0.dist-info}/METADATA +5 -5
  30. {pyedb-0.7.1.dist-info → pyedb-0.8.0.dist-info}/RECORD +32 -28
  31. {pyedb-0.7.1.dist-info → pyedb-0.8.0.dist-info}/LICENSE +0 -0
  32. {pyedb-0.7.1.dist-info → pyedb-0.8.0.dist-info}/WHEEL +0 -0
@@ -73,6 +73,19 @@ DC_ATTRIBUTES = [
73
73
  ]
74
74
 
75
75
 
76
+ def get_line_float_value(line):
77
+ """Retrieve the float value expected in the line of an AMAT file.
78
+
79
+ The associated string is expected to follow one of the following cases:
80
+ - simple('permittivity', 12.)
81
+ - permittivity='12'.
82
+ """
83
+ try:
84
+ return float(re.split(",|=", line)[-1].strip("'\n)"))
85
+ except ValueError:
86
+ return None
87
+
88
+
76
89
  class MaterialProperties(BaseModel):
77
90
  """Store material properties."""
78
91
 
@@ -400,7 +413,7 @@ class Material(object):
400
413
  for attribute in ATTRIBUTES:
401
414
  if attribute in input_dict:
402
415
  setattr(self, attribute, input_dict[attribute])
403
- if "loss_tangent" in input_dict:
416
+ if "loss_tangent" in input_dict: # pragma: no cover
404
417
  setattr(self, "loss_tangent", input_dict["loss_tangent"])
405
418
 
406
419
  # Update DS model
@@ -521,7 +534,7 @@ class Materials(object):
521
534
  material_def = self.__edb_definition.MaterialDef.Create(self.__edb.active_db, name)
522
535
  material = Material(self.__edb, material_def)
523
536
  attributes_input_dict = {key: val for (key, val) in kwargs.items() if key in ATTRIBUTES + DC_ATTRIBUTES}
524
- if "loss_tangent" in kwargs:
537
+ if "loss_tangent" in kwargs: # pragma: no cover
525
538
  warnings.warn(
526
539
  "This key is deprecated in versions >0.7.0 and will soon be removed. "
527
540
  "Use key dielectric_loss_tangent instead.",
@@ -550,14 +563,10 @@ class Materials(object):
550
563
  :class:`pyedb.dotnet.edb_core.materials.Material`
551
564
 
552
565
  """
553
- if name in self.__materials:
554
- raise ValueError(f"Material {name} already exists in material library.")
555
-
556
566
  extended_kwargs = {key: value for (key, value) in kwargs.items()}
557
567
  extended_kwargs["conductivity"] = conductivity
558
568
  material = self.add_material(name, **extended_kwargs)
559
569
 
560
- self.__materials[name] = material
561
570
  return material
562
571
 
563
572
  @pyedb_function_handler()
@@ -577,15 +586,11 @@ class Materials(object):
577
586
  -------
578
587
  :class:`pyedb.dotnet.edb_core.materials.Material`
579
588
  """
580
- if name in self.__materials:
581
- raise ValueError(f"Material {name} already exists in material library.")
582
-
583
589
  extended_kwargs = {key: value for (key, value) in kwargs.items()}
584
590
  extended_kwargs["permittivity"] = permittivity
585
591
  extended_kwargs["dielectric_loss_tangent"] = dielectric_loss_tangent
586
592
  material = self.add_material(name, **extended_kwargs)
587
593
 
588
- self.__materials[name] = material
589
594
  return material
590
595
 
591
596
  @pyedb_function_handler()
@@ -632,7 +637,7 @@ class Materials(object):
632
637
  material = self.__add_dielectric_material_model(name, material_model)
633
638
  for key, value in kwargs.items():
634
639
  setattr(material, key, value)
635
- if "loss_tangent" in kwargs:
640
+ if "loss_tangent" in kwargs: # pragma: no cover
636
641
  warnings.warn(
637
642
  "This key is deprecated in versions >0.7.0 and will soon be removed. "
638
643
  "Use key dielectric_loss_tangent instead.",
@@ -698,7 +703,7 @@ class Materials(object):
698
703
  material = self.__add_dielectric_material_model(name, material_model)
699
704
  for key, value in kwargs.items():
700
705
  setattr(material, key, value)
701
- if "loss_tangent" in kwargs:
706
+ if "loss_tangent" in kwargs: # pragma: no cover
702
707
  warnings.warn(
703
708
  "This key is deprecated in versions >0.7.0 and will soon be removed. "
704
709
  "Use key dielectric_loss_tangent instead.",
@@ -761,7 +766,7 @@ class Materials(object):
761
766
  material = self.__add_dielectric_material_model(name, material_model)
762
767
  for key, value in kwargs.items():
763
768
  setattr(material, key, value)
764
- if "loss_tangent" in kwargs:
769
+ if "loss_tangent" in kwargs: # pragma: no cover
765
770
  warnings.warn(
766
771
  "This key is deprecated in versions >0.7.0 and will soon be removed. "
767
772
  "Use key dielectric_loss_tangent instead.",
@@ -837,7 +842,7 @@ class Materials(object):
837
842
 
838
843
  material = self[material_name]
839
844
  attributes_input_dict = {key: val for (key, val) in input_dict.items() if key in ATTRIBUTES + DC_ATTRIBUTES}
840
- if "loss_tangent" in input_dict:
845
+ if "loss_tangent" in input_dict: # pragma: no cover
841
846
  warnings.warn(
842
847
  "This key is deprecated in versions >0.7.0 and will soon be removed. "
843
848
  "Use key dielectric_loss_tangent instead.",
@@ -852,26 +857,6 @@ class Materials(object):
852
857
  @pyedb_function_handler()
853
858
  def load_material(self, material):
854
859
  """Load material."""
855
- if self.materials:
856
- mat_keys = [i.lower() for i in self.materials.keys()]
857
- mat_keys_case = [i for i in self.materials.keys()]
858
- else:
859
- mat_keys = []
860
- mat_keys_case = []
861
-
862
- if not material:
863
- return
864
- if material["name"].lower() not in mat_keys:
865
- if "conductivity" not in material:
866
- self.add_dielectric_material(material["name"], material["permittivity"], material["loss_tangent"])
867
- elif material["conductivity"] > 1e4:
868
- self.add_conductor_material(material["name"], material["conductivity"])
869
- else:
870
- self.add_dielectric_material(material["name"], material["permittivity"], material["loss_tangent"])
871
- self.materials[material["name"]]._load(material)
872
- else:
873
- self.materials[mat_keys_case[mat_keys.index(material["name"].lower())]]._load(material)
874
-
875
860
  if material:
876
861
  material_name = material["name"]
877
862
  material_conductivity = material.get("conductivity", None)
@@ -879,7 +864,7 @@ class Materials(object):
879
864
  self.add_conductor_material(material_name, material_conductivity)
880
865
  else:
881
866
  material_permittivity = material["permittivity"]
882
- if "loss_tangent" in material:
867
+ if "loss_tangent" in material: # pragma: no cover
883
868
  warnings.warn(
884
869
  "This key is deprecated in versions >0.7.0 and will soon be removed. "
885
870
  "Use key dielectric_loss_tangent instead.",
@@ -953,7 +938,7 @@ class Materials(object):
953
938
  if "tangent_delta" in material_properties:
954
939
  material_properties["dielectric_loss_tangent"] = material_properties["tangent_delta"]
955
940
  del material_properties["tangent_delta"]
956
- elif "loss_tangent" in material_properties:
941
+ elif "loss_tangent" in material_properties: # pragma: no cover
957
942
  warnings.warn(
958
943
  "This key is deprecated in versions >0.7.0 and will soon be removed. "
959
944
  "Use key dielectric_loss_tangent instead.",
@@ -966,9 +951,83 @@ class Materials(object):
966
951
  self.__edb.logger.warning(f"Material {material_name} already exist and was not loaded from AMAT file.")
967
952
  return True
968
953
 
969
- @staticmethod
970
954
  @pyedb_function_handler()
971
- def read_materials(amat_file):
955
+ def iterate_materials_in_amat(self, amat_file=None):
956
+ """Iterate over material description in an AMAT file.
957
+
958
+ Parameters
959
+ ----------
960
+ amat_file : str
961
+ Full path to the AMAT file to read.
962
+
963
+ Yields
964
+ ------
965
+ dict
966
+ """
967
+ if amat_file is None:
968
+ amat_file = os.path.join(self.__edb.base_path, "syslib", "Materials.amat")
969
+
970
+ begin_regex = re.compile(r"^\$begin '(.+)'")
971
+ end_regex = re.compile(r"^\$end '(.+)'")
972
+ material_properties = ATTRIBUTES.copy()
973
+ # Remove cases manually handled
974
+ material_properties.remove("conductivity")
975
+
976
+ with open(amat_file, "r") as amat_fh:
977
+ in_material_def = False
978
+ material_description = {}
979
+ for line in amat_fh:
980
+ if in_material_def:
981
+ # Yield material definition
982
+ if end_regex.search(line):
983
+ in_material_def = False
984
+ yield material_description
985
+ material_description = {}
986
+ # Extend material definition if possible
987
+ else:
988
+ for material_property in material_properties:
989
+ if material_property in line:
990
+ value = get_line_float_value(line)
991
+ if value is not None:
992
+ material_description[material_property] = value
993
+ break
994
+ # Extra case to cover bug in syslib AMAT file (see #364)
995
+ if "thermal_expansion_coeffcient" in line:
996
+ value = get_line_float_value(line)
997
+ if value is not None:
998
+ material_description["thermal_expansion_coefficient"] = value
999
+ # Extra case to avoid confusion ("conductivity" is included in "thermal_conductivity")
1000
+ if "conductivity" in line and "thermal_conductivity" not in line:
1001
+ value = get_line_float_value(line)
1002
+ if value is not None:
1003
+ material_description["conductivity"] = value
1004
+ # Extra case to avoid confusion ("conductivity" is included in "thermal_conductivity")
1005
+ if (
1006
+ "loss_tangent" in line
1007
+ and "dielectric_loss_tangent" not in line
1008
+ and "magnetic_loss_tangent" not in line
1009
+ ):
1010
+ warnings.warn(
1011
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
1012
+ "Use key dielectric_loss_tangent instead.",
1013
+ DeprecationWarning,
1014
+ )
1015
+ value = get_line_float_value(line)
1016
+ if value is not None:
1017
+ material_description["dielectric_loss_tangent"] = value
1018
+ # Check if we reach the beginning of a material description
1019
+ else:
1020
+ match = begin_regex.search(line)
1021
+ if match:
1022
+ material_name = match.group(1)
1023
+ # Skip unwanted data
1024
+ if material_name in ("$index$", "$base_index$"):
1025
+ continue
1026
+ material_description["name"] = match.group(1)
1027
+ in_material_def = True
1028
+
1029
+ @pyedb_function_handler()
1030
+ def read_materials(self, amat_file):
972
1031
  """Read materials from an AMAT file.
973
1032
 
974
1033
  Parameters
@@ -981,76 +1040,39 @@ class Materials(object):
981
1040
  dict
982
1041
  {material name: dict of material properties}.
983
1042
  """
1043
+ res = {}
1044
+ for material in self.iterate_materials_in_amat(amat_file):
1045
+ material_name = material["name"]
1046
+ res[material_name] = {}
1047
+ for material_property, value in material.items():
1048
+ if material_property != "name":
1049
+ res[material_name][material_property] = value
984
1050
 
985
- def get_line_float_value(line):
986
- """Retrieve the float value expected in the line of an AMAT file.
987
-
988
- The associated string is expected to follow one of the following cases:
989
- - simple('permittivity', 12.)
990
- - permittivity='12'.
991
- """
992
- try:
993
- return float(re.split(",|=", line)[-1].strip(")'"))
994
- except ValueError:
995
- return None
1051
+ return res
996
1052
 
997
- res = {}
998
- _begin_search = re.compile(r"^\$begin '(.+)'")
999
- _end_search = re.compile(r"^\$end '(.+)'")
1000
- material_properties = [
1001
- "thermal_conductivity",
1002
- "permittivity",
1003
- "dielectric_loss_tangent",
1004
- "permeability",
1005
- "magnetic_loss_tangent",
1006
- "thermal_expansion_coeffcient",
1007
- "specific_heat",
1008
- "mass_density",
1009
- ]
1053
+ @pyedb_function_handler()
1054
+ def read_syslib_material(self, material_name):
1055
+ """Read a specific material from syslib AMAT file.
1010
1056
 
1011
- with open(amat_file, "r") as amat_fh:
1012
- raw_lines = amat_fh.read().splitlines()
1013
- material_name = ""
1014
- for line in raw_lines:
1015
- b = _begin_search.search(line)
1016
- if b: # walk down a level
1017
- material_name = b.group(1)
1018
- res.setdefault(material_name, {})
1019
- if len(res) > 165:
1020
- pass
1021
- if material_name:
1022
- for material_property in material_properties:
1023
- if material_property in line:
1024
- value = get_line_float_value(line)
1025
- if value is not None:
1026
- res[material_name][material_property] = value
1027
- break
1028
- # Extra case to avoid confusion ("conductivity" is included in "thermal_conductivity")
1029
- if "conductivity" in line and "thermal_conductivity" not in line:
1030
- value = get_line_float_value(line)
1031
- if value is not None:
1032
- res[material_name]["conductivity"] = value
1033
- # Extra case to avoid confusion ("conductivity" is included in "thermal_conductivity")
1034
- if (
1035
- "loss_tangent" in line
1036
- and "dielectric_loss_tangent" not in line
1037
- and "magnetic_loss_tangent" not in line
1038
- ):
1039
- warnings.warn(
1040
- "This key is deprecated in versions >0.7.0 and will soon be removed. "
1041
- "Use key dielectric_loss_tangent instead.",
1042
- DeprecationWarning,
1043
- )
1044
- value = get_line_float_value(line)
1045
- if value is not None:
1046
- res[material_name]["dielectric_loss_tangent"] = value
1047
- end = _end_search.search(line)
1048
- if end:
1049
- material_name = ""
1050
-
1051
- # Clean unwanted data
1052
- for key in ("$index$", "$base_index$"):
1053
- if key in res:
1054
- del res[key]
1057
+ Parameters
1058
+ ----------
1059
+ material_name : str
1060
+ Name of the material.
1055
1061
 
1062
+ Returns
1063
+ -------
1064
+ dict
1065
+ {material name: dict of material properties}.
1066
+ """
1067
+ res = {}
1068
+ amat_file = os.path.join(self.__edb.base_path, "syslib", "Materials.amat")
1069
+ for material in self.iterate_materials_in_amat(amat_file):
1070
+ iter_material_name = material["name"]
1071
+ if iter_material_name == material_name:
1072
+ for material_property, value in material.items():
1073
+ if material_property != "name":
1074
+ res[material_property] = value
1075
+ return res
1076
+
1077
+ self.__edb.logger.error(f"Material {material_name} does not exist in syslib AMAT file.")
1056
1078
  return res
@@ -472,7 +472,7 @@ class EdbNets(object):
472
472
  @pyedb_function_handler()
473
473
  def get_plot_data(
474
474
  self,
475
- nets,
475
+ nets=None,
476
476
  layers=None,
477
477
  color_by_net=False,
478
478
  outline=None,
@@ -483,8 +483,8 @@ class EdbNets(object):
483
483
 
484
484
  Parameters
485
485
  ----------
486
- nets : str, list
487
- Name of the net or list of nets to plot. If `None` all nets will be plotted.
486
+ nets : str, list, optional
487
+ Name of the net or list of nets to plot. If `None` (default value) all nets will be plotted.
488
488
  layers : str, list, optional
489
489
  Name of the layers to include in the plot. If `None` all the signal layers will be considered.
490
490
  color_by_net : bool, optional
@@ -0,0 +1,94 @@
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
+ from pyedb.dotnet.clr_module import Tuple
24
+ from pyedb.dotnet.edb_core.geometry.point_data import PointData
25
+
26
+
27
+ class BBox:
28
+ """Bounding box."""
29
+
30
+ def __init__(self, pedb, edb_object=None, point_1=None, point_2=None):
31
+ self._pedb = pedb
32
+ if edb_object:
33
+ self._edb_object = edb_object
34
+ else:
35
+ point_1 = PointData(self._pedb, x=point_1[0], y=point_1[1])
36
+ point_2 = PointData(self._pedb, x=point_2[0], y=point_2[1])
37
+ self._edb_object = Tuple[self._pedb.edb_api.Geometry.PointData, self._pedb.edb_api.Geometry.PointData](
38
+ point_1._edb_object, point_2._edb_object
39
+ )
40
+
41
+ @property
42
+ def point_1(self):
43
+ return [self._edb_object.Item1.X.ToDouble(), self._edb_object.Item1.Y.ToDouble()]
44
+
45
+ @property
46
+ def point_2(self):
47
+ return [self._edb_object.Item2.X.ToDouble(), self._edb_object.Item2.Y.ToDouble()]
48
+
49
+ @property
50
+ def corner_points(self):
51
+ return [self.point_1, self.point_2]
52
+
53
+
54
+ class ObjBase(object):
55
+ """Manages EDB functionalities for a base object."""
56
+
57
+ def __init__(self, pedb, edb_object):
58
+ self._pedb = pedb
59
+ self._edb_object = edb_object
60
+
61
+ @property
62
+ def is_null(self):
63
+ """Flag indicating if this object is null."""
64
+ return self._edb_object.IsNull()
65
+
66
+ @property
67
+ def type(self):
68
+ """Type of the edb object."""
69
+ try:
70
+ return self._edb_object.GetType()
71
+ except AttributeError: # pragma: no cover
72
+ return None
73
+
74
+ @property
75
+ def name(self):
76
+ """Name of the definition."""
77
+ return self._edb_object.GetName()
78
+
79
+ @name.setter
80
+ def name(self, value):
81
+ self._edb_object.SetName(value)
82
+
83
+ @property
84
+ def bounding_box(self):
85
+ """Bounding box.
86
+
87
+ Returns
88
+ -------
89
+ List[float]
90
+ List of coordinates for the component's bounding box, with the list of
91
+ coordinates in this order: [X lower left corner, Y lower left corner,
92
+ X upper right corner, Y upper right corner].
93
+ """
94
+ return BBox(self._pedb, self._edb_object.GetBBox()).corner_points
@@ -1196,8 +1196,8 @@ class EdbSiwave(object):
1196
1196
 
1197
1197
  Parameters
1198
1198
  ----------
1199
- pins : list[Edb.Primitive.PadstackInstance]
1200
- List of EDB pins, length must be 2, since only 2 pins component are currently supported.
1199
+ pins : list[Edb.Cell.Primitive.PadstackInstance]
1200
+ List of EDB pins.
1201
1201
 
1202
1202
  component_name : str
1203
1203
  Component name.
@@ -33,8 +33,6 @@ import logging
33
33
  import math
34
34
  import warnings
35
35
 
36
- import matplotlib.colors
37
-
38
36
  from pyedb.dotnet.edb_core.edb_data.layer_data import (
39
37
  LayerEdbClass,
40
38
  StackupLayerEdbClass,
@@ -48,9 +46,15 @@ from pyedb.generic.general_methods import (
48
46
  )
49
47
  from pyedb.misc.aedtlib_personalib_install import write_pretty_xml
50
48
 
49
+ colors = None
51
50
  pd = None
52
51
  np = None
53
52
  if not is_ironpython:
53
+ try:
54
+ import matplotlib.colors as colors
55
+ except ImportError:
56
+ colors = None
57
+
54
58
  try:
55
59
  import numpy as np
56
60
  except ImportError:
@@ -200,7 +204,7 @@ class Stackup(object):
200
204
  inner_layer_thickness="17um",
201
205
  outer_layer_thickness="50um",
202
206
  dielectric_thickness="100um",
203
- dielectric_material="fr4_epoxy",
207
+ dielectric_material="FR4_epoxy",
204
208
  soldermask=True,
205
209
  soldermask_thickness="20um",
206
210
  ): # pragma: no cover
@@ -243,7 +247,7 @@ class Stackup(object):
243
247
  self.add_layer(
244
248
  "D" + str(int(layer_count / 2)),
245
249
  None,
246
- material="fr4_epoxy",
250
+ material="FR4_epoxy",
247
251
  thickness=dielectric_thickness,
248
252
  layer_type="dielectric",
249
253
  fillMaterial=dielectric_material,
@@ -259,7 +263,7 @@ class Stackup(object):
259
263
  self.add_layer(
260
264
  "SMT",
261
265
  None,
262
- material="solder_mask",
266
+ material="SolderMask",
263
267
  thickness=soldermask_thickness,
264
268
  layer_type="dielectric",
265
269
  fillMaterial=dielectric_material,
@@ -267,14 +271,14 @@ class Stackup(object):
267
271
  self.add_layer(
268
272
  "SMB",
269
273
  None,
270
- material="solder_mask",
274
+ material="SolderMask",
271
275
  thickness=soldermask_thickness,
272
276
  layer_type="dielectric",
273
277
  fillMaterial=dielectric_material,
274
278
  method="add_on_bottom",
275
279
  )
276
- self.stackup_layers["TOP"].dielectric_fill = "solder_mask"
277
- self.stackup_layers["BOT"].dielectric_fill = "solder_mask"
280
+ self.stackup_layers["TOP"].dielectric_fill = "SolderMask"
281
+ self.stackup_layers["BOT"].dielectric_fill = "SolderMask"
278
282
 
279
283
  for layer_num in np.arange(int(layer_count / 2), 1, -1):
280
284
  # Generate upper half
@@ -634,6 +638,7 @@ class Stackup(object):
634
638
  else:
635
639
  return False
636
640
 
641
+ # TODO: Update optional argument material into material_name and fillMaterial into fill_material_name
637
642
  @pyedb_function_handler()
638
643
  def add_layer(
639
644
  self,
@@ -642,7 +647,7 @@ class Stackup(object):
642
647
  method="add_on_top",
643
648
  layer_type="signal",
644
649
  material="copper",
645
- fillMaterial="fr4_epoxy",
650
+ fillMaterial="FR4_epoxy",
646
651
  thickness="35um",
647
652
  etch_factor=None,
648
653
  is_negative=False,
@@ -686,25 +691,25 @@ class Stackup(object):
686
691
  if layer_name in self.layers:
687
692
  logger.error("layer {} exists.".format(layer_name))
688
693
  return False
689
- materials_lower = {m.lower(): m for m in list(self._pedb.materials.materials.keys())}
690
694
  if not material:
691
- if layer_type == "signal":
692
- material = "copper"
693
- else:
694
- material = "fr4_epoxy"
695
+ material = "copper" if layer_type == "signal" else "FR4_epoxy"
695
696
  if not fillMaterial:
696
- fillMaterial = "fr4_epoxy"
697
+ fillMaterial = "FR4_epoxy"
697
698
 
698
- if material.lower() not in materials_lower:
699
- logger.error(material + " does not exist in material library")
700
- else:
701
- material = materials_lower[material.lower()]
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)
702
706
 
703
- if layer_type != "dielectric":
704
- if fillMaterial.lower() not in materials_lower:
705
- logger.error(fillMaterial + " does not exist in material library")
706
- else:
707
- fillMaterial = materials_lower[fillMaterial.lower()]
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)
708
713
 
709
714
  if layer_type in ["signal", "dielectric"]:
710
715
  new_layer = self._create_stackup_layer(layer_name, thickness, layer_type)
@@ -1716,7 +1721,7 @@ class Stackup(object):
1716
1721
  "name": "default",
1717
1722
  "type": "signal",
1718
1723
  "material": "copper",
1719
- "dielectric_fill": "fr4_epoxy",
1724
+ "dielectric_fill": "FR4_epoxy",
1720
1725
  "thickness": 3.5e-05,
1721
1726
  "etch_factor": 0.0,
1722
1727
  "roughness_enabled": False,
@@ -1747,7 +1752,7 @@ class Stackup(object):
1747
1752
  "name": "default",
1748
1753
  "type": "signal",
1749
1754
  "material": "copper",
1750
- "dielectric_fill": "fr4_epoxy",
1755
+ "dielectric_fill": "FR4_epoxy",
1751
1756
  "thickness": 3.5e-05,
1752
1757
  "etch_factor": 0.0,
1753
1758
  "roughness_enabled": False,
@@ -2072,25 +2077,24 @@ class Stackup(object):
2072
2077
 
2073
2078
  @pyedb_function_handler()
2074
2079
  def _add_materials_from_dictionary(self, material_dict):
2075
- mat_keys = [i.lower() for i in self._pedb.materials.materials.keys()]
2076
- mat_keys_case = [i for i in self._pedb.materials.materials.keys()]
2077
- for name, attr in material_dict.items():
2078
- if not name.lower() in mat_keys:
2079
- if "Conductivity" in attr:
2080
- 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"])
2081
2085
  else:
2082
- self._pedb.materials.add_dielectric_material(
2086
+ materials.add_dielectric_material(
2083
2087
  name,
2084
- attr["Permittivity"],
2085
- attr["DielectricLossTangent"],
2088
+ material_properties["Permittivity"],
2089
+ material_properties["DielectricLossTangent"],
2086
2090
  )
2087
2091
  else:
2088
- local_material = self._pedb.materials[mat_keys_case[mat_keys.index(name.lower())]]
2089
- if "Conductivity" in attr:
2090
- local_material.conductivity = attr["Conductivity"]
2092
+ material = materials[name]
2093
+ if "Conductivity" in material_properties:
2094
+ material.conductivity = material_properties["Conductivity"]
2091
2095
  else:
2092
- local_material.permittivity = attr["Permittivity"]
2093
- local_material.loss_tanget = attr["DielectricLossTangent"]
2096
+ material.permittivity = material_properties["Permittivity"]
2097
+ material.loss_tanget = material_properties["DielectricLossTangent"]
2094
2098
  return True
2095
2099
 
2096
2100
  @pyedb_function_handler()
@@ -2110,6 +2114,9 @@ class Stackup(object):
2110
2114
  bool
2111
2115
  ``True`` when successful, ``False`` when failed.
2112
2116
  """
2117
+ if not colors:
2118
+ self._pedb.logger.error("Matplotlib is needed. Please, install it first.")
2119
+ return False
2113
2120
  tree = ET.parse(file_path)
2114
2121
  root = tree.getroot()
2115
2122
  stackup = root.find("Stackup")
@@ -2130,7 +2137,7 @@ class Stackup(object):
2130
2137
  layer = {"name": l.attrib["Name"]}
2131
2138
  for k, v in l.attrib.items():
2132
2139
  if k == "Color":
2133
- layer[k.lower()] = [int(x * 255) for x in list(matplotlib.colors.to_rgb(v))]
2140
+ layer[k.lower()] = [int(x * 255) for x in list(colors.to_rgb(v))]
2134
2141
  elif k == "Thickness":
2135
2142
  layer[k.lower()] = v + length_unit
2136
2143
  elif v == "conductor":