pyedb 0.38.0__py3-none-any.whl → 0.39.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 (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 +4151 -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.0.dist-info}/METADATA +5 -2
  175. pyedb-0.39.0.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.0.dist-info}/LICENSE +0 -0
  205. {pyedb-0.38.0.dist-info → pyedb-0.39.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,1468 @@
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 these classes: `EdbLayout` and `Shape`.
25
+ """
26
+ import math
27
+
28
+ from ansys.edb.core.geometry.arc_data import ArcData as GrpcArcData
29
+ from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
30
+ from ansys.edb.core.geometry.polygon_data import (
31
+ PolygonSenseType as GrpcPolygonSenseType,
32
+ )
33
+ from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
34
+ from ansys.edb.core.hierarchy.pin_group import PinGroup as GrpcPinGroup
35
+ from ansys.edb.core.inner.exceptions import InvalidArgumentException
36
+ from ansys.edb.core.primitive.primitive import (
37
+ RectangleRepresentationType as GrpcRectangleRepresentationType,
38
+ )
39
+ from ansys.edb.core.primitive.primitive import BondwireType as GrpcBondwireType
40
+ from ansys.edb.core.primitive.primitive import PathCornerType as GrpcPathCornerType
41
+ from ansys.edb.core.primitive.primitive import PathEndCapType as GrpcPathEndCapType
42
+ from ansys.edb.core.utility.value import Value as GrpcValue
43
+
44
+ from pyedb.grpc.database.primitive.bondwire import Bondwire
45
+ from pyedb.grpc.database.primitive.circle import Circle
46
+ from pyedb.grpc.database.primitive.path import Path
47
+ from pyedb.grpc.database.primitive.polygon import Polygon
48
+ from pyedb.grpc.database.primitive.primitive import Primitive
49
+ from pyedb.grpc.database.primitive.rectangle import Rectangle
50
+ from pyedb.grpc.database.utility.layout_statistics import LayoutStatistics
51
+
52
+
53
+ class Modeler(object):
54
+ """Manages EDB methods for primitives management accessible from `Edb.modeler` property.
55
+
56
+ Examples
57
+ --------
58
+ >>> from pyedb import Edb
59
+ >>> edbapp = Edb("myaedbfolder", edbversion="2021.2")
60
+ >>> edb_layout = edbapp.modeler
61
+ """
62
+
63
+ def __getitem__(self, name):
64
+ """Get a layout instance from the Edb project.
65
+
66
+ Parameters
67
+ ----------
68
+ name : str, int
69
+
70
+ Returns
71
+ -------
72
+ :class:`pyedb.dotnet.database.cell.hierarchy.component.EDBComponent`
73
+
74
+ """
75
+ for i in self.primitives:
76
+ if (
77
+ (isinstance(name, str) and i.aedt_name == name)
78
+ or (isinstance(name, str) and i.aedt_name == name.replace("__", "_"))
79
+ or (isinstance(name, int) and i.id == name)
80
+ ):
81
+ return i
82
+ self._pedb.logger.error("Primitive not found.")
83
+ return
84
+
85
+ def __init__(self, p_edb):
86
+ self._pedb = p_edb
87
+ self._primitives = []
88
+
89
+ @property
90
+ def _edb(self):
91
+ return self._pedb
92
+
93
+ @property
94
+ def _logger(self):
95
+ """Logger."""
96
+ return self._pedb.logger
97
+
98
+ @property
99
+ def _active_layout(self):
100
+ return self._pedb.active_layout
101
+
102
+ @property
103
+ def _layout(self):
104
+ return self._pedb.layout
105
+
106
+ @property
107
+ def _cell(self):
108
+ return self._pedb.active_cell
109
+
110
+ @property
111
+ def db(self):
112
+ """Db object."""
113
+ return self._pedb.active_db
114
+
115
+ @property
116
+ def layers(self):
117
+ """Dictionary of layers.
118
+
119
+ Returns
120
+ -------
121
+ dict
122
+ Dictionary of layers.
123
+ """
124
+ return self._pedb.stackup.layers
125
+
126
+ def get_primitive(self, primitive_id):
127
+ """Retrieve primitive from give id.
128
+
129
+ Parameters
130
+ ----------
131
+ primitive_id : int
132
+ Primitive id.
133
+
134
+ Returns
135
+ -------
136
+ list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
137
+ List of primitives.
138
+ """
139
+ for p in self._layout.primitives:
140
+ if p.edb_uid == primitive_id:
141
+ return self.__mapping_primitive_type(p)
142
+ for p in self._layout.primitives:
143
+ for v in p.voids:
144
+ if v.edb_uid == primitive_id:
145
+ return self.__mapping_primitive_type(v)
146
+
147
+ def __mapping_primitive_type(self, primitive):
148
+ from ansys.edb.core.primitive.primitive import (
149
+ PrimitiveType as GrpcPrimitiveType,
150
+ )
151
+
152
+ if primitive.primitive_type == GrpcPrimitiveType.POLYGON:
153
+ return Polygon(self._pedb, primitive)
154
+ elif primitive.primitive_type == GrpcPrimitiveType.PATH:
155
+ return Path(self._pedb, primitive)
156
+ elif primitive.primitive_type == GrpcPrimitiveType.RECTANGLE:
157
+ return Rectangle(self._pedb, primitive)
158
+ elif primitive.primitive_type == GrpcPrimitiveType.CIRCLE:
159
+ return Circle(self._pedb, primitive)
160
+ elif primitive.primitive_type == GrpcPrimitiveType.BONDWIRE:
161
+ return Bondwire(self._pedb, primitive)
162
+ else:
163
+ return False
164
+
165
+ @property
166
+ def primitives(self):
167
+ """Primitives.
168
+
169
+ Returns
170
+ -------
171
+ list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
172
+ List of primitives.
173
+ """
174
+ return [self.__mapping_primitive_type(prim) for prim in self._pedb.layout.primitives]
175
+
176
+ @property
177
+ def polygons_by_layer(self):
178
+ """Primitives with layer names as keys.
179
+
180
+ Returns
181
+ -------
182
+ dict
183
+ Dictionary of primitives with layer names as keys.
184
+ """
185
+ _primitives_by_layer = {}
186
+ for lay in self.layers:
187
+ _primitives_by_layer[lay] = self.get_polygons_by_layer(lay)
188
+ return _primitives_by_layer
189
+
190
+ @property
191
+ def primitives_by_net(self):
192
+ """Primitives with net names as keys.
193
+
194
+ Returns
195
+ -------
196
+ dict
197
+ Dictionary of primitives with nat names as keys.
198
+ """
199
+ _prim_by_net = {}
200
+ for net, net_obj in self._pedb.nets.nets.items():
201
+ _prim_by_net[net] = [i for i in net_obj.primitives]
202
+ return _prim_by_net
203
+
204
+ @property
205
+ def primitives_by_layer(self):
206
+ """Primitives with layer names as keys.
207
+
208
+ Returns
209
+ -------
210
+ dict
211
+ Dictionary of primitives with layer names as keys.
212
+ """
213
+ _primitives_by_layer = {}
214
+ for lay in self.layers:
215
+ _primitives_by_layer[lay] = []
216
+ for lay in self._pedb.stackup.non_stackup_layers:
217
+ _primitives_by_layer[lay] = []
218
+ for i in self._layout.primitives:
219
+ try:
220
+ lay = i.layer.name
221
+ if lay in _primitives_by_layer:
222
+ _primitives_by_layer[lay].append(Primitive(self._pedb, i))
223
+ except (InvalidArgumentException, AttributeError):
224
+ pass
225
+ return _primitives_by_layer
226
+
227
+ @property
228
+ def rectangles(self):
229
+ """Rectangles.
230
+
231
+ Returns
232
+ -------
233
+ list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
234
+ List of rectangles.
235
+
236
+ """
237
+ return [Rectangle(self._pedb, i) for i in self.primitives if i.type == "rectangle"]
238
+
239
+ @property
240
+ def circles(self):
241
+ """Circles.
242
+
243
+ Returns
244
+ -------
245
+ list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
246
+ List of circles.
247
+
248
+ """
249
+ return [Circle(self._pedb, i) for i in self.primitives if i.type == "circle"]
250
+
251
+ @property
252
+ def paths(self):
253
+ """Paths.
254
+
255
+ Returns
256
+ -------
257
+ list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
258
+ List of paths.
259
+ """
260
+ return [Path(self._pedb, i) for i in self.primitives if i.type == "path"]
261
+
262
+ @property
263
+ def polygons(self):
264
+ """Polygons.
265
+
266
+ Returns
267
+ -------
268
+ list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
269
+ List of polygons.
270
+ """
271
+ return [Polygon(self._pedb, i) for i in self.primitives if i.type == "polygon"]
272
+
273
+ def get_polygons_by_layer(self, layer_name, net_list=None):
274
+ """Retrieve polygons by a layer.
275
+
276
+ Parameters
277
+ ----------
278
+ layer_name : str
279
+ Name of the layer.
280
+ net_list : list, optional
281
+ List of net names.
282
+
283
+ Returns
284
+ -------
285
+ list
286
+ List of primitive objects.
287
+ """
288
+ objinst = []
289
+ for el in self.polygons:
290
+ if el.layer.name == layer_name:
291
+ if not el.net.is_null:
292
+ if net_list and el.net.name in net_list:
293
+ objinst.append(el)
294
+ else:
295
+ objinst.append(el)
296
+ return objinst
297
+
298
+ def get_primitive_by_layer_and_point(self, point=None, layer=None, nets=None):
299
+ """Return primitive given coordinate point [x, y], layer name and nets.
300
+
301
+ Parameters
302
+ ----------
303
+ point : list
304
+ Coordinate [x, y]
305
+
306
+ layer : list or str, optional
307
+ list of layer name or layer name applied on filter.
308
+
309
+ nets : list or str, optional
310
+ list of net name or single net name applied on filter
311
+
312
+ Returns
313
+ -------
314
+ list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
315
+ List of primitives, polygons, paths and rectangles.
316
+ """
317
+ from ansys.edb.core.primitive.primitive import Circle as GrpcCircle
318
+ from ansys.edb.core.primitive.primitive import Path as GrpcPath
319
+ from ansys.edb.core.primitive.primitive import Polygon as GrpcPolygon
320
+ from ansys.edb.core.primitive.primitive import Rectangle as GrpcRectangle
321
+
322
+ if isinstance(layer, str) and layer not in list(self._pedb.stackup.signal_layers.keys()):
323
+ layer = None
324
+ if not isinstance(point, list) and len(point) == 2:
325
+ self._logger.error("Provided point must be a list of two values")
326
+ return False
327
+ pt = GrpcPointData(point)
328
+ if isinstance(nets, str):
329
+ nets = [nets]
330
+ elif nets and not isinstance(nets, list) and len(nets) == len([net for net in nets if isinstance(net, str)]):
331
+ _nets = []
332
+ for net in nets:
333
+ if net not in self._pedb.nets:
334
+ self._logger.error(
335
+ f"Net {net} used to find primitive from layer point and net not found, skipping it."
336
+ )
337
+ else:
338
+ _nets.append(self._pedb.nets[net])
339
+ if _nets:
340
+ nets = _nets
341
+ if not isinstance(layer, list) and layer:
342
+ layer = [layer]
343
+ _obj_instances = self._pedb.layout_instance.query_layout_obj_instances(
344
+ layer_filter=layer, net_filter=nets, spatial_filter=pt
345
+ )
346
+ returned_obj = []
347
+ for inst in _obj_instances:
348
+ primitive = inst.layout_obj.cast()
349
+ if isinstance(primitive, GrpcPath):
350
+ returned_obj.append(Path(self._pedb, primitive))
351
+ elif isinstance(primitive, GrpcPolygon):
352
+ returned_obj.append(Polygon(self._pedb, primitive))
353
+ elif isinstance(primitive, GrpcRectangle):
354
+ returned_obj.append(Rectangle(self._pedb, primitive))
355
+ elif isinstance(primitive, GrpcCircle):
356
+ returned_obj.append(Circle(self._pedb, primitive))
357
+ return returned_obj
358
+
359
+ @staticmethod
360
+ def get_polygon_bounding_box(polygon):
361
+ """Retrieve a polygon bounding box.
362
+
363
+ Parameters
364
+ ----------
365
+ polygon :
366
+ Name of the polygon.
367
+
368
+ Returns
369
+ -------
370
+ list
371
+ List of bounding box coordinates in the format ``[-x, -y, +x, +y]``.
372
+
373
+ Examples
374
+ --------
375
+ >>> poly = database.modeler.get_polygons_by_layer("GND")
376
+ >>> bounding = database.modeler.get_polygon_bounding_box(poly[0])
377
+ """
378
+ bounding_box = polygon.polygon_data.bbox()
379
+ return [
380
+ bounding_box[0].x.value,
381
+ bounding_box[0].y.value,
382
+ bounding_box[1].x.value,
383
+ bounding_box[1].y.value,
384
+ ]
385
+
386
+ @staticmethod
387
+ def get_polygon_points(polygon):
388
+ """Retrieve polygon points.
389
+
390
+ .. note::
391
+ For arcs, one point is returned.
392
+
393
+ Parameters
394
+ ----------
395
+ polygon :
396
+ class: `dotnet.database.edb_data.primitives_data.Primitive`
397
+
398
+ Returns
399
+ -------
400
+ list
401
+ List of tuples. Each tuple provides x, y point coordinate. If the length of two consecutives tuples
402
+ from the list equals 2, a segment is defined. The first tuple defines the starting point while the second
403
+ tuple the ending one. If the length of one tuple equals one, that means a polyline is defined and the value
404
+ is giving the arc height. Therefore to polyline is defined as starting point for the tuple
405
+ before in the list, the current one the arc height and the tuple after the polyline ending point.
406
+
407
+ Examples
408
+ --------
409
+
410
+ >>> poly = database.modeler.get_polygons_by_layer("GND")
411
+ >>> points = database.modeler.get_polygon_points(poly[0])
412
+
413
+ """
414
+ points = []
415
+ i = 0
416
+ continue_iterate = True
417
+ prev_point = None
418
+ while continue_iterate:
419
+ try:
420
+ point = polygon.polygon_data.points[i]
421
+ if prev_point != point:
422
+ if point.is_arc:
423
+ points.append([point.x.value])
424
+ else:
425
+ points.append([point.x.value, point.y.value])
426
+ prev_point = point
427
+ i += 1
428
+ else:
429
+ continue_iterate = False
430
+ except:
431
+ continue_iterate = False
432
+ return points
433
+
434
+ def parametrize_polygon(self, polygon, selection_polygon, offset_name="offsetx", origin=None):
435
+ """Parametrize pieces of a polygon based on another polygon.
436
+
437
+ Parameters
438
+ ----------
439
+ polygon :
440
+ Name of the polygon.
441
+ selection_polygon :
442
+ Polygon to use as a filter.
443
+ offset_name : str, optional
444
+ Name of the offset to create. The default is ``"offsetx"``.
445
+ origin : list, optional
446
+ List of the X and Y origins, which impacts the vector
447
+ computation and is needed to determine expansion direction.
448
+ The default is ``None``, in which case the vector is
449
+ computed from the polygon's center.
450
+
451
+ Returns
452
+ -------
453
+ bool
454
+ ``True`` when successful, ``False`` when failed.
455
+ """
456
+
457
+ def calc_slope(point, origin):
458
+ if point[0] - origin[0] != 0:
459
+ slope = math.atan((point[1] - origin[1]) / (point[0] - origin[0]))
460
+ xcoeff = math.sin(slope)
461
+ ycoeff = math.cos(slope)
462
+
463
+ else:
464
+ if point[1] > 0:
465
+ xcoeff = 0
466
+ ycoeff = 1
467
+ else:
468
+ xcoeff = 0
469
+ ycoeff = -1
470
+ if ycoeff > 0:
471
+ ycoeff = "+" + str(ycoeff)
472
+ else:
473
+ ycoeff = str(ycoeff)
474
+ if xcoeff > 0:
475
+ xcoeff = "+" + str(xcoeff)
476
+ else:
477
+ xcoeff = str(xcoeff)
478
+ return xcoeff, ycoeff
479
+
480
+ selection_polygon_data = selection_polygon.polygon_data
481
+ polygon_data = polygon.polygon_data
482
+ bound_center = polygon_data.bounding_circle()[0]
483
+ bound_center2 = selection_polygon_data.bounding_circle()[0]
484
+ center = [bound_center.x.value, bound_center.y.value]
485
+ center2 = [bound_center2.x.value, bound_center2.y.value]
486
+ x1, y1 = calc_slope(center2, center)
487
+
488
+ if not origin:
489
+ origin = [center[0] + float(x1) * 10000, center[1] + float(y1) * 10000]
490
+ self._pedb.add_design_variable(offset_name, 0.0, is_parameter=True)
491
+ i = 0
492
+ continue_iterate = True
493
+ prev_point = None
494
+ while continue_iterate:
495
+ try:
496
+ point = polygon_data.points[i]
497
+ if prev_point != point:
498
+ check_inside = selection_polygon_data.is_inside(point)
499
+ if check_inside:
500
+ xcoeff, ycoeff = calc_slope([point.x.value, point.x.value], origin)
501
+
502
+ new_points = GrpcPointData(
503
+ [
504
+ GrpcValue(str(point.x.value) + f"{xcoeff}*{offset_name}"),
505
+ GrpcValue(str(point.y.value) + f"{ycoeff}*{offset_name}"),
506
+ ]
507
+ )
508
+ polygon_data.points[i] = new_points
509
+ prev_point = point
510
+ i += 1
511
+ else:
512
+ continue_iterate = False
513
+ except:
514
+ continue_iterate = False
515
+ polygon.polygon_data = polygon_data
516
+ return True
517
+
518
+ def _create_path(
519
+ self,
520
+ points,
521
+ layer_name,
522
+ width=1,
523
+ net_name="",
524
+ start_cap_style="Round",
525
+ end_cap_style="Round",
526
+ corner_style="Round",
527
+ ):
528
+ """
529
+ Create a path based on a list of points.
530
+
531
+ Parameters
532
+ ----------
533
+ points: .:class:`dotnet.database.layout.Shape`
534
+ List of points.
535
+ layer_name : str
536
+ Name of the layer on which to create the path.
537
+ width : float, optional
538
+ Width of the path. The default is ``1``.
539
+ net_name: str, optional
540
+ Name of the net. The default is ``""``.
541
+ start_cap_style : str, optional
542
+ Style of the cap at its start. Options are ``"Round"``,
543
+ ``"Extended", `` and ``"Flat"``. The default is
544
+ ``"Round".
545
+ end_cap_style : str, optional
546
+ Style of the cap at its end. Options are ``"Round"``,
547
+ ``"Extended", `` and ``"Flat"``. The default is
548
+ ``"Round".
549
+ corner_style : str, optional
550
+ Style of the corner. Options are ``"Round"``,
551
+ ``"Sharp"`` and ``"Mitered"``. The default is ``"Round".
552
+
553
+ Returns
554
+ -------
555
+ :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
556
+ ``True`` when successful, ``False`` when failed.
557
+ """
558
+ net = self._pedb.nets.find_or_create_net(net_name)
559
+ if start_cap_style.lower() == "round":
560
+ start_cap_style = GrpcPathEndCapType.ROUND
561
+ elif start_cap_style.lower() == "extended":
562
+ start_cap_style = GrpcPathEndCapType.EXTENDED
563
+ else:
564
+ start_cap_style = GrpcPathEndCapType.FLAT
565
+ if end_cap_style.lower() == "round":
566
+ end_cap_style = GrpcPathEndCapType.ROUND
567
+ elif end_cap_style.lower() == "extended":
568
+ end_cap_style = GrpcPathEndCapType.EXTENDED
569
+ else:
570
+ end_cap_style = GrpcPathEndCapType.FLAT
571
+ if corner_style.lower() == "round":
572
+ corner_style = GrpcPathEndCapType.ROUND
573
+ elif corner_style.lower() == "sharp":
574
+ corner_style = GrpcPathCornerType.SHARP
575
+ else:
576
+ corner_style = GrpcPathCornerType.MITER
577
+ _points = []
578
+ for pt in points:
579
+ _pt = []
580
+ for coord in pt:
581
+ coord = GrpcValue(coord, self._pedb.active_cell)
582
+ _pt.append(coord)
583
+ _points.append(_pt)
584
+ points = _points
585
+
586
+ width = GrpcValue(width, self._pedb.active_cell)
587
+
588
+ polygon_data = GrpcPolygonData(points=[GrpcPointData(i) for i in points])
589
+ path = Path.create(
590
+ layout=self._active_layout,
591
+ layer=layer_name,
592
+ net=net,
593
+ width=width,
594
+ end_cap1=start_cap_style,
595
+ end_cap2=end_cap_style,
596
+ corner_style=corner_style,
597
+ points=polygon_data,
598
+ )
599
+ if path.is_null: # pragma: no cover
600
+ self._logger.error("Null path created")
601
+ return False
602
+ return Path(self._pedb, path)
603
+
604
+ def create_trace(
605
+ self,
606
+ path_list,
607
+ layer_name,
608
+ width=1,
609
+ net_name="",
610
+ start_cap_style="Round",
611
+ end_cap_style="Round",
612
+ corner_style="Round",
613
+ ):
614
+ """
615
+ Create a trace based on a list of points.
616
+
617
+ Parameters
618
+ ----------
619
+ path_list : list
620
+ List of points.
621
+ layer_name : str
622
+ Name of the layer on which to create the path.
623
+ width : float, optional
624
+ Width of the path. The default is ``1``.
625
+ net_name : str, optional
626
+ Name of the net. The default is ``""``.
627
+ start_cap_style : str, optional
628
+ Style of the cap at its start. Options are ``"Round"``,
629
+ ``"Extended",`` and ``"Flat"``. The default is
630
+ ``"Round"``.
631
+ end_cap_style : str, optional
632
+ Style of the cap at its end. Options are ``"Round"``,
633
+ ``"Extended",`` and ``"Flat"``. The default is
634
+ ``"Round"``.
635
+ corner_style : str, optional
636
+ Style of the corner. Options are ``"Round"``,
637
+ ``"Sharp"`` and ``"Mitered"``. The default is ``"Round"``.
638
+
639
+ Returns
640
+ -------
641
+ :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
642
+ """
643
+
644
+ primitive = self._create_path(
645
+ points=path_list,
646
+ layer_name=layer_name,
647
+ net_name=net_name,
648
+ width=width,
649
+ start_cap_style=start_cap_style,
650
+ end_cap_style=end_cap_style,
651
+ corner_style=corner_style,
652
+ )
653
+
654
+ return primitive
655
+
656
+ def create_polygon(self, points, layer_name, voids=[], net_name=""):
657
+ """Create a polygon based on a list of points and voids.
658
+
659
+ Parameters
660
+ ----------
661
+ points : list of points or PolygonData.
662
+ - [x, y] coordinate
663
+ - [x, y, height] for an arc with specific height (between previous point and actual point)
664
+ - [x, y, rotation, xc, yc] for an arc given a point, rotation and center.
665
+ layer_name : str
666
+ Name of the layer on which to create the polygon.
667
+ voids : list, optional
668
+ List of shape objects for voids or points that creates the shapes. The default is``[]``.
669
+ net_name : str, optional
670
+ Name of the net. The default is ``""``.
671
+
672
+ Returns
673
+ -------
674
+ bool, :class:`dotnet.database.edb_data.primitives.Primitive`
675
+ Polygon when successful, ``False`` when failed.
676
+ """
677
+ net = self._pedb.nets.find_or_create_net(net_name)
678
+ if isinstance(points, list):
679
+ new_points = []
680
+ for idx, i in enumerate(points):
681
+ new_points.append(
682
+ GrpcPointData([GrpcValue(i[0], self._pedb.active_cell), GrpcValue(i[1], self._pedb.active_cell)])
683
+ )
684
+ polygon_data = GrpcPolygonData(points=new_points)
685
+
686
+ elif isinstance(points, GrpcPolygonData):
687
+ polygon_data = points
688
+ else:
689
+ polygon_data = points
690
+ if not polygon_data.points:
691
+ self._logger.error("Failed to create main shape polygon data")
692
+ return False
693
+ for void in voids:
694
+ if isinstance(void, list):
695
+ void_polygon_data = GrpcPolygonData(points=void)
696
+ else:
697
+ void_polygon_data = void.polygon_data
698
+ if not void_polygon_data.points:
699
+ self._logger.error("Failed to create void polygon data")
700
+ return False
701
+ polygon_data.holes.append(void_polygon_data)
702
+ polygon = Polygon.create(layout=self._active_layout, layer=layer_name, net=net, polygon_data=polygon_data)
703
+ if polygon.is_null or polygon_data is False: # pragma: no cover
704
+ self._logger.error("Null polygon created")
705
+ return False
706
+ return Polygon(self._pedb, polygon)
707
+
708
+ def create_rectangle(
709
+ self,
710
+ layer_name,
711
+ net_name="",
712
+ lower_left_point="",
713
+ upper_right_point="",
714
+ center_point="",
715
+ width="",
716
+ height="",
717
+ representation_type="lower_left_upper_right",
718
+ corner_radius="0mm",
719
+ rotation="0deg",
720
+ ):
721
+ """Create rectangle.
722
+
723
+ Parameters
724
+ ----------
725
+ layer_name : str
726
+ Name of the layer on which to create the rectangle.
727
+ net_name : str
728
+ Name of the net. The default is ``""``.
729
+ lower_left_point : list
730
+ Lower left point when ``representation_type="lower_left_upper_right"``. The default is ``""``.
731
+ upper_right_point : list
732
+ Upper right point when ``representation_type="lower_left_upper_right"``. The default is ``""``.
733
+ center_point : list
734
+ Center point when ``representation_type="center_width_height"``. The default is ``""``.
735
+ width : str
736
+ Width of the rectangle when ``representation_type="center_width_height"``. The default is ``""``.
737
+ height : str
738
+ Height of the rectangle when ``representation_type="center_width_height"``. The default is ``""``.
739
+ representation_type : str, optional
740
+ Type of the rectangle representation. The default is ``lower_left_upper_right``. Options are
741
+ ``"lower_left_upper_right"`` and ``"center_width_height"``.
742
+ corner_radius : str, optional
743
+ Radius of the rectangle corner. The default is ``"0mm"``.
744
+ rotation : str, optional
745
+ Rotation of the rectangle. The default is ``"0deg"``.
746
+
747
+ Returns
748
+ -------
749
+ :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
750
+ Rectangle when successful, ``False`` when failed.
751
+ """
752
+ edb_net = self._pedb.nets.find_or_create_net(net_name)
753
+ if representation_type == "lower_left_upper_right":
754
+ rep_type = GrpcRectangleRepresentationType.LOWER_LEFT_UPPER_RIGHT
755
+ rect = Rectangle.create(
756
+ layout=self._active_layout,
757
+ layer=layer_name,
758
+ net=edb_net,
759
+ rep_type=rep_type,
760
+ param1=GrpcValue(lower_left_point[0]),
761
+ param2=GrpcValue(lower_left_point[1]),
762
+ param3=GrpcValue(upper_right_point[0]),
763
+ param4=GrpcValue(upper_right_point[1]),
764
+ corner_rad=GrpcValue(corner_radius),
765
+ rotation=GrpcValue(rotation),
766
+ )
767
+ else:
768
+ rep_type = GrpcRectangleRepresentationType.CENTER_WIDTH_HEIGHT
769
+ if isinstance(width, str):
770
+ if width in self._pedb.variables:
771
+ width = GrpcValue(width, self._pedb.active_cell)
772
+ else:
773
+ width = GrpcValue(width)
774
+ else:
775
+ width = GrpcValue(width)
776
+ if isinstance(height, str):
777
+ if height in self._pedb.variables:
778
+ height = GrpcValue(height, self._pedb.active_cell)
779
+ else:
780
+ height = GrpcValue(width)
781
+ else:
782
+ height = GrpcValue(width)
783
+ rect = Rectangle.create(
784
+ layout=self._active_layout,
785
+ layer=layer_name,
786
+ net=edb_net,
787
+ rep_type=rep_type,
788
+ param1=GrpcValue(center_point[0]),
789
+ param2=GrpcValue(center_point[1]),
790
+ param3=GrpcValue(width),
791
+ param4=GrpcValue(height),
792
+ corner_rad=GrpcValue(corner_radius),
793
+ rotation=GrpcValue(rotation),
794
+ )
795
+ if not rect.is_null:
796
+ return Rectangle(self._pedb, rect)
797
+ return False
798
+
799
+ def create_circle(self, layer_name, x, y, radius, net_name=""):
800
+ """Create a circle on a specified layer.
801
+
802
+ Parameters
803
+ ----------
804
+ layer_name : str
805
+ Name of the layer.
806
+ x : float
807
+ Position on the X axis.
808
+ y : float
809
+ Position on the Y axis.
810
+ radius : float
811
+ Radius of the circle.
812
+ net_name : str, optional
813
+ Name of the net. The default is ``None``, in which case the
814
+ default name is assigned.
815
+
816
+ Returns
817
+ -------
818
+ :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
819
+ Objects of the circle created when successful.
820
+ """
821
+ edb_net = self._pedb.nets.find_or_create_net(net_name)
822
+
823
+ circle = Circle.create(
824
+ layout=self._active_layout,
825
+ layer=layer_name,
826
+ net=edb_net,
827
+ center_x=GrpcValue(x),
828
+ center_y=GrpcValue(y),
829
+ radius=GrpcValue(radius),
830
+ )
831
+ if not circle.is_null:
832
+ return Circle(self._pedb, circle)
833
+ return False
834
+
835
+ def delete_primitives(self, net_names):
836
+ """Delete primitives by net names.
837
+
838
+ Parameters
839
+ ----------
840
+ net_names : str, list
841
+ Names of the nets to delete.
842
+
843
+ Returns
844
+ -------
845
+ bool
846
+ ``True`` when successful, ``False`` when failed.
847
+
848
+ References
849
+ ----------
850
+
851
+ >>> Edb.modeler.delete_primitives(net_names=["GND"])
852
+ """
853
+ if not isinstance(net_names, list): # pragma: no cover
854
+ net_names = [net_names]
855
+
856
+ for p in self.primitives[:]:
857
+ if p.net_name in net_names:
858
+ p.delete()
859
+ return True
860
+
861
+ def get_primitives(self, net_name=None, layer_name=None, prim_type=None, is_void=False):
862
+ """Get primitives by conditions.
863
+
864
+ Parameters
865
+ ----------
866
+ net_name : str, optional
867
+ Set filter on net_name. Default is `None`.
868
+ layer_name : str, optional
869
+ Set filter on layer_name. Default is `None`.
870
+ prim_type : str, optional
871
+ Set filter on primitive type. Default is `None`.
872
+ is_void : bool
873
+ Set filter on is_void. Default is 'False'
874
+ Returns
875
+ -------
876
+ list
877
+ List of filtered primitives
878
+ """
879
+ prims = []
880
+ for el in self.primitives:
881
+ if not el.primitive_type:
882
+ continue
883
+ if net_name:
884
+ if not el.net.name == net_name:
885
+ continue
886
+ if layer_name:
887
+ if not el.layer.name == layer_name:
888
+ continue
889
+ if prim_type:
890
+ if not el.primitive_type.name.lower() == prim_type:
891
+ continue
892
+ if not el.is_void == is_void:
893
+ continue
894
+ prims.append(el)
895
+ return prims
896
+
897
+ def fix_circle_void_for_clipping(self):
898
+ """Fix issues when circle void are clipped due to a bug in EDB.
899
+
900
+ Returns
901
+ -------
902
+ bool
903
+ ``True`` when successful, ``False`` when no changes were applied.
904
+ """
905
+ for void_circle in self.circles:
906
+ if not void_circle.is_void:
907
+ continue
908
+ circ_params = void_circle.get_parameters()
909
+
910
+ cloned_circle = Circle.create(
911
+ layout=self._active_layout,
912
+ layer=void_circle.layer_name,
913
+ net=void_circle.net,
914
+ center_x=GrpcValue(circ_params[0]),
915
+ center_y=GrpcValue(circ_params[1]),
916
+ radius=GrpcValue(circ_params[2]),
917
+ )
918
+ if not cloned_circle.is_null:
919
+ cloned_circle.is_negative = True
920
+ void_circle.delete()
921
+ return True
922
+
923
+ @staticmethod
924
+ def add_void(shape, void_shape):
925
+ """Add a void into a shape.
926
+
927
+ Parameters
928
+ ----------
929
+ shape : Polygon
930
+ Shape of the main object.
931
+ void_shape : list, Path
932
+ Shape of the voids.
933
+ """
934
+ flag = False
935
+ if not isinstance(void_shape, list):
936
+ void_shape = [void_shape]
937
+ for void in void_shape:
938
+ if isinstance(void, Primitive):
939
+ shape._edb_object.add_void(void)
940
+ flag = True
941
+ else:
942
+ shape._edb_object.add_void(void)
943
+ flag = True
944
+ if not flag:
945
+ return flag
946
+ return True
947
+
948
+ def shape_to_polygon_data(self, shape):
949
+ """Convert a shape to polygon data.
950
+
951
+ Parameters
952
+ ----------
953
+ shape : :class:`pyedb.dotnet.database.modeler.Modeler.Shape`
954
+ Type of the shape to convert. Options are ``"rectangle"`` and ``"polygon"``.
955
+ """
956
+ if shape.type == "polygon":
957
+ return self._createPolygonDataFromPolygon(shape)
958
+ elif shape.type == "rectangle":
959
+ return self._createPolygonDataFromRectangle(shape)
960
+ else:
961
+ self._logger.error(
962
+ "Unsupported shape type %s when creating a polygon primitive.",
963
+ shape.type,
964
+ )
965
+ return None
966
+
967
+ def _createPolygonDataFromPolygon(self, shape):
968
+ points = shape.points
969
+ if not self._validatePoint(points[0]):
970
+ self._logger.error("Error validating point.")
971
+ return None
972
+ arcs = []
973
+ is_parametric = False
974
+ for i in range(len(points) - 1):
975
+ if i == 0:
976
+ startPoint = points[-1]
977
+ endPoint = points[i]
978
+ else:
979
+ startPoint = points[i - 1]
980
+ endPoint = points[i]
981
+
982
+ if not self._validatePoint(endPoint):
983
+ return None
984
+ startPoint = [GrpcValue(i) for i in startPoint]
985
+ endPoint = [GrpcValue(i) for i in endPoint]
986
+ if len(endPoint) == 2:
987
+ is_parametric = (
988
+ is_parametric
989
+ or startPoint[0].is_parametric
990
+ or startPoint[1].is_parametric
991
+ or endPoint[0].is_parametric
992
+ or endPoint[1].is_parametric
993
+ )
994
+ arc = GrpcArcData(
995
+ GrpcPointData([startPoint[0], startPoint[1]]), GrpcPointData([endPoint[0], endPoint[1]])
996
+ )
997
+ arcs.append(arc)
998
+ elif len(endPoint) == 3:
999
+ is_parametric = (
1000
+ is_parametric
1001
+ or startPoint[0].is_parametric
1002
+ or startPoint[1].is_parametric
1003
+ or endPoint[0].is_parametric
1004
+ or endPoint[1].is_parametric
1005
+ or endPoint[2].is_parametric
1006
+ )
1007
+ arc = GrpcArcData(
1008
+ GrpcPointData([startPoint[0], startPoint[1]]),
1009
+ GrpcPointData([endPoint[0], endPoint[1]]),
1010
+ kwarg={"height": endPoint[2]},
1011
+ )
1012
+ arcs.append(arc)
1013
+ elif len(endPoint) == 5:
1014
+ is_parametric = (
1015
+ is_parametric
1016
+ or startPoint[0].is_parametric
1017
+ or startPoint[1].is_parametric
1018
+ or endPoint[0].is_parametric
1019
+ or endPoint[1].is_parametric
1020
+ or endPoint[3].is_parametric
1021
+ or endPoint[4].is_parametric
1022
+ )
1023
+ if endPoint[2].is_cw:
1024
+ rotationDirection = GrpcPolygonSenseType.SENSE_CW
1025
+ elif endPoint[2].is_ccw:
1026
+ rotationDirection = GrpcPolygonSenseType.SENSE_CCW
1027
+ else:
1028
+ self._logger.error("Invalid rotation direction %s is specified.", endPoint[2])
1029
+ return None
1030
+ arc = GrpcArcData(
1031
+ GrpcPointData(startPoint),
1032
+ GrpcPointData(endPoint),
1033
+ )
1034
+ # arc.direction = rotationDirection,
1035
+ # arc.center = GrpcPointData([endPoint[3], endPoint[4]]),
1036
+ arcs.append(arc)
1037
+ polygon = GrpcPolygonData(arcs=arcs)
1038
+ if not is_parametric:
1039
+ return polygon
1040
+ else:
1041
+ k = 0
1042
+ for pt in points:
1043
+ point = [GrpcValue(i) for i in pt]
1044
+ new_points = GrpcPointData(point)
1045
+ if len(point) > 2:
1046
+ k += 1
1047
+ polygon.set_point(k, new_points)
1048
+ k += 1
1049
+ return polygon
1050
+
1051
+ def _validatePoint(self, point, allowArcs=True):
1052
+ if len(point) == 2:
1053
+ if not isinstance(point[0], (int, float, str)):
1054
+ self._logger.error("Point X value must be a number.")
1055
+ return False
1056
+ if not isinstance(point[1], (int, float, str)):
1057
+ self._logger.error("Point Y value must be a number.")
1058
+ return False
1059
+ return True
1060
+ elif len(point) == 3:
1061
+ if not allowArcs: # pragma: no cover
1062
+ self._logger.error("Arc found but arcs are not allowed in _validatePoint.")
1063
+ return False
1064
+ if not isinstance(point[0], (int, float, str)): # pragma: no cover
1065
+ self._logger.error("Point X value must be a number.")
1066
+ return False
1067
+ if not isinstance(point[1], (int, float, str)): # pragma: no cover
1068
+ self._logger.error("Point Y value must be a number.")
1069
+ return False
1070
+ if not isinstance(point[1], (int, float, str)): # pragma: no cover
1071
+ self._logger.error("Invalid point height.")
1072
+ return False
1073
+ return True
1074
+ elif len(point) == 5:
1075
+ if not allowArcs: # pragma: no cover
1076
+ self._logger.error("Arc found but arcs are not allowed in _validatePoint.")
1077
+ return False
1078
+ if not isinstance(point[0], (int, float, str)): # pragma: no cover
1079
+ self._logger.error("Point X value must be a number.")
1080
+ return False
1081
+ if not isinstance(point[1], (int, float, str)): # pragma: no cover
1082
+ self._logger.error("Point Y value must be a number.")
1083
+ return False
1084
+ if not isinstance(point[2], str) or point[2] not in ["cw", "ccw"]:
1085
+ self._logger.error("Invalid rotation direction {} is specified.")
1086
+ return False
1087
+ if not isinstance(point[3], (int, float, str)): # pragma: no cover
1088
+ self._logger.error("Arc center point X value must be a number.")
1089
+ return False
1090
+ if not isinstance(point[4], (int, float, str)): # pragma: no cover
1091
+ self._logger.error("Arc center point Y value must be a number.")
1092
+ return False
1093
+ return True
1094
+ else: # pragma: no cover
1095
+ self._logger.error("Arc point descriptor has incorrect number of elements (%s)", len(point))
1096
+ return False
1097
+
1098
+ def _createPolygonDataFromRectangle(self, shape):
1099
+ # if not self._validatePoint(shape.pointA, False) or not self._validatePoint(shape.pointB, False):
1100
+ # return None
1101
+ # pointA = GrpcPointData(pointA[0]), self._get_edb_value(shape.pointA[1])
1102
+ # )
1103
+ # pointB = self._edb.geometry.point_data(
1104
+ # self._get_edb_value(shape.pointB[0]), self._get_edb_value(shape.pointB[1])
1105
+ # )
1106
+ # return self._edb.geometry.polygon_data.create_from_bbox((pointA, pointB))
1107
+ pass
1108
+
1109
+ def parametrize_trace_width(
1110
+ self,
1111
+ nets_name,
1112
+ layers_name=None,
1113
+ parameter_name="trace_width",
1114
+ variable_value=None,
1115
+ ):
1116
+ """Parametrize a Trace on specific layer or all stackup.
1117
+
1118
+ Parameters
1119
+ ----------
1120
+ nets_name : str, list
1121
+ name of the net or list of nets to parametrize.
1122
+ layers_name : str, optional
1123
+ name of the layer or list of layers to which the net to parametrize has to be included.
1124
+ parameter_name : str, optional
1125
+ name of the parameter to create.
1126
+ variable_value : str, float, optional
1127
+ value with units of parameter to create.
1128
+ If None, the first trace width of Net will be used as parameter value.
1129
+
1130
+ Returns
1131
+ -------
1132
+ bool
1133
+ """
1134
+ if isinstance(nets_name, str):
1135
+ nets_name = [nets_name]
1136
+ if isinstance(layers_name, str):
1137
+ layers_name = [layers_name]
1138
+ for net_name in nets_name:
1139
+ for p in self.paths:
1140
+ _parameter_name = f"{parameter_name}_{p.id}"
1141
+ if not p.net.is_null:
1142
+ if p.net.name == net_name:
1143
+ if not layers_name:
1144
+ if not variable_value:
1145
+ variable_value = p.width
1146
+ self._pedb.active_cell.add_variable(
1147
+ name=_parameter_name, value=GrpcValue(variable_value), is_param=True
1148
+ )
1149
+ p.width = GrpcValue(_parameter_name, self._pedb.active_cell)
1150
+ elif p.layer.name in layers_name:
1151
+ if not variable_value:
1152
+ variable_value = p.width
1153
+ self._pedb.add_design_variable(parameter_name, variable_value, True)
1154
+ p.width = GrpcValue(_parameter_name, self._pedb.active_cell)
1155
+ return True
1156
+
1157
+ def unite_polygons_on_layer(self, layer_name=None, delete_padstack_gemometries=False, net_names_list=[]):
1158
+ """Try to unite all Polygons on specified layer.
1159
+
1160
+ Parameters
1161
+ ----------
1162
+ layer_name : str, optional
1163
+ Name of layer name to unite objects on. The default is ``None``, in which case all layers are taken.
1164
+ delete_padstack_gemometries : bool, optional
1165
+ Whether to delete all padstack geometries. The default is ``False``.
1166
+ net_names_list : list[str] : optional
1167
+ Net names list filter. The default is ``[]``, in which case all nets are taken.
1168
+
1169
+ Returns
1170
+ -------
1171
+ bool
1172
+ ``True`` is successful.
1173
+ """
1174
+ if isinstance(layer_name, str):
1175
+ layer_name = [layer_name]
1176
+ if not layer_name:
1177
+ layer_name = list(self._pedb.stackup.signal_layers.keys())
1178
+
1179
+ for lay in layer_name:
1180
+ self._logger.info(f"Uniting Objects on layer {lay}.")
1181
+ poly_by_nets = {}
1182
+ all_voids = []
1183
+ list_polygon_data = []
1184
+ delete_list = []
1185
+ if lay in list(self.polygons_by_layer.keys()):
1186
+ for poly in self.polygons_by_layer[lay]:
1187
+ poly = poly
1188
+ if not poly.net.name in list(poly_by_nets.keys()):
1189
+ if poly.net.name:
1190
+ poly_by_nets[poly.net.name] = [poly]
1191
+ else:
1192
+ if poly.net.name:
1193
+ poly_by_nets[poly.net.name].append(poly)
1194
+ for net in poly_by_nets:
1195
+ if net in net_names_list or not net_names_list:
1196
+ for i in poly_by_nets[net]:
1197
+ list_polygon_data.append(i.polygon_data)
1198
+ delete_list.append(i)
1199
+ all_voids.append(i.voids)
1200
+ a = GrpcPolygonData.unite(list_polygon_data)
1201
+ for item in a:
1202
+ for v in all_voids:
1203
+ for void in v:
1204
+ if item.intersection_type(void.polygon_data) == 2:
1205
+ item.add_hole(void.polygon_data)
1206
+ self.create_polygon(item, layer_name=lay, voids=[], net_name=net)
1207
+ for v in all_voids:
1208
+ for void in v:
1209
+ for poly in poly_by_nets[net]: # pragma no cover
1210
+ if void.polygon_data.intersection_type(poly.polygon_data).value >= 2:
1211
+ try:
1212
+ id = delete_list.index(poly)
1213
+ except ValueError:
1214
+ id = -1
1215
+ if id >= 0:
1216
+ delete_list.pop(id)
1217
+ for poly in delete_list:
1218
+ poly.delete()
1219
+
1220
+ if delete_padstack_gemometries:
1221
+ self._logger.info("Deleting Padstack Definitions")
1222
+ for pad in self._pedb.padstacks.definitions:
1223
+ p1 = self._pedb.padstacks.definitions[pad].edb_padstack.data
1224
+ if len(p1.get_layer_names()) > 1:
1225
+ self._pedb.padstacks.remove_pads_from_padstack(pad)
1226
+ return True
1227
+
1228
+ def defeature_polygon(self, poly, tolerance=0.001):
1229
+ """Defeature the polygon based on the maximum surface deviation criteria.
1230
+
1231
+ Parameters
1232
+ ----------
1233
+ maximum_surface_deviation : float
1234
+ poly : Edb Polygon primitive
1235
+ Polygon to defeature.
1236
+ tolerance : float, optional
1237
+ Maximum tolerance criteria. The default is ``0.001``.
1238
+
1239
+ Returns
1240
+ -------
1241
+ bool
1242
+ ``True`` when successful, ``False`` when failed.
1243
+ """
1244
+ new_poly = poly.polygon_data.defeature(tol=tolerance)
1245
+ if not new_poly.points:
1246
+ self._pedb.logger.error(
1247
+ f"Defeaturing on polygon {poly.id} returned empty polygon, tolerance threshold " f"might too large. "
1248
+ )
1249
+ return False
1250
+ poly.polygon_data = new_poly
1251
+ return True
1252
+
1253
+ def get_layout_statistics(self, evaluate_area=False, net_list=None):
1254
+ """Return EDBStatistics object from a layout.
1255
+
1256
+ Parameters
1257
+ ----------
1258
+
1259
+ evaluate_area : optional bool
1260
+ When True evaluates the layout metal surface, can take time-consuming,
1261
+ avoid using this option on large design.
1262
+
1263
+ Returns
1264
+ -------
1265
+
1266
+ EDBStatistics object.
1267
+
1268
+ """
1269
+ stat_model = LayoutStatistics()
1270
+ stat_model.num_layers = len(list(self._pedb.stackup.layers.values()))
1271
+ stat_model.num_capacitors = len(self._pedb.components.capacitors)
1272
+ stat_model.num_resistors = len(self._pedb.components.resistors)
1273
+ stat_model.num_inductors = len(self._pedb.components.inductors)
1274
+ bbox = self._pedb._hfss.get_layout_bounding_box(self._active_layout)
1275
+ stat_model._layout_size = bbox[2] - bbox[0], bbox[3] - bbox[1]
1276
+ stat_model.num_discrete_components = (
1277
+ len(self._pedb.components.Others) + len(self._pedb.components.ICs) + len(self._pedb.components.IOs)
1278
+ )
1279
+ stat_model.num_inductors = len(self._pedb.components.inductors)
1280
+ stat_model.num_resistors = len(self._pedb.components.resistors)
1281
+ stat_model.num_capacitors = len(self._pedb.components.capacitors)
1282
+ stat_model.num_nets = len(self._pedb.nets.nets)
1283
+ stat_model.num_traces = len(self._pedb.modeler.paths)
1284
+ stat_model.num_polygons = len(self._pedb.modeler.polygons)
1285
+ stat_model.num_vias = len(self._pedb.padstacks.instances)
1286
+ stat_model.stackup_thickness = self._pedb.stackup.get_layout_thickness()
1287
+ if evaluate_area:
1288
+ outline_surface = stat_model.layout_size[0] * stat_model.layout_size[1]
1289
+ if net_list:
1290
+ netlist = list(self._pedb.nets.nets.keys())
1291
+ _poly = self._pedb.get_conformal_polygon_from_netlist(netlist)
1292
+ else:
1293
+ for layer in list(self._pedb.stackup.signal_layers.keys()):
1294
+ surface = 0.0
1295
+ primitives = self.primitives_by_layer[layer]
1296
+ for prim in primitives:
1297
+ if prim.primitive_type.name == "PATH":
1298
+ surface += Path(self._pedb, prim).length * prim.cast().width.value
1299
+ if prim.primitive_type.name == "POLYGON":
1300
+ surface += prim.polygon_data.area()
1301
+ stat_model.occupying_surface[layer] = surface
1302
+ stat_model.occupying_ratio[layer] = surface / outline_surface
1303
+ return stat_model
1304
+
1305
+ def create_bondwire(
1306
+ self,
1307
+ definition_name,
1308
+ placement_layer,
1309
+ width,
1310
+ material,
1311
+ start_layer_name,
1312
+ start_x,
1313
+ start_y,
1314
+ end_layer_name,
1315
+ end_x,
1316
+ end_y,
1317
+ net,
1318
+ start_cell_instance_name=None,
1319
+ end_cell_instance_name=None,
1320
+ bondwire_type="jedec4",
1321
+ ):
1322
+ """Create a bondwire object.
1323
+
1324
+ Parameters
1325
+ ----------
1326
+ bondwire_type : :class:`BondwireType`
1327
+ Type of bondwire: kAPDBondWire or kJDECBondWire types.
1328
+ definition_name : str
1329
+ Bondwire definition name.
1330
+ placement_layer : str
1331
+ Layer name this bondwire will be on.
1332
+ width : :class:`Value <ansys.edb.utility.Value>`
1333
+ Bondwire width.
1334
+ material : str
1335
+ Bondwire material name.
1336
+ start_layer_name : str
1337
+ Name of start layer.
1338
+ start_x : :class:`Value <ansys.edb.utility.Value>`
1339
+ X value of start point.
1340
+ start_y : :class:`Value <ansys.edb.utility.Value>`
1341
+ Y value of start point.
1342
+ end_layer_name : str
1343
+ Name of end layer.
1344
+ end_x : :class:`Value <ansys.edb.utility.Value>`
1345
+ X value of end point.
1346
+ end_y : :class:`Value <ansys.edb.utility.Value>`
1347
+ Y value of end point.
1348
+ net : str or :class:`Net <ansys.edb.net.Net>` or None
1349
+ Net of the Bondwire.
1350
+ start_cell_instance_name : str, optional
1351
+ Cell instance name where the bondwire starts.
1352
+ end_cell_instance_name : str, optional
1353
+ Cell instance name where the bondwire ends.
1354
+
1355
+ Returns
1356
+ -------
1357
+ :class:`pyedb.dotnet.database.dotnet.primitive.BondwireDotNet`
1358
+ Bondwire object created.
1359
+ """
1360
+ from ansys.edb.core.hierarchy.cell_instance import (
1361
+ CellInstance as GrpcCellInstance,
1362
+ )
1363
+
1364
+ start_cell_inst = None
1365
+ end_cell_inst = None
1366
+ cell_instances = {cell_inst.name: cell_inst for cell_inst in self._active_layout.cell_instances}
1367
+ if start_cell_instance_name:
1368
+ if start_cell_instance_name not in cell_instances:
1369
+ start_cell_inst = GrpcCellInstance.create(
1370
+ self._pedb.active_layout, start_cell_instance_name, ref=self._pedb.active_layout
1371
+ )
1372
+ else:
1373
+ start_cell_inst = cell_instances[start_cell_instance_name]
1374
+ cell_instances = {cell_inst.name: cell_inst for cell_inst in self._active_layout.cell_instances}
1375
+ if end_cell_instance_name:
1376
+ if end_cell_instance_name not in cell_instances:
1377
+ end_cell_inst = GrpcCellInstance.create(
1378
+ self._pedb.active_layout, end_cell_instance_name, ref=self._pedb.active_layout
1379
+ )
1380
+ else:
1381
+ end_cell_inst = cell_instances[end_cell_instance_name]
1382
+
1383
+ if bondwire_type == "jedec4":
1384
+ bondwire_type = GrpcBondwireType.JEDEC4
1385
+ elif bondwire_type == "jedec5":
1386
+ bondwire_type = GrpcBondwireType.JEDEC5
1387
+ elif bondwire_type == "apd":
1388
+ bondwire_type = GrpcBondwireType.APD
1389
+ else:
1390
+ bondwire_type = GrpcBondwireType.JEDEC4
1391
+ bw = Bondwire.create(
1392
+ layout=self._active_layout,
1393
+ bondwire_type=bondwire_type,
1394
+ definition_name=definition_name,
1395
+ placement_layer=placement_layer,
1396
+ width=GrpcValue(width),
1397
+ material=material,
1398
+ start_layer_name=start_layer_name,
1399
+ start_x=GrpcValue(start_x),
1400
+ start_y=GrpcValue(start_y),
1401
+ end_layer_name=end_layer_name,
1402
+ end_x=GrpcValue(end_x),
1403
+ end_y=GrpcValue(end_y),
1404
+ net=net,
1405
+ end_context=end_cell_inst,
1406
+ start_context=start_cell_inst,
1407
+ )
1408
+ return Bondwire(self._pedb, bw)
1409
+
1410
+ def create_pin_group(
1411
+ self,
1412
+ name: str,
1413
+ pins_by_id=None,
1414
+ pins_by_aedt_name=None,
1415
+ pins_by_name=None,
1416
+ ):
1417
+ """Create a PinGroup.
1418
+
1419
+ Parameters
1420
+ name : str,
1421
+ Name of the PinGroup.
1422
+ pins_by_id : list[int] or None
1423
+ List of pins by ID.
1424
+ pins_by_aedt_name : list[str] or None
1425
+ List of pins by AEDT name.
1426
+ pins_by_name : list[str] or None
1427
+ List of pins by name.
1428
+ """
1429
+ # TODO move this method to components and merge with existing one
1430
+ pins = {}
1431
+ if pins_by_id:
1432
+ if isinstance(pins_by_id, int):
1433
+ pins_by_id = [pins_by_id]
1434
+ for p in pins_by_id:
1435
+ edb_pin = None
1436
+ if p in self._pedb.padstacks.instances:
1437
+ edb_pin = self._pedb.padstacks.instances[p]
1438
+ if edb_pin and not p in pins:
1439
+ pins[p] = edb_pin
1440
+ if not pins_by_aedt_name:
1441
+ pins_by_aedt_name = []
1442
+ if not pins_by_name:
1443
+ pins_by_name = []
1444
+ if pins_by_aedt_name or pins_by_name:
1445
+ if isinstance(pins_by_aedt_name, str):
1446
+ pins_by_aedt_name = [pins_by_aedt_name]
1447
+ if isinstance(pins_by_name, str):
1448
+ pins_by_name = [pins_by_name]
1449
+ p_inst = self._pedb.layout.padstack_instances
1450
+ _pins = {pin.id: pin for pin in p_inst if pin.aedt_name in pins_by_aedt_name or pin.name in pins_by_name}
1451
+ if not pins:
1452
+ pins = _pins
1453
+ else:
1454
+ for id, pin in _pins.items():
1455
+ if not id in pins:
1456
+ pins[id] = pin
1457
+ if not pins:
1458
+ self._logger.error("No pin found.")
1459
+ return False
1460
+ pins = list(pins.values())
1461
+ obj = GrpcPinGroup.create(layout=self._pedb.active_layout, name=name, padstack_instances=pins)
1462
+ if obj.is_null:
1463
+ raise RuntimeError(f"Failed to create pin group {name}.")
1464
+ else:
1465
+ net_obj = [i.net for i in pins if not i.net.is_null]
1466
+ if net_obj:
1467
+ obj.net = net_obj[0]
1468
+ return self._pedb.siwave.pin_groups[name]