pyedb 0.37.0__py3-none-any.whl → 0.38.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.

@@ -19,6 +19,9 @@
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 os
23
+
24
+ import numpy as np
22
25
 
23
26
  from pyedb.configuration.cfg_common import CfgBase
24
27
  from pyedb.dotnet.edb_core.edb_data.ports import WavePort
@@ -33,9 +36,9 @@ class CfgTerminalInfo(CfgBase):
33
36
  self._pedb = pedb
34
37
  self.type = list(kwargs.keys())[0]
35
38
  self.value = kwargs[self.type]
36
- self.contact_radius = kwargs.get("contact_radius", None)
37
- self.num_of_contact = kwargs.get("num_of_contact", 1)
38
- self.inline = kwargs.get("inline", False)
39
+ self.contact_type = kwargs.get("contact_type", "default") # options are full, center, quad, inline
40
+ self.contact_radius = self._pedb.edb_value(kwargs.get("contact_radius", "0.1mm")).ToDouble()
41
+ self.num_of_contact = kwargs.get("num_of_contact", 4)
39
42
 
40
43
  def export_properties(self):
41
44
  return {self.type: self.value}
@@ -208,6 +211,16 @@ class CfgPorts:
208
211
  return ports
209
212
 
210
213
 
214
+ class CfgProbes:
215
+ def __init__(self, pedb, data):
216
+ self._pedb = pedb
217
+ self.probes = [CfgProbe(self._pedb, **probe) for probe in data]
218
+
219
+ def apply(self):
220
+ for probe in self.probes:
221
+ probe.api.set_parameters_to_edb()
222
+
223
+
211
224
  class CfgCircuitElement(CfgBase):
212
225
  def __init__(self, pedb, **kwargs):
213
226
  self._pedb = pedb
@@ -215,6 +228,7 @@ class CfgCircuitElement(CfgBase):
215
228
  self.type = kwargs["type"]
216
229
  self.reference_designator = kwargs.get("reference_designator", None)
217
230
  self.distributed = kwargs.get("distributed", False)
231
+ self._elem_num = 1
218
232
 
219
233
  pos = kwargs["positive_terminal"] # {"pin" : "A1"}
220
234
  if list(pos.keys())[0] == "coordinates":
@@ -232,9 +246,10 @@ class CfgCircuitElement(CfgBase):
232
246
  else:
233
247
  self.negative_terminal_info = CfgTerminalInfo(self._pedb, **neg)
234
248
 
235
- def _create_terminals(self):
249
+ def create_terminals(self):
236
250
  """Create step 1. Collect positive and negative terminals."""
237
251
 
252
+ # Collect all positive terminals
238
253
  pos_type, pos_value = self.positive_terminal_info.type, self.positive_terminal_info.value
239
254
  pos_objs = dict()
240
255
  pos_coor_terminal = dict()
@@ -248,32 +263,57 @@ class CfgCircuitElement(CfgBase):
248
263
  net_name = self.positive_terminal_info.net
249
264
  pos_coor_terminal[self.name] = self._pedb.get_point_terminal(self.name, net_name, point, layer)
250
265
 
266
+ elif pos_type == "pin":
267
+ pins = {pos_value: self._pedb.components.instances[self.reference_designator].pins[pos_value]}
268
+ if self.positive_terminal_info.contact_type in ["quad", "inline"]:
269
+ for _, pin in pins.items():
270
+ contact_type = self.positive_terminal_info.contact_type
271
+ radius = self.positive_terminal_info.contact_radius
272
+ num_of_contact = self.positive_terminal_info.num_of_contact
273
+ virtual_pins = self._create_virtual_pins_on_pin(pin, contact_type, radius, num_of_contact)
274
+ pos_objs.update(virtual_pins)
275
+ self._elem_num = len(pos_objs)
276
+ else:
277
+ pos_objs.update(pins)
251
278
  elif pos_type == "pin_group":
279
+ pins = self._get_pins(pos_type, pos_value)
252
280
  if self.distributed:
253
- pins = self._get_pins(pos_type, pos_value)
254
281
  pos_objs.update(pins)
