pyedb 0.56.0__py3-none-any.whl → 0.58.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 (110) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_data.py +3 -0
  3. pyedb/configuration/cfg_pin_groups.py +2 -0
  4. pyedb/configuration/cfg_terminals.py +232 -0
  5. pyedb/configuration/configuration.py +146 -3
  6. pyedb/dotnet/clr_module.py +1 -2
  7. pyedb/dotnet/database/Variables.py +30 -22
  8. pyedb/dotnet/database/cell/hierarchy/component.py +2 -8
  9. pyedb/dotnet/database/cell/layout.py +5 -1
  10. pyedb/dotnet/database/cell/primitive/primitive.py +2 -2
  11. pyedb/dotnet/database/cell/terminal/bundle_terminal.py +12 -0
  12. pyedb/dotnet/database/cell/terminal/pingroup_terminal.py +1 -1
  13. pyedb/dotnet/database/cell/terminal/terminal.py +38 -0
  14. pyedb/dotnet/database/components.py +15 -19
  15. pyedb/dotnet/database/dotnet/database.py +1 -0
  16. pyedb/dotnet/database/edb_data/control_file.py +19 -8
  17. pyedb/dotnet/database/edb_data/nets_data.py +3 -3
  18. pyedb/dotnet/database/edb_data/padstacks_data.py +39 -14
  19. pyedb/dotnet/database/edb_data/ports.py +0 -25
  20. pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
  21. pyedb/dotnet/database/edb_data/raptor_x_simulation_setup_data.py +18 -19
  22. pyedb/dotnet/database/edb_data/simulation_configuration.py +3 -3
  23. pyedb/dotnet/database/edb_data/sources.py +21 -2
  24. pyedb/dotnet/database/general.py +1 -6
  25. pyedb/dotnet/database/hfss.py +9 -8
  26. pyedb/dotnet/database/layout_validation.py +14 -3
  27. pyedb/dotnet/database/materials.py +1 -3
  28. pyedb/dotnet/database/modeler.py +7 -3
  29. pyedb/dotnet/database/nets.py +27 -19
  30. pyedb/dotnet/database/padstack.py +4 -2
  31. pyedb/dotnet/database/sim_setup_data/io/siwave.py +54 -1
  32. pyedb/dotnet/database/siwave.py +4 -3
  33. pyedb/dotnet/database/stackup.py +55 -58
  34. pyedb/dotnet/database/utilities/heatsink.py +0 -1
  35. pyedb/dotnet/database/utilities/hfss_simulation_setup.py +81 -0
  36. pyedb/dotnet/database/utilities/simulation_setup.py +7 -5
  37. pyedb/dotnet/database/utilities/siwave_cpa_simulation_setup.py +1 -0
  38. pyedb/dotnet/database/utilities/siwave_simulation_setup.py +264 -13
  39. pyedb/dotnet/edb.py +65 -47
  40. pyedb/exceptions.py +1 -2
  41. pyedb/extensions/create_cell_array.py +67 -49
  42. pyedb/generic/data_handlers.py +13 -23
  43. pyedb/generic/design_types.py +9 -35
  44. pyedb/generic/filesystem.py +4 -2
  45. pyedb/generic/general_methods.py +28 -41
  46. pyedb/generic/plot.py +8 -23
  47. pyedb/generic/process.py +78 -10
  48. pyedb/grpc/database/_typing.py +0 -0
  49. pyedb/grpc/database/components.py +14 -13
  50. pyedb/grpc/database/control_file.py +27 -40
  51. pyedb/grpc/database/definition/materials.py +1 -1
  52. pyedb/grpc/database/definition/package_def.py +6 -3
  53. pyedb/grpc/database/definition/padstack_def.py +14 -12
  54. pyedb/grpc/database/hfss.py +1 -4
  55. pyedb/grpc/database/hierarchy/component.py +5 -13
  56. pyedb/grpc/database/hierarchy/pingroup.py +16 -3
  57. pyedb/grpc/database/layers/layer.py +1 -2
  58. pyedb/grpc/database/layers/stackup_layer.py +42 -19
  59. pyedb/grpc/database/layout/layout.py +43 -27
  60. pyedb/grpc/database/layout/voltage_regulator.py +6 -1
  61. pyedb/grpc/database/layout_validation.py +5 -2
  62. pyedb/grpc/database/modeler.py +254 -252
  63. pyedb/grpc/database/net/differential_pair.py +9 -2
  64. pyedb/grpc/database/net/extended_net.py +24 -9
  65. pyedb/grpc/database/net/net.py +14 -5
  66. pyedb/grpc/database/net/net_class.py +24 -7
  67. pyedb/grpc/database/nets.py +11 -43
  68. pyedb/grpc/database/padstacks.py +67 -119
  69. pyedb/grpc/database/primitive/bondwire.py +3 -67
  70. pyedb/grpc/database/primitive/circle.py +42 -3
  71. pyedb/grpc/database/primitive/padstack_instance.py +58 -31
  72. pyedb/grpc/database/primitive/path.py +160 -11
  73. pyedb/grpc/database/primitive/polygon.py +73 -7
  74. pyedb/grpc/database/primitive/primitive.py +2 -2
  75. pyedb/grpc/database/primitive/rectangle.py +105 -4
  76. pyedb/grpc/database/simulation_setup/hfss_general_settings.py +0 -2
  77. pyedb/grpc/database/simulation_setup/hfss_settings_options.py +0 -4
  78. pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +79 -0
  79. pyedb/grpc/database/simulation_setup/siwave_cpa_simulation_setup.py +1 -0
  80. pyedb/grpc/database/simulation_setup/sweep_data.py +1 -3
  81. pyedb/grpc/database/siwave.py +6 -13
  82. pyedb/grpc/database/source_excitations.py +46 -63
  83. pyedb/grpc/database/stackup.py +55 -60
  84. pyedb/grpc/database/terminal/bundle_terminal.py +10 -3
  85. pyedb/grpc/database/terminal/padstack_instance_terminal.py +9 -11
  86. pyedb/grpc/database/terminal/pingroup_terminal.py +8 -1
  87. pyedb/grpc/database/terminal/point_terminal.py +30 -0
  88. pyedb/grpc/database/terminal/terminal.py +35 -10
  89. pyedb/grpc/database/utility/heat_sink.py +0 -1
  90. pyedb/grpc/database/utility/hfss_extent_info.py +2 -2
  91. pyedb/grpc/database/utility/xml_control_file.py +19 -8
  92. pyedb/grpc/edb.py +63 -32
  93. pyedb/grpc/edb_init.py +1 -0
  94. pyedb/ipc2581/ecad/cad_data/layer_feature.py +6 -2
  95. pyedb/ipc2581/ecad/cad_data/step.py +1 -1
  96. pyedb/ipc2581/ipc2581.py +8 -7
  97. pyedb/libraries/common.py +3 -4
  98. pyedb/libraries/rf_libraries/base_functions.py +7 -16
  99. pyedb/libraries/rf_libraries/planar_antennas.py +3 -21
  100. pyedb/misc/aedtlib_personalib_install.py +2 -2
  101. pyedb/misc/downloads.py +19 -3
  102. pyedb/misc/misc.py +5 -2
  103. pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +3 -2
  104. pyedb/misc/siw_feature_config/xtalk_scan/scan_config.py +0 -1
  105. pyedb/misc/utilities.py +0 -1
  106. pyedb/modeler/geometry_operators.py +3 -2
  107. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/METADATA +6 -7
  108. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/RECORD +110 -108
  109. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/WHEEL +0 -0
  110. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/licenses/LICENSE +0 -0
