pyedb 0.53.0__py3-none-any.whl → 0.54.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 (84) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_ports_sources.py +6 -8
  3. pyedb/configuration/configuration.py +2 -1
  4. pyedb/dotnet/database/cell/hierarchy/model.py +1 -1
  5. pyedb/dotnet/database/cell/layout.py +1 -1
  6. pyedb/dotnet/database/cell/layout_obj.py +3 -3
  7. pyedb/dotnet/database/cell/primitive/path.py +1 -1
  8. pyedb/dotnet/database/cell/primitive/primitive.py +8 -8
  9. pyedb/dotnet/database/cell/terminal/pingroup_terminal.py +1 -1
  10. pyedb/dotnet/database/cell/terminal/point_terminal.py +1 -1
  11. pyedb/dotnet/database/cell/terminal/terminal.py +24 -26
  12. pyedb/dotnet/database/components.py +24 -24
  13. pyedb/dotnet/database/definition/component_def.py +3 -3
  14. pyedb/dotnet/database/definition/component_model.py +1 -1
  15. pyedb/dotnet/database/definition/package_def.py +1 -1
  16. pyedb/dotnet/database/dotnet/database.py +43 -38
  17. pyedb/dotnet/database/dotnet/primitive.py +16 -16
  18. pyedb/dotnet/database/edb_data/hfss_extent_info.py +6 -6
  19. pyedb/dotnet/database/edb_data/layer_data.py +14 -14
  20. pyedb/dotnet/database/edb_data/padstacks_data.py +12 -12
  21. pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
  22. pyedb/dotnet/database/edb_data/sources.py +6 -6
  23. pyedb/dotnet/database/edb_data/variables.py +7 -3
  24. pyedb/dotnet/database/geometry/point_data.py +1 -1
  25. pyedb/dotnet/database/geometry/polygon_data.py +2 -4
  26. pyedb/dotnet/database/hfss.py +7 -7
  27. pyedb/dotnet/database/materials.py +2 -2
  28. pyedb/dotnet/database/modeler.py +8 -11
  29. pyedb/dotnet/database/nets.py +1 -1
  30. pyedb/dotnet/database/padstack.py +72 -1
  31. pyedb/dotnet/database/sim_setup_data/data/settings.py +24 -0
  32. pyedb/dotnet/database/sim_setup_data/io/siwave.py +26 -1
  33. pyedb/dotnet/database/siwave.py +5 -5
  34. pyedb/dotnet/database/stackup.py +74 -77
  35. pyedb/dotnet/database/utilities/heatsink.py +4 -4
  36. pyedb/dotnet/database/utilities/obj_base.py +1 -1
  37. pyedb/dotnet/database/utilities/value.py +116 -0
  38. pyedb/dotnet/edb.py +43 -43
  39. pyedb/generic/design_types.py +12 -3
  40. pyedb/grpc/__init__.py +0 -0
  41. pyedb/grpc/database/components.py +53 -17
  42. pyedb/grpc/database/definition/materials.py +23 -30
  43. pyedb/grpc/database/definition/package_def.py +15 -15
  44. pyedb/grpc/database/definition/padstack_def.py +51 -51
  45. pyedb/grpc/database/geometry/arc_data.py +7 -5
  46. pyedb/grpc/database/geometry/point_3d_data.py +8 -7
  47. pyedb/grpc/database/geometry/polygon_data.py +3 -2
  48. pyedb/grpc/database/hierarchy/component.py +43 -38
  49. pyedb/grpc/database/hierarchy/pin_pair_model.py +15 -14
  50. pyedb/grpc/database/hierarchy/pingroup.py +9 -9
  51. pyedb/grpc/database/layers/stackup_layer.py +45 -44
  52. pyedb/grpc/database/layout/layout.py +9 -8
  53. pyedb/grpc/database/layout/voltage_regulator.py +7 -7
  54. pyedb/grpc/database/layout_validation.py +13 -12
  55. pyedb/grpc/database/modeler.py +51 -54
  56. pyedb/grpc/database/nets.py +42 -31
  57. pyedb/grpc/database/padstacks.py +270 -175
  58. pyedb/grpc/database/ports/ports.py +5 -6
  59. pyedb/grpc/database/primitive/bondwire.py +8 -7
  60. pyedb/grpc/database/primitive/circle.py +4 -4
  61. pyedb/grpc/database/primitive/padstack_instance.py +18 -18
  62. pyedb/grpc/database/primitive/path.py +7 -7
  63. pyedb/grpc/database/primitive/polygon.py +3 -3
  64. pyedb/grpc/database/primitive/primitive.py +13 -17
  65. pyedb/grpc/database/primitive/rectangle.py +13 -13
  66. pyedb/grpc/database/simulation_setup/hfss_general_settings.py +1 -1
  67. pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +10 -0
  68. pyedb/grpc/database/simulation_setup/siwave_simulation_setup.py +17 -1
  69. pyedb/grpc/database/siwave.py +30 -24
  70. pyedb/grpc/database/source_excitations.py +333 -229
  71. pyedb/grpc/database/stackup.py +164 -147
  72. pyedb/grpc/database/terminal/bundle_terminal.py +17 -7
  73. pyedb/grpc/database/terminal/edge_terminal.py +10 -0
  74. pyedb/grpc/database/terminal/padstack_instance_terminal.py +15 -4
  75. pyedb/grpc/database/terminal/pingroup_terminal.py +11 -10
  76. pyedb/grpc/database/terminal/point_terminal.py +4 -3
  77. pyedb/grpc/database/terminal/terminal.py +9 -9
  78. pyedb/grpc/database/utility/value.py +109 -0
  79. pyedb/grpc/edb.py +59 -38
  80. pyedb/grpc/edb_init.py +0 -7
  81. {pyedb-0.53.0.dist-info → pyedb-0.54.0.dist-info}/METADATA +3 -3
  82. {pyedb-0.53.0.dist-info → pyedb-0.54.0.dist-info}/RECORD +84 -81
  83. {pyedb-0.53.0.dist-info → pyedb-0.54.0.dist-info}/WHEEL +0 -0
  84. {pyedb-0.53.0.dist-info → pyedb-0.54.0.dist-info}/licenses/LICENSE +0 -0