255
282
  self._elem_num = len(pos_objs)
283
+ elif self.positive_terminal_info.contact_type in ["quad", "inline"]:
284
+ for _, pin in pins.items():
285
+ contact_type = self.positive_terminal_info.contact_type
286
+ radius = self.positive_terminal_info.contact_radius
287
+ num_of_contact = self.positive_terminal_info.num_of_contact
288
+ virtual_pins = self._create_virtual_pins_on_pin(pin, contact_type, radius, num_of_contact)
289
+ pos_objs.update(virtual_pins)
290
+ self._elem_num = len(pos_objs)
256
291
  else:
257
292
  pos_objs[pos_value] = self._pedb.siwave.pin_groups[pos_value]
258
293
  elif pos_type == "net":
294
+ pins = self._get_pins(pos_type, pos_value)
259
295
  if self.distributed:
260
- pins = self._get_pins(pos_type, pos_value)
261
296
  pos_objs.update(pins)
262
297
  self._elem_num = len(pos_objs)
298
+ elif self.positive_terminal_info.contact_type in ["quad", "inline"]:
299
+ for _, pin in pins.items():
300
+ contact_type = self.positive_terminal_info.contact_type
301
+ radius = self.positive_terminal_info.contact_radius
302
+ num_of_contact = self.positive_terminal_info.num_of_contact
303
+ virtual_pins = self._create_virtual_pins_on_pin(pin, contact_type, radius, num_of_contact)
304
+ pos_objs.update(virtual_pins)
305
+ self._elem_num = len(pos_objs)
263
306
  else:
264
- pins = self._get_pins(pos_type, pos_value)
265
307
  # create pin group
266
308
  neg_obj = self._create_pin_group(pins)
267
309
  pos_objs.update(neg_obj)
268
- elif pos_type == "pin":
269
- pins = {pos_value: self._pedb.components.instances[self.reference_designator].pins[pos_value]}
270
- pos_objs.update(pins)
271
310
  else:
272
311
  raise Exception(f"Wrong positive terminal type {pos_type}.")
273
312
 
274
313
  self.pos_terminals = {i: j.create_terminal(i) for i, j in pos_objs.items()}
275
314
  self.pos_terminals.update(pos_coor_terminal)
276
315
 
316
+ # Collect all negative terminals
277
317
  self.neg_terminal = None
278
318
  if self.negative_terminal_info:
279
319
  neg_type, neg_value = self.negative_terminal_info.type, self.negative_terminal_info.value
@@ -330,6 +370,66 @@ class CfgCircuitElement(CfgBase):
330
370
  pins.update({f"{self.reference_designator}_{terminal_value[0]}_{i}": j for i, j in temp.items()})
331
371
  return pins
332
372
 
