pyedb 0.43.0__py3-none-any.whl → 0.44.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 (52) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_boundaries.py +20 -14
  3. pyedb/configuration/cfg_components.py +1 -1
  4. pyedb/configuration/cfg_general.py +6 -4
  5. pyedb/configuration/cfg_modeler.py +1 -0
  6. pyedb/configuration/cfg_package_definition.py +41 -1
  7. pyedb/configuration/cfg_padstacks.py +611 -256
  8. pyedb/configuration/cfg_ports_sources.py +75 -15
  9. pyedb/configuration/cfg_s_parameter_models.py +30 -0
  10. pyedb/configuration/cfg_setup.py +94 -21
  11. pyedb/configuration/cfg_stackup.py +44 -0
  12. pyedb/dotnet/database/edb_data/design_options.py +19 -1
  13. pyedb/dotnet/database/edb_data/padstacks_data.py +9 -4
  14. pyedb/dotnet/database/geometry/polygon_data.py +4 -2
  15. pyedb/dotnet/database/padstack.py +6 -2
  16. pyedb/dotnet/database/utilities/simulation_setup.py +7 -17
  17. pyedb/dotnet/database/utilities/siwave_simulation_setup.py +30 -0
  18. pyedb/grpc/database/components.py +1 -1
  19. pyedb/grpc/database/definition/component_def.py +15 -0
  20. pyedb/grpc/database/definition/component_pin.py +1 -1
  21. pyedb/grpc/database/definition/materials.py +27 -0
  22. pyedb/grpc/database/definition/package_def.py +20 -2
  23. pyedb/grpc/database/definition/padstack_def.py +5 -2
  24. pyedb/grpc/database/hierarchy/component.py +4 -2
  25. pyedb/grpc/database/hierarchy/pingroup.py +12 -8
  26. pyedb/grpc/database/layers/layer.py +28 -0
  27. pyedb/grpc/database/layers/stackup_layer.py +281 -40
  28. pyedb/grpc/database/layout/layout.py +12 -6
  29. pyedb/grpc/database/modeler.py +8 -8
  30. pyedb/grpc/database/primitive/bondwire.py +3 -3
  31. pyedb/grpc/database/primitive/circle.py +1 -1
  32. pyedb/grpc/database/primitive/padstack_instance.py +13 -3
  33. pyedb/grpc/database/primitive/path.py +2 -2
  34. pyedb/grpc/database/primitive/polygon.py +3 -3
  35. pyedb/grpc/database/primitive/primitive.py +1 -1
  36. pyedb/grpc/database/primitive/rectangle.py +2 -2
  37. pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +73 -30
  38. pyedb/grpc/database/source_excitations.py +7 -7
  39. pyedb/grpc/database/stackup.py +14 -6
  40. pyedb/grpc/database/terminal/bundle_terminal.py +3 -3
  41. pyedb/grpc/database/terminal/edge_terminal.py +2 -2
  42. pyedb/grpc/database/terminal/padstack_instance_terminal.py +42 -2
  43. pyedb/grpc/database/terminal/pingroup_terminal.py +35 -2
  44. pyedb/grpc/database/terminal/point_terminal.py +10 -1
  45. pyedb/grpc/database/terminal/terminal.py +4 -4
  46. pyedb/grpc/database/utility/hfss_extent_info.py +14 -10
  47. pyedb/grpc/edb.py +8 -8
  48. pyedb/misc/misc.py +13 -0
  49. {pyedb-0.43.0.dist-info → pyedb-0.44.0.dist-info}/METADATA +1 -1
  50. {pyedb-0.43.0.dist-info → pyedb-0.44.0.dist-info}/RECORD +52 -52
  51. {pyedb-0.43.0.dist-info → pyedb-0.44.0.dist-info}/LICENSE +0 -0
  52. {pyedb-0.43.0.dist-info → pyedb-0.44.0.dist-info}/WHEEL +0 -0
@@ -33,9 +33,33 @@ from pyedb.dotnet.database.geometry.point_data import PointData
33
33
  class CfgTerminalInfo(CfgBase):
34
34
  CFG_TERMINAL_TYPES = ["pin", "net", "pin_group", "nearest_pin", "coordinates"]
35
35
 
36
+ class Grpc:
37
+ def __init__(self, parent):
38
+ self.parent = parent
39
+ self._pedb = parent._pedb
40
+
41
+ def update_contact_radius(self, radius):
42
+ from ansys.edb.core.utility.value import Value as GrpcValue
43
+
44
+ self.parent.contact_radius = GrpcValue(radius).value
45
+
46
+ class DotNet(Grpc):
47
+ def __init__(self, parent):
48
+ super().__init__(parent)
49
+
50
+ def update_contact_radius(self, radius):
51
+ self.parent.contact_radius = self._pedb.edb_value(radius).ToDouble()
52
+
36
53
  def __init__(self, pedb, **kwargs):
