pyedb 0.52.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.
- pyedb/__init__.py +1 -1
- pyedb/configuration/cfg_common.py +12 -15
- pyedb/configuration/cfg_data.py +2 -2
- pyedb/configuration/cfg_modeler.py +163 -234
- pyedb/configuration/cfg_ports_sources.py +6 -8
- pyedb/configuration/cfg_stackup.py +62 -249
- pyedb/configuration/configuration.py +272 -170
- pyedb/dotnet/database/cell/hierarchy/model.py +1 -1
- pyedb/dotnet/database/cell/layout.py +1 -1
- pyedb/dotnet/database/cell/layout_obj.py +3 -3
- pyedb/dotnet/database/cell/primitive/path.py +1 -1
- pyedb/dotnet/database/cell/primitive/primitive.py +8 -8
- pyedb/dotnet/database/cell/terminal/pingroup_terminal.py +1 -1
- pyedb/dotnet/database/cell/terminal/point_terminal.py +1 -1
- pyedb/dotnet/database/cell/terminal/terminal.py +24 -26
- pyedb/dotnet/database/components.py +33 -27
- pyedb/dotnet/database/definition/component_def.py +3 -3
- pyedb/dotnet/database/definition/component_model.py +1 -1
- pyedb/dotnet/database/definition/package_def.py +1 -1
- pyedb/dotnet/database/dotnet/database.py +47 -38
- pyedb/dotnet/database/dotnet/primitive.py +16 -16
- pyedb/dotnet/database/edb_data/hfss_extent_info.py +6 -6
- pyedb/dotnet/database/edb_data/layer_data.py +17 -15
- pyedb/dotnet/database/edb_data/padstacks_data.py +12 -12
- pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
- pyedb/dotnet/database/edb_data/sources.py +6 -6
- pyedb/dotnet/database/edb_data/variables.py +7 -3
- pyedb/dotnet/database/geometry/point_data.py +1 -1
- pyedb/dotnet/database/geometry/polygon_data.py +2 -4
- pyedb/dotnet/database/hfss.py +7 -7
- pyedb/dotnet/database/materials.py +2 -2
- pyedb/dotnet/database/modeler.py +8 -11
- pyedb/dotnet/database/nets.py +1 -1
- pyedb/dotnet/database/padstack.py +72 -1
- pyedb/dotnet/database/sim_setup_data/data/settings.py +24 -0
- pyedb/dotnet/database/sim_setup_data/io/siwave.py +26 -1
- pyedb/dotnet/database/siwave.py +19 -5
- pyedb/dotnet/database/stackup.py +80 -137
- pyedb/dotnet/database/utilities/heatsink.py +4 -4
- pyedb/dotnet/database/utilities/obj_base.py +1 -1
- pyedb/dotnet/database/utilities/simulation_setup.py +1 -1
- pyedb/dotnet/database/utilities/siwave_cpa_simulation_setup.py +894 -0
- pyedb/dotnet/database/utilities/siwave_simulation_setup.py +15 -0
- pyedb/dotnet/database/utilities/value.py +116 -0
- pyedb/dotnet/edb.py +58 -45
- pyedb/generic/design_types.py +39 -1
- pyedb/generic/grpc_warnings.py +5 -0
- pyedb/grpc/__init__.py +0 -0
- pyedb/grpc/database/components.py +155 -98
- pyedb/grpc/database/control_file.py +240 -193
- pyedb/grpc/database/definition/materials.py +23 -30
- pyedb/grpc/database/definition/package_def.py +15 -15
- pyedb/grpc/database/definition/padstack_def.py +51 -51
- pyedb/grpc/database/definitions.py +7 -5
- pyedb/grpc/database/geometry/arc_data.py +7 -5
- pyedb/grpc/database/geometry/point_3d_data.py +8 -7
- pyedb/grpc/database/geometry/polygon_data.py +3 -2
- pyedb/grpc/database/hierarchy/component.py +43 -38
- pyedb/grpc/database/hierarchy/pin_pair_model.py +15 -14
- pyedb/grpc/database/hierarchy/pingroup.py +9 -9
- pyedb/grpc/database/layers/stackup_layer.py +45 -44
- pyedb/grpc/database/layout/layout.py +9 -8
- pyedb/grpc/database/layout/voltage_regulator.py +7 -7
- pyedb/grpc/database/layout_validation.py +13 -12
- pyedb/grpc/database/modeler.py +156 -131
- pyedb/grpc/database/nets.py +42 -31
- pyedb/grpc/database/padstacks.py +270 -175
- pyedb/grpc/database/ports/ports.py +5 -6
- pyedb/grpc/database/primitive/bondwire.py +8 -7
- pyedb/grpc/database/primitive/circle.py +4 -4
- pyedb/grpc/database/primitive/padstack_instance.py +18 -18
- pyedb/grpc/database/primitive/path.py +7 -7
- pyedb/grpc/database/primitive/polygon.py +3 -3
- pyedb/grpc/database/primitive/primitive.py +13 -17
- pyedb/grpc/database/primitive/rectangle.py +13 -13
- pyedb/grpc/database/simulation_setup/hfss_general_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +10 -0
- pyedb/grpc/database/simulation_setup/siwave_cpa_simulation_setup.py +961 -0
- pyedb/grpc/database/simulation_setup/siwave_simulation_setup.py +17 -1
- pyedb/grpc/database/siwave.py +44 -24
- pyedb/grpc/database/source_excitations.py +333 -229
- pyedb/grpc/database/stackup.py +164 -147
- pyedb/grpc/database/terminal/bundle_terminal.py +17 -7
- pyedb/grpc/database/terminal/edge_terminal.py +10 -0
- pyedb/grpc/database/terminal/padstack_instance_terminal.py +15 -4
- pyedb/grpc/database/terminal/pingroup_terminal.py +11 -10
- pyedb/grpc/database/terminal/point_terminal.py +4 -3
- pyedb/grpc/database/terminal/terminal.py +9 -9
- pyedb/grpc/database/utility/value.py +109 -0
- pyedb/grpc/edb.py +129 -45
- pyedb/grpc/edb_init.py +0 -7
- pyedb/siwave_core/cpa/simulation_setup_data_model.py +132 -0
- pyedb/siwave_core/product_properties.py +198 -0
- {pyedb-0.52.0.dist-info → pyedb-0.54.0.dist-info}/METADATA +15 -13
- {pyedb-0.52.0.dist-info → pyedb-0.54.0.dist-info}/RECORD +97 -89
- {pyedb-0.52.0.dist-info → pyedb-0.54.0.dist-info}/WHEEL +1 -1
- {pyedb-0.52.0.dist-info → pyedb-0.54.0.dist-info/licenses}/LICENSE +0 -0
pyedb/grpc/database/padstacks.py
CHANGED
|
@@ -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 = [
|
|
398
|
+
list_values = [Value(holediam), Value(paddiam), Value(antipaddiam)]
|
|
397
399
|
padstack_data.set_hole_parameters(
|
|
398
|
-
offset_x=
|
|
399
|
-
offset_y=
|
|
400
|
-
rotation=
|
|
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=
|
|
419
|
-
offset_y=
|
|
420
|
-
rotation=
|
|
421
|
-
sizes=[
|
|
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=
|
|
429
|
-
offset_y=
|
|
430
|
-
rotation=
|
|
431
|
-
sizes=[
|
|
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=
|
|
445
|
-
offset_y=
|
|
446
|
-
rotation=
|
|
447
|
-
sizes=[
|
|
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=
|
|
455
|
-
offset_y=
|
|
456
|
-
rotation=
|
|
457
|
-
sizes=[
|
|
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
|
|
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
|
|
492
|
+
"""Set solderball for the given PadstackInstance.
|
|
491
493
|
|
|
492
494
|
Parameters
|
|
493
495
|
----------
|
|
494
|
-
padstackInst :
|
|
495
|
-
Padstack instance
|
|
496
|
-
sballLayer_name : str
|
|
497
|
-
Name of the layer where the solder ball is placed.
|
|
498
|
-
isTopPlaced : bool, optional
|
|
499
|
-
|
|
500
|
-
ballDiam :
|
|
501
|
-
Solder ball diameter
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
679
|
-
offset_x = padparams[2]
|
|
680
|
-
offset_y = padparams[3]
|
|
681
|
-
rotation = padparams[4]
|
|
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
|
|
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=
|
|
729
|
-
offset_y=
|
|
730
|
-
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=[
|
|
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(
|
|
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 =
|
|
900
|
-
paddiam =
|
|
901
|
-
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 =
|
|
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 =
|
|
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 =
|
|
930
|
+
padstack_data.plating_percentage = Value(20.0)
|
|
928
931
|
else:
|
|
929
932
|
pass
|
|
930
933
|
|
|
931
|
-
x_size =
|
|
932
|
-
y_size =
|
|
933
|
-
corner_radius =
|
|
934
|
-
pad_offset_x =
|
|
935
|
-
pad_offset_y =
|
|
936
|
-
pad_rotation =
|
|
937
|
-
anti_pad_x_size =
|
|
938
|
-
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
|
-
[
|
|
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 =
|
|
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 =
|
|
1170
|
-
params = [
|
|
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 = [
|
|
1249
|
-
pad_x_offset =
|
|
1250
|
-
pad_y_offset =
|
|
1251
|
-
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 = [
|
|
1257
|
-
antipad_x_offset =
|
|
1258
|
-
antipad_y_offset =
|
|
1259
|
-
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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)
|