373
+ def _create_virtual_pins_on_pin(self, pin, contact_type, radius, num_of_contact=4):
374
+ component = pin.component
375
+ placement_layer = component.placement_layer
376
+ pos_x, pos_y = pin.position
377
+ comp_rotation = self._pedb.edb_value(component.rotation).ToDouble() % 3.141592653589793
378
+
379
+ pad = pin.definition.pad_by_layer[placement_layer]
380
+ if pad.shape.lower() in ["rectangle", "oval"]:
381
+ width, height = pad.parameters_values[0:2]
382
+ elif pad.shape.lower() == "nogeometry":
383
+ polygon_data = pad.polygon_data
384
+ if polygon_data:
385
+ p1, p2 = polygon_data.bounding_box
386
+ width = p2[0] - p1[0]
387
+ height = p2[1] - p1[1]
388
+ else:
389
+ raise AttributeError(f"Unsupported pad shape {pad.shape.lower()}")
390
+ else: # pragma no cover
391
+ raise AttributeError(f"Unsupported pad shape {pad.shape.lower()}")
392
+
393
+ positions = []
394
+ if contact_type.lower() == "inline":
395
+ if width > height:
396
+ offset = (width - radius * 2) / (num_of_contact - 1)
397
+ else:
398
+ offset = (height - radius * 2) / (num_of_contact - 1)
399
+
400
+ start_pos = (num_of_contact - 1) / 2
401
+ offset = [offset * i for i in np.arange(start_pos * -1, start_pos + 1)]
402
+
403
+ if (width > height and comp_rotation == 0) or (width < height and comp_rotation != 0):
404
+ positions.extend(list(zip(offset, [0] * num_of_contact)))
405
+ else:
406
+ positions.extend(list(zip([0] * num_of_contact, offset)))
407
+ else: # quad
408
+ x_offset = width / 2 - radius if comp_rotation == 0 else height / 2 - radius
409
+ y_offset = height / 2 - radius if comp_rotation == 0 else width / 2 - radius
410
+
411
+ for x, y in [[1, 1], [-1, 1], [1, -1], [-1, -1]]:
412
+ positions.append([x_offset * x, y_offset * y])
413
+
414
+ pdef_name = f"{self.name}_{pin.pin_number}"
415
+ self._pedb.padstacks.create(padstackname=pdef_name, has_hole=False, paddiam=radius * 2, antipaddiam=0)
416
+ instances = {}
417
+ for idx, xy in enumerate(positions):
418
+ x = xy[0] + pos_x
419
+ y = xy[1] + pos_y
420
+ pin_name = f"{pdef_name}_{idx}"
421
+ p_inst = self._pedb.padstacks.place(
422
+ position=[x, y],
423
+ definition_name=pdef_name,
424
+ net_name=pin.net_name,
425
+ via_name=pin_name,
426
+ fromlayer=placement_layer,
427
+ tolayer=placement_layer,
428
+ is_pin=True,
429
+ )
430
+ instances[pin_name] = p_inst
431
+ return instances
432
+
333
433
  def _create_pin_group(self, pins, is_ref=False):
334
434
  if is_ref:
335
435
  pg_name = f"pg_{self.name}_{self.reference_designator}_ref"
@@ -350,7 +450,7 @@ class CfgPort(CfgCircuitElement):
350
450
 
351
451
  def set_parameters_to_edb(self):
352
452
  """Create port."""
353
- self._create_terminals()
453
+ self.create_terminals()
354
454
  is_circuit_port = True if self.type == "circuit" else False
355
455
  circuit_elements = []
356
456
  for name, j in self.pos_terminals.items():
@@ -382,56 +482,66 @@ class CfgSource(CfgCircuitElement):
382
482
  super().__init__(pedb, **kwargs)
383
483
 
384
484
  self.magnitude = kwargs.get("magnitude", 0.001)
385
- self.equipotential = kwargs.get("equipotential", False)
386
485
 
387
486
  def set_parameters_to_edb(self):
388
487
  """Create sources."""
389
- self._create_terminals()
488
+ self.create_terminals()
390
489
  # is_circuit_port = True if self.type == "circuit" else False
391
490
  circuit_elements = []
392
- method = self._pedb.create_current_source if self.type == "current" else self._pedb.create_voltage_source
491
+ create_xxx_source = (
492
+ self._pedb.create_current_source if self.type == "current" else self._pedb.create_voltage_source
493
+ )
393
494
  for name, j in self.pos_terminals.items():
394
495
  if isinstance(self.neg_terminal, dict):
395
- elem = method(j, self.neg_terminal[name])
496
+ elem = create_xxx_source(j, self.neg_terminal[name])
396
497
  else:
397
- elem = method(j, self.neg_terminal)
398
- if not self.distributed:
498
+ elem = create_xxx_source(j, self.neg_terminal)
499
+ if self._elem_num == 1:
399
500
  elem.name = self.name
400
501
  elem.magnitude = self.magnitude
401
502
  else:
402
- elem.name = f"{self.name}_{elem.name}"
503
+ elem.name = name
403
504
  elem.magnitude = self.magnitude / self._elem_num
505
+ elem = self._pedb.terminals[elem.name]
404
506
  circuit_elements.append(elem)