@@ -23,7 +23,9 @@
23
23
  """
24
24
  This module contains the `EdbPadstacks` class.
25
25
  """
26
+ from collections import defaultdict
26
27
  import math
28
+ from typing import Any, Dict, List, Optional, Tuple, Union
27
29
  import warnings
28
30
 
29
31
  from ansys.edb.core.definition.padstack_def_data import (
@@ -44,13 +46,13 @@ from ansys.edb.core.definition.padstack_def_data import (
44
46
  from ansys.edb.core.definition.padstack_def_data import PadType as GrpcPadType
45
47
  from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
46
48
  from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
47
- from ansys.edb.core.utility.value import Value as GrpcValue
48
49
  import numpy as np
49
50
  import rtree
50
51
 
51
52
  from pyedb.generic.general_methods import generate_unique_name
52
53
  from pyedb.grpc.database.definition.padstack_def import PadstackDef
53
54
  from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
55
+ from pyedb.grpc.database.utility.value import Value
54
56
  from pyedb.modeler.geometry_operators import GeometryOperators
55
57
 
56
58
 
@@ -89,37 +91,37 @@ class Padstacks(object):
89
91
  self._pedb.logger.error("Component or definition not found.")
90
92
  return
91
93
 
92
- def __init__(self, p_edb):
94
+ def __init__(self, p_edb: Any) -> None:
93
95
  self._pedb = p_edb
94
- self._instances = {}
95
- self._definitions = {}
96
+ self._instances: Dict[int, PadstackInstance] = {}
97
+ self._definitions: Dict[str, Any] = {}
96
98
 
97
99
  @property
98
- def _active_layout(self):
100
+ def _active_layout(self) -> Any:
99
101
  """ """
100
102
  return self._pedb.active_layout
101
103
 
102
104
  @property
103
- def _layout(self):
105
+ def _layout(self) -> Any:
104
106
  """ """
105
107
  return self._pedb.layout
106
108
 
107
109
  @property
108
- def db(self):
110
+ def db(self) -> Any:
109
111
  """Db object."""
110
112
  return self._pedb.active_db
111
113
 
112
114
  @property
113
- def _logger(self):
115
+ def _logger(self) -> Any:
114
116
  """ """
115
117
  return self._pedb.logger
116
118
 
117
119
  @property
118
- def _layers(self):
120
+ def _layers(self) -> Any:
119
121
  """ """
120
122
  return self._pedb.stackup.layers
121
123
 
122
- def int_to_pad_type(self, val=0):
124
+ def int_to_pad_type(self, val=0) -> GrpcPadType:
123
125
  """Convert an integer to an EDB.PadGeometryType.
124
126
 
125
127
  Parameters
@@ -150,7 +152,7 @@ class Padstacks(object):
150
152
  else:
151
153
  return val
152
154
 
153
- def int_to_geometry_type(self, val=0):
155
+ def int_to_geometry_type(self, val: int = 0) -> GrpcPadGeometryType:
154
156
  """Convert an integer to an EDB.PadGeometryType.
155
157
 
156
158
  Parameters
@@ -195,7 +197,7 @@ class Padstacks(object):
195
197
  return val
196
198
 
197
199
  @property
198
- def definitions(self):
200
+ def definitions(self) -> Dict[str, PadstackDef]:
199
201
  """Padstack definitions.
200
202
 
201
203
  Returns
@@ -218,7 +220,7 @@ class Padstacks(object):
218
220
  return self._definitions
219
221
 
220
222
  @property
221
- def instances(self):
223
+ def instances(self) -> Dict[int, PadstackInstance]:
222
224
  """All padstack instances (vias and pins) in the layout.
223
225
 
224
226
  Returns
@@ -239,7 +241,7 @@ class Padstacks(object):
239
241
  return self._instances
240
242
 
241
243
  @property
242
- def instances_by_name(self):
244
+ def instances_by_name(self) -> Dict[str, PadstackInstance]:
243
245
  """All padstack instances (vias and pins) indexed by name.
244
246
 
245
247
  Returns
@@ -259,7 +261,7 @@ class Padstacks(object):
259
261
  padstack_instances[edb_padstack_instance.aedt_name] = edb_padstack_instance
260
262
  return padstack_instances
261
263
 
262
- def find_instance_by_id(self, value: int):
264
+ def find_instance_by_id(self, value: int) -> Optional[PadstackInstance]:
263
265
  """Find a padstack instance by database ID.
264
266
 
265
267
  Parameters
@@ -281,7 +283,7 @@ class Padstacks(object):
281
283
  return self._pedb.modeler.find_object_by_id(value)
282
284
 
283
285
  @property
284
- def pins(self):
286
+ def pins(self) -> Dict[int, PadstackInstance]:
285
287
  """All pin instances belonging to components.
286
288
 
287
289
  Returns
@@ -302,7 +304,7 @@ class Padstacks(object):
302
304
  return pins
303
305
 
304
306
  @property
305
- def vias(self):
307
+ def vias(self) -> Dict[int, PadstackInstance]:
306
308
  """All via instances not belonging to components.
307
309
 
308
310
  Returns
@@ -321,7 +323,7 @@ class Padstacks(object):
321
323
  return vias
322
324
 
323
325
  @property
324
- def pingroups(self):
326
+ def pingroups(self) -> List[Any]:
325
327
  """All Layout Pin groups.
326
328
 
327
329
  . deprecated:: pyedb 0.28.0
@@ -344,18 +346,18 @@ class Padstacks(object):
344
346
  return self._layout.pin_groups
345
347
 
346
348
  @property
347
- def pad_type(self):
349
+ def pad_type(self) -> GrpcPadType:
348
350
  """Return a PadType Enumerator."""
349
351
 
350
352
  def create_circular_padstack(
351
353
  self,
352
- padstackname=None,
353
- holediam="300um",
354
- paddiam="400um",
355
- antipaddiam="600um",
356
- startlayer=None,
357
- endlayer=None,
358
- ):
354
+ padstackname: Optional[str] = None,
355
+ holediam: str = "300um",
356
+ paddiam: str = "400um",
357
+ antipaddiam: str = "600um",
358
+ startlayer: Optional[str] = None,
359
+ endlayer: Optional[str] = None,
360
+ ) -> str:
359
361
  """Create a circular padstack.
360
362
 
361
363
  Parameters
@@ -393,11 +395,11 @@ class Padstacks(object):
393
395
  padstack_def = PadstackDef.create(self._pedb.db, padstackname)
394
396
 
395
397
  padstack_data = GrpcPadstackDefData.create()
396
- list_values = [GrpcValue(holediam), GrpcValue(paddiam), GrpcValue(antipaddiam)]
398
+ list_values = [Value(holediam), Value(paddiam), Value(antipaddiam)]
397
399
  padstack_data.set_hole_parameters(
398
- offset_x=GrpcValue(0),
399
- offset_y=GrpcValue(0),
400
- rotation=GrpcValue(0),
400
+ offset_x=Value(0),
401
+ offset_y=Value(0),
402
+ rotation=Value(0),
401
403
  type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
402
404
  sizes=list_values,
403
405
  )
@@ -415,20 +417,20 @@ class Padstacks(object):
415
417
  layer="Default",
416
418
  pad_type=GrpcPadType.REGULAR_PAD,
417
419
  type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
418
- offset_x=GrpcValue(0),
419
- offset_y=GrpcValue(0),
420
- rotation=GrpcValue(0),
421
- sizes=[GrpcValue(paddiam)],
420
+ offset_x=Value(0),
421
+ offset_y=Value(0),
422
+ rotation=Value(0),
423
+ sizes=[Value(paddiam)],
422
424
  )
423
425
 
424
426
  padstack_data.set_pad_parameters(
425
427
  layer="Default",
426
428
  pad_type=GrpcPadType.ANTI_PAD,
427
429
  type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
428
- offset_x=GrpcValue(0),
429
- offset_y=GrpcValue(0),
430
- rotation=GrpcValue(0),
431
- sizes=[GrpcValue(antipaddiam)],
430
+ offset_x=Value(0),
431
+ offset_y=Value(0),
432
+ rotation=Value(0),
433
+ sizes=[Value(antipaddiam)],
432
434
  )
433
435
 
434
436
  for layer in layers:
@@ -441,25 +443,25 @@ class Padstacks(object):
441
443
  layer=layer,
442
444
  pad_type=GrpcPadType.ANTI_PAD,
443
445
  type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
444
- offset_x=GrpcValue(0),
445
- offset_y=GrpcValue(0),
446
- rotation=GrpcValue(0),
447
- sizes=[GrpcValue(antipaddiam)],
446
+ offset_x=Value(0),
447
+ offset_y=Value(0),
448
+ rotation=Value(0),
449
+ sizes=[Value(antipaddiam)],
448
450
  )