37
54
  self._pedb = pedb
38
- if "pin" in kwargs:
55
+ if self._pedb.grpc:
56
+ self.api = self.Grpc(self)
57
+ else:
58
+ self.api = self.DotNet(self)
59
+
60
+ if kwargs.get("padstack"):
61
+ self.type = "padstack"
62
+ elif "pin" in kwargs:
39
63
  self.type = "pin"
40
64
  elif "net" in kwargs:
41
65
  self.type = "net"
@@ -52,7 +76,7 @@ class CfgTerminalInfo(CfgBase):
52
76
 
53
77
  self.contact_type = kwargs.get("contact_type", "default") # options are full, center, quad, inline
54
78
  contact_radius = "0.1mm" if kwargs.get("contact_radius") is None else kwargs.get("contact_radius")
55
- self.contact_radius = self._pedb.edb_value(contact_radius).ToDouble()
79
+ self.api.update_contact_radius(contact_radius)
56
80
  self.num_of_contact = kwargs.get("num_of_contact", 4)
57
81
  self.contact_expansion = kwargs.get("contact_expansion", 1)
58
82
 
@@ -60,7 +84,7 @@ class CfgTerminalInfo(CfgBase):
60
84
  return {self.type: self.value}
61
85
 
62
86
 
63
- class CfgCoordianteTerminalInfo(CfgTerminalInfo):
87
+ class CfgCoordinateTerminalInfo(CfgTerminalInfo):
64
88
  def __init__(self, pedb, **kwargs):
65
89
  super().__init__(pedb, **kwargs)
66
90
 
@@ -108,14 +132,14 @@ class CfgSources:
108
132
  pos_term_info = {"pin_group": pg.name}
109
133
  elif src.terminal_type == "PadstackInstanceTerminal":
110
134
  refdes = src.component.refdes if src.component else ""
111
- pos_term_info = {"pin": src.padstack_instance.component_pin}
135
+ pos_term_info = {"padstack": src.padstack_instance.aedt_name}
112
136
 
113
137
  neg_term = self._pedb.terminals[src.ref_terminal.name]
114
138
  if neg_term.terminal_type == "PinGroupTerminal":
115
139
  pg = self._pedb.siwave.pin_groups[neg_term._edb_object.GetPinGroup().GetName()]
116
140
  neg_term_info = {"pin_group": pg.name}
117
141
  elif neg_term.terminal_type == "PadstackInstanceTerminal":
118
- neg_term_info = {"pin": neg_term.padstack_instance.component_pin}
142
+ neg_term_info = {"padstack": neg_term.padstack_instance.aedt_name}
119
143
 