405
- for terminal in circuit_elements:
406
- if self.equipotential:
407
- terms = [terminal, terminal.ref_terminal] if terminal.ref_terminal else [terminal]
408
- for t in terms:
409
- if not t.is_reference_terminal:
410
- radius = self.positive_terminal_info.contact_radius
411
- num_of_contact = self.positive_terminal_info.num_of_contact
412
- inline = self.positive_terminal_info.inline
413
- else:
414
- radius = self.negative_terminal_info.contact_radius
415
- num_of_contact = self.negative_terminal_info.num_of_contact
416
- inline = self.negative_terminal_info.inline
417
-
418
- pads = []
419
- if t.terminal_type == "PadstackInstanceTerminal":
420
- pads.append(t.reference_object)
421
- elif t.terminal_type == "PinGroupTerminal":
422
- name = t._edb_object.GetPinGroup().GetName()
423
- pg = self._pedb.siwave.pin_groups[name]
424
- pads.extend([i for _, i in pg.pins.items()])
425
- elif t.terminal_type == "PointTerminal" and radius:
426
- temp = [i for i in self._pedb.layout.terminals if i.name == t.name][0]
427
- if radius is not None:
428
- prim = self._pedb.modeler.create_circle(
429
- temp.layer.name, temp.location[0], temp.location[1], radius, temp.net_name
430
- )
431
- prim.dcir_equipotential_region = True
432
507
 
508
+ for terminal in circuit_elements:
509
+ # Get reference terminal
510
+ terms = [terminal, terminal.ref_terminal] if terminal.ref_terminal else [terminal]
511
+ for t in terms:
512
+ if not t.is_reference_terminal:
513
+ radius = self.positive_terminal_info.contact_radius
514
+ contact_type = self.positive_terminal_info.contact_type
515
+ else:
516
+ radius = self.negative_terminal_info.contact_radius
517
+ contact_type = self.negative_terminal_info.contact_type
518
+ if t.terminal_type == "PointTerminal":
519
+ temp = [i for i in self._pedb.layout.terminals if i.name == t.name][0]
520
+ prim = self._pedb.modeler.create_circle(
521
+ temp.layer.name, temp.location[0], temp.location[1], radius, temp.net_name
522
+ )
523
+ prim.dcir_equipotential_region = True
524
+ continue
525
+ elif contact_type.lower() == "default":
526
+ continue
527
+ elif t.terminal_type == "PadstackInstanceTerminal":
528
+ if contact_type.lower() in ["full", "quad", "inline"]:
529
+ t.padstack_instance._set_equipotential()
530
+ elif contact_type.lower() == "center":
531
+ t.padstack_instance._set_equipotential(contact_radius=radius)
532
+ elif t.terminal_type == "PinGroupTerminal":
533
+ name = t._edb_object.GetPinGroup().GetName()
534
+ pg = self._pedb.siwave.pin_groups[name]
535
+ pads = [i for _, i in pg.pins.items()]
433
536
  for i in pads:
434
- i._set_equipotential(contact_radius=radius, inline=inline, num_of_contact=num_of_contact)
537
+ if contact_type.lower() in ["full", "quad", "inline"]:
538
+ i._set_equipotential()
539
+ elif contact_type.lower() == "center":
540
+ i._set_equipotential(contact_radius=radius)
541
+ elif t.is_reference_terminal:
542
+ continue
543
+ else:
544
+ raise AttributeError("Unsupported terminal type.")
435
545
 
436
546
  return circuit_elements
437
547
 
@@ -446,6 +556,41 @@ class CfgSource(CfgCircuitElement):
446
556
  }
447
557
 
448
558
 
