pyedb 0.12.2__py3-none-any.whl → 0.13.dev0__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 (30) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_common.py +48 -0
  3. pyedb/configuration/cfg_components.py +94 -166
  4. pyedb/configuration/cfg_data.py +8 -10
  5. pyedb/configuration/cfg_operations.py +63 -0
  6. pyedb/configuration/cfg_package_definition.py +128 -0
  7. pyedb/configuration/cfg_stackup.py +80 -46
  8. pyedb/configuration/configuration.py +45 -15
  9. pyedb/dotnet/edb.py +167 -7
  10. pyedb/dotnet/edb_core/cell/hierarchy/model.py +1 -1
  11. pyedb/dotnet/edb_core/{edb_data/connectable.py → cell/layout_obj.py} +1 -1
  12. pyedb/dotnet/edb_core/cell/primitive.py +142 -0
  13. pyedb/dotnet/edb_core/components.py +1 -1
  14. pyedb/dotnet/edb_core/definition/component_def.py +1 -1
  15. pyedb/dotnet/edb_core/definition/component_model.py +1 -1
  16. pyedb/dotnet/edb_core/definition/definition_obj.py +1 -1
  17. pyedb/dotnet/edb_core/definition/package_def.py +2 -1
  18. pyedb/dotnet/edb_core/edb_data/components_data.py +19 -7
  19. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +3 -3
  20. pyedb/dotnet/edb_core/edb_data/primitives_data.py +5 -126
  21. pyedb/dotnet/edb_core/edb_data/terminals.py +2 -2
  22. pyedb/dotnet/edb_core/geometry/polygon_data.py +1 -1
  23. pyedb/dotnet/edb_core/materials.py +2 -1
  24. pyedb/dotnet/edb_core/padstack.py +86 -27
  25. pyedb/generic/general_methods.py +33 -0
  26. {pyedb-0.12.2.dist-info → pyedb-0.13.dev0.dist-info}/METADATA +1 -1
  27. {pyedb-0.12.2.dist-info → pyedb-0.13.dev0.dist-info}/RECORD +30 -26
  28. /pyedb/dotnet/edb_core/{obj_base.py → utilities/obj_base.py} +0 -0
  29. {pyedb-0.12.2.dist-info → pyedb-0.13.dev0.dist-info}/LICENSE +0 -0
  30. {pyedb-0.12.2.dist-info → pyedb-0.13.dev0.dist-info}/WHEEL +0 -0
@@ -20,34 +20,68 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
-
23
+ from pyedb.configuration.cfg_common import CfgBase
24
24
  from pyedb.generic.general_methods import pyedb_function_handler
25
25
 
26
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
+
27
50
  class CfgStackup:
28
51
  def __init__(self, pedb, data):
29
52
  self._pedb = pedb
30
53
 
31
- self.materials = [CfgMaterial(self._pedb, **mat) for mat in data.get("materials", [])]
32
- self.layers = [CfgLayer(self._pedb, **lay) for lay in data.get("layers", [])]
54
+ self.materials = [CfgMaterial(**mat) for mat in data.get("materials", [])]
55
+ self.layers = [CfgLayer(**lay) for lay in data.get("layers", [])]
33
56
 
34
57
  @pyedb_function_handler
35
58
  def apply(self):
36
59
  """Apply configuration settings to the current design"""
37
60
  if len(self.materials):
38
61
  self.__apply_materials()
62
+
63
+ input_signal_layers = [i for i in self.layers if i.type.lower() == "signal"]
64
+
39
65
  if len(self.layers):
40
- self.__apply_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)
41
79
 
42
80
  @pyedb_function_handler
43
81
  def __apply_layers(self):
44
82
  """Apply layer settings to the current design"""
45
83
  layers = list()
46
84
  layers.extend(self.layers)
47
- input_signal_layers = [i for i in layers if i.type.lower() == "signal"]
48
- if not len(input_signal_layers) == len(self._pedb.stackup.signal_layers):
49
- self._pedb.logger.error("Input signal layer count do not match.")
50
- return False
51
85
 
52
86
  removal_list = []
53
87
  lc_signal_layers = []
@@ -66,9 +100,7 @@ class CfgStackup:
66
100
  if l.type == "signal":
67
101
  layer_id = lc_signal_layers[signal_idx]
68
102
  layer_name = id_name[layer_id]
69
- attrs = {
70
- i: j for i, j in l.__dict__.items() if not i.startswith("_") and isinstance(j, (int, float, str))
71
- }
103
+ attrs = l.get_attributes()
72
104
  self._pedb.stackup.layers[layer_name].update(**attrs)