449
451
 
450
452
  padstack_data.set_pad_parameters(
451
453
  layer=layer,
452
454
  pad_type=GrpcPadType.ANTI_PAD,
453
455
  type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
454
- offset_x=GrpcValue(0),
455
- offset_y=GrpcValue(0),
456
- rotation=GrpcValue(0),
457
- sizes=[GrpcValue(antipaddiam)],
456
+ offset_x=Value(0),
457
+ offset_y=Value(0),
458
+ rotation=Value(0),
459
+ sizes=[Value(antipaddiam)],
458
460
  )
459
461
 
460
462
  padstack_def.data = padstack_data
461
463
 
462
- def delete_padstack_instances(self, net_names): # pragma: no cover
464
+ def delete_padstack_instances(self, net_names: Union[str, List[str]]) -> bool:
463
465
  """Delete padstack instances by net names.
464
466
 
465
467
  Parameters
@@ -487,28 +489,23 @@ class Padstacks(object):
487
489
  return True
488
490
 
489
491
  def set_solderball(self, padstackInst, sballLayer_name, isTopPlaced=True, ballDiam=100e-6):
490
- """Set solderball for a padstack instance.
492
+ """Set solderball for the given PadstackInstance.
491
493
 
492
494
  Parameters
493
495
  ----------
494
- padstackInst : int or :class:`pyedb.grpc.database.primitive.padstack_instance.PadstackInstance`
495
- Padstack instance ID or object.
496
- sballLayer_name : str
497
- Name of the layer where the solder ball is placed.
498
- isTopPlaced : bool, optional
499
- Whether the solder ball is placed on top of the layer stackup. Default is ``True``.
500
- ballDiam : float, optional
501
- Solder ball diameter in meters. Default is ``100e-6`` (100 um).
496
+ padstackInst : Edb.Cell.Primitive.PadstackInstance or int
497
+ Padstack instance id or object.
498
+ sballLayer_name : str,
499
+ Name of the layer where the solder ball is placed. No default values.
500
+ isTopPlaced : bool, optional.
501
+ Bollean triggering is the solder ball is placed on Top or Bottom of the layer stackup.
502
+ ballDiam : double, optional,
503
+ Solder ball diameter value.
502
504
 
503
505
  Returns
504
506
  -------
505
507
  bool
506
- ``True`` when successful, ``False`` when failed.
507
508
 
508
- Examples
509
- --------
510
- >>> via_id = 123
511
- >>> success = edb_padstacks.set_solderball(via_id, "SolderBall_Top", True, 150e-6)
512
509
  """