559
+ class CfgProbe(CfgCircuitElement):
560
+ class Common:
561
+ def __init__(self, parent):
562
+ self.parent = parent
563
+ self.pedb = parent._pedb
564
+
565
+ def set_parameters_to_edb(self):
566
+ self.parent.create_terminals()
567
+ circuit_elements = []
568
+ for name, j in self.parent.pos_terminals.items():
569
+ if isinstance(self.parent.neg_terminal, dict):
570
+ elem = self.pedb.create_voltage_probe(j, self.parent.neg_terminal[name])
571
+ else:
572
+ elem = self.pedb.create_voltage_probe(j, self.parent.neg_terminal)
573
+ elem.name = self.parent.name
574
+ circuit_elements.append(elem)
575
+ return circuit_elements
576
+
577
+ class Grpc(Common):
578
+ def __init__(self, parent):
579
+ super().__init__(parent)
580
+
581
+ class DotNet(Grpc):
582
+ def __init__(self, parent):
583
+ super().__init__(parent)
584
+
585
+ def __init__(self, pedb, **kwargs):
586
+ kwargs["type"] = "probe"
587
+ super().__init__(pedb, **kwargs)
588
+ if os.environ["PYEDB_USE_DOTNET"] == "0":
589
+ self.api = self.Grpc(self)
590
+ else:
591
+ self.api = self.DotNet(self)
592
+
593
+
449
594
  class CfgWavePort:
450
595
  def __init__(self, pedb, **kwargs):
451
596
  self._pedb = pedb
@@ -140,11 +140,11 @@ class Configuration:
140
140
  temp = []
141
141
  for _, pdef in pedb_defs.items():
142
142
  cfg_def = CfgPadstackDefinition(self._pedb, pdef)
143
- cfg_def.retrieve_parameters_from_edb()
143
+ cfg_def.api.retrieve_parameters_from_edb()
144
144
  temp.append(cfg_def)
145
145
  self.cfg_data.stackup.apply()
146
146
  for cfg_pdef in temp:
147
- cfg_pdef.set_parameters_to_edb()
147
+ cfg_pdef.api.set_parameters_to_edb()
148
148
  else:
149
149
  self.cfg_data.stackup.apply()
150
150
 
@@ -171,6 +171,9 @@ class Configuration:
171
171
  # Configure ports
172
172
  self.cfg_data.ports.apply()
173
173
 
174
+ # Configure probes
175
+ self.cfg_data.probes.apply()
176
+
174
177
  return True
175
178
 
176
179
  def _load_stackup(self):
pyedb/dotnet/edb.py CHANGED
@@ -4106,7 +4106,7 @@ class Edb(Database):
4106
4106
  term.boundary_type = "kVoltageSource"
4107
4107
 
4108
4108
  ref_term = Terminal(self, ref_terminal._edb_object)
4109
- ref_term.boundary_type = "kVoltageProbe"
4109
+ ref_term.boundary_type = "kVoltageSource"
4110
4110
 
4111
4111
  term.ref_terminal = ref_terminal
4112
4112
  return self.sources[term.name]
@@ -66,7 +66,7 @@ def primitive_cast(pedb, edb_object):
66
66
  elif edb_object.GetPrimitiveType().ToString() == "Bondwire":
67
67
  return Bondwire(pedb, edb_object)
68
68
  elif edb_object.GetPrimitiveType().ToString() == "Text":
69
- return EdbText(edb_object, pedb)
69
+ return EdbText(pedb, edb_object)
70
70
  elif edb_object.GetPrimitiveType().ToString() == "PrimitivePlugin":
71
71
  return
72
72
  elif edb_object.GetPrimitiveType().ToString() == "Path3D":
@@ -152,7 +152,7 @@ class Layout(ObjBase):
152
152
  is_pins : bool, optional
153
153
  True for pins, false for vias (default).
154
154
  """
155
- self._edb_object.ConvertPrimitivesToVias(convert_py_list_to_net_list(primitives), is_pins)
155
+ self._edb_object.ConvertPrimitivestoVias(convert_py_list_to_net_list(primitives), is_pins)
156
156
 
157
157
  @property
158
158
  def zone_primitives(self):
@@ -24,8 +24,6 @@ from collections import OrderedDict
24
24
  import math
25
25
  import warnings
26
26
 
27
- import numpy as np
28
-
29
27
  from pyedb.dotnet.clr_module import String
30
28
  from pyedb.dotnet.edb_core.cell.primitive.primitive import Primitive
31
29
  from pyedb.dotnet.edb_core.dotnet.database import PolygonDataDotNet
@@ -36,6 +34,7 @@ from pyedb.dotnet.edb_core.general import (
36
34
  pascal_to_snake,
37
35
  snake_to_pascal,
38
36
  )
37
+ from pyedb.dotnet.edb_core.geometry.polygon_data import PolygonData
39
38
  from pyedb.generic.general_methods import generate_unique_name
40
39
  from pyedb.modeler.geometry_operators import GeometryOperators
41
40
 
@@ -157,6 +156,24 @@ class EDBPadProperties(object):
157
156
  def polygon_data(self):
158
157
  """Parameters.