120
144
  cfg_src = CfgSource(
121
145
  self._pedb,
@@ -137,9 +161,30 @@ class CfgSources:
137
161
 
138
162
 
139
163
  class CfgPorts:
164
+ class Grpc:
165
+ def __init__(self, parent):
166
+ self.parent = parent
167
+ self._pedb = parent._pedb
168
+
169
+ def get_pin_group(self, port):
170
+ return self._pedb.siwave.pin_groups[port._edb_object.pin_group.name]
171
+
172
+ def get_edge_info(self, port):
173
+ return port._edb_object.GetEdges()[0].GetParameters()
174
+
175
+ class DotNet(Grpc):
176
+ def __init__(self, parent):
177
+ super().__init__(parent)
178
+
179
+ def get_pin_group(self, port):
180
+ return self._pedb.siwave.pin_groups[port._edb_object.GetPinGroup().GetName()]
181
+
140
182
  def __init__(self, pedb, ports_data):
141
183
  self._pedb = pedb
142
-
184
+ if self._pedb.grpc:
185
+ self.api = self.Grpc(self)
186
+ else:
187
+ self.api = self.DotNet(self)
143
188
  self.ports = []
144
189
  for p in ports_data:
145
190
  if p["type"] == "wave_port":
@@ -156,6 +201,9 @@ class CfgPorts:
156
201
  for i in self._pedb.layout.primitives:
157
202
  if i.aedt_name:
158
203
  edb_primitives[i.aedt_name] = i
204
+ for i in self._pedb.layout.padstack_instances:
205
+ if i.aedt_name:
206
+ edb_primitives[i.aedt_name] = i
159
207
  for p in self.ports:
160
208
  if p.type in ["wave_port", "diff_wave_port"]:
161
209
  p.set_parameters_to_edb(edb_primitives)
@@ -171,6 +219,8 @@ class CfgPorts:
171
219
  if not p.ref_terminal:
172
220
  if p.terminal_type == "PadstackInstanceTerminal":
173
221
  port_type = "coax"
222
+ elif p.terminal_type == "PinGroupTerminal":
223
+ port_type = "circuit"
174
224
  elif p.hfss_type == "Wave":
175
225
  port_type = "wave_port"
176
226
  else:
@@ -180,11 +230,11 @@ class CfgPorts:
180
230
 
181
231
  if p.terminal_type == "PinGroupTerminal":
182
232
  refdes = ""
183
- pg = self._pedb.siwave.pin_groups[p._edb_object.GetPinGroup().GetName()]
233
+ pg = self.api.get_pin_group(p)
184
234
  pos_term_info = {"pin_group": pg.name}
185
235
  elif p.terminal_type == "PadstackInstanceTerminal":
186
236
  refdes = p.component.refdes if p.component else ""
187
- pos_term_info = {"pin": p.padstack_instance.component_pin}
237
+ pos_term_info = {"padstack": p.padstack_instance.aedt_name}
188
238
  elif p.terminal_type == "PointTerminal":
189
239
  refdes = ""
190
240
  pos_term_info = {"coordinates": {"layer": p.layer.name, "point": p.location, "net": p.net.name}}
@@ -192,10 +242,11 @@ class CfgPorts:
192
242
  if port_type == "circuit":
193
243
  neg_term = self._pedb.terminals[p.ref_terminal.name]
194
244
  if neg_term.terminal_type == "PinGroupTerminal":
195
- pg = self._pedb.siwave.pin_groups[neg_term._edb_object.GetPinGroup().GetName()]
245
+ pg = self.api.get_pin_group(neg_term)
246
+ # pg = self._pedb.siwave.pin_groups[neg_term._edb_object.GetPinGroup().GetName()]
196
247
  neg_term_info = {"pin_group": pg.name}
197
248
  elif neg_term.terminal_type == "PadstackInstanceTerminal":
198
- neg_term_info = {"pin": neg_term.padstack_instance.component_pin}
249
+ neg_term_info = {"padstack": neg_term.padstack_instance.aedt_name}
199
250
  elif neg_term.terminal_type == "PointTerminal":
200
251
  neg_term_info = {
201
252
  "coordinates": {
@@ -269,7 +320,7 @@ class CfgCircuitElement(CfgBase):
269
320
 
270
321
  pos = kwargs["positive_terminal"] # {"pin" : "A1"}
271
322
  if list(pos.keys())[0] == "coordinates":
272
- self.positive_terminal_info = CfgCoordianteTerminalInfo(self._pedb, **pos)
323
+ self.positive_terminal_info = CfgCoordinateTerminalInfo(self._pedb, **pos)
273
324
  else:
274
325
  self.positive_terminal_info = CfgTerminalInfo(self._pedb, **pos)
275
326
  if not self.positive_terminal_info.reference_designator:
@@ -279,7 +330,7 @@ class CfgCircuitElement(CfgBase):
279
330
  if len(neg) == 0:
280
331
  self.negative_terminal_info = None
281
332
  elif list(neg.keys())[0] == "coordinates":
282
- self.negative_terminal_info = CfgCoordianteTerminalInfo(self._pedb, **neg)
333
+ self.negative_terminal_info = CfgCoordinateTerminalInfo(self._pedb, **neg)
283
334
  elif list(neg.keys())[0] == "nearest_pin":
284
335
  self.negative_terminal_info = CfgNearestPinTerminalInfo(self._pedb, **neg)
285
336
  else:
@@ -306,6 +357,11 @@ class CfgCircuitElement(CfgBase):
306
357
  self._pedb.nets.find_or_create_net(net_name)
307
358
  pos_coor_terminal[self.name] = self._pedb.get_point_terminal(self.name, net_name, point, layer)
308
359
 
360
+ elif pos_type == "padstack":
361
+ for pds in self._pedb.layout.padstack_instances:
362
+ if pds.aedt_name == pos_value:
363
+ pos_objs.update({pos_value: pds})
364
+ break
309
365
  elif pos_type == "pin":
310
366
  pins = {
311
367
  pos_value: self._pedb.components.instances[self.positive_terminal_info.reference_designator].pins[
@@ -403,6 +459,11 @@ class CfgCircuitElement(CfgBase):
403
459
  ) # terminal type pin or net
404
460
  # create pin group
405
461
  neg_obj = self._create_pin_group(pins, self.negative_terminal_info.reference_designator, True)
462
+ elif neg_type == "padstack":
463
+ for pds in self._pedb.layout.padstack_instances:
464
+ if pds.aedt_name == neg_value:
465
+ neg_obj = {neg_value: pds}
466
+ break
406
467
  elif neg_type == "pin":
407
468
  terminal_name = f"{self.negative_terminal_info.reference_designator}_{neg_value}"
408
469
  neg_obj = {
@@ -412,9 +473,8 @@ class CfgCircuitElement(CfgBase):
412
473
  }
413
474
  else:
414
475
  raise Exception(f"Wrong negative terminal type {neg_type}.")
415
- self.neg_terminal = [
416
- j.create_terminal(i) if not j.terminal else j.terminal for i, j in neg_obj.items()
417
- ][0]
476
+ neg_term = [j.create_terminal(i) if not j.terminal else j.terminal for i, j in neg_obj.items()][0]
477
+ self.neg_terminal = neg_term
418
478
 
419
479
  def _get_pins(self, terminal_type, terminal_value, reference_designator):
420
480
  terminal_value = terminal_value if isinstance(terminal_value, list) else [terminal_value]
@@ -125,6 +125,36 @@ class CfgSParameters:
125
125
  def __init__(self, parent):
126
126
  super().__init__(parent)
127
127
 
128
+ def apply(self):
129
+ for s_param in self.parent.s_parameters_models:
130
+ fpath = s_param.file_path
131
+ if not Path(fpath).anchor:
132
+ fpath = str(Path(self.parent.path_libraries) / fpath)
133
+ comp_def = self._pedb.definitions.component[s_param.component_definition]
134
+ if s_param.pin_order:
135
+ comp_def.set_properties(pin_order=s_param.pin_order)
136
+ comp_def.add_n_port_model(fpath, s_param.name)
137
+ comp_list = dict()
138
+ if s_param.apply_to_all:
139
+ comp_list.update(
140
+ {
141
+ refdes: comp
142
+ for refdes, comp in comp_def.components.items()
143
+ if refdes not in s_param.components
144
+ }
145
+ )
146
+ else:
147
+ comp_list.update(
148
+ {refdes: comp for refdes, comp in comp_def.components.items() if refdes in s_param.components}
149
+ )
150
+
151
+ for refdes, comp in comp_list.items():
152
+ if refdes in s_param.reference_net_per_component:
153
+ ref_net = s_param.reference_net_per_component[refdes]
154
+ else:
155
+ ref_net = s_param.reference_net
156
+ comp.use_s_parameter_model(s_param.name, reference_net=ref_net)
157
+
128
158
  def __init__(self, pedb, data, path_lib=None):
129
159
  self._pedb = pedb
130
160
  if self._pedb.grpc:
@@ -148,9 +148,8 @@ class CfgSIwaveDCSetup(CfgSetup):
148
148
  edb_setup = self.pedb.create_siwave_dc_setup(
149
149
  name=self.parent.name, dc_slider_position=self.parent.dc_slider_position
150
150
  )
151
- edb_setup.dc_settings.dc_slider_position = self.parent.dc_slider_position
152
- dc_ir_settings = self.parent.dc_ir_settings
153
- edb_setup.dc_ir_settings.export_dc_thermal_data = dc_ir_settings["export_dc_thermal_data"]
151
+ edb_setup.settings.dc.dc_slider_pos = self.parent.dc_slider_position
152
+ edb_setup.settings.export_dc_thermal_data = self.parent.dc_ir_settings["export_dc_thermal_data"]
154
153
 
155
154
  def retrieve_parameters_from_edb(self):
156
155
  self._retrieve_parameters_from_edb_common()
@@ -163,6 +162,14 @@ class CfgSIwaveDCSetup(CfgSetup):
163
162
  def __init__(self, parent):
164
163
  super().__init__(parent)
165
164
 
165
+ def set_parameters_to_edb(self):
166
+ edb_setup = self.pedb.create_siwave_dc_setup(
167
+ name=self.parent.name, dc_slider_position=self.parent.dc_slider_position
168
+ )
169
+ edb_setup.dc_settings.dc_slider_position = self.parent.dc_slider_position
170
+ dc_ir_settings = self.parent.dc_ir_settings
171
+ edb_setup.dc_ir_settings.export_dc_thermal_data = dc_ir_settings["export_dc_thermal_data"]
172
+
166
173
  def __init__(self, pedb, pyedb_obj, **kwargs):
167
174
  super().__init__(pedb, pyedb_obj, **kwargs)
168
175
  self.type = "siwave_dc"
@@ -202,6 +209,42 @@ class CfgHFSSSetup(CfgSetup):
202
209
  net_layer_list=i.get("nets_layers_list", {}),
203
210
  )
204
211
 
212
+ def retrieve_parameters_from_edb(self):
213
+ self._retrieve_parameters_from_edb_common()
214
+ single_frequency_adaptive_solution = self.pyedb_obj.settings.general.single_frequency_adaptive_solution
215
+ self.parent.f_adapt = single_frequency_adaptive_solution.adaptive_frequency
216
+ self.parent.max_num_passes = single_frequency_adaptive_solution.max_passes
217
+ self.parent.max_mag_delta_s = float(single_frequency_adaptive_solution.max_delta)
218
+ self.parent.freq_sweep = []
219
+ for sw in self.pyedb_obj.sweep_data:
220
+ self.parent.freq_sweep.append({"name": sw.name, "type": sw.type, "frequencies": sw.frequency_string})
221
+
222
+ self.parent.mesh_operations = []
223
+ from ansys.edb.core.simulation_setup.mesh_operation import (
224
+ LengthMeshOperation as GrpcLengthMeshOperation,
225
+ )
226
+
227
+ for mesh_op in self.pyedb_obj.mesh_operations:
228
+ if isinstance(mesh_op, GrpcLengthMeshOperation):
229
+ mop_type = "length"
230
+ else:
231
+ mop_type = "skin"
232
+ self.parent.mesh_operations.append(
233
+ {
234
+ "name": mesh_op.name,
235
+ "type": mop_type,
236
+ "max_elements": mesh_op.max_elements,
237
+ "max_length": mesh_op.max_length,
238
+ "restrict_length": mesh_op.restrict_max_length,
239
+ "refine_inside": mesh_op.refine_inside,
240
+ "nets_layers_list": mesh_op.net_layer_info,
241
+ }
242
+ )
243
+
244
+ class DotNet(Grpc):
245
+ def __init__(self, parent):
246
+ super().__init__(parent)
247
+
205
248
  def retrieve_parameters_from_edb(self):
206
249
  self._retrieve_parameters_from_edb_common()
207
250
  adaptive_frequency_data_list = list(self.pyedb_obj.adaptive_settings.adaptive_frequency_data_list)[0]
@@ -226,10 +269,6 @@ class CfgHFSSSetup(CfgSetup):
226
269
  }
227
270
  )
228
271
 
229
- class DotNet(Grpc):
230
- def __init__(self, parent):
231
- super().__init__(parent)
232
-
233
272
  def __init__(self, pedb, pyedb_obj, **kwargs):
234
273
  super().__init__(pedb, pyedb_obj, **kwargs)
235
274
  self.type = "hfss"
@@ -254,9 +293,56 @@ class CfgHFSSSetup(CfgSetup):
254
293
 
255
294
 
256
295
  class CfgSetups:
296
+ class Grpc:
297
+ def __init__(self, parent):
298
+ self.parent = parent
299
+ self._pedb = parent.pedb
300
+
301
+ def retrieve_parameters_from_edb(self):
302
+ self.parent.setups = []
303
+ for _, setup in self._pedb.setups.items():
304
+ if setup.type.name.lower() == "hfss":
305
+ hfss = CfgHFSSSetup(self._pedb, setup)
306
+ hfss.api.retrieve_parameters_from_edb()
307
+ self.parent.setups.append(hfss)
308
+ elif setup.type.name.lower() == "si_wave_dcir":
309
+ siwave_dc = CfgSIwaveDCSetup(self._pedb, setup)
310
+ siwave_dc.api.retrieve_parameters_from_edb()
311
+ self.parent.setups.append(siwave_dc)
312
+ elif setup.type.name.lower() == "si_wave":
313
+ siwave_ac = CfgSIwaveACSetup(self._pedb, setup)
314
+ siwave_ac.api.retrieve_parameters_from_edb()
315
+ self.parent.setups.append(siwave_ac)
316
+ elif setup.type.name.lower() == "raptor_x":
317
+ pass
318
+
319
+ class DotNet(Grpc):
320
+ def __init__(self, parent):
321
+ super().__init__(parent)
322
+
323
+ def retrieve_parameters_from_edb(self):
324
+ self.parent.setups = []
325
+ for _, setup in self._pedb.setups.items():
326
+ if setup.type == "hfss":
327
+ hfss = CfgHFSSSetup(self._pedb, setup)
328
+ hfss.api.retrieve_parameters_from_edb()
329
+ self.parent.setups.append(hfss)
330
+ elif setup.type == "siwave_dc":
331
+ siwave_dc = CfgSIwaveDCSetup(self._pedb, setup)
332
+ siwave_dc.api.retrieve_parameters_from_edb()
333
+ self.parent.setups.append(siwave_dc)
334
+ elif setup.type == "siwave_ac":
335
+ siwave_ac = CfgSIwaveACSetup(self._pedb, setup)
336
+ siwave_ac.api.retrieve_parameters_from_edb()
337
+ self.parent.setups.append(siwave_ac)
338
+
257
339
  def __init__(self, pedb, setups_data):
258
340
  self.pedb = pedb
259
341
  self.setups = []
342
+ if self.pedb.grpc:
343
+ self.api = self.Grpc(self)
344
+ else:
345
+ self.api = self.DotNet(self)
260
346
  for stp in setups_data:
261
347
  if stp["type"].lower() == "hfss":
262
348
  self.setups.append(CfgHFSSSetup(self.pedb, None, **stp))
@@ -273,17 +359,4 @@ class CfgSetups:
273
359
  return [i.to_dict() for i in self.setups]
274
360
 
275
361
  def retrieve_parameters_from_edb(self):
276
- self.setups = []
277
- for _, setup in self.pedb.setups.items():
278
- if setup.type == "hfss":
279
- hfss = CfgHFSSSetup(self.pedb, setup)
280
- hfss.api.retrieve_parameters_from_edb()
281
- self.setups.append(hfss)
282
- elif setup.type == "siwave_dc":
283
- siwave_dc = CfgSIwaveDCSetup(self.pedb, setup)
284
- siwave_dc.api.retrieve_parameters_from_edb()
285
- self.setups.append(siwave_dc)
286
- elif setup.type == "siwave_ac":
287
- siwave_ac = CfgSIwaveACSetup(self.pedb, setup)
288
- siwave_ac.api.retrieve_parameters_from_edb()
289
- self.setups.append(siwave_ac)
362
+ self.api.retrieve_parameters_from_edb()
@@ -164,6 +164,50 @@ class CfgStackup:
164
164
  def __init__(self, parent):
165
165
  super().__init__(parent)
166
166
 
167
+ def __apply_layers(self):
168
+ """Apply layer settings to the current design"""
169
+ layers = list()
170
+ layers.extend(self.parent.layers)
171
+
172
+ removal_list = []
173
+ lc_signal_layers = []
174
+ for name, obj in self._pedb.stackup.all_layers.items():
175
+ if obj.type == "dielectric":
176
+ removal_list.append(name)
177
+ elif obj.type == "signal":
178
+ lc_signal_layers.append(obj.id)
179
+ for l in removal_list:
180
+ self._pedb.stackup.remove_layer(l)
181
+
182
+ # update all signal layers
183
+ id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id}
184
+ signal_idx = 0
185
+ for l in layers:
186
+ if l.type == "signal":
187
+ layer_id = lc_signal_layers[signal_idx]
188
+ layer_name = id_name[layer_id]
189
+ attrs = l.get_attributes()
190
+ self._pedb.stackup.layers[layer_name].update(**attrs)
191
+ signal_idx = signal_idx + 1
192
+
193
+ # add all dielectric layers. Dielectric layers must be added last. Otherwise,
194
+ # dielectric layer will occupy signal and document layer id.
195
+ prev_layer_clone = None
196
+ l = layers.pop(0)
197
+ if l.type == "signal":
198
+ prev_layer_clone = self._pedb.stackup.layers[l.name]
199
+ else:
200
+ attrs = l.get_attributes()
201
+ prev_layer_clone = self._pedb.stackup.add_layer_top(**attrs)
202
+ for idx, l in enumerate(layers):
203
+ if l.type == "dielectric":
204
+ attrs = l.get_attributes()
205
+ prev_layer_clone = self._pedb.stackup.add_layer_below(
206
+ base_layer_name=prev_layer_clone.name, **attrs
207
+ )
208
+ elif l.type == "signal":
209
+ prev_layer_clone = self._pedb.stackup.layers[l.name]
210
+
167
211
  def __init__(self, pedb: Edb, data):