513
510
  if isinstance(padstackInst, int):
514
511
  psdef = self.definitions[self.instances[padstackInst].padstack_definition].edb_padstack
@@ -518,7 +515,7 @@ class Padstacks(object):
518
515
  psdef = padstackInst.padstack_def
519
516
  newdefdata = GrpcPadstackDefData.create(psdef.data)
520
517
  newdefdata.solder_ball_shape = GrpcSolderballShape.SOLDERBALL_CYLINDER
521
- newdefdata.solder_ball_param(GrpcValue(ballDiam), GrpcValue(ballDiam))
518
+ newdefdata.solder_ball_param(Value(ballDiam), Value(ballDiam))
522
519
  sball_placement = (
523
520
  GrpcSolderballPlacement.ABOVE_PADSTACK if isTopPlaced else GrpcSolderballPlacement.BELOW_PADSTACK
524
521
  )
@@ -568,7 +565,9 @@ class Padstacks(object):
568
565
  self, padstackinstance, use_dot_separator=use_dot_separator, name=name
569
566
  )
570
567
 
571
- def get_pin_from_component_and_net(self, refdes=None, netname=None):
568
+ def get_pin_from_component_and_net(
569
+ self, refdes: Optional[str] = None, netname: Optional[str] = None
570
+ ) -> (List)[PadstackInstance]:
572
571
  """Retrieve pins by component reference designator and net name.
573
572
 
574
573
  Parameters
@@ -637,7 +636,9 @@ class Padstacks(object):
637
636
  )
638
637
  return self.get_pin_from_component_and_net(refdes=refdes, netname=netname)
639
638
 
640
- def get_pad_parameters(self, pin, layername, pad_type="regular_pad"):
639
+ def get_pad_parameters(
640
+ self, pin: PadstackInstance, layername: str, pad_type: str = "regular_pad"
641
+ ) -> Tuple[GrpcPadGeometryType, List[float], List[float], float]:
641
642
  """Get pad parameters for a pin on a specific layer.
642
643
 
643
644
  Parameters
@@ -675,10 +676,10 @@ class Padstacks(object):
675
676
  padparams = pin.padstack_def.data.get_pad_parameters(layername, pad_type)
676
677
  if len(padparams) == 5: # non polygon via
677
678
  geometry_type = padparams[0]
678
- parameters = [i.value for i in padparams[1]]
679
- offset_x = padparams[2].value
680
- offset_y = padparams[3].value
681
- rotation = padparams[4].value
679
+ parameters = [Value(i) for i in padparams[1]]
680
+ offset_x = Value(padparams[2])
681
+ offset_y = Value(padparams[3])
682
+ rotation = Value(padparams[4])
682
683
  return geometry_type.name, parameters, offset_x, offset_y, rotation
683
684
  elif len(padparams) == 4: # polygon based
684
685
  from ansys.edb.core.geometry.polygon_data import (
@@ -686,15 +687,15 @@ class Padstacks(object):
686
687
  )
687
688
 
688
689
  if isinstance(padparams[0], GrpcPolygonData):
689
- points = [[pt.x.value, pt.y.value] for pt in padparams[0].points]
690
- offset_x = padparams[1]
691
- offset_y = padparams[2]
692
- rotation = padparams[3]
690
+ points = [[Value(pt.x), Value(pt.y)] for pt in padparams[0].points]
691
+ offset_x = Value(padparams[1])
692
+ offset_y = Value(padparams[2])
693
+ rotation = Value(padparams[3])
693
694
  geometry_type = GrpcPadGeometryType.PADGEOMTYPE_POLYGON
694
695
  return geometry_type.name, points, offset_x, offset_y, rotation
695
696
  return 0, [0], 0, 0, 0
696
697
 