159
158
 
159
+ Returns
160
+ -------
161
+ list
162
+ List of parameters.
163
+ """
164
+
165
+ flag, edb_object, _, _, _ = self._edb_padstack.GetData().GetPolygonalPadParameters(
166
+ self.layer_name, self.int_to_pad_type(self.pad_type)
167
+ )
168
+ if flag:
169
+ return PolygonData(self._edb._app, edb_object)
170
+ else: # pragma no cover
171
+ raise AttributeError("No polygon data.")
172
+
173
+ @property
174
+ def _polygon_data_dotnet(self):
175
+ """Parameters.
176
+
160
177
  Returns
161
178
  -------
162
179
  list
@@ -782,7 +799,7 @@ class EDBPadstack(object):
782
799
  pdef_data.SetHoleRange(getattr(self._edb.definition.PadstackHoleRange, snake_to_pascal(value)))
783
800
  self._padstack_def_data = pdef_data
784
801
 
785
- def convert_to_3d_microvias(self, convert_only_signal_vias=True, hole_wall_angle=15, delete_padstack_def=True):
802
+ def convert_to_3d_microvias(self, convert_only_signal_vias=True, hole_wall_angle=75, delete_padstack_def=True):
786
803
  """Convert actual padstack instance to microvias 3D Objects with a given aspect ratio.
787
804
 
788
805
  Parameters
@@ -814,22 +831,9 @@ class EDBPadstack(object):
814
831
  layer_names = [i for i in list(layers.keys())]
815
832
  if convert_only_signal_vias:
816
833
  signal_nets = [i for i in list(self._ppadstack._pedb.nets.signal_nets.keys())]
817
- topl, topz, bottoml, bottomz = self._ppadstack._pedb.stackup.limits(True)
818
- if self.via_start_layer in layers:
819
- start_elevation = layers[self.via_start_layer].lower_elevation
820
- else:
821
- start_elevation = layers[self.instances[0].start_layer].lower_elevation
822
- if self.via_stop_layer in layers:
823
- stop_elevation = layers[self.via_stop_layer].upper_elevation
824
- else:
825
- stop_elevation = layers[self.instances[0].stop_layer].upper_elevation
826
834
 
827
- diel_thick = abs(start_elevation - stop_elevation)
828
- rad1 = self.hole_properties[0] / 2 - math.tan(hole_wall_angle * diel_thick * math.pi / 180)
829
- rad2 = self.hole_properties[0] / 2
835
+ layer_count = len(self._ppadstack._pedb.stackup.signal_layers)
830
836
 
831
- if start_elevation < (topz + bottomz) / 2:
832
- rad1, rad2 = rad2, rad1
833
837
  i = 0
834
838
  for via in list(self.padstack_instances.values()):
835
839
  if convert_only_signal_vias and via.net_name in signal_nets or not convert_only_signal_vias:
@@ -865,18 +869,33 @@ class EDBPadstack(object):
865
869
  self._get_edb_value(pos[1]),
866
870
  self._get_edb_value(self.pad_by_layer[self.via_stop_layer].parameters_values[0] / 2),
867
871
  )
868
- for layer_name in layer_names:
872
+ for layer_idx, layer_name in enumerate(layer_names):
869
873
  stop = ""
870
874
  if layer_name == via.start_layer or started:
871
875
  start = layer_name
872
876
  stop = layer_names[layer_names.index(layer_name) + 1]
877
+
878
+ start_elevation = layers[start].lower_elevation
879
+ stop_elevation = layers[stop].upper_elevation
880
+ diel_thick = abs(start_elevation - stop_elevation)
881
+
882
+ rad_large = self.hole_diameter / 2
883
+ rad_small = rad_large - diel_thick * 1 / math.tan(math.radians(hole_wall_angle))
884
+
885
+ if layer_idx + 1 < layer_count / 2: # upper half of stack
886
+ rad_u = rad_large
887
+ rad_l = rad_small
888
+ else:
889
+ rad_u = rad_small
890
+ rad_l = rad_large
891
+
873
892
  cloned_circle = self._edb.cell.primitive.circle.create(
874
893
  layout,
875
894
  start,
876
895
  via._edb_padstackinstance.GetNet(),
877
896
  self._get_edb_value(pos[0]),
878
897
  self._get_edb_value(pos[1]),
879
- self._get_edb_value(rad1),
898
+ self._get_edb_value(rad_u),
880
899
  )
881
900
  cloned_circle2 = self._edb.cell.primitive.circle.create(
882
901
  layout,
@@ -884,7 +903,7 @@ class EDBPadstack(object):
884
903
  via._edb_padstackinstance.GetNet(),
885
904
  self._get_edb_value(pos[0]),
886
905
  self._get_edb_value(pos[1]),
887
- self._get_edb_value(rad2),
906
+ self._get_edb_value(rad_l),
888
907
  )
889
908
  s3d = self._edb.cell.hierarchy._hierarchy.Structure3D.Create(
890
909
  layout, generate_unique_name("via3d_" + via.aedt_name.replace("via_", ""), n=3)
@@ -1249,47 +1268,15 @@ class EDBPadstackInstance(Primitive):
1249
1268
 
1250
1269
  return self._pedb.create_port(terminal, ref_terminal, is_circuit_port)
1251
1270
 
1252
- def _set_equipotential(self, contact_radius=None, inline=False, num_of_contact=1):
1271
+ def _set_equipotential(self, contact_radius=None):
1253
1272
  """Workaround solution. Remove when EDBAPI bug is fixed for dcir_equipotential_region."""
1254
1273
  pad = self.definition.pad_by_layer[self.start_layer]
1255
1274
 
1256
1275
  pos_x, pos_y = self.position
1257
- comp_rotation = self._pedb.edb_value(self.component.rotation).ToDouble() % 3.141592653589793
1258
1276
 
1259
1277
  if contact_radius is not None:
1260
- if num_of_contact == 1:
1261
- prim = self._pedb.modeler.create_circle(pad.layer_name, pos_x, pos_y, contact_radius, self.net_name)
1262
- prim.dcir_equipotential_region = True
1263
- else:
1264
- if pad.shape.lower() in ["rectangle", "oval"]:
1265
- width, height = pad.parameters_values[0:2]
1266
- radius = self._pedb.edb_value(contact_radius).ToDouble()
1267
- else:
1268
- return
1269
-
1270
- if inline is False:
1271
- x_offset = width / 2 - radius if comp_rotation == 0 else height / 2 - radius
1272
- y_offset = height / 2 - radius if comp_rotation == 0 else width / 2 - radius
1273
- positions = []
1274
- for x, y in [[1, 1], [-1, 1], [1, -1], [-1, -1]]:
1275
- positions.append([x_offset * x, y_offset * y])
1276
- else:
1277
- if width > height:
1278
- offset = (width - radius * 2) / (num_of_contact - 1)
1279
- else:
1280
- offset = (height - radius * 2) / (num_of_contact - 1)
1281
-
1282
- start_pos = (num_of_contact - 1) / 2
1283
- offset = [offset * i for i in np.arange(start_pos * -1, start_pos + 1)]
1284
-
1285
- if (width > height and comp_rotation == 0) or (width < height and comp_rotation != 0):
1286
- positions = list(zip(offset, [0] * num_of_contact))
1287
- else:
1288
- positions = list(zip([0] * num_of_contact, offset))
1289
-
1290
- for x, y in positions:
1291
- prim = self._pedb.modeler.create_circle(pad.layer_name, pos_x + x, pos_y + y, radius, self.net_name)
1292
- prim.dcir_equipotential_region = True
1278
+ prim = self._pedb.modeler.create_circle(pad.layer_name, pos_x, pos_y, contact_radius, self.net_name)
1279
+ prim.dcir_equipotential_region = True
1293
1280
  return
1294
1281
 
1295
1282
  elif pad.shape.lower() == "circle":
@@ -1970,7 +1957,7 @@ class EDBPadstackInstance(Primitive):
1970
1957
 
1971
1958
  pad_shape = padstack_pad.geometry_type
1972
1959
  params = padstack_pad.parameters_values
1973
- polygon_data = padstack_pad.polygon_data
1960
+ polygon_data = padstack_pad._polygon_data_dotnet
1974
1961
 
1975
1962
  def _rotate(p):
1976
1963
  x = p[0] * math.cos(rotation) - p[1] * math.sin(rotation)
@@ -2087,8 +2074,8 @@ class EDBPadstackInstance(Primitive):
2087
2074
  # Polygon
2088
2075
  points = []
2089
2076
  i = 0
2090
- while i < polygon_data.edb_api.Count:
2091
- point = polygon_data.edb_api.GetPoint(i)
2077
+ while i < polygon_data._edb_object.Count:
2078
+ point = polygon_data._edb_object.GetPoint(i)
2092
2079
  i += 1
2093
2080
  if point.IsArc():
2094
2081
  continue
@@ -1859,8 +1859,9 @@ class EdbPadstacks(object):
1859
1859
  for _x, _y in zip(x_grid.ravel(), y_grid.ravel())
1860
1860
  }
1861
1861
 
1862
+ all_instances = self.instances
1862
1863
  for item in padstacks_inbox:
1863
1864
  if item not in to_keep:
1864
- self.instances[item].delete()
1865
+ all_instances[item].delete()
1865
1866
 
1866
1867
  return True
@@ -149,6 +149,21 @@ class AdaptiveSettings(object):
149
149
  self.adaptive_settings.MinPasses = value
150
150
  self._parent._update_setup()
151
151
 
152
+ @property
153
+ def min_converged_passes(self):
154
+ """Minimum number of converged passes.
155
+
156
+ Returns
157
+ -------
158
+ int
159
+ """
160
+ return self.adaptive_settings.MinConvergedPasses
161
+
162
+ @min_converged_passes.setter
163
+ def min_converged_passes(self, value):
164
+ self.adaptive_settings.MinConvergedPasses = value
165
+ self._parent._update_setup()
166
+
152
167
  @property
153
168
  def save_fields(self):
154
169
  """Whether to turn on save fields.