168
212
  self._pedb = pedb
169
213
  if self._pedb.grpc:
@@ -19,6 +19,7 @@
19
19
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
+ import warnings
22
23
 
23
24
 
24
25
  class EdbDesignOptions:
@@ -51,8 +52,25 @@ class EdbDesignOptions:
51
52
  ``True`` if antipad is always on, ``False`` otherwise.
52
53
 
53
54
  """
54
- return self._active_cell.GetAntiPadsAlwaysOn()
55
+ warnings.warn("Use new property :func:`anti_pads_always_on` instead.", DeprecationWarning)
56
+ return self.anti_pads_always_on
55
57
 
56
58
  @antipads_always_on.setter
57
59
  def antipads_always_on(self, value):
60
+ self.anti_pads_always_on = value
61
+
62
+ @property
63
+ def anti_pads_always_on(self):
64
+ """Whether to always turn on antipad.
65
+
66
+ Returns
67
+ -------
68
+ bool
69
+ ``True`` if antipad is always on, ``False`` otherwise.
70
+
71
+ """
72
+ return self._active_cell.GetAntiPadsAlwaysOn()
73
+
74
+ @anti_pads_always_on.setter
75
+ def anti_pads_always_on(self, value):
58
76
  self._active_cell.SetAntiPadsAlwaysOn(value)
@@ -35,6 +35,7 @@ from pyedb.dotnet.database.general import (
35
35
  snake_to_pascal,
36
36
  )
37
37
  from pyedb.dotnet.database.geometry.polygon_data import PolygonData
38
+ from pyedb.generic.data_handlers import float_units
38
39
  from pyedb.generic.general_methods import generate_unique_name
39
40
  from pyedb.modeler.geometry_operators import GeometryOperators
40
41
 
@@ -1958,15 +1959,18 @@ class EDBPadstackInstance(Primitive):
1958
1959
  pad_shape = padstack_pad.geometry_type
1959
1960
  params = padstack_pad.parameters_values
1960
1961
  polygon_data = padstack_pad._polygon_data_dotnet
1962
+ pad_offset = [float_units(padstack_pad.offset_x), float_units(padstack_pad.offset_y)]
1961
1963
 
1962
1964
  def _rotate(p):
1963
1965
  x = p[0] * math.cos(rotation) - p[1] * math.sin(rotation)
1964
1966
  y = p[0] * math.sin(rotation) + p[1] * math.cos(rotation)
1965
1967
  return [x, y]
1966
1968
 
1967
- def _translate(p):
1968
- x = p[0] + padstack_center[0]
1969
- y = p[1] + padstack_center[1]
1969
+ def _translate(p, t=None):
1970
+ if t is None:
1971
+ t = padstack_center
1972
+ x = p[0] + t[0]
1973
+ y = p[1] + t[1]
1970
1974
  return [x, y]
1971
1975
 
1972
1976
  rect = None
@@ -2092,7 +2096,8 @@ class EDBPadstackInstance(Primitive):
2092
2096
 
2093
2097
  if rect is None or len(rect) != 4:
2094
2098
  return False
2095
- path = self._pedb.modeler.Shape("polygon", points=rect)
2099
+ offset_rect = [_translate(p, _rotate(pad_offset)) for p in rect]
2100
+ path = self._pedb.modeler.Shape("polygon", points=offset_rect)
2096
2101
  pdata = self._pedb.modeler.shape_to_polygon_data(path)
2097
2102
  new_rect = []
2098
2103
  for point in pdata.Points:
@@ -89,8 +89,10 @@ class PolygonData:
89
89
  def create_from_points(self, points, closed=True):
90
90
  list_of_point_data = []
91
91
  for pt in points:
92
- list_of_point_data.append(PointData(self._pedb, x=pt[0], y=pt[1]))
93
- return self._pedb.edb_api.geometry.api_class.PolygonData(list_of_point_data, closed)
92
+ list_of_point_data.append(PointData(self._pedb, x=pt[0], y=pt[1])._edb_object)
93
+ return self._pedb.edb_api.geometry.api_class.PolygonData(
94
+ convert_py_list_to_net_list(list_of_point_data), closed
95
+ )
94
96
 
95
97
  def create_from_bounding_box(self, points):
96
98
  bbox = BBox(self._pedb, point_1=points[0], point_2=points[1])
@@ -1153,7 +1153,7 @@ class EdbPadstacks(object):
1153
1153
  Name of the net. The default is ``""``.
1154
1154
  via_name : str, optional
1155
1155
  The default is ``""``.
1156
- rotation : float, optional
1156
+ rotation : float, str, optional
1157
1157
  Rotation of the padstack in degrees. The default
1158
1158
  is ``0``.
1159
1159
  fromlayer :
@@ -1175,7 +1175,11 @@ class EdbPadstacks(object):
1175
1175
  padstack = self.definitions[pad].edb_padstack
1176
1176
  position = self._edb.geometry.point_data(position[0], position[1])
1177
1177
  net = self._pedb.nets.find_or_create_net(net_name)
1178
- rotation = self._get_edb_value(rotation * math.pi / 180)
1178
+ rotation = (
1179
+ self._get_edb_value(rotation * math.pi / 180)
1180
+ if not isinstance(rotation, str)
1181
+ else self._get_edb_value(rotation)
1182
+ )
1179
1183
  sign_layers_values = {i: v for i, v in self._pedb.stackup.signal_layers.items()}
1180
1184
  sign_layers = list(sign_layers_values.keys())
1181
1185
  if not fromlayer:
@@ -96,8 +96,6 @@ class SimulationSetup(object):
96
96
  if self._edb_object:
97
97
  self._name = self._edb_object.GetName()
98
98
 
99
- self._sweep_list = {}
100
-
101
99
  @property
102
100
  def sim_setup_info(self):
103
101
  return SimSetupInfo(self._pedb, sim_setup=self, edb_object=self._edb_object.GetSimSetupInfo())
@@ -239,7 +237,7 @@ class SimulationSetup(object):
239
237
  """List of frequency sweeps."""
240
238
  return {i.name: i for i in self.sim_setup_info.sweep_data_list}
241
239
 
242
- def add_sweep(self, name, frequency_set: list = None, sweep_type: str = "interpolation", **kwargs):
240
+ def add_sweep(self, name: str = None, frequency_set: list = None, sweep_type: str = "interpolation", **kwargs):
243
241
  """Add frequency sweep.