697
- def set_all_antipad_value(self, value):
698
+ def set_all_antipad_value(self, value: Union[float, str]) -> bool:
698
699
  """Set anti-pad value for all padstack definitions.
699
700
 
700
701
  Parameters
@@ -725,11 +726,11 @@ class Padstacks(object):
725
726
  cloned_padstack_data.set_pad_parameters(
726
727
  layer=layer,
727
728
  pad_type=GrpcPadType.ANTI_PAD,
728
- offset_x=GrpcValue(offset_x),
729
- offset_y=GrpcValue(offset_y),
730
- rotation=GrpcValue(rotation),
729
+ offset_x=Value(offset_x),
730
+ offset_y=Value(offset_y),
731
+ rotation=Value(rotation),
731
732
  type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
732
- sizes=[GrpcValue(value)],
733
+ sizes=[Value(value)],
733
734
  )
734
735
  self._logger.info(
735
736
  "Pad-stack definition {}, anti-pad on layer {}, has been set to {}".format(
@@ -749,7 +750,9 @@ class Padstacks(object):
749
750
  padstack.data = cloned_padstack_data
750
751
  return all_succeed
751
752
 
752
- def check_and_fix_via_plating(self, minimum_value_to_replace=0.0, default_plating_ratio=0.2):
753
+ def check_and_fix_via_plating(
754
+ self, minimum_value_to_replace: float = 0.0, default_plating_ratio: float = 0.2
755
+ ) -> bool:
753
756
  """Check and fix via plating ratios below a minimum value.
754
757
 
755
758
  Parameters
@@ -776,7 +779,7 @@ class Padstacks(object):
776
779
  )
777
780
  return True
778
781
 
779
- def get_via_instance_from_net(self, net_list=None):
782
+ def get_via_instance_from_net(self, net_list: Optional[Union[str, List[str]]] = None) -> List[PadstackInstance]:
780
783
  """Get via instances by net names.
781
784
 
782
785
  Parameters
@@ -809,31 +812,31 @@ class Padstacks(object):
809
812
 
810
813
  def create(
811
814
  self,
812
- padstackname=None,
813
- holediam="300um",
814
- paddiam="400um",
815
- antipaddiam="600um",
816
- pad_shape="Circle",
817
- antipad_shape="Circle",
818
- x_size="600um",
819
- y_size="600um",
820
- corner_radius="300um",
821
- offset_x="0.0",
822
- offset_y="0.0",
823
- rotation="0.0",
824
- has_hole=True,
825
- pad_offset_x="0.0",
826
- pad_offset_y="0.0",
827
- pad_rotation="0.0",
828
- pad_polygon=None,
829
- antipad_polygon=None,
830
- polygon_hole=None,
831
- start_layer=None,
832
- stop_layer=None,
833
- add_default_layer=False,
834
- anti_pad_x_size="600um",
835
- anti_pad_y_size="600um",
836
- hole_range="upper_pad_to_lower_pad",
815
+ padstackname: Optional[str] = None,
816
+ holediam: str = "300um",
817
+ paddiam: str = "400um",
818
+ antipaddiam: str = "600um",
819
+ pad_shape: str = "Circle",
820
+ antipad_shape: str = "Circle",
821
+ x_size: str = "600um",
822
+ y_size: str = "600um",
823
+ corner_radius: str = "300um",
824
+ offset_x: str = "0.0",
825
+ offset_y: str = "0.0",
826
+ rotation: str = "0.0",
827
+ has_hole: bool = True,
828
+ pad_offset_x: str = "0.0",
829
+ pad_offset_y: str = "0.0",
830
+ pad_rotation: str = "0.0",
831
+ pad_polygon: Optional[Any] = None,
832
+ antipad_polygon: Optional[Any] = None,
833
+ polygon_hole: Optional[Any] = None,
834
+ start_layer: Optional[str] = None,
835
+ stop_layer: Optional[str] = None,
836
+ add_default_layer: bool = False,
837
+ anti_pad_x_size: str = "600um",
838
+ anti_pad_y_size: str = "600um",
839
+ hole_range: str = "upper_pad_to_lower_pad",
837
840
  ):
838
841
  """Create a padstack definition.
839
842
 
@@ -896,11 +899,11 @@ class Padstacks(object):
896
899
  str
897
900
  Name of the created padstack definition.
898
901
  """
899
- holediam = GrpcValue(holediam)
900
- paddiam = GrpcValue(paddiam)
901
- antipaddiam = GrpcValue(antipaddiam)
902
+ holediam = Value(holediam)
903
+ paddiam = Value(paddiam)
904
+ antipaddiam = Value(antipaddiam)
902
905
  layers = list(self._pedb.stackup.signal_layers.keys())[:]
903
- value0 = GrpcValue("0.0")
906
+ value0 = Value("0.0")
904
907
  if not padstackname:
905
908
  padstackname = generate_unique_name("VIA")
906
909
  padstack_data = GrpcPadstackDefData.create()
@@ -913,7 +916,7 @@ class Padstacks(object):
913
916
  type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
914
917
  sizes=hole_param,
915
918
  )
916
- padstack_data.plating_percentage = GrpcValue(20.0)
919
+ padstack_data.plating_percentage = Value(20.0)
917
920
  elif polygon_hole:
918
921
  if isinstance(polygon_hole, list):
919
922
  polygon_hole = GrpcPolygonData(points=polygon_hole)
@@ -924,18 +927,18 @@ class Padstacks(object):
924
927
  type_geom=GrpcPadGeometryType.PADGEOMTYPE_POLYGON,
925
928
  fp=polygon_hole,
926
929
  )
927
- padstack_data.plating_percentage = GrpcValue(20.0)
930
+ padstack_data.plating_percentage = Value(20.0)
928
931
  else:
929
932
  pass
930
933
 
931
- x_size = GrpcValue(x_size)
932
- y_size = GrpcValue(y_size)
933
- corner_radius = GrpcValue(corner_radius)
934
- pad_offset_x = GrpcValue(pad_offset_x)
935
- pad_offset_y = GrpcValue(pad_offset_y)
936
- pad_rotation = GrpcValue(pad_rotation)
937
- anti_pad_x_size = GrpcValue(anti_pad_x_size)
938
- anti_pad_y_size = GrpcValue(anti_pad_y_size)
934
+ x_size = Value(x_size)
935
+ y_size = Value(y_size)
936
+ corner_radius = Value(corner_radius)
937
+ pad_offset_x = Value(pad_offset_x)
938
+ pad_offset_y = Value(pad_offset_y)
939
+ pad_rotation = Value(pad_rotation)
940
+ anti_pad_x_size = Value(anti_pad_x_size)
941
+ anti_pad_y_size = Value(anti_pad_y_size)
939
942
 
940
943
  if hole_range == "through": # pragma no cover
941
944
  padstack_data.hole_range = GrpcPadstackHoleRange.THROUGH
@@ -1031,14 +1034,14 @@ class Padstacks(object):
1031
1034
  self._logger.info(f"Padstack {padstackname} create correctly")
1032
1035
  return padstackname
1033
1036
 
1034
- def _get_pin_layer_range(self, pin):
1037
+ def _get_pin_layer_range(self, pin: PadstackInstance) -> Union[Tuple[str, str], bool]:
1035
1038
  layers = pin.get_layer_range()
1036
1039
  if layers:
1037
1040
  return layers[0], layers[1]
1038
1041
  else:
1039
1042
  return False
1040
1043
 
1041
- def duplicate(self, target_padstack_name, new_padstack_name=""):
1044
+ def duplicate(self, target_padstack_name: str, new_padstack_name: str = "") -> str:
1042
1045
  """Duplicate a padstack definition.