@@ -20,15 +20,18 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
+ from __future__ import annotations
23
24
 
25
+ from typing import TYPE_CHECKING
26
+
27
+ if TYPE_CHECKING:
28
+ from pyedb.grpc.database.net.net import Net
24
29
  import re
25
30
 
26
31
  from ansys.edb.core.net.differential_pair import (
27
32
  DifferentialPair as GrpcDifferentialPair,
28
33
  )
29
34
 
30
- from pyedb.grpc.database.net.net import Net
31
-
32
35
 
33
36
  class DifferentialPairs:
34
37
  def __init__(self, pedb):
@@ -131,9 +134,13 @@ class DifferentialPair(GrpcDifferentialPair):
131
134
  @property
132
135
  def positive_net(self) -> Net:
133
136
  """Positive Net."""
137
+ from pyedb.grpc.database.net.net import Net
138
+
134
139
  return Net(self._pedb, super().positive_net)
135
140
 
136
141
  @property
137
142
  def negative_net(self) -> Net:
138
143
  """Negative Net."""
144
+ from pyedb.grpc.database.net.net import Net
145
+
139
146
  return Net(self._pedb, super().negative_net)
@@ -20,9 +20,13 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- from ansys.edb.core.net.extended_net import ExtendedNet as GrpcExtendedNet
23
+ from __future__ import annotations
24
+
25
+ from typing import TYPE_CHECKING
24
26
 
25
- from pyedb.grpc.database.net.net import Net
27
+ if TYPE_CHECKING:
28
+ from pyedb.grpc.database.net.net import Net
29
+ from ansys.edb.core.net.extended_net import ExtendedNet as GrpcExtendedNet
26
30
 
27
31
 
28
32
  class ExtendedNets:
@@ -204,12 +208,21 @@ class ExtendedNets:
204
208
  val_value = cmp.rlc_values
205
209
  if refdes in exception_list:
206
210
  pass
207
- elif cmp.type == "inductor" and val_value[1] < inductor_below:
208
- pass
209
- elif cmp.type == "resistor" and val_value[0] < resistor_below:
210
- pass
211
- elif cmp.type == "capacitor" and val_value[2] > capacitor_above:
212
- pass
211
+ elif cmp.type == "inductor":
212
+ if val_value[1] is None:
213
+ continue
214
+ elif not val_value[1] < inductor_below:
215
+ continue
216
+ elif cmp.type == "resistor":
217
+ if val_value[0] is None:
218
+ continue
219
+ elif not val_value[0] < resistor_below:
220
+ continue
221
+ elif cmp.type == "capacitor":
222
+ if val_value[2] is None:
223
+ continue
224
+ elif not val_value[2] > capacitor_above:
225
+ continue
213
226
  else:
214
227
  continue
215
228
  for net in comp_dict[refdes]:
@@ -263,7 +276,7 @@ class ExtendedNet(GrpcExtendedNet):
263
276
  self._pedb = pedb
264
277
 
265
278
  @property
266
- def nets(self):
279
+ def nets(self) -> dict[str, Net]:
267
280
  """Nets dictionary.
268
281
 
269
282
  Returns
@@ -271,6 +284,8 @@ class ExtendedNet(GrpcExtendedNet):
271
284
  Dict[str, :class:`Net <pyedb.grpc.database.net.net.Net>`]
272
285
  Dict[net name, Net object].
273
286
  """
287
+ from pyedb.grpc.database.net.net import Net
288
+
274
289
  return {net.name: Net(self._pedb, net) for net in super().nets}
275
290
 
276
291
  @property
@@ -20,14 +20,17 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- from typing import Union
23
+ from __future__ import annotations
24
24
 
25
+ from typing import TYPE_CHECKING, Union
26
+
27
+ if TYPE_CHECKING:
28
+ from pyedb.grpc.database.primitive.circle import Circle
29
+ from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
25
30
  from ansys.edb.core.net.net import Net as GrpcNet
26
31
  from ansys.edb.core.primitive.primitive import PrimitiveType as GrpcPrimitiveType
27
32
 
28
33
  from pyedb.grpc.database.primitive.bondwire import Bondwire
29
- from pyedb.grpc.database.primitive.circle import Circle
30
- from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
31
34
  from pyedb.grpc.database.primitive.path import Path
32
35
  from pyedb.grpc.database.primitive.polygon import Polygon
33
36
  from pyedb.grpc.database.primitive.rectangle import Rectangle
@@ -76,6 +79,7 @@ class Net(GrpcNet):
76
79
  - :class:`Rectangle <pyedb.grpc.database.primitive.rectangle.Rectangle>`
77
80
  - :class:`Bondwire <pyedb.grpc.database.primitive.bondwire.Bondwire>`
78
81
  """
82
+
79
83
  primitives = super().primitives
80
84
  if not len(self.__primitives) == len(primitives):
81
85
  for primitive in primitives:
@@ -100,6 +104,8 @@ class Net(GrpcNet):
100
104
  list of :class:`PadstackInstance <pyedb.grpc.database.primitive.padstack_instance.PadstackInstance>`
101
105
  Padstack instances associated with the net.
102
106
  """
107
+ from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
108
+
103
109
  return [PadstackInstance(self._pedb, i) for i in super().padstack_instances]
104
110
 
105
111
  @property
@@ -118,8 +124,11 @@ class Net(GrpcNet):
118
124
  if component:
119
125
  try:
120
126
  components[component.name] = component
121
- except:
122
- pass
127
+ except Exception as e:
128
+ self._pedb.logger.error(
129
+ f"A(n) {type(e).__name__} error occurred while attempting to access "
130
+ f"'components' property for object {self} - Empty dict is returned: {str(e)}"
131
+ )
123
132
  return components
124
133
 
125
134
  def find_dc_short(self, fix=False) -> list[list[str, str]]:
@@ -20,9 +20,14 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
+ from __future__ import annotations
24
+
25
+ from typing import TYPE_CHECKING
26
+
23
27
  from ansys.edb.core.net.net_class import NetClass as GrpcNetClass
24
28
 
25
- from pyedb.grpc.database.net.net import Net
29
+ if TYPE_CHECKING:
30
+ from pyedb.grpc.database.net.net import Net
26
31
 
27
32
 
28
33
  class NetClass(GrpcNetClass):
@@ -49,6 +54,8 @@ class NetClass(GrpcNetClass):
49
54
  List[:class:`Net <pyedb.grpc.database.net.net.Net>`].
50
55
  List of Net object.