244
242
 
245
243
  Parameters
@@ -292,15 +290,7 @@ class SimulationSetup(object):
292
290
  sweep_data: SweepData
293
291
  """
294
292
  warnings.warn("Use new property :func:`add_sweep_data` instead.", DeprecationWarning)
295
- self._sweep_list[sweep_data.name] = sweep_data
296
- edb_setup_info = self.sim_setup_info
297
-
298
- if self._setup_type in ["kSIwave", "kHFSS", "kRaptorX", "kHFSSPI"]:
299
- for _, v in self._sweep_list.items():
300
- edb_setup_info.SweepDataList.Add(v._edb_object)
301
-
302
- self._edb_object = self._set_edb_setup_info(edb_setup_info)
303
- self._update_setup()
293
+ return self.sim_setup_info.add_sweep_data(sweep_data)
304
294
 
305
295
  def delete_frequency_sweep(self, sweep_data):
306
296
  """Delete a frequency sweep.
@@ -310,17 +300,17 @@ class SimulationSetup(object):
310
300
  sweep_data : EdbFrequencySweep.
311
301
  """
312
302
  name = sweep_data.name
313
- if name in self._sweep_list:
314
- self._sweep_list.pop(name)
303
+ if name in self.sweeps:
304
+ self.sweeps.pop(name)
315
305
 
316
306
  fsweep = []
317
- if self.frequency_sweeps:
318
- fsweep = [val for key, val in self.frequency_sweeps.items() if not key == name]
307
+ if self.sweeps:
308
+ fsweep = [val for key, val in self.sweeps.items() if not key == name]
319
309
  self.sim_setup_info._edb_object.SweepDataList.Clear()
320
310
  for i in fsweep:
321
311
  self.sim_setup_info._edb_object.SweepDataList.Add(i._edb_object)
322
312
  self._update_setup()
323
- return True if name in self.frequency_sweeps else False
313
+ return False if name in self.sweeps else True
324
314
 
325
315
  def add_frequency_sweep(self, name=None, frequency_sweep=None):
326
316
  """Add frequency sweep.