1043
1046
 
1044
1047
  Parameters
@@ -1062,16 +1065,16 @@ class Padstacks(object):
1062
1065
 
1063
1066
  def place(
1064
1067
  self,
1065
- position,
1066
- definition_name,
1067
- net_name="",
1068
- via_name="",
1069
- rotation=0.0,
1070
- fromlayer=None,
1071
- tolayer=None,
1072
- solderlayer=None,
1073
- is_pin=False,
1074
- ):
1068
+ position: List[float],
1069
+ definition_name: str,
1070
+ net_name: str = "",
1071
+ via_name: str = "",
1072
+ rotation: float = 0.0,
1073
+ fromlayer: Optional[str] = None,
1074
+ tolayer: Optional[str] = None,
1075
+ solderlayer: Optional[str] = None,
1076
+ is_pin: bool = False,
1077
+ ) -> PadstackInstance:
1075
1078
  """Place a padstack instance.
1076
1079
 
1077
1080
  Parameters
@@ -1105,10 +1108,10 @@ class Padstacks(object):
1105
1108
  if pad == definition_name:
1106
1109
  padstack_def = self.definitions[pad]
1107
1110
  position = GrpcPointData(
1108
- [GrpcValue(position[0], self._pedb.active_cell), GrpcValue(position[1], self._pedb.active_cell)]
1111
+ [Value(position[0], self._pedb.active_cell), Value(position[1], self._pedb.active_cell)]
1109
1112
  )
1110
1113
  net = self._pedb.nets.find_or_create_net(net_name)
1111
- rotation = GrpcValue(rotation * math.pi / 180)
1114
+ rotation = Value(rotation * math.pi / 180)
1112
1115
  sign_layers_values = {i: v for i, v in self._pedb.stackup.signal_layers.items()}
1113
1116
  sign_layers = list(sign_layers_values.keys())
1114
1117
  if not fromlayer:
@@ -1149,7 +1152,7 @@ class Padstacks(object):
1149
1152
  else:
1150
1153
  return False
1151
1154
 
1152
- def remove_pads_from_padstack(self, padstack_name, layer_name=None):
1155
+ def remove_pads_from_padstack(self, padstack_name: str, layer_name: Optional[str] = None):
1153
1156
  """Remove pads from a padstack definition on specified layers.
1154
1157
 
1155
1158
  Parameters
@@ -1166,8 +1169,8 @@ class Padstacks(object):
1166
1169
  """
1167
1170
  pad_type = GrpcPadType.REGULAR_PAD
1168
1171
  pad_geo = GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
1169
- vals = GrpcValue(0)
1170
- params = [GrpcValue(0)]
1172
+ vals = Value(0)
1173
+ params = [Value(0)]
1171
1174
  new_padstack_definition_data = GrpcPadstackDefData(self.definitions[padstack_name].data)
1172
1175
  if not layer_name:
1173
1176
  layer_name = list(self._pedb.stackup.signal_layers.keys())
@@ -1188,18 +1191,18 @@ class Padstacks(object):
1188
1191
 
1189
1192
  def set_pad_property(
1190
1193
  self,
1191
- padstack_name,
1192
- layer_name=None,
1193
- pad_shape="Circle",
1194
- pad_params=0,
1195
- pad_x_offset=0,
1196
- pad_y_offset=0,
1197
- pad_rotation=0,
1198
- antipad_shape="Circle",
1199
- antipad_params=0,
1200
- antipad_x_offset=0,
1201
- antipad_y_offset=0,
1202
- antipad_rotation=0,
1194
+ padstack_name: str,
1195
+ layer_name: Optional[str] = None,
1196
+ pad_shape: str = "Circle",
1197
+ pad_params: Union[float, List[float]] = 0,
1198
+ pad_x_offset: float = 0,
1199
+ pad_y_offset: float = 0,
1200
+ pad_rotation: float = 0,
1201
+ antipad_shape: str = "Circle",
1202
+ antipad_params: Union[float, List[float]] = 0,
1203
+ antipad_x_offset: float = 0,
1204
+ antipad_y_offset: float = 0,
1205
+ antipad_rotation: float = 0,
1203
1206
  ):
1204
1207
  """Set pad and anti-pad properties for a padstack definition.
