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,2572 @@
1
+ # Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ from ansys.edb.core.database import ProductIdType as GrpcProductIdType
24
+ from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
25
+ from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
26
+ from ansys.edb.core.hierarchy.component_group import (
27
+ ComponentGroup as GrpcComponentGroup,
28
+ )
29
+ from ansys.edb.core.terminal.terminals import BoundaryType as GrpcBoundaryType
30
+ from ansys.edb.core.terminal.terminals import EdgeTerminal as GrpcEdgeTerminal
31
+ from ansys.edb.core.terminal.terminals import PrimitiveEdge as GrpcPrimitiveEdge
32
+ from ansys.edb.core.utility.rlc import Rlc as GrpcRlc
33
+ from ansys.edb.core.utility.value import Value as GrpcValue
34
+
35
+ from pyedb.generic.general_methods import generate_unique_name
36
+ from pyedb.grpc.database.layers.stackup_layer import StackupLayer
37
+ from pyedb.grpc.database.net.net import Net
38
+ from pyedb.grpc.database.ports.ports import BundleWavePort, WavePort
39
+ from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
40
+ from pyedb.grpc.database.primitive.primitive import Primitive
41
+ from pyedb.grpc.database.terminal.bundle_terminal import BundleTerminal
42
+ from pyedb.grpc.database.terminal.padstack_instance_terminal import (
43
+ PadstackInstanceTerminal,
44
+ )
45
+ from pyedb.grpc.database.terminal.pingroup_terminal import PinGroupTerminal
46
+ from pyedb.grpc.database.terminal.point_terminal import PointTerminal
47
+ from pyedb.grpc.database.utility.sources import Source, SourceType
48
+ from pyedb.modeler.geometry_operators import GeometryOperators
49
+
50
+
51
+ class SourceExcitation:
52
+ def __init__(self, pedb):
53
+ self._pedb = pedb
54
+
55
+ @property
56
+ def _logger(self):
57
+ return self._pedb.logger
58
+
59
+ @property
60
+ def excitations(self):
61
+ """Get all excitations."""
62
+ return self._pedb.excitations
63
+
64
+ @property
65
+ def sources(self):
66
+ """Get all sources."""
67
+ return self._pedb.sources
68
+
69
+ @property
70
+ def probes(self):
71
+ """Get all probes."""
72
+ return self._pedb.probes
73
+
74
+ def create_source_on_component(self, sources=None):
75
+ """Create voltage, current source, or resistor on component.
76
+
77
+ Parameters
78
+ ----------
79
+ sources : list[Source]
80
+ List of ``pyedb.grpc.utility.sources.Source`` objects.
81
+
82
+ Returns
83
+ -------
84
+ bool
85
+ ``True`` when successful, ``False`` when failed.
86
+
87
+ """
88
+
89
+ if not sources: # pragma: no cover
90
+ return False
91
+ if isinstance(sources, Source): # pragma: no cover
92
+ sources = [sources]
93
+ if isinstance(sources, list): # pragma: no cover
94
+ for src in sources:
95
+ if not isinstance(src, Source): # pragma: no cover
96
+ self._pedb.logger.error("List of source objects must be passed as an argument.")
97
+ return False
98
+ for source in sources:
99
+ positive_pins = self._pedb.padstack.get_instances(source.positive_node.component, source.positive_node.net)
100
+ negative_pins = self._pedb.padstack.get_instances(source.negative_node.component, source.negative_node.net)
101
+ positive_pin_group = self._pedb.components.create_pingroup_from_pins(positive_pins)
102
+ if not positive_pin_group: # pragma: no cover
103
+ return False
104
+ positive_pin_group = self._pedb.siwave.pin_groups[positive_pin_group.name]
105
+ negative_pin_group = self._pedb.components.create_pingroup_from_pins(negative_pins)
106
+ if not negative_pin_group: # pragma: no cover
107
+ return False
108
+ negative_pin_group = self._pedb.siwave.pin_groups[negative_pin_group.GetName()]
109
+ if source.source_type == SourceType.Vsource: # pragma: no cover
110
+ positive_pin_group_term = self._pedb.components._create_pin_group_terminal(
111
+ positive_pin_group,
112
+ )
113
+ negative_pin_group_term = self._pedb.components._create_pin_group_terminal(
114
+ negative_pin_group, isref=True
115
+ )
116
+ positive_pin_group_term.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
117
+ negative_pin_group_term.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
118
+ term_name = source.name
119
+ positive_pin_group_term.SetName(term_name)
120
+ negative_pin_group_term.SetName("{}_ref".format(term_name))
121
+ positive_pin_group_term.source_amplitude = GrpcValue(source.amplitude)
122
+ negative_pin_group_term.source_amplitude = GrpcValue(source.amplitude)
123
+ positive_pin_group_term.source_phase = GrpcValue(source.phase)
124
+ negative_pin_group_term.source_phase = GrpcValue(source.phase)
125
+ positive_pin_group_term.impedance = GrpcValue(source.impedance)
126
+ negative_pin_group_term.impedance = GrpcValue(source.impedance)
127
+ positive_pin_group_term.reference_terminal = negative_pin_group_term
128
+ elif source.source_type == SourceType.Isource: # pragma: no cover
129
+ positive_pin_group_term = self._pedb.components._create_pin_group_terminal(
130
+ positive_pin_group,
131
+ )
132
+ negative_pin_group_term = self._pedb.components._create_pin_group_terminal(
133
+ negative_pin_group, isref=True
134
+ )
135
+ positive_pin_group_term.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
136
+ negative_pin_group_term.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
137
+ positive_pin_group_term.name = source.name
138
+ negative_pin_group_term.name = "{}_ref".format(source.name)
139
+ positive_pin_group_term.source_amplitude = GrpcValue(source.amplitude)
140
+ negative_pin_group_term.source_amplitude = GrpcValue(source.amplitude)
141
+ positive_pin_group_term.source_phase = GrpcValue(source.phase)
142
+ negative_pin_group_term.source_phase = GrpcValue(source.phase)
143
+ positive_pin_group_term.impedance = GrpcValue(source.impedance)
144
+ negative_pin_group_term.impedance = GrpcValue(source.impedance)
145
+ positive_pin_group_term.reference_terminal = negative_pin_group_term
146
+ elif source.source_type == SourceType.Rlc: # pragma: no cover
147
+ self._pedb.components.create(
148
+ pins=[positive_pins[0], negative_pins[0]],
149
+ component_name=source.name,
150
+ is_rlc=True,
151
+ r_value=source.r_value,
152
+ l_value=source.l_value,
153
+ c_value=source.c_value,
154
+ )
155
+ return True
156
+
157
+ def create_port_on_pins(
158
+ self,
159
+ refdes,
160
+ pins,
161
+ reference_pins,
162
+ impedance=50.0,
163
+ port_name=None,
164
+ pec_boundary=False,
165
+ pingroup_on_single_pin=False,
166
+ ):
167
+ """Create circuit port between pins and reference ones.
168
+
169
+ Parameters
170
+ ----------
171
+ refdes : Component reference designator
172
+ str or EDBComponent object.
173
+ pins : pin name where the terminal has to be created. Single pin or several ones can be provided.If several
174
+ pins are provided a pin group will is created. Pin names can be the EDB name or the EDBPadstackInstance one.
175
+ For instance the pin called ``Pin1`` located on component ``U1``, ``U1-Pin1`` or ``Pin1`` can be provided and
176
+ will be handled.
177
+ str, [str], EDBPadstackInstance, [EDBPadstackInstance]
178
+ reference_pins : reference pin name used for terminal reference. Single pin or several ones can be provided.
179
+ If several pins are provided a pin group will is created. Pin names can be the EDB name or the
180
+ EDBPadstackInstance one. For instance the pin called ``Pin1`` located on component ``U1``, ``U1-Pin1``
181
+ or ``Pin1`` can be provided and will be handled.
182
+ str, [str], EDBPadstackInstance, [EDBPadstackInstance]
183
+ impedance : Port impedance
184
+ str, float
185
+ port_name : str, optional
186
+ Port name. The default is ``None``, in which case a name is automatically assigned.
187
+ pec_boundary : bool, optional
188
+ Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
189
+ a perfect short is created between the pin and impedance is ignored. This
190
+ parameter is only supported on a port created between two pins, such as
191
+ when there is no pin group.
192
+ pingroup_on_single_pin : bool
193
+ If ``True`` force using pingroup definition on single pin to have the port created at the pad center. If
194
+ ``False`` the port is created at the pad edge. Default value is ``False``.
195
+
196
+ Returns
197
+ -------
198
+ EDB terminal created, or False if failed to create.
199
+
200
+ Example:
201
+ >>> from pyedb import Edb
202
+ >>> edb = Edb(path_to_edb_file)
203
+ >>> pin = "AJ6"
204
+ >>> ref_pins = ["AM7", "AM4"]
205
+ Or to take all reference pins
206
+ >>> ref_pins = [pin for pin in list(edb.components["U2A5"].pins.values()) if pin.net_name == "GND"]
207
+ >>> edb.components.create_port_on_pins(refdes="U2A5", pins=pin, reference_pins=ref_pins)
208
+ >>> edb.save_edb()
209
+ >>> edb.close_edb()
210
+ """
211
+ from pyedb.grpc.database.components import Component
212
+
213
+ if isinstance(pins, str):
214
+ pins = [pins]
215
+ elif isinstance(pins, PadstackInstance):
216
+ pins = [pins.name]
217
+ if not reference_pins:
218
+ self._logger.error("No reference pin provided.")
219
+ return False
220
+ if isinstance(reference_pins, str):
221
+ reference_pins = [reference_pins]
222
+ elif isinstance(reference_pins, int):
223
+ reference_pins = [reference_pins]
224
+ elif isinstance(reference_pins, PadstackInstance):
225
+ reference_pins = [reference_pins]
226
+ if isinstance(reference_pins, list):
227
+ _temp = []
228
+ for ref_pin in reference_pins:
229
+ if isinstance(ref_pin, int):
230
+ pins = self._pedb.padstacks.instances
231
+ reference_pins = [pins[ref_pin] for ref_pin in reference_pins if ref_pin in pins]
232
+ # if reference_pins in pins:
233
+ # reference_pins = pins[reference_pins]
234
+ elif isinstance(ref_pin, str):
235
+ component_pins = self._pedb.components.instances[refdes].pins
236
+ if ref_pin in component_pins:
237
+ _temp.append(component_pins[ref_pin])
238
+ else:
239
+ p = [pp for pp in list(self._pedb.padstack.instances.values()) if pp.name == ref_pin]
240
+ if p:
241
+ _temp.extend(p)
242
+ elif isinstance(ref_pin, PadstackInstance):
243
+ _temp.append(ref_pin)
244
+ reference_pins = _temp
245
+ if isinstance(refdes, str):
246
+ refdes = self._pedb.components.instances[refdes]
247
+ elif isinstance(refdes, GrpcComponentGroup):
248
+ refdes = Component(self._pedb, refdes)
249
+ refdes_pins = refdes.pins
250
+ if any(refdes.rlc_values):
251
+ return self._pedb.components.deactivate_rlc_component(component=refdes, create_circuit_port=True)
252
+ if len([pin for pin in pins if isinstance(pin, str)]) == len(pins):
253
+ cmp_pins = []
254
+ for pin_name in pins:
255
+ cmp_pins = [pin for pin in list(refdes_pins.values()) if pin_name == pin.name]
256
+ if not cmp_pins:
257
+ for pin in list(refdes_pins.values()):
258
+ if pin.name and "-" in pin.name:
259
+ if pin_name == pin.name.split("-")[1]:
260
+ cmp_pins.append(pin)
261
+ if not cmp_pins:
262
+ self._logger.warning("No pin found during port creation. Port is not defined.")
263
+ return
264
+ pins = cmp_pins
265
+ if not len([pin for pin in pins if isinstance(pin, PadstackInstance)]) == len(pins):
266
+ self._logger.error("Pin list must contain only pins instances")
267
+ return False
268
+ if not port_name:
269
+ pin = pins[0]
270
+ if pin.net.is_null:
271
+ pin_net_name = "no_net"
272
+ else:
273
+ pin_net_name = pin.net.name
274
+ port_name = f"Port_{pin_net_name}_{refdes.name}_{pins[0].name}"
275
+
276
+ ref_cmp_pins = []
277
+ for ref_pin in reference_pins:
278
+ if ref_pin.name in refdes_pins:
279
+ ref_cmp_pins.append(ref_pin)
280
+ elif "-" in ref_pin.name:
281
+ if ref_pin.name.split("-")[1] in refdes_pins:
282
+ ref_cmp_pins.append(ref_pin)
283
+ elif "via" in ref_pin.name:
284
+ _ref_pin = [
285
+ pin for pin in list(self._pedb.padstacks.instances.values()) if pin.aedt_name == ref_pin.name
286
+ ]
287
+ if _ref_pin:
288
+ _ref_pin[0].is_layout_pin = True
289
+ ref_cmp_pins.append(_ref_pin[0])
290
+ if not ref_cmp_pins:
291
+ self._logger.error("No reference pins found.")
292
+ return False
293
+ reference_pins = ref_cmp_pins
294
+ if len(pins) > 1 or pingroup_on_single_pin:
295
+ pec_boundary = False
296
+ self._logger.info(
297
+ "Disabling PEC boundary creation, this feature is supported on single pin "
298
+ "ports only, {} pins found".format(len(pins))
299
+ )
300
+ group_name = "group_{}".format(port_name)
301
+ pin_group = self._pedb.components.create_pingroup_from_pins(pins, group_name)
302
+ term = self._create_pin_group_terminal(pingroup=pin_group, term_name=port_name)
303
+
304
+ else:
305
+ term = self._create_terminal(pins[0], term_name=port_name)
306
+ term.is_circuit_port = True
307
+ if len(reference_pins) > 1 or pingroup_on_single_pin:
308
+ pec_boundary = False
309
+ self._logger.info(
310
+ "Disabling PEC boundary creation. This feature is supported on single pin"
311
+ "ports only {} reference pins found.".format(len(reference_pins))
312
+ )
313
+ ref_group_name = "group_{}_ref".format(port_name)
314
+ ref_pin_group = self._pedb.components.create_pingroup_from_pins(reference_pins, ref_group_name)
315
+ ref_pin_group = self._pedb.siwave.pin_groups[ref_pin_group.name]
316
+ ref_term = self._create_pin_group_terminal(pingroup=ref_pin_group, term_name=port_name + "_ref")
317
+
318
+ else:
319
+ ref_term = self._create_terminal(reference_pins[0], term_name=port_name + "_ref")
320
+ ref_term.is_circuit_port = True
321
+ term.impedance = GrpcValue(impedance)
322
+ term.reference_terminal = ref_term
323
+ if pec_boundary:
324
+ term.is_circuit_port = False
325
+ ref_term.is_circuit_port = False
326
+ term.boundary_type = GrpcBoundaryType.PEC
327
+ ref_term.boundary_type = GrpcBoundaryType.PEC
328
+ self._logger.info(
329
+ "PEC boundary created between pin {} and reference pin {}".format(pins[0].name, reference_pins[0].name)
330
+ )
331
+ if term:
332
+ return term
333
+ return False
334
+
335
+ def create_port_on_component(
336
+ self,
337
+ component,
338
+ net_list,
339
+ port_type=SourceType.CoaxPort,
340
+ do_pingroup=True,
341
+ reference_net="gnd",
342
+ port_name=None,
343
+ solder_balls_height=None,
344
+ solder_balls_size=None,
345
+ solder_balls_mid_size=None,
346
+ extend_reference_pins_outside_component=False,
347
+ ):
348
+ """Create ports on a component.
349
+
350
+ Parameters
351
+ ----------
352
+ component : str or self._pedb.component
353
+ EDB component or str component name.
354
+ net_list : str or list of string.
355
+ List of nets where ports must be created on the component.
356
+ If the net is not part of the component, this parameter is skipped.
357
+ port_type : str, optional
358
+ Type of port to create. ``coax_port`` generates solder balls.
359
+ ``circuit_port`` generates circuit ports on pins belonging to the net list.
360
+ do_pingroup : bool
361
+ True activate pingroup during port creation (only used with combination of CircPort),
362
+ False will take the closest reference pin and generate one port per signal pin.
363
+ refnet : string or list of string.
364
+ list of the reference net.
365
+ port_name : str
366
+ Port name for overwriting the default port-naming convention,
367
+ which is ``[component][net][pin]``. The port name must be unique.
368
+ If a port with the specified name already exists, the
369
+ default naming convention is used so that port creation does
370
+ not fail.
371
+ solder_balls_height : float, optional
372
+ Solder balls height used for the component. When provided default value is overwritten and must be
373
+ provided in meter.
374
+ solder_balls_size : float, optional
375
+ Solder balls diameter. When provided auto evaluation based on padstack size will be disabled.
376
+ solder_balls_mid_size : float, optional
377
+ Solder balls mid-diameter. When provided if value is different than solder balls size, spheroid shape will
378
+ be switched.
379
+ extend_reference_pins_outside_component : bool
380
+ When no reference pins are found on the component extend the pins search with taking the closest one. If
381
+ `do_pingroup` is `True` will be set to `False`. Default value is `False`.
382
+
383
+ Returns
384
+ -------
385
+ double, bool
386
+ Salder ball height vale, ``False`` when failed.
387
+
388
+ Examples
389
+ --------
390
+
391
+ >>> from pyedb import Edb
392
+ >>> edbapp = Edb("myaedbfolder")
393
+ >>> net_list = ["M_DQ<1>", "M_DQ<2>", "M_DQ<3>", "M_DQ<4>", "M_DQ<5>"]
394
+ >>> edbapp.components.create_port_on_component(cmp="U2A5", net_list=net_list,
395
+ >>> port_type=SourceType.CoaxPort, do_pingroup=False, refnet="GND")
396
+
397
+ """
398
+ if isinstance(component, str):
399
+ component = self._pedb.components.instances[component]
400
+ if not isinstance(net_list, list):
401
+ net_list = [net_list]
402
+ for net in net_list:
403
+ if not isinstance(net, str):
404
+ try:
405
+ net_name = net.name
406
+ if net_name:
407
+ net_list.append(net_name)
408
+ except:
409
+ pass
410
+ if reference_net in net_list:
411
+ net_list.remove(reference_net)
412
+ cmp_pins = [p for p in list(component.pins.values()) if p.net_name in net_list]
413
+ for p in cmp_pins: # pragma no cover
414
+ p.is_layout_pin = True
415
+ if len(cmp_pins) == 0:
416
+ self._logger.info(f"No pins found on component {component.name}, searching padstack instances instead")
417
+ return False
418
+ pin_layers = cmp_pins[0].padstack_def.data.layer_names
419
+ if port_type == "coax_port":
420
+ if not solder_balls_height:
421
+ solder_balls_height = self._pedb.components.instances[component.name].solder_ball_height
422
+ if not solder_balls_size:
423
+ solder_balls_size = self._pedb.components.instances[component.name].solder_ball_diameter[0]
424
+ if not solder_balls_mid_size:
425
+ solder_balls_mid_size = self._pedb.components.instances[component.name].solder_ball_diameter[1]
426
+ ref_pins = [p for p in list(component.pins.values()) if p.net_name in reference_net]
427
+ if not ref_pins:
428
+ self._logger.error(
429
+ "No reference pins found on component. You might consider"
430
+ "using Circuit port instead since reference pins can be extended"
431
+ "outside the component when not found if argument extend_reference_pins_outside_component is True."
432
+ )
433
+ return False
434
+ pad_params = self._pedb.padstack.get_pad_parameters(pin=cmp_pins[0], layername=pin_layers[0], pad_type=0)
435
+ if not pad_params[0] == 7:
436
+ if not solder_balls_size: # pragma no cover
437
+ sball_diam = min([GrpcValue(val).value for val in pad_params[1]])
438
+ sball_mid_diam = sball_diam
439
+ else: # pragma no cover
440
+ sball_diam = solder_balls_size
441
+ if solder_balls_mid_size:
442
+ sball_mid_diam = solder_balls_mid_size
443
+ else:
444
+ sball_mid_diam = solder_balls_size
445
+ if not solder_balls_height: # pragma no cover
446
+ solder_balls_height = 2 * sball_diam / 3
447
+ else: # pragma no cover
448
+ if not solder_balls_size:
449
+ bbox = pad_params[1]
450
+ sball_diam = min([abs(bbox[2] - bbox[0]), abs(bbox[3] - bbox[1])]) * 0.8
451
+ else:
452
+ sball_diam = solder_balls_size
453
+ if not solder_balls_height:
454
+ solder_balls_height = 2 * sball_diam / 3
455
+ if solder_balls_mid_size:
456
+ sball_mid_diam = solder_balls_mid_size
457
+ else:
458
+ sball_mid_diam = sball_diam
459
+ sball_shape = "Cylinder"
460
+ if not sball_diam == sball_mid_diam:
461
+ sball_shape = "Spheroid"
462
+ self._pedb.components.set_solder_ball(
463
+ component=component,
464
+ sball_height=solder_balls_height,
465
+ sball_diam=sball_diam,
466
+ sball_mid_diam=sball_mid_diam,
467
+ shape=sball_shape,
468
+ )
469
+ for pin in cmp_pins:
470
+ self._pedb.padstack.create_coax_port(padstackinstance=pin, name=port_name)
471
+
472
+ elif port_type == "circuit_port": # pragma no cover
473
+ ref_pins = [p for p in list(component.pins.values()) if p.net_name in reference_net]
474
+ for p in ref_pins:
475
+ p.is_layout_pin = True
476
+ if not ref_pins:
477
+ self._logger.warning("No reference pins found on component")
478
+ if not extend_reference_pins_outside_component:
479
+ self._logger.warning(
480
+ "argument extend_reference_pins_outside_component is False. You might want "
481
+ "setting to True to extend the reference pin search outside the component"
482
+ )
483
+ else:
484
+ do_pingroup = False
485
+ if do_pingroup:
486
+ if len(ref_pins) == 1:
487
+ ref_pins.is_pin = True
488
+ ref_pin_group_term = self._create_terminal(ref_pins[0])
489
+ else:
490
+ for pin in ref_pins:
491
+ pin.is_pin = True
492
+ ref_pin_group = self._pedb.components.create_pingroup_from_pins(ref_pins)
493
+ if ref_pin_group.is_null:
494
+ self._logger.error(f"Failed to create reference pin group on component {component.GetName()}.")
495
+ return False
496
+ ref_pin_group_term = self._create_pin_group_terminal(ref_pin_group, isref=False)
497
+ if not ref_pin_group_term:
498
+ self._logger.error(
499
+ f"Failed to create reference pin group terminal on component {component.GetName()}"
500
+ )
501
+ return False
502
+ for net in net_list:
503
+ pins = [pin for pin in list(component.pins.values()) if pin.net_name == net]
504
+ if pins:
505
+ if len(pins) == 1:
506
+ pin_term = self._create_terminal(pins[0])
507
+ if pin_term:
508
+ pin_term.reference_terminal = ref_pin_group_term
509
+ else:
510
+ pin_group = self._pedb.components.create_pingroup_from_pins(pins)
511
+ if pin_group.is_null:
512
+ self._logger.error(
513
+ f"Failed to create pin group terminal on component {component.GetName()}"
514
+ )
515
+ return False
516
+ pin_group_term = self._create_pin_group_terminal(pin_group)
517
+ if pin_group_term:
518
+ pin_group_term.reference_terminal = ref_pin_group_term
519
+ else:
520
+ self._logger.info("No pins found on component {} for the net {}".format(component, net))
521
+ else:
522
+ for net in net_list:
523
+ pins = [pin for pin in list(component.pins.values()) if pin.net_name == net]
524
+ for pin in pins:
525
+ if ref_pins:
526
+ self.create_port_on_pins(component, pin, ref_pins)
527
+ else:
528
+ if extend_reference_pins_outside_component:
529
+ _pin = PadstackInstance(self._pedb, pin)
530
+ ref_pin = _pin.get_reference_pins(
531
+ reference_net=reference_net[0],
532
+ max_limit=1,
533
+ component_only=False,
534
+ search_radius=3e-3,
535
+ )
536
+ if ref_pin:
537
+ if not isinstance(ref_pin, list):
538
+ ref_pin = [ref_pin]
539
+ self.create_port_on_pins(component, [pin.name], ref_pin[0])
540
+ else:
541
+ self._logger.error("Skipping port creation no reference pin found.")
542
+ return True
543
+
544
+ def _create_terminal(self, pin, term_name=None):
545
+ """Create terminal on component pin.
546
+
547
+ Parameters
548
+ ----------
549
+ pin : Edb padstack instance.
550
+
551
+ term_name : Terminal name (Optional).
552
+ str.
553
+
554
+ Returns
555
+ -------
556
+ EDB terminal.
557
+ """
558
+
559
+ from_layer, _ = pin.get_layer_range()
560
+ if term_name is None:
561
+ term_name = "{}.{}.{}".format(pin.component.name, pin.name, pin.net.name)
562
+ for term in list(self._pedb.active_layout.terminals):
563
+ if term.name == term_name:
564
+ return term
565
+ return PadstackInstanceTerminal.create(
566
+ layout=self._pedb.layout, name=term_name, padstack_instance=pin, layer=from_layer, net=pin.net, is_ref=False
567
+ )
568
+
569
+ def add_port_on_rlc_component(self, component=None, circuit_ports=True, pec_boundary=False):
570
+ """Deactivate RLC component and replace it with a circuit port.
571
+ The circuit port supports only two-pin components.
572
+
573
+ Parameters
574
+ ----------
575
+ component : str
576
+ Reference designator of the RLC component.
577
+
578
+ circuit_ports : bool
579
+ ``True`` will replace RLC component by circuit ports, ``False`` gap ports compatible with HFSS 3D modeler
580
+ export.
581
+
582
+ pec_boundary : bool, optional
583
+ Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
584
+ a perfect short is created between the pin and impedance is ignored. This
585
+ parameter is only supported on a port created between two pins, such as
586
+ when there is no pin group.
587
+
588
+ Returns
589
+ -------
590
+ bool
591
+ ``True`` when successful, ``False`` when failed.
592
+ """
593
+ from pyedb.grpc.database.components import Component
594
+
595
+ if isinstance(component, str):
596
+ component = self._pedb.components.instances[component]
597
+ if not isinstance(component, Component): # pragma: no cover
598
+ return False
599
+ self._pedb.components.set_component_rlc(component.refdes)
600
+ pins = list(self._pedb.components.instances[component.refdes].pins.values())
601
+ if len(pins) == 2:
602
+ pin_layers = pins[0].get_layer_range()
603
+ pos_pin_term = PadstackInstanceTerminal.create(
604
+ layout=self._pedb.active_layout,
605
+ name=f"{component.name}_{pins[0].name}",
606
+ padstack_instance=pins[0],
607
+ layer=pin_layers[0],
608
+ net=pins[0].net,
609
+ is_ref=False,
610
+ )
611
+ if not pos_pin_term: # pragma: no cover
612
+ return False
613
+ neg_pin_term = PadstackInstanceTerminal.create(
614
+ layout=self._pedb.active_layout,
615
+ name="{}_{}_ref".format(component.name, pins[1].name),
616
+ padstack_instance=pins[1],
617
+ layer=pin_layers[0],
618
+ net=pins[1].net,
619
+ is_ref=False,
620
+ )
621
+ if not neg_pin_term: # pragma: no cover
622
+ return False
623
+ if pec_boundary:
624
+ pos_pin_term.boundary_type = GrpcBoundaryType.PEC
625
+ neg_pin_term.boundary_type = GrpcBoundaryType.PEC
626
+ else:
627
+ pos_pin_term.boundary_type = GrpcBoundaryType.PORT
628
+ neg_pin_term.boundary_type = GrpcBoundaryType.PORT
629
+ pos_pin_term.name = component.name
630
+ pos_pin_term.reference_terminal = neg_pin_term
631
+ if circuit_ports and not pec_boundary:
632
+ pos_pin_term.is_circuit_port = True
633
+ neg_pin_term.is_circuit_port = True
634
+ elif pec_boundary:
635
+ pos_pin_term.is_circuit_port = False
636
+ neg_pin_term.is_circuit_port = False
637
+ else:
638
+ pos_pin_term.is_circuit_port = False
639
+ neg_pin_term.is_circuit_port = False
640
+ self._logger.info(f"Component {component.refdes} has been replaced by port")
641
+ return True
642
+ return False
643
+
644
+ def add_rlc_boundary(self, component=None, circuit_type=True):
645
+ """Add RLC gap boundary on component and replace it with a circuit port.
646
+ The circuit port supports only 2-pin components.
647
+
648
+ Parameters
649
+ ----------
650
+ component : str
651
+ Reference designator of the RLC component.
652
+ circuit_type : bool
653
+ When ``True`` circuit type are defined, if ``False`` gap type will be used instead (compatible with HFSS 3D
654
+ modeler). Default value is ``True``.
655
+
656
+ Returns
657
+ -------
658
+ bool
659
+ ``True`` when successful, ``False`` when failed.
660
+ """
661
+ from pyedb.grpc.database.components import Component
662
+
663
+ if isinstance(component, str): # pragma: no cover
664
+ component = self._pedb.components.instances[component]
665
+ if not isinstance(component, Component): # pragma: no cover
666
+ return False
667
+ self._pedb.components.set_component_rlc(component.name)
668
+ pins = list(component.pins.values())
669
+ if len(pins) == 2: # pragma: no cover
670
+ pin_layer = pins[0].get_layer_range()[0]
671
+ pos_pin_term = PadstackInstanceTerminal.create(
672
+ layout=self._pedb.active_layout,
673
+ net=pins[0].net,
674
+ name=f"{component.name}_{pins[0].name}",
675
+ padstack_instance=pins[0],
676
+ layer=pin_layer,
677
+ is_ref=False,
678
+ )
679
+ if not pos_pin_term: # pragma: no cover
680
+ return False
681
+ neg_pin_term = PadstackInstanceTerminal.create(
682
+ layout=self._pedb.active_layout,
683
+ net=pins[1].net,
684
+ name="{}_{}_ref".format(component.name, pins[1].name),
685
+ padstack_instance=pins[1],
686
+ layer=pin_layer,
687
+ is_ref=True,
688
+ )
689
+ if not neg_pin_term: # pragma: no cover
690
+ return False
691
+ pos_pin_term.boundary_type = GrpcBoundaryType.RLC
692
+ if not circuit_type:
693
+ pos_pin_term.is_circuit_port = False
694
+ else:
695
+ pos_pin_term.is_circuit_port = True
696
+ pos_pin_term.name = component.name
697
+ neg_pin_term.boundary_type = GrpcBoundaryType.RLC
698
+ if not circuit_type:
699
+ neg_pin_term.is_circuit_port = False
700
+ else:
701
+ neg_pin_term.is_circuit_port = True
702
+ pos_pin_term.reference_terminal = neg_pin_term
703
+ rlc_values = component.rlc_values
704
+ rlc = GrpcRlc()
705
+ if rlc_values[0]:
706
+ rlc.r_enabled = True
707
+ rlc.r = GrpcValue(rlc_values[0])
708
+ if rlc_values[1]:
709
+ rlc.l_enabled = True
710
+ rlc.l = GrpcValue(rlc_values[1])
711
+ if rlc_values[2]:
712
+ rlc.c_enabled = True
713
+ rlc.c = GrpcValue(rlc_values[2])
714
+ rlc.is_parallel = component.is_parallel_rlc
715
+ pos_pin_term.rlc_boundary = rlc
716
+ self._logger.info("Component {} has been replaced by port".format(component.refdes))
717
+ return True
718
+
719
+ def _create_pin_group_terminal(self, pingroup, isref=False, term_name=None, term_type="circuit"):
720
+ """Creates an EDB pin group terminal from a given EDB pin group.
721
+
722
+ Parameters
723
+ ----------
724
+ pingroup : Pin group.
725
+
726
+ isref : bool
727
+ Specify if this terminal a reference terminal.
728
+
729
+ term_name : Terminal name (Optional). If not provided default name is Component name, Pin name, Net name.
730
+ str.
731
+
732
+ term_type: Type of terminal, gap, circuit or auto.
733
+ str.
734
+ Returns
735
+ -------
736
+ Edb pin group terminal.
737
+ """
738
+ if pingroup.is_null:
739
+ self._logger.error(f"{pingroup} is null")
740
+ pin = PadstackInstance(self._pedb, pingroup.pins[0])
741
+ if term_name is None:
742
+ term_name = f"{pin.component.name}.{pin.name}.{pin.net_name}"
743
+ for t in self._pedb.active_layout.terminals:
744
+ if t.name == term_name:
745
+ self._logger.warning(
746
+ f"Terminal {term_name} already created in current layout. Returning the "
747
+ f"already defined one. Make sure to delete the terminal before to create a new one."
748
+ )
749
+ return t
750
+ pingroup_term = PinGroupTerminal.create(
751
+ layout=self._pedb.active_layout, name=term_name, net=pingroup.net, pin_group=pingroup, is_ref=isref
752
+ )
753
+ if term_type == "circuit" or "auto":
754
+ pingroup_term.is_circuit_port = True
755
+ return pingroup_term
756
+
757
+ def create_coax_port(self, padstackinstance, use_dot_separator=True, name=None, create_on_top=True):
758
+ """Create HFSS 3Dlayout coaxial lumped port on a pastack
759
+ Requires to have solder ball defined before calling this method.
760
+
761
+ Parameters
762
+ ----------
763
+ padstackinstance : `Edb.Cell.Primitive.PadstackInstance` or int
764
+ Padstack instance object.
765
+ use_dot_separator : bool, optional
766
+ Whether to use ``.`` as the separator for the naming convention, which
767
+ is ``[component][net][pin]``. The default is ``True``. If ``False``, ``_`` is
768
+ used as the separator instead.
769
+ name : str
770
+ Port name for overwriting the default port-naming convention,
771
+ which is ``[component][net][pin]``. The port name must be unique.
772
+ If a port with the specified name already exists, the
773
+ default naming convention is used so that port creation does
774
+ not fail.
775
+
776
+ Returns
777
+ -------
778
+ str
779
+ Terminal name.
780
+
781
+ """
782
+ if isinstance(padstackinstance, int):
783
+ padstackinstance = self._pedb.padstacks.instances[padstackinstance]
784
+ cmp_name = padstackinstance.component.name
785
+ if cmp_name == "":
786
+ cmp_name = "no_comp"
787
+ net_name = padstackinstance.net.name
788
+ if net_name == "":
789
+ net_name = "no_net"
790
+ pin_name = padstackinstance.name
791
+ if pin_name == "":
792
+ pin_name = "no_pin_name"
793
+ if use_dot_separator:
794
+ port_name = "{0}.{1}.{2}".format(cmp_name, pin_name, net_name)
795
+ else:
796
+ port_name = "{0}_{1}_{2}".format(cmp_name, pin_name, net_name)
797
+ padstackinstance.is_layout_pin = True
798
+ layer_range = padstackinstance.get_layer_range()
799
+ if create_on_top:
800
+ terminal_layer = layer_range[0]
801
+ else:
802
+ terminal_layer = layer_range[1]
803
+ if name:
804
+ port_name = name
805
+ if self._port_exist(port_name):
806
+ port_name = generate_unique_name(port_name, n=2)
807
+ self._logger.info("An existing port already has this same name. Renaming to {}.".format(port_name))
808
+ PadstackInstanceTerminal.create(
809
+ layout=self._pedb._active_layout,
810
+ name=port_name,
811
+ padstack_instance=padstackinstance,
812
+ layer=terminal_layer,
813
+ net=padstackinstance.net,
814
+ is_ref=False,
815
+ )
816
+ return port_name
817
+
818
+ def _port_exist(self, port_name):
819
+ return any(port for port in list(self._pedb.excitations.keys()) if port == port_name)
820
+
821
+ def _create_edge_terminal(self, prim_id, point_on_edge, terminal_name=None, is_ref=False):
822
+ """Create an edge terminal.
823
+
824
+ Parameters
825
+ ----------
826
+ prim_id : int
827
+ Primitive ID.
828
+ point_on_edge : list
829
+ Coordinate of the point to define the edge terminal.
830
+ The point must be on the target edge but not on the two
831
+ ends of the edge.
832
+ terminal_name : str, optional
833
+ Name of the terminal. The default is ``None``, in which case the
834
+ default name is assigned.
835
+ is_ref : bool, optional
836
+ Whether it is a reference terminal. The default is ``False``.
837
+
838
+ Returns
839
+ -------
840
+ Edb.Cell.Terminal.EdgeTerminal
841
+ """
842
+ if not terminal_name:
843
+ terminal_name = generate_unique_name("Terminal_")
844
+ if isinstance(point_on_edge, tuple):
845
+ point_on_edge = GrpcPointData(point_on_edge)
846
+ prim = [i for i in self._pedb.modeler.primitives if i.id == prim_id]
847
+ if not prim:
848
+ self._pedb.logger.error(f"No primitive found for ID {prim_id}")
849
+ return False
850
+ prim = prim[0]
851
+ pos_edge = [GrpcPrimitiveEdge.create(prim, point_on_edge)]
852
+ return GrpcEdgeTerminal.create(
853
+ layout=prim.layout, name=terminal_name, edges=pos_edge, net=prim.net, is_ref=is_ref
854
+ )
855
+
856
+ def create_circuit_port_on_pin(self, pos_pin, neg_pin, impedance=50, port_name=None):
857
+ """Create a circuit port on a pin.
858
+
859
+ Parameters
860
+ ----------
861
+ pos_pin : Object
862
+ Edb Pin
863
+ neg_pin : Object
864
+ Edb Pin
865
+ impedance : float
866
+ Port Impedance
867
+ port_name : str, optional
868
+ Port Name
869
+
870
+ Returns
871
+ -------
872
+ str
873
+ Port Name.
874
+
875
+ Examples
876
+ --------
877
+
878
+ >>> from pyedb import Edb
879
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
880
+ >>> pins = edbapp.components.get_pin_from_component("U2A5")
881
+ >>> edbapp.siwave.create_circuit_port_on_pin(pins[0], pins[1], 50, "port_name")
882
+ """
883
+ if not port_name:
884
+ port_name = f"Port_{pos_pin.component.name}_{pos_pin.net_name}_{neg_pin.component.name}_{neg_pin.net_name}"
885
+ return self._create_terminal_on_pins(
886
+ positive_pin=pos_pin, negative_pin=neg_pin, impedance=impedance, name=port_name
887
+ )
888
+
889
+ def _create_terminal_on_pins(
890
+ self,
891
+ positive_pin,
892
+ negative_pin,
893
+ name=None,
894
+ use_pin_top_layer=True,
895
+ source_type="circuit_port",
896
+ impedance=50,
897
+ magnitude=0,
898
+ phase=0,
899
+ r=0,
900
+ l=0,
901
+ c=0,
902
+ ):
903
+ """Create a terminal on pins.
904
+
905
+ Parameters
906
+ ----------
907
+ positive_pin : :class: `PadstackInstance`
908
+ Positive padstack instance.
909
+ negative_pin : :class: `PadstackInstance`
910
+ Negative padstack instance.
911
+ name : str, optional
912
+ terminal name
913
+ use_pin_top_layer : bool, optional
914
+ Use :class: `PadstackInstance` top layer or bottom for terminal assignment.
915
+ source_type : str, optional
916
+ Specify the source type created. Supported values: `"circuit_port"`, `"lumped_port"`, `"current_source"`,
917
+ `"voltage_port"`, `"rlc"`.
918
+ impedance : float, int or str, optional
919
+ Terminal impedance value
920
+ magnitude : float, int or str, optional
921
+ Terminal magnitude.
922
+ phase : float, int or str, optional
923
+ Terminal phase
924
+ r : float, int
925
+ Resistor value
926
+ l : float, int
927
+ Inductor value
928
+ c : float, int
929
+ Capacitor value
930
+ """
931
+
932
+ top_layer_pos, bottom_layer_pos = positive_pin.get_layer_range()
933
+ top_layer_neg, bottom_layer_neg = negative_pin.get_layer_range()
934
+ pos_term_layer = bottom_layer_pos
935
+ neg_term_layer = bottom_layer_neg
936
+ if use_pin_top_layer:
937
+ pos_term_layer = top_layer_pos
938
+ neg_term_layer = top_layer_neg
939
+ if not name:
940
+ name = positive_pin.name
941
+ pos_terminal = PadstackInstanceTerminal.create(
942
+ layout=self._pedb.active_layout,
943
+ padstack_instance=positive_pin,
944
+ name=name,
945
+ layer=pos_term_layer,
946
+ is_ref=False,
947
+ net=positive_pin.net,
948
+ )
949
+
950
+ neg_terminal = PadstackInstanceTerminal.create(
951
+ layout=self._pedb.active_layout,
952
+ padstack_instance=negative_pin,
953
+ name=negative_pin.name,
954
+ layer=neg_term_layer,
955
+ is_ref=False,
956
+ net=negative_pin.net,
957
+ )
958
+ if source_type in ["circuit_port", "lumped_port"]:
959
+ pos_terminal.boundary_type = GrpcBoundaryType.PORT
960
+ neg_terminal.boundary_type = GrpcBoundaryType.PORT
961
+ pos_terminal.impedance = GrpcValue(impedance)
962
+ if source_type == "lumped_port":
963
+ pos_terminal.is_circuit_port = False
964
+ neg_terminal.is_circuit_port = False
965
+ else:
966
+ pos_terminal.is_circuit_port = True
967
+ neg_terminal.is_circuit_port = True
968
+ pos_terminal.reference_terminal = neg_terminal
969
+ pos_terminal.name = name
970
+
971
+ elif source_type == "current_source":
972
+ pos_terminal.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
973
+ neg_terminal.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
974
+ pos_terminal.source_amplitude = GrpcValue(magnitude)
975
+ pos_terminal.source_phase = GrpcValue(phase)
976
+ pos_terminal.impedance = GrpcValue(impedance)
977
+ pos_terminal.reference_terminal = neg_terminal
978
+ pos_terminal.name = name
979
+
980
+ elif source_type == "voltage_source":
981
+ pos_terminal.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
982
+ neg_terminal.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
983
+ pos_terminal.source_amplitude = GrpcValue(magnitude)
984
+ pos_terminal.impedance = GrpcValue(impedance)
985
+ pos_terminal.source_phase = GrpcValue(phase)
986
+ pos_terminal.reference_terminal = neg_terminal
987
+ pos_terminal.name = name
988
+
989
+ elif source_type == "rlc":
990
+ pos_terminal.boundary_type = GrpcBoundaryType.RLC
991
+ neg_terminal.boundary_type = GrpcBoundaryType.RLC
992
+ pos_terminal.reference_terminal = neg_terminal
993
+ rlc = GrpcRlc()
994
+ rlc.r_enabled = bool(r)
995
+ rlc.l_enabled = bool(l)
996
+ rlc.c_enabled = bool(c)
997
+ rlc.r = GrpcValue(r)
998
+ rlc.l = GrpcValue(l)
999
+ rlc.c = GrpcValue(c)
1000
+ pos_terminal.rlc_boundary_parameters = rlc
1001
+ pos_terminal.name = name
1002
+
1003
+ else:
1004
+ self._pedb.logger.error("No valid source type specified.")
1005
+ return False
1006
+ return pos_terminal.name
1007
+
1008
+ def create_voltage_source_on_pin(self, pos_pin, neg_pin, voltage_value=0, phase_value=0, source_name=None):
1009
+ """Create a voltage source.
1010
+
1011
+ Parameters
1012
+ ----------
1013
+ pos_pin : Object
1014
+ Positive Pin.
1015
+ neg_pin : Object
1016
+ Negative Pin.
1017
+ voltage_value : float, optional
1018
+ Value for the voltage. The default is ``3.3``.
1019
+ phase_value : optional
1020
+ Value for the phase. The default is ``0``.
1021
+ source_name : str, optional
1022
+ Name of the source. The default is ``""``.
1023
+
1024
+ Returns
1025
+ -------
1026
+ str
1027
+ Source Name.
1028
+
1029
+ Examples
1030
+ --------
1031
+
1032
+ >>> from pyedb import Edb
1033
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
1034
+ >>> pins = edbapp.components.get_pin_from_component("U2A5")
1035
+ >>> edbapp.excitations.create_voltage_source_on_pin(pins[0], pins[1], 50, "source_name")
1036
+ """
1037
+
1038
+ if not source_name:
1039
+ source_name = (
1040
+ f"VSource_{pos_pin.component.name}_{pos_pin.net_name}_{neg_pin.component.name}_{neg_pin.net_name}"
1041
+ )
1042
+ return self._create_terminal_on_pins(
1043
+ positive_pin=pos_pin,
1044
+ negative_pin=neg_pin,
1045
+ name=source_name,
1046
+ magnitude=voltage_value,
1047
+ phase=phase_value,
1048
+ source_type="voltage_source",
1049
+ )
1050
+
1051
+ def create_current_source_on_pin(self, pos_pin, neg_pin, current_value=0, phase_value=0, source_name=None):
1052
+ """Create a voltage source.
1053
+
1054
+ Parameters
1055
+ ----------
1056
+ pos_pin : Object
1057
+ Positive Pin.
1058
+ neg_pin : Object
1059
+ Negative Pin.
1060
+ voltage_value : float, optional
1061
+ Value for the voltage. The default is ``3.3``.
1062
+ phase_value : optional
1063
+ Value for the phase. The default is ``0``.
1064
+ source_name : str, optional
1065
+ Name of the source. The default is ``""``.
1066
+
1067
+ Returns
1068
+ -------
1069
+ str
1070
+ Source Name.
1071
+
1072
+ Examples
1073
+ --------
1074
+
1075
+ >>> from pyedb import Edb
1076
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
1077
+ >>> pins = edbapp.components.get_pin_from_component("U2A5")
1078
+ >>> edbapp.excitations.create_voltage_source_on_pin(pins[0], pins[1], 50, "source_name")
1079
+ """
1080
+
1081
+ if not source_name:
1082
+ source_name = (
1083
+ f"VSource_{pos_pin.component.name}_{pos_pin.net_name}_{neg_pin.component.name}_{neg_pin.net_name}"
1084
+ )
1085
+ return self._create_terminal_on_pins(
1086
+ positive_pin=pos_pin,
1087
+ negative_pin=neg_pin,
1088
+ name=source_name,
1089
+ magnitude=current_value,
1090
+ phase=phase_value,
1091
+ source_type="current_source",
1092
+ )
1093
+
1094
+ def create_resistor_on_pin(self, pos_pin, neg_pin, rvalue=1, resistor_name=""):
1095
+ """Create a Resistor boundary between two given pins..
1096
+
1097
+ Parameters
1098
+ ----------
1099
+ pos_pin : Object
1100
+ Positive Pin.
1101
+ neg_pin : Object
1102
+ Negative Pin.
1103
+ rvalue : float, optional
1104
+ Resistance value. The default is ``1``.
1105
+ resistor_name : str, optional
1106
+ Name of the resistor. The default is ``""``.
1107
+
1108
+ Returns
1109
+ -------
1110
+ str
1111
+ Name of the resistor.
1112
+
1113
+ Examples
1114
+ --------
1115
+
1116
+ >>> from pyedb import Edb
1117
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
1118
+ >>> pins =edbapp.components.get_pin_from_component("U2A5")
1119
+ >>> edbapp.excitation.create_resistor_on_pin(pins[0], pins[1],50,"res_name")
1120
+ """
1121
+ if not resistor_name:
1122
+ resistor_name = (
1123
+ f"Res_{pos_pin.component.name}_{pos_pin.net.name}_{neg_pin.component.name}_{neg_pin.net.name}"
1124
+ )
1125
+ return self._create_terminal_on_pins(
1126
+ positive_pin=pos_pin, negative_pin=neg_pin, name=resistor_name, source_type="rlc", r=rvalue
1127
+ )
1128
+
1129
+ def create_circuit_port_on_net(
1130
+ self,
1131
+ positive_component_name,
1132
+ positive_net_name,
1133
+ negative_component_name=None,
1134
+ negative_net_name=None,
1135
+ impedance=50,
1136
+ port_name="",
1137
+ ):
1138
+ """Create a circuit port on a NET.
1139
+
1140
+ It groups all pins belonging to the specified net and then applies the port on PinGroups.
1141
+
1142
+ Parameters
1143
+ ----------
1144
+ positive_component_name : str
1145
+ Name of the positive component.
1146
+ positive_net_name : str
1147
+ Name of the positive net.
1148
+ negative_component_name : str, optional
1149
+ Name of the negative component. The default is ``None``, in which case the name of
1150
+ the positive net is assigned.
1151
+ negative_net_name : str, optional
1152
+ Name of the negative net name. The default is ``None`` which will look for GND Nets.
1153
+ impedance_value : float, optional
1154
+ Port impedance value. The default is ``50``.
1155
+ port_name : str, optional
1156
+ Name of the port. The default is ``""``.
1157
+
1158
+ Returns
1159
+ -------
1160
+ str
1161
+ The name of the port.
1162
+
1163
+ Examples
1164
+ --------
1165
+
1166
+ >>> from pyedb import Edb
1167
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
1168
+ >>> edbapp.excitations.create_circuit_port_on_net("U2A5", "V1P5_S3", "U2A5", "GND", 50, "port_name")
1169
+ """
1170
+ if not negative_component_name:
1171
+ negative_component_name = positive_component_name
1172
+ if not negative_net_name:
1173
+ negative_net_name = self._check_gnd(negative_component_name)
1174
+ if not port_name:
1175
+ port_name = (
1176
+ f"Port_{positive_component_name}_{positive_net_name}_{negative_component_name}_{negative_net_name}"
1177
+ )
1178
+ positive_pins = []
1179
+ for pin in list(self._pedb.components.instances[positive_component_name].pins.values()):
1180
+ if pin and not pin.net.is_null:
1181
+ if pin.net_name == positive_net_name:
1182
+ positive_pins.append(pin)
1183
+ if not positive_pins:
1184
+ self._pedb.logger.error(
1185
+ f"No positive pins found component {positive_component_name} net {positive_net_name}"
1186
+ )
1187
+ return False
1188
+ negative_pins = []
1189
+ for pin in list(self._pedb.components.instances[negative_component_name].pins.values()):
1190
+ if pin and not pin.net.is_null:
1191
+ if pin.net_name == negative_net_name:
1192
+ negative_pins.append(pin)
1193
+ if not negative_pins:
1194
+ self._pedb.logger.error(
1195
+ f"No negative pins found component {negative_component_name} net {negative_net_name}"
1196
+ )
1197
+ return False
1198
+
1199
+ return self.create_pin_group_terminal(
1200
+ positive_pins=positive_pins,
1201
+ negatives_pins=negative_pins,
1202
+ name=port_name,
1203
+ impedance=impedance,
1204
+ source_type="circuit_port",
1205
+ )
1206
+
1207
+ def create_pin_group_terminal(
1208
+ self,
1209
+ positive_pins,
1210
+ negatives_pins,
1211
+ name=None,
1212
+ impedance=50,
1213
+ source_type="circuit_port",
1214
+ magnitude=1.0,
1215
+ phase=0,
1216
+ r=0.0,
1217
+ l=0.0,
1218
+ c=0.0,
1219
+ ):
1220
+ """Create a pin group terminal.
1221
+
1222
+ Parameters
1223
+ ----------
1224
+ positive_pins : positive pins used.
1225
+ :class: `PadstackInstance` or List[:class: ´PadstackInstance´]
1226
+ negatives_pins : negative pins used.
1227
+ :class: `PadstackInstance` or List[:class: ´PadstackInstance´]
1228
+ impedance : float, int or str
1229
+ Terminal impedance. Default value is `50` Ohms.
1230
+ source_type : str
1231
+ Source type assigned on terminal. Supported values : `"circuit_port"`, `"lumped_port"`, `"current_source"`,
1232
+ `"voltage_source"`, `"rlc"`, `"dc_terminal"`. Default value is `"circuit_port"`.
1233
+ name : str, optional
1234
+ Source name.
1235
+ magnitude : float, int or str, optional
1236
+ source magnitude.
1237
+ phase : float, int or str, optional
1238
+ phase magnitude.
1239
+ r : float, optional
1240
+ Resistor value.
1241
+ l : float, optional
1242
+ Inductor value.
1243
+ c : float, optional
1244
+ Capacitor value.
1245
+ """
1246
+ if isinstance(positive_pins, PadstackInstance):
1247
+ positive_pins = [positive_pins]
1248
+ if negatives_pins:
1249
+ if isinstance(negatives_pins, PadstackInstance):
1250
+ negatives_pins = [negatives_pins]
1251
+ if not name:
1252
+ name = (
1253
+ f"Port_{positive_pins[0].component.name}_{positive_pins[0].net.name}_{positive_pins[0].name}_"
1254
+ f"{negatives_pins.name}"
1255
+ )
1256
+ if name in [i.name for i in self._pedb.active_layout.terminals]:
1257
+ name = generate_unique_name(name, n=3)
1258
+ self._logger.warning(f"Port already exists with same name. Renaming to {name}")
1259
+
1260
+ pos_pin_group = self._pedb.components.create_pingroup_from_pins(positive_pins)
1261
+ pos_pingroup_terminal = PinGroupTerminal.create(
1262
+ layout=self._pedb.active_layout,
1263
+ name=name,
1264
+ pin_group=pos_pin_group,
1265
+ net=positive_pins[0].net,
1266
+ is_ref=False,
1267
+ )
1268
+ if not source_type == "dc_terminal":
1269
+ neg_pin_group = self._pedb.components.create_pingroup_from_pins(negatives_pins)
1270
+ neg_pingroup_terminal = PinGroupTerminal.create(
1271
+ layout=self._pedb.active_layout,
1272
+ name=f"{name}_ref",
1273
+ pin_group=neg_pin_group,
1274
+ net=negatives_pins[0].net,
1275
+ is_ref=False,
1276
+ )
1277
+ if source_type in ["circuit_port", "lumped_port"]:
1278
+ pos_pingroup_terminal.boundary_type = GrpcBoundaryType.PORT
1279
+ pos_pingroup_terminal.impedance = GrpcValue(impedance)
1280
+ if len(positive_pins) > 1 and len(negatives_pins) > 1:
1281
+ if source_type == "lumped_port":
1282
+ source_type = "circuit_port"
1283
+ if source_type == "circuit_port":
1284
+ pos_pingroup_terminal.is_circuit_port = True
1285
+ neg_pingroup_terminal.is_circuit_port = True
1286
+ else:
1287
+ pos_pingroup_terminal.is_circuit_port = False
1288
+ neg_pingroup_terminal.is_circuit_port = False
1289
+ pos_pingroup_terminal.reference_terminal = neg_pingroup_terminal
1290
+ pos_pingroup_terminal.name = name
1291
+
1292
+ elif source_type == "current_source":
1293
+ pos_pingroup_terminal.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
1294
+ neg_pingroup_terminal.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
1295
+ pos_pingroup_terminal.source_amplitude = GrpcValue(magnitude)
1296
+ pos_pingroup_terminal.source_phase = GrpcValue(phase)
1297
+ pos_pingroup_terminal.reference_terminal = neg_pingroup_terminal
1298
+ pos_pingroup_terminal.name = name
1299
+
1300
+ elif source_type == "voltage_source":
1301
+ pos_pingroup_terminal.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
1302
+ neg_pingroup_terminal.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
1303
+ pos_pingroup_terminal.source_amplitude = GrpcValue(magnitude)
1304
+ pos_pingroup_terminal.source_phase = GrpcValue(phase)
1305
+ pos_pingroup_terminal.reference_terminal = neg_pingroup_terminal
1306
+ pos_pingroup_terminal.name = name
1307
+
1308
+ elif source_type == "rlc":
1309
+ pos_pingroup_terminal.boundary_type = GrpcBoundaryType.RLC
1310
+ neg_pingroup_terminal.boundary_type = GrpcBoundaryType.RLC
1311
+ pos_pingroup_terminal.reference_terminal = neg_pingroup_terminal
1312
+ Rlc = GrpcRlc()
1313
+ Rlc.r_enabled = bool(r)
1314
+ Rlc.l_enabled = bool(l)
1315
+ Rlc.c_enabled = bool(c)
1316
+ Rlc.r = GrpcValue(r)
1317
+ Rlc.l = GrpcValue(l)
1318
+ Rlc.c = GrpcValue(c)
1319
+ pos_pingroup_terminal.rlc_boundary_parameters = Rlc
1320
+
1321
+ elif source_type == "dc_terminal":
1322
+ pos_pingroup_terminal.boundary_type = GrpcBoundaryType.DC_TERMINAL
1323
+ else:
1324
+ pass
1325
+ return pos_pingroup_terminal.name
1326
+
1327
+ def _check_gnd(self, component_name):
1328
+ negative_net_name = None
1329
+ if self._pedb.nets.is_net_in_component(component_name, "GND"):
1330
+ negative_net_name = "GND"
1331
+ elif self._pedb.nets.is_net_in_component(component_name, "PGND"):
1332
+ negative_net_name = "PGND"
1333
+ elif self._pedb.nets.is_net_in_component(component_name, "AGND"):
1334
+ negative_net_name = "AGND"
1335
+ elif self._pedb.nets.is_net_in_component(component_name, "DGND"):
1336
+ negative_net_name = "DGND"
1337
+ if not negative_net_name:
1338
+ raise ValueError("No GND, PGND, AGND, DGND found. Please setup the negative net name manually.")
1339
+ return negative_net_name
1340
+
1341
+ def create_voltage_source_on_net(
1342
+ self,
1343
+ positive_component_name,
1344
+ positive_net_name,
1345
+ negative_component_name=None,
1346
+ negative_net_name=None,
1347
+ voltage_value=3.3,
1348
+ phase_value=0,
1349
+ source_name=None,
1350
+ ):
1351
+ """Create a voltage source.
1352
+
1353
+ Parameters
1354
+ ----------
1355
+ positive_component_name : str
1356
+ Name of the positive component.
1357
+ positive_net_name : str
1358
+ Name of the positive net.
1359
+ negative_component_name : str, optional
1360
+ Name of the negative component. The default is ``None``, in which case the name of
1361
+ the positive net is assigned.
1362
+ negative_net_name : str, optional
1363
+ Name of the negative net name. The default is ``None`` which will look for GND Nets.
1364
+ voltage_value : float, optional
1365
+ Value for the voltage. The default is ``3.3``.
1366
+ phase_value : optional
1367
+ Value for the phase. The default is ``0``.
1368
+ source_name : str, optional
1369
+ Name of the source. The default is ``""``.
1370
+
1371
+ Returns
1372
+ -------
1373
+ str
1374
+ The name of the source.
1375
+
1376
+ Examples
1377
+ --------
1378
+
1379
+ >>> from pyedb import Edb
1380
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
1381
+ >>> edb.excitations.create_voltage_source_on_net("U2A5","V1P5_S3","U2A5","GND",3.3,0,"source_name")
1382
+ """
1383
+ if not negative_component_name:
1384
+ negative_component_name = positive_component_name
1385
+ if not negative_net_name:
1386
+ negative_net_name = self._check_gnd(negative_component_name)
1387
+ pos_node_pins = self._pedb.components.get_pin_from_component(positive_component_name, positive_net_name)
1388
+ neg_node_pins = self._pedb.components.get_pin_from_component(negative_component_name, negative_net_name)
1389
+
1390
+ if not source_name:
1391
+ source_name = (
1392
+ f"Vsource_{positive_component_name}_{positive_net_name}_"
1393
+ f"{negative_component_name}_{negative_net_name}"
1394
+ )
1395
+ return self.create_pin_group_terminal(
1396
+ positive_pins=pos_node_pins,
1397
+ negatives_pins=neg_node_pins,
1398
+ name=source_name,
1399
+ magnitude=voltage_value,
1400
+ phase=phase_value,
1401
+ impedance=1e-6,
1402
+ source_type="voltage_source",
1403
+ )
1404
+
1405
+ def create_current_source_on_net(
1406
+ self,
1407
+ positive_component_name,
1408
+ positive_net_name,
1409
+ negative_component_name=None,
1410
+ negative_net_name=None,
1411
+ current_value=3.3,
1412
+ phase_value=0,
1413
+ source_name=None,
1414
+ ):
1415
+ """Create a voltage source.
1416
+
1417
+ Parameters
1418
+ ----------
1419
+ positive_component_name : str
1420
+ Name of the positive component.
1421
+ positive_net_name : str
1422
+ Name of the positive net.
1423
+ negative_component_name : str, optional
1424
+ Name of the negative component. The default is ``None``, in which case the name of
1425
+ the positive net is assigned.
1426
+ negative_net_name : str, optional
1427
+ Name of the negative net name. The default is ``None`` which will look for GND Nets.
1428
+ voltage_value : float, optional
1429
+ Value for the voltage. The default is ``3.3``.
1430
+ phase_value : optional
1431
+ Value for the phase. The default is ``0``.
1432
+ source_name : str, optional
1433
+ Name of the source. The default is ``""``.
1434
+
1435
+ Returns
1436
+ -------
1437
+ str
1438
+ The name of the source.
1439
+
1440
+ Examples
1441
+ --------
1442
+
1443
+ >>> from pyedb import Edb
1444
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
1445
+ >>> edb.excitations.create_voltage_source_on_net("U2A5","V1P5_S3","U2A5","GND",3.3,0,"source_name")
1446
+ """
1447
+ if not negative_component_name:
1448
+ negative_component_name = positive_component_name
1449
+ if not negative_net_name:
1450
+ negative_net_name = self._check_gnd(negative_component_name)
1451
+ pos_node_pins = self._pedb.components.get_pin_from_component(positive_component_name, positive_net_name)
1452
+ neg_node_pins = self._pedb.components.get_pin_from_component(negative_component_name, negative_net_name)
1453
+
1454
+ if not source_name:
1455
+ source_name = (
1456
+ f"Vsource_{positive_component_name}_{positive_net_name}_"
1457
+ f"{negative_component_name}_{negative_net_name}"
1458
+ )
1459
+ return self.create_pin_group_terminal(
1460
+ positive_pins=pos_node_pins,
1461
+ negatives_pins=neg_node_pins,
1462
+ name=source_name,
1463
+ magnitude=current_value,
1464
+ phase=phase_value,
1465
+ impedance=1e6,
1466
+ source_type="current_source",
1467
+ )
1468
+
1469
+ def create_coax_port_on_component(self, ref_des_list, net_list, delete_existing_terminal=False):
1470
+ """Create a coaxial port on a component or component list on a net or net list.
1471
+ The name of the new coaxial port is automatically assigned.
1472
+
1473
+ Parameters
1474
+ ----------
1475
+ ref_des_list : list, str
1476
+ List of one or more reference designators.
1477
+
1478
+ net_list : list, str
1479
+ List of one or more nets.
1480
+
1481
+ delete_existing_terminal : bool
1482
+ Delete existing terminal with same name if exists.
1483
+ Port naming convention is `ref_des`_`pin.net.name`_`pin.name`
1484
+
1485
+ Returns
1486
+ -------
1487
+ bool
1488
+ ``True`` when successful, ``False`` when failed.
1489
+
1490
+ """
1491
+ coax = []
1492
+ if not isinstance(ref_des_list, list):
1493
+ ref_des_list = [ref_des_list]
1494
+ if not isinstance(net_list, list):
1495
+ net_list = [net_list]
1496
+ for ref in ref_des_list:
1497
+ for _, pin in self._pedb.components.instances[ref].pins.items():
1498
+ try: # trying due to grpc crash when no net is defined on pin.
1499
+ try:
1500
+ pin_net = pin.net
1501
+ except:
1502
+ pin_net = None
1503
+ if pin_net and pin.net.is_null:
1504
+ self._logger.warning(f"Pin {pin.id} has no net defined")
1505
+ elif pin.net.name in net_list:
1506
+ pin.is_pin = True
1507
+ port_name = f"{ref}_{pin.net.name}_{pin.name}"
1508
+ if self.check_before_terminal_assignement(
1509
+ connectable=pin, delete_existing_terminal=delete_existing_terminal
1510
+ ):
1511
+ top_layer = pin.get_layer_range()[0]
1512
+ term = PadstackInstanceTerminal.create(
1513
+ layout=pin.layout,
1514
+ name=port_name,
1515
+ padstack_instance=pin,
1516
+ layer=top_layer,
1517
+ net=pin.net,
1518
+ is_ref=False,
1519
+ )
1520
+ if not term.is_null:
1521
+ coax.append(port_name)
1522
+ except RuntimeError as error:
1523
+ self._logger.error(error)
1524
+ return coax
1525
+
1526
+ def check_before_terminal_assignement(self, connectable, delete_existing_terminal=False):
1527
+ if not connectable:
1528
+ return False
1529
+ existing_terminals = [term for term in self._pedb.active_layout.terminals if term.id == connectable.id]
1530
+ if existing_terminals:
1531
+ if not delete_existing_terminal:
1532
+ self._pedb.logger.error(
1533
+ f"Terminal {connectable.name} already defined in design, please make sure to have unique name."
1534
+ )
1535
+ return False
1536
+ else:
1537
+ if isinstance(connectable, PadstackInstanceTerminal):
1538
+ self._pedb.logger.error(
1539
+ f"Terminal {connectable.name} already defined, check status on bug "
1540
+ f"https://github.com/ansys/pyedb-core/issues/429"
1541
+ )
1542
+ return False
1543
+ else:
1544
+ return True
1545
+
1546
+ def create_differential_wave_port(
1547
+ self,
1548
+ positive_primitive_id,
1549
+ positive_points_on_edge,
1550
+ negative_primitive_id,
1551
+ negative_points_on_edge,
1552
+ port_name=None,
1553
+ horizontal_extent_factor=5,
1554
+ vertical_extent_factor=3,
1555
+ pec_launch_width="0.01mm",
1556
+ ):
1557
+ """Create a differential wave port.
1558
+
1559
+ Parameters
1560
+ ----------
1561
+ positive_primitive_id : int, EDBPrimitives
1562
+ Primitive ID of the positive terminal.
1563
+ positive_points_on_edge : list
1564
+ Coordinate of the point to define the edge terminal.
1565
+ The point must be close to the target edge but not on the two
1566
+ ends of the edge.
1567
+ negative_primitive_id : int, EDBPrimitives
1568
+ Primitive ID of the negative terminal.
1569
+ negative_points_on_edge : list
1570
+ Coordinate of the point to define the edge terminal.
1571
+ The point must be close to the target edge but not on the two
1572
+ ends of the edge.
1573
+ port_name : str, optional
1574
+ Name of the port. The default is ``None``.
1575
+ horizontal_extent_factor : int, float, optional
1576
+ Horizontal extent factor. The default value is ``5``.
1577
+ vertical_extent_factor : int, float, optional
1578
+ Vertical extent factor. The default value is ``3``.
1579
+ pec_launch_width : str, optional
1580
+ Launch Width of PEC. The default value is ``"0.01mm"``.
1581
+
1582
+ Returns
1583
+ -------
1584
+ tuple
1585
+ The tuple contains: (port_name, pyedb.dotnet.database.edb_data.sources.ExcitationDifferential).
1586
+
1587
+ Examples
1588
+ --------
1589
+ >>> edb.hfss.create_differential_wave_port(0, ["-50mm", "-0mm"], 1, ["-50mm", "-0.2mm"])
1590
+ """
1591
+ if not port_name:
1592
+ port_name = generate_unique_name("diff")
1593
+
1594
+ if isinstance(positive_primitive_id, Primitive):
1595
+ positive_primitive_id = positive_primitive_id.id
1596
+
1597
+ if isinstance(negative_primitive_id, Primitive):
1598
+ negative_primitive_id = negative_primitive_id.id
1599
+
1600
+ _, pos_term = self.create_wave_port(
1601
+ positive_primitive_id,
1602
+ positive_points_on_edge,
1603
+ horizontal_extent_factor=horizontal_extent_factor,
1604
+ vertical_extent_factor=vertical_extent_factor,
1605
+ pec_launch_width=pec_launch_width,
1606
+ )
1607
+ _, neg_term = self.create_wave_port(
1608
+ negative_primitive_id,
1609
+ negative_points_on_edge,
1610
+ horizontal_extent_factor=horizontal_extent_factor,
1611
+ vertical_extent_factor=vertical_extent_factor,
1612
+ pec_launch_width=pec_launch_width,
1613
+ )
1614
+ edb_list = [pos_term, neg_term]
1615
+
1616
+ boundle_terminal = BundleTerminal.create(edb_list)
1617
+ boundle_terminal.name = port_name
1618
+ bundle_term = boundle_terminal.terminals
1619
+ bundle_term[0].name = port_name + ":T1"
1620
+ bundle_term[1].mame = port_name + ":T2"
1621
+ return port_name, boundle_terminal
1622
+
1623
+ def create_wave_port(
1624
+ self,
1625
+ prim_id,
1626
+ point_on_edge,
1627
+ port_name=None,
1628
+ impedance=50,
1629
+ horizontal_extent_factor=5,
1630
+ vertical_extent_factor=3,
1631
+ pec_launch_width="0.01mm",
1632
+ ):
1633
+ """Create a wave port.
1634
+
1635
+ Parameters
1636
+ ----------
1637
+ prim_id : int, Primitive
1638
+ Primitive ID.
1639
+ point_on_edge : list
1640
+ Coordinate of the point to define the edge terminal.
1641
+ The point must be on the target edge but not on the two
1642
+ ends of the edge.
1643
+ port_name : str, optional
1644
+ Name of the port. The default is ``None``.
1645
+ impedance : int, float, optional
1646
+ Impedance of the port. The default value is ``50``.
1647
+ horizontal_extent_factor : int, float, optional
1648
+ Horizontal extent factor. The default value is ``5``.
1649
+ vertical_extent_factor : int, float, optional
1650
+ Vertical extent factor. The default value is ``3``.
1651
+ pec_launch_width : str, optional
1652
+ Launch Width of PEC. The default value is ``"0.01mm"``.
1653
+
1654
+ Returns
1655
+ -------
1656
+ tuple
1657
+ The tuple contains: (Port name, pyedb.dotnet.database.edb_data.sources.Excitation).
1658
+
1659
+ Examples
1660
+ --------
1661
+ >>> edb.excitations.create_wave_port(0, ["-50mm", "-0mm"])
1662
+ """
1663
+ if not port_name:
1664
+ port_name = generate_unique_name("Terminal_")
1665
+
1666
+ if isinstance(prim_id, Primitive):
1667
+ prim_id = prim_id.id
1668
+ pos_edge_term = self._create_edge_terminal(prim_id, point_on_edge, port_name)
1669
+ pos_edge_term.impedance = GrpcValue(impedance)
1670
+ wave_port = WavePort(self._pedb, pos_edge_term)
1671
+ wave_port.horizontal_extent_factor = horizontal_extent_factor
1672
+ wave_port.vertical_extent_factor = vertical_extent_factor
1673
+ wave_port.pec_launch_width = pec_launch_width
1674
+ wave_port.hfss_type = "Wave"
1675
+ wave_port.do_renormalize = True
1676
+ if pos_edge_term:
1677
+ return port_name, wave_port
1678
+ else:
1679
+ return False
1680
+
1681
+ def create_edge_port_vertical(
1682
+ self,
1683
+ prim_id,
1684
+ point_on_edge,
1685
+ port_name=None,
1686
+ impedance=50,
1687
+ reference_layer=None,
1688
+ hfss_type="Gap",
1689
+ horizontal_extent_factor=5,
1690
+ vertical_extent_factor=3,
1691
+ pec_launch_width="0.01mm",
1692
+ ):
1693
+ """Create a vertical edge port.
1694
+
1695
+ Parameters
1696
+ ----------
1697
+ prim_id : int
1698
+ Primitive ID.
1699
+ point_on_edge : list
1700
+ Coordinate of the point to define the edge terminal.
1701
+ The point must be on the target edge but not on the two
1702
+ ends of the edge.
1703
+ port_name : str, optional
1704
+ Name of the port. The default is ``None``.
1705
+ impedance : int, float, optional
1706
+ Impedance of the port. The default value is ``50``.
1707
+ reference_layer : str, optional
1708
+ Reference layer of the port. The default is ``None``.
1709
+ hfss_type : str, optional
1710
+ Type of the port. The default value is ``"Gap"``. Options are ``"Gap"``, ``"Wave"``.
1711
+ horizontal_extent_factor : int, float, optional
1712
+ Horizontal extent factor. The default value is ``5``.
1713
+ vertical_extent_factor : int, float, optional
1714
+ Vertical extent factor. The default value is ``3``.
1715
+ radial_extent_factor : int, float, optional
1716
+ Radial extent factor. The default value is ``0``.
1717
+ pec_launch_width : str, optional
1718
+ Launch Width of PEC. The default value is ``"0.01mm"``.
1719
+
1720
+ Returns
1721
+ -------
1722
+ str
1723
+ Port name.
1724
+ """
1725
+ if not port_name:
1726
+ port_name = generate_unique_name("Terminal_")
1727
+ pos_edge_term = self._create_edge_terminal(prim_id, point_on_edge, port_name)
1728
+ pos_edge_term.impedance = GrpcValue(impedance)
1729
+ if reference_layer:
1730
+ reference_layer = self._pedb.stackup.signal_layers[reference_layer]
1731
+ pos_edge_term.reference_layer = reference_layer
1732
+
1733
+ prop = ", ".join(
1734
+ [
1735
+ f"HFSS('HFSS Type'='{hfss_type}'",
1736
+ " Orientation='Vertical'",
1737
+ " 'Layer Alignment'='Upper'",
1738
+ f" 'Horizontal Extent Factor'='{horizontal_extent_factor}'",
1739
+ f" 'Vertical Extent Factor'='{vertical_extent_factor}'",
1740
+ f" 'PEC Launch Width'='{pec_launch_width}')",
1741
+ ]
1742
+ )
1743
+ pos_edge_term.set_product_solver_option(
1744
+ GrpcProductIdType.DESIGNER,
1745
+ "HFSS",
1746
+ prop,
1747
+ )
1748
+ if not pos_edge_term.is_null:
1749
+ return pos_edge_term
1750
+ else:
1751
+ return False
1752
+
1753
+ def create_edge_port_horizontal(
1754
+ self,
1755
+ prim_id,
1756
+ point_on_edge,
1757
+ ref_prim_id=None,
1758
+ point_on_ref_edge=None,
1759
+ port_name=None,
1760
+ impedance=50,
1761
+ layer_alignment="Upper",
1762
+ ):
1763
+ """Create a horizontal edge port.
1764
+
1765
+ Parameters
1766
+ ----------
1767
+ prim_id : int
1768
+ Primitive ID.
1769
+ point_on_edge : list
1770
+ Coordinate of the point to define the edge terminal.
1771
+ The point must be on the target edge but not on the two
1772
+ ends of the edge.
1773
+ ref_prim_id : int, optional
1774
+ Reference primitive ID. The default is ``None``.
1775
+ point_on_ref_edge : list, optional
1776
+ Coordinate of the point to define the reference edge
1777
+ terminal. The point must be on the target edge but not
1778
+ on the two ends of the edge. The default is ``None``.
1779
+ port_name : str, optional
1780
+ Name of the port. The default is ``None``.
1781
+ impedance : int, float, optional
1782
+ Impedance of the port. The default value is ``50``.
1783
+ layer_alignment : str, optional
1784
+ Layer alignment. The default value is ``Upper``. Options are ``"Upper"``, ``"Lower"``.
1785
+
1786
+ Returns
1787
+ -------
1788
+ str
1789
+ Name of the port.
1790
+ """
1791
+ pos_edge_term = self._create_edge_terminal(prim_id, point_on_edge, port_name)
1792
+ neg_edge_term = self._create_edge_terminal(ref_prim_id, point_on_ref_edge, port_name + "_ref", is_ref=True)
1793
+
1794
+ pos_edge_term.impedance = GrpcValue(impedance)
1795
+ pos_edge_term.reference_terminal = neg_edge_term
1796
+ if not layer_alignment == "Upper":
1797
+ layer_alignment = "Lower"
1798
+ pos_edge_term.set_product_solver_option(
1799
+ GrpcProductIdType.DESIGNER,
1800
+ "HFSS",
1801
+ f"HFSS('HFSS Type'='Gap(coax)', Orientation='Horizontal', 'Layer Alignment'='{layer_alignment}')",
1802
+ )
1803
+ if pos_edge_term:
1804
+ return port_name
1805
+ else:
1806
+ return False
1807
+
1808
+ def create_lumped_port_on_net(
1809
+ self, nets=None, reference_layer=None, return_points_only=False, digit_resolution=6, at_bounding_box=True
1810
+ ):
1811
+ """Create an edge port on nets. This command looks for traces and polygons on the
1812
+ nets and tries to assign vertical lumped port.
1813
+
1814
+ Parameters
1815
+ ----------
1816
+ nets : list, optional
1817
+ List of nets, str or Edb net.
1818
+
1819
+ reference_layer : str, Edb layer.
1820
+ Name or Edb layer object.
1821
+
1822
+ return_points_only : bool, optional
1823
+ Use this boolean when you want to return only the points from the edges and not creating ports. Default
1824
+ value is ``False``.
1825
+
1826
+ digit_resolution : int, optional
1827
+ The number of digits carried for the edge location accuracy. The default value is ``6``.
1828
+
1829
+ at_bounding_box : bool
1830
+ When ``True`` will keep the edges from traces at the layout bounding box location. This is recommended when
1831
+ a cutout has been performed before and lumped ports have to be created on ending traces. Default value is
1832
+ ``True``.
1833
+
1834
+ Returns
1835
+ -------
1836
+ bool
1837
+ ``True`` when successful, ``False`` when failed.
1838
+ """
1839
+ if isinstance(nets, str):
1840
+ nets = [self._pedb.nets.signal[nets]]
1841
+ if isinstance(nets, Net):
1842
+ nets = [nets]
1843
+ nets = [self._pedb.nets.signal[net] for net in nets if isinstance(net, str)]
1844
+ port_created = False
1845
+ if nets:
1846
+ edges_pts = []
1847
+ if isinstance(reference_layer, str):
1848
+ try:
1849
+ reference_layer = self._pedb.stackup.signal_layers[reference_layer]
1850
+ except:
1851
+ raise Exception(f"Failed to get the layer {reference_layer}")
1852
+ if not isinstance(reference_layer, StackupLayer):
1853
+ return False
1854
+ layout_bbox = self._pedb.get_conformal_polygon_from_netlist(self._pedb.nets.netlist)
1855
+ layout_extent_segments = [pt for pt in list(layout_bbox.arc_data) if pt.is_segment]
1856
+ first_pt = layout_extent_segments[0]
1857
+ layout_extent_points = [
1858
+ [first_pt.start.x.value, first_pt.end.x.value],
1859
+ [first_pt.Start.y.value, first_pt.end.y.value],
1860
+ ]
1861
+ for segment in layout_extent_segments[1:]:
1862
+ end_point = (segment.end.x.value, segment.end.y.value)
1863
+ layout_extent_points[0].append(end_point[0])
1864
+ layout_extent_points[1].append(end_point[1])
1865
+ for net in nets:
1866
+ net_primitives = self._pedb.nets[net.name].primitives
1867
+ net_paths = [pp for pp in net_primitives if pp.type == "Path"]
1868
+ for path in net_paths:
1869
+ trace_path_pts = list(path.center_line.Points)
1870
+ port_name = f"{net.name}_{path.id}"
1871
+ for pt in trace_path_pts:
1872
+ _pt = [
1873
+ round(pt.x.value, digit_resolution),
1874
+ round(pt.y.value, digit_resolution),
1875
+ ]
1876
+ if at_bounding_box:
1877
+ if GeometryOperators.point_in_polygon(_pt, layout_extent_points) == 0:
1878
+ if return_points_only:
1879
+ edges_pts.append(_pt)
1880
+ else:
1881
+ term = self._create_edge_terminal(path.id, pt, port_name) # pragma no cover
1882
+ term.reference_layer = reference_layer
1883
+ port_created = True
1884
+ else:
1885
+ if return_points_only: # pragma: no cover
1886
+ edges_pts.append(_pt)
1887
+ else:
1888
+ term = self._create_edge_terminal(path.id, pt, port_name)
1889
+ term.reference_layer = reference_layer
1890
+ port_created = True
1891
+ net_poly = [pp for pp in net_primitives if pp.type == "Polygon"]
1892
+ for poly in net_poly:
1893
+ poly_segment = [aa for aa in poly.arcs if aa.is_segment]
1894
+ for segment in poly_segment:
1895
+ if (
1896
+ GeometryOperators.point_in_polygon(
1897
+ [segment.mid_point.x.value, segment.mid_point.y.value], layout_extent_points
1898
+ )
1899
+ == 0
1900
+ ):
1901
+ if return_points_only:
1902
+ edges_pts.append(segment.mid_point)
1903
+ else:
1904
+ port_name = f"{net.name}_{poly.id}"
1905
+ term = self._create_edge_terminal(
1906
+ poly.id, segment.mid_point, port_name
1907
+ ) # pragma no cover
1908
+ term.set_reference_layer = reference_layer
1909
+ port_created = True
1910
+ if return_points_only:
1911
+ return edges_pts
1912
+ return port_created
1913
+
1914
+ def create_vertical_circuit_port_on_clipped_traces(self, nets=None, reference_net=None, user_defined_extent=None):
1915
+ """Create an edge port on clipped signal traces.
1916
+
1917
+ Parameters
1918
+ ----------
1919
+ nets : list, optional
1920
+ String of one net or EDB net or a list of multiple nets or EDB nets.
1921
+
1922
+ reference_net : str, Edb net.
1923
+ Name or EDB reference net.
1924
+
1925
+ user_defined_extent : [x, y], EDB PolygonData
1926
+ Use this point list or PolygonData object to check if ports are at this polygon border.
1927
+
1928
+ Returns
1929
+ -------
1930
+ [[str]]
1931
+ Nested list of str, with net name as first value, X value for point at border, Y value for point at border,
1932
+ and terminal name.
1933
+ """
1934
+ if not isinstance(nets, list):
1935
+ if isinstance(nets, str):
1936
+ nets = list(self._pedb.nets.signal.values())
1937
+ else:
1938
+ nets = [self._pedb.nets.signal[net] for net in nets]
1939
+ if nets:
1940
+ if isinstance(reference_net, str):
1941
+ reference_net = self._pedb.nets.nets[reference_net]
1942
+ if not reference_net:
1943
+ self._logger.error("No reference net provided for creating port")
1944
+ return False
1945
+ if user_defined_extent:
1946
+ if isinstance(user_defined_extent, GrpcPolygonData):
1947
+ _points = [pt for pt in list(user_defined_extent.points)]
1948
+ _x = []
1949
+ _y = []
1950
+ for pt in _points:
1951
+ if pt.x.value < 1e100 and pt.y.value < 1e100:
1952
+ _x.append(pt.x.value)
1953
+ _y.append(pt.y.value)
1954
+ user_defined_extent = [_x, _y]
1955
+ terminal_info = []
1956
+ for net in nets:
1957
+ net_polygons = [prim for prim in self._pedb.modeler.primitives if prim.type in ["polygon", "rectangle"]]
1958
+ for poly in net_polygons:
1959
+ mid_points = [[arc.midpoint.x.value, arc.midpoint.y.value] for arc in poly.arcs]
1960
+ for mid_point in mid_points:
1961
+ if GeometryOperators.point_in_polygon(mid_point, user_defined_extent) == 0:
1962
+ port_name = generate_unique_name(f"{poly.net_name}_{poly.id}")
1963
+ term = self._create_edge_terminal(poly.id, mid_point, port_name) # pragma no cover
1964
+ if not term.is_null:
1965
+ self._logger.info(f"Terminal {term.name} created")
1966
+ term.is_circuit_port = True
1967
+ terminal_info.append([poly.net_name, mid_point[0], mid_point[1], term.name])
1968
+ mid_pt_data = GrpcPointData(mid_point)
1969
+ ref_prim = [
1970
+ prim
1971
+ for prim in reference_net.primitives
1972
+ if prim.polygon_data.point_in_polygon(mid_pt_data)
1973
+ ]
1974
+ if not ref_prim:
1975
+ self._logger.warning("no reference primitive found, trying to extend scanning area")
1976
+ scanning_zone = [
1977
+ (mid_point[0] - mid_point[0] * 1e-3, mid_point[1] - mid_point[1] * 1e-3),
1978
+ (mid_point[0] - mid_point[0] * 1e-3, mid_point[1] + mid_point[1] * 1e-3),
1979
+ (mid_point[0] + mid_point[0] * 1e-3, mid_point[1] + mid_point[1] * 1e-3),
1980
+ (mid_point[0] + mid_point[0] * 1e-3, mid_point[1] - mid_point[1] * 1e-3),
1981
+ ]
1982
+ for new_point in scanning_zone:
1983
+ mid_pt_data = GrpcPointData(new_point)
1984
+ ref_prim = [
1985
+ prim
1986
+ for prim in reference_net.primitives
1987
+ if prim.polygon_data.point_in_polygon(mid_pt_data)
1988
+ ]
1989
+ if ref_prim:
1990
+ self._logger.info("Reference primitive found")
1991
+ break
1992
+ if not ref_prim:
1993
+ self._logger.error("Failed to collect valid reference primitives for terminal")
1994
+ if ref_prim:
1995
+ reference_layer = ref_prim[0].layer
1996
+ if term.reference_layer == reference_layer:
1997
+ self._logger.info(f"Port {port_name} created")
1998
+ return terminal_info
1999
+ return False
2000
+
2001
+ def create_bundle_wave_port(
2002
+ self,
2003
+ primitives_id,
2004
+ points_on_edge,
2005
+ port_name=None,
2006
+ horizontal_extent_factor=5,
2007
+ vertical_extent_factor=3,
2008
+ pec_launch_width="0.01mm",
2009
+ ):
2010
+ """Create a bundle wave port.
2011
+
2012
+ Parameters
2013
+ ----------
2014
+ primitives_id : list
2015
+ Primitive ID of the positive terminal.
2016
+ points_on_edge : list
2017
+ Coordinate of the point to define the edge terminal.
2018
+ The point must be close to the target edge but not on the two
2019
+ ends of the edge.
2020
+ port_name : str, optional
2021
+ Name of the port. The default is ``None``.
2022
+ horizontal_extent_factor : int, float, optional
2023
+ Horizontal extent factor. The default value is ``5``.
2024
+ vertical_extent_factor : int, float, optional
2025
+ Vertical extent factor. The default value is ``3``.
2026
+ pec_launch_width : str, optional
2027
+ Launch Width of PEC. The default value is ``"0.01mm"``.
2028
+
2029
+ Returns
2030
+ -------
2031
+ tuple
2032
+ The tuple contains: (port_name, pyedb.egacy.database.edb_data.sources.ExcitationDifferential).
2033
+
2034
+ Examples
2035
+ --------
2036
+ >>> edb.excitations.create_bundle_wave_port(0, ["-50mm", "-0mm"], 1, ["-50mm", "-0.2mm"])
2037
+ """
2038
+ if not port_name:
2039
+ port_name = generate_unique_name("bundle_port")
2040
+
2041
+ if isinstance(primitives_id[0], Primitive):
2042
+ primitives_id = [i.id for i in primitives_id]
2043
+
2044
+ terminals = []
2045
+ _port_name = port_name
2046
+ for p_id, loc in list(zip(primitives_id, points_on_edge)):
2047
+ _, term = self.create_wave_port(
2048
+ p_id,
2049
+ loc,
2050
+ port_name=_port_name,
2051
+ horizontal_extent_factor=horizontal_extent_factor,
2052
+ vertical_extent_factor=vertical_extent_factor,
2053
+ pec_launch_width=pec_launch_width,
2054
+ )
2055
+ _port_name = None
2056
+ terminals.append(term)
2057
+
2058
+ _edb_bundle_terminal = BundleTerminal.create(terminals)
2059
+ return port_name, BundleWavePort(self._pedb, _edb_bundle_terminal)
2060
+
2061
+ def create_hfss_ports_on_padstack(self, pinpos, portname=None):
2062
+ """Create an HFSS port on a padstack.
2063
+
2064
+ Parameters
2065
+ ----------
2066
+ pinpos :
2067
+ Position of the pin.
2068
+
2069
+ portname : str, optional
2070
+ Name of the port. The default is ``None``.
2071
+
2072
+ Returns
2073
+ -------
2074
+ bool
2075
+ ``True`` when successful, ``False`` when failed.
2076
+ """
2077
+ top_layer, bottom_layer = pinpos.get_layer_range()
2078
+
2079
+ if not portname:
2080
+ portname = generate_unique_name("Port_" + pinpos.net.name)
2081
+ edbpointTerm_pos = PadstackInstanceTerminal.create(
2082
+ padstack_instance=pinpos, name=portname, layer=top_layer, is_ref=False
2083
+ )
2084
+ if edbpointTerm_pos:
2085
+ return True
2086
+ else:
2087
+ return False
2088
+
2089
+ def get_ports_number(self):
2090
+ """Return the total number of excitation ports in a layout.
2091
+
2092
+ Parameters
2093
+ ----------
2094
+ None
2095
+
2096
+ Returns
2097
+ -------
2098
+ int
2099
+ Number of ports.
2100
+
2101
+ """
2102
+ terms = [term for term in self._pedb.layout.terminals]
2103
+ return len([i for i in terms if not i.is_reference_terminal])
2104
+
2105
+ def create_rlc_boundary_on_pins(self, positive_pin=None, negative_pin=None, rvalue=0.0, lvalue=0.0, cvalue=0.0):
2106
+ """Create hfss rlc boundary on pins.
2107
+
2108
+ Parameters
2109
+ ----------
2110
+ positive_pin : Positive pin.
2111
+ Edb.Cell.Primitive.PadstackInstance
2112
+
2113
+ negative_pin : Negative pin.
2114
+ Edb.Cell.Primitive.PadstackInstance
2115
+
2116
+ rvalue : Resistance value
2117
+
2118
+ lvalue : Inductance value
2119
+
2120
+ cvalue . Capacitance value.
2121
+
2122
+ Returns
2123
+ -------
2124
+ bool
2125
+ ``True`` when successful, ``False`` when failed.
2126
+
2127
+ """
2128
+
2129
+ if positive_pin and negative_pin:
2130
+ positive_pin_term = positive_pin.get_terminal(create_new_terminal=True)
2131
+ negative_pin_term = negative_pin.get_terminal(create_new_terminal=True)
2132
+ positive_pin_term.boundary_type = GrpcBoundaryType.RLC
2133
+ negative_pin_term.boundary_type = GrpcBoundaryType.RLC
2134
+ rlc = GrpcRlc()
2135
+ rlc.is_parallel = True
2136
+ rlc.r_enabled = True
2137
+ rlc.l_enabled = True
2138
+ rlc.c_enabled = True
2139
+ rlc.r = GrpcValue(rvalue)
2140
+ rlc.l = GrpcValue(lvalue)
2141
+ rlc.c = GrpcValue(cvalue)
2142
+ positive_pin_term.rlc_boundary_parameters = rlc
2143
+ term_name = f"{positive_pin.component.name}_{positive_pin.net.name}_{positive_pin.name}"
2144
+ positive_pin_term.name = term_name
2145
+ negative_pin_term.name = f"{term_name}_ref"
2146
+ positive_pin_term.reference_terminal = negative_pin_term
2147
+ return positive_pin_term
2148
+ return False
2149
+
2150
+ def create_edge_port_on_polygon(
2151
+ self,
2152
+ polygon=None,
2153
+ reference_polygon=None,
2154
+ terminal_point=None,
2155
+ reference_point=None,
2156
+ reference_layer=None,
2157
+ port_name=None,
2158
+ port_impedance=50.0,
2159
+ force_circuit_port=False,
2160
+ ):
2161
+ """Create lumped port between two edges from two different polygons. Can also create a vertical port when
2162
+ the reference layer name is only provided. When a port is created between two edge from two polygons which don't
2163
+ belong to the same layer, a circuit port will be automatically created instead of lumped. To enforce the circuit
2164
+ port instead of lumped,use the boolean force_circuit_port.
2165
+
2166
+ Parameters
2167
+ ----------
2168
+ polygon : The EDB polygon object used to assign the port.
2169
+ Edb.Cell.Primitive.Polygon object.
2170
+
2171
+ reference_polygon : The EDB polygon object used to define the port reference.
2172
+ Edb.Cell.Primitive.Polygon object.
2173
+
2174
+ terminal_point : The coordinate of the point to define the edge terminal of the port. This point must be
2175
+ located on the edge of the polygon where the port has to be placed. For instance taking the middle point
2176
+ of an edge is a good practice but any point of the edge should be valid. Taking a corner might cause unwanted
2177
+ port location.
2178
+ list[float, float] with values provided in meter.
2179
+
2180
+ reference_point : same as terminal_point but used for defining the reference location on the edge.
2181
+ list[float, float] with values provided in meter.
2182
+
2183
+ reference_layer : Name used to define port reference for vertical ports.
2184
+ str the layer name.
2185
+
2186
+ port_name : Name of the port.
2187
+ str.
2188
+
2189
+ port_impedance : port impedance value. Default value is 50 Ohms.
2190
+ float, impedance value.
2191
+
2192
+ force_circuit_port ; used to force circuit port creation instead of lumped. Works for vertical and coplanar
2193
+ ports.
2194
+
2195
+ Examples
2196
+ --------
2197
+
2198
+ >>> edb_path = path_to_edb
2199
+ >>> edb = Edb(edb_path)
2200
+ >>> poly_list = [poly for poly in list(edb.layout.primitives) if poly.GetPrimitiveType() == 2]
2201
+ >>> port_poly = [poly for poly in poly_list if poly.GetId() == 17][0]
2202
+ >>> ref_poly = [poly for poly in poly_list if poly.GetId() == 19][0]
2203
+ >>> port_location = [-65e-3, -13e-3]
2204
+ >>> ref_location = [-63e-3, -13e-3]
2205
+ >>> edb.hfss.create_edge_port_on_polygon(polygon=port_poly, reference_polygon=ref_poly,
2206
+ >>> terminal_point=port_location, reference_point=ref_location)
2207
+
2208
+ """
2209
+ if not polygon:
2210
+ self._logger.error("No polygon provided for port {} creation".format(port_name))
2211
+ return False
2212
+ if reference_layer:
2213
+ reference_layer = self._pedb.stackup.signal_layers[reference_layer]
2214
+ if not reference_layer:
2215
+ self._logger.error("Specified layer for port {} creation was not found".format(port_name))
2216
+ if not isinstance(terminal_point, list):
2217
+ self._logger.error("Terminal point must be a list of float with providing the point location in meter")
2218
+ return False
2219
+ terminal_point = GrpcPointData(terminal_point)
2220
+ if reference_point and isinstance(reference_point, list):
2221
+ reference_point = GrpcPointData(reference_point)
2222
+ if not port_name:
2223
+ port_name = generate_unique_name("Port_")
2224
+ edge = GrpcPrimitiveEdge.create(polygon, terminal_point)
2225
+ edges = [edge]
2226
+ edge_term = GrpcEdgeTerminal.create(
2227
+ layout=polygon.layout, edges=edges, net=polygon.net, name=port_name, is_ref=False
2228
+ )
2229
+ if force_circuit_port:
2230
+ edge_term.is_circuit_port = True
2231
+ else:
2232
+ edge_term.is_circuit_port = False
2233
+
2234
+ if port_impedance:
2235
+ edge_term.impedance = GrpcValue(port_impedance)
2236
+ edge_term.name = port_name
2237
+ if reference_polygon and reference_point:
2238
+ ref_edge = GrpcPrimitiveEdge.create(reference_polygon, reference_point)
2239
+ ref_edges = [ref_edge]
2240
+ ref_edge_term = GrpcEdgeTerminal.create(
2241
+ layout=reference_polygon.layout,
2242
+ name=port_name + "_ref",
2243
+ edges=ref_edges,
2244
+ net=reference_polygon.net,
2245
+ is_ref=True,
2246
+ )
2247
+ if reference_layer:
2248
+ ref_edge_term.reference_layer = reference_layer
2249
+ if force_circuit_port:
2250
+ ref_edge_term.is_circuit_port = True
2251
+ else:
2252
+ ref_edge_term.is_circuit_port = False
2253
+
2254
+ if port_impedance:
2255
+ ref_edge_term.impedance = GrpcValue(port_impedance)
2256
+ edge_term.reference_terminal = ref_edge_term
2257
+ return True
2258
+
2259
+ def create_port_between_pin_and_layer(
2260
+ self, component_name=None, pins_name=None, layer_name=None, reference_net=None, impedance=50.0
2261
+ ):
2262
+ """Create circuit port between pin and a reference layer.
2263
+
2264
+ Parameters
2265
+ ----------
2266
+ component_name : str
2267
+ Component name. The default is ``None``.
2268
+ pins_name : str
2269
+ Pin name or list of pin names. The default is ``None``.
2270
+ layer_name : str
2271
+ Layer name. The default is ``None``.
2272
+ reference_net : str
2273
+ Reference net name. The default is ``None``.
2274
+ impedance : float, optional
2275
+ Port impedance. The default is ``50.0`` in ohms.
2276
+
2277
+ Returns
2278
+ -------
2279
+ PadstackInstanceTerminal
2280
+ Created terminal.
2281
+
2282
+ """
2283
+ if not pins_name:
2284
+ pins_name = []
2285
+ if pins_name:
2286
+ if not isinstance(pins_name, list): # pragma no cover
2287
+ pins_name = [pins_name]
2288
+ if not reference_net:
2289
+ self._logger.info("no reference net provided, searching net {} instead.".format(layer_name))
2290
+ reference_net = self._pedb.nets.get_net_by_name(layer_name)
2291
+ if not reference_net: # pragma no cover
2292
+ self._logger.error("reference net {} not found.".format(layer_name))
2293
+ return False
2294
+ else:
2295
+ if not isinstance(reference_net, Net): # pragma no cover
2296
+ reference_net = self._pedb.nets.get_net_by_name(reference_net)
2297
+ if not reference_net:
2298
+ self._logger.error("Net {} not found".format(reference_net))
2299
+ return False
2300
+ terms = []
2301
+ pins = self._pedb.components.instances[component_name].pins
2302
+ for __pin in pins_name:
2303
+ if __pin in pins:
2304
+ pin = pins[__pin]
2305
+ term_name = f"{pin.component.name}_{pin.net.name}_{pin.component}"
2306
+ start_layer, stop_layer = pin.get_layer_range()
2307
+ if start_layer:
2308
+ positive_terminal = PadstackInstanceTerminal.create(
2309
+ layout=pin.layout, net=pin.net, padstack_instance=pin, name=term_name, layer=start_layer
2310
+ )
2311
+ positive_terminal.boundary_type = GrpcBoundaryType.PORT
2312
+ positive_terminal.impedance = GrpcValue(impedance)
2313
+ positive_terminal.Is_circuit_port = True
2314
+ position = GrpcPointData(self._pedb.components.get_pin_position(pin))
2315
+ negative_terminal = PointTerminal.create(
2316
+ layout=self._pedb.active_layout,
2317
+ net=reference_net,
2318
+ layer=self._pedb.stackup.signal_layers[layer_name],
2319
+ name=f"{term_name}_ref",
2320
+ point=position,
2321
+ )
2322
+ negative_terminal.boundary_type = GrpcBoundaryType.PORT
2323
+ negative_terminal.impedance = GrpcValue(impedance)
2324
+ negative_terminal.is_circuit_port = True
2325
+ positive_terminal.reference_terminal = negative_terminal
2326
+ self._logger.info("Port {} successfully created".format(term_name))
2327
+ if not positive_terminal.is_null:
2328
+ terms.append(positive_terminal)
2329
+ else:
2330
+ self._logger.error(f"pin {__pin} not found on component {component_name}")
2331
+ if terms:
2332
+ return terms
2333
+ return False
2334
+
2335
+ def create_current_source_on_pin_group(
2336
+ self, pos_pin_group_name, neg_pin_group_name, magnitude=1, phase=0, name=None
2337
+ ):
2338
+ """Create current source between two pin groups.
2339
+
2340
+ Parameters
2341
+ ----------
2342
+ pos_pin_group_name : str
2343
+ Name of the positive pin group.
2344
+ neg_pin_group_name : str
2345
+ Name of the negative pin group.
2346
+ magnitude : int, float, optional
2347
+ Magnitude of the source.
2348
+ phase : int, float, optional
2349
+ Phase of the source
2350
+
2351
+ Returns
2352
+ -------
2353
+ bool
2354
+
2355
+ """
2356
+ pos_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == pos_pin_group_name)
2357
+ if not pos_pin_group:
2358
+ self._pedb.logger.error(f"Pin group {pos_pin_group_name} not found.")
2359
+ return False
2360
+ pos_terminal = pos_pin_group.create_current_source_terminal(magnitude, phase)
2361
+ if name:
2362
+ pos_terminal.name = name
2363
+ else:
2364
+ name = generate_unique_name("isource")
2365
+ pos_terminal.name = name
2366
+ neg_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == neg_pin_group_name)
2367
+ if not neg_pin_group:
2368
+ self._pedb.logger.error(f"Pin group {pos_pin_group_name} not found.")
2369
+ return False
2370
+ neg_terminal = neg_pin_group.create_current_source_terminal()
2371
+ neg_terminal.name = f"{name}_ref"
2372
+ pos_terminal.reference_terminal = neg_terminal
2373
+ return True
2374
+
2375
+ def create_voltage_source_on_pin_group(
2376
+ self, pos_pin_group_name, neg_pin_group_name, magnitude=1, phase=0, name=None, impedance=0.001
2377
+ ):
2378
+ """Create voltage source between two pin groups.
2379
+
2380
+ Parameters
2381
+ ----------
2382
+ pos_pin_group_name : str
2383
+ Name of the positive pin group.
2384
+ neg_pin_group_name : str
2385
+ Name of the negative pin group.
2386
+ magnitude : int, float, optional
2387
+ Magnitude of the source.
2388
+ phase : int, float, optional
2389
+ Phase of the source
2390
+
2391
+ Returns
2392
+ -------
2393
+ bool
2394
+
2395
+ """
2396
+ pos_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == pos_pin_group_name)
2397
+ if not pos_pin_group:
2398
+ self._pedb.logger.error(f"Pingroup {pos_pin_group_name} not found.")
2399
+ return False
2400
+ pos_terminal = pos_pin_group.create_voltage_source_terminal(magnitude, phase, impedance)
2401
+ if name:
2402
+ pos_terminal.name = name
2403
+ else:
2404
+ name = generate_unique_name("vsource")
2405
+ pos_terminal.name = name
2406
+ neg_pin_group_name = next(pg for pg in self._pedb.layout.pin_groups if pg.name == neg_pin_group_name)
2407
+ if not neg_pin_group_name:
2408
+ self._pedb.logger.error(f"Pingroup {neg_pin_group_name} not found.")
2409
+ return False
2410
+ neg_terminal = neg_pin_group_name.create_voltage_source_terminal(magnitude, phase)
2411
+ neg_terminal.name = f"{name}_ref"
2412
+ pos_terminal.reference_terminal = neg_terminal
2413
+ return True
2414
+
2415
+ def create_voltage_probe_on_pin_group(self, probe_name, pos_pin_group_name, neg_pin_group_name, impedance=1000000):
2416
+ """Create voltage probe between two pin groups.
2417
+
2418
+ Parameters
2419
+ ----------
2420
+ probe_name : str
2421
+ Name of the probe.
2422
+ pos_pin_group_name : str
2423
+ Name of the positive pin group.
2424
+ neg_pin_group_name : str
2425
+ Name of the negative pin group.
2426
+ impedance : int, float, optional
2427
+ Phase of the source.
2428
+
2429
+ Returns
2430
+ -------
2431
+ bool
2432
+
2433
+ """
2434
+ pos_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == pos_pin_group_name)
2435
+ if not pos_pin_group:
2436
+ self._pedb.logger.error(f"Pingroup {pos_pin_group_name} not found.")
2437
+ return False
2438
+ pos_terminal = pos_pin_group.create_voltage_probe_terminal(impedance)
2439
+ if probe_name:
2440
+ pos_terminal.name = probe_name
2441
+ else:
2442
+ probe_name = generate_unique_name("vprobe")
2443
+ pos_terminal.name = probe_name
2444
+ neg_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == neg_pin_group_name)
2445
+ if not neg_pin_group:
2446
+ self._pedb.logger.error(f"Pingroup {neg_pin_group_name} not found.")
2447
+ return False
2448
+ neg_terminal = neg_pin_group.create_voltage_probe_terminal()
2449
+ neg_terminal.name = f"{probe_name}_ref"
2450
+ pos_terminal.reference_terminal = neg_terminal
2451
+ return not pos_terminal.is_null
2452
+
2453
+ def create_dc_terminal(
2454
+ self,
2455
+ component_name,
2456
+ net_name,
2457
+ source_name=None,
2458
+ ):
2459
+ """Create a dc terminal.
2460
+
2461
+ Parameters
2462
+ ----------
2463
+ component_name : str
2464
+ Name of the positive component.
2465
+ net_name : str
2466
+ Name of the positive net.
2467
+
2468
+ source_name : str, optional
2469
+ Name of the source. The default is ``""``.
2470
+
2471
+ Returns
2472
+ -------
2473
+ str
2474
+ The name of the source.
2475
+
2476
+ Examples
2477
+ --------
2478
+
2479
+ >>> from pyedb import Edb
2480
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
2481
+ >>> edb.siwave.create_dc_terminal("U2A5", "V1P5_S3", "source_name")
2482
+ """
2483
+
2484
+ node_pin = self._pedb.components.get_pin_from_component(component_name, net_name)
2485
+ if node_pin:
2486
+ node_pin = node_pin[0]
2487
+ if not source_name:
2488
+ source_name = f"DC_{component_name}_{net_name}"
2489
+ return self.create_pin_group_terminal(
2490
+ positive_pins=node_pin, name=source_name, source_type="dc_terminal", negatives_pins=None
2491
+ )
2492
+
2493
+ def create_circuit_port_on_pin_group(self, pos_pin_group_name, neg_pin_group_name, impedance=50, name=None):
2494
+ """Create a port between two pin groups.
2495
+
2496
+ Parameters
2497
+ ----------
2498
+ pos_pin_group_name : str
2499
+ Name of the positive pin group.
2500
+ neg_pin_group_name : str
2501
+ Name of the negative pin group.
2502
+ impedance : int, float, optional
2503
+ Impedance of the port. Default is ``50``.
2504
+ name : str, optional
2505
+ Port name.
2506
+
2507
+ Returns
2508
+ -------
2509
+ bool
2510
+
2511
+ """
2512
+ pos_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == pos_pin_group_name)
2513
+ if not pos_pin_group:
2514
+ self._pedb.logger.error("No positive pin group found")
2515
+ return False
2516
+ pos_terminal = pos_pin_group.create_port_terminal(impedance)
2517
+ if name: # pragma: no cover
2518
+ pos_terminal.name = name
2519
+ else:
2520
+ name = generate_unique_name("port")
2521
+ pos_terminal.name = name
2522
+ neg_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == neg_pin_group_name)
2523
+ neg_terminal = neg_pin_group.create_port_terminal(impedance)
2524
+ neg_terminal.name = f"{name}_ref"
2525
+ pos_terminal.reference_terminal = neg_terminal
2526
+ return True
2527
+
2528
+ def place_voltage_probe(
2529
+ self,
2530
+ name,
2531
+ positive_net_name,
2532
+ positive_location,
2533
+ positive_layer,
2534
+ negative_net_name,
2535
+ negative_location,
2536
+ negative_layer,
2537
+ ):
2538
+ """Place a voltage probe between two points.
2539
+
2540
+ Parameters
2541
+ ----------
2542
+ name : str,
2543
+ Name of the probe.
2544
+ positive_net_name : str
2545
+ Name of the positive net.
2546
+ positive_location : list
2547
+ Location of the positive terminal.
2548
+ positive_layer : str,
2549
+ Layer of the positive terminal.
2550
+ negative_net_name : str,
2551
+ Name of the negative net.
2552
+ negative_location : list
2553
+ Location of the negative terminal.
2554
+ negative_layer : str
2555
+ Layer of the negative terminal.
2556
+ """
2557
+ p_terminal = PointTerminal.create(
2558
+ layout=self._pedb.active_layout,
2559
+ net=positive_net_name,
2560
+ layer=positive_layer,
2561
+ name=name,
2562
+ point=GrpcPointData(positive_location),
2563
+ )
2564
+ n_terminal = PointTerminal.create(
2565
+ layout=self._pedb.active_layout,
2566
+ net=negative_net_name,
2567
+ layer=negative_layer,
2568
+ name=f"{name}_ref",
2569
+ point=GrpcPointData(negative_location),
2570
+ )
2571
+ p_terminal.reference_terminal = n_terminal
2572
+ return self._pedb.create_voltage_probe(p_terminal, n_terminal)