51
56
  """
57
+ from pyedb.grpc.database.net.net import Net
58
+
52
59
  return [Net(self._pedb, i) for i in super().nets]
53
60
 
54
61
  def add_net(self, net):
@@ -59,21 +66,30 @@ class NetClass(GrpcNetClass):
59
66
  bool
60
67
  """
61
68
  if isinstance(net, str):
69
+ from pyedb.grpc.database.net.net import Net
70
+
62
71
  net = Net.find_by_name(self._pedb.active_layout, name=net)
63
72
  if isinstance(net, Net) and not net.is_null:
64
73
  self.add_net(net)
65
74
  return True
66
75
  return False
67
76
 
68
- def contains_net(self, net):
69
- """Determine if a net exists in the net class.
77
+ def contains_net(self, net) -> bool:
78
+ """
79
+ Determine if a net exists in the net class.
80
+
81
+ Parameters
82
+ ----------
83
+ net : str or Net
84
+ The net to check. This can be a string representing the net name or a `Net` object.
70
85
 
71
- returns
86
+ Returns
72
87
  -------
73
- List[:class:`Net <pyedb.grpc.database.net.net.Net>`].
74
- List of Net object.
88
+ bool
89
+ True if the net exists in the net class, False otherwise.
75
90
 
76
91
  """
92
+
77
93
  if isinstance(net, str):
78
94
  net = Net.find_by_name(self._pedb.active_layout, name=net)
79
95
  return super().contains_net(net)
@@ -85,9 +101,10 @@ class NetClass(GrpcNetClass):
85
101
  -------
86
102
  bool
87
103
  """
104
+
88
105
  if isinstance(net, str):
89
106
  net = Net.find_by_name(self._pedb.active_layout, name=net)
90
107
  if isinstance(net, Net) and not net.is_null:
91
- self.remove(net)
108
+ self.remove_net(net)
92
109
  return True
93
110
  return False
@@ -95,34 +95,21 @@ class Nets(CommonNets):
95
95
  >>> print("Eligible power nets:", [net.name for net in eligible_pwr])
96
96
 
97
97
  >>> # Generate extended nets (deprecated)
98
- >>> nets.generate_extended_nets(
99
- >>> resistor_below=5,
100
- >>>inductor_below=0.5,
101
- >>> capacitor_above=0.1
102
- >>> )
98
+ >>> nets.generate_extended_nets(resistor_below=5, inductor_below=0.5, capacitor_above=0.1)
103
99
 
104
100
  >>> # Classify nets
105
- >>> nets.classify_nets(
106
- >>> power_nets=["VDD_CPU", "VDD_MEM"],
107
- >>> signal_nets=["PCIe_TX", "ETH_RX"]
108
- >>> )
101
+ >>> nets.classify_nets(power_nets=["VDD_CPU", "VDD_MEM"], signal_nets=["PCIe_TX", "ETH_RX"])
109
102
 
110
103
  >>> # Check power/ground status
111
104
  >>> is_power = nets.is_power_gound_net(["VDD_CPU", "PCIe_TX"])
112
105
  >>> print("Is power net:", is_power)
113
106
 
114
107
  >>> # Get DC-connected nets
115
- >>> dc_connected = nets.get_dcconnected_net_list(
116
- >>> ground_nets=["GND"],
117
- >>> res_value=0.002
118
- >>> )
108
+ >>> dc_connected = nets.get_dcconnected_net_list(ground_nets=["GND"], res_value=0.002)
119
109
  print("DC-connected nets:", dc_connected)
120
110
 
121
111
  >>> # Get power tree
122
- >>> comp_list, columns, net_group = nets.get_powertree(
123
- >>> power_net_name="VDD_CPU",
124
- >>> ground_nets=["GND"]
125
- >>> )
112
+ >>> comp_list, columns, net_group = nets.get_powertree(power_net_name="VDD_CPU", ground_nets=["GND"])
126
113
  >>> print("Power tree components:", comp_list)
127
114
 
128
115
  >>> # Find net by name
@@ -142,10 +129,7 @@ class Nets(CommonNets):
142
129
  >>> print("Net in component:", in_component)
143
130
 
144
131
  >>> # Find and fix disjoint nets (deprecated)
145
- >>> fixed_nets = nets.find_and_fix_disjoint_nets(
146
- >>> net_list=["PCIe_TX"],
147
- >>> clean_disjoints_less_than=1e-6
148
- >>> )
132
+ >>> fixed_nets = nets.find_and_fix_disjoint_nets(net_list=["PCIe_TX"], clean_disjoints_less_than=1e-6)
149
133
  >>> print("Fixed nets:", fixed_nets)
150
134
 
151
135
  >>> # Merge net polygons
@@ -422,11 +406,7 @@ class Nets(CommonNets):
422
406
 
423
407
  Examples
424
408
  --------
