pyedb 0.38.0__py3-none-any.whl → 0.39.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 (205) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/common/nets.py +53 -139
  3. pyedb/configuration/cfg_components.py +1 -1
  4. pyedb/configuration/cfg_general.py +4 -2
  5. pyedb/configuration/cfg_modeler.py +1 -1
  6. pyedb/configuration/cfg_package_definition.py +1 -1
  7. pyedb/configuration/cfg_padstacks.py +1 -1
  8. pyedb/configuration/cfg_ports_sources.py +56 -23
  9. pyedb/configuration/configuration.py +18 -1
  10. pyedb/dotnet/{application → database}/Variables.py +21 -21
  11. pyedb/dotnet/{edb_core → database}/cell/connectable.py +5 -5
  12. pyedb/dotnet/{edb_core → database}/cell/hierarchy/component.py +11 -11
  13. pyedb/dotnet/{edb_core → database}/cell/hierarchy/hierarchy_obj.py +1 -1
  14. pyedb/dotnet/{edb_core → database}/cell/hierarchy/model.py +1 -1
  15. pyedb/dotnet/{edb_core → database}/cell/layout.py +17 -17
  16. pyedb/dotnet/{edb_core → database}/cell/layout_obj.py +3 -3
  17. pyedb/dotnet/{edb_core → database}/cell/primitive/bondwire.py +1 -1
  18. pyedb/dotnet/{edb_core → database}/cell/primitive/path.py +4 -4
  19. pyedb/dotnet/{edb_core → database}/cell/primitive/primitive.py +14 -14
  20. pyedb/dotnet/{edb_core → database}/cell/terminal/bundle_terminal.py +2 -2
  21. pyedb/dotnet/{edb_core → database}/cell/terminal/edge_terminal.py +4 -4
  22. pyedb/dotnet/{edb_core → database}/cell/terminal/padstack_instance_terminal.py +2 -2
  23. pyedb/dotnet/{edb_core → database}/cell/terminal/pingroup_terminal.py +2 -2
  24. pyedb/dotnet/{edb_core → database}/cell/terminal/point_terminal.py +2 -2
  25. pyedb/dotnet/{edb_core → database}/cell/terminal/terminal.py +11 -11
  26. pyedb/dotnet/{edb_core → database}/cell/voltage_regulator.py +2 -2
  27. pyedb/dotnet/{edb_core → database}/components.py +101 -124
  28. pyedb/dotnet/{edb_core → database}/definition/component_def.py +5 -5
  29. pyedb/dotnet/{edb_core → database}/definition/component_model.py +1 -1
  30. pyedb/dotnet/{edb_core → database}/definition/definition_obj.py +1 -1
  31. pyedb/dotnet/{edb_core → database}/definition/definitions.py +2 -2
  32. pyedb/dotnet/{edb_core → database}/definition/package_def.py +4 -4
  33. pyedb/dotnet/{edb_core → database}/dotnet/database.py +8 -8
  34. pyedb/dotnet/{edb_core → database}/dotnet/primitive.py +9 -9
  35. pyedb/dotnet/{edb_core → database}/edb_data/control_file.py +12 -12
  36. pyedb/dotnet/{edb_core → database}/edb_data/hfss_extent_info.py +7 -7
  37. pyedb/dotnet/{edb_core → database}/edb_data/nets_data.py +10 -13
  38. pyedb/dotnet/{edb_core → database}/edb_data/padstacks_data.py +16 -16
  39. pyedb/dotnet/{edb_core → database}/edb_data/ports.py +4 -4
  40. pyedb/dotnet/{edb_core → database}/edb_data/primitives_data.py +5 -5
  41. pyedb/dotnet/{edb_core → database}/edb_data/raptor_x_simulation_setup_data.py +4 -4
  42. pyedb/dotnet/{edb_core → database}/edb_data/simulation_configuration.py +10 -10
  43. pyedb/dotnet/{edb_core → database}/edb_data/sources.py +4 -4
  44. pyedb/dotnet/{edb_core → database}/edb_data/variables.py +1 -1
  45. pyedb/dotnet/{edb_core → database}/geometry/polygon_data.py +4 -4
  46. pyedb/dotnet/{edb_core → database}/hfss.py +8 -8
  47. pyedb/dotnet/{edb_core → database}/layout_obj_instance.py +1 -1
  48. pyedb/dotnet/{edb_core → database}/layout_validation.py +2 -2
  49. pyedb/dotnet/{edb_core → database}/materials.py +23 -8
  50. pyedb/dotnet/{edb_core → database}/modeler.py +27 -27
  51. pyedb/dotnet/{edb_core → database}/net_class.py +8 -8
  52. pyedb/dotnet/{edb_core → database}/nets.py +12 -12
  53. pyedb/dotnet/{edb_core → database}/padstack.py +15 -15
  54. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/mesh_operation.py +1 -1
  55. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/settings.py +3 -3
  56. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sim_setup_info.py +2 -2
  57. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/simulation_settings.py +1 -1
  58. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/siw_dc_ir_settings.py +1 -1
  59. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sweep_data.py +1 -1
  60. pyedb/dotnet/{edb_core → database}/siwave.py +10 -10
  61. pyedb/dotnet/{edb_core → database}/stackup.py +12 -12
  62. pyedb/dotnet/{edb_core → database}/utilities/hfss_simulation_setup.py +15 -15
  63. pyedb/dotnet/{edb_core → database}/utilities/obj_base.py +1 -1
  64. pyedb/dotnet/{edb_core → database}/utilities/simulation_setup.py +3 -3
  65. pyedb/dotnet/{edb_core → database}/utilities/siwave_simulation_setup.py +6 -6
  66. pyedb/dotnet/edb.py +117 -112
  67. pyedb/generic/design_types.py +26 -19
  68. pyedb/generic/general_methods.py +1 -1
  69. pyedb/generic/plot.py +0 -2
  70. pyedb/grpc/database/__init__.py +1 -0
  71. pyedb/grpc/database/components.py +2354 -0
  72. pyedb/grpc/database/control_file.py +1277 -0
  73. pyedb/grpc/database/definition/component_def.py +218 -0
  74. pyedb/grpc/database/definition/component_model.py +39 -0
  75. pyedb/grpc/database/definition/component_pin.py +32 -0
  76. pyedb/grpc/database/definition/materials.py +1207 -0
  77. pyedb/grpc/database/definition/n_port_component_model.py +34 -0
  78. pyedb/grpc/database/definition/package_def.py +227 -0
  79. pyedb/grpc/database/definition/padstack_def.py +842 -0
  80. pyedb/grpc/database/definitions.py +70 -0
  81. pyedb/grpc/database/general.py +43 -0
  82. pyedb/grpc/database/geometry/__init__.py +0 -0
  83. pyedb/grpc/database/geometry/arc_data.py +93 -0
  84. pyedb/grpc/database/geometry/point_3d_data.py +79 -0
  85. pyedb/grpc/database/geometry/point_data.py +30 -0
  86. pyedb/grpc/database/geometry/polygon_data.py +133 -0
  87. pyedb/grpc/database/hfss.py +1279 -0
  88. pyedb/grpc/database/hierarchy/__init__.py +0 -0
  89. pyedb/grpc/database/hierarchy/component.py +1301 -0
  90. pyedb/grpc/database/hierarchy/model.py +31 -0
  91. pyedb/grpc/database/hierarchy/netlist_model.py +30 -0
  92. pyedb/grpc/database/hierarchy/pin_pair_model.py +128 -0
  93. pyedb/grpc/database/hierarchy/pingroup.py +245 -0
  94. pyedb/grpc/database/hierarchy/s_parameter_model.py +33 -0
  95. pyedb/grpc/database/hierarchy/spice_model.py +48 -0
  96. pyedb/grpc/database/layers/__init__.py +0 -0
  97. pyedb/grpc/database/layers/layer.py +57 -0
  98. pyedb/grpc/database/layers/stackup_layer.py +410 -0
  99. pyedb/grpc/database/layout/__init__.py +0 -0
  100. pyedb/grpc/database/layout/cell.py +30 -0
  101. pyedb/grpc/database/layout/layout.py +196 -0
  102. pyedb/grpc/database/layout/voltage_regulator.py +149 -0
  103. pyedb/grpc/database/layout_validation.py +319 -0
  104. pyedb/grpc/database/modeler.py +1468 -0
  105. pyedb/grpc/database/net/__init__.py +0 -0
  106. pyedb/grpc/database/net/differential_pair.py +138 -0
  107. pyedb/grpc/database/net/extended_net.py +340 -0
  108. pyedb/grpc/database/net/net.py +198 -0
  109. pyedb/grpc/database/net/net_class.py +93 -0
  110. pyedb/grpc/database/nets.py +633 -0
  111. pyedb/grpc/database/padstacks.py +1500 -0
  112. pyedb/grpc/database/ports/__init__.py +0 -0
  113. pyedb/grpc/database/ports/ports.py +396 -0
  114. pyedb/grpc/database/primitive/__init__.py +3 -0
  115. pyedb/grpc/database/primitive/bondwire.py +181 -0
  116. pyedb/grpc/database/primitive/circle.py +75 -0
  117. pyedb/grpc/database/primitive/padstack_instance.py +1116 -0
  118. pyedb/grpc/database/primitive/path.py +346 -0
  119. pyedb/grpc/database/primitive/polygon.py +276 -0
  120. pyedb/grpc/database/primitive/primitive.py +739 -0
  121. pyedb/grpc/database/primitive/rectangle.py +146 -0
  122. pyedb/grpc/database/simulation_setup/__init__.py +0 -0
  123. pyedb/grpc/database/simulation_setup/adaptive_frequency.py +33 -0
  124. pyedb/grpc/database/simulation_setup/hfss_advanced_meshing_settings.py +32 -0
  125. pyedb/grpc/database/simulation_setup/hfss_advanced_settings.py +59 -0
  126. pyedb/grpc/database/simulation_setup/hfss_dcr_settings.py +35 -0
  127. pyedb/grpc/database/simulation_setup/hfss_general_settings.py +61 -0
  128. pyedb/grpc/database/simulation_setup/hfss_settings_options.py +78 -0
  129. pyedb/grpc/database/simulation_setup/hfss_simulation_settings.py +118 -0
  130. pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +355 -0
  131. pyedb/grpc/database/simulation_setup/hfss_solver_settings.py +34 -0
  132. pyedb/grpc/database/simulation_setup/mesh_operation.py +34 -0
  133. pyedb/grpc/database/simulation_setup/raptor_x_advanced_settings.py +34 -0
  134. pyedb/grpc/database/simulation_setup/raptor_x_general_settings.py +33 -0
  135. pyedb/grpc/database/simulation_setup/raptor_x_simulation_settings.py +64 -0
  136. pyedb/grpc/database/simulation_setup/raptor_x_simulation_setup.py +125 -0
  137. pyedb/grpc/database/simulation_setup/siwave_dcir_simulation_setup.py +34 -0
  138. pyedb/grpc/database/simulation_setup/siwave_simulation_setup.py +119 -0
  139. pyedb/grpc/database/simulation_setup/sweep_data.py +32 -0
  140. pyedb/grpc/database/siwave.py +1023 -0
  141. pyedb/grpc/database/source_excitations.py +2572 -0
  142. pyedb/grpc/database/stackup.py +2574 -0
  143. pyedb/grpc/database/terminal/__init__.py +0 -0
  144. pyedb/grpc/database/terminal/bundle_terminal.py +218 -0
  145. pyedb/grpc/database/terminal/edge_terminal.py +51 -0
  146. pyedb/grpc/database/terminal/padstack_instance_terminal.py +171 -0
  147. pyedb/grpc/database/terminal/pingroup_terminal.py +162 -0
  148. pyedb/grpc/database/terminal/point_terminal.py +99 -0
  149. pyedb/grpc/database/terminal/terminal.py +470 -0
  150. pyedb/grpc/database/utility/__init__.py +3 -0
  151. pyedb/grpc/database/utility/constants.py +25 -0
  152. pyedb/grpc/database/utility/heat_sink.py +124 -0
  153. pyedb/grpc/database/utility/hfss_extent_info.py +448 -0
  154. pyedb/grpc/database/utility/layout_statistics.py +277 -0
  155. pyedb/grpc/database/utility/rlc.py +80 -0
  156. pyedb/grpc/database/utility/simulation_configuration.py +3305 -0
  157. pyedb/grpc/database/utility/sources.py +388 -0
  158. pyedb/grpc/database/utility/sweep_data_distribution.py +83 -0
  159. pyedb/grpc/database/utility/xml_control_file.py +1277 -0
  160. pyedb/grpc/edb.py +4152 -0
  161. pyedb/grpc/edb_init.py +481 -0
  162. pyedb/grpc/rpc_session.py +177 -0
  163. pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +3 -2
  164. pyedb/ipc2581/ecad/cad_data/feature.py +4 -3
  165. pyedb/ipc2581/ecad/cad_data/layer_feature.py +32 -20
  166. pyedb/ipc2581/ecad/cad_data/outline.py +3 -2
  167. pyedb/ipc2581/ecad/cad_data/package.py +4 -3
  168. pyedb/ipc2581/ecad/cad_data/path.py +82 -31
  169. pyedb/ipc2581/ecad/cad_data/polygon.py +122 -60
  170. pyedb/ipc2581/ecad/cad_data/profile.py +13 -12
  171. pyedb/ipc2581/ecad/cad_data/step.py +53 -21
  172. pyedb/ipc2581/ipc2581.py +47 -49
  173. pyedb/modeler/geometry_operators.py +1 -1
  174. {pyedb-0.38.0.dist-info → pyedb-0.39.1.dist-info}/METADATA +5 -2
  175. pyedb-0.39.1.dist-info/RECORD +288 -0
  176. pyedb-0.38.0.dist-info/RECORD +0 -195
  177. /pyedb/dotnet/{edb_core → database}/__init__.py +0 -0
  178. /pyedb/dotnet/{application → database/cell}/__init__.py +0 -0
  179. /pyedb/dotnet/{edb_core/cell → database/cell/hierarchy}/__init__.py +0 -0
  180. /pyedb/dotnet/{edb_core → database}/cell/hierarchy/netlist_model.py +0 -0
  181. /pyedb/dotnet/{edb_core → database}/cell/hierarchy/pin_pair_model.py +0 -0
  182. /pyedb/dotnet/{edb_core → database}/cell/hierarchy/s_parameter_model.py +0 -0
  183. /pyedb/dotnet/{edb_core → database}/cell/hierarchy/spice_model.py +0 -0
  184. /pyedb/dotnet/{edb_core → database}/cell/primitive/__init__.py +0 -0
  185. /pyedb/dotnet/{edb_core/cell/hierarchy → database/cell/terminal}/__init__.py +0 -0
  186. /pyedb/dotnet/{edb_core/cell/terminal → database/definition}/__init__.py +0 -0
  187. /pyedb/dotnet/{edb_core/definition → database/dotnet}/__init__.py +0 -0
  188. /pyedb/dotnet/{edb_core/dotnet → database/edb_data}/__init__.py +0 -0
  189. /pyedb/dotnet/{edb_core → database}/edb_data/design_options.py +0 -0
  190. /pyedb/dotnet/{edb_core → database}/edb_data/edbvalue.py +0 -0
  191. /pyedb/dotnet/{edb_core → database}/edb_data/layer_data.py +0 -0
  192. /pyedb/dotnet/{edb_core → database}/edb_data/utilities.py +0 -0
  193. /pyedb/dotnet/{edb_core → database}/general.py +0 -0
  194. /pyedb/dotnet/{edb_core/edb_data → database/geometry}/__init__.py +0 -0
  195. /pyedb/dotnet/{edb_core → database}/geometry/point_data.py +0 -0
  196. /pyedb/dotnet/{edb_core → database}/sim_setup_data/__init__.py +0 -0
  197. /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/__init__.py +0 -0
  198. /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/adaptive_frequency_data.py +0 -0
  199. /pyedb/dotnet/{edb_core/geometry → database/sim_setup_data/io}/__init__.py +0 -0
  200. /pyedb/dotnet/{edb_core → database}/sim_setup_data/io/siwave.py +0 -0
  201. /pyedb/dotnet/{edb_core → database}/utilities/__init__.py +0 -0
  202. /pyedb/dotnet/{edb_core → database}/utilities/heatsink.py +0 -0
  203. /pyedb/{dotnet/edb_core/sim_setup_data/io → grpc/database/definition}/__init__.py +0 -0
  204. {pyedb-0.38.0.dist-info → pyedb-0.39.1.dist-info}/LICENSE +0 -0
  205. {pyedb-0.38.0.dist-info → pyedb-0.39.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,1500 @@
1
+ # Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ """
24
+ This module contains the `EdbPadstacks` class.
25
+ """
26
+ import math
27
+ import warnings
28
+
29
+ from ansys.edb.core.definition.padstack_def_data import (
30
+ PadGeometryType as GrpcPadGeometryType,
31
+ )
32
+ from ansys.edb.core.definition.padstack_def_data import (
33
+ PadstackDefData as GrpcPadstackDefData,
34
+ )
35
+ from ansys.edb.core.definition.padstack_def_data import (
36
+ PadstackHoleRange as GrpcPadstackHoleRange,
37
+ )
38
+ from ansys.edb.core.definition.padstack_def_data import (
39
+ SolderballPlacement as GrpcSolderballPlacement,
40
+ )
41
+ from ansys.edb.core.definition.padstack_def_data import (
42
+ SolderballShape as GrpcSolderballShape,
43
+ )
44
+ from ansys.edb.core.definition.padstack_def_data import PadType as GrpcPadType
45
+ from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
46
+ from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
47
+ from ansys.edb.core.utility.value import Value as GrpcValue
48
+ import rtree
49
+
50
+ from pyedb.generic.general_methods import generate_unique_name
51
+ from pyedb.grpc.database.definition.padstack_def import PadstackDef
52
+ from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
53
+ from pyedb.modeler.geometry_operators import GeometryOperators
54
+
55
+
56
+ class Padstacks(object):
57
+ """Manages EDB methods for nets management accessible from `Edb.padstacks` property.
58
+
59
+ Examples
60
+ --------
61
+ >>> from pyedb import Edb
62
+ >>> edbapp = Edb("myaedbfolder", edbversion="2024.2")
63
+ >>> edb_padstacks = edbapp.padstacks
64
+ """
65
+
66
+ def __getitem__(self, name):
67
+ """Get a padstack definition or instance from the Edb project.
68
+
69
+ Parameters
70
+ ----------
71
+ name : str, int
72
+
73
+ Returns
74
+ -------
75
+ :class:`pyedb.dotnet.database.cell.hierarchy.component.EDBComponent`
76
+
77
+ """
78
+ if isinstance(name, int) and name in self.instances:
79
+ return self.instances(name)
80
+ elif name in self.definitions:
81
+ return self.definitions[name]
82
+ else:
83
+ for i in list(self.instances.values()):
84
+ if i.name == name or i.aedt_name == name:
85
+ return i
86
+ self._pedb.logger.error("Component or definition not found.")
87
+ return
88
+
89
+ def __init__(self, p_edb):
90
+ self._pedb = p_edb
91
+ self._instances = {}
92
+ self._definitions = {}
93
+
94
+ @property
95
+ def _active_layout(self):
96
+ """ """
97
+ return self._pedb.active_layout
98
+
99
+ @property
100
+ def _layout(self):
101
+ """ """
102
+ return self._pedb.layout
103
+
104
+ @property
105
+ def db(self):
106
+ """Db object."""
107
+ return self._pedb.active_db
108
+
109
+ @property
110
+ def _logger(self):
111
+ """ """
112
+ return self._pedb.logger
113
+
114
+ @property
115
+ def _layers(self):
116
+ """ """
117
+ return self._pedb.stackup.layers
118
+
119
+ def int_to_pad_type(self, val=0):
120
+ """Convert an integer to an EDB.PadGeometryType.
121
+
122
+ Parameters
123
+ ----------
124
+ val : int
125
+
126
+ Returns
127
+ -------
128
+ object
129
+ EDB.PadType enumerator value.
130
+ """
131
+
132
+ if val == 0:
133
+ return GrpcPadType.REGULAR_PAD
134
+ elif val == 1:
135
+ return GrpcPadType.ANTI_PAD
136
+ elif val == 2:
137
+ return GrpcPadType.THERMAL_PAD
138
+ elif val == 3:
139
+ return GrpcPadType.HOLE
140
+ elif val == 4:
141
+ return GrpcPadType.UNKNOWN_GEOM_TYPE
142
+ else:
143
+ return val
144
+
145
+ def int_to_geometry_type(self, val=0):
146
+ """Convert an integer to an EDB.PadGeometryType.
147
+
148
+ Parameters
149
+ ----------
150
+ val : int
151
+
152
+ Returns
153
+ -------
154
+ object
155
+ EDB.PadGeometryType enumerator value.
156
+ """
157
+ if val == 0:
158
+ return GrpcPadGeometryType.PADGEOMTYPE_NO_GEOMETRY
159
+ elif val == 1:
160
+ return GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
161
+ elif val == 2:
162
+ return GrpcPadGeometryType.PADGEOMTYPE_SQUARE
163
+ elif val == 3:
164
+ return GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE
165
+ elif val == 4:
166
+ return GrpcPadGeometryType.PADGEOMTYPE_OVAL
167
+ elif val == 5:
168
+ return GrpcPadGeometryType.PADGEOMTYPE_BULLET
169
+ elif val == 6:
170
+ return GrpcPadGeometryType.PADGEOMTYPE_NSIDED_POLYGON
171
+ elif val == 7:
172
+ return GrpcPadGeometryType.PADGEOMTYPE_POLYGON
173
+ elif val == 8:
174
+ return GrpcPadGeometryType.PADGEOMTYPE_ROUND45
175
+ elif val == 9:
176
+ return GrpcPadGeometryType.PADGEOMTYPE_ROUND90
177
+ elif val == 10:
178
+ return GrpcPadGeometryType.PADGEOMTYPE_SQUARE45
179
+ elif val == 11:
180
+ return GrpcPadGeometryType.PADGEOMTYPE_SQUARE90
181
+ else:
182
+ return val
183
+
184
+ @property
185
+ def definitions(self):
186
+ """Padstack definitions.
187
+
188
+ Returns
189
+ -------
190
+ dict[str, :class:`pyedb.dotnet.database.edb_data.padstacks_data.EdbPadstack`]
191
+ List of definitions via padstack definitions.
192
+
193
+ """
194
+ if len(self._definitions) == len(self.db.padstack_defs):
195
+ return self._definitions
196
+ self._definitions = {}
197
+ for padstack_def in self._pedb.db.padstack_defs:
198
+ if len(padstack_def.data.layer_names) >= 1:
199
+ self._definitions[padstack_def.name] = PadstackDef(self._pedb, padstack_def)
200
+ return self._definitions
201
+
202
+ @property
203
+ def instances(self):
204
+ """Dictionary of all padstack instances (vias and pins).
205
+
206
+ Returns
207
+ -------
208
+ dict[int, :class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`]
209
+ List of padstack instances.
210
+
211
+ """
212
+ pad_stack_inst = self._pedb.layout.padstack_instances
213
+ if len(self._instances) == len(pad_stack_inst):
214
+ return self._instances
215
+ self._instances = {i.edb_uid: PadstackInstance(self._pedb, i) for i in pad_stack_inst}
216
+ return self._instances
217
+
218
+ @property
219
+ def instances_by_name(self):
220
+ """Dictionary of all padstack instances (vias and pins) by name.
221
+
222
+ Returns
223
+ -------
224
+ dict[str, :class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`]
225
+ List of padstack instances.
226
+
227
+ """
228
+ padstack_instances = {}
229
+ for _, edb_padstack_instance in self.instances.items():
230
+ if edb_padstack_instance.aedt_name:
231
+ padstack_instances[edb_padstack_instance.aedt_name] = edb_padstack_instance
232
+ return padstack_instances
233
+
234
+ def find_instance_by_id(self, value: int):
235
+ """Find a padstack instance by database id.
236
+
237
+ Parameters
238
+ ----------
239
+ value : int
240
+ """
241
+ return self._pedb.modeler.find_object_by_id(value)
242
+
243
+ @property
244
+ def pins(self):
245
+ """Dictionary of all pins instances (belonging to component).
246
+
247
+ Returns
248
+ -------
249
+ dic[str, :class:`dotnet.database.edb_data.definitions.EDBPadstackInstance`]
250
+ Dictionary of EDBPadstackInstance Components.
251
+
252
+
253
+ Examples
254
+ --------
255
+ >>> edbapp = dotnet.Edb("myproject.aedb")
256
+ >>> pin_net_name = edbapp.pins[424968329].netname
257
+ """
258
+ pins = {}
259
+ for instancename, instance in self.instances.items():
260
+ if instance.is_pin and instance.component:
261
+ pins[instancename] = instance
262
+ return pins
263
+
264
+ @property
265
+ def vias(self):
266
+ """Dictionary of all vias instances not belonging to component.
267
+
268
+ Returns
269
+ -------
270
+ dic[str, :class:`dotnet.database.edb_data.definitions.EDBPadstackInstance`]
271
+ Dictionary of EDBPadstackInstance Components.
272
+
273
+
274
+ Examples
275
+ --------
276
+ >>> edbapp = dotnet.Edb("myproject.aedb")
277
+ >>> pin_net_name = edbapp.pins[424968329].netname
278
+ """
279
+ pnames = list(self.pins.keys())
280
+ vias = {i: j for i, j in self.instances.items() if i not in pnames}
281
+ return vias
282
+
283
+ @property
284
+ def pingroups(self):
285
+ """All Layout Pin groups.
286
+
287
+ . deprecated:: pyedb 0.28.0
288
+ Use :func:`pyedb.grpc.core.layout.pin_groups` instead.
289
+
290
+ Returns
291
+ -------
292
+ list
293
+ List of all layout pin groups.
294
+ """
295
+ warnings.warn(
296
+ "`pingroups` is deprecated and is now located here " "`pyedb.grpc.core.layout.pin_groups` instead.",
297
+ DeprecationWarning,
298
+ )
299
+ return self._layout.pin_groups
300
+
301
+ @property
302
+ def pad_type(self):
303
+ """Return a PadType Enumerator."""
304
+
305
+ def create_circular_padstack(
306
+ self,
307
+ padstackname=None,
308
+ holediam="300um",
309
+ paddiam="400um",
310
+ antipaddiam="600um",
311
+ startlayer=None,
312
+ endlayer=None,
313
+ ):
314
+ """Create a circular padstack.
315
+
316
+ Parameters
317
+ ----------
318
+ padstackname : str, optional
319
+ Name of the padstack. The default is ``None``.
320
+ holediam : str, optional
321
+ Diameter of the hole with units. The default is ``"300um"``.
322
+ paddiam : str, optional
323
+ Diameter of the pad with units. The default is ``"400um"``.
324
+ antipaddiam : str, optional
325
+ Diameter of the antipad with units. The default is ``"600um"``.
326
+ startlayer : str, optional
327
+ Starting layer. The default is ``None``, in which case the top
328
+ is the starting layer.
329
+ endlayer : str, optional
330
+ Ending layer. The default is ``None``, in which case the bottom
331
+ is the ending layer.
332
+
333
+ Returns
334
+ -------
335
+ str
336
+ Name of the padstack if the operation is successful.
337
+ """
338
+
339
+ padstack_def = PadstackDef.create(self._pedb.db, padstackname)
340
+
341
+ padstack_data = GrpcPadstackDefData.create()
342
+ list_values = [GrpcValue(holediam), GrpcValue(paddiam), GrpcValue(antipaddiam)]
343
+ padstack_data.set_hole_parameters(
344
+ offset_x=GrpcValue(0),
345
+ offset_y=GrpcValue(0),
346
+ rotation=GrpcValue(0),
347
+ type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
348
+ sizes=list_values,
349
+ )
350
+
351
+ padstack_data.hole_range = GrpcPadstackHoleRange.UPPER_PAD_TO_LOWER_PAD
352
+ layers = list(self._pedb.stackup.signal_layers.keys())
353
+ if not startlayer:
354
+ startlayer = layers[0]
355
+ if not endlayer:
356
+ endlayer = layers[len(layers) - 1]
357
+
358
+ antipad_shape = GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
359
+ started = False
360
+ padstack_data.set_pad_parameters(
361
+ layer="Default",
362
+ pad_type=GrpcPadType.REGULAR_PAD,
363
+ type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
364
+ offset_x=GrpcValue(0),
365
+ offset_y=GrpcValue(0),
366
+ rotation=GrpcValue(0),
367
+ sizes=[GrpcValue(paddiam)],
368
+ )
369
+
370
+ padstack_data.set_pad_parameters(
371
+ layer="Default",
372
+ pad_type=GrpcPadType.ANTI_PAD,
373
+ type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
374
+ offset_x=GrpcValue(0),
375
+ offset_y=GrpcValue(0),
376
+ rotation=GrpcValue(0),
377
+ sizes=[GrpcValue(antipaddiam)],
378
+ )
379
+
380
+ for layer in layers:
381
+ if layer == startlayer:
382
+ started = True
383
+ if layer == endlayer:
384
+ started = False
385
+ if started:
386
+ padstack_data.set_pad_parameters(
387
+ layer=layer,
388
+ pad_type=GrpcPadType.ANTI_PAD,
389
+ type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
390
+ offset_x=GrpcValue(0),
391
+ offset_y=GrpcValue(0),
392
+ rotation=GrpcValue(0),
393
+ sizes=[GrpcValue(antipaddiam)],
394
+ )
395
+
396
+ padstack_data.set_pad_parameters(
397
+ layer=layer,
398
+ pad_type=GrpcPadType.ANTI_PAD,
399
+ type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
400
+ offset_x=GrpcValue(0),
401
+ offset_y=GrpcValue(0),
402
+ rotation=GrpcValue(0),
403
+ sizes=[GrpcValue(antipaddiam)],
404
+ )
405
+
406
+ padstack_def.data = padstack_data
407
+
408
+ def delete_padstack_instances(self, net_names): # pragma: no cover
409
+ """Delete padstack instances by net names.
410
+
411
+ Parameters
412
+ ----------
413
+ net_names : str, list
414
+ Names of the nets to delete.
415
+
416
+ Returns
417
+ -------
418
+ bool
419
+ ``True`` when successful, ``False`` when failed.
420
+
421
+ References
422
+ ----------
423
+
424
+ >>> Edb.padstacks.delete_padstack_instances(net_names=["GND"])
425
+ """
426
+ if not isinstance(net_names, list): # pragma: no cover
427
+ net_names = [net_names]
428
+
429
+ for p_id, p in self.instances.items():
430
+ if p.net_name in net_names:
431
+ if not p.delete(): # pragma: no cover
432
+ return False
433
+ return True
434
+
435
+ def set_solderball(self, padstackInst, sballLayer_name, isTopPlaced=True, ballDiam=100e-6):
436
+ """Set solderball for the given PadstackInstance.
437
+
438
+ Parameters
439
+ ----------
440
+ padstackInst : Edb.Cell.Primitive.PadstackInstance or int
441
+ Padstack instance id or object.
442
+ sballLayer_name : str,
443
+ Name of the layer where the solder ball is placed. No default values.
444
+ isTopPlaced : bool, optional.
445
+ Bollean triggering is the solder ball is placed on Top or Bottom of the layer stackup.
446
+ ballDiam : double, optional,
447
+ Solder ball diameter value.
448
+
449
+ Returns
450
+ -------
451
+ bool
452
+
453
+ """
454
+ if isinstance(padstackInst, int):
455
+ psdef = self.definitions[self.instances[padstackInst].padstack_definition].edb_padstack
456
+ padstackInst = self.instances[padstackInst]
457
+
458
+ else:
459
+ psdef = padstackInst.padstack_def
460
+ newdefdata = GrpcPadstackDefData.create(psdef.data)
461
+ newdefdata.solder_ball_shape = GrpcSolderballShape.SOLDERBALL_CYLINDER
462
+ newdefdata.solder_ball_param(GrpcValue(ballDiam), GrpcValue(ballDiam))
463
+ sball_placement = (
464
+ GrpcSolderballPlacement.ABOVE_PADSTACK if isTopPlaced else GrpcSolderballPlacement.BELOW_PADSTACK
465
+ )
466
+ newdefdata.solder_ball_placement = sball_placement
467
+ psdef.data = newdefdata
468
+ sball_layer = [lay._edb_layer for lay in list(self._layers.values()) if lay.name == sballLayer_name][0]
469
+ if sball_layer is not None:
470
+ padstackInst.solder_ball_layer = sball_layer
471
+ return True
472
+
473
+ return False
474
+
475
+ def create_coax_port(self, padstackinstance, use_dot_separator=True, name=None):
476
+ """Create HFSS 3Dlayout coaxial lumped port on a pastack
477
+ Requires to have solder ball defined before calling this method.
478
+
479
+ . deprecated:: pyedb 0.28.0
480
+ Use :func:`pyedb.grpc.core.excitations.create_source_on_component` instead.
481
+
482
+ Parameters
483
+ ----------
484
+ padstackinstance : `Edb.Cell.Primitive.PadstackInstance` or int
485
+ Padstack instance object.
486
+ use_dot_separator : bool, optional
487
+ Whether to use ``.`` as the separator for the naming convention, which
488
+ is ``[component][net][pin]``. The default is ``True``. If ``False``, ``_`` is
489
+ used as the separator instead.
490
+ name : str
491
+ Port name for overwriting the default port-naming convention,
492
+ which is ``[component][net][pin]``. The port name must be unique.
493
+ If a port with the specified name already exists, the
494
+ default naming convention is used so that port creation does
495
+ not fail.
496
+
497
+ Returns
498
+ -------
499
+ str
500
+ Terminal name.
501
+
502
+ """
503
+ warnings.warn(
504
+ "`create_coax_port` is deprecated and is now located here "
505
+ "`pyedb.grpc.core.excitations.create_coax_port` instead.",
506
+ DeprecationWarning,
507
+ )
508
+ self._pedb.excitations.create_coax_port(self, padstackinstance, use_dot_separator=use_dot_separator, name=name)
509
+
510
+ def get_pin_from_component_and_net(self, refdes=None, netname=None):
511
+ """Retrieve pins given a component's reference designator and net name.
512
+
513
+ Parameters
514
+ ----------
515
+ refdes : str, optional
516
+ Reference designator of the component. The default is ``None``.
517
+ netname : str optional
518
+ Name of the net. The default is ``None``.
519
+
520
+ Returns
521
+ -------
522
+ dict
523
+ Dictionary of pins if the operation is successful.
524
+ ``False`` is returned if the net does not belong to the component.
525
+
526
+ """
527
+ pinlist = []
528
+ if refdes:
529
+ if refdes in self._pedb.components.instances:
530
+ if netname:
531
+ for pin, val in self._pedb.components.instances[refdes].pins.items():
532
+ if val.net_name == netname:
533
+ pinlist.append(val)
534
+ else:
535
+ for pin in self._pedb.components.instances[refdes].pins.values():
536
+ pinlist.append(pin)
537
+ elif netname:
538
+ for pin in self._pedb.pins:
539
+ if pin.net_name == netname:
540
+ pinlist.append(pin)
541
+ else:
542
+ self._logger.error("At least a component or a net name has to be provided")
543
+
544
+ return pinlist
545
+
546
+ def get_pinlist_from_component_and_net(self, refdes=None, netname=None):
547
+ """Retrieve pins given a component's reference designator and net name.
548
+
549
+ . deprecated:: pyedb 0.28.0
550
+ Use :func:`get_pin_from_component_and_net` instead.
551
+
552
+ Parameters
553
+ ----------
554
+ refdes : str, optional
555
+ Reference designator of the component. The default is ``None``.
556
+ netname : str optional
557
+ Name of the net. The default is ``None``.
558
+
559
+ Returns
560
+ -------
561
+ dict
562
+ Dictionary of pins if the operation is successful.
563
+ ``False`` is returned if the net does not belong to the component.
564
+
565
+ """
566
+ warnings.warn(
567
+ "`get_pinlist_from_component_and_net` is deprecated use `get_pin_from_component_and_net` instead.",
568
+ DeprecationWarning,
569
+ )
570
+ return self.get_pin_from_component_and_net(refdes=refdes, netname=netname)
571
+
572
+ def get_pad_parameters(self, pin, layername, pad_type="regular_pad"):
573
+ """Get Padstack Parameters from Pin or Padstack Definition.
574
+
575
+ Parameters
576
+ ----------
577
+ pin : Edb.definition.PadstackDef or Edb.definition.PadstackInstance
578
+ Pin or PadstackDef on which get values.
579
+ layername : str
580
+ Layer on which get properties.
581
+ pad_type : str
582
+ Pad Type, `"pad"`, `"anti_pad"`, `"thermal_pad"`
583
+
584
+ Returns
585
+ -------
586
+ tuple
587
+ Tuple of (GeometryType, ParameterList, OffsetX, OffsetY, Rot).
588
+ """
589
+ if pad_type == "regular_pad":
590
+ pad_type = GrpcPadType.REGULAR_PAD
591
+ elif pad_type == "anti_pad":
592
+ pad_type = GrpcPadType.ANTI_PAD
593
+ elif pad_type == "thermal_pad":
594
+ pad_type = GrpcPadType.THERMAL_PAD
595
+ else:
596
+ pad_type = pad_type = GrpcPadType.REGULAR_PAD
597
+ padparams = pin.padstack_def.data.get_pad_parameters(layername, pad_type)
598
+ if len(padparams) == 5: # non polygon via
599
+ geometry_type = padparams[0]
600
+ parameters = [i.value for i in padparams[1]]
601
+ offset_x = padparams[2].value
602
+ offset_y = padparams[3].value
603
+ rotation = padparams[4].value
604
+ return geometry_type.name, parameters, offset_x, offset_y, rotation
605
+ elif len(padparams) == 4: # polygon based
606
+ from ansys.edb.core.geometry.polygon_data import (
607
+ PolygonData as GrpcPolygonData,
608
+ )
609
+
610
+ if isinstance(padparams[0], GrpcPolygonData):
611
+ points = [[pt.x.value, pt.y.value] for pt in padparams[0].points]
612
+ offset_x = padparams[1]
613
+ offset_y = padparams[2]
614
+ rotation = padparams[3]
615
+ geometry_type = GrpcPadGeometryType.PADGEOMTYPE_POLYGON
616
+ return geometry_type.name, points, offset_x, offset_y, rotation
617
+ return 0, [0], 0, 0, 0
618
+
619
+ def set_all_antipad_value(self, value):
620
+ """Set all anti-pads from all pad-stack definition to the given value.
621
+
622
+ Parameters
623
+ ----------
624
+ value : float, str
625
+ Anti-pad value.
626
+
627
+ Returns
628
+ -------
629
+ bool
630
+ ``True`` when successful, ``False`` if an anti-pad value fails to be assigned.
631
+ """
632
+ if self.definitions:
633
+ all_succeed = True
634
+ for padstack in list(self.definitions.values()):
635
+ cloned_padstack_data = GrpcPadstackDefData(padstack.data.msg)
636
+ layers_name = cloned_padstack_data.layer_names
637
+ for layer in layers_name:
638
+ try:
639
+ geom_type, points, offset_x, offset_y, rotation = cloned_padstack_data.get_pad_parameters(
640
+ layer, GrpcPadType.ANTI_PAD
641
+ )
642
+ if geom_type == GrpcPadGeometryType.PADGEOMTYPE_CIRCLE.name:
643
+ cloned_padstack_data.set_pad_parameters(
644
+ layer=layer,
645
+ pad_type=GrpcPadType.ANTI_PAD,
646
+ offset_x=GrpcValue(offset_x),
647
+ offset_y=GrpcValue(offset_y),
648
+ rotation=GrpcValue(rotation),
649
+ type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
650
+ sizes=[GrpcValue(value)],
651
+ )
652
+ self._logger.info(
653
+ "Pad-stack definition {}, anti-pad on layer {}, has been set to {}".format(
654
+ padstack.edb_padstack.GetName(), layer, str(value)
655
+ )
656
+ )
657
+ else: # pragma no cover
658
+ self._logger.error(
659
+ f"Failed to reassign anti-pad value {value} on Pads-stack definition {padstack.name},"
660
+ f" layer{layer}. This feature only support circular shape anti-pads."
661
+ )
662
+ all_succeed = False
663
+ except:
664
+ self._pedb.logger.info(
665
+ f"No antipad defined for padstack definition {padstack.name}-layer{layer}"
666
+ )
667
+ padstack.data = cloned_padstack_data
668
+ return all_succeed
669
+
670
+ def check_and_fix_via_plating(self, minimum_value_to_replace=0.0, default_plating_ratio=0.2):
671
+ """Check for minimum via plating ration value, values found below the minimum one are replaced by default
672
+ plating ratio.
673
+
674
+ Parameters
675
+ ----------
676
+ minimum_value_to_replace : float
677
+ Plating ratio that is below or equal to this value is to be replaced
678
+ with the value specified for the next parameter. Default value ``0.0``.
679
+ default_plating_ratio : float
680
+ Default value to use for plating ratio. The default value is ``0.2``.
681
+
682
+ Returns
683
+ -------
684
+ bool
685
+ ``True`` when successful, ``False`` if an anti-pad value fails to be assigned.
686
+ """
687
+ for padstack_def in list(self.definitions.values()):
688
+ if padstack_def.hole_plating_ratio <= minimum_value_to_replace:
689
+ padstack_def.hole_plating_ratio = default_plating_ratio
690
+ self._logger.info(
691
+ "Padstack definition with zero plating ratio, defaulting to 20%".format(padstack_def.name)
692
+ )
693
+ return True
694
+
695
+ def get_via_instance_from_net(self, net_list=None):
696
+ """Get the list for EDB vias from a net name list.
697
+
698
+ Parameters
699
+ ----------
700
+ net_list : str or list
701
+ The list of the net name to be used for filtering vias. If no net is provided the command will
702
+ return an all vias list.
703
+
704
+ Returns
705
+ -------
706
+ list of Edb.Cell.Primitive.PadstackInstance
707
+ List of EDB vias.
708
+ """
709
+ if net_list and not isinstance(net_list, list):
710
+ net_list = [net_list]
711
+ via_list = []
712
+ for inst in self._layout.padstack_instances:
713
+ pad_layers_name = inst.padstack_def.data.layer_names
714
+ if len(pad_layers_name) > 1:
715
+ if not net_list:
716
+ via_list.append(inst)
717
+ elif not inst.net.is_null:
718
+ if inst.net.name in net_list:
719
+ via_list.append(inst)
720
+ return via_list
721
+
722
+ def create(
723
+ self,
724
+ padstackname=None,
725
+ holediam="300um",
726
+ paddiam="400um",
727
+ antipaddiam="600um",
728
+ pad_shape="Circle",
729
+ antipad_shape="Circle",
730
+ x_size="600um",
731
+ y_size="600um",
732
+ corner_radius="300um",
733
+ offset_x="0.0",
734
+ offset_y="0.0",
735
+ rotation="0.0",
736
+ has_hole=True,
737
+ pad_offset_x="0.0",
738
+ pad_offset_y="0.0",
739
+ pad_rotation="0.0",
740
+ pad_polygon=None,
741
+ antipad_polygon=None,
742
+ polygon_hole=None,
743
+ start_layer=None,
744
+ stop_layer=None,
745
+ add_default_layer=False,
746
+ anti_pad_x_size="600um",
747
+ anti_pad_y_size="600um",
748
+ hole_range="upper_pad_to_lower_pad",
749
+ ):
750
+ """Create a padstack.
751
+
752
+ Parameters
753
+ ----------
754
+ padstackname : str, optional
755
+ Name of the padstack. The default is ``None``.
756
+ holediam : str, optional
757
+ Diameter of the hole with units. The default is ``"300um"``.
758
+ paddiam : str, optional
759
+ Diameter of the pad with units, used with ``"Circle"`` shape. The default is ``"400um"``.
760
+ antipaddiam : str, optional
761
+ Diameter of the antipad with units. The default is ``"600um"``.
762
+ pad_shape : str, optional
763
+ Shape of the pad. The default is ``"Circle``. Options are ``"Circle"``, ``"Rectangle"`` and ``"Polygon"``.
764
+ antipad_shape : str, optional
765
+ Shape of the antipad. The default is ``"Circle"``. Options are ``"Circle"`` ``"Rectangle"`` and
766
+ ``"Bullet"``.
767
+ x_size : str, optional
768
+ Only applicable to bullet and rectangle shape. The default is ``"600um"``.
769
+ y_size : str, optional
770
+ Only applicable to bullet and rectangle shape. The default is ``"600um"``.
771
+ corner_radius :
772
+ Only applicable to bullet shape. The default is ``"300um"``.
773
+ offset_x : str, optional
774
+ X offset of antipad. The default is ``"0.0"``.
775
+ offset_y : str, optional
776
+ Y offset of antipad. The default is ``"0.0"``.
777
+ rotation : str, optional
778
+ rotation of antipad. The default is ``"0.0"``.
779
+ has_hole : bool, optional
780
+ Whether this padstack has a hole.
781
+ pad_offset_x : str, optional
782
+ Padstack offset in X direction.
783
+ pad_offset_y : str, optional
784
+ Padstack offset in Y direction.
785
+ pad_rotation : str, optional
786
+ Padstack rotation.
787
+ start_layer : str, optional
788
+ Start layer of the padstack definition.
789
+ stop_layer : str, optional
790
+ Stop layer of the padstack definition.
791
+ add_default_layer : bool, optional
792
+ Add ``"Default"`` to padstack definition. Default is ``False``.
793
+ anti_pad_x_size : str, optional
794
+ Only applicable to bullet and rectangle shape. The default is ``"600um"``.
795
+ anti_pad_y_size : str, optional
796
+ Only applicable to bullet and rectangle shape. The default is ``"600um"``.
797
+ hole_range : str, optional
798
+ Define the padstack hole range. Arguments supported, ``"through"``, ``"begin_on_upper_pad"``,
799
+ ``"end_on_lower_pad"``, ``"upper_pad_to_lower_pad"``.
800
+
801
+ Returns
802
+ -------
803
+ str
804
+ Name of the padstack if the operation is successful.
805
+ """
806
+ holediam = GrpcValue(holediam)
807
+ paddiam = GrpcValue(paddiam)
808
+ antipaddiam = GrpcValue(antipaddiam)
809
+ layers = list(self._pedb.stackup.signal_layers.keys())[:]
810
+ value0 = GrpcValue("0.0")
811
+ if not padstackname:
812
+ padstackname = generate_unique_name("VIA")
813
+ padstack_data = GrpcPadstackDefData.create()
814
+ if has_hole and not polygon_hole:
815
+ hole_param = [holediam, holediam]
816
+ padstack_data.set_hole_parameters(
817
+ offset_x=value0,
818
+ offset_y=value0,
819
+ rotation=value0,
820
+ type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
821
+ sizes=hole_param,
822
+ )
823
+ padstack_data.plating_percentage = GrpcValue(20.0)
824
+ elif polygon_hole:
825
+ if isinstance(polygon_hole, list):
826
+ polygon_hole = GrpcPolygonData(points=polygon_hole)
827
+
828
+ padstack_data.set_hole_parameters(
829
+ offset_x=value0,
830
+ offset_y=value0,
831
+ rotation=value0,
832
+ type_geom=GrpcPadGeometryType.PADGEOMTYPE_POLYGON,
833
+ sizes=polygon_hole,
834
+ )
835
+ padstack_data.plating_percentage = GrpcValue(20.0)
836
+ else:
837
+ pass
838
+
839
+ x_size = GrpcValue(x_size)
840
+ y_size = GrpcValue(y_size)
841
+ corner_radius = GrpcValue(corner_radius)
842
+ pad_offset_x = GrpcValue(pad_offset_x)
843
+ pad_offset_y = GrpcValue(pad_offset_y)
844
+ pad_rotation = GrpcValue(pad_rotation)
845
+ anti_pad_x_size = GrpcValue(anti_pad_x_size)
846
+ anti_pad_y_size = GrpcValue(anti_pad_y_size)
847
+
848
+ if hole_range == "through": # pragma no cover
849
+ padstack_data.hole_range = GrpcPadstackHoleRange.THROUGH
850
+ elif hole_range == "begin_on_upper_pad": # pragma no cover
851
+ padstack_data.hole_range = GrpcPadstackHoleRange.BEGIN_ON_UPPER_PAD
852
+ elif hole_range == "end_on_lower_pad": # pragma no cover
853
+ padstack_data.hole_range = GrpcPadstackHoleRange.END_ON_LOWER_PAD
854
+ elif hole_range == "upper_pad_to_lower_pad": # pragma no cover
855
+ padstack_data.hole_range = GrpcPadstackHoleRange.UPPER_PAD_TO_LOWER_PAD
856
+ else: # pragma no cover
857
+ self._logger.error("Unknown padstack hole range")
858
+ padstack_data.material = "copper"
859
+
860
+ if start_layer and start_layer in layers: # pragma no cover
861
+ layers = layers[layers.index(start_layer) :]
862
+ if stop_layer and stop_layer in layers: # pragma no cover
863
+ layers = layers[: layers.index(stop_layer) + 1]
864
+ if not isinstance(paddiam, list):
865
+ pad_array = [paddiam]
866
+ else:
867
+ pad_array = paddiam
868
+ if pad_shape == "Circle": # pragma no cover
869
+ pad_shape = GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
870
+ elif pad_shape == "Rectangle": # pragma no cover
871
+ pad_array = [x_size, y_size]
872
+ pad_shape = GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE
873
+ elif pad_shape == "Polygon":
874
+ if isinstance(pad_polygon, list):
875
+ pad_array = GrpcPolygonData(points=pad_polygon)
876
+ elif isinstance(pad_polygon, GrpcPolygonData):
877
+ pad_array = pad_polygon
878
+ if antipad_shape == "Bullet": # pragma no cover
879
+ antipad_array = [x_size, y_size, corner_radius]
880
+ antipad_shape = GrpcPadGeometryType.PADGEOMTYPE_BULLET
881
+ elif antipad_shape == "Rectangle": # pragma no cover
882
+ antipad_array = [anti_pad_x_size, anti_pad_y_size]
883
+ antipad_shape = GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE
884
+ elif antipad_shape == "Polygon":
885
+ if isinstance(antipad_polygon, list):
886
+ antipad_array = GrpcPolygonData(points=antipad_polygon)
887
+ elif isinstance(antipad_polygon, GrpcPolygonData):
888
+ antipad_array = antipad_polygon
889
+ else:
890
+ if not isinstance(antipaddiam, list):
891
+ antipad_array = [antipaddiam]
892
+ else:
893
+ antipad_array = antipaddiam
894
+ antipad_shape = GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
895
+ if add_default_layer: # pragma no cover
896
+ layers = layers + ["Default"]
897
+ if antipad_shape == "Polygon" and pad_shape == "Polygon":
898
+ for layer in layers:
899
+ padstack_data.set_pad_parameters(
900
+ layer=layer,
901
+ pad_type=GrpcPadType.REGULAR_PAD,
902
+ offset_x=pad_offset_x,
903
+ offset_y=pad_offset_y,
904
+ rotation=pad_rotation,
905
+ fp=pad_array,
906
+ )
907
+ padstack_data.set_pad_parameters(
908
+ layer=layer,
909
+ pad_type=GrpcPadType.ANTI_PAD,
910
+ offset_x=pad_offset_x,
911
+ offset_y=pad_offset_y,
912
+ rotation=pad_rotation,
913
+ fp=antipad_array,
914
+ )
915
+ else:
916
+ for layer in layers:
917
+ padstack_data.set_pad_parameters(
918
+ layer=layer,
919
+ pad_type=GrpcPadType.REGULAR_PAD,
920
+ offset_x=pad_offset_x,
921
+ offset_y=pad_offset_y,
922
+ rotation=pad_rotation,
923
+ type_geom=pad_shape,
924
+ sizes=pad_array,
925
+ )
926
+
927
+ padstack_data.set_pad_parameters(
928
+ layer=layer,
929
+ pad_type=GrpcPadType.ANTI_PAD,
930
+ offset_x=pad_offset_x,
931
+ offset_y=pad_offset_y,
932
+ rotation=pad_rotation,
933
+ type_geom=antipad_shape,
934
+ sizes=antipad_array,
935
+ )
936
+
937
+ padstack_definition = PadstackDef.create(self.db, padstackname)
938
+ padstack_definition.data = padstack_data
939
+ self._logger.info(f"Padstack {padstackname} create correctly")
940
+ return padstackname
941
+
942
+ def _get_pin_layer_range(self, pin):
943
+ layers = pin.get_layer_range()
944
+ if layers:
945
+ return layers[0], layers[1]
946
+ else:
947
+ return False
948
+
949
+ def duplicate(self, target_padstack_name, new_padstack_name=""):
950
+ """Duplicate a padstack.
951
+
952
+ Parameters
953
+ ----------
954
+ target_padstack_name : str
955
+ Name of the padstack to be duplicated.
956
+ new_padstack_name : str, optional
957
+ Name of the new padstack.
958
+
959
+ Returns
960
+ -------
961
+ str
962
+ Name of the new padstack.
963
+ """
964
+ new_padstack_definition_data = GrpcPadstackDefData(self.definitions[target_padstack_name].data.msg)
965
+ if not new_padstack_name:
966
+ new_padstack_name = generate_unique_name(target_padstack_name)
967
+ padstack_definition = PadstackDef.create(self.db, new_padstack_name)
968
+ padstack_definition.data = new_padstack_definition_data
969
+ return new_padstack_name
970
+
971
+ def place(
972
+ self,
973
+ position,
974
+ definition_name,
975
+ net_name="",
976
+ via_name="",
977
+ rotation=0.0,
978
+ fromlayer=None,
979
+ tolayer=None,
980
+ solderlayer=None,
981
+ is_pin=False,
982
+ ):
983
+ """Place a via.
984
+
985
+ Parameters
986
+ ----------
987
+ position : list
988
+ List of float values for the [x,y] positions where the via is to be placed.
989
+ definition_name : str
990
+ Name of the padstack definition.
991
+ net_name : str, optional
992
+ Name of the net. The default is ``""``.
993
+ via_name : str, optional
994
+ The default is ``""``.
995
+ rotation : float, optional
996
+ Rotation of the padstack in degrees. The default
997
+ is ``0``.
998
+ fromlayer :
999
+ The default is ``None``.
1000
+ tolayer :
1001
+ The default is ``None``.
1002
+ solderlayer :
1003
+ The default is ``None``.
1004
+ is_pin : bool, optional
1005
+ Whether if the padstack is a pin or not. Default is `False`.
1006
+
1007
+ Returns
1008
+ -------
1009
+ :class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`
1010
+ """
1011
+ padstack_def = None
1012
+ for pad in list(self.definitions.keys()):
1013
+ if pad == definition_name:
1014
+ padstack_def = self.definitions[pad]
1015
+ position = GrpcPointData(
1016
+ [GrpcValue(position[0], self._pedb.active_cell), GrpcValue(position[1], self._pedb.active_cell)]
1017
+ )
1018
+ net = self._pedb.nets.find_or_create_net(net_name)
1019
+ rotation = GrpcValue(rotation * math.pi / 180)
1020
+ sign_layers_values = {i: v for i, v in self._pedb.stackup.signal_layers.items()}
1021
+ sign_layers = list(sign_layers_values.keys())
1022
+ if not fromlayer:
1023
+ try:
1024
+ fromlayer = sign_layers_values[list(self.definitions[pad].pad_by_layer.keys())[0]]
1025
+ except KeyError:
1026
+ fromlayer = sign_layers_values[sign_layers[0]]
1027
+ else:
1028
+ fromlayer = sign_layers_values[fromlayer]
1029
+
1030
+ if not tolayer:
1031
+ try:
1032
+ tolayer = sign_layers_values[list(self.definitions[pad].pad_by_layer.keys())[-1]]
1033
+ except KeyError:
1034
+ tolayer = sign_layers_values[sign_layers[-1]]
1035
+ else:
1036
+ tolayer = sign_layers_values[tolayer]
1037
+ if solderlayer:
1038
+ solderlayer = sign_layers_values[solderlayer]
1039
+ if not via_name:
1040
+ via_name = generate_unique_name(padstack_def.name)
1041
+ if padstack_def:
1042
+ padstack_instance = PadstackInstance.create(
1043
+ layout=self._active_layout,
1044
+ net=net,
1045
+ name=via_name,
1046
+ padstack_def=padstack_def,
1047
+ position_x=position.x,
1048
+ position_y=position.y,
1049
+ rotation=rotation,
1050
+ top_layer=fromlayer,
1051
+ bottom_layer=tolayer,
1052
+ solder_ball_layer=solderlayer,
1053
+ layer_map=None,
1054
+ )
1055
+ padstack_instance.is_layout_pin = is_pin
1056
+ return PadstackInstance(self._pedb, padstack_instance)
1057
+ else:
1058
+ return False
1059
+
1060
+ def remove_pads_from_padstack(self, padstack_name, layer_name=None):
1061
+ """Remove the Pad from a padstack on a specific layer by setting it as a 0 thickness circle.
1062
+
1063
+ Parameters
1064
+ ----------
1065
+ padstack_name : str
1066
+ padstack name
1067
+ layer_name : str, optional
1068
+ Layer name on which remove the PadParameters. If None, all layers will be taken.
1069
+
1070
+ Returns
1071
+ -------
1072
+ bool
1073
+ ``True`` if successful.
1074
+ """
1075
+ pad_type = GrpcPadType.REGULAR_PAD
1076
+ pad_geo = GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
1077
+ vals = GrpcValue(0)
1078
+ params = [GrpcValue(0)]
1079
+ new_padstack_definition_data = GrpcPadstackDefData(self.definitions[padstack_name].data)
1080
+ if not layer_name:
1081
+ layer_name = list(self._pedb.stackup.signal_layers.keys())
1082
+ elif isinstance(layer_name, str):
1083
+ layer_name = [layer_name]
1084
+ for lay in layer_name:
1085
+ new_padstack_definition_data.set_pad_parameters(
1086
+ layer=lay,
1087
+ pad_type=pad_type,
1088
+ offset_x=vals,
1089
+ offset_y=vals,
1090
+ rotation=vals,
1091
+ type_geom=pad_geo,
1092
+ sizes=params,
1093
+ )
1094
+ self.definitions[padstack_name].data = new_padstack_definition_data
1095
+ return True
1096
+
1097
+ def set_pad_property(
1098
+ self,
1099
+ padstack_name,
1100
+ layer_name=None,
1101
+ pad_shape="Circle",
1102
+ pad_params=0,
1103
+ pad_x_offset=0,
1104
+ pad_y_offset=0,
1105
+ pad_rotation=0,
1106
+ antipad_shape="Circle",
1107
+ antipad_params=0,
1108
+ antipad_x_offset=0,
1109
+ antipad_y_offset=0,
1110
+ antipad_rotation=0,
1111
+ ):
1112
+ """Set pad and antipad properties of the padstack.
1113
+
1114
+ Parameters
1115
+ ----------
1116
+ padstack_name : str
1117
+ Name of the padstack.
1118
+ layer_name : str, optional
1119
+ Name of the layer. If None, all layers will be taken.
1120
+ pad_shape : str, optional
1121
+ Shape of the pad. The default is ``"Circle"``. Options are ``"Circle"``, ``"Square"``, ``"Rectangle"``,
1122
+ ``"Oval"`` and ``"Bullet"``.
1123
+ pad_params : str, optional
1124
+ Dimension of the pad. The default is ``"0"``.
1125
+ pad_x_offset : str, optional
1126
+ X offset of the pad. The default is ``"0"``.
1127
+ pad_y_offset : str, optional
1128
+ Y offset of the pad. The default is ``"0"``.
1129
+ pad_rotation : str, optional
1130
+ Rotation of the pad. The default is ``"0"``.
1131
+ antipad_shape : str, optional
1132
+ Shape of the antipad. The default is ``"0"``.
1133
+ antipad_params : str, optional
1134
+ Dimension of the antipad. The default is ``"0"``.
1135
+ antipad_x_offset : str, optional
1136
+ X offset of the antipad. The default is ``"0"``.
1137
+ antipad_y_offset : str, optional
1138
+ Y offset of the antipad. The default is ``"0"``.
1139
+ antipad_rotation : str, optional
1140
+ Rotation of the antipad. The default is ``"0"``.
1141
+
1142
+ Returns
1143
+ -------
1144
+ bool
1145
+ ``True`` if successful.
1146
+ """
1147
+ shape_dict = {
1148
+ "Circle": GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
1149
+ "Square": GrpcPadGeometryType.PADGEOMTYPE_SQUARE,
1150
+ "Rectangle": GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE,
1151
+ "Oval": GrpcPadGeometryType.PADGEOMTYPE_OVAL,
1152
+ "Bullet": GrpcPadGeometryType.PADGEOMTYPE_BULLET,
1153
+ }
1154
+ pad_shape = shape_dict[pad_shape]
1155
+ if not isinstance(pad_params, list):
1156
+ pad_params = [pad_params]
1157
+ pad_params = [GrpcValue(i) for i in pad_params]
1158
+ pad_x_offset = GrpcValue(pad_x_offset)
1159
+ pad_y_offset = GrpcValue(pad_y_offset)
1160
+ pad_rotation = GrpcValue(pad_rotation)
1161
+
1162
+ antipad_shape = shape_dict[antipad_shape]
1163
+ if not isinstance(antipad_params, list):
1164
+ antipad_params = [antipad_params]
1165
+ antipad_params = [GrpcValue(i) for i in antipad_params]
1166
+ antipad_x_offset = GrpcValue(antipad_x_offset)
1167
+ antipad_y_offset = GrpcValue(antipad_y_offset)
1168
+ antipad_rotation = GrpcValue(antipad_rotation)
1169
+ new_padstack_def = GrpcPadstackDefData(self.definitions[padstack_name].data.msg)
1170
+ if not layer_name:
1171
+ layer_name = list(self._pedb.stackup.signal_layers.keys())
1172
+ elif isinstance(layer_name, str):
1173
+ layer_name = [layer_name]
1174
+ for layer in layer_name:
1175
+ new_padstack_def.set_pad_parameters(
1176
+ layer=layer,
1177
+ pad_type=GrpcPadType.REGULAR_PAD,
1178
+ offset_x=pad_x_offset,
1179
+ offset_y=pad_y_offset,
1180
+ rotation=pad_rotation,
1181
+ type_geom=pad_shape,
1182
+ sizes=pad_params,
1183
+ )
1184
+ new_padstack_def.set_pad_parameters(
1185
+ layer=layer,
1186
+ pad_type=GrpcPadType.ANTI_PAD,
1187
+ offset_x=antipad_x_offset,
1188
+ offset_y=antipad_y_offset,
1189
+ rotation=antipad_rotation,
1190
+ type_geom=antipad_shape,
1191
+ sizes=antipad_params,
1192
+ )
1193
+ self.definitions[padstack_name].data = new_padstack_def
1194
+ return True
1195
+
1196
+ def get_instances(
1197
+ self,
1198
+ name=None,
1199
+ pid=None,
1200
+ definition_name=None,
1201
+ net_name=None,
1202
+ component_reference_designator=None,
1203
+ component_pin=None,
1204
+ ):
1205
+ """Get padstack instances by conditions.
1206
+
1207
+ Parameters
1208
+ ----------
1209
+ name : str, optional
1210
+ Name of the padstack.
1211
+ pid : int, optional
1212
+ Id of the padstack.
1213
+ definition_name : str, list, optional
1214
+ Name of the padstack definition.
1215
+ net_name : str, optional
1216
+ The net name to be used for filtering padstack instances.
1217
+ component_pin: str, optional
1218
+ Pin Number of the component.
1219
+ Returns
1220
+ -------
1221
+ list
1222
+ List of :class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`.
1223
+ """
1224
+
1225
+ instances_by_id = self.instances
1226
+ if pid:
1227
+ return instances_by_id[pid]
1228
+ elif name:
1229
+ instances = [inst for inst in list(self.instances.values()) if inst.name == name]
1230
+ if instances:
1231
+ return instances
1232
+ else:
1233
+ instances = list(instances_by_id.values())
1234
+ if definition_name:
1235
+ definition_name = definition_name if isinstance(definition_name, list) else [definition_name]
1236
+ instances = [inst for inst in instances if inst.padstack_def.name in definition_name]
1237
+ if net_name:
1238
+ net_name = net_name if isinstance(net_name, list) else [net_name]
1239
+ instances = [inst for inst in instances if inst.net_name in net_name]
1240
+ if component_reference_designator:
1241
+ refdes = (
1242
+ component_reference_designator
1243
+ if isinstance(component_reference_designator, list)
1244
+ else [component_reference_designator]
1245
+ )
1246
+ instances = [inst for inst in instances if inst.component]
1247
+ instances = [inst for inst in instances if inst.component.refdes in refdes]
1248
+ if component_pin:
1249
+ component_pin = component_pin if isinstance(component_pin, list) else [component_pin]
1250
+ instances = [inst for inst in instances if inst.component_pin in component_pin]
1251
+ return instances
1252
+
1253
+ def get_reference_pins(
1254
+ self, positive_pin, reference_net="gnd", search_radius=5e-3, max_limit=0, component_only=True
1255
+ ):
1256
+ """Search for reference pins using given criteria.
1257
+
1258
+ Parameters
1259
+ ----------
1260
+ positive_pin : EDBPadstackInstance
1261
+ Pin used for evaluating the distance on the reference pins found.
1262
+ reference_net : str, optional
1263
+ Reference net. The default is ``"gnd"``.
1264
+ search_radius : float, optional
1265
+ Search radius for finding padstack instances. The default is ``5e-3``.
1266
+ max_limit : int, optional
1267
+ Maximum limit for the padstack instances found. The default is ``0``, in which
1268
+ case no limit is applied. The maximum limit value occurs on the nearest
1269
+ reference pins from the positive one that is found.
1270
+ component_only : bool, optional
1271
+ Whether to limit the search to component padstack instances only. The
1272
+ default is ``True``. When ``False``, the search is extended to the entire layout.
1273
+
1274
+ Returns
1275
+ -------
1276
+ list
1277
+ List of :class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`.
1278
+
1279
+ Examples
1280
+ --------
1281
+ >>> edbapp = Edb("target_path")
1282
+ >>> pin = edbapp.components.instances["J5"].pins["19"]
1283
+ >>> reference_pins = edbapp.padstacks.get_reference_pins(positive_pin=pin, reference_net="GND",
1284
+ >>> search_radius=5e-3, max_limit=0, component_only=True)
1285
+ """
1286
+ pinlist = []
1287
+ if not positive_pin:
1288
+ search_radius = 10e-2
1289
+ component_only = True
1290
+ if component_only:
1291
+ references_pins = [
1292
+ pin for pin in list(positive_pin.component.pins.values()) if pin.net_name == reference_net
1293
+ ]
1294
+ if not references_pins:
1295
+ return pinlist
1296
+ else:
1297
+ references_pins = self.get_instances(net_name=reference_net)
1298
+ if not references_pins:
1299
+ return pinlist
1300
+ pinlist = [
1301
+ p
1302
+ for p in references_pins
1303
+ if GeometryOperators.points_distance(positive_pin.position, p.position) <= search_radius
1304
+ ]
1305
+ if max_limit and len(pinlist) > max_limit:
1306
+ pin_dict = {GeometryOperators.points_distance(positive_pin.position, p.position): p for p in pinlist}
1307
+ pinlist = [pin[1] for pin in sorted(pin_dict.items())[:max_limit]]
1308
+ return pinlist
1309
+
1310
+ def get_padstack_instances_rtree_index(self, nets=None):
1311
+ """Returns padstack instances Rtree index.
1312
+
1313
+ Parameters
1314
+ ----------
1315
+ nets : str or list, optional
1316
+ net name of list of nets name applying filtering on padstack instances selection. If ``None`` is provided
1317
+ all instances are included in the index. Default value is ``None``.
1318
+
1319
+ Returns
1320
+ -------
1321
+ Rtree index object.
1322
+
1323
+ """
1324
+ if isinstance(nets, str):
1325
+ nets = [nets]
1326
+ padstack_instances_index = rtree.index.Index()
1327
+ if nets:
1328
+ instances = [inst for inst in list(self.instances.values()) if inst.net_name in nets]
1329
+ else:
1330
+ instances = list(self.instances.values())
1331
+ for inst in instances:
1332
+ padstack_instances_index.insert(inst.id, inst.position)
1333
+ return padstack_instances_index
1334
+
1335
+ def get_padstack_instances_intersecting_bounding_box(self, bounding_box, nets=None):
1336
+ """Returns the list of padstack instances ID intersecting a given bounding box and nets.
1337
+
1338
+ Parameters
1339
+ ----------
1340
+ bounding_box : tuple or list.
1341
+ bounding box, [x1, y1, x2, y2]
1342
+ nets : str or list, optional
1343
+ net name of list of nets name applying filtering on padstack instances selection. If ``None`` is provided
1344
+ all instances are included in the index. Default value is ``None``.
1345
+
1346
+ Returns
1347
+ -------
1348
+ List of padstack instances ID intersecting the bounding box.
1349
+ """
1350
+ if not bounding_box:
1351
+ raise Exception("No bounding box was provided")
1352
+ index = self.get_padstack_instances_rtree_index(nets=nets)
1353
+ if not len(bounding_box) == 4:
1354
+ raise Exception("The bounding box length must be equal to 4")
1355
+ if isinstance(bounding_box, list):
1356
+ bounding_box = tuple(bounding_box)
1357
+ return list(index.intersection(bounding_box))
1358
+
1359
+ def merge_via_along_lines(
1360
+ self, net_name="GND", distance_threshold=5e-3, minimum_via_number=6, selected_angles=None
1361
+ ):
1362
+ """Replace padstack instances along lines into a single polygon.
1363
+
1364
+ Detect all padstack instances that are placed along lines and replace them by a single polygon based one
1365
+ forming a wall shape. This method is designed to simplify meshing on via fence usually added to shield RF traces
1366
+ on PCB.
1367
+
1368
+ Parameters
1369
+ ----------
1370
+ net_name : str
1371
+ Net name used for detected padstack instances. Default value is ``"GND"``.
1372
+
1373
+ distance_threshold : float, None, optional
1374
+ If two points in a line are separated by a distance larger than `distance_threshold`,
1375
+ the line is divided in two parts. Default is ``5e-3`` (5mm), in which case the control is not performed.
1376
+
1377
+ minimum_via_number : int, optional
1378
+ The minimum number of points that a line must contain. Default is ``6``.
1379
+
1380
+ selected_angles : list[int, float]
1381
+ Specify angle in degrees to detected, for instance [0, 180] is only detecting horizontal and vertical lines.
1382
+ Other values can be assigned like 45 degrees. When `None` is provided all lines are detected. Default value
1383
+ is `None`.
1384
+
1385
+ Returns
1386
+ -------
1387
+ bool
1388
+ ``True`` when succeeded ``False`` when failed. <
1389
+
1390
+ """
1391
+ _def = list(set([inst.padstack_def for inst in list(self.instances.values()) if inst.net_name == net_name]))
1392
+ if not _def:
1393
+ self._logger.error(f"No padstack definition found for net {net_name}")
1394
+ return False
1395
+ _instances_to_delete = []
1396
+ padstack_instances = []
1397
+ for pdstk_def in _def:
1398
+ padstack_instances.append(
1399
+ [inst for inst in self.definitions[pdstk_def.name].instances if inst.net_name == net_name]
1400
+ )
1401
+ for pdstk_series in padstack_instances:
1402
+ instances_location = [inst.position for inst in pdstk_series]
1403
+ lines, line_indexes = GeometryOperators.find_points_along_lines(
1404
+ points=instances_location,
1405
+ minimum_number_of_points=minimum_via_number,
1406
+ distance_threshold=distance_threshold,
1407
+ selected_angles=selected_angles,
1408
+ )
1409
+ for line in line_indexes:
1410
+ [_instances_to_delete.append(pdstk_series[ind]) for ind in line]
1411
+ start_point = pdstk_series[line[0]]
1412
+ stop_point = pdstk_series[line[-1]]
1413
+ padstack_def = start_point.padstack_def
1414
+ trace_width = (
1415
+ self.definitions[padstack_def.name].pad_by_layer[stop_point.start_layer].parameters_values[0]
1416
+ )
1417
+ trace = self._pedb.modeler.create_trace(
1418
+ path_list=[start_point.position, stop_point.position],
1419
+ layer_name=start_point.start_layer,
1420
+ width=trace_width,
1421
+ )
1422
+ polygon_data = trace.polygon_data
1423
+ trace.delete()
1424
+ new_padstack_def = generate_unique_name(padstack_def.name)
1425
+ if not self.create(
1426
+ padstackname=new_padstack_def,
1427
+ pad_shape="Polygon",
1428
+ antipad_shape="Polygon",
1429
+ pad_polygon=polygon_data,
1430
+ antipad_polygon=polygon_data,
1431
+ polygon_hole=polygon_data,
1432
+ ):
1433
+ self._logger.error(f"Failed to create padstack definition {new_padstack_def.name}")
1434
+ if not self.place(position=[0, 0], definition_name=new_padstack_def, net_name=net_name):
1435
+ self._logger.error(f"Failed to place padstack instance {new_padstack_def.name}")
1436
+ for inst in _instances_to_delete:
1437
+ inst.delete()
1438
+ return True
1439
+
1440
+ def merge_via(self, contour_boxes, net_filter=None, start_layer=None, stop_layer=None):
1441
+ """Evaluate padstack instances included on the provided point list and replace all by single instance.
1442
+
1443
+ Parameters
1444
+ ----------
1445
+ contour_boxes : List[List[List[float, float]]]
1446
+ Nested list of polygon with points [x,y].
1447
+ net_filter : optional
1448
+ List[str: net_name] apply a net filter,
1449
+ nets included in the filter are excluded from the via merge.
1450
+ start_layer : optional, str
1451
+ Padstack instance start layer, if `None` the top layer is selected.
1452
+ stop_layer : optional, str
1453
+ Padstack instance stop layer, if `None` the bottom layer is selected.
1454
+
1455
+ Return
1456
+ ------
1457
+ List[str], list of created padstack instances ID.
1458
+
1459
+ """
1460
+ merged_via_ids = []
1461
+ if not contour_boxes:
1462
+ self._pedb.logger.error("No contour box provided, you need to pass a nested list as argument.")
1463
+ return False
1464
+ if not start_layer:
1465
+ start_layer = list(self._pedb.stackup.layers.values())[0].name
1466
+ if not stop_layer:
1467
+ stop_layer = list(self._pedb.stackup.layers.values())[-1].name
1468
+ instances_index = {}
1469
+ for id, inst in self.instances.items():
1470
+ instances_index[id] = inst.position
1471
+ for contour_box in contour_boxes:
1472
+ instances = self.get_padstack_instances_id_intersecting_polygon(
1473
+ points=contour_box, padstack_instances_index=instances_index
1474
+ )
1475
+ if net_filter:
1476
+ instances = [self.instances[id] for id in instances if not self.instances[id].net.name in net_filter]
1477
+ net = self.instances[instances[0]].net.name
1478
+ instances_pts = np.array([self.instances[id].position for id in instances])
1479
+ convex_hull_contour = ConvexHull(instances_pts)
1480
+ contour_points = list(instances_pts[convex_hull_contour.vertices])
1481
+ layer = list(self._pedb.stackup.layers.values())[0].name
1482
+ polygon = self._pedb.modeler.create_polygon(main_shape=contour_points, layer_name=layer)
1483
+ polygon_data = polygon.polygon_data
1484
+ polygon.delete()
1485
+ new_padstack_def = generate_unique_name("test")
1486
+ if not self.create(
1487
+ padstackname=new_padstack_def,
1488
+ pad_shape="Polygon",
1489
+ antipad_shape="Polygon",
1490
+ pad_polygon=polygon_data,
1491
+ antipad_polygon=polygon_data,
1492
+ polygon_hole=polygon_data,
1493
+ start_layer=start_layer,
1494
+ stop_layer=stop_layer,
1495
+ ):
1496
+ self._logger.error(f"Failed to create padstack definition {new_padstack_def}")
1497
+ merged_instance = self.place(position=[0, 0], definition_name=new_padstack_def, net_name=net)
1498
+ merged_via_ids.append(merged_instance.id)
1499
+ [self.instances[id].delete() for id in instances]
1500
+ return merged_via_ids