pyedb 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyedb might be problematic. Click here for more details.

Files changed (128) hide show
  1. pyedb/__init__.py +17 -0
  2. pyedb/dotnet/__init__.py +0 -0
  3. pyedb/dotnet/application/Variables.py +2261 -0
  4. pyedb/dotnet/application/__init__.py +0 -0
  5. pyedb/dotnet/clr_module.py +103 -0
  6. pyedb/dotnet/edb.py +4237 -0
  7. pyedb/dotnet/edb_core/__init__.py +1 -0
  8. pyedb/dotnet/edb_core/cell/__init__.py +0 -0
  9. pyedb/dotnet/edb_core/cell/hierarchy/__init__.py +0 -0
  10. pyedb/dotnet/edb_core/cell/hierarchy/model.py +66 -0
  11. pyedb/dotnet/edb_core/components.py +2669 -0
  12. pyedb/dotnet/edb_core/configuration.py +423 -0
  13. pyedb/dotnet/edb_core/definition/__init__.py +0 -0
  14. pyedb/dotnet/edb_core/definition/component_def.py +166 -0
  15. pyedb/dotnet/edb_core/definition/component_model.py +30 -0
  16. pyedb/dotnet/edb_core/definition/definition_obj.py +18 -0
  17. pyedb/dotnet/edb_core/definition/definitions.py +12 -0
  18. pyedb/dotnet/edb_core/dotnet/__init__.py +0 -0
  19. pyedb/dotnet/edb_core/dotnet/database.py +1218 -0
  20. pyedb/dotnet/edb_core/dotnet/layout.py +238 -0
  21. pyedb/dotnet/edb_core/dotnet/primitive.py +1517 -0
  22. pyedb/dotnet/edb_core/edb_data/__init__.py +0 -0
  23. pyedb/dotnet/edb_core/edb_data/components_data.py +938 -0
  24. pyedb/dotnet/edb_core/edb_data/connectable.py +113 -0
  25. pyedb/dotnet/edb_core/edb_data/control_file.py +1268 -0
  26. pyedb/dotnet/edb_core/edb_data/design_options.py +35 -0
  27. pyedb/dotnet/edb_core/edb_data/edbvalue.py +45 -0
  28. pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +330 -0
  29. pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +1607 -0
  30. pyedb/dotnet/edb_core/edb_data/layer_data.py +576 -0
  31. pyedb/dotnet/edb_core/edb_data/nets_data.py +281 -0
  32. pyedb/dotnet/edb_core/edb_data/obj_base.py +19 -0
  33. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +2080 -0
  34. pyedb/dotnet/edb_core/edb_data/ports.py +287 -0
  35. pyedb/dotnet/edb_core/edb_data/primitives_data.py +1397 -0
  36. pyedb/dotnet/edb_core/edb_data/simulation_configuration.py +2914 -0
  37. pyedb/dotnet/edb_core/edb_data/simulation_setup.py +716 -0
  38. pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +1205 -0
  39. pyedb/dotnet/edb_core/edb_data/sources.py +514 -0
  40. pyedb/dotnet/edb_core/edb_data/terminals.py +632 -0
  41. pyedb/dotnet/edb_core/edb_data/utilities.py +148 -0
  42. pyedb/dotnet/edb_core/edb_data/variables.py +91 -0
  43. pyedb/dotnet/edb_core/general.py +181 -0
  44. pyedb/dotnet/edb_core/hfss.py +1646 -0
  45. pyedb/dotnet/edb_core/layout.py +1244 -0
  46. pyedb/dotnet/edb_core/layout_validation.py +272 -0
  47. pyedb/dotnet/edb_core/materials.py +939 -0
  48. pyedb/dotnet/edb_core/net_class.py +335 -0
  49. pyedb/dotnet/edb_core/nets.py +1215 -0
  50. pyedb/dotnet/edb_core/padstack.py +1389 -0
  51. pyedb/dotnet/edb_core/siwave.py +1427 -0
  52. pyedb/dotnet/edb_core/stackup.py +2703 -0
  53. pyedb/edb_logger.py +396 -0
  54. pyedb/generic/__init__.py +0 -0
  55. pyedb/generic/constants.py +1063 -0
  56. pyedb/generic/data_handlers.py +320 -0
  57. pyedb/generic/design_types.py +104 -0
  58. pyedb/generic/filesystem.py +150 -0
  59. pyedb/generic/general_methods.py +1535 -0
  60. pyedb/generic/plot.py +1840 -0
  61. pyedb/generic/process.py +285 -0
  62. pyedb/generic/settings.py +224 -0
  63. pyedb/ipc2581/__init__.py +0 -0
  64. pyedb/ipc2581/bom/__init__.py +0 -0
  65. pyedb/ipc2581/bom/bom.py +21 -0
  66. pyedb/ipc2581/bom/bom_item.py +32 -0
  67. pyedb/ipc2581/bom/characteristics.py +37 -0
  68. pyedb/ipc2581/bom/refdes.py +16 -0
  69. pyedb/ipc2581/content/__init__.py +0 -0
  70. pyedb/ipc2581/content/color.py +38 -0
  71. pyedb/ipc2581/content/content.py +55 -0
  72. pyedb/ipc2581/content/dictionary_color.py +29 -0
  73. pyedb/ipc2581/content/dictionary_fill.py +28 -0
  74. pyedb/ipc2581/content/dictionary_line.py +30 -0
  75. pyedb/ipc2581/content/entry_color.py +13 -0
  76. pyedb/ipc2581/content/entry_line.py +14 -0
  77. pyedb/ipc2581/content/fill.py +15 -0
  78. pyedb/ipc2581/content/layer_ref.py +10 -0
  79. pyedb/ipc2581/content/standard_geometries_dictionary.py +72 -0
  80. pyedb/ipc2581/ecad/__init__.py +0 -0
  81. pyedb/ipc2581/ecad/cad_data/__init__.py +0 -0
  82. pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +26 -0
  83. pyedb/ipc2581/ecad/cad_data/cad_data.py +37 -0
  84. pyedb/ipc2581/ecad/cad_data/component.py +41 -0
  85. pyedb/ipc2581/ecad/cad_data/drill.py +30 -0
  86. pyedb/ipc2581/ecad/cad_data/feature.py +54 -0
  87. pyedb/ipc2581/ecad/cad_data/layer.py +41 -0
  88. pyedb/ipc2581/ecad/cad_data/layer_feature.py +151 -0
  89. pyedb/ipc2581/ecad/cad_data/logical_net.py +32 -0
  90. pyedb/ipc2581/ecad/cad_data/outline.py +25 -0
  91. pyedb/ipc2581/ecad/cad_data/package.py +104 -0
  92. pyedb/ipc2581/ecad/cad_data/padstack_def.py +38 -0
  93. pyedb/ipc2581/ecad/cad_data/padstack_hole_def.py +24 -0
  94. pyedb/ipc2581/ecad/cad_data/padstack_instance.py +62 -0
  95. pyedb/ipc2581/ecad/cad_data/padstack_pad_def.py +26 -0
  96. pyedb/ipc2581/ecad/cad_data/path.py +89 -0
  97. pyedb/ipc2581/ecad/cad_data/phy_net.py +80 -0
  98. pyedb/ipc2581/ecad/cad_data/pin.py +31 -0
  99. pyedb/ipc2581/ecad/cad_data/polygon.py +169 -0
  100. pyedb/ipc2581/ecad/cad_data/profile.py +40 -0
  101. pyedb/ipc2581/ecad/cad_data/stackup.py +31 -0
  102. pyedb/ipc2581/ecad/cad_data/stackup_group.py +42 -0
  103. pyedb/ipc2581/ecad/cad_data/stackup_layer.py +21 -0
  104. pyedb/ipc2581/ecad/cad_data/step.py +275 -0
  105. pyedb/ipc2581/ecad/cad_header.py +33 -0
  106. pyedb/ipc2581/ecad/ecad.py +19 -0
  107. pyedb/ipc2581/ecad/spec.py +46 -0
  108. pyedb/ipc2581/history_record.py +37 -0
  109. pyedb/ipc2581/ipc2581.py +387 -0
  110. pyedb/ipc2581/logistic_header.py +25 -0
  111. pyedb/misc/__init__.py +0 -0
  112. pyedb/misc/aedtlib_personalib_install.py +14 -0
  113. pyedb/misc/downloads.py +322 -0
  114. pyedb/misc/misc.py +67 -0
  115. pyedb/misc/pyedb.runtimeconfig.json +13 -0
  116. pyedb/misc/siw_feature_config/__init__.py +0 -0
  117. pyedb/misc/siw_feature_config/emc/__init__.py +0 -0
  118. pyedb/misc/siw_feature_config/emc/component_tags.py +46 -0
  119. pyedb/misc/siw_feature_config/emc/net_tags.py +37 -0
  120. pyedb/misc/siw_feature_config/emc/tag_library.py +62 -0
  121. pyedb/misc/siw_feature_config/emc/xml_generic.py +78 -0
  122. pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +179 -0
  123. pyedb/misc/utilities.py +27 -0
  124. pyedb/modeler/geometry_operators.py +2082 -0
  125. pyedb-0.2.0.dist-info/LICENSE +21 -0
  126. pyedb-0.2.0.dist-info/METADATA +208 -0
  127. pyedb-0.2.0.dist-info/RECORD +128 -0
  128. pyedb-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,1646 @@
1
+ """
2
+ This module contains the ``EdbHfss`` class.
3
+ """
4
+ import math
5
+
6
+ from pyedb.dotnet.edb_core.edb_data.hfss_extent_info import HfssExtentInfo
7
+ from pyedb.dotnet.edb_core.edb_data.ports import BundleWavePort, WavePort
8
+ from pyedb.dotnet.edb_core.edb_data.primitives_data import EDBPrimitives
9
+ from pyedb.dotnet.edb_core.edb_data.simulation_configuration import (
10
+ SimulationConfiguration,
11
+ )
12
+ from pyedb.dotnet.edb_core.general import (
13
+ convert_py_list_to_net_list,
14
+ convert_pytuple_to_nettuple,
15
+ )
16
+ from pyedb.generic.constants import RadiationBoxType, SweepType
17
+ from pyedb.generic.general_methods import (
18
+ generate_unique_name,
19
+ is_ironpython,
20
+ pyedb_function_handler,
21
+ )
22
+ from pyedb.modeler.geometry_operators import GeometryOperators
23
+
24
+
25
+ class EdbHfss(object):
26
+ """Manages EDB method to configure Hfss setup accessible from `Edb.hfss` property.
27
+
28
+ Examples
29
+ --------
30
+ >>> from pyedb import Edb
31
+ >>> edbapp = Edb("myaedbfolder")
32
+ >>> edb_hfss = edb_3dedbapp.hfss
33
+ """
34
+
35
+ def __init__(self, p_edb):
36
+ self._pedb = p_edb
37
+
38
+ @property
39
+ def hfss_extent_info(self):
40
+ """HFSS extent information."""
41
+ return HfssExtentInfo(self._pedb)
42
+
43
+ @property
44
+ def _logger(self):
45
+ return self._pedb.logger
46
+
47
+ @property
48
+ def _edb(self):
49
+ """EDB object.
50
+
51
+ Returns
52
+ -------
53
+ Ansys.Ansoft.Edb
54
+ """
55
+ return self._pedb.edb_api
56
+
57
+ @property
58
+ def _active_layout(self):
59
+ return self._pedb.active_layout
60
+
61
+ @property
62
+ def _layout(self):
63
+ return self._pedb.layout
64
+
65
+ @property
66
+ def _cell(self):
67
+ return self._pedb.cell
68
+
69
+ @property
70
+ def _db(self):
71
+ return self._pedb.active_db
72
+
73
+ @property
74
+ def excitations(self):
75
+ """Get all excitations."""
76
+ return self._pedb.excitations
77
+
78
+ @property
79
+ def sources(self):
80
+ """Get all sources."""
81
+ return self._pedb.sources
82
+
83
+ @property
84
+ def probes(self):
85
+ """Get all probes."""
86
+ return self._pedb.probes
87
+
88
+ def _get_edb_value(self, value):
89
+ return self._pedb.edb_value(value)
90
+
91
+ @pyedb_function_handler()
92
+ def _create_edge_terminal(self, prim_id, point_on_edge, terminal_name=None, is_ref=False):
93
+ """Create an edge terminal.
94
+
95
+ Parameters
96
+ ----------
97
+ prim_id : int
98
+ Primitive ID.
99
+ point_on_edge : list
100
+ Coordinate of the point to define the edge terminal.
101
+ The point must be on the target edge but not on the two
102
+ ends of the edge.
103
+ terminal_name : str, optional
104
+ Name of the terminal. The default is ``None``, in which case the
105
+ default name is assigned.
106
+ is_ref : bool, optional
107
+ Whether it is a reference terminal. The default is ``False``.
108
+
109
+ Returns
110
+ -------
111
+ Edb.Cell.Terminal.EdgeTerminal
112
+ """
113
+ if not terminal_name:
114
+ terminal_name = generate_unique_name("Terminal_")
115
+ if isinstance(point_on_edge, (list, tuple)):
116
+ point_on_edge = self._edb.geometry.point_data(
117
+ self._get_edb_value(point_on_edge[0]), self._get_edb_value(point_on_edge[1])
118
+ )
119
+ if hasattr(prim_id, "GetId"):
120
+ prim = prim_id
121
+ else:
122
+ prim = [i for i in self._pedb.modeler.primitives if i.id == prim_id][0].primitive_object
123
+ pos_edge = self._edb.cell.terminal.PrimitiveEdge.Create(prim, point_on_edge)
124
+ pos_edge = convert_py_list_to_net_list(pos_edge, self._edb.cell.terminal.Edge)
125
+ return self._edb.cell.terminal.EdgeTerminal.Create(
126
+ prim.GetLayout(), prim.GetNet(), terminal_name, pos_edge, isRef=is_ref
127
+ )
128
+
129
+ @pyedb_function_handler()
130
+ def get_trace_width_for_traces_with_ports(self):
131
+ """Retrieve the trace width for traces with ports.
132
+
133
+ Returns
134
+ -------<
135
+ dict
136
+ Dictionary of trace width data.
137
+ """
138
+ nets = {}
139
+ for net in self._pedb.excitations_nets:
140
+ smallest = self._pedb.nets[net].get_smallest_trace_width()
141
+ if smallest < 1e10:
142
+ nets[net] = self._pedb.nets[net].get_smallest_trace_width()
143
+ return nets
144
+
145
+ @pyedb_function_handler()
146
+ def create_circuit_port_on_pin(self, pos_pin, neg_pin, impedance=50, port_name=None):
147
+ """Create Circuit Port on Pin.
148
+
149
+ Parameters
150
+ ----------
151
+ pos_pin : Object
152
+ Edb Pin
153
+ neg_pin : Object
154
+ Edb Pin
155
+ impedance : float
156
+ Port Impedance
157
+ port_name : str, optional
158
+ Port Name
159
+
160
+ >>> from pyedb import Edb
161
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
162
+ >>> pins =edbapp.components.get_pin_from_component("U2A5")
163
+ >>> edbapp.hfss.create_circuit_port_on_pin(pins[0], pins[1],50,"port_name")
164
+
165
+ Returns
166
+ -------
167
+ str
168
+ Port Name.
169
+
170
+ """
171
+ return self._pedb.siwave.create_circuit_port_on_pin(pos_pin, neg_pin, impedance, port_name)
172
+
173
+ @pyedb_function_handler()
174
+ def create_voltage_source_on_pin(self, pos_pin, neg_pin, voltage_value=3.3, phase_value=0, source_name=""):
175
+ """Create a voltage source.
176
+
177
+ Parameters
178
+ ----------
179
+ pos_pin : Object
180
+ Positive Pin.
181
+ neg_pin : Object
182
+ Negative Pin.
183
+ voltage_value : float, optional
184
+ Value for the voltage. The default is ``3.3``.
185
+ phase_value : optional
186
+ Value for the phase. The default is ``0``.
187
+ source_name : str, optional
188
+ Name of the source. The default is ``""``.
189
+
190
+ Returns
191
+ -------
192
+ str
193
+ Source Name.
194
+
195
+ Examples
196
+ --------
197
+
198
+ >>> from pyedb import Edb
199
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
200
+ >>> pins =edbapp.components.get_pin_from_component("U2A5")
201
+ >>> edbapp.hfss.create_voltage_source_on_pin(pins[0], pins[1],50,"source_name")
202
+ """
203
+ return self._pedb.siwave.create_voltage_source_on_pin(pos_pin, neg_pin, voltage_value, phase_value, source_name)
204
+
205
+ @pyedb_function_handler()
206
+ def create_current_source_on_pin(self, pos_pin, neg_pin, current_value=0.1, phase_value=0, source_name=""):
207
+ """Create a current source.
208
+
209
+ Parameters
210
+ ----------
211
+ pos_pin : Object
212
+ Positive Pin.
213
+ neg_pin : Object
214
+ Negative Pin.
215
+ current_value : float, optional
216
+ Value for the current. The default is ``0.1``.
217
+ phase_value : optional
218
+ Value for the phase. The default is ``0``.
219
+ source_name : str, optional
220
+ Name of the source. The default is ``""``.
221
+
222
+ Returns
223
+ -------
224
+ str
225
+ Source Name.
226
+
227
+ Examples
228
+ --------
229
+
230
+ >>> from pyedb import Edb
231
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
232
+ >>> pins =edbapp.components.get_pin_from_component("U2A5")
233
+ >>> edbapp.hfss.create_current_source_on_pin(pins[0], pins[1],50,"source_name")
234
+ """
235
+
236
+ return self._pedb.siwave.create_current_source_on_pin(pos_pin, neg_pin, current_value, phase_value, source_name)
237
+
238
+ @pyedb_function_handler()
239
+ def create_resistor_on_pin(self, pos_pin, neg_pin, rvalue=1, resistor_name=""):
240
+ """Create a Resistor boundary between two given pins.
241
+
242
+ Parameters
243
+ ----------
244
+ pos_pin : Object
245
+ Positive Pin.
246
+ neg_pin : Object
247
+ Negative Pin.
248
+ rvalue : float, optional
249
+ Resistance value. The default is ``1``.
250
+ resistor_name : str, optional
251
+ Name of the resistor. The default is ``""``.
252
+
253
+ Returns
254
+ -------
255
+ str
256
+ Name of the Resistor.
257
+
258
+ Examples
259
+ --------
260
+
261
+ >>> from pyedb import Edb
262
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
263
+ >>> pins =edbapp.components.get_pin_from_component("U2A5")
264
+ >>> edbapp.hfss.create_resistor_on_pin(pins[0], pins[1],50,"res_name")
265
+ """
266
+ return self._pedb.siwave.create_resistor_on_pin(pos_pin, neg_pin, rvalue, resistor_name)
267
+
268
+ @pyedb_function_handler()
269
+ def create_circuit_port_on_net(
270
+ self,
271
+ positive_component_name,
272
+ positive_net_name,
273
+ negative_component_name=None,
274
+ negative_net_name="GND",
275
+ impedance_value=50,
276
+ port_name="",
277
+ ):
278
+ """Create a circuit port on a NET.
279
+ It groups all pins belonging to the specified net and then applies the port on PinGroups.
280
+
281
+ Parameters
282
+ ----------
283
+ positive_component_name : str
284
+ Name of the positive component.
285
+ positive_net_name : str
286
+ Name of the positive net.
287
+ negative_component_name : str, optional
288
+ Name of the negative component. The default is ``None``, in which case the name of
289
+ the positive net is assigned.
290
+ negative_net_name : str, optional
291
+ Name of the negative net name. The default is ``"GND"``.
292
+ impedance_value : float, optional
293
+ Port impedance value. The default is ``50``.
294
+ port_name : str, optional
295
+ Name of the port. The default is ``""``.
296
+
297
+ Returns
298
+ -------
299
+ str
300
+ The name of the port.
301
+
302
+ Examples
303
+ --------
304
+ >>> from pyedb import Edb
305
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
306
+ >>> edbapp.hfss.create_circuit_port_on_net("U2A5", "V1P5_S3", "U2A5", "GND", 50, "port_name")
307
+ """
308
+ return self._pedb.siwave.create_circuit_port_on_net(
309
+ positive_component_name,
310
+ positive_net_name,
311
+ negative_component_name,
312
+ negative_net_name,
313
+ impedance_value,
314
+ port_name,
315
+ )
316
+
317
+ @pyedb_function_handler()
318
+ def create_voltage_source_on_net(
319
+ self,
320
+ positive_component_name,
321
+ positive_net_name,
322
+ negative_component_name=None,
323
+ negative_net_name="GND",
324
+ voltage_value=3.3,
325
+ phase_value=0,
326
+ source_name="",
327
+ ):
328
+ """Create a voltage source.
329
+
330
+ Parameters
331
+ ----------
332
+ positive_component_name : str
333
+ Name of the positive component.
334
+ positive_net_name : str
335
+ Name of the positive net.
336
+ negative_component_name : str, optional
337
+ Name of the negative component. The default is ``None``, in which case the name of
338
+ the positive net is assigned.
339
+ negative_net_name : str, optional
340
+ Name of the negative net. The default is ``"GND"``.
341
+ voltage_value : float, optional
342
+ Value for the voltage. The default is ``3.3``.
343
+ phase_value : optional
344
+ Value for the phase. The default is ``0``.
345
+ source_name : str, optional
346
+ Name of the source. The default is ``""``.
347
+
348
+ Returns
349
+ -------
350
+ str
351
+ Source Name.
352
+
353
+ Examples
354
+ --------
355
+
356
+ >>> from pyedb import Edb
357
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
358
+ >>> edb.hfss.create_voltage_source_on_net("U2A5", "V1P5_S3", "U2A5", "GND", 3.3, 0, "source_name")
359
+ """
360
+ return self._pedb.siwave.create_voltage_source_on_net(
361
+ positive_component_name,
362
+ positive_net_name,
363
+ negative_component_name,
364
+ negative_net_name,
365
+ voltage_value,
366
+ phase_value,
367
+ source_name,
368
+ )
369
+
370
+ @pyedb_function_handler()
371
+ def create_current_source_on_net(
372
+ self,
373
+ positive_component_name,
374
+ positive_net_name,
375
+ negative_component_name=None,
376
+ negative_net_name="GND",
377
+ current_value=0.1,
378
+ phase_value=0,
379
+ source_name="",
380
+ ):
381
+ """Create a current source.
382
+
383
+ Parameters
384
+ ----------
385
+ positive_component_name : str
386
+ Name of the positive component.
387
+ positive_net_name : str
388
+ Name of the positive net.
389
+ negative_component_name : str, optional
390
+ Name of the negative component. The default is ``None``, in which case the name of
391
+ the positive net is assigned.
392
+ negative_net_name : str, optional
393
+ Name of the negative net. The default is ``"GND"``.
394
+ current_value : float, optional
395
+ Value for the current. The default is ``0.1``.
396
+ phase_value : optional
397
+ Value for the phase. The default is ``0``.
398
+ source_name : str, optional
399
+ Name of the source. The default is ``""``.
400
+
401
+ Returns
402
+ -------
403
+ str
404
+ Source Name.
405
+
406
+ Examples
407
+ --------
408
+
409
+ >>> from pyedb import Edb
410
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
411
+ >>> edb.hfss.create_current_source_on_net("U2A5", "V1P5_S3", "U2A5", "GND", 0.1, 0, "source_name")
412
+ """
413
+ return self._pedb.siwave.create_current_source_on_net(
414
+ positive_component_name,
415
+ positive_net_name,
416
+ negative_component_name,
417
+ negative_net_name,
418
+ current_value,
419
+ phase_value,
420
+ source_name,
421
+ )
422
+
423
+ @pyedb_function_handler()
424
+ def create_coax_port_on_component(self, ref_des_list, net_list):
425
+ """Create a coaxial port on a component or component list on a net or net list.
426
+ The name of the new coaxial port is automatically assigned.
427
+
428
+ Parameters
429
+ ----------
430
+ ref_des_list : list, str
431
+ List of one or more reference designators.
432
+
433
+ net_list : list, str
434
+ List of one or more nets.
435
+
436
+ Returns
437
+ -------
438
+ bool
439
+ ``True`` when successful, ``False`` when failed.
440
+
441
+ """
442
+ coax = []
443
+ if not isinstance(ref_des_list, list):
444
+ ref_des_list = [ref_des_list]
445
+ if not isinstance(net_list, list):
446
+ net_list = [net_list]
447
+ for ref in ref_des_list:
448
+ for _, py_inst in self._pedb.components.components[ref].pins.items():
449
+ if py_inst.net_name in net_list and py_inst.is_pin:
450
+ port_name = "{}_{}_{}".format(ref, py_inst.net_name, py_inst.pin.GetName())
451
+ (
452
+ res,
453
+ from_layer_pos,
454
+ to_layer_pos,
455
+ ) = py_inst.pin.GetLayerRange()
456
+ if (
457
+ res
458
+ and from_layer_pos
459
+ and self._edb.cell.terminal.PadstackInstanceTerminal.Create(
460
+ self._active_layout,
461
+ py_inst.pin.GetNet(),
462
+ port_name,
463
+ py_inst.pin,
464
+ to_layer_pos,
465
+ )
466
+ ):
467
+ coax.append(port_name)
468
+ return coax
469
+
470
+ @pyedb_function_handler()
471
+ def create_differential_wave_port(
472
+ self,
473
+ positive_primitive_id,
474
+ positive_points_on_edge,
475
+ negative_primitive_id,
476
+ negative_points_on_edge,
477
+ port_name=None,
478
+ horizontal_extent_factor=5,
479
+ vertical_extent_factor=3,
480
+ pec_launch_width="0.01mm",
481
+ ):
482
+ """Create a differential wave port.
483
+
484
+ Parameters
485
+ ----------
486
+ positive_primitive_id : int, EDBPrimitives
487
+ Primitive ID of the positive terminal.
488
+ positive_points_on_edge : list
489
+ Coordinate of the point to define the edge terminal.
490
+ The point must be close to the target edge but not on the two
491
+ ends of the edge.
492
+ negative_primitive_id : int, EDBPrimitives
493
+ Primitive ID of the negative terminal.
494
+ negative_points_on_edge : list
495
+ Coordinate of the point to define the edge terminal.
496
+ The point must be close to the target edge but not on the two
497
+ ends of the edge.
498
+ port_name : str, optional
499
+ Name of the port. The default is ``None``.
500
+ horizontal_extent_factor : int, float, optional
501
+ Horizontal extent factor. The default value is ``5``.
502
+ vertical_extent_factor : int, float, optional
503
+ Vertical extent factor. The default value is ``3``.
504
+ pec_launch_width : str, optional
505
+ Launch Width of PEC. The default value is ``"0.01mm"``.
506
+
507
+ Returns
508
+ -------
509
+ tuple
510
+ The tuple contains: (port_name, pyedb.dotnet.edb_core.edb_data.sources.ExcitationDifferential).
511
+
512
+ Examples
513
+ --------
514
+ >>> edb.hfss.create_differential_wave_port(0, ["-50mm", "-0mm"], 1, ["-50mm", "-0.2mm"])
515
+ """
516
+ if not port_name:
517
+ port_name = generate_unique_name("diff")
518
+
519
+ if isinstance(positive_primitive_id, EDBPrimitives):
520
+ positive_primitive_id = positive_primitive_id.id
521
+
522
+ if isinstance(negative_primitive_id, EDBPrimitives):
523
+ negative_primitive_id = negative_primitive_id.id
524
+
525
+ _, pos_term = self.create_wave_port(
526
+ positive_primitive_id,
527
+ positive_points_on_edge,
528
+ horizontal_extent_factor=horizontal_extent_factor,
529
+ vertical_extent_factor=vertical_extent_factor,
530
+ pec_launch_width=pec_launch_width,
531
+ )
532
+ _, neg_term = self.create_wave_port(
533
+ negative_primitive_id,
534
+ negative_points_on_edge,
535
+ horizontal_extent_factor=horizontal_extent_factor,
536
+ vertical_extent_factor=vertical_extent_factor,
537
+ pec_launch_width=pec_launch_width,
538
+ )
539
+ edb_list = convert_py_list_to_net_list(
540
+ [pos_term._edb_object, neg_term._edb_object], self._edb.cell.terminal.Terminal
541
+ )
542
+ _edb_boundle_terminal = self._edb.cell.terminal.BundleTerminal.Create(edb_list)
543
+ # _edb_boundle_terminal.SetName("Wave_"+port_name)
544
+ pos_term._edb_object.SetName(port_name)
545
+ return port_name, BundleWavePort(self._pedb, _edb_boundle_terminal)
546
+
547
+ @pyedb_function_handler()
548
+ def create_bundle_wave_port(
549
+ self,
550
+ primitives_id,
551
+ points_on_edge,
552
+ port_name=None,
553
+ horizontal_extent_factor=5,
554
+ vertical_extent_factor=3,
555
+ pec_launch_width="0.01mm",
556
+ ):
557
+ """Create a bundle wave port.
558
+
559
+ Parameters
560
+ ----------
561
+ primitives_id : list
562
+ Primitive ID of the positive terminal.
563
+ points_on_edge : list
564
+ Coordinate of the point to define the edge terminal.
565
+ The point must be close to the target edge but not on the two
566
+ ends of the edge.
567
+ port_name : str, optional
568
+ Name of the port. The default is ``None``.
569
+ horizontal_extent_factor : int, float, optional
570
+ Horizontal extent factor. The default value is ``5``.
571
+ vertical_extent_factor : int, float, optional
572
+ Vertical extent factor. The default value is ``3``.
573
+ pec_launch_width : str, optional
574
+ Launch Width of PEC. The default value is ``"0.01mm"``.
575
+
576
+ Returns
577
+ -------
578
+ tuple
579
+ The tuple contains: (port_name, pyedb.egacy.edb_core.edb_data.sources.ExcitationDifferential).
580
+
581
+ Examples
582
+ --------
583
+ >>> edb.hfss.create_bundle_wave_port(0, ["-50mm", "-0mm"], 1, ["-50mm", "-0.2mm"])
584
+ """
585
+ if not port_name:
586
+ port_name = generate_unique_name("bundle_port")
587
+
588
+ if isinstance(primitives_id[0], EDBPrimitives):
589
+ primitives_id = [i.id for i in primitives_id]
590
+
591
+ terminals = []
592
+ _port_name = port_name
593
+ for p_id, loc in list(zip(primitives_id, points_on_edge)):
594
+ _, term = self.create_wave_port(
595
+ p_id,
596
+ loc,
597
+ port_name=_port_name,
598
+ horizontal_extent_factor=horizontal_extent_factor,
599
+ vertical_extent_factor=vertical_extent_factor,
600
+ pec_launch_width=pec_launch_width,
601
+ )
602
+ _port_name = None
603
+ terminals.append(term)
604
+
605
+ edb_list = convert_py_list_to_net_list([i._edb_object for i in terminals], self._edb.cell.terminal.Terminal)
606
+ _edb_bundle_terminal = self._edb.cell.terminal.BundleTerminal.Create(edb_list)
607
+ return port_name, BundleWavePort(self._pedb, _edb_bundle_terminal)
608
+
609
+ @pyedb_function_handler()
610
+ def create_hfss_ports_on_padstack(self, pinpos, portname=None):
611
+ """Create an HFSS port on a padstack.
612
+
613
+ Parameters
614
+ ----------
615
+ pinpos :
616
+ Position of the pin.
617
+
618
+ portname : str, optional
619
+ Name of the port. The default is ``None``.
620
+
621
+ Returns
622
+ -------
623
+ bool
624
+ ``True`` when successful, ``False`` when failed.
625
+ """
626
+ res, fromLayer_pos, toLayer_pos = pinpos.GetLayerRange()
627
+
628
+ if not portname:
629
+ portname = generate_unique_name("Port_" + pinpos.GetNet().GetName())
630
+ edbpointTerm_pos = self._edb.cell.terminal.PadstackInstanceTerminal.Create(
631
+ self._active_layout, pinpos.GetNet(), portname, pinpos, toLayer_pos
632
+ )
633
+ if edbpointTerm_pos:
634
+ return True
635
+ else:
636
+ return False
637
+
638
+ @pyedb_function_handler()
639
+ def create_edge_port_on_polygon(
640
+ self,
641
+ polygon=None,
642
+ reference_polygon=None,
643
+ terminal_point=None,
644
+ reference_point=None,
645
+ reference_layer=None,
646
+ port_name=None,
647
+ port_impedance=50.0,
648
+ force_circuit_port=False,
649
+ ):
650
+ """Create lumped port between two edges from two different polygons. Can also create a vertical port when
651
+ the reference layer name is only provided. When a port is created between two edge from two polygons which don't
652
+ belong to the same layer, a circuit port will be automatically created instead of lumped. To enforce the circuit
653
+ port instead of lumped,use the boolean force_circuit_port.
654
+
655
+ Parameters
656
+ ----------
657
+ polygon : The EDB polygon object used to assign the port.
658
+ Edb.Cell.Primitive.Polygon object.
659
+
660
+ reference_polygon : The EDB polygon object used to define the port reference.
661
+ Edb.Cell.Primitive.Polygon object.
662
+
663
+ terminal_point : The coordinate of the point to define the edge terminal of the port. This point must be
664
+ located on the edge of the polygon where the port has to be placed. For instance taking the middle point
665
+ of an edge is a good practice but any point of the edge should be valid. Taking a corner might cause unwanted
666
+ port location.
667
+ list[float, float] with values provided in meter.
668
+
669
+ reference_point : same as terminal_point but used for defining the reference location on the edge.
670
+ list[float, float] with values provided in meter.
671
+
672
+ reference_layer : Name used to define port reference for vertical ports.
673
+ str the layer name.
674
+
675
+ port_name : Name of the port.
676
+ str.
677
+
678
+ port_impedance : port impedance value. Default value is 50 Ohms.
679
+ float, impedance value.
680
+
681
+ force_circuit_port ; used to force circuit port creation instead of lumped. Works for vertical and coplanar
682
+ ports.
683
+
684
+ Examples
685
+ --------
686
+
687
+ >>> edb_path = path_to_edb
688
+ >>> edb = Edb(edb_path)
689
+ >>> poly_list = [poly for poly in list(edb.layout.primitives) if poly.GetPrimitiveType() == 2]
690
+ >>> port_poly = [poly for poly in poly_list if poly.GetId() == 17][0]
691
+ >>> ref_poly = [poly for poly in poly_list if poly.GetId() == 19][0]
692
+ >>> port_location = [-65e-3, -13e-3]
693
+ >>> ref_location = [-63e-3, -13e-3]
694
+ >>> edb.hfss.create_edge_port_on_polygon(polygon=port_poly, reference_polygon=ref_poly,
695
+ >>> terminal_point=port_location, reference_point=ref_location)
696
+
697
+ """
698
+ if not polygon:
699
+ self._logger.error("No polygon provided for port {} creation".format(port_name))
700
+ return False
701
+ if reference_layer:
702
+ reference_layer = self._pedb.stackup.signal_layers[reference_layer]._edb_layer
703
+ if not reference_layer:
704
+ self._logger.error("Specified layer for port {} creation was not found".format(port_name))
705
+ if not isinstance(terminal_point, list):
706
+ self._logger.error("Terminal point must be a list of float with providing the point location in meter")
707
+ return False
708
+ terminal_point = self._edb.geometry.point_data(
709
+ self._get_edb_value(terminal_point[0]), self._get_edb_value(terminal_point[1])
710
+ )
711
+ if reference_point and isinstance(reference_point, list):
712
+ reference_point = self._edb.geometry.point_data(
713
+ self._get_edb_value(reference_point[0]), self._get_edb_value(reference_point[1])
714
+ )
715
+ if not port_name:
716
+ port_name = generate_unique_name("Port_")
717
+ edge = self._edb.cell.terminal.PrimitiveEdge.Create(polygon.prim_obj, terminal_point)
718
+ edges = convert_py_list_to_net_list(edge, self._edb.cell.terminal.Edge)
719
+ edge_term = self._edb.cell.terminal.EdgeTerminal.Create(
720
+ polygon.GetLayout(), polygon.GetNet(), port_name, edges, isRef=False
721
+ )
722
+ if force_circuit_port:
723
+ edge_term.SetIsCircuitPort(True)
724
+ else:
725
+ edge_term.SetIsCircuitPort(False)
726
+
727
+ if port_impedance:
728
+ edge_term.SetImpedance(self._pedb.edb_value(port_impedance))
729
+ edge_term.SetName(port_name)
730
+ if reference_polygon and reference_point:
731
+ ref_edge = self._edb.cell.terminal.PrimitiveEdge.Create(reference_polygon.prim_obj, reference_point)
732
+ ref_edges = convert_py_list_to_net_list(ref_edge, self._edb.cell.terminal.Edge)
733
+ ref_edge_term = self._edb.cell.terminal.EdgeTerminal.Create(
734
+ reference_polygon.GetLayout(), reference_polygon.GetNet(), port_name + "_ref", ref_edges, isRef=True
735
+ )
736
+ if reference_layer:
737
+ ref_edge_term.SetReferenceLayer(reference_layer)
738
+ if force_circuit_port:
739
+ ref_edge_term.SetIsCircuitPort(True)
740
+ else:
741
+ ref_edge_term.SetIsCircuitPort(False)
742
+
743
+ if port_impedance:
744
+ ref_edge_term.SetImpedance(self._pedb.edb_value(port_impedance))
745
+ edge_term.SetReferenceTerminal(ref_edge_term)
746
+ return True
747
+
748
+ @pyedb_function_handler()
749
+ def create_wave_port(
750
+ self,
751
+ prim_id,
752
+ point_on_edge,
753
+ port_name=None,
754
+ impedance=50,
755
+ horizontal_extent_factor=5,
756
+ vertical_extent_factor=3,
757
+ pec_launch_width="0.01mm",
758
+ ):
759
+ """Create a wave port.
760
+
761
+ Parameters
762
+ ----------
763
+ prim_id : int, EDBPrimitives
764
+ Primitive ID.
765
+ point_on_edge : list
766
+ Coordinate of the point to define the edge terminal.
767
+ The point must be on the target edge but not on the two
768
+ ends of the edge.
769
+ port_name : str, optional
770
+ Name of the port. The default is ``None``.
771
+ impedance : int, float, optional
772
+ Impedance of the port. The default value is ``50``.
773
+ horizontal_extent_factor : int, float, optional
774
+ Horizontal extent factor. The default value is ``5``.
775
+ vertical_extent_factor : int, float, optional
776
+ Vertical extent factor. The default value is ``3``.
777
+ pec_launch_width : str, optional
778
+ Launch Width of PEC. The default value is ``"0.01mm"``.
779
+
780
+ Returns
781
+ -------
782
+ tuple
783
+ The tuple contains: (Port name, pyedb.dotnet.edb_core.edb_data.sources.Excitation).
784
+
785
+ Examples
786
+ --------
787
+ >>> edb.hfss.create_wave_port(0, ["-50mm", "-0mm"])
788
+ """
789
+ if not port_name:
790
+ port_name = generate_unique_name("Terminal_")
791
+
792
+ if isinstance(prim_id, EDBPrimitives):
793
+ prim_id = prim_id.id
794
+
795
+ pos_edge_term = self._create_edge_terminal(prim_id, point_on_edge, port_name)
796
+ pos_edge_term.SetImpedance(self._pedb.edb_value(impedance))
797
+
798
+ wave_port = WavePort(self._pedb, pos_edge_term)
799
+ wave_port.horizontal_extent_factor = horizontal_extent_factor
800
+ wave_port.vertical_extent_factor = vertical_extent_factor
801
+ wave_port.pec_launch_width = pec_launch_width
802
+ wave_port.hfss_type = "Wave"
803
+ wave_port.do_renormalize = True
804
+ if pos_edge_term:
805
+ return port_name, wave_port
806
+ else:
807
+ return False
808
+
809
+ @pyedb_function_handler()
810
+ def create_edge_port_vertical(
811
+ self,
812
+ prim_id,
813
+ point_on_edge,
814
+ port_name=None,
815
+ impedance=50,
816
+ reference_layer=None,
817
+ hfss_type="Gap",
818
+ horizontal_extent_factor=5,
819
+ vertical_extent_factor=3,
820
+ pec_launch_width="0.01mm",
821
+ ):
822
+ """Create a vertical edge port.
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
+ port_name : str, optional
833
+ Name of the port. The default is ``None``.
834
+ impedance : int, float, optional
835
+ Impedance of the port. The default value is ``50``.
836
+ reference_layer : str, optional
837
+ Reference layer of the port. The default is ``None``.
838
+ hfss_type : str, optional
839
+ Type of the port. The default value is ``"Gap"``. Options are ``"Gap"``, ``"Wave"``.
840
+ horizontal_extent_factor : int, float, optional
841
+ Horizontal extent factor. The default value is ``5``.
842
+ vertical_extent_factor : int, float, optional
843
+ Vertical extent factor. The default value is ``3``.
844
+ radial_extent_factor : int, float, optional
845
+ Radial extent factor. The default value is ``0``.
846
+ pec_launch_width : str, optional
847
+ Launch Width of PEC. The default value is ``"0.01mm"``.
848
+
849
+ Returns
850
+ -------
851
+ str
852
+ Port name.
853
+ """
854
+ if not port_name:
855
+ port_name = generate_unique_name("Terminal_")
856
+ pos_edge_term = self._create_edge_terminal(prim_id, point_on_edge, port_name)
857
+ pos_edge_term.SetImpedance(self._pedb.edb_value(impedance))
858
+ if reference_layer:
859
+ reference_layer = self._pedb.stackup.signal_layers[reference_layer]._edb_layer
860
+ pos_edge_term.SetReferenceLayer(reference_layer)
861
+
862
+ prop = ", ".join(
863
+ [
864
+ "HFSS('HFSS Type'='{}'".format(hfss_type),
865
+ " Orientation='Vertical'",
866
+ " 'Layer Alignment'='Upper'",
867
+ " 'Horizontal Extent Factor'='{}'".format(horizontal_extent_factor),
868
+ " 'Vertical Extent Factor'='{}'".format(vertical_extent_factor),
869
+ " 'PEC Launch Width'='{}')".format(pec_launch_width),
870
+ ]
871
+ )
872
+ pos_edge_term.SetProductSolverOption(
873
+ self._pedb.edb_api.ProductId.Designer,
874
+ "HFSS",
875
+ prop,
876
+ )
877
+ if pos_edge_term:
878
+ return port_name, self._pedb.hfss.excitations[port_name]
879
+ else:
880
+ return False
881
+
882
+ @pyedb_function_handler()
883
+ def create_edge_port_horizontal(
884
+ self,
885
+ prim_id,
886
+ point_on_edge,
887
+ ref_prim_id=None,
888
+ point_on_ref_edge=None,
889
+ port_name=None,
890
+ impedance=50,
891
+ layer_alignment="Upper",
892
+ ):
893
+ """Create a horizontal edge port.
894
+
895
+ Parameters
896
+ ----------
897
+ prim_id : int
898
+ Primitive ID.
899
+ point_on_edge : list
900
+ Coordinate of the point to define the edge terminal.
901
+ The point must be on the target edge but not on the two
902
+ ends of the edge.
903
+ ref_prim_id : int, optional
904
+ Reference primitive ID. The default is ``None``.
905
+ point_on_ref_edge : list, optional
906
+ Coordinate of the point to define the reference edge
907
+ terminal. The point must be on the target edge but not
908
+ on the two ends of the edge. The default is ``None``.
909
+ port_name : str, optional
910
+ Name of the port. The default is ``None``.
911
+ impedance : int, float, optional
912
+ Impedance of the port. The default value is ``50``.
913
+ layer_alignment : str, optional
914
+ Layer alignment. The default value is ``Upper``. Options are ``"Upper"``, ``"Lower"``.
915
+
916
+ Returns
917
+ -------
918
+ str
919
+ Name of the port.
920
+ """
921
+ pos_edge_term = self._create_edge_terminal(prim_id, point_on_edge, port_name)
922
+ neg_edge_term = self._create_edge_terminal(ref_prim_id, point_on_ref_edge, port_name + "_ref", is_ref=True)
923
+
924
+ pos_edge_term.SetImpedance(self._pedb.edb_value(impedance))
925
+ pos_edge_term.SetReferenceTerminal(neg_edge_term)
926
+ if not layer_alignment == "Upper":
927
+ layer_alignment = "Lower"
928
+ pos_edge_term.SetProductSolverOption(
929
+ self._pedb.edb_api.ProductId.Designer,
930
+ "HFSS",
931
+ "HFSS('HFSS Type'='Gap(coax)', Orientation='Horizontal', 'Layer Alignment'='{}')".format(layer_alignment),
932
+ )
933
+ if pos_edge_term:
934
+ return port_name
935
+ else:
936
+ return False
937
+
938
+ @pyedb_function_handler()
939
+ def create_lumped_port_on_net(
940
+ self, nets=None, reference_layer=None, return_points_only=False, digit_resolution=6, at_bounding_box=True
941
+ ):
942
+ """Create an edge port on nets. This command looks for traces and polygons on the
943
+ nets and tries to assign vertical lumped port.
944
+
945
+ Parameters
946
+ ----------
947
+ nets : list, optional
948
+ List of nets, str or Edb net.
949
+
950
+ reference_layer : str, Edb layer.
951
+ Name or Edb layer object.
952
+
953
+ return_points_only : bool, optional
954
+ Use this boolean when you want to return only the points from the edges and not creating ports. Default
955
+ value is ``False``.
956
+
957
+ digit_resolution : int, optional
958
+ The number of digits carried for the edge location accuracy. The default value is ``6``.
959
+
960
+ at_bounding_box : bool
961
+ When ``True`` will keep the edges from traces at the layout bounding box location. This is recommended when
962
+ a cutout has been performed before and lumped ports have to be created on ending traces. Default value is
963
+ ``True``.
964
+
965
+ Returns
966
+ -------
967
+ bool
968
+ ``True`` when successful, ``False`` when failed.
969
+ """
970
+ if not isinstance(nets, list):
971
+ if isinstance(nets, str):
972
+ nets = [self._edb.cell.net.find_by_name(self._active_layout, nets)]
973
+ elif isinstance(nets, self._edb.cell.net.net):
974
+ nets = [nets]
975
+ else:
976
+ temp_nets = []
977
+ for nn in nets:
978
+ if isinstance(nn, str):
979
+ temp_nets.append(self._edb.cell.net.find_by_name(self._active_layout, nn))
980
+ elif isinstance(nn, self._edb.cell.net.net):
981
+ temp_nets.append(nn)
982
+ nets = temp_nets
983
+ port_created = False
984
+ if nets:
985
+ edges_pts = []
986
+ if isinstance(reference_layer, str):
987
+ try:
988
+ reference_layer = self._pedb.stackup.signal_layers[reference_layer]._edb_layer
989
+ except:
990
+ raise Exception("Failed to get the layer {}".format(reference_layer))
991
+ if not isinstance(reference_layer, self._edb.Cell.ILayerReadOnly):
992
+ return False
993
+ layout = nets[0].GetLayout()
994
+ layout_bbox = self._pedb.get_conformal_polygon_from_netlist(self._pedb.nets.netlist)
995
+ layout_extent_segments = [pt for pt in list(layout_bbox.GetArcData()) if pt.IsSegment()]
996
+ first_pt = layout_extent_segments[0]
997
+ layout_extent_points = [
998
+ [first_pt.Start.X.ToDouble(), first_pt.End.X.ToDouble()],
999
+ [first_pt.Start.Y.ToDouble(), first_pt.End.Y.ToDouble()],
1000
+ ]
1001
+ for segment in layout_extent_segments[1:]:
1002
+ end_point = (segment.End.X.ToDouble(), segment.End.Y.ToDouble())
1003
+ layout_extent_points[0].append(end_point[0])
1004
+ layout_extent_points[1].append(end_point[1])
1005
+ for net in nets:
1006
+ net_primitives = self._pedb.nets[net.name].primitives
1007
+ net_paths = [pp for pp in net_primitives if pp.type == "Path"]
1008
+ for path in net_paths:
1009
+ trace_path_pts = list(path.center_line.Points)
1010
+ port_name = "{}_{}".format(net.name, path.GetId())
1011
+ for pt in trace_path_pts:
1012
+ _pt = [
1013
+ round(pt.X.ToDouble(), digit_resolution),
1014
+ round(pt.Y.ToDouble(), digit_resolution),
1015
+ ]
1016
+ if at_bounding_box:
1017
+ if GeometryOperators.point_in_polygon(_pt, layout_extent_points) == 0:
1018
+ if return_points_only:
1019
+ edges_pts.append(_pt)
1020
+ else:
1021
+ term = self._create_edge_terminal(path.id, pt, port_name) # pragma no cover
1022
+ term.SetReferenceLayer(reference_layer) # pragma no cover
1023
+ port_created = True
1024
+ else:
1025
+ if return_points_only: # pragma: no cover
1026
+ edges_pts.append(_pt)
1027
+ else:
1028
+ term = self._create_edge_terminal(path.id, pt, port_name)
1029
+ term.SetReferenceLayer(reference_layer)
1030
+ port_created = True
1031
+ net_poly = [pp for pp in net_primitives if pp.type == "Polygon"]
1032
+ for poly in net_poly:
1033
+ poly_segment = [aa for aa in poly.arcs if aa.is_segment]
1034
+ for segment in poly_segment:
1035
+ if (
1036
+ GeometryOperators.point_in_polygon(
1037
+ [segment.mid_point.X.ToDouble(), segment.mid_point.Y.ToDouble()], layout_extent_points
1038
+ )
1039
+ == 0
1040
+ ):
1041
+ if return_points_only:
1042
+ edges_pts.append(segment.mid_point)
1043
+ else:
1044
+ port_name = "{}_{}".format(net.name, poly.GetId())
1045
+ term = self._create_edge_terminal(
1046
+ poly.id, segment.mid_point, port_name
1047
+ ) # pragma no cover
1048
+ term.SetReferenceLayer(reference_layer) # pragma no cover
1049
+ port_created = True
1050
+ if return_points_only:
1051
+ return edges_pts
1052
+ return port_created
1053
+
1054
+ @pyedb_function_handler()
1055
+ def create_vertical_circuit_port_on_clipped_traces(self, nets=None, reference_net=None, user_defined_extent=None):
1056
+ """Create an edge port on clipped signal traces.
1057
+
1058
+ Parameters
1059
+ ----------
1060
+ nets : list, optional
1061
+ String of one net or EDB net or a list of multiple nets or EDB nets.
1062
+
1063
+ reference_net : str, Edb net.
1064
+ Name or EDB reference net.
1065
+
1066
+ user_defined_extent : [x, y], EDB PolygonData
1067
+ Use this point list or PolygonData object to check if ports are at this polygon border.
1068
+
1069
+ Returns
1070
+ -------
1071
+ [[str]]
1072
+ Nested list of str, with net name as first value, X value for point at border, Y value for point at border,
1073
+ and terminal name.
1074
+ """
1075
+ if not isinstance(nets, list):
1076
+ if isinstance(nets, str):
1077
+ nets = list(self._pedb.nets.signal.values())
1078
+ else:
1079
+ nets = [self._pedb.nets.signal[net] for net in nets]
1080
+ if nets:
1081
+ if isinstance(reference_net, str):
1082
+ reference_net = self._pedb.nets[reference_net]
1083
+ if not reference_net:
1084
+ self._logger.error("No reference net provided for creating port")
1085
+ return False
1086
+ if user_defined_extent:
1087
+ if isinstance(user_defined_extent, self._edb.Geometry.PolygonData):
1088
+ _points = [pt for pt in list(user_defined_extent.Points)]
1089
+ _x = []
1090
+ _y = []
1091
+ for pt in _points:
1092
+ if pt.X.ToDouble() < 1e100 and pt.Y.ToDouble() < 1e100:
1093
+ _x.append(pt.X.ToDouble())
1094
+ _y.append(pt.Y.ToDouble())
1095
+ user_defined_extent = [_x, _y]
1096
+ terminal_info = []
1097
+ for net in nets:
1098
+ net_polygons = [
1099
+ pp
1100
+ for pp in net.primitives
1101
+ if pp.GetPrimitiveType() == self._edb.cell.primitive.PrimitiveType.Polygon
1102
+ ]
1103
+ for poly in net_polygons:
1104
+ mid_points = [[arc.mid_point.X.ToDouble(), arc.mid_point.Y.ToDouble()] for arc in poly.arcs]
1105
+ for mid_point in mid_points:
1106
+ if GeometryOperators.point_in_polygon(mid_point, user_defined_extent) == 0:
1107
+ port_name = generate_unique_name("{}_{}".format(poly.GetNet().GetName(), poly.GetId()))
1108
+ term = self._create_edge_terminal(poly.GetId(), mid_point, port_name) # pragma no cover
1109
+ if not term.IsNull():
1110
+ self._logger.info("Terminal {} created".format(term.GetName()))
1111
+ term.SetIsCircuitPort(True)
1112
+ terminal_info.append(
1113
+ [poly.GetNet().GetName(), mid_point[0], mid_point[1], term.GetName()]
1114
+ )
1115
+ mid_pt_data = self._edb.geometry.point_data(
1116
+ self._edb.utility.value(mid_point[0]), self._edb.utility.value(mid_point[1])
1117
+ )
1118
+ ref_prim = [
1119
+ prim
1120
+ for prim in reference_net.primitives
1121
+ if prim.polygon_data.edb_api.PointInPolygon(mid_pt_data)
1122
+ ]
1123
+ if not ref_prim:
1124
+ self._logger.warning("no reference primitive found, trying to extend scanning area")
1125
+ scanning_zone = [
1126
+ (mid_point[0] - mid_point[0] * 1e-3, mid_point[1] - mid_point[1] * 1e-3),
1127
+ (mid_point[0] - mid_point[0] * 1e-3, mid_point[1] + mid_point[1] * 1e-3),
1128
+ (mid_point[0] + mid_point[0] * 1e-3, mid_point[1] + mid_point[1] * 1e-3),
1129
+ (mid_point[0] + mid_point[0] * 1e-3, mid_point[1] - mid_point[1] * 1e-3),
1130
+ ]
1131
+ for new_point in scanning_zone:
1132
+ mid_pt_data = self._edb.geometry.point_data(
1133
+ self._edb.utility.value(new_point[0]), self._edb.utility.value(new_point[1])
1134
+ )
1135
+ ref_prim = [
1136
+ prim
1137
+ for prim in reference_net.primitives
1138
+ if prim.polygon_data.edb_api.PointInPolygon(mid_pt_data)
1139
+ ]
1140
+ if ref_prim:
1141
+ self._logger.info("Reference primitive found")
1142
+ break
1143
+ if not ref_prim:
1144
+ self._logger.error("Failed to collect valid reference primitives for terminal")
1145
+ if ref_prim:
1146
+ reference_layer = ref_prim[0].layer
1147
+ if term.SetReferenceLayer(reference_layer): # pragma no cover
1148
+ self._logger.info("Port {} created".format(port_name))
1149
+ return terminal_info
1150
+ return False
1151
+
1152
+ @pyedb_function_handler()
1153
+ def get_layout_bounding_box(self, layout=None, digit_resolution=6):
1154
+ """Evaluate the layout bounding box.
1155
+
1156
+ Parameters
1157
+ ----------
1158
+ layout :
1159
+ Edb layout.
1160
+
1161
+ digit_resolution : int, optional
1162
+ Digit Resolution. The default value is ``6``.
1163
+
1164
+ Returns
1165
+ -------
1166
+ list
1167
+ [lower left corner X, lower left corner, upper right corner X, upper right corner Y].
1168
+ """
1169
+ if layout == None:
1170
+ return False
1171
+ layout_obj_instances = layout.GetLayoutInstance().GetAllLayoutObjInstances()
1172
+ tuple_list = []
1173
+ for lobj in layout_obj_instances.Items:
1174
+ lobj_bbox = lobj.GetLayoutInstanceContext().GetBBox(False)
1175
+ tuple_list.append(lobj_bbox)
1176
+ _bbox = self._edb.geometry.polygon_data.get_bbox_of_boxes(tuple_list)
1177
+ layout_bbox = [
1178
+ round(_bbox.Item1.X.ToDouble(), digit_resolution),
1179
+ round(_bbox.Item1.Y.ToDouble(), digit_resolution),
1180
+ round(_bbox.Item2.X.ToDouble(), digit_resolution),
1181
+ round(_bbox.Item2.Y.ToDouble(), digit_resolution),
1182
+ ]
1183
+ return layout_bbox
1184
+
1185
+ @pyedb_function_handler()
1186
+ def configure_hfss_extents(self, simulation_setup=None):
1187
+ """Configure the HFSS extent box.
1188
+
1189
+ Parameters
1190
+ ----------
1191
+ simulation_setup :
1192
+ Edb_DATA.SimulationConfiguration object
1193
+
1194
+ Returns
1195
+ -------
1196
+ bool
1197
+ True when succeeded, False when failed.
1198
+ """
1199
+
1200
+ if not isinstance(simulation_setup, SimulationConfiguration):
1201
+ self._logger.error(
1202
+ "Configure HFSS extent requires edb_data.simulation_configuration.SimulationConfiguration object"
1203
+ )
1204
+ return False
1205
+ hfss_extent = self._edb.utility.utility.HFSSExtentInfo()
1206
+ if simulation_setup.radiation_box == RadiationBoxType.BoundingBox:
1207
+ hfss_extent.ExtentType = self._edb.utility.utility.HFSSExtentInfoType.BoundingBox
1208
+ elif simulation_setup.radiation_box == RadiationBoxType.Conformal:
1209
+ hfss_extent.ExtentType = self._edb.utility.utility.HFSSExtentInfoType.Conforming
1210
+ else:
1211
+ hfss_extent.ExtentType = self._edb.utility.utility.HFSSExtentInfoType.ConvexHull
1212
+ hfss_extent.DielectricExtentSize = convert_pytuple_to_nettuple(
1213
+ (simulation_setup.dielectric_extent, simulation_setup.use_dielectric_extent_multiple)
1214
+ )
1215
+ hfss_extent.AirBoxHorizontalExtent = convert_pytuple_to_nettuple(
1216
+ (simulation_setup.airbox_horizontal_extent, simulation_setup.use_airbox_horizontal_extent_multiple)
1217
+ )
1218
+ hfss_extent.AirBoxNegativeVerticalExtent = convert_pytuple_to_nettuple(
1219
+ (
1220
+ simulation_setup.airbox_negative_vertical_extent,
1221
+ simulation_setup.use_airbox_negative_vertical_extent_multiple,
1222
+ )
1223
+ )
1224
+ hfss_extent.AirBoxPositiveVerticalExtent = convert_pytuple_to_nettuple(
1225
+ (
1226
+ simulation_setup.airbox_positive_vertical_extent,
1227
+ simulation_setup.use_airbox_positive_vertical_extent_multiple,
1228
+ )
1229
+ )
1230
+ hfss_extent.HonorUserDielectric = simulation_setup.honor_user_dielectric
1231
+ hfss_extent.TruncateAirBoxAtGround = simulation_setup.truncate_airbox_at_ground
1232
+ hfss_extent.UseOpenRegion = simulation_setup.use_radiation_boundary
1233
+ self._layout.cell.SetHFSSExtentInfo(hfss_extent) # returns void
1234
+ return True
1235
+
1236
+ @pyedb_function_handler()
1237
+ def configure_hfss_analysis_setup(self, simulation_setup=None):
1238
+ """
1239
+ Configure HFSS analysis setup.
1240
+
1241
+ Parameters
1242
+ ----------
1243
+ simulation_setup :
1244
+ Edb_DATA.SimulationConfiguration object
1245
+
1246
+ Returns
1247
+ -------
1248
+ bool
1249
+ True when succeeded, False when failed.
1250
+ """
1251
+ if not isinstance(simulation_setup, SimulationConfiguration):
1252
+ self._logger.error(
1253
+ "Configure HFSS analysis requires and edb_data.simulation_configuration.SimulationConfiguration object \
1254
+ as argument"
1255
+ )
1256
+ return False
1257
+ adapt = self._pedb.simsetupdata.AdaptiveFrequencyData()
1258
+ adapt.AdaptiveFrequency = simulation_setup.mesh_freq
1259
+ adapt.MaxPasses = int(simulation_setup.max_num_passes)
1260
+ adapt.MaxDelta = str(simulation_setup.max_mag_delta_s)
1261
+ simsetup_info = self._pedb.simsetupdata.SimSetupInfo[self._pedb.simsetupdata.HFSSSimulationSettings]()
1262
+ simsetup_info.Name = simulation_setup.setup_name
1263
+
1264
+ simsetup_info.SimulationSettings.CurveApproxSettings.ArcAngle = simulation_setup.arc_angle
1265
+ simsetup_info.SimulationSettings.CurveApproxSettings.UseArcToChordError = (
1266
+ simulation_setup.use_arc_to_chord_error
1267
+ )
1268
+ simsetup_info.SimulationSettings.CurveApproxSettings.ArcToChordError = simulation_setup.arc_to_chord_error
1269
+ if is_ironpython:
1270
+ simsetup_info.SimulationSettings.AdaptiveSettings.AdaptiveFrequencyDataList.Clear()
1271
+ simsetup_info.SimulationSettings.AdaptiveSettings.AdaptiveFrequencyDataList.Add(adapt)
1272
+ else:
1273
+ simsetup_info.SimulationSettings.AdaptiveSettings.AdaptiveFrequencyDataList = convert_py_list_to_net_list(
1274
+ [adapt]
1275
+ )
1276
+ simsetup_info.SimulationSettings.InitialMeshSettings.LambdaRefine = simulation_setup.do_lambda_refinement
1277
+ if simulation_setup.mesh_sizefactor > 0.0:
1278
+ simsetup_info.SimulationSettings.InitialMeshSettings.MeshSizefactor = simulation_setup.mesh_sizefactor
1279
+ simsetup_info.SimulationSettings.InitialMeshSettings.LambdaRefine = False
1280
+ simsetup_info.SimulationSettings.AdaptiveSettings.MaxRefinePerPass = 30
1281
+ simsetup_info.SimulationSettings.AdaptiveSettings.MinPasses = simulation_setup.min_num_passes
1282
+ simsetup_info.SimulationSettings.AdaptiveSettings.MinConvergedPasses = 1
1283
+ simsetup_info.SimulationSettings.HFSSSolverSettings.OrderBasis = simulation_setup.basis_order
1284
+ simsetup_info.SimulationSettings.HFSSSolverSettings.UseHFSSIterativeSolver = False
1285
+ simsetup_info.SimulationSettings.DefeatureSettings.UseDefeature = False # set True when using defeature ratio
1286
+ simsetup_info.SimulationSettings.DefeatureSettings.UseDefeatureAbsLength = simulation_setup.defeature_layout
1287
+ simsetup_info.SimulationSettings.DefeatureSettings.DefeatureAbsLength = simulation_setup.defeature_abs_length
1288
+
1289
+ try:
1290
+ if simulation_setup.add_frequency_sweep:
1291
+ self._logger.info("Adding frequency sweep")
1292
+ sweep = self._pedb.simsetupdata.SweepData(simulation_setup.sweep_name)
1293
+ sweep.IsDiscrete = False
1294
+ sweep.UseQ3DForDC = simulation_setup.use_q3d_for_dc
1295
+ sweep.RelativeSError = simulation_setup.relative_error
1296
+ sweep.InterpUsePortImpedance = False
1297
+ sweep.EnforceCausality = simulation_setup.enforce_causality
1298
+ # sweep.EnforceCausality = False
1299
+ sweep.EnforcePassivity = simulation_setup.enforce_passivity
1300
+ sweep.PassivityTolerance = simulation_setup.passivity_tolerance
1301
+ sweep.Frequencies.Clear()
1302
+
1303
+ if simulation_setup.sweep_type == SweepType.LogCount: # setup_info.SweepType == 'DecadeCount'
1304
+ self._setup_decade_count_sweep(
1305
+ sweep,
1306
+ str(simulation_setup.start_freq),
1307
+ str(simulation_setup.stop_freq),
1308
+ str(simulation_setup.decade_count),
1309
+ ) # Added DecadeCount as a new attribute
1310
+
1311
+ else:
1312
+ sweep.Frequencies = self._pedb.simsetupdata.SweepData.SetFrequencies(
1313
+ simulation_setup.start_freq,
1314
+ simulation_setup.stop_freq,
1315
+ simulation_setup.step_freq,
1316
+ )
1317
+
1318
+ simsetup_info.SweepDataList.Add(sweep)
1319
+ else:
1320
+ self._logger.info("Adding frequency sweep disabled")
1321
+
1322
+ except Exception as err:
1323
+ self._logger.error("Exception in Sweep configuration: {0}".format(err))
1324
+
1325
+ sim_setup = self._edb.utility.utility.HFSSSimulationSetup(simsetup_info)
1326
+ for setup in self._layout.cell.SimulationSetups:
1327
+ self._layout.cell.DeleteSimulationSetup(setup.GetName())
1328
+ self._logger.warning("Setup {} has been deleted".format(setup.GetName()))
1329
+ return self._layout.cell.AddSimulationSetup(sim_setup)
1330
+
1331
+ def _setup_decade_count_sweep(self, sweep, start_freq="1", stop_freq="1MHz", decade_count="10"):
1332
+ start_f = GeometryOperators.parse_dim_arg(start_freq)
1333
+ if start_f == 0.0:
1334
+ start_f = 10
1335
+ self._logger.warning("Decade Count sweep does not support DC value, defaulting starting frequency to 10Hz")
1336
+
1337
+ stop_f = GeometryOperators.parse_dim_arg(stop_freq)
1338
+ decade_cnt = GeometryOperators.parse_dim_arg(decade_count)
1339
+ freq = start_f
1340
+ sweep.Frequencies.Add(str(freq))
1341
+
1342
+ while freq < stop_f:
1343
+ freq = freq * math.pow(10, 1.0 / decade_cnt)
1344
+ sweep.Frequencies.Add(str(freq))
1345
+
1346
+ @pyedb_function_handler()
1347
+ def trim_component_reference_size(self, simulation_setup=None, trim_to_terminals=False):
1348
+ """Trim the common component reference to the minimally acceptable size.
1349
+
1350
+ Parameters
1351
+ ----------
1352
+ simulation_setup :
1353
+ Edb_DATA.SimulationConfiguration object
1354
+
1355
+ trim_to_terminals :
1356
+ bool.
1357
+ True, reduce the reference to a box covering only the active terminals (i.e. those with
1358
+ ports).
1359
+ False, reduce the reference to the minimal size needed to cover all pins
1360
+
1361
+ Returns
1362
+ -------
1363
+ bool
1364
+ True when succeeded, False when failed.
1365
+ """
1366
+
1367
+ if not isinstance(simulation_setup, SimulationConfiguration):
1368
+ self._logger.error(
1369
+ "Trim component reference size requires an edb_data.simulation_configuration.SimulationConfiguration \
1370
+ object as argument"
1371
+ )
1372
+ return False
1373
+
1374
+ if not simulation_setup.components: # pragma: no cover
1375
+ return
1376
+
1377
+ layout = self._cell.GetLayout()
1378
+ l_inst = layout.GetLayoutInstance()
1379
+
1380
+ for inst in simulation_setup.components: # pragma: no cover
1381
+ comp = self._pedb.edb_api.cell.hierarchy.component.FindByName(layout, inst)
1382
+ if comp.IsNull():
1383
+ continue
1384
+
1385
+ terms_bbox_pts = self._get_terminals_bbox(comp, l_inst, trim_to_terminals)
1386
+ if not terms_bbox_pts:
1387
+ continue
1388
+
1389
+ terms_bbox = self._edb.geometry.polygon_data.create_from_bbox(terms_bbox_pts)
1390
+
1391
+ if trim_to_terminals:
1392
+ # Remove any pins that aren't interior to the Terminals bbox
1393
+ pin_list = [
1394
+ obj
1395
+ for obj in list(comp.LayoutObjs)
1396
+ if obj.GetObjType() == self._edb.cell.layout_object_type.PadstackInstance
1397
+ ]
1398
+ for pin in pin_list:
1399
+ loi = l_inst.GetLayoutObjInstance(pin, None)
1400
+ bb_c = loi.GetCenter()
1401
+ if not terms_bbox.PointInPolygon(bb_c):
1402
+ comp.RemoveMember(pin)
1403
+
1404
+ # Set the port property reference size
1405
+ cmp_prop = comp.GetComponentProperty().Clone()
1406
+ port_prop = cmp_prop.GetPortProperty().Clone()
1407
+ port_prop.SetReferenceSizeAuto(False)
1408
+ port_prop.SetReferenceSize(
1409
+ terms_bbox_pts.Item2.X.ToDouble() - terms_bbox_pts.Item1.X.ToDouble(),
1410
+ terms_bbox_pts.Item2.Y.ToDouble() - terms_bbox_pts.Item1.Y.ToDouble(),
1411
+ )
1412
+ cmp_prop.SetPortProperty(port_prop)
1413
+ comp.SetComponentProperty(cmp_prop)
1414
+ return True
1415
+
1416
+ @pyedb_function_handler()
1417
+ def set_coax_port_attributes(self, simulation_setup=None):
1418
+ """Set coaxial port attribute with forcing default impedance to 50 Ohms and adjusting the coaxial extent radius.
1419
+
1420
+ Parameters
1421
+ ----------
1422
+ simulation_setup :
1423
+ Edb_DATA.SimulationConfiguration object.
1424
+
1425
+ Returns
1426
+ -------
1427
+ bool
1428
+ True when succeeded, False when failed.
1429
+ """
1430
+
1431
+ if not isinstance(simulation_setup, SimulationConfiguration):
1432
+ self._logger.error(
1433
+ "Set coax port attribute requires an edb_data.simulation_configuration.SimulationConfiguration object \
1434
+ as argument."
1435
+ )
1436
+ return False
1437
+ net_names = [net.name for net in self._layout.nets if not net.IsPowerGround()]
1438
+ if simulation_setup.components and isinstance(simulation_setup.components[0], str):
1439
+ cmp_names = (
1440
+ simulation_setup.components
1441
+ if simulation_setup.components
1442
+ else [gg.GetName() for gg in self._layout.groups]
1443
+ )
1444
+ elif (
1445
+ simulation_setup.components
1446
+ and isinstance(simulation_setup.components[0], dict)
1447
+ and "refdes" in simulation_setup.components[0]
1448
+ ):
1449
+ cmp_names = [cmp["refdes"] for cmp in simulation_setup.components]
1450
+ else:
1451
+ cmp_names = []
1452
+ ii = 0
1453
+ for cc in cmp_names:
1454
+ cmp = self._pedb.edb_api.cell.hierarchy.component.FindByName(self._active_layout, cc)
1455
+ if cmp.IsNull():
1456
+ self._logger.warning("RenamePorts: could not find component {0}".format(cc))
1457
+ continue
1458
+ terms = [
1459
+ obj for obj in list(cmp.LayoutObjs) if obj.GetObjType() == self._edb.cell.layout_object_type.Terminal
1460
+ ]
1461
+ for nn in net_names:
1462
+ for tt in [term for term in terms if term.GetNet().GetName() == nn]:
1463
+ if not tt.SetImpedance(self._pedb.edb_value("50ohm")):
1464
+ self._logger.warning("Could not set terminal {0} impedance as 50ohm".format(tt.GetName()))
1465
+ continue
1466
+ ii += 1
1467
+
1468
+ if not simulation_setup.use_default_coax_port_radial_extension:
1469
+ # Set the Radial Extent Factor
1470
+ typ = cmp.GetComponentType()
1471
+ if typ in [
1472
+ self._edb.definition.ComponentType.Other,
1473
+ self._edb.definition.ComponentType.IC,
1474
+ self._edb.definition.ComponentType.IO,
1475
+ ]:
1476
+ cmp_prop = cmp.GetComponentProperty().Clone()
1477
+ (
1478
+ success,
1479
+ diam1,
1480
+ diam2,
1481
+ ) = cmp_prop.GetSolderBallProperty().GetDiameter()
1482
+ if success and diam1 and diam2 > 0: # pragma: no cover
1483
+ option = (
1484
+ "HFSS('HFSS Type'='**Invalid**', "
1485
+ "Orientation='**Invalid**', "
1486
+ "'Layer Alignment'='Upper', "
1487
+ "'Horizontal Extent Factor'='5', "
1488
+ "'Vertical Extent Factor'='3', "
1489
+ "'Radial Extent Factor'='0.25', "
1490
+ "'PEC Launch Width'='0mm')"
1491
+ )
1492
+ for tt in terms:
1493
+ tt.SetProductSolverOption(self._edb.edb_api.ProductId.Designer, "HFSS", option)
1494
+ return True
1495
+
1496
+ @pyedb_function_handler()
1497
+ def _get_terminals_bbox(self, comp, l_inst, terminals_only):
1498
+ terms_loi = []
1499
+ if terminals_only:
1500
+ term_list = [
1501
+ obj for obj in list(comp.LayoutObjs) if obj.GetObjType() == self._edb.cell.layout_object_type.Terminal
1502
+ ]
1503
+ for tt in term_list:
1504
+ success, p_inst, lyr = tt.GetParameters()
1505
+ if success and lyr:
1506
+ loi = l_inst.GetLayoutObjInstance(p_inst, None)
1507
+ terms_loi.append(loi)
1508
+ else:
1509
+ pin_list = [
1510
+ obj
1511
+ for obj in list(comp.LayoutObjs)
1512
+ if obj.GetObjType() == self._edb.cell.layout_object_type.PadstackInstance
1513
+ ]
1514
+ for pi in pin_list:
1515
+ loi = l_inst.GetLayoutObjInstance(pi, None)
1516
+ terms_loi.append(loi)
1517
+
1518
+ if len(terms_loi) == 0:
1519
+ return None
1520
+
1521
+ terms_bbox = []
1522
+ for loi in terms_loi:
1523
+ # Need to account for the coax port dimension
1524
+ bb = loi.GetBBox()
1525
+ ll = [bb.Item1.X.ToDouble(), bb.Item1.Y.ToDouble()]
1526
+ ur = [bb.Item2.X.ToDouble(), bb.Item2.Y.ToDouble()]
1527
+ # dim = 0.26 * max(abs(UR[0]-LL[0]), abs(UR[1]-LL[1])) # 0.25 corresponds to the default 0.5
1528
+ # Radial Extent Factor, so set slightly larger to avoid validation errors
1529
+ dim = 0.30 * max(abs(ur[0] - ll[0]), abs(ur[1] - ll[1])) # 0.25 corresponds to the default 0.5
1530
+ terms_bbox.append(
1531
+ self._edb.geometry.polygon_data.dotnetobj(ll[0] - dim, ll[1] - dim, ur[0] + dim, ur[1] + dim)
1532
+ )
1533
+ return self._edb.geometry.polygon_data.get_bbox_of_polygons(terms_bbox)
1534
+
1535
+ @pyedb_function_handler()
1536
+ def get_ports_number(self):
1537
+ """Return the total number of excitation ports in a layout.
1538
+
1539
+ Parameters
1540
+ ----------
1541
+ None
1542
+
1543
+ Returns
1544
+ -------
1545
+ int
1546
+ Number of ports.
1547
+
1548
+ """
1549
+ terms = [term for term in self._layout.terminals if int(term.GetBoundaryType()) == 0]
1550
+ return len([i for i in terms if not i.IsReferenceTerminal()])
1551
+
1552
+ @pyedb_function_handler()
1553
+ def layout_defeaturing(self, simulation_setup=None):
1554
+ """Defeature the layout by reducing the number of points for polygons based on surface deviation criteria.
1555
+
1556
+ Parameters
1557
+ ----------
1558
+ simulation_setup : Edb_DATA.SimulationConfiguration object
1559
+
1560
+ Returns
1561
+ -------
1562
+ bool
1563
+ ``True`` when successful, ``False`` when failed.
1564
+
1565
+ """
1566
+ if not isinstance(simulation_setup, SimulationConfiguration):
1567
+ self._logger.error(
1568
+ "Layout defeaturing requires an edb_data.simulation_configuration.SimulationConfiguration object."
1569
+ )
1570
+ return False
1571
+ self._logger.info("Starting Layout Defeaturing")
1572
+ polygon_list = self._pedb.modeler.polygons
1573
+ polygon_with_voids = self._pedb.core_layout.get_poly_with_voids(polygon_list)
1574
+ self._logger.info("Number of polygons with voids found: {0}".format(str(polygon_with_voids.Count)))
1575
+ for _poly in polygon_list:
1576
+ voids_from_current_poly = _poly.Voids
1577
+ new_poly_data = self._pedb.core_layout.defeature_polygon(setup_info=simulation_setup, poly=_poly)
1578
+ _poly.SetPolygonData(new_poly_data)
1579
+ if len(voids_from_current_poly) > 0:
1580
+ for void in voids_from_current_poly:
1581
+ void_data = void.GetPolygonData()
1582
+ if void_data.Area() < float(simulation_setup.minimum_void_surface):
1583
+ void.Delete()
1584
+ self._logger.warning(
1585
+ "Defeaturing Polygon {0}: Deleting Void {1} area is lower than the minimum criteria".format(
1586
+ str(_poly.GetId()), str(void.GetId())
1587
+ )
1588
+ )
1589
+ else:
1590
+ self._logger.info(
1591
+ "Defeaturing polygon {0}: void {1}".format(str(_poly.GetId()), str(void.GetId()))
1592
+ )
1593
+ new_void_data = self._pedb.core_layout.defeature_polygon(
1594
+ setup_info=simulation_setup, poly=void_data
1595
+ )
1596
+ void.SetPolygonData(new_void_data)
1597
+
1598
+ return True
1599
+
1600
+ @pyedb_function_handler()
1601
+ def create_rlc_boundary_on_pins(self, positive_pin=None, negative_pin=None, rvalue=0.0, lvalue=0.0, cvalue=0.0):
1602
+ """Create hfss rlc boundary on pins.
1603
+
1604
+ Parameters
1605
+ ----------
1606
+ positive_pin : Positive pin.
1607
+ Edb.Cell.Primitive.PadstackInstance
1608
+
1609
+ negative_pin : Negative pin.
1610
+ Edb.Cell.Primitive.PadstackInstance
1611
+
1612
+ rvalue : Resistance value
1613
+
1614
+ lvalue : Inductance value
1615
+
1616
+ cvalue . Capacitance value.
1617
+
1618
+ Returns
1619
+ -------
1620
+ bool
1621
+ ``True`` when successful, ``False`` when failed.
1622
+
1623
+ """
1624
+
1625
+ if positive_pin and negative_pin:
1626
+ positive_pin_term = self._pedb.components._create_terminal(positive_pin)
1627
+ negative_pin_term = self._pedb.components._create_terminal(negative_pin)
1628
+ positive_pin_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.RlcBoundary)
1629
+ negative_pin_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.RlcBoundary)
1630
+ rlc = self._edb.utility.utility.Rlc()
1631
+ rlc.IsParallel = True
1632
+ rlc.REnabled = True
1633
+ rlc.LEnabled = True
1634
+ rlc.CEnabled = True
1635
+ rlc.R = self._get_edb_value(rvalue)
1636
+ rlc.L = self._get_edb_value(lvalue)
1637
+ rlc.C = self._get_edb_value(cvalue)
1638
+ positive_pin_term.SetRlcBoundaryParameters(rlc)
1639
+ term_name = "{}_{}_{}".format(
1640
+ positive_pin.GetComponent().GetName(), positive_pin.GetNet().GetName(), positive_pin.GetName()
1641
+ )
1642
+ positive_pin_term.SetName(term_name)
1643
+ negative_pin_term.SetName("{}_ref".format(term_name))
1644
+ positive_pin_term.SetReferenceTerminal(negative_pin_term)
1645
+ return True
1646
+ return False # pragma no cover