pyedb 0.54.0__py3-none-any.whl → 0.55.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 (95) hide show
  1. pyedb/__init__.py +1 -8
  2. pyedb/configuration/cfg_boundaries.py +69 -151
  3. pyedb/configuration/cfg_components.py +201 -460
  4. pyedb/configuration/cfg_data.py +4 -2
  5. pyedb/configuration/cfg_general.py +13 -36
  6. pyedb/configuration/cfg_modeler.py +2 -1
  7. pyedb/configuration/cfg_nets.py +21 -35
  8. pyedb/configuration/cfg_operations.py +22 -151
  9. pyedb/configuration/cfg_package_definition.py +56 -112
  10. pyedb/configuration/cfg_padstacks.py +292 -688
  11. pyedb/configuration/cfg_pin_groups.py +32 -79
  12. pyedb/configuration/cfg_ports_sources.py +19 -6
  13. pyedb/configuration/cfg_s_parameter_models.py +67 -172
  14. pyedb/configuration/cfg_setup.py +102 -295
  15. pyedb/configuration/configuration.py +64 -5
  16. pyedb/dotnet/database/cell/connectable.py +38 -9
  17. pyedb/dotnet/database/cell/hierarchy/component.py +28 -28
  18. pyedb/dotnet/database/cell/hierarchy/model.py +1 -1
  19. pyedb/dotnet/database/cell/layout.py +63 -2
  20. pyedb/dotnet/database/cell/layout_obj.py +2 -2
  21. pyedb/dotnet/database/cell/primitive/path.py +6 -8
  22. pyedb/dotnet/database/cell/primitive/primitive.py +3 -24
  23. pyedb/dotnet/database/cell/terminal/edge_terminal.py +2 -2
  24. pyedb/dotnet/database/cell/terminal/padstack_instance_terminal.py +1 -1
  25. pyedb/dotnet/database/cell/terminal/pingroup_terminal.py +1 -1
  26. pyedb/dotnet/database/cell/terminal/point_terminal.py +1 -1
  27. pyedb/dotnet/database/cell/terminal/terminal.py +24 -24
  28. pyedb/dotnet/database/cell/voltage_regulator.py +0 -21
  29. pyedb/dotnet/database/components.py +96 -88
  30. pyedb/dotnet/database/definition/component_def.py +4 -4
  31. pyedb/dotnet/database/definition/component_model.py +1 -1
  32. pyedb/dotnet/database/definition/package_def.py +2 -3
  33. pyedb/dotnet/database/dotnet/database.py +3 -199
  34. pyedb/dotnet/database/dotnet/primitive.py +3 -3
  35. pyedb/dotnet/database/edb_data/control_file.py +5 -5
  36. pyedb/dotnet/database/edb_data/hfss_extent_info.py +6 -6
  37. pyedb/dotnet/database/edb_data/layer_data.py +23 -23
  38. pyedb/dotnet/database/edb_data/padstacks_data.py +63 -88
  39. pyedb/dotnet/database/edb_data/primitives_data.py +5 -5
  40. pyedb/dotnet/database/edb_data/sources.py +6 -6
  41. pyedb/dotnet/database/edb_data/variables.py +1 -1
  42. pyedb/dotnet/database/geometry/point_data.py +14 -10
  43. pyedb/dotnet/database/geometry/polygon_data.py +3 -3
  44. pyedb/dotnet/database/hfss.py +46 -48
  45. pyedb/dotnet/database/layout_validation.py +14 -11
  46. pyedb/dotnet/database/materials.py +10 -11
  47. pyedb/dotnet/database/modeler.py +97 -91
  48. pyedb/dotnet/database/nets.py +19 -22
  49. pyedb/dotnet/database/padstack.py +84 -83
  50. pyedb/dotnet/database/siwave.py +42 -42
  51. pyedb/dotnet/database/stackup.py +140 -72
  52. pyedb/dotnet/database/utilities/heatsink.py +4 -4
  53. pyedb/dotnet/database/utilities/obj_base.py +2 -2
  54. pyedb/dotnet/database/utilities/simulation_setup.py +2 -2
  55. pyedb/dotnet/database/utilities/value.py +16 -16
  56. pyedb/dotnet/edb.py +228 -150
  57. pyedb/edb_logger.py +12 -27
  58. pyedb/extensions/via_design_backend.py +6 -3
  59. pyedb/generic/design_types.py +67 -29
  60. pyedb/generic/general_methods.py +0 -120
  61. pyedb/generic/process.py +44 -108
  62. pyedb/generic/settings.py +75 -19
  63. pyedb/grpc/database/components.py +2 -0
  64. pyedb/grpc/database/control_file.py +5 -5
  65. pyedb/grpc/database/definition/materials.py +1 -1
  66. pyedb/grpc/database/definition/package_def.py +3 -3
  67. pyedb/grpc/database/definition/padstack_def.py +53 -0
  68. pyedb/grpc/database/geometry/polygon_data.py +1 -1
  69. pyedb/grpc/database/layout/layout.py +8 -5
  70. pyedb/grpc/database/layout_validation.py +3 -3
  71. pyedb/grpc/database/modeler.py +9 -4
  72. pyedb/grpc/database/net/net.py +15 -14
  73. pyedb/grpc/database/nets.py +70 -0
  74. pyedb/grpc/database/padstacks.py +35 -17
  75. pyedb/grpc/database/primitive/padstack_instance.py +175 -7
  76. pyedb/grpc/database/siwave.py +1 -1
  77. pyedb/grpc/database/source_excitations.py +2 -4
  78. pyedb/grpc/database/stackup.py +1 -1
  79. pyedb/grpc/database/terminal/bundle_terminal.py +1 -1
  80. pyedb/grpc/database/terminal/padstack_instance_terminal.py +1 -1
  81. pyedb/grpc/database/terminal/pingroup_terminal.py +1 -1
  82. pyedb/grpc/database/utility/xml_control_file.py +5 -5
  83. pyedb/grpc/edb.py +73 -27
  84. pyedb/grpc/edb_init.py +3 -3
  85. pyedb/grpc/rpc_session.py +10 -10
  86. pyedb/libraries/common.py +366 -0
  87. pyedb/libraries/rf_libraries/base_functions.py +1358 -0
  88. pyedb/libraries/rf_libraries/planar_antennas.py +628 -0
  89. pyedb/misc/decorators.py +61 -0
  90. pyedb/misc/misc.py +0 -13
  91. pyedb/siwave.py +2 -2
  92. {pyedb-0.54.0.dist-info → pyedb-0.55.0.dist-info}/METADATA +1 -2
  93. {pyedb-0.54.0.dist-info → pyedb-0.55.0.dist-info}/RECORD +95 -91
  94. {pyedb-0.54.0.dist-info → pyedb-0.55.0.dist-info}/WHEEL +0 -0
  95. {pyedb-0.54.0.dist-info → pyedb-0.55.0.dist-info}/licenses/LICENSE +0 -0