1205
1208
 
@@ -1245,18 +1248,18 @@ class Padstacks(object):
1245
1248
  pad_shape = shape_dict[pad_shape]
1246
1249
  if not isinstance(pad_params, list):
1247
1250
  pad_params = [pad_params]
1248
- pad_params = [GrpcValue(i) for i in pad_params]
1249
- pad_x_offset = GrpcValue(pad_x_offset)
1250
- pad_y_offset = GrpcValue(pad_y_offset)
1251
- pad_rotation = GrpcValue(pad_rotation)
1251
+ pad_params = [Value(i) for i in pad_params]
1252
+ pad_x_offset = Value(pad_x_offset)
1253
+ pad_y_offset = Value(pad_y_offset)
1254
+ pad_rotation = Value(pad_rotation)
1252
1255
 
1253
1256
  antipad_shape = shape_dict[antipad_shape]
1254
1257
  if not isinstance(antipad_params, list):
1255
1258
  antipad_params = [antipad_params]
1256
- antipad_params = [GrpcValue(i) for i in antipad_params]
1257
- antipad_x_offset = GrpcValue(antipad_x_offset)
1258
- antipad_y_offset = GrpcValue(antipad_y_offset)
1259
- antipad_rotation = GrpcValue(antipad_rotation)
1259
+ antipad_params = [Value(i) for i in antipad_params]
1260
+ antipad_x_offset = Value(antipad_x_offset)
1261
+ antipad_y_offset = Value(antipad_y_offset)
1262
+ antipad_rotation = Value(antipad_rotation)
1260
1263
  new_padstack_def = GrpcPadstackDefData(self.definitions[padstack_name].data.msg)
1261
1264
  if not layer_name:
1262
1265
  layer_name = list(self._pedb.stackup.signal_layers.keys())
@@ -1286,13 +1289,13 @@ class Padstacks(object):
1286
1289
 
1287
1290
  def get_instances(
1288
1291
  self,
1289
- name=None,
1290
- pid=None,
1291
- definition_name=None,
1292
- net_name=None,
1293
- component_reference_designator=None,
1294
- component_pin=None,
1295
- ):
1292
+ name: Optional[str] = None,
1293
+ pid: Optional[int] = None,
1294
+ definition_name: Optional[str] = None,
1295
+ net_name: Optional[str] = None,
1296
+ component_reference_designator: Optional[str] = None,
1297
+ component_pin: Optional[str] = None,
1298
+ ) -> List[PadstackInstance]:
1296
1299
  """Get padstack instances by search criteria.
1297
1300
 
1298
1301
  Parameters
@@ -1344,8 +1347,13 @@ class Padstacks(object):
1344
1347
  return instances
1345
1348
 
1346
1349
  def get_reference_pins(
1347
- self, positive_pin, reference_net="gnd", search_radius=5e-3, max_limit=0, component_only=True
1348
- ):
1350
+ self,
1351
+ positive_pin: Union[int, str, PadstackInstance],
1352
+ reference_net: str = "gnd",
1353
+ search_radius: float = 5e-3,
1354
+ max_limit: int = 0,
1355
+ component_only: bool = True,
1356
+ ) -> List[PadstackInstance]:
1349
1357
  """Find reference pins near a specified pin.
1350
1358
 
1351
1359
  Parameters
@@ -1390,7 +1398,7 @@ class Padstacks(object):
1390
1398
  pinlist = [pin[1] for pin in sorted(pin_dict.items())[:max_limit]]
1391
1399
  return pinlist
1392
1400
 
1393
- def get_padstack_instances_rtree_index(self, nets=None):
1401
+ def get_padstack_instances_rtree_index(self, nets: Optional[Union[str, List[str]]] = None) -> rtree.index.Index:
1394
1402
  """Returns padstack instances Rtree index.
1395
1403
 
1396
1404
  Parameters
@@ -1415,7 +1423,12 @@ class Padstacks(object):
1415
1423
  padstack_instances_index.insert(inst.edb_uid, inst.position)
1416
1424
  return padstack_instances_index
1417
1425
 
1418
- def get_padstack_instances_id_intersecting_polygon(self, points, nets=None, padstack_instances_index=None):
1426
+ def get_padstack_instances_id_intersecting_polygon(
1427
+ self,
1428
+ points: List[Tuple[float, float]],
1429
+ nets: Optional[Union[str, List[str]]] = None,
1430
+ padstack_instances_index: Optional[Dict[int, Tuple[float, float]]] = None,
1431
+ ) -> List[int]:
1419
1432
  """Returns the list of padstack instances ID intersecting a given bounding box and nets.
1420
1433
 
1421
1434
  Parameters
@@ -1446,7 +1459,12 @@ class Padstacks(object):
1446
1459
  ind for ind, pt in padstack_instances_index.items() if GeometryOperators.is_point_in_polygon(pt, points)
1447
1460
  ]
1448
1461
 