73
105
  signal_idx = signal_idx + 1
74
106
 
@@ -79,13 +111,11 @@ class CfgStackup:
79
111
  if l.type == "signal":
80
112
  prev_layer_clone = self._pedb.stackup.layers[l.name]
81
113
  else:
82
- attrs = {i: j for i, j in l.__dict__.items() if not i.startswith("_") and isinstance(j, (int, float, str))}
114
+ attrs = l.get_attributes()
83
115
  prev_layer_clone = self._pedb.stackup.add_layer_top(**attrs)
84
116
  for idx, l in enumerate(layers):
85
117
  if l.type == "dielectric":
86
- attrs = {
87
- i: j for i, j in l.__dict__.items() if not i.startswith("_") and isinstance(j, (int, float, str))
88
- }
118
+ attrs = l.get_attributes()
89
119
  prev_layer_clone = self._pedb.stackup.add_layer_below(base_layer_name=prev_layer_clone.name, **attrs)
90
120
  elif l.type == "signal":
91
121
  prev_layer_clone = self._pedb.stackup.layers[l.name]
@@ -98,38 +128,42 @@ class CfgStackup:
98
128
  if mat_in_cfg.name.lower() in materials_in_db:
99
129
  self._pedb.materials.delete_material(materials_in_db[mat_in_cfg.name.lower()])
100
130
 
101
- attrs = {
102
- i: j
103
- for i, j in mat_in_cfg.__dict__.items()
104
- if not i.startswith("_") and isinstance(j, (int, float, str))
105
- }
131
+ attrs = mat_in_cfg.get_attributes()
106
132
  mat = self._pedb.materials.add_material(**attrs)
107
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
108
143
 
109
- class CfgMaterial:
110
- def __init__(self, pedb, **kwargs):
111
- self._pedb = pedb
112
- self.name = kwargs.get("name")
113
- self.permittivity = kwargs.get("permittivity", None)
114
- self.conductivity = kwargs.get("conductivity", None)
115
- self.dielectric_loss_tangent = kwargs.get("dielectric_loss_tangent", None)
116
- self.magnetic_loss_tangent = kwargs.get("magnetic_loss_tangent", None)
117
- self.mass_density = kwargs.get("mass_density", None)
118
- self.permeability = kwargs.get("permeability", None)
119
- self.poisson_ratio = kwargs.get("poisson_ratio", None)
120
- self.specific_heat = kwargs.get("specific_heat", None)
121
- self.thermal_conductivity = kwargs.get("thermal_conductivity", None)
122
- self.youngs_modulus = kwargs.get("youngs_modulus", None)
123
- self.thermal_expansion_coefficient = kwargs.get("thermal_expansion_coefficient", None)
124
-
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
125
155
 
126
- class CfgLayer:
127
- def __init__(self, pedb, **kwargs):
128
- self._pedb = pedb
129
- self.name = kwargs.get("name", None)
130
- self.type = kwargs.get("type", None)
131
- self.material = kwargs.get("material", None)
132
- self.fill_material = kwargs.get("fill_material", None)
133
- self.thickness = kwargs.get("thickness", None)
134
- self.etch_factor = kwargs.get("etch_factor", None)
135
- self.lower_elevation = kwargs.get("lower_elevation", None)
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:
@@ -151,12 +150,10 @@ class Configuration:
151
150
  spice_model.apply()
152
151
 
153
152
  # Configure package definitions
154
- if "package_definitions" in self.data:
155
- self._load_package_def()
153
+ self.cfg_data.package_definitions.apply()
156
154
 
157
155
  # Configure operations
158
- if "operations" in self.data:
159
- self._load_operations()
156
+ self.cfg_data.operations.apply()
160
157
 
161
158
  return True
162
159
 
@@ -217,14 +214,6 @@ class Configuration:
217
214
  elif l["type"] == "signal":
218
215
  prev_layer_clone = self._pedb.stackup.layers[l["name"]]
219
216
 
220
- @pyedb_function_handler
221
- def _load_operations(self):
222
- """Imports operation information from JSON."""
223
- operations = self.data["operations"]
224
- cutout = operations.get("cutout", None)
225
- if cutout:
226
- self._pedb.cutout(**cutout)
227
-
228
217
  @pyedb_function_handler
229
218
  def _load_package_def(self):
230
219
  """Imports package definition information from JSON."""
@@ -268,3 +257,44 @@ class Configuration:
268
257
  )