@@ -95,6 +95,8 @@ class SiwaveSimulationSetup(SimulationSetup):
95
95
  self._edb_object = self._simulation_setup_builder(sim_setup_info._edb_object)
96
96
  self._update_setup()
97
97
 
98
+ self._siwave_sweeps_list = []
99
+
98
100
  def create(self, name=None):
99
101
  """Create a SIwave SYZ setup.
100
102
 
@@ -248,6 +250,34 @@ class SiwaveSimulationSetup(SimulationSetup):
248
250
  self._edb_object = self._set_edb_setup_info(edb_setup_info)
249
251
  self._update_setup()
250
252
 
253
+ def add_sweep(self, name: str = None, frequency_set: list = None, sweep_type: str = "interpolation", **kwargs):
254
+ """Add frequency sweep.
255
+
256
+ Parameters
257
+ ----------
258
+ name : str, optional
259
+ Name of the frequency sweep. The default is ``None``.
260
+ frequency_set : list, optional
261
+ List of frequency points. The default is ``None``.
262
+ sweep_type : str, optional
263
+ Sweep type. The default is ``"interpolation"``. Options are ``"discrete"``,"discrete"``.
264
+ Returns
265
+ -------
266
+
267
+ Examples
268
+ --------
269
+ >>> setup1 = edbapp.create_siwave_syz_setup("setup1")
270
+ >>> setup1.add_sweep(name="sw1", frequency_set=["linear count", "1MHz", "100MHz", 10])
271
+ """
272
+ sweep_data = SimulationSetup.add_sweep(self, name, frequency_set, sweep_type, **kwargs)
273
+ self._siwave_sweeps_list.append(sweep_data)
274
+ return sweep_data
275
+
276
+ @property
277
+ def sweeps(self):
278
+ """List of frequency sweeps."""
279
+ return {i.name: i for i in self._siwave_sweeps_list}
280
+
251
281
 
252
282
  class SiwaveDCSimulationSetup(SimulationSetup):
253
283
  """Manages EDB methods for SIwave DC simulation setup."""
@@ -2280,7 +2280,7 @@ class Components(object):
2280
2280
  if not pin.net.is_null:
2281
2281
  if pin.net.name:
2282
2282
  pingroup.net = pin.net
2283
- return group_name
2283
+ return group_name, PinGroup(self._pedb, pingroup)
2284
2284
  return False
2285
2285
 
2286
2286
  def create_pin_group_on_net(self, reference_designator, net_name, group_name=None):