@@ -160,11 +160,11 @@ class SweepData(object):
160
160
  @freq_sweep_type.setter
161
161
  def freq_sweep_type(self, value):
162
162
  edb_freq_sweep_type = self._edb_object.TFreqSweepType
163
- if value in [0, "kInterpolatingSweep"]:
163
+ if value in [0, "kInterpolatingSweep", "interpolation"]:
164
164
  self._edb_object.FreqSweepType = edb_freq_sweep_type.kInterpolatingSweep
165
- elif value in [1, "kDiscreteSweep"]:
165
+ elif value in [1, "kDiscreteSweep", "discrete"]:
166
166
  self._edb_object.FreqSweepType = edb_freq_sweep_type.kDiscreteSweep
167
- elif value in [2, "kBroadbandFastSweep"]:
167
+ elif value in [2, "kBroadbandFastSweep", "broadband"]:
168
168
  self._edb_object.FreqSweepType = edb_freq_sweep_type.kBroadbandFastSweep
169
169
  elif value in [3, "kNumSweepTypes"]:
170
170
  self._edb_object.FreqSweepType = edb_freq_sweep_type.kNumSweepTypes
@@ -265,6 +265,7 @@ class SimulationSetup(object):
265
265
  for k, v in kwargs.items():
266
266
  if k in dir(sweep_data):
267
267
  setattr(sweep_data, k, v)
268
+ sweep_data.freq_sweep_type = kwargs.get("sweep_type") if kwargs.get("sweep_type") else "interpolation"
268
269
 
269
270
  if frequency_set is None:
270
271
  sweep_type = "linear_scale"