1449
- def get_padstack_instances_intersecting_bounding_box(self, bounding_box, nets=None, padstack_instances_index=None):
1462
+ def get_padstack_instances_intersecting_bounding_box(
1463
+ self,
1464
+ bounding_box: List[float],
1465
+ nets: Optional[Union[str, List[str]]] = None,
1466
+ padstack_instances_index: Optional[rtree.index.Index] = None,
1467
+ ) -> List[PadstackInstance]:
1450
1468
  """Returns the list of padstack instances ID intersecting a given bounding box and nets.
1451
1469
  Parameters
1452
1470
  ----------
@@ -1475,12 +1493,12 @@ class Padstacks(object):
1475
1493
 
1476
1494
  def merge_via_along_lines(
1477
1495
  self,
1478
- net_name="GND",
1479
- distance_threshold=5e-3,
1480
- minimum_via_number=6,
1481
- selected_angles=None,
1482
- padstack_instances_id=None,
1483
- ):
1496
+ net_name: str = "GND",
1497
+ distance_threshold: float = 5e-3,
1498
+ minimum_via_number: int = 6,
1499
+ selected_angles: Optional[List[float]] = None,
1500
+ padstack_instances_id: Optional[List[int]] = None,
1501
+ ) -> None:
1484
1502
  """Replace padstack instances along lines into a single polygon.
1485
1503
 
1486
1504
  Detect all pad-stack instances that are placed along lines and replace them by a single polygon based one
@@ -1622,7 +1640,13 @@ class Padstacks(object):
1622
1640
 
1623
1641
  return True
1624
1642
 
1625
- def merge_via(self, contour_boxes, net_filter=None, start_layer=None, stop_layer=None):
1643
+ def merge_via(
1644
+ self,
1645
+ contour_boxes: List[List[float]],
1646
+ net_filter: Optional[Union[str, List[str]]] = None,
1647
+ start_layer: Optional[str] = None,
1648
+ stop_layer: Optional[str] = None,
1649
+ ) -> bool:
1626
1650
  """Evaluate pad-stack instances included on the provided point list and replace all by single instance.
1627
1651
 
1628
1652
  Parameters
@@ -1688,7 +1712,9 @@ class Padstacks(object):
1688
1712
  [self.instances[inst].delete() for inst in instances]
1689
1713
  return merged_via_ids
1690
1714
 
1691
- def reduce_via_in_bounding_box(self, bounding_box, x_samples, y_samples, nets=None):
1715
+ def reduce_via_in_bounding_box(
1716
+ self, bounding_box: List[float], x_samples: int, y_samples: int, nets: Optional[Union[str, List[str]]] = None
1717
+ ) -> bool:
1692
1718
  """
1693
1719
  reduce the number of vias intersecting bounding box and nets by x and y samples.
1694
1720
 
@@ -1740,3 +1766,72 @@ class Padstacks(object):
1740
1766
  if item not in to_keep:
1741
1767
  all_instances[item].delete()
1742
1768
  return True
1769
+
1770
+ @staticmethod
1771
+ def dbscan(
1772
+ padstack: Dict[int, List[float]], max_distance: float = 1e-3, min_samples: int = 5
1773
+ ) -> Dict[int, List[str]]:
1774
+ """
1775
+ density based spatial clustering for padstack instances
1776
+
1777
+ Parameters
1778
+ ----------
1779
+ padstack : dict.
1780
+ padstack id: [x, y]
1781
+
1782
+ max_distance: float
1783
+ maximum distance between two points to be included in one cluster
1784
+
1785
+ min_samples: int
1786
+ minimum number of points that a cluster must have
1787
+
1788
+ Returns
1789
+ -------
1790
+ dict
1791
+ clusters {cluster label: [padstack ids]} <
1792
+ """
1793
+
1794
+ padstack_ids = list(padstack.keys())
1795
+ xy_array = np.array([padstack[pid] for pid in padstack_ids])
1796
+ n = len(padstack_ids)
1797
+
1798
+ labels = -1 * np.ones(n, dtype=int)
1799
+ visited = np.zeros(n, dtype=bool)
1800
+ cluster_id = 0
1801
+
1802
+ def region_query(point_idx):
1803
+ distances = np.linalg.norm(xy_array - xy_array[point_idx], axis=1)
1804
+ return np.where(distances <= max_distance)[0]
1805
+
1806
+ def expand_cluster(point_idx, neighbors):
1807
+ nonlocal cluster_id
1808
+ labels[point_idx] = cluster_id
1809
+ i = 0
1810
+ while i < len(neighbors):
1811
+ neighbor_idx = neighbors[i]
1812
+ if not visited[neighbor_idx]:
1813
+ visited[neighbor_idx] = True
1814
+ neighbor_neighbors = region_query(neighbor_idx)
1815
+ if len(neighbor_neighbors) >= min_samples:
1816
+ neighbors = np.concatenate((neighbors, neighbor_neighbors))
1817
+ if labels[neighbor_idx] == -1:
1818
+ labels[neighbor_idx] = cluster_id
1819
+ i += 1
1820
+
1821
+ for point_idx in range(n):
1822
+ if visited[point_idx]:
1823
+ continue
1824
+ visited[point_idx] = True
1825
+ neighbors = region_query(point_idx)
1826
+ if len(neighbors) < min_samples:
1827
+ labels[point_idx] = -1
1828
+ else:
1829
+ expand_cluster(point_idx, neighbors)
1830
+ cluster_id += 1
1831
+
1832
+ # group point IDs by label
1833
+ clusters = defaultdict(list)
1834
+ for i, label in enumerate(labels):
1835
+ clusters[int(label)].append(padstack_ids[i])
1836
+
1837
+ return dict(clusters)