269
258
  for _, i in comp_list.items():
270
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
 
@@ -0,0 +1,142 @@
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.dotnet.edb_core.cell.layout_obj import Connectable
24
+
25
+
26
+ class Primitive(Connectable):
27
+ """Manages EDB functionalities for a primitives.
28
+
29
+ It inherits EDB Object properties.
30
+
31
+ Examples
32
+ --------
33
+ >>> from pyedb import Edb
34
+ >>> edb = Edb(myedb, edbversion="2021.2")
35
+ >>> edb_prim = edb.modeler.primitives[0]
36
+ >>> edb_prim.is_void # Class Property
37
+ >>> edb_prim.IsVoid() # EDB Object Property
38
+ """
39
+
40
+ def __init__(self, pedb, edb_object):
41
+ super().__init__(pedb, edb_object)
42
+ self._app = self._pedb
43
+ self._core_stackup = pedb.stackup
44
+ self._core_net = pedb.nets
45
+ self.primitive_object = self._edb_object
46
+
47
+ @property
48
+ def type(self):
49
+ """Return the type of the primitive.
50
+
51
+ Expected output is among ``"Circle"``, ``"Rectangle"``,``"Polygon"``,``"Path"`` or ``"Bondwire"``.
52
+
53
+ Returns
54
+ -------
55
+ str
56
+ """
57
+ try:
58
+ return self._edb_object.GetPrimitiveType().ToString()
59
+ except AttributeError: # pragma: no cover
60
+ return ""
61
+
62
+ @property
63
+ def net_name(self):
64
+ """Get the primitive net name.
65
+
66
+ Returns
67
+ -------
68
+ str
69
+ """
70
+ return self.net.GetName()
71
+
72
+ @net_name.setter
73
+ def net_name(self, name):
74
+ if isinstance(name, str):
75
+ net = self._app.nets.nets[name].net_object
76
+ self.primitive_object.SetNet(net)
77
+ else:
78
+ try:
79
+ self.net = name.name
80
+ except: # pragma: no cover
81
+ self._app.logger.error("Failed to set net name.")
82
+
83
+ @property
84
+ def layer(self):
85
+ """Get the primitive edb layer object."""
86
+ try:
87
+ layer_name = self.primitive_object.GetLayer().GetName()
88
+ return self._pedb.stackup.layers[layer_name]
89
+ except (KeyError, AttributeError): # pragma: no cover
90
+ return None
91
+
92
+ @property
93
+ def layer_name(self):
94
+ """Get the primitive layer name.
95
+
96
+ Returns
97
+ -------
98
+ str
99
+ """
100
+ try:
101
+ return self.layer.name
102
+ except (KeyError, AttributeError): # pragma: no cover
103
+ return None
104
+
105
+ @layer_name.setter
106
+ def layer_name(self, val):
107
+ layer_list = list(self._core_stackup.layers.keys())
108
+ if isinstance(val, str) and val in layer_list:
109
+ layer = self._core_stackup.layers[val]._edb_layer
110
+ if layer:
111
+ self.primitive_object.SetLayer(layer)
112
+ else:
113
+ raise AttributeError("Layer {} not found.".format(val))
114
+ elif isinstance(val, type(self._core_stackup.layers[layer_list[0]])):
115
+ try:
116
+ self.primitive_object.SetLayer(val._edb_layer)
117
+ except:
118
+ raise AttributeError("Failed to assign new layer on primitive.")
119
+ else:
120
+ raise AttributeError("Invalid input value")
121
+
122
+ @property
123
+ def is_void(self):
124
+ """Either if the primitive is a void or not.
125
+
126
+ Returns
127
+ -------
128
+ bool
129
+ """
130
+ try:
131
+ return self._edb_object.IsVoid()
132
+ except AttributeError: # pragma: no cover
133
+ return None
134
+
135
+ def get_connected_objects(self):
136
+ """Get connected objects.
137
+
138
+ Returns
139
+ -------
140
+ list
141
+ """
142
+ return self._pedb.get_connected_objects(self._layout_obj_instance)
@@ -1983,7 +1983,7 @@ class Components(object):
1983
1983
  if not sball_mid_diam:
1984
1984
  sball_mid_diam = sball_diam
1985
1985
 
1986
- if shape == "Cylinder":
1986
+ if shape.lower() == "cylinder":
1987
1987
  sball_shape = self._edb.definition.SolderballShape.Cylinder
1988
1988
  else:
1989
1989
  sball_shape = self._edb.definition.SolderballShape.Spheroid