425
- >>> edb_nets.generate_extended_nets(
426
- ... resistor_below=5,
427
- ... inductor_below=0.5,
428
- ... capacitor_above=0.1
429
- ... )
409
+ >>> edb_nets.generate_extended_nets(resistor_below=5, inductor_below=0.5, capacitor_above=0.1)
430
410
  """
431
411
  warnings.warn("Use new method :func:`edb.extended_nets.generate_extended_nets` instead.", DeprecationWarning)
432
412
  self._pedb.extended_nets.generate_extended_nets(
@@ -487,10 +467,7 @@ class Nets(CommonNets):
487
467
 
488
468
  Examples
489
469
  --------
490
- >>> edb_nets.classify_nets(
491
- ... power_nets=["VDD_CPU", "VDD_MEM"],
492
- ... signal_nets=["PCIe_TX", "ETH_RX"]
493
- ... )
470
+ >>> edb_nets.classify_nets(power_nets=["VDD_CPU", "VDD_MEM"], signal_nets=["PCIe_TX", "ETH_RX"])
494
471
  """
495
472
  if isinstance(power_nets, str):
496
473
  power_nets = []
@@ -551,10 +528,7 @@ class Nets(CommonNets):
551
528
 
552
529
  Examples
553
530
  --------
554
- >>> dc_connected = edb_nets.get_dcconnected_net_list(
555
- ... ground_nets=["GND"],
556
- ... res_value=0.002
557
- ... )
531
+ >>> dc_connected = edb_nets.get_dcconnected_net_list(ground_nets=["GND"], res_value=0.002)
558
532
  >>> for net_group in dc_connected:
559
533
  ... print("Connected nets:", net_group)
560
534
  """
@@ -611,10 +585,7 @@ class Nets(CommonNets):
611
585
 
612
586
  Examples
613
587
  --------
614
- >>> comp_list, columns, net_group = edb_nets.get_powertree(
615
- ... power_net_name="VDD_CPU",
616
- ... ground_nets=["GND"]
617
- ... )
588
+ >>> comp_list, columns, net_group = edb_nets.get_powertree(power_net_name="VDD_CPU", ground_nets=["GND"])
618
589
  >>> print("Power tree components:", comp_list)
619
590
  """
620
591
  flag_in_ng = False
@@ -700,7 +671,7 @@ class Nets(CommonNets):
700
671
 
701
672
  Examples
702
673
  --------
703
- >>> deleted_nets = database.nets.delete(["Net1","Net2"])
674
+ >>> deleted_nets = database.nets.delete(["Net1", "Net2"])
704
675
  """
705
676
  if isinstance(netlist, str):
706
677
  netlist = [netlist]
@@ -858,10 +829,7 @@ class Nets(CommonNets):
858
829
 
859
830
  Examples
860
831
  --------
861
- >>> fixed_nets = edb_nets.find_and_fix_disjoint_nets(
862
- ... net_list=["PCIe_TX"],
863
- ... clean_disjoints_less_than=1e-6
864
- ... )
832
+ >>> fixed_nets = edb_nets.find_and_fix_disjoint_nets(net_list=["PCIe_TX"], clean_disjoints_less_than=1e-6)
865
833
  >>> print("Fixed nets:", fixed_nets)
866
834
  """
867
835
 
@@ -23,27 +23,21 @@
23
23
  """
24
24
  This module contains the `EdbPadstacks` class.
25
25
  """
26
+
26
27
  from collections import defaultdict
28
+ from functools import lru_cache
27
29
  import math
28
30
  from typing import Any, Dict, List, Optional, Tuple, Union
29
31
  import warnings
30
32
 
31
33
  from ansys.edb.core.definition.padstack_def_data import (
32
34
  PadGeometryType as GrpcPadGeometryType,
33
- )
34
- from ansys.edb.core.definition.padstack_def_data import (
35
35
  PadstackDefData as GrpcPadstackDefData,
36
- )
37
- from ansys.edb.core.definition.padstack_def_data import (
38
36
  PadstackHoleRange as GrpcPadstackHoleRange,
39
- )
40
- from ansys.edb.core.definition.padstack_def_data import (
37
+ PadType as GrpcPadType,
41
38
  SolderballPlacement as GrpcSolderballPlacement,
42
- )
43
- from ansys.edb.core.definition.padstack_def_data import (
44
39
  SolderballShape as GrpcSolderballShape,
45
40
  )
46
- from ansys.edb.core.definition.padstack_def_data import PadType as GrpcPadType
47
41
  from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
48
42
  from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
49
43
  import numpy as np
@@ -55,6 +49,29 @@ from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
55
49
  from pyedb.grpc.database.utility.value import Value
56
50
  from pyedb.modeler.geometry_operators import GeometryOperators
57
51
 
52
+ GEOMETRY_MAP = {
53
+ 0: GrpcPadGeometryType.PADGEOMTYPE_NO_GEOMETRY,
54
+ 1: GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
55
+ 2: GrpcPadGeometryType.PADGEOMTYPE_SQUARE,
56
+ 3: GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE,
57
+ 4: GrpcPadGeometryType.PADGEOMTYPE_OVAL,
58
+ 5: GrpcPadGeometryType.PADGEOMTYPE_BULLET,
59
+ 6: GrpcPadGeometryType.PADGEOMTYPE_NSIDED_POLYGON,
60
+ 7: GrpcPadGeometryType.PADGEOMTYPE_POLYGON,
61
+ 8: GrpcPadGeometryType.PADGEOMTYPE_ROUND45,
62
+ 9: GrpcPadGeometryType.PADGEOMTYPE_ROUND90,
63
+ 10: GrpcPadGeometryType.PADGEOMTYPE_SQUARE45,
64
+ 11: GrpcPadGeometryType.PADGEOMTYPE_SQUARE90,
65
+ }
66
+
67
+ PAD_TYPE_MAP = {
68
+ 0: GrpcPadType.REGULAR_PAD,
69
+ 1: GrpcPadType.ANTI_PAD,
70
+ 2: GrpcPadType.THERMAL_PAD,
71
+ 3: GrpcPadType.HOLE,
72
+ 4: GrpcPadType.UNKNOWN_GEOM_TYPE,
73
+ }
74
+
58
75
 
59
76
  class Padstacks(object):
60
77
  """Manages EDB methods for padstacks accessible from `Edb.padstacks` property.
@@ -94,6 +111,15 @@ class Padstacks(object):
94
111
  def __init__(self, p_edb: Any) -> None:
95
112
  self._pedb = p_edb
96
113
  self.__definitions: Dict[str, Any] = {}
114
+ self._instances = None
115
+ self._instances_by_name = {}
116
+ self._instances_by_net = {}
117
+
118
+ def clear_instances_cache(self):
119
+ """Clear the cached padstack instances."""
120
+ self._instances = None
121
+ self._instances_by_name = {}
122
+ self._instances_by_net = {}
97
123
 
98
124
  @property
99
125
  def _active_layout(self) -> Any:
@@ -120,7 +146,8 @@ class Padstacks(object):
120
146
  """ """
121
147
  return self._pedb.stackup.layers
122
148
 
123
- def int_to_pad_type(self, val=0) -> GrpcPadType:
149
+ @staticmethod
150
+ def int_to_pad_type(val=0) -> GrpcPadType:
124
151
  """Convert an integer to an EDB.PadGeometryType.
125
152
 
126
153
  Parameters
@@ -138,20 +165,10 @@ class Padstacks(object):
138
165
  >>> pad_type = edb_padstacks.int_to_pad_type(1) # Returns ANTI_PAD
139
166
  """
140
167
 
141
- if val == 0:
142
- return GrpcPadType.REGULAR_PAD
143
- elif val == 1:
144
- return GrpcPadType.ANTI_PAD
145
- elif val == 2:
146
- return GrpcPadType.THERMAL_PAD
147
- elif val == 3:
148
- return GrpcPadType.HOLE
149
- elif val == 4:
150
- return GrpcPadType.UNKNOWN_GEOM_TYPE
151
- else:
152
- return val
168
+ return PAD_TYPE_MAP.get(val, val)
153
169
 
154
- def int_to_geometry_type(self, val: int = 0) -> GrpcPadGeometryType:
170
+ @staticmethod
171
+ def int_to_geometry_type(val: int = 0) -> GrpcPadGeometryType:
155
172
  """Convert an integer to an EDB.PadGeometryType.
156
173
 
157
174
  Parameters
@@ -168,32 +185,7 @@ class Padstacks(object):
168
185
  >>> geom_type = edb_padstacks.int_to_geometry_type(1) # Returns CIRCLE
169
186
  >>> geom_type = edb_padstacks.int_to_geometry_type(2) # Returns SQUARE
170
187
  """
171
- if val == 0:
172
- return GrpcPadGeometryType.PADGEOMTYPE_NO_GEOMETRY
173
- elif val == 1:
174
- return GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
175
- elif val == 2:
176
- return GrpcPadGeometryType.PADGEOMTYPE_SQUARE
177
- elif val == 3:
178
- return GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE
179
- elif val == 4:
180
- return GrpcPadGeometryType.PADGEOMTYPE_OVAL
181
- elif val == 5:
182
- return GrpcPadGeometryType.PADGEOMTYPE_BULLET
183
- elif val == 6:
184
- return GrpcPadGeometryType.PADGEOMTYPE_NSIDED_POLYGON
185
- elif val == 7:
186
- return GrpcPadGeometryType.PADGEOMTYPE_POLYGON
187
- elif val == 8:
188
- return GrpcPadGeometryType.PADGEOMTYPE_ROUND45
189
- elif val == 9:
190
- return GrpcPadGeometryType.PADGEOMTYPE_ROUND90
191
- elif val == 10:
192
- return GrpcPadGeometryType.PADGEOMTYPE_SQUARE45
193
- elif val == 11:
194
- return GrpcPadGeometryType.PADGEOMTYPE_SQUARE90
195
- else:
196
- return val
188
+ return GEOMETRY_MAP.get(val, val)
197
189
 
198
190
  @property
199
191
  def definitions(self) -> Dict[str, PadstackDef]:
@@ -232,7 +224,17 @@ class Padstacks(object):
232
224
  >>> for id, instance in all_instances.items():
233
225
  ... print(f"Instance {id}: {instance.name}")
234
226
  """
235
- return self._pedb.layout.padstack_instances
227
+ if self._instances is None:
228
+ self._instances = self._pedb.layout.padstack_instances
229
+ return self._instances
230
+
231
+ @property
232
+ def instances_by_net(self) -> Dict[Any, PadstackInstance]:
233
+ if not self._instances_by_net:
234
+ for edb_padstack_instance in self._instances.values():
235
+ if edb_padstack_instance.net_name:
236
+ self._instances_by_net.setdefault(edb_padstack_instance.net_name, []).append(edb_padstack_instance)
237
+ return self._instances_by_net
236
238
 
237
239
  @property
238
240
  def instances_by_name(self) -> Dict[str, PadstackInstance]:
@@ -249,11 +251,11 @@ class Padstacks(object):
249
251
  >>> for name, instance in named_instances.items():
250
252
  ... print(f"Instance named {name}")
251
253
  """
252
- padstack_instances = {}
253
- for _, edb_padstack_instance in self.instances.items():
254
- if edb_padstack_instance.aedt_name:
255
- padstack_instances[edb_padstack_instance.aedt_name] = edb_padstack_instance
256
- return padstack_instances
254
+ if not self._instances_by_name:
255
+ for _, edb_padstack_instance in self.instances.items():
256
+ if edb_padstack_instance.aedt_name:
257
+ self._instances_by_name[edb_padstack_instance.aedt_name] = edb_padstack_instance
258
+ return self._instances_by_name
257
259
 
258
260
  def find_instance_by_id(self, value: int) -> Optional[PadstackInstance]:
259
261
  """Find a padstack instance by database ID.
@@ -334,7 +336,7 @@ class Padstacks(object):
334
336
  >>> groups = edb_padstacks._layout.pin_groups # New way
335
337
  """
336
338
  warnings.warn(
337
- "`pingroups` is deprecated and is now located here " "`pyedb.grpc.core.layout.pin_groups` instead.",
339
+ "`pingroups` is deprecated and is now located here `pyedb.grpc.core.layout.pin_groups` instead.",
338
340
  DeprecationWarning,
339
341
  )
340
342
  return self._layout.pin_groups
@@ -380,10 +382,7 @@ class Padstacks(object):
380
382
  Examples
381
383
  --------
382
384
  >>> via_name = edb_padstacks.create_circular_padstack(
383
- ... padstackname="VIA1",
384
- ... holediam="200um",
385
- ... paddiam="400um",
386
- ... antipaddiam="600um"
385
+ ... padstackname="VIA1", holediam="200um", paddiam="400um", antipaddiam="600um"
387
386
  ... )
388
387
  """
389
388
 
@@ -481,6 +480,7 @@ class Padstacks(object):
481
480
  if p.net_name in net_names:
482
481
  if not p.delete(): # pragma: no cover
483
482
  return False
483
+ self.clear_instances_cache()
484
484
  return True
485
485
 
486
486
  def set_solderball(self, padstackInst, sballLayer_name, isTopPlaced=True, ballDiam=100e-6):
@@ -1143,6 +1143,7 @@ class Padstacks(object):
1143
1143
  layer_map=None,
1144
1144
  )
1145
1145
  padstack_instance.is_layout_pin = is_pin
1146
+ self.clear_instances_cache()
1146
1147
  return PadstackInstance(self._pedb, padstack_instance)
1147
1148
  else:
1148
1149
  raise RuntimeError("Place padstack failed")
@@ -1299,8 +1300,7 @@ class Padstacks(object):
1299
1300
  List of padstack instances associated with the specified net.
1300
1301
  """
1301
1302
  warnings.warn(
1302
- "`get_padstack_instance_by_net_name` is deprecated, use `get_instances` with `net_name` "
1303
- "parameter instead.",
1303
+ "`get_padstack_instance_by_net_name` is deprecated, use `get_instances` with `net_name` parameter instead.",
1304
1304
  DeprecationWarning,
1305
1305
  )
1306
1306
  return self.get_instances(net_name=net)
@@ -1516,7 +1516,7 @@ class Padstacks(object):
1516
1516
  minimum_via_number: int = 6,
1517
1517
  selected_angles: Optional[List[float]] = None,
1518
1518
  padstack_instances_id: Optional[List[int]] = None,
1519
- ) -> None:
1519
+ ) -> List[str]:
1520
1520
  """Replace padstack instances along lines into a single polygon.
1521
1521
 
1522
1522
  Detect all pad-stack instances that are placed along lines and replace them by a single polygon based one
@@ -1605,66 +1605,13 @@ class Padstacks(object):
1605
1605
  inst.delete()
1606
1606
  return instances_created
1607
1607
 
1608
- def reduce_via_in_bounding_box(self, bounding_box, x_samples, y_samples, nets=None):
1609
- """Reduces the number of vias intersecting bounding box and nets by x and y samples.
1610
-
1611
- Parameters
1612
- ----------
1613
- bounding_box : tuple or list.
1614
- bounding box, [x1, y1, x2, y2]
1615
- x_samples : int
1616
- y_samples : int
1617
- nets : str or list, optional
1618
- net name or list of nets name applying filtering on padstack instances selection. If ``None`` is provided
1619
- all instances are included in the index. Default value is ``None``.
1620
-
1621
- Returns
1622
- -------
1623
- bool
1624
- ``True`` when succeeded ``False`` when failed.
1625
- """
1626
-
1627
- padstacks_inbox = self.get_padstack_instances_intersecting_bounding_box(bounding_box, nets)
1628
- if not padstacks_inbox:
1629
- self._logger.info("no padstack in bounding box")
1630
- return False
1631
- else:
1632
- if len(padstacks_inbox) <= (x_samples * y_samples):
1633
- self._logger.info(f"more samples {x_samples * y_samples} than existing {len(padstacks_inbox)}")
1634
- return False
1635
- else:
1636
- # extract ids and positions
1637
- vias = {item: self.instances[item].position for item in padstacks_inbox}
1638
- ids, positions = zip(*vias.items())
1639
- pt_x, pt_y = zip(*positions)
1640
-
1641
- # meshgrid
1642
- _x_min, _x_max = min(pt_x), max(pt_x)
1643
- _y_min, _y_max = min(pt_y), max(pt_y)
1644
-
1645
- x_grid, y_grid = np.meshgrid(
1646
- np.linspace(_x_min, _x_max, x_samples), np.linspace(_y_min, _y_max, y_samples)
1647
- )
1648
-
1649
- # mapping to meshgrid
1650
- to_keep = {
1651
- ids[np.argmin(np.square(_x - pt_x) + np.square(_y - pt_y))]
1652
- for _x, _y in zip(x_grid.ravel(), y_grid.ravel())
1653
- }
1654
-
1655
- for item in padstacks_inbox:
1656
- if item not in to_keep:
1657
- self.instances[item].delete()
1658
-
1659
- return True
1660
-
1661
1608
  def merge_via(
1662
1609
  self,
1663
1610
  contour_boxes: List[List[float]],
1664
1611
  net_filter: Optional[Union[str, List[str]]] = None,
1665
1612
  start_layer: Optional[str] = None,
1666
1613
  stop_layer: Optional[str] = None,
1667
- ) -> bool:
1614
+ ) -> List[str]:
1668
1615
  """Evaluate pad-stack instances included on the provided point list and replace all by single instance.
1669
1616
 
1670
1617
  Parameters
@@ -1684,7 +1631,6 @@ class Padstacks(object):
1684
1631
 
1685
1632
  """
1686
1633
 
1687
- import numpy as np
1688
1634
  from scipy.spatial import ConvexHull
1689
1635
 
1690
1636
  merged_via_ids = []
@@ -1728,6 +1674,7 @@ class Padstacks(object):
1728
1674
  )
1729
1675
  merged_via_ids.append(merged_instance.edb_uid)
1730
1676
  [self.instances[inst].delete() for inst in instances]
1677
+ self.clear_instances_cache()
1731
1678
  return merged_via_ids
1732
1679
 
1733
1680
  def reduce_via_in_bounding_box(
@@ -1783,6 +1730,7 @@ class Padstacks(object):
1783
1730
  for item in padstacks_inbox:
1784
1731
  if item not in to_keep:
1785
1732
  all_instances[item].delete()
1733
+ self.clear_instances_cache()
1786
1734
  return True
1787
1735
 
1788
1736
  @staticmethod
@@ -1938,5 +1886,5 @@ class Padstacks(object):
1938
1886
  to_delete = set(padstacks) - to_keep
1939
1887
  for _id in to_delete:
1940
1888
  all_instances[_id].delete()
1941
-
1889
+ self.clear_instances_cache()
1942
1890
  return list(to_keep), grid