pyedb/generic/settings.py CHANGED
@@ -21,12 +21,24 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  import os
24
+ import re
25
+ import sys
24
26
  import time
27
+ import warnings
25
28
 
26
29
 
27
30
  class Settings(object):
28
31
  """Manages all PyEDB environment variables and global settings."""
29
32
 
33
+ INSTALLED_VERSIONS = None
34
+ INSTALLED_STUDENT_VERSIONS = None
35
+ INSTALLED_CLIENT_VERSIONS = None
36
+ LATEST_VERSION = None
37
+ LATEST_STUDENT_VERSION = None
38
+
39
+ specified_version = None
40
+ is_student_version = False
41
+
30
42
  def __init__(self):
31
43
  self.remote_rpc_session = False
32
44
  self._enable_screen_logs = True
@@ -58,26 +70,12 @@ class Settings(object):
58
70
  self._global_log_file_size = 10
59
71
  self._lsf_queue = None
60
72
  self._edb_environment_variables = {}
61
- self._use_pyaedt_log = False
62
- self._logger = None
63
-
64
- @property
65
- def logger(self):
66
- """Active logger."""
67
- return self._logger
68
-
69
- @logger.setter
70
- def logger(self, val):
71
- self._logger = val
73
+ self.logger = None
74
+ self.log_file = None
75
+ self._aedt_version = None
72
76
 
73
- @property
74
- def use_pyaedt_log(self):
75
- """Flag that disable Edb log when PyAEDT is used."""
76
- return self._use_pyaedt_log
77
-
78
- @use_pyaedt_log.setter
79
- def use_pyaedt_log(self, value):
80
- self._use_pyaedt_log = value
77
+ self.__get_version_information()
78
+ self.__init_logger()
81
79
 
82
80
  @property
83
81
  def edb_environment_variables(self):
@@ -261,5 +259,63 @@ class Settings(object):
261
259
  def retry_n_times_time_interval(self, value):
262
260
  self._retry_n_times_time_interval = float(value)
263
261
 
262
+ def __get_version_information(self):
263
+ """Get the installed AEDT versions.
264
+
265
+ This method returns a dictionary, with the version as the key and the installation path
266
+ as the value."""
267
+ version_pattern = re.compile(r"^(ANSYSEM_ROOT|ANSYSEM_PY_CLIENT_ROOT|ANSYSEMSV_ROOT)\d{3}$")
268
+ env_list = sorted([x for x in os.environ if version_pattern.match(x)], reverse=True)
269
+ if not env_list: # pragma: no cover
270
+ warnings.warn("No installed versions of AEDT are found in the system environment variables.")
271
+ return
272
+
273
+ aedt_system_env_variables = {i: os.environ[i] for i in env_list}
274
+
275
+ standard_versions = {}
276
+ client_versions = {}
277
+ student_versions = {}
278
+ # version_list is ordered: first normal versions, then client versions, finally student versions
279
+ for var_name, aedt_path in aedt_system_env_variables.items():
280
+ version_id = var_name[-3:]
281
+ version, release = version_id[0:2], version_id[2]
282
+ version_name = f"20{version}.{release}"
283
+ if "ANSYSEM_ROOT" in var_name:
284
+ standard_versions[version_name] = aedt_path
285
+ elif "ANSYSEM_PY_CLIENT_ROOT" in var_name:
286
+ client_versions[version_name] = aedt_path
287
+ else:
288
+ student_versions[version_name] = aedt_path
289
+ self.INSTALLED_VERSIONS = standard_versions
290
+ self.INSTALLED_STUDENT_VERSIONS = student_versions
291
+ self.INSTALLED_CLIENT_VERSIONS = client_versions
292
+
293
+ if len(self.INSTALLED_VERSIONS):
294
+ self.LATEST_VERSION = max(standard_versions.keys(), key=lambda x: tuple(map(int, x.split("."))))
295
+ if len(self.INSTALLED_STUDENT_VERSIONS):
296
+ self.LATEST_STUDENT_VERSION = max(student_versions.keys(), key=lambda x: tuple(map(int, x.split("."))))
297
+
298
+ @property
299
+ def aedt_installation_path(self):
300
+ if self.edb_dll_path:
301
+ return self.edb_dll_path
302
+ elif self.is_student_version:
303
+ return self.INSTALLED_STUDENT_VERSIONS[self.specified_version]
304
+ elif self.specified_version in self.INSTALLED_VERSIONS.keys():
305
+ return self.INSTALLED_VERSIONS[self.specified_version]
306
+ elif os.name == "posix":
307
+ main = sys.modules["__main__"]
308
+ if "oDesktop" in dir(main):
309
+ return main.oDesktop.GetExeDir()
310
+ else:
311
+ raise RuntimeError(f"Version {self.specified_version} is not installed on the system. ")
312
+ else:
313
+ raise RuntimeError(f"Version {self.specified_version} is not installed on the system. ")
314
+
315
+ def __init_logger(self):
316
+ from pyedb.edb_logger import EdbLogger
317
+
318
+ self.logger = EdbLogger(to_stdout=self.enable_screen_logs, settings=self)
319
+
264
320
 
265
321
  settings = Settings()
@@ -57,6 +57,7 @@ from pyedb.grpc.database.hierarchy.pingroup import PinGroup
57
57
  from pyedb.grpc.database.padstacks import Padstacks
58
58
  from pyedb.grpc.database.utility.sources import SourceType
59
59
  from pyedb.grpc.database.utility.value import Value
60
+ from pyedb.misc.decorators import deprecate_argument_name
60
61
  from pyedb.modeler.geometry_operators import GeometryOperators
61
62
 
62
63
 
@@ -500,6 +501,7 @@ class Components(object):
500
501
  """
501
502
  return self.instances[name]
502
503
 
504
+ @deprecate_argument_name({"pinName": "pin_name"})
503
505
  def get_pin_from_component(
504
506
  self,
505
507
  component: Union[str, Component],
@@ -27,8 +27,8 @@ import subprocess
27
27
  import sys
28
28
  from typing import Any, Dict, List, Optional, Union
29
29
 
30
- from pyedb.edb_logger import pyedb_logger
31
30
  from pyedb.generic.general_methods import ET, env_path, env_value, is_linux
31
+ from pyedb.generic.settings import settings
32
32
  from pyedb.misc.aedtlib_personalib_install import write_pretty_xml
33
33
  from pyedb.misc.misc import list_installed_ansysem
34
34
 
@@ -194,7 +194,7 @@ def convert_technology_file(tech_file, edbversion=None, control_file=None):
194
194
  base_path = env_path(edbversion)
195
195
  sys.path.append(base_path)
196
196
  else:
197
- pyedb_logger.error("No EDB installation found. Check environment variables")
197
+ settings.logger.error("No EDB installation found. Check environment variables")
198
198
  return False
199
199
  os.environ["HELIC_ROOT"] = os.path.join(base_path, "helic")
200
200
  if os.getenv("ANSYSLMD_LICENCE_FILE", None) is None:
@@ -207,7 +207,7 @@ def convert_technology_file(tech_file, edbversion=None, control_file=None):
207
207
  os.environ["ANSYSLMD_LICENSE_FILE"] = line.split("=")[1]
208
208
  break
209
209
  else:
210
- pyedb_logger.error("ANSYSLMD_LICENSE_FILE is not defined.")
210
+ settings.logger.error("ANSYSLMD_LICENSE_FILE is not defined.")
211
211
  vlc_file_name = os.path.splitext(tech_file)[0]
212
212
  if not control_file:
213
213
  control_file = vlc_file_name + ".xml"
@@ -241,9 +241,9 @@ def convert_technology_file(tech_file, edbversion=None, control_file=None):
241
241
  p = subprocess.Popen(command, env=my_env)
242
242
  p.wait()
243
243
  if os.path.exists(control_file):
244
- pyedb_logger.info("XML file created.")
244
+ settings.logger.info("XML file created.")
245
245
  return control_file
246
- pyedb_logger.error("Technology files are supported only in Linux. Use control file instead.")
246
+ settings.logger.error("Technology files are supported only in Linux. Use control file instead.")
247
247
  return False
248
248
 
249
249
 
@@ -378,7 +378,7 @@ class Material(GrpcMaterialDef):
378
378
  "Use property dielectric_loss_tangent instead.",
379
379
  DeprecationWarning,
380
380
  )
381
- self.dielectric_loss_tangent(value)
381
+ self.dielectric_loss_tangent = value
382
382
 
383
383
  @dielectric_loss_tangent.setter
384
384
  def dielectric_loss_tangent(self, value):
@@ -23,10 +23,10 @@
23
23
  from ansys.edb.core.definition.package_def import PackageDef as GrpcPackageDef
24
24
  from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
25
25
 
26
- from pyedb.edb_logger import pyedb_logger
26
+ from pyedb.generic.settings import settings
27
27
  from pyedb.grpc.database.utility.heat_sink import HeatSink
28
28
  from pyedb.grpc.database.utility.value import Value
29
- from pyedb.misc.misc import deprecated_property
29
+ from pyedb.misc.decorators import deprecated_property
30
30
 
31
31
 
32
32
  class PackageDef(GrpcPackageDef):
@@ -82,7 +82,7 @@ class PackageDef(GrpcPackageDef):
82
82
  else:
83
83
  bbox = extent_bounding_box
84
84
  if bbox is None:
85
- pyedb_logger.warning(
85
+ settings.logger.warning(
86
86
  "Package creation uses bounding box but it cannot be inferred. "
87
87
  "Please set argument 'component_part_name' or 'extent_bounding_box'."
88
88
  )
@@ -21,6 +21,7 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  import math
24
+ import warnings
24
25
 
25
26
  from ansys.edb.core.definition.padstack_def import PadstackDef as GrpcPadstackDef
26
27
  from ansys.edb.core.definition.padstack_def_data import (
@@ -109,6 +110,26 @@ class PadProperties:
109
110
  """
110
111
  return self._pad_parameter_value[0].name.split("_")[-1].lower()
111
112
 
113
+ @shape.setter
114
+ def shape(self, value: str):
115
+ """Set pad shape.
116
+
117
+ Parameters
118
+ ----------
119
+ value : str
120
+ Pad shape.
121
+ """
122
+ if value.lower() == "circle":
123
+ self._update_pad_parameters_parameters(geom_type=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE)
124
+ elif value.lower() == "rectangle":
125
+ self._update_pad_parameters_parameters(geom_type=GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE)
126
+ elif value.lower() == "polygon":
127
+ self._update_pad_parameters_parameters(geom_type=GrpcPadGeometryType.PADGEOMTYPE_POLYGON)
128
+ else:
129
+ raise ValueError(
130
+ f"Unsupported pad shape: {value}. Supported shapes are 'circle', " f"'rectangle', and 'polygon'."
131
+ )
132
+
112
133
  @property
113
134
  def parameters_values(self):
114
135
  """Parameters.
@@ -300,6 +321,22 @@ class PadstackDef(GrpcPadstackDef):
300
321
  """
301
322
  return self.layers[0]
302
323
 
324
+ @property
325
+ def via_start_layer(self):
326
+ """Via starting layer.
327
+
328
+ .deprecated
329
+ Use: :method:`start_layer <pyedb.grpc.database.definition.padstack_def.PadstackDef.start_layer>`
330
+ instead.
331
+
332
+ Returns
333
+ -------
334
+ str
335
+ Name of the via starting layer.
336
+ """
337
+ warnings.warn("via_start_layer is deprecated. Use start_layer instead.", DeprecationWarning)
338
+ return self.start_layer
339
+
303
340
  @property
304
341
  def stop_layer(self):
305
342
  """Stopping layer.
@@ -311,6 +348,22 @@ class PadstackDef(GrpcPadstackDef):
311
348
  """
312
349
  return self.layers[-1]
313
350
 
351
+ @property
352
+ def via_stop_layer(self):
353
+ """Via stop layer.
354
+
355
+ .deprecated
356
+ Use :method:`stop_layer <pyedb.grpc.database.definition.padstack_def.PadstackDef.stop_layer>`
357
+ instead.
358
+
359
+ Returns
360
+ -------
361
+ str
362
+ Name of the via stop layer.
363
+ """
364
+ warnings.warn("via_stop_layer is deprecated. Use stop_layer instead.", DeprecationWarning)
365
+ return self.stop_layer
366
+
314
367
  @property
315
368
  def hole_diameter(self) -> float:
316
369
  """Hole diameter.
@@ -127,7 +127,7 @@ class PolygonData(GrpcPolygonData):
127
127
  bool
128
128
 
129
129
  """
130
- new_poly = self.expand(offset, tolerance, round_corners, maximum_corner_extension)
130
+ new_poly = super().expand(offset, tolerance, round_corners, maximum_corner_extension)
131
131
  if not new_poly[0].points:
132
132
  return False
133
133
  self._edb_object = new_poly[0]
@@ -23,7 +23,7 @@
23
23
  """
24
24
  This module contains these classes: `EdbLayout` and `Shape`.
25
25
  """
26
- from typing import Union
26
+ from typing import Dict, Union
27
27
 
28
28
  from ansys.edb.core.layout.layout import Layout as GrpcLayout
29
29
  import ansys.edb.core.primitive.bondwire
@@ -63,6 +63,7 @@ class Layout(GrpcLayout):
63
63
  super().__init__(pedb.active_cell._Cell__stub.GetLayout(pedb.active_cell.msg))
64
64
  self._pedb = pedb
65
65
  self.__primitives = []
66
+ self.__padstack_instances = {}
66
67
 
67
68
  @property
68
69
  def cell(self):
@@ -74,8 +75,9 @@ class Layout(GrpcLayout):
74
75
 
75
76
  @property
76
77
  def primitives(self) -> list[any]:
78
+ primitives = super().primitives
77
79
  self.__primitives = []
78
- for prim in super().primitives:
80
+ for prim in primitives:
79
81
  if isinstance(prim, ansys.edb.core.primitive.path.Path):
80
82
  self.__primitives.append(Path(self._pedb, prim))
81
83
  elif isinstance(prim, ansys.edb.core.primitive.polygon.Polygon):
@@ -196,11 +198,12 @@ class Layout(GrpcLayout):
196
198
  return [DifferentialPair(self._pedb, i) for i in self._pedb.active_cell.layout.differential_pairs]
197
199
 
198
200
  @property
199
- def padstack_instances(self) -> list[PadstackInstance]:
201
+ def padstack_instances(self) -> Dict[int, PadstackInstance]:
200
202
  """Get all padstack instances in a list."""
201
- return [PadstackInstance(self._pedb, i) for i in self._pedb.active_cell.layout.padstack_instances]
203
+ pad_stack_inst = super().padstack_instances
204
+ self.__padstack_instances = {i.edb_uid: PadstackInstance(self._pedb, i) for i in pad_stack_inst}
205
+ return self.__padstack_instances
202
206
 
203
- #
204
207
  @property
205
208
  def voltage_regulators(self) -> list[VoltageRegulator]:
206
209
  """Voltage regulators.
@@ -334,7 +334,7 @@ class LayoutValidation:
334
334
  new_name = re.sub(pattern, "_", net)
335
335
  val.name = new_name
336
336
 
337
- self._pedb._logger.info("Found {} illegal net names.".format(len(renamed_nets)))
337
+ self._pedb.logger.info("Found {} illegal net names.".format(len(renamed_nets)))
338
338
  return
339
339
 
340
340
  def illegal_rlc_values(self, fix: bool = False) -> List[str]:
@@ -358,7 +358,7 @@ class LayoutValidation:
358
358
  temp.append(k)
359
359
  if fix:
360
360
  v.rlc_values = [0, 1, 0]
361
- self._pedb._logger.info(f"Found {len(temp)} inductors have no value.")
361
+ self._pedb.logger.info(f"Found {len(temp)} inductors have no value.")
362
362
  return temp
363
363
 
364
364
  def padstacks_no_name(self, fix: bool = False) -> None:
@@ -389,4 +389,4 @@ class LayoutValidation:
389
389
  obj.set_product_property(
390
390
  GrpcProductIdType.DESIGNER, 11, f"{obj.component.name}-{obj.component_pin}"
391
391
  )
392
- self._pedb._logger.info(f"Found {counts}/{len(pds)} padstacks have no name.")
392
+ self._pedb.logger.info(f"Found {counts}/{len(pds)} padstacks have no name.")
@@ -92,7 +92,8 @@ class Modeler(object):
92
92
  def __init__(self, p_edb) -> None:
93
93
  """Initialize Modeler instance."""
94
94
  self._pedb = p_edb
95
- self._primitives = []
95
+ self.__primitives = []
96
+ self.__primitives_by_layer = {}
96
97
 
97
98
  @property
98
99
  def _edb(self) -> Any:
@@ -267,7 +268,7 @@ class Modeler(object):
267
268
  try:
268
269
  lay = i.layer.name
269
270
  if lay in _primitives_by_layer:
270
- _primitives_by_layer[lay].append(Primitive(self._pedb, i))
271
+ _primitives_by_layer[lay].append(i)
271
272
  except (InvalidArgumentException, AttributeError):
272
273
  pass
273
274
  return _primitives_by_layer
@@ -1114,7 +1115,7 @@ class Modeler(object):
1114
1115
  # return None
1115
1116
  # pointA = GrpcPointData(pointA[0]), self._get_edb_value(shape.pointA[1])
1116
1117
  # )
1117
- # pointB = self._edb.geometry.point_data(
1118
+ # pointB = self._edb.Geometry.PointData(
1118
1119
  # self._get_edb_value(shape.pointB[0]), self._get_edb_value(shape.pointB[1])
1119
1120
  # )
1120
1121
  # return self._edb.geometry.polygon_data.create_from_bbox((pointA, pointB))
@@ -1475,7 +1476,11 @@ class Modeler(object):
1475
1476
  if isinstance(pins_by_name, str):
1476
1477
  pins_by_name = [pins_by_name]
1477
1478
  p_inst = self._pedb.layout.padstack_instances
1478
- _pins = {pin.id: pin for pin in p_inst if pin.aedt_name in pins_by_aedt_name or pin.name in pins_by_name}
1479
+ _pins = {
1480
+ pin_id: pin
1481
+ for pin_id, pin in p_inst.items()
1482
+ if pin.aedt_name in pins_by_aedt_name or pin.name in pins_by_name
1483
+ }
1479
1484
  if not pins:
1480
1485
  pins = _pins
1481
1486
  else:
@@ -60,7 +60,7 @@ class Net(GrpcNet):
60
60
  self._pedb = pedb
61
61
  self._core_components = pedb.components
62
62
  self._core_primitive = pedb.modeler
63
- self._edb_object = raw_net
63
+ self.__primitives = []
64
64
 
65
65
  @property
66
66
  def primitives(self) -> list[Union[Path, Polygon, Circle, Rectangle, Bondwire]]:
@@ -76,19 +76,20 @@ class Net(GrpcNet):
76
76
  - :class:`Rectangle <pyedb.grpc.database.primitive.rectangle.Rectangle>`
77
77
  - :class:`Bondwire <pyedb.grpc.database.primitive.bondwire.Bondwire>`
78
78
  """
79
- primitives = []
80
- for primitive in super().primitives:
81
- if primitive.primitive_type == GrpcPrimitiveType.PATH:
82
- primitives.append(Path(self._pedb, primitive))
83
- elif primitive.primitive_type == GrpcPrimitiveType.POLYGON:
84
- primitives.append(Polygon(self._pedb, primitive))
85
- elif primitive.primitive_type == GrpcPrimitiveType.CIRCLE:
86
- primitives.append(Circle(self._pedb, primitive))
87
- elif primitive.primitive_type == GrpcPrimitiveType.RECTANGLE:
88
- primitives.append(Rectangle(self._pedb, primitive))
89
- elif primitive.primitive_type == GrpcPrimitiveType.BONDWIRE:
90
- primitives.append(Bondwire(self._pedb, primitive))
91
- return primitives
79
+ primitives = super().primitives
80
+ if not len(self.__primitives) == len(primitives):
81
+ for primitive in primitives:
82
+ if primitive.primitive_type == GrpcPrimitiveType.PATH:
83
+ self.__primitives.append(Path(self._pedb, primitive))
84
+ elif primitive.primitive_type == GrpcPrimitiveType.POLYGON:
85
+ self.__primitives.append(Polygon(self._pedb, primitive))
86
+ elif primitive.primitive_type == GrpcPrimitiveType.CIRCLE:
87
+ self.__primitives.append(Circle(self._pedb, primitive))
88
+ elif primitive.primitive_type == GrpcPrimitiveType.RECTANGLE:
89
+ self.__primitives.append(Rectangle(self._pedb, primitive))
90
+ elif primitive.primitive_type == GrpcPrimitiveType.BONDWIRE:
91
+ self.__primitives.append(Bondwire(self._pedb, primitive))
92
+ return self.__primitives
92
93
 
93
94
  @property
94
95
  def padstack_instances(self) -> list[PadstackInstance]:
@@ -25,9 +25,12 @@ from __future__ import absolute_import # noreorder
25
25
  from typing import Any, Dict, List, Optional, Set, Tuple, Union
26
26
  import warnings
27
27
 
28
+ from ansys.edb.core.net.net_class import NetClass as GrpcNetClass
29
+
28
30
  from pyedb.common.nets import CommonNets
29
31
  from pyedb.generic.general_methods import generate_unique_name
30
32
  from pyedb.grpc.database.net.net import Net
33
+ from pyedb.grpc.database.net.net_class import NetClass
31
34
  from pyedb.grpc.database.primitive.bondwire import Bondwire
32
35
  from pyedb.grpc.database.primitive.path import Path
33
36
  from pyedb.grpc.database.primitive.polygon import Polygon
@@ -888,3 +891,70 @@ class Nets(CommonNets):
888
891
  if isinstance(net_names_list, str):
889
892
  net_names_list = [net_names_list]
890
893
  return self._pedb.modeler.unite_polygons_on_layer(net_names_list=net_names_list)
894
+
895
+
896
+ class NetClasses:
897
+ """Net classes management.
898
+
899
+ This class provides access to net classes in the EDB layout.
900
+ It allows for operations like retrieving nets, adding/removing nets,
901
+ and checking if a net is part of a net class.
902
+
903
+ Examples
904
+ --------
905
+ >>> from pyedb import Edb
906
+ >>> edb = Edb(myedb, edbversion="2025.1")
907
+ >>> net_classes = edb.net_classes
908
+ """
909
+
910
+ def __init__(self, pedb):
911
+ self._pedb = pedb
912
+ self._net_classes = pedb.active_layout.net_classes
913
+
914
+ def __getitem__(self, name: str) -> NetClass:
915
+ """Get a net by name.
916
+
917
+ Parameters
918
+ ----------
919
+ name : str
920
+ Name of the net to retrieve.
921
+
922
+ """
923
+ return self.items[name]
924
+
925
+ @property
926
+ def items(self) -> Dict[str, NetClass]:
927
+ """Extended nets.
928
+
929
+ Returns
930
+ -------
931
+ Dict[str, :class:`pyedb.grpc.database.nets.nets_class.NetClass`]
932
+ Dictionary of extended nets.
933
+ """
934
+ return {i.name: i for i in self._pedb.layout.net_classes}
935
+
936
+ def create(self, name, net) -> Union[bool, NetClass]:
937
+ """Create a new net class.
938
+
939
+ Parameters
940
+ ----------
941
+ name : str
942
+ Name of the net class.
943
+ net : str, list
944
+ Name of the nets to be added into this net class.
945
+
946
+ Returns
947
+ -------
948
+ :class:`pyedb.dotnet.database.edb_data.nets_data.EDBNetClassData` `False` if net name already exists.
949
+ """
950
+ if name in self.items:
951
+ self._pedb.logger.error("{} already exists.".format(name))
952
+ return False
953
+ grpc_net_class = GrpcNetClass.create(self._pedb.active_layout, name)
954
+ if isinstance(net, str):
955
+ net = [net]
956
+ for i in net:
957
+ grpc_net_class.add_net(self._pedb.nets[i])
958
+ net_class = NetClass(self._pedb, grpc_net_class)
959
+ self.items[name] = net_class
960
+ return net_class
@@ -93,8 +93,7 @@ class Padstacks(object):
93
93
 
94
94
  def __init__(self, p_edb: Any) -> None:
95
95
  self._pedb = p_edb
96
- self._instances: Dict[int, PadstackInstance] = {}
97
- self._definitions: Dict[str, Any] = {}
96
+ self.__definitions: Dict[str, Any] = {}
98
97
 
99
98
  @property
100
99
  def _active_layout(self) -> Any:
@@ -211,13 +210,12 @@ class Padstacks(object):
211
210
  >>> for name, definition in all_definitions.items():
212
211
  ... print(f"Padstack: {name}")
213
212
  """
214
- if len(self._definitions) == len(self.db.padstack_defs):
215
- return self._definitions
216
- self._definitions = {}
217
- for padstack_def in self._pedb.db.padstack_defs:
213
+ padstack_defs = self._pedb.db.padstack_defs
214
+ self.__definitions = {}
215
+ for padstack_def in padstack_defs:
218
216
  if len(padstack_def.data.layer_names) >= 1:
219
- self._definitions[padstack_def.name] = PadstackDef(self._pedb, padstack_def)
220
- return self._definitions
217
+ self.__definitions[padstack_def.name] = PadstackDef(self._pedb, padstack_def)
218
+ return self.__definitions
221
219
 
222
220
  @property
223
221
  def instances(self) -> Dict[int, PadstackInstance]:
@@ -230,15 +228,11 @@ class Padstacks(object):
230
228
 
231
229
  Examples
232
230
  --------
233
- >>> all_instances = edb_padstacks.instances
231
+ >>> all_instances = edb.padstacks.instances
234
232
  >>> for id, instance in all_instances.items():
235
233
  ... print(f"Instance {id}: {instance.name}")
236
234
  """
237
- pad_stack_inst = self._pedb.layout.padstack_instances
238
- if len(self._instances) == len(pad_stack_inst):
239
- return self._instances
240
- self._instances = {i.edb_uid: PadstackInstance(self._pedb, i) for i in pad_stack_inst}
241
- return self._instances
235
+ return self._pedb.layout.padstack_instances
242
236
 
243
237
  @property
244
238
  def instances_by_name(self) -> Dict[str, PadstackInstance]:
@@ -348,6 +342,7 @@ class Padstacks(object):
348
342
  @property
349
343
  def pad_type(self) -> GrpcPadType:
350
344
  """Return a PadType Enumerator."""
345
+ return GrpcPadType
351
346
 
352
347
  def create_circular_padstack(
353
348
  self,
@@ -800,7 +795,7 @@ class Padstacks(object):
800
795
  if net_list and not isinstance(net_list, list):
801
796
  net_list = [net_list]
802
797
  via_list = []
803
- for inst in self._layout.padstack_instances:
798
+ for inst_id, inst in self._layout.padstack_instances.items():
804
799
  pad_layers_name = inst.padstack_def.data.layer_names
805
800
  if len(pad_layers_name) > 1:
806
801
  if not net_list:
@@ -1150,7 +1145,7 @@ class Padstacks(object):
1150
1145
  padstack_instance.is_layout_pin = is_pin
1151
1146
  return PadstackInstance(self._pedb, padstack_instance)
1152
1147
  else:
1153
- return False
1148
+ raise RuntimeError("Place padstack failed")
1154
1149
 
1155
1150
  def remove_pads_from_padstack(self, padstack_name: str, layer_name: Optional[str] = None):
1156
1151
  """Remove pads from a padstack definition on specified layers.
@@ -1287,6 +1282,29 @@ class Padstacks(object):
1287
1282
  self.definitions[padstack_name].data = new_padstack_def
1288
1283
  return True
1289
1284
 
1285
+ def get_padstack_instance_by_net_name(self, net: str):
1286
+ """Get padstack instances by net name.
1287
+
1288
+ .. deprecated:: 0.55.0
1289
+ Use: :func:`get_instances` with `net_name` parameter instead.
1290
+
1291
+ Parameters
1292
+ ----------
1293
+ net : str
1294
+ Net name to filter padstack instances.
1295
+
1296
+ Returns
1297
+ -------
1298
+ list[:class:`pyedb.grpc.database.primitive.padstack_instance.PadstackInstance`]
1299
+ List of padstack instances associated with the specified net.
1300
+ """
1301
+ warnings.warn(
1302
+ "`get_padstack_instance_by_net_name` is deprecated, use `get_instances` with `net_name` "
1303
+ "parameter instead.",
1304
+ DeprecationWarning,
1305
+ )
1306
+ return self.get_instances(net_name=net)
1307
+
1290
1308
  def get_instances(
1291
1309
  self,
1292
1310
  name: Optional[str] = None,
@@ -1322,7 +1340,7 @@ class Padstacks(object):
1322
1340
  if pid:
1323
1341
  return instances_by_id[pid]
1324
1342
  elif name:
1325
- instances = [inst for inst in list(self.instances.values()) if inst.name == name]
1343
+ instances = [inst for inst in list(self.instances.values()) if inst.aedt_name == name]
1326
1344
  if instances:
1327
1345
  return instances
1328
1346
  else: