pyedb 0.12.1__py3-none-any.whl → 0.13.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 (40) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_boundaries.py +1 -1
  3. pyedb/configuration/cfg_common.py +48 -0
  4. pyedb/configuration/cfg_components.py +94 -166
  5. pyedb/configuration/cfg_data.py +12 -7
  6. pyedb/configuration/cfg_general.py +1 -1
  7. pyedb/configuration/cfg_nets.py +1 -1
  8. pyedb/configuration/cfg_operations.py +63 -0
  9. pyedb/configuration/cfg_package_definition.py +128 -0
  10. pyedb/configuration/cfg_padstacks.py +3 -3
  11. pyedb/configuration/cfg_pin_groups.py +1 -1
  12. pyedb/configuration/cfg_ports_sources.py +4 -4
  13. pyedb/configuration/cfg_s_parameter_models.py +1 -1
  14. pyedb/configuration/cfg_setup.py +1 -1
  15. pyedb/configuration/cfg_spice_models.py +1 -1
  16. pyedb/configuration/cfg_stackup.py +169 -0
  17. pyedb/configuration/configuration.py +46 -19
  18. pyedb/dotnet/edb.py +167 -7
  19. pyedb/dotnet/edb_core/cell/hierarchy/model.py +1 -1
  20. pyedb/dotnet/edb_core/{edb_data/connectable.py → cell/layout_obj.py} +1 -1
  21. pyedb/dotnet/edb_core/cell/primitive.py +142 -0
  22. pyedb/dotnet/edb_core/components.py +1 -1
  23. pyedb/dotnet/edb_core/definition/component_def.py +1 -1
  24. pyedb/dotnet/edb_core/definition/component_model.py +1 -1
  25. pyedb/dotnet/edb_core/definition/definition_obj.py +1 -1
  26. pyedb/dotnet/edb_core/definition/package_def.py +2 -1
  27. pyedb/dotnet/edb_core/edb_data/components_data.py +19 -7
  28. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +3 -3
  29. pyedb/dotnet/edb_core/edb_data/primitives_data.py +5 -126
  30. pyedb/dotnet/edb_core/edb_data/terminals.py +2 -2
  31. pyedb/dotnet/edb_core/geometry/polygon_data.py +1 -1
  32. pyedb/dotnet/edb_core/materials.py +2 -1
  33. pyedb/dotnet/edb_core/padstack.py +86 -27
  34. pyedb/generic/general_methods.py +33 -0
  35. pyedb/siwave.py +23 -2
  36. {pyedb-0.12.1.dist-info → pyedb-0.13.0.dist-info}/METADATA +2 -2
  37. {pyedb-0.12.1.dist-info → pyedb-0.13.0.dist-info}/RECORD +40 -35
  38. /pyedb/dotnet/edb_core/{obj_base.py → utilities/obj_base.py} +0 -0
  39. {pyedb-0.12.1.dist-info → pyedb-0.13.0.dist-info}/LICENSE +0 -0
  40. {pyedb-0.12.1.dist-info → pyedb-0.13.0.dist-info}/WHEEL +0 -0
@@ -27,7 +27,7 @@ class CfgCircuitElement:
27
27
  @property
28
28
  def pedb(self):
29
29
  """Edb."""
30
- return self._pdata.pedb
30
+ return self._pdata._pedb
31
31
 
32
32
  @pyedb_function_handler
33
33
  def __init__(self, pdata, **kwargs):
@@ -76,7 +76,7 @@ class CfgCircuitElement:
76
76
  search_radius = neg_value.get("search_radius", "5e-3")
77
77
  temp = dict()
78
78
  for i, j in pos_objs.items():
79
- temp[i] = self._pdata.pedb.padstacks.get_reference_pins(j, ref_net, search_radius, max_limit=1)[0]
79
+ temp[i] = self._pdata._pedb.padstacks.get_reference_pins(j, ref_net, search_radius, max_limit=1)[0]
80
80
  self.neg_terminal = {
81
81
  i: j.create_terminal(i + "_ref") if not j.terminal else j.terminal for i, j in temp.items()
82
82
  }
@@ -105,7 +105,7 @@ class CfgCircuitElement:
105
105
  pins.update(get_pin_obj(i))
106
106
  else:
107
107
  if terminal_type == "net":
108
- temp = self._pdata.pedb.components.get_pins(self.reference_designator, terminal_value[0])
108
+ temp = self._pdata._pedb.components.get_pins(self.reference_designator, terminal_value[0])
109
109
  elif terminal_type == "pin_group":
110
110
  pin_group = self.pedb.siwave.pin_groups[terminal_value[0]]
111
111
  temp = pin_group.pins
@@ -119,7 +119,7 @@ class CfgCircuitElement:
119
119
  else:
120
120
  pg_name = f"pg_{self.name}_{self.reference_designator}"
121
121
  pin_names = [i.pin_number for i in pins.values()]
122
- name, temp = self._pdata.pedb.siwave.create_pin_group(self.reference_designator, pin_names, pg_name)
122
+ name, temp = self._pdata._pedb.siwave.create_pin_group(self.reference_designator, pin_names, pg_name)
123
123
  return {name: temp}
124
124
 
125
125
 
@@ -25,7 +25,7 @@ from pathlib import Path
25
25
 
26
26
  class CfgSParameterModel:
27
27
  def __init__(self, pdata, path_lib, sparam_dict):
28
- self._pedb = pdata.pedb
28
+ self._pedb = pdata._pedb
29
29
  self.path_libraries = path_lib
30
30
  self._sparam_dict = sparam_dict
31
31
  self.name = self._sparam_dict.get("name", "")
@@ -89,7 +89,7 @@ class DcIrSettings:
89
89
 
90
90
  class CfgSetup:
91
91
  def __init__(self, pdata, setup_dict=None):
92
- self._pedb = pdata.pedb
92
+ self._pedb = pdata._pedb
93
93
  self._setup_dict = None
94
94
  self.name = "PyEDB_setup"
95
95
  self.type = self.SetupType.HFSS
@@ -25,7 +25,7 @@ from pathlib import Path
25
25
 
26
26
  class CfgSpiceModel:
27
27
  def __init__(self, pdata, path_lib, spice_dict):
28
- self._pedb = pdata.pedb
28
+ self._pedb = pdata._pedb
29
29
  self.path_libraries = path_lib
30
30
  self._spice_dict = spice_dict
31
31
  self.name = self._spice_dict.get("name", "")
@@ -0,0 +1,169 @@
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 pyedb.configuration.cfg_common import CfgBase
24
+ from pyedb.generic.general_methods import pyedb_function_handler
25
+
26
+
27
+ class CfgMaterial(CfgBase):
28
+ def __init__(self, **kwargs):
29
+ self.name = kwargs.get("name", None)
30
+ self.permittivity = kwargs.get("permittivity", None)
31
+ self.conductivity = kwargs.get("conductivity", None)
32
+ self.dielectric_loss_tangent = kwargs.get("dielectric_loss_tangent", None)
33
+ self.magnetic_loss_tangent = kwargs.get("magnetic_loss_tangent", None)
34
+ self.mass_density = kwargs.get("mass_density", None)
35
+ self.permeability = kwargs.get("permeability", None)
36
+ self.poisson_ratio = kwargs.get("poisson_ratio", None)
37
+ self.specific_heat = kwargs.get("specific_heat", None)
38
+ self.thermal_conductivity = kwargs.get("thermal_conductivity", None)
39
+
40
+
41
+ class CfgLayer(CfgBase):
42
+ def __init__(self, **kwargs):
43
+ self.name = kwargs.get("name", None)
44
+ self.type = kwargs.get("type", None)
45
+ self.material = kwargs.get("material", None)
46
+ self.fill_material = kwargs.get("fill_material", None)
47
+ self.thickness = kwargs.get("thickness", None)
48
+
49
+
50
+ class CfgStackup:
51
+ def __init__(self, pedb, data):
52
+ self._pedb = pedb
53
+
54
+ self.materials = [CfgMaterial(**mat) for mat in data.get("materials", [])]
55
+ self.layers = [CfgLayer(**lay) for lay in data.get("layers", [])]
56
+
57
+ @pyedb_function_handler
58
+ def apply(self):
59
+ """Apply configuration settings to the current design"""
60
+ if len(self.materials):
61
+ self.__apply_materials()
62
+
63
+ input_signal_layers = [i for i in self.layers if i.type.lower() == "signal"]
64
+
65
+ if len(self.layers):
66
+ if len(self._pedb.stackup.signal_layers) == 0:
67
+ self.__create_stackup()
68
+ elif not len(input_signal_layers) == len(self._pedb.stackup.signal_layers):
69
+ raise Exception(f"Input signal layer count do not match.")
70
+ else:
71
+ self.__apply_layers()
72
+
73
+ def __create_stackup(self):
74
+ layers = list()
75
+ layers.extend(self.layers)
76
+ for l_attrs in layers:
77
+ attrs = l_attrs.get_attributes()
78
+ self._pedb.stackup.add_layer_bottom(**attrs)
79
+
80
+ @pyedb_function_handler
81
+ def __apply_layers(self):
82
+ """Apply layer settings to the current design"""
83
+ layers = list()
84
+ layers.extend(self.layers)
85
+
86
+ removal_list = []
87
+ lc_signal_layers = []
88
+ for name, obj in self._pedb.stackup.all_layers.items():
89
+ if obj.type == "dielectric":
90
+ removal_list.append(name)
91
+ elif obj.type == "signal":
92
+ lc_signal_layers.append(obj.id)
93
+ for l in removal_list:
94
+ self._pedb.stackup.remove_layer(l)
95
+
96
+ # update all signal layers
97
+ id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id}
98
+ signal_idx = 0
99
+ for l in layers:
100
+ if l.type == "signal":
101
+ layer_id = lc_signal_layers[signal_idx]
102
+ layer_name = id_name[layer_id]
103
+ attrs = l.get_attributes()
104
+ self._pedb.stackup.layers[layer_name].update(**attrs)
105
+ signal_idx = signal_idx + 1
106
+
107
+ # add all dielectric layers. Dielectric layers must be added last. Otherwise,
108
+ # dielectric layer will occupy signal and document layer id.
109
+ prev_layer_clone = None
110
+ l = layers.pop(0)
111
+ if l.type == "signal":
112
+ prev_layer_clone = self._pedb.stackup.layers[l.name]
113
+ else:
114
+ attrs = l.get_attributes()
115
+ prev_layer_clone = self._pedb.stackup.add_layer_top(**attrs)
116
+ for idx, l in enumerate(layers):
117
+ if l.type == "dielectric":
118
+ attrs = l.get_attributes()
119
+ prev_layer_clone = self._pedb.stackup.add_layer_below(base_layer_name=prev_layer_clone.name, **attrs)
120
+ elif l.type == "signal":
121
+ prev_layer_clone = self._pedb.stackup.layers[l.name]
122
+
123
+ @pyedb_function_handler
124
+ def __apply_materials(self):
125
+ """Apply material settings to the current design"""
126
+ materials_in_db = {i.lower(): i for i, _ in self._pedb.materials.materials.items()}
127
+ for mat_in_cfg in self.materials:
128
+ if mat_in_cfg.name.lower() in materials_in_db:
129
+ self._pedb.materials.delete_material(materials_in_db[mat_in_cfg.name.lower()])
130
+
131
+ attrs = mat_in_cfg.get_attributes()
132
+ mat = self._pedb.materials.add_material(**attrs)
133
+
134
+ @pyedb_function_handler
135
+ def __get_materials_from_db(self):
136
+ materials = []
137
+ for name, p in self._pedb.materials.materials.items():
138
+ mat = {}
139
+ for p_name in CfgMaterial().__dict__:
140
+ mat[p_name] = getattr(p, p_name)
141
+ materials.append(mat)
142
+ return materials
143
+
144
+ @pyedb_function_handler
145
+ def __get_layers_from_db(self):
146
+ layers = []
147
+ for name, obj in self._pedb.stackup.all_layers.items():
148
+ layer = {}
149
+ for p_name in CfgLayer().__dict__:
150
+ p_value = getattr(obj, p_name, None)
151
+ if p_value is not None:
152
+ layer[p_name] = getattr(obj, p_name)
153
+ layers.append(layer)
154
+ return layers
155
+
156
+ @pyedb_function_handler
157
+ def get_data_from_db(self):
158
+ """Get configuration data from layout.
159
+
160
+ Returns
161
+ -------
162
+ dict
163
+ """
164
+ stackup = {}
165
+ materials = self.__get_materials_from_db()
166
+ stackup["materials"] = materials
167
+ layers = self.__get_layers_from_db()
168
+ stackup["layers"] = layers
169
+ return stackup
@@ -22,6 +22,7 @@
22
22
 
23
23
  import json
24
24
  import os
25
+ from pathlib import Path
25
26
 
26
27
  import toml
27
28
 
@@ -115,9 +116,7 @@ class Configuration:
115
116
  self.cfg_data.nets.apply()
116
117
 
117
118
  # Configure components
118
- if self.cfg_data.components:
119
- for comp in self.cfg_data.components:
120
- comp.apply()
119
+ self.cfg_data.components.apply()
121
120
 
122
121
  # Configure padstacks
123
122
  if self.cfg_data.padstacks:
@@ -132,8 +131,6 @@ class Configuration:
132
131
  port.create()
133
132
 
134
133
  # Configure sources
135
- """if "sources" in self.data:
136
- self._load_sources()"""
137
134
  for source in self.cfg_data.sources:
138
135
  source.create()
139
136
 
@@ -142,8 +139,7 @@ class Configuration:
142
139
  setup.apply()
143
140
 
144
141
  # Configure stackup
145
- if "stackup" in self.data:
146
- self._load_stackup()
142
+ self.cfg_data.stackup.apply()
147
143
 
148
144
  # Configure S-parameter
149
145
  for s_parameter_model in self.cfg_data.s_parameters:
@@ -154,12 +150,10 @@ class Configuration:
154
150
  spice_model.apply()
155
151
 
156
152
  # Configure package definitions
157
- if "package_definitions" in self.data:
158
- self._load_package_def()
153
+ self.cfg_data.package_definitions.apply()
159
154
 
160
155
  # Configure operations
161
- if "operations" in self.data:
162
- self._load_operations()
156
+ self.cfg_data.operations.apply()
163
157
 
164
158
  return True
165
159
 
@@ -220,14 +214,6 @@ class Configuration:
220
214
  elif l["type"] == "signal":
221
215
  prev_layer_clone = self._pedb.stackup.layers[l["name"]]
222
216
 
223
- @pyedb_function_handler
224
- def _load_operations(self):
225
- """Imports operation information from JSON."""
226
- operations = self.data["operations"]
227
- cutout = operations.get("cutout", None)
228
- if cutout:
229
- self._pedb.cutout(**cutout)
230
-
231
217
  @pyedb_function_handler
232
218
  def _load_package_def(self):
233
219
  """Imports package definition information from JSON."""
@@ -271,3 +257,44 @@ class Configuration:
271
257
  )
272
258
  for _, i in comp_list.items():
273
259
  i.package_def = name
260
+
261
+ def get_data_from_db(self, **kwargs):
262
+ """Get configuration data from layout.
263
+
264
+ Parameters
265
+ ----------
266
+ stackup
267
+
268
+ Returns
269
+ -------
270
+
271
+ """
272
+ data = {}
273
+ if kwargs.get("stackup", False):
274
+ data["stackup"] = self.cfg_data.stackup.get_data_from_db()
275
+ if kwargs.get("package_definitions", False):
276
+ data["package_definitions"] = self.cfg_data.package_definitions.get_data_from_db()
277
+
278
+ return data
279
+
280
+ @pyedb_function_handler
281
+ def export(self, file_path, stackup=True, package_definitions=True):
282
+ """Export the configuration data from layout to a file.
283
+
284
+ Parameters
285
+ ----------
286
+ file_path : str, Path
287
+ File path to export the configuration data.
288
+ stackup : bool
289
+ Whether to export stackup or not.
290
+
291
+ Returns
292
+ -------
293
+ bool
294
+ """
295
+ file_path = file_path if isinstance(file_path, Path) else Path(file_path)
296
+ file_path = file_path if file_path.suffix == ".json" else file_path.with_suffix(".json")
297
+ data = self.get_data_from_db(stackup=stackup, package_definitions=package_definitions)
298
+ with open(file_path, "w") as f:
299
+ json.dump(data, f, ensure_ascii=False, indent=4)
300
+ return True if os.path.isfile(file_path) else False
pyedb/dotnet/edb.py CHANGED
@@ -27,12 +27,14 @@ This module is implicitly loaded in HFSS 3D Layout when launched.
27
27
  """
28
28
  from itertools import combinations
29
29
  import os
30
+ from pathlib import Path
30
31
  import re
31
32
  import shutil
32
33
  import sys
33
34
  import tempfile
34
35
  import time
35
36
  import traceback
37
+ from typing import Union
36
38
  import warnings
37
39
 
38
40
  from pyedb.configuration.configuration import Configuration
@@ -111,6 +113,8 @@ if is_linux and is_ironpython:
111
113
  else:
112
114
  import subprocess
113
115
 
116
+ import rtree
117
+
114
118
 
115
119
  class Edb(Database):
116
120
  """Provides the EDB application interface.
@@ -180,7 +184,7 @@ class Edb(Database):
180
184
 
181
185
  def __init__(
182
186
  self,
183
- edbpath: str = None,
187
+ edbpath: Union[str, Path] = None,
184
188
  cellname: str = None,
185
189
  isreadonly: bool = False,
186
190
  edbversion: str = None,
@@ -191,6 +195,9 @@ class Edb(Database):
191
195
  technology_file: str = None,
192
196
  remove_existing_aedt: bool = False,
193
197
  ):
198
+ if isinstance(edbpath, Path):
199
+ edbpath = str(edbpath)
200
+
194
201
  edbversion = get_string_version(edbversion)
195
202
  self._clean_variables()
196
203
  Database.__init__(self, edbversion=edbversion, student_version=student_version)
@@ -315,7 +322,9 @@ class Edb(Database):
315
322
  if os.path.isfile(file):
316
323
  if not remove_existing_aedt:
317
324
  self.logger.warning(
318
- f"AEDT project-related file {file} exists and may need to be deleted before opening the EDB in HFSS 3D Layout." # noqa: E501
325
+ f"AEDT project-related file {file} exists and may need to be deleted before opening the EDB in "
326
+ f"HFSS 3D Layout."
327
+ # noqa: E501
319
328
  )
320
329
  else:
321
330
  try:
@@ -1523,12 +1532,11 @@ class Edb(Database):
1523
1532
  ``True`` when successful, ``False`` when failed.
1524
1533
 
1525
1534
  """
1526
- if tech_file or map_file:
1527
- control_file_temp = os.path.join(tempfile.gettempdir(), os.path.split(inputGDS)[-1][:-3] + "xml")
1528
- ControlFile(xml_input=control_file, tecnhology=tech_file, layer_map=map_file).write_xml(control_file_temp)
1529
- elif tech_file:
1535
+ if not is_linux and tech_file:
1530
1536
  self.logger.error("Technology files are supported only in Linux. Use control file instead.")
1531
1537
  return False
1538
+ control_file_temp = os.path.join(tempfile.gettempdir(), os.path.split(inputGDS)[-1][:-3] + "xml")
1539
+ ControlFile(xml_input=control_file, tecnhology=tech_file, layer_map=map_file).write_xml(control_file_temp)
1532
1540
  if self.import_layout_pcb(
1533
1541
  inputGDS,
1534
1542
  working_dir=WorkDir,
@@ -4314,7 +4322,7 @@ class Edb(Database):
4314
4322
  @pyedb_function_handler
4315
4323
  def _clean_string_for_variable_name(self, variable_name):
4316
4324
  """Remove forbidden character for variable name.
4317
- Parameter
4325
+ Parameters
4318
4326
  ----------
4319
4327
  variable_name : str
4320
4328
  Variable name.
@@ -4331,6 +4339,158 @@ class Edb(Database):
4331
4339
 
4332
4340
  return variable_name
4333
4341
 
4342
+ def create_model_for_arbitrary_wave_ports(
4343
+ self,
4344
+ temp_directory,
4345
+ mounting_side="top",
4346
+ signal_nets=None,
4347
+ terminal_diameter=None,
4348
+ output_edb=None,
4349
+ launching_box_thickness="100um",
4350
+ ):
4351
+ """Generate EDB design to be consumed by PyAEDT to generate arbitrary wave ports shapes.
4352
+ This model has to be considered as merged onto another one. The current opened design must have voids
4353
+ surrounding the pad-stacks where wave ports terminal will be created. THe open design won't be edited, only
4354
+ primitives like voids and pads-stack definition included in the voids are collected to generate a new design.
4355
+
4356
+ Parameters
4357
+ ----------
4358
+ temp_directory : str
4359
+ Temporary directory used during the method execution.
4360
+
4361
+ mounting_side : str
4362
+ Gives the orientation to be considered for the current design. 2 options are available ``"top"`` and
4363
+ ``"bottom". Default value is ``"top"``. If ``"top"`` is selected the method will voids at the top signal
4364
+ layer, and the bottom layer if ``"bottom"`` is used.
4365
+
4366
+ signal_nets : List[str], optional
4367
+ Provides the nets to be included for the model creation. Default value is ``None``. If None is provided,
4368
+ all nets will be included.
4369
+
4370
+ terminal_diameter : float, str, optional
4371
+ When ``None``, the terminal diameter is evaluated at each pads-tack instance found inside the voids. The top
4372
+ or bottom layer pad diameter will be taken, depending on ``mounting_side`` selected. If value is provided,
4373
+ it will overwrite the evaluated diameter.
4374
+
4375
+ output_edb : str, optional
4376
+ The output EDB absolute. If ``None`` the edb is created in the ``temp_directory`` as default name
4377
+ `"waveport_model.aedb"``
4378
+
4379
+ launching_box_thickness : float, str, optional
4380
+ Launching box thickness used for wave ports. Default value is ``"100um"``.
4381
+
4382
+ Returns
4383
+ -------
4384
+ bool
4385
+ ``True`` when succeeded, ``False`` if failed.
4386
+ """
4387
+ if not temp_directory:
4388
+ self.logger.error("Temp directory must be provided when creating model foe arbitrary wave port")
4389
+ return False
4390
+ if not output_edb:
4391
+ output_edb = os.path.join(temp_directory, "waveport_model.aedb")
4392
+ if os.path.isdir(temp_directory):
4393
+ shutil.rmtree(temp_directory)
4394
+ reference_layer = list(self.stackup.signal_layers.keys())[0]
4395
+ if mounting_side.lower == "bottom":
4396
+ reference_layer = list(self.stackup.signal_layers.keys())[-1]
4397
+ if not signal_nets:
4398
+ signal_nets = list(self.nets.signal.keys())
4399
+
4400
+ used_padstack_defs = []
4401
+ padstack_instances_index = rtree.index.Index()
4402
+ for padstack_inst in list(self.padstacks.instances.values()):
4403
+ if not reference_layer in [padstack_inst.start_layer, padstack_inst.stop_layer]:
4404
+ padstack_inst.delete()
4405
+ else:
4406
+ if padstack_inst.net_name in signal_nets:
4407
+ padstack_instances_index.insert(padstack_inst.id, padstack_inst.position)
4408
+ if not padstack_inst.padstack_definition in used_padstack_defs:
4409
+ used_padstack_defs.append(padstack_inst.padstack_definition)
4410
+
4411
+ polys = [
4412
+ poly
4413
+ for poly in self.modeler.primitives
4414
+ if poly.layer_name == reference_layer and poly.type == "Polygon" and poly.has_voids
4415
+ ]
4416
+ if not polys:
4417
+ self.logger.error(
4418
+ f"No polygon found with voids on layer {reference_layer} during model creation for "
4419
+ f"arbitrary wave ports"
4420
+ )
4421
+ return False
4422
+ void_padstacks = []
4423
+ for poly in polys:
4424
+ for void in poly.voids:
4425
+ void_bbox = (
4426
+ void.polygon_data.edb_api.GetBBox().Item1.X.ToDouble(),
4427
+ void.polygon_data.edb_api.GetBBox().Item1.Y.ToDouble(),
4428
+ void.polygon_data.edb_api.GetBBox().Item2.X.ToDouble(),
4429
+ void.polygon_data.edb_api.GetBBox().Item2.Y.ToDouble(),
4430
+ )
4431
+ included_instances = list(padstack_instances_index.intersection(void_bbox))
4432
+ if included_instances:
4433
+ void_padstacks.append((void, [self.padstacks.instances[edb_id] for edb_id in included_instances]))
4434
+
4435
+ if not void_padstacks:
4436
+ self.logger.error(
4437
+ "No padstack instances found inside evaluated voids during model creation for arbitrary" "waveports"
4438
+ )
4439
+ return False
4440
+ cloned_edb = Edb(edbpath=output_edb, edbversion=self.edbversion)
4441
+ cloned_edb.stackup.add_layer(layer_name="ref", layer_type="signal", thickness=0.0, material="pec")
4442
+ cloned_edb.stackup.add_layer(
4443
+ layer_name="ports",
4444
+ layer_type="signal",
4445
+ thickness=self.stackup.signal_layers[reference_layer].thickness,
4446
+ material="pec",
4447
+ )
4448
+ box_thick = "100um"
4449
+ if launching_box_thickness:
4450
+ box_thick = self.edb_value(launching_box_thickness).ToString()
4451
+ if mounting_side == "top":
4452
+ cloned_edb.stackup.add_layer(
4453
+ layer_name="port_pec", layer_type="signal", thickness=box_thick, method="add_on_bottom", material="pec"
4454
+ )
4455
+ else:
4456
+ cloned_edb.stackup.add_layer(
4457
+ layer_name="port_pec", layer_type="signal", thickness=box_thick, method="add_on_top", material="pec"
4458
+ )
4459
+
4460
+ for void_info in void_padstacks:
4461
+ port_poly = cloned_edb.modeler.create_polygon(
4462
+ main_shape=void_info[0].polygon_data.edb_api, layer_name="ref", net_name="GND"
4463
+ )
4464
+ pec_poly = cloned_edb.modeler.create_polygon(
4465
+ main_shape=port_poly.polygon_data.edb_api, layer_name="port_pec", net_name="GND"
4466
+ )
4467
+ pec_poly.scale(1.5)
4468
+
4469
+ for void_info in void_padstacks:
4470
+ for inst in void_info[1]:
4471
+ if not terminal_diameter:
4472
+ pad_diameter = (
4473
+ self.padstacks.definitions[inst.padstack_definition]
4474
+ .pad_by_layer[reference_layer]
4475
+ .parameters_values[0]
4476
+ )
4477
+ else:
4478
+ pad_diameter = self.edb_value(terminal_diameter).ToDouble()
4479
+ _temp_circle = cloned_edb.modeler.create_circle(
4480
+ layer_name="ports",
4481
+ x=inst.position[0],
4482
+ y=inst.position[1],
4483
+ radius=pad_diameter / 2,
4484
+ net_name=inst.net_name,
4485
+ )
4486
+ if not _temp_circle:
4487
+ self.logger.error(
4488
+ f"Failed to create circle for terminal during create_model_for_arbitrary_wave_ports"
4489
+ )
4490
+ cloned_edb.save_as(output_edb)
4491
+ cloned_edb.close()
4492
+ return True
4493
+
4334
4494
  @property
4335
4495
  def definitions(self):
4336
4496
  """Definitions class."""
@@ -20,7 +20,7 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- from pyedb.dotnet.edb_core.obj_base import ObjBase
23
+ from pyedb.dotnet.edb_core.utilities.obj_base import ObjBase
24
24
  from pyedb.generic.general_methods import pyedb_function_handler
25
25
 
26
26
 
@@ -20,7 +20,7 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- from pyedb.dotnet.edb_core.obj_base import ObjBase
23
+ from pyedb.dotnet.edb_core.utilities.obj_base import ObjBase
24
24
  from pyedb.generic.general_methods import pyedb_function_handler
25
25
 
26
26