pyedb 0.37.0__py3-none-any.whl → 0.39.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/common/nets.py +53 -139
- pyedb/configuration/cfg_common.py +1 -1
- pyedb/configuration/cfg_components.py +229 -201
- pyedb/configuration/cfg_data.py +3 -1
- pyedb/configuration/cfg_general.py +4 -2
- pyedb/configuration/cfg_modeler.py +7 -7
- pyedb/configuration/cfg_package_definition.py +1 -1
- pyedb/configuration/cfg_padstacks.py +346 -290
- pyedb/configuration/cfg_ports_sources.py +243 -65
- pyedb/configuration/configuration.py +23 -3
- pyedb/dotnet/{application → database}/Variables.py +21 -21
- pyedb/dotnet/{edb_core → database}/cell/connectable.py +5 -5
- pyedb/dotnet/{edb_core → database}/cell/hierarchy/component.py +11 -11
- pyedb/dotnet/{edb_core → database}/cell/hierarchy/hierarchy_obj.py +1 -1
- pyedb/dotnet/{edb_core → database}/cell/hierarchy/model.py +1 -1
- pyedb/dotnet/{edb_core → database}/cell/layout.py +19 -19
- pyedb/dotnet/{edb_core → database}/cell/layout_obj.py +3 -3
- pyedb/dotnet/{edb_core → database}/cell/primitive/bondwire.py +1 -1
- pyedb/dotnet/{edb_core → database}/cell/primitive/path.py +4 -4
- pyedb/dotnet/{edb_core → database}/cell/primitive/primitive.py +14 -14
- pyedb/dotnet/{edb_core → database}/cell/terminal/bundle_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/edge_terminal.py +4 -4
- pyedb/dotnet/{edb_core → database}/cell/terminal/padstack_instance_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/pingroup_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/point_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/terminal.py +11 -11
- pyedb/dotnet/{edb_core → database}/cell/voltage_regulator.py +2 -2
- pyedb/dotnet/{edb_core → database}/components.py +101 -124
- pyedb/dotnet/{edb_core → database}/definition/component_def.py +5 -5
- pyedb/dotnet/{edb_core → database}/definition/component_model.py +1 -1
- pyedb/dotnet/{edb_core → database}/definition/definition_obj.py +1 -1
- pyedb/dotnet/{edb_core → database}/definition/definitions.py +2 -2
- pyedb/dotnet/{edb_core → database}/definition/package_def.py +4 -4
- pyedb/dotnet/{edb_core → database}/dotnet/database.py +8 -8
- pyedb/dotnet/{edb_core → database}/dotnet/primitive.py +9 -9
- pyedb/dotnet/{edb_core → database}/edb_data/control_file.py +12 -12
- pyedb/dotnet/{edb_core → database}/edb_data/hfss_extent_info.py +7 -7
- pyedb/dotnet/{edb_core → database}/edb_data/nets_data.py +10 -13
- pyedb/dotnet/{edb_core → database}/edb_data/padstacks_data.py +60 -73
- pyedb/dotnet/{edb_core → database}/edb_data/ports.py +4 -4
- pyedb/dotnet/{edb_core → database}/edb_data/primitives_data.py +5 -5
- pyedb/dotnet/{edb_core → database}/edb_data/raptor_x_simulation_setup_data.py +4 -4
- pyedb/dotnet/{edb_core → database}/edb_data/simulation_configuration.py +10 -10
- pyedb/dotnet/{edb_core → database}/edb_data/sources.py +4 -4
- pyedb/dotnet/{edb_core → database}/edb_data/variables.py +1 -1
- pyedb/dotnet/{edb_core → database}/geometry/polygon_data.py +4 -4
- pyedb/dotnet/{edb_core → database}/hfss.py +8 -8
- pyedb/dotnet/{edb_core → database}/layout_obj_instance.py +1 -1
- pyedb/dotnet/{edb_core → database}/layout_validation.py +2 -2
- pyedb/dotnet/{edb_core → database}/materials.py +23 -8
- pyedb/dotnet/{edb_core → database}/modeler.py +27 -27
- pyedb/dotnet/{edb_core → database}/net_class.py +8 -8
- pyedb/dotnet/{edb_core → database}/nets.py +12 -12
- pyedb/dotnet/{edb_core → database}/padstack.py +17 -16
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/mesh_operation.py +1 -1
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/settings.py +18 -3
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sim_setup_info.py +2 -2
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/simulation_settings.py +1 -1
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/siw_dc_ir_settings.py +1 -1
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sweep_data.py +4 -4
- pyedb/dotnet/{edb_core → database}/siwave.py +10 -10
- pyedb/dotnet/{edb_core → database}/stackup.py +12 -12
- pyedb/dotnet/{edb_core → database}/utilities/hfss_simulation_setup.py +15 -15
- pyedb/dotnet/{edb_core → database}/utilities/obj_base.py +1 -1
- pyedb/dotnet/{edb_core → database}/utilities/simulation_setup.py +4 -3
- pyedb/dotnet/{edb_core → database}/utilities/siwave_simulation_setup.py +6 -6
- pyedb/dotnet/edb.py +118 -113
- pyedb/extensions/pre_layout_design_toolkit/via_design.py +1151 -0
- pyedb/generic/design_types.py +26 -19
- pyedb/generic/general_methods.py +1 -1
- pyedb/generic/plot.py +0 -2
- pyedb/grpc/database/__init__.py +1 -0
- pyedb/grpc/database/components.py +2354 -0
- pyedb/grpc/database/control_file.py +1277 -0
- pyedb/grpc/database/definition/component_def.py +218 -0
- pyedb/grpc/database/definition/component_model.py +39 -0
- pyedb/grpc/database/definition/component_pin.py +32 -0
- pyedb/grpc/database/definition/materials.py +1207 -0
- pyedb/grpc/database/definition/n_port_component_model.py +34 -0
- pyedb/grpc/database/definition/package_def.py +227 -0
- pyedb/grpc/database/definition/padstack_def.py +842 -0
- pyedb/grpc/database/definitions.py +70 -0
- pyedb/grpc/database/general.py +43 -0
- pyedb/grpc/database/geometry/__init__.py +0 -0
- pyedb/grpc/database/geometry/arc_data.py +93 -0
- pyedb/grpc/database/geometry/point_3d_data.py +79 -0
- pyedb/grpc/database/geometry/point_data.py +30 -0
- pyedb/grpc/database/geometry/polygon_data.py +133 -0
- pyedb/grpc/database/hfss.py +1279 -0
- pyedb/grpc/database/hierarchy/__init__.py +0 -0
- pyedb/grpc/database/hierarchy/component.py +1301 -0
- pyedb/grpc/database/hierarchy/model.py +31 -0
- pyedb/grpc/database/hierarchy/netlist_model.py +30 -0
- pyedb/grpc/database/hierarchy/pin_pair_model.py +128 -0
- pyedb/grpc/database/hierarchy/pingroup.py +245 -0
- pyedb/grpc/database/hierarchy/s_parameter_model.py +33 -0
- pyedb/grpc/database/hierarchy/spice_model.py +48 -0
- pyedb/grpc/database/layers/__init__.py +0 -0
- pyedb/grpc/database/layers/layer.py +57 -0
- pyedb/grpc/database/layers/stackup_layer.py +410 -0
- pyedb/grpc/database/layout/__init__.py +0 -0
- pyedb/grpc/database/layout/cell.py +30 -0
- pyedb/grpc/database/layout/layout.py +196 -0
- pyedb/grpc/database/layout/voltage_regulator.py +149 -0
- pyedb/grpc/database/layout_validation.py +319 -0
- pyedb/grpc/database/modeler.py +1468 -0
- pyedb/grpc/database/net/__init__.py +0 -0
- pyedb/grpc/database/net/differential_pair.py +138 -0
- pyedb/grpc/database/net/extended_net.py +340 -0
- pyedb/grpc/database/net/net.py +198 -0
- pyedb/grpc/database/net/net_class.py +93 -0
- pyedb/grpc/database/nets.py +633 -0
- pyedb/grpc/database/padstacks.py +1500 -0
- pyedb/grpc/database/ports/__init__.py +0 -0
- pyedb/grpc/database/ports/ports.py +396 -0
- pyedb/grpc/database/primitive/__init__.py +3 -0
- pyedb/grpc/database/primitive/bondwire.py +181 -0
- pyedb/grpc/database/primitive/circle.py +75 -0
- pyedb/grpc/database/primitive/padstack_instance.py +1116 -0
- pyedb/grpc/database/primitive/path.py +346 -0
- pyedb/grpc/database/primitive/polygon.py +276 -0
- pyedb/grpc/database/primitive/primitive.py +739 -0
- pyedb/grpc/database/primitive/rectangle.py +146 -0
- pyedb/grpc/database/simulation_setup/__init__.py +0 -0
- pyedb/grpc/database/simulation_setup/adaptive_frequency.py +33 -0
- pyedb/grpc/database/simulation_setup/hfss_advanced_meshing_settings.py +32 -0
- pyedb/grpc/database/simulation_setup/hfss_advanced_settings.py +59 -0
- pyedb/grpc/database/simulation_setup/hfss_dcr_settings.py +35 -0
- pyedb/grpc/database/simulation_setup/hfss_general_settings.py +61 -0
- pyedb/grpc/database/simulation_setup/hfss_settings_options.py +78 -0
- pyedb/grpc/database/simulation_setup/hfss_simulation_settings.py +118 -0
- pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +355 -0
- pyedb/grpc/database/simulation_setup/hfss_solver_settings.py +34 -0
- pyedb/grpc/database/simulation_setup/mesh_operation.py +34 -0
- pyedb/grpc/database/simulation_setup/raptor_x_advanced_settings.py +34 -0
- pyedb/grpc/database/simulation_setup/raptor_x_general_settings.py +33 -0
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_settings.py +64 -0
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_setup.py +125 -0
- pyedb/grpc/database/simulation_setup/siwave_dcir_simulation_setup.py +34 -0
- pyedb/grpc/database/simulation_setup/siwave_simulation_setup.py +119 -0
- pyedb/grpc/database/simulation_setup/sweep_data.py +32 -0
- pyedb/grpc/database/siwave.py +1023 -0
- pyedb/grpc/database/source_excitations.py +2572 -0
- pyedb/grpc/database/stackup.py +2574 -0
- pyedb/grpc/database/terminal/__init__.py +0 -0
- pyedb/grpc/database/terminal/bundle_terminal.py +218 -0
- pyedb/grpc/database/terminal/edge_terminal.py +51 -0
- pyedb/grpc/database/terminal/padstack_instance_terminal.py +171 -0
- pyedb/grpc/database/terminal/pingroup_terminal.py +162 -0
- pyedb/grpc/database/terminal/point_terminal.py +99 -0
- pyedb/grpc/database/terminal/terminal.py +470 -0
- pyedb/grpc/database/utility/__init__.py +3 -0
- pyedb/grpc/database/utility/constants.py +25 -0
- pyedb/grpc/database/utility/heat_sink.py +124 -0
- pyedb/grpc/database/utility/hfss_extent_info.py +448 -0
- pyedb/grpc/database/utility/layout_statistics.py +277 -0
- pyedb/grpc/database/utility/rlc.py +80 -0
- pyedb/grpc/database/utility/simulation_configuration.py +3305 -0
- pyedb/grpc/database/utility/sources.py +388 -0
- pyedb/grpc/database/utility/sweep_data_distribution.py +83 -0
- pyedb/grpc/database/utility/xml_control_file.py +1277 -0
- pyedb/grpc/edb.py +4151 -0
- pyedb/grpc/edb_init.py +481 -0
- pyedb/grpc/rpc_session.py +177 -0
- pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +3 -2
- pyedb/ipc2581/ecad/cad_data/feature.py +4 -3
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +32 -20
- pyedb/ipc2581/ecad/cad_data/outline.py +3 -2
- pyedb/ipc2581/ecad/cad_data/package.py +4 -3
- pyedb/ipc2581/ecad/cad_data/path.py +82 -31
- pyedb/ipc2581/ecad/cad_data/polygon.py +122 -60
- pyedb/ipc2581/ecad/cad_data/profile.py +13 -12
- pyedb/ipc2581/ecad/cad_data/step.py +52 -20
- pyedb/ipc2581/ipc2581.py +47 -49
- pyedb/modeler/geometry_operators.py +1 -1
- {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/METADATA +9 -6
- pyedb-0.39.0.dist-info/RECORD +288 -0
- pyedb-0.37.0.dist-info/RECORD +0 -194
- /pyedb/dotnet/{edb_core → database}/__init__.py +0 -0
- /pyedb/dotnet/{application → database/cell}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/cell → database/cell/hierarchy}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/netlist_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/pin_pair_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/s_parameter_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/spice_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/primitive/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/cell/hierarchy → database/cell/terminal}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/cell/terminal → database/definition}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/definition → database/dotnet}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/dotnet → database/edb_data}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/design_options.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/edbvalue.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/layer_data.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/utilities.py +0 -0
- /pyedb/dotnet/{edb_core → database}/general.py +0 -0
- /pyedb/dotnet/{edb_core/edb_data → database/geometry}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/geometry/point_data.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/adaptive_frequency_data.py +0 -0
- /pyedb/dotnet/{edb_core/geometry → database/sim_setup_data/io}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/io/siwave.py +0 -0
- /pyedb/dotnet/{edb_core → database}/utilities/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/utilities/heatsink.py +0 -0
- /pyedb/{dotnet/edb_core/sim_setup_data/io → grpc/database/definition}/__init__.py +0 -0
- {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/LICENSE +0 -0
- {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,1116 @@
|
|
|
1
|
+
# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
|
|
23
|
+
import math
|
|
24
|
+
import re
|
|
25
|
+
|
|
26
|
+
from ansys.edb.core.database import ProductIdType as GrpcProductIdType
|
|
27
|
+
from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
|
|
28
|
+
from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
|
|
29
|
+
from ansys.edb.core.hierarchy.pin_group import PinGroup as GrpcPinGroup
|
|
30
|
+
from ansys.edb.core.primitive.primitive import PadstackInstance as GrpcPadstackInstance
|
|
31
|
+
from ansys.edb.core.terminal.terminals import PinGroupTerminal as GrpcPinGroupTerminal
|
|
32
|
+
from ansys.edb.core.utility.value import Value as GrpcValue
|
|
33
|
+
|
|
34
|
+
from pyedb.grpc.database.definition.padstack_def import PadstackDef
|
|
35
|
+
from pyedb.grpc.database.terminal.padstack_instance_terminal import (
|
|
36
|
+
PadstackInstanceTerminal,
|
|
37
|
+
)
|
|
38
|
+
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PadstackInstance(GrpcPadstackInstance):
|
|
42
|
+
"""Manages EDB functionalities for a padstack.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
:class:`PadstackInstance <pyedb.grpc.dataybase.primitive.PadstackInstance>`
|
|
47
|
+
PadstackInstance object.
|
|
48
|
+
|
|
49
|
+
Examples
|
|
50
|
+
--------
|
|
51
|
+
>>> from pyedb import Edb
|
|
52
|
+
>>> edb = Edb(myedb, edbversion="2021.2")
|
|
53
|
+
>>> edb_padstack_instance = edb.padstacks.instances[0]
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self, pedb, edb_instance):
|
|
57
|
+
super().__init__(edb_instance.msg)
|
|
58
|
+
self._edb_object = edb_instance
|
|
59
|
+
self._bounding_box = []
|
|
60
|
+
self._position = []
|
|
61
|
+
self._pdef = None
|
|
62
|
+
self._pedb = pedb
|
|
63
|
+
self._object_instance = None
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def definition(self):
|
|
67
|
+
"""Padstack definition.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
:class:`PadstackDef`<pyedb.grpc.database.definition.padstack_def.PadstackDef>`
|
|
72
|
+
"""
|
|
73
|
+
return PadstackDef(self._pedb, self.padstack_def)
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def padstack_definition(self):
|
|
77
|
+
"""Padstack definition name.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
str
|
|
82
|
+
Padstack definition name.
|
|
83
|
+
|
|
84
|
+
"""
|
|
85
|
+
return self.padstack_def.name
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def terminal(self):
|
|
89
|
+
"""PadstackInstanceTerminal.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
:class:`PadstackInstanceTerminal <pyedb.grpc.database.terminal.padstack_instance_terminal.
|
|
94
|
+
PadstackInstanceTerminal>`
|
|
95
|
+
PadstackInstanceTerminal object.
|
|
96
|
+
"""
|
|
97
|
+
from pyedb.grpc.database.terminal.padstack_instance_terminal import (
|
|
98
|
+
PadstackInstanceTerminal,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
term = PadstackInstanceTerminal(self._pedb, self._edb_object)
|
|
102
|
+
return term if not term.is_null else None
|
|
103
|
+
|
|
104
|
+
def create_terminal(self, name=None):
|
|
105
|
+
"""Create a padstack instance terminal.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
:class:`PadstackInstanceTerminal <pyedb.grpc.database.terminal.padstack_instance_terminal.
|
|
110
|
+
PadstackInstanceTerminal>`
|
|
111
|
+
PadstackInstanceTerminal object.
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
if not name:
|
|
115
|
+
name = self.name
|
|
116
|
+
term = PadstackInstanceTerminal.create(
|
|
117
|
+
layout=self.layout,
|
|
118
|
+
name=name,
|
|
119
|
+
padstack_instance=self,
|
|
120
|
+
layer=self.get_layer_range()[0],
|
|
121
|
+
net=self.net,
|
|
122
|
+
is_ref=False,
|
|
123
|
+
)
|
|
124
|
+
return PadstackInstanceTerminal(self._pedb, term)
|
|
125
|
+
|
|
126
|
+
def get_terminal(self, create_new_terminal=True):
|
|
127
|
+
"""Returns padstack instance terminal.
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
create_new_terminal : bool, optional
|
|
132
|
+
If terminal instance is not created,
|
|
133
|
+
and value is ``True``, a new PadstackInstanceTerminal is created.
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
:class:`PadstackInstanceTerminal <pyedb.grpc.database.terminal.padstack_instance_terminal.
|
|
138
|
+
PadstackInstanceTerminal>`
|
|
139
|
+
PadstackInstanceTerminal object.
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
inst_term = self.get_padstack_instance_terminal()
|
|
143
|
+
if inst_term.is_null and create_new_terminal:
|
|
144
|
+
inst_term = self.create_terminal()
|
|
145
|
+
return PadstackInstanceTerminal(self._pedb, inst_term)
|
|
146
|
+
|
|
147
|
+
def create_coax_port(self, name=None, radial_extent_factor=0):
|
|
148
|
+
"""Create a coax port.
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
name : str, optional.
|
|
153
|
+
Port name, the default is ``None``, in which case a name is automatically assigned.
|
|
154
|
+
radial_extent_factor : int, float, optional
|
|
155
|
+
Radial extent of coaxial port.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
:class:`Terminal <pyedb.grpc.database.terminal.terminal.Terminal>`
|
|
160
|
+
Port terminal.
|
|
161
|
+
"""
|
|
162
|
+
port = self.create_port(name)
|
|
163
|
+
port.radial_extent_factor = radial_extent_factor
|
|
164
|
+
return port
|
|
165
|
+
|
|
166
|
+
def create_port(self, name=None, reference=None, is_circuit_port=False):
|
|
167
|
+
"""Create a port on the padstack instance.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
name : str, optional
|
|
172
|
+
Name of the port. The default is ``None``, in which case a name is automatically assigned.
|
|
173
|
+
reference : reference net or pingroup optional
|
|
174
|
+
Negative terminal of the port.
|
|
175
|
+
is_circuit_port : bool, optional
|
|
176
|
+
Whether it is a circuit port.
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
-------
|
|
180
|
+
:class:`Terminal <pyedb.grpc.database.terminal.terminal.Terminal>`
|
|
181
|
+
Port terminal.
|
|
182
|
+
"""
|
|
183
|
+
if not reference:
|
|
184
|
+
return self.create_terminal(name)
|
|
185
|
+
else:
|
|
186
|
+
positive_terminal = self.create_terminal()
|
|
187
|
+
if positive_terminal.is_null:
|
|
188
|
+
self._pedb.logger(
|
|
189
|
+
f"Positive terminal on padsatck instance {self.name} is null. Make sure a terminal"
|
|
190
|
+
f"is not already defined."
|
|
191
|
+
)
|
|
192
|
+
negative_terminal = None
|
|
193
|
+
if isinstance(reference, list):
|
|
194
|
+
pg = GrpcPinGroup.create(self.layout, name=f"pingroup_{self.name}_ref", padstack_instances=reference)
|
|
195
|
+
negative_terminal = GrpcPinGroupTerminal.create(
|
|
196
|
+
layout=self.layout,
|
|
197
|
+
name=f"pingroup_term{self.name}_ref)",
|
|
198
|
+
pin_group=pg,
|
|
199
|
+
net=reference[0].net,
|
|
200
|
+
is_ref=True,
|
|
201
|
+
)
|
|
202
|
+
is_circuit_port = True
|
|
203
|
+
else:
|
|
204
|
+
if isinstance(reference, PadstackInstance):
|
|
205
|
+
negative_terminal = reference.create_terminal()
|
|
206
|
+
elif isinstance(reference, str):
|
|
207
|
+
if reference in self._pedb.padstacks.instances:
|
|
208
|
+
reference = self._pedb.padstacks.instances[reference]
|
|
209
|
+
else:
|
|
210
|
+
pin_groups = [pg for pg in self._pedb.active_layout.pin_groups if pg.name == reference]
|
|
211
|
+
if pin_groups:
|
|
212
|
+
reference = pin_groups[0]
|
|
213
|
+
else:
|
|
214
|
+
self._pedb.logger.error(f"No reference found for {reference}")
|
|
215
|
+
return False
|
|
216
|
+
negative_terminal = reference.create_terminal()
|
|
217
|
+
if negative_terminal:
|
|
218
|
+
positive_terminal.reference_terminal = negative_terminal
|
|
219
|
+
else:
|
|
220
|
+
self._pedb.logger.error("No reference terminal created")
|
|
221
|
+
return False
|
|
222
|
+
positive_terminal.is_circuit_port = is_circuit_port
|
|
223
|
+
negative_terminal.is_circuit_port = is_circuit_port
|
|
224
|
+
return positive_terminal
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def _em_properties(self):
|
|
228
|
+
"""Get EM properties."""
|
|
229
|
+
from ansys.edb.core.database import ProductIdType
|
|
230
|
+
|
|
231
|
+
default = (
|
|
232
|
+
r"$begin 'EM properties'\n"
|
|
233
|
+
r"\tType('Mesh')\n"
|
|
234
|
+
r"\tDataId='EM properties1'\n"
|
|
235
|
+
r"\t$begin 'Properties'\n"
|
|
236
|
+
r"\t\tGeneral=''\n"
|
|
237
|
+
r"\t\tModeled='true'\n"
|
|
238
|
+
r"\t\tUnion='true'\n"
|
|
239
|
+
r"\t\t'Use Precedence'='false'\n"
|
|
240
|
+
r"\t\t'Precedence Value'='1'\n"
|
|
241
|
+
r"\t\tPlanarEM=''\n"
|
|
242
|
+
r"\t\tRefined='true'\n"
|
|
243
|
+
r"\t\tRefineFactor='1'\n"
|
|
244
|
+
r"\t\tNoEdgeMesh='false'\n"
|
|
245
|
+
r"\t\tHFSS=''\n"
|
|
246
|
+
r"\t\t'Solve Inside'='false'\n"
|
|
247
|
+
r"\t\tSIwave=''\n"
|
|
248
|
+
r"\t\t'DCIR Equipotential Region'='false'\n"
|
|
249
|
+
r"\t$end 'Properties'\n"
|
|
250
|
+
r"$end 'EM properties'\n"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
p = self.get_product_property(ProductIdType.DESIGNER, 18)
|
|
254
|
+
if p:
|
|
255
|
+
return p
|
|
256
|
+
else:
|
|
257
|
+
return default
|
|
258
|
+
|
|
259
|
+
@_em_properties.setter
|
|
260
|
+
def _em_properties(self, em_prop):
|
|
261
|
+
"""Set EM properties"""
|
|
262
|
+
pid = self._pedb.edb_api.ProductId.Designer
|
|
263
|
+
self.set_product_property(pid, 18, em_prop)
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def dcir_equipotential_region(self):
|
|
267
|
+
"""Check whether dcir equipotential region is enabled.
|
|
268
|
+
|
|
269
|
+
Returns
|
|
270
|
+
-------
|
|
271
|
+
bool
|
|
272
|
+
|
|
273
|
+
"""
|
|
274
|
+
pattern = r"'DCIR Equipotential Region'='([^']+)'"
|
|
275
|
+
em_pp = self._em_properties
|
|
276
|
+
result = re.search(pattern, em_pp).group(1)
|
|
277
|
+
if result == "true":
|
|
278
|
+
return True
|
|
279
|
+
else:
|
|
280
|
+
return False
|
|
281
|
+
|
|
282
|
+
@dcir_equipotential_region.setter
|
|
283
|
+
def dcir_equipotential_region(self, value):
|
|
284
|
+
"""Set dcir equipotential region."""
|
|
285
|
+
pp = r"'DCIR Equipotential Region'='true'" if value else r"'DCIR Equipotential Region'='false'"
|
|
286
|
+
em_pp = self._em_properties
|
|
287
|
+
pattern = r"'DCIR Equipotential Region'='([^']+)'"
|
|
288
|
+
new_em_pp = re.sub(pattern, pp, em_pp)
|
|
289
|
+
self._em_properties = new_em_pp
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def object_instance(self):
|
|
293
|
+
"""Layout object instance.
|
|
294
|
+
|
|
295
|
+
Returns
|
|
296
|
+
-------
|
|
297
|
+
:class:`LayoutObjInstance <ansys.edb.core.layout_instance.layout_obj_instance import.LayoutObjInstance>`
|
|
298
|
+
|
|
299
|
+
"""
|
|
300
|
+
if not self._object_instance:
|
|
301
|
+
self._object_instance = self.layout.layout_instance.get_layout_obj_instance_in_context(self, None)
|
|
302
|
+
return self._object_instance
|
|
303
|
+
|
|
304
|
+
@property
|
|
305
|
+
def bounding_box(self):
|
|
306
|
+
"""Padstack instance bounding box.
|
|
307
|
+
Because this method is slow, the bounding box is stored in a variable and reused.
|
|
308
|
+
|
|
309
|
+
Returns
|
|
310
|
+
-------
|
|
311
|
+
list of float
|
|
312
|
+
"""
|
|
313
|
+
# TODO check to implement in grpc
|
|
314
|
+
if self._bounding_box:
|
|
315
|
+
return self._bounding_box
|
|
316
|
+
return self._bounding_box
|
|
317
|
+
|
|
318
|
+
def in_polygon(self, polygon_data, include_partial=True):
|
|
319
|
+
"""Check if padstack Instance is in given polygon data.
|
|
320
|
+
|
|
321
|
+
Parameters
|
|
322
|
+
----------
|
|
323
|
+
polygon_data : PolygonData Object
|
|
324
|
+
include_partial : bool, optional
|
|
325
|
+
Whether to include partial intersecting instances. The default is ``True``.
|
|
326
|
+
simple_check : bool, optional
|
|
327
|
+
Whether to perform a single check based on the padstack center or check the padstack bounding box.
|
|
328
|
+
|
|
329
|
+
Returns
|
|
330
|
+
-------
|
|
331
|
+
bool
|
|
332
|
+
``True`` when successful, ``False`` when failed.
|
|
333
|
+
"""
|
|
334
|
+
int_val = 1 if polygon_data.point_in_polygon(GrpcPointData(self.position)) else 0
|
|
335
|
+
if int_val == 0:
|
|
336
|
+
return False
|
|
337
|
+
else:
|
|
338
|
+
int_val = polygon_data.intersection_type(GrpcPolygonData(self.bounding_box))
|
|
339
|
+
# Intersection type:
|
|
340
|
+
# 0 = objects do not intersect
|
|
341
|
+
# 1 = this object fully inside other (no common contour points)
|
|
342
|
+
# 2 = other object fully inside this
|
|
343
|
+
# 3 = common contour points 4 = undefined intersection
|
|
344
|
+
if int_val == 0:
|
|
345
|
+
return False
|
|
346
|
+
elif include_partial:
|
|
347
|
+
return True
|
|
348
|
+
elif int_val < 3:
|
|
349
|
+
return True
|
|
350
|
+
else:
|
|
351
|
+
return False
|
|
352
|
+
|
|
353
|
+
@property
|
|
354
|
+
def start_layer(self):
|
|
355
|
+
"""Starting layer.
|
|
356
|
+
|
|
357
|
+
Returns
|
|
358
|
+
-------
|
|
359
|
+
str
|
|
360
|
+
Name of the starting layer.
|
|
361
|
+
"""
|
|
362
|
+
return self.get_layer_range()[0].name
|
|
363
|
+
|
|
364
|
+
@start_layer.setter
|
|
365
|
+
def start_layer(self, layer_name):
|
|
366
|
+
stop_layer = self._pedb.stackup.signal_layers[self.stop_layer]
|
|
367
|
+
start_layer = self._pedb.stackup.signal_layers[layer_name]
|
|
368
|
+
self.set_layer_range(start_layer, stop_layer)
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def stop_layer(self):
|
|
372
|
+
"""Stopping layer.
|
|
373
|
+
|
|
374
|
+
Returns
|
|
375
|
+
-------
|
|
376
|
+
str
|
|
377
|
+
Name of the stopping layer.
|
|
378
|
+
"""
|
|
379
|
+
return self.get_layer_range()[-1].name
|
|
380
|
+
|
|
381
|
+
@stop_layer.setter
|
|
382
|
+
def stop_layer(self, layer_name):
|
|
383
|
+
start_layer = self._pedb.stackup.signal_layers[self.start_layer]
|
|
384
|
+
stop_layer = self._pedb.stackup.signal_layers[layer_name]
|
|
385
|
+
self.set_layer_range(start_layer, stop_layer)
|
|
386
|
+
|
|
387
|
+
@property
|
|
388
|
+
def layer_range_names(self):
|
|
389
|
+
"""List of all layers to which the padstack instance belongs.
|
|
390
|
+
|
|
391
|
+
Returns
|
|
392
|
+
-------
|
|
393
|
+
List[str]
|
|
394
|
+
List of layer names.
|
|
395
|
+
|
|
396
|
+
"""
|
|
397
|
+
start_layer, stop_layer = self.get_layer_range()
|
|
398
|
+
started = False
|
|
399
|
+
layer_list = []
|
|
400
|
+
start_layer_name = start_layer.name
|
|
401
|
+
stop_layer_name = stop_layer.name
|
|
402
|
+
for layer_name in list(self._pedb.stackup.layers.keys()):
|
|
403
|
+
if started:
|
|
404
|
+
layer_list.append(layer_name)
|
|
405
|
+
if layer_name == stop_layer_name or layer_name == start_layer_name:
|
|
406
|
+
break
|
|
407
|
+
elif layer_name == start_layer_name:
|
|
408
|
+
started = True
|
|
409
|
+
layer_list.append(layer_name)
|
|
410
|
+
if layer_name == stop_layer_name:
|
|
411
|
+
break
|
|
412
|
+
elif layer_name == stop_layer_name:
|
|
413
|
+
started = True
|
|
414
|
+
layer_list.append(layer_name)
|
|
415
|
+
if layer_name == start_layer_name:
|
|
416
|
+
break
|
|
417
|
+
return layer_list
|
|
418
|
+
|
|
419
|
+
@property
|
|
420
|
+
def net_name(self):
|
|
421
|
+
"""Net name.
|
|
422
|
+
|
|
423
|
+
Returns
|
|
424
|
+
-------
|
|
425
|
+
str
|
|
426
|
+
Name of the net.
|
|
427
|
+
"""
|
|
428
|
+
if self.is_null:
|
|
429
|
+
return ""
|
|
430
|
+
elif self.net.is_null:
|
|
431
|
+
return ""
|
|
432
|
+
else:
|
|
433
|
+
return self.net.name
|
|
434
|
+
|
|
435
|
+
@net_name.setter
|
|
436
|
+
def net_name(self, val):
|
|
437
|
+
if not self.is_null and not self.net.is_null:
|
|
438
|
+
self.net = self._pedb.nets.nets[val]
|
|
439
|
+
|
|
440
|
+
@property
|
|
441
|
+
def layout_object_instance(self):
|
|
442
|
+
"""Layout object instance.
|
|
443
|
+
|
|
444
|
+
Returns
|
|
445
|
+
-------
|
|
446
|
+
:class:`LayoutObjInstance <ansys.edb.core.layout_instance.layout_obj_instance.LayoutObjInstance>`
|
|
447
|
+
|
|
448
|
+
"""
|
|
449
|
+
obj_inst = [
|
|
450
|
+
obj
|
|
451
|
+
for obj in self._pedb.layout_instance.query_layout_obj_instances(
|
|
452
|
+
spatial_filter=GrpcPointData(self.position)
|
|
453
|
+
)
|
|
454
|
+
if obj.layout_obj.id == self.id
|
|
455
|
+
]
|
|
456
|
+
return obj_inst[0] if obj_inst else None
|
|
457
|
+
|
|
458
|
+
@property
|
|
459
|
+
def is_pin(self):
|
|
460
|
+
"""Determines whether this padstack instance is a layout pin.
|
|
461
|
+
|
|
462
|
+
Returns
|
|
463
|
+
-------
|
|
464
|
+
bool
|
|
465
|
+
True if this padstack type is a layout pin, False otherwise.
|
|
466
|
+
"""
|
|
467
|
+
return self.is_layout_pin
|
|
468
|
+
|
|
469
|
+
@is_pin.setter
|
|
470
|
+
def is_pin(self, value):
|
|
471
|
+
self.is_layout_pin = value
|
|
472
|
+
|
|
473
|
+
@property
|
|
474
|
+
def component(self):
|
|
475
|
+
"""Component.
|
|
476
|
+
|
|
477
|
+
Returns
|
|
478
|
+
-------
|
|
479
|
+
:class:`Component <pyedb.grpc.database.hierarchy.component.Component>`
|
|
480
|
+
|
|
481
|
+
"""
|
|
482
|
+
from pyedb.grpc.database.hierarchy.component import Component
|
|
483
|
+
|
|
484
|
+
comp = Component(self._pedb, super().component)
|
|
485
|
+
return comp if not comp.is_null else False
|
|
486
|
+
|
|
487
|
+
@property
|
|
488
|
+
def position(self):
|
|
489
|
+
"""Padstack instance position.
|
|
490
|
+
|
|
491
|
+
Returns
|
|
492
|
+
-------
|
|
493
|
+
list
|
|
494
|
+
List of ``[x, y]`` coordinates for the padstack instance position.
|
|
495
|
+
"""
|
|
496
|
+
position = self.get_position_and_rotation()
|
|
497
|
+
if self.component:
|
|
498
|
+
out2 = self.component.transform.transform_point(GrpcPointData(position[:2]))
|
|
499
|
+
self._position = [round(out2[0].value, 6), round(out2[1].value, 6)]
|
|
500
|
+
else:
|
|
501
|
+
self._position = [round(pt.value, 6) for pt in position[:2]]
|
|
502
|
+
return self._position
|
|
503
|
+
|
|
504
|
+
@position.setter
|
|
505
|
+
def position(self, value):
|
|
506
|
+
pos = []
|
|
507
|
+
for v in value:
|
|
508
|
+
if isinstance(v, (float, int, str)):
|
|
509
|
+
pos.append(GrpcValue(v, self._pedb.active_cell))
|
|
510
|
+
else:
|
|
511
|
+
pos.append(v)
|
|
512
|
+
point_data = GrpcPointData(pos[0], pos[1])
|
|
513
|
+
self.set_position_and_rotation(
|
|
514
|
+
x=point_data.x, y=point_data.y, rotation=GrpcValue(self.rotation, self._pedb.active_cell)
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
@property
|
|
518
|
+
def rotation(self):
|
|
519
|
+
"""Padstack instance rotation.
|
|
520
|
+
|
|
521
|
+
Returns
|
|
522
|
+
-------
|
|
523
|
+
float
|
|
524
|
+
Rotatation value for the padstack instance.
|
|
525
|
+
"""
|
|
526
|
+
return self.get_position_and_rotation()[-1].value
|
|
527
|
+
|
|
528
|
+
@property
|
|
529
|
+
def name(self):
|
|
530
|
+
"""Padstack Instance Name.
|
|
531
|
+
|
|
532
|
+
Returns
|
|
533
|
+
-------
|
|
534
|
+
str
|
|
535
|
+
If it is a pin, the syntax will be like in AEDT ComponentName-PinName.
|
|
536
|
+
|
|
537
|
+
"""
|
|
538
|
+
if not super().name:
|
|
539
|
+
return self.aedt_name
|
|
540
|
+
else:
|
|
541
|
+
return super().name
|
|
542
|
+
|
|
543
|
+
@name.setter
|
|
544
|
+
def name(self, value):
|
|
545
|
+
super(PadstackInstance, self.__class__).name.__set__(self, value)
|
|
546
|
+
self.set_product_property(GrpcProductIdType.DESIGNER, 11, value)
|
|
547
|
+
|
|
548
|
+
@property
|
|
549
|
+
def backdrill_type(self):
|
|
550
|
+
"""Backdrill type.
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
Returns
|
|
554
|
+
-------
|
|
555
|
+
str
|
|
556
|
+
Backdrill type.
|
|
557
|
+
|
|
558
|
+
"""
|
|
559
|
+
return self.get_backdrill_type()
|
|
560
|
+
|
|
561
|
+
@property
|
|
562
|
+
def backdrill_top(self):
|
|
563
|
+
if self.get_back_drill_type(False).value == 0:
|
|
564
|
+
return False
|
|
565
|
+
else:
|
|
566
|
+
try:
|
|
567
|
+
if self.get_back_drill_by_layer(from_bottom=False):
|
|
568
|
+
return True
|
|
569
|
+
except:
|
|
570
|
+
return False
|
|
571
|
+
|
|
572
|
+
@property
|
|
573
|
+
def backdrill_bottom(self):
|
|
574
|
+
"""Check is backdrill is starting at bottom.
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
Returns
|
|
578
|
+
-------
|
|
579
|
+
bool
|
|
580
|
+
|
|
581
|
+
"""
|
|
582
|
+
if self.get_back_drill_type(True).value == 0:
|
|
583
|
+
return False
|
|
584
|
+
else:
|
|
585
|
+
try:
|
|
586
|
+
if self.get_back_drill_by_layer(True):
|
|
587
|
+
return True
|
|
588
|
+
except:
|
|
589
|
+
return False
|
|
590
|
+
|
|
591
|
+
@property
|
|
592
|
+
def metal_volume(self):
|
|
593
|
+
"""Metal volume of the via hole instance in cubic units (m3). Metal plating ratio is accounted.
|
|
594
|
+
|
|
595
|
+
Returns
|
|
596
|
+
-------
|
|
597
|
+
float
|
|
598
|
+
Metal volume of the via hole instance.
|
|
599
|
+
|
|
600
|
+
"""
|
|
601
|
+
volume = 0
|
|
602
|
+
if not self.start_layer == self.stop_layer:
|
|
603
|
+
start_layer = self.start_layer
|
|
604
|
+
stop_layer = self.stop_layer
|
|
605
|
+
via_length = (
|
|
606
|
+
self._pedb.stackup.signal_layers[start_layer].upper_elevation
|
|
607
|
+
- self._pedb.stackup.signal_layers[stop_layer].lower_elevation
|
|
608
|
+
)
|
|
609
|
+
if self.get_backdrill_type == "layer_drill":
|
|
610
|
+
layer, _, _ = self.get_back_drill_by_layer()
|
|
611
|
+
start_layer = self._pedb.stackup.signal_layers[0]
|
|
612
|
+
stop_layer = self._pedb.stackup.signal_layers[layer.name]
|
|
613
|
+
via_length = (
|
|
614
|
+
self._pedb.stackup.signal_layers[start_layer].upper_elevation
|
|
615
|
+
- self._pedb.stackup.signal_layers[stop_layer].lower_elevation
|
|
616
|
+
)
|
|
617
|
+
elif self.get_backdrill_type == "depth_drill":
|
|
618
|
+
drill_depth, _ = self.get_back_drill_by_depth()
|
|
619
|
+
start_layer = self._pedb.stackup.signal_layers[0]
|
|
620
|
+
via_length = self._pedb.stackup.signal_layers[start_layer].upper_elevation - drill_depth
|
|
621
|
+
padstack_def = self._pedb.padstacks.definitions[self.padstack_def.name]
|
|
622
|
+
hole_diameter = padstack_def.hole_diameter
|
|
623
|
+
if hole_diameter:
|
|
624
|
+
hole_finished_size = padstack_def.hole_finished_size
|
|
625
|
+
volume = (math.pi * (hole_diameter / 2) ** 2 - math.pi * (hole_finished_size / 2) ** 2) * via_length
|
|
626
|
+
return volume
|
|
627
|
+
|
|
628
|
+
@property
|
|
629
|
+
def component_pin(self):
|
|
630
|
+
"""Component pin.
|
|
631
|
+
|
|
632
|
+
Returns
|
|
633
|
+
-------
|
|
634
|
+
str
|
|
635
|
+
Component pin name.
|
|
636
|
+
|
|
637
|
+
"""
|
|
638
|
+
return self.name
|
|
639
|
+
|
|
640
|
+
@property
|
|
641
|
+
def aedt_name(self):
|
|
642
|
+
"""Retrieve the pin name that is shown in AEDT.
|
|
643
|
+
|
|
644
|
+
.. note::
|
|
645
|
+
To obtain the EDB core pin name, use `pin.name`.
|
|
646
|
+
|
|
647
|
+
Returns
|
|
648
|
+
-------
|
|
649
|
+
str
|
|
650
|
+
Name of the pin in AEDT.
|
|
651
|
+
|
|
652
|
+
Examples
|
|
653
|
+
--------
|
|
654
|
+
|
|
655
|
+
>>> from pyedb import Edb
|
|
656
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
657
|
+
>>> edbapp.padstacks.instances[111].get_aedt_pin_name()
|
|
658
|
+
|
|
659
|
+
"""
|
|
660
|
+
|
|
661
|
+
name = self.get_product_property(GrpcProductIdType.DESIGNER, 11)
|
|
662
|
+
return str(name).strip("'")
|
|
663
|
+
|
|
664
|
+
def get_backdrill_type(self, from_bottom=True):
|
|
665
|
+
"""Return backdrill type
|
|
666
|
+
Parameters
|
|
667
|
+
----------
|
|
668
|
+
from_bottom : bool, optional
|
|
669
|
+
default value is `True.`
|
|
670
|
+
|
|
671
|
+
Return
|
|
672
|
+
------
|
|
673
|
+
str
|
|
674
|
+
Back drill type, `"layer_drill"`,`"depth_drill"`, `"no_drill"`.
|
|
675
|
+
|
|
676
|
+
"""
|
|
677
|
+
return super().get_back_drill_type(from_bottom).name.lower()
|
|
678
|
+
|
|
679
|
+
def get_back_drill_by_layer(self, from_bottom=True):
|
|
680
|
+
"""Get backdrill by layer.
|
|
681
|
+
|
|
682
|
+
Parameters
|
|
683
|
+
----------
|
|
684
|
+
from_bottom : bool, optional.
|
|
685
|
+
Default value is `True`.
|
|
686
|
+
|
|
687
|
+
Return
|
|
688
|
+
------
|
|
689
|
+
tuple (layer, offset, diameter) (str, [float, float], float).
|
|
690
|
+
|
|
691
|
+
"""
|
|
692
|
+
back_drill = super().get_back_drill_by_layer(from_bottom)
|
|
693
|
+
layer = back_drill[0].name
|
|
694
|
+
offset = round(back_drill[1].value, 9)
|
|
695
|
+
diameter = round(back_drill[2].value, 9)
|
|
696
|
+
return layer, offset, diameter
|
|
697
|
+
|
|
698
|
+
def get_back_drill_by_depth(self, from_bottom=True):
|
|
699
|
+
"""Get back drill by depth parameters
|
|
700
|
+
Parameters
|
|
701
|
+
----------
|
|
702
|
+
from_bottom : bool, optional
|
|
703
|
+
Default value is `True`.
|
|
704
|
+
|
|
705
|
+
Return
|
|
706
|
+
------
|
|
707
|
+
tuple (drill_depth, drill_diameter) (float, float)
|
|
708
|
+
"""
|
|
709
|
+
back_drill = super().get_back_drill_by_depth(from_bottom)
|
|
710
|
+
drill_depth = back_drill[0].value
|
|
711
|
+
drill_diameter = back_drill[1].value
|
|
712
|
+
return drill_depth, drill_diameter
|
|
713
|
+
|
|
714
|
+
def set_back_drill_by_depth(self, drill_depth, diameter, from_bottom=True):
|
|
715
|
+
"""Set back drill by depth.
|
|
716
|
+
|
|
717
|
+
Parameters
|
|
718
|
+
----------
|
|
719
|
+
drill_depth : str, float
|
|
720
|
+
drill depth value
|
|
721
|
+
diameter : str, float
|
|
722
|
+
drill diameter
|
|
723
|
+
from_bottom : bool, optional
|
|
724
|
+
Default value is `True`.
|
|
725
|
+
"""
|
|
726
|
+
super().set_back_drill_by_depth(
|
|
727
|
+
drill_depth=GrpcValue(drill_depth), diameter=GrpcValue(diameter), from_bottom=from_bottom
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
def set_back_drill_by_layer(self, drill_to_layer, offset, diameter, from_bottom=True):
|
|
731
|
+
"""Set back drill layer.
|
|
732
|
+
|
|
733
|
+
Parameters
|
|
734
|
+
----------
|
|
735
|
+
drill_to_layer : str, Layer
|
|
736
|
+
Layer to drill to.
|
|
737
|
+
offset : str, float
|
|
738
|
+
Offset value
|
|
739
|
+
diameter : str, float
|
|
740
|
+
Drill diameter
|
|
741
|
+
from_bottom : bool, optional
|
|
742
|
+
Default value is `True`
|
|
743
|
+
"""
|
|
744
|
+
if isinstance(drill_to_layer, str):
|
|
745
|
+
drill_to_layer = self._pedb.stackup.layers[drill_to_layer]
|
|
746
|
+
super().set_back_drill_by_layer(
|
|
747
|
+
drill_to_layer=drill_to_layer,
|
|
748
|
+
offset=GrpcValue(offset),
|
|
749
|
+
diameter=GrpcValue(diameter),
|
|
750
|
+
from_bottom=from_bottom,
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
def parametrize_position(self, prefix=None):
|
|
754
|
+
"""Parametrize the instance position.
|
|
755
|
+
|
|
756
|
+
Parameters
|
|
757
|
+
----------
|
|
758
|
+
prefix : str, optional
|
|
759
|
+
Prefix for the variable name. Default is ``None``.
|
|
760
|
+
Example `"MyVariableName"` will create 2 Project variables $MyVariableNamesX and $MyVariableNamesY.
|
|
761
|
+
|
|
762
|
+
Returns
|
|
763
|
+
-------
|
|
764
|
+
List
|
|
765
|
+
List of variables created.
|
|
766
|
+
"""
|
|
767
|
+
p = self.position
|
|
768
|
+
if not prefix:
|
|
769
|
+
var_name = "${}_pos".format(self.name)
|
|
770
|
+
else:
|
|
771
|
+
var_name = "${}".format(prefix)
|
|
772
|
+
self._pedb.add_project_variable(var_name + "X", p[0])
|
|
773
|
+
self._pedb.add_project_variable(var_name + "Y", p[1])
|
|
774
|
+
self.position = [var_name + "X", var_name + "Y"]
|
|
775
|
+
return [var_name + "X", var_name + "Y"]
|
|
776
|
+
|
|
777
|
+
def in_voids(self, net_name=None, layer_name=None):
|
|
778
|
+
"""Check if this padstack instance is in any void.
|
|
779
|
+
|
|
780
|
+
Parameters
|
|
781
|
+
----------
|
|
782
|
+
net_name : str
|
|
783
|
+
Net name of the voids to be checked. Default is ``None``.
|
|
784
|
+
layer_name : str
|
|
785
|
+
Layer name of the voids to be checked. Default is ``None``.
|
|
786
|
+
|
|
787
|
+
Returns
|
|
788
|
+
-------
|
|
789
|
+
List[:class:`PadstackInstance <pyedb.grpc.database.primitive.padstack_instance.PadstackInstance>`]
|
|
790
|
+
List of the voids that include this padstack instance.
|
|
791
|
+
"""
|
|
792
|
+
x_pos = GrpcValue(self.position[0])
|
|
793
|
+
y_pos = GrpcValue(self.position[1])
|
|
794
|
+
point_data = GrpcPointData([x_pos, y_pos])
|
|
795
|
+
|
|
796
|
+
voids = []
|
|
797
|
+
for prim in self._pedb.modeler.get_primitives(net_name, layer_name, is_void=True):
|
|
798
|
+
if prim.polygon_data.point_in_polygon(point_data):
|
|
799
|
+
voids.append(prim)
|
|
800
|
+
return voids
|
|
801
|
+
|
|
802
|
+
@property
|
|
803
|
+
def pingroups(self):
|
|
804
|
+
"""Pin groups that the pin belongs to.
|
|
805
|
+
|
|
806
|
+
Returns
|
|
807
|
+
-------
|
|
808
|
+
List[:class:`PinGroup <ansys.edb.core.hierarchy.pin_group>`]
|
|
809
|
+
List of pin groups that the pin belongs to.
|
|
810
|
+
"""
|
|
811
|
+
return self.pin_groups
|
|
812
|
+
|
|
813
|
+
@property
|
|
814
|
+
def placement_layer(self):
|
|
815
|
+
"""Placement layer name.
|
|
816
|
+
|
|
817
|
+
Returns
|
|
818
|
+
-------
|
|
819
|
+
str
|
|
820
|
+
Name of the placement layer.
|
|
821
|
+
"""
|
|
822
|
+
return self.component.placement_layer
|
|
823
|
+
|
|
824
|
+
@property
|
|
825
|
+
def layer(self):
|
|
826
|
+
"""Placement layer object.
|
|
827
|
+
|
|
828
|
+
Returns
|
|
829
|
+
-------
|
|
830
|
+
:class:`pyedb.grpc.database.layers.stackup_layer.StackupLayer`
|
|
831
|
+
Placement layer.
|
|
832
|
+
"""
|
|
833
|
+
return self.component.layer
|
|
834
|
+
|
|
835
|
+
@property
|
|
836
|
+
def lower_elevation(self):
|
|
837
|
+
"""Lower elevation of the placement layer.
|
|
838
|
+
|
|
839
|
+
Returns
|
|
840
|
+
-------
|
|
841
|
+
float
|
|
842
|
+
Lower elavation of the placement layer.
|
|
843
|
+
"""
|
|
844
|
+
return self._pedb.stackup.layers[self.component.placement_layer].lower_elevation
|
|
845
|
+
|
|
846
|
+
@property
|
|
847
|
+
def upper_elevation(self):
|
|
848
|
+
"""Upper elevation of the placement layer.
|
|
849
|
+
|
|
850
|
+
Returns
|
|
851
|
+
-------
|
|
852
|
+
float
|
|
853
|
+
Upper elevation of the placement layer.
|
|
854
|
+
"""
|
|
855
|
+
return self._pedb.stackup.layers[self.component.placement_layer].upper_elevation
|
|
856
|
+
|
|
857
|
+
@property
|
|
858
|
+
def top_bottom_association(self):
|
|
859
|
+
"""Top/bottom association of the placement layer.
|
|
860
|
+
|
|
861
|
+
Returns
|
|
862
|
+
-------
|
|
863
|
+
int
|
|
864
|
+
Top/bottom association of the placement layer.
|
|
865
|
+
|
|
866
|
+
* 0 Top associated.
|
|
867
|
+
* 1 No association.
|
|
868
|
+
* 2 Bottom associated.
|
|
869
|
+
* 4 Number of top/bottom association type.
|
|
870
|
+
* -1 Undefined.
|
|
871
|
+
"""
|
|
872
|
+
return self._pedb.stackup.layers[self.component.placement_layer].top_bottom_association.value
|
|
873
|
+
|
|
874
|
+
def create_rectangle_in_pad(self, layer_name, return_points=False, partition_max_order=16):
|
|
875
|
+
"""Create a rectangle inscribed inside a padstack instance pad.
|
|
876
|
+
|
|
877
|
+
The rectangle is fully inscribed in the pad and has the maximum area.
|
|
878
|
+
It is necessary to specify the layer on which the rectangle will be created.
|
|
879
|
+
|
|
880
|
+
Parameters
|
|
881
|
+
----------
|
|
882
|
+
layer_name : str
|
|
883
|
+
Name of the layer on which to create the polygon.
|
|
884
|
+
return_points : bool, optional
|
|
885
|
+
If `True` does not create the rectangle and just returns a list containing the rectangle vertices.
|
|
886
|
+
Default is `False`.
|
|
887
|
+
partition_max_order : float, optional
|
|
888
|
+
Order of the lattice partition used to find the quasi-lattice polygon that approximates ``polygon``.
|
|
889
|
+
Default is ``16``.
|
|
890
|
+
|
|
891
|
+
Returns
|
|
892
|
+
-------
|
|
893
|
+
bool, List, :class:`Primitive <pyedb.grpc.database.primitive.primitive.Primitive>`
|
|
894
|
+
Polygon when successful, ``False`` when failed, list of list if `return_points=True`.
|
|
895
|
+
|
|
896
|
+
Examples
|
|
897
|
+
--------
|
|
898
|
+
>>> from pyedb import Edb
|
|
899
|
+
>>> edbapp = Edb("myaedbfolder", edbversion="2021.2")
|
|
900
|
+
>>> edb_layout = edbapp.modeler
|
|
901
|
+
>>> list_of_padstack_instances = list(edbapp.padstacks.instances.values())
|
|
902
|
+
>>> padstack_inst = list_of_padstack_instances[0]
|
|
903
|
+
>>> padstack_inst.create_rectangle_in_pad("TOP")
|
|
904
|
+
"""
|
|
905
|
+
# TODO check if still used anf fix if yes.
|
|
906
|
+
padstack_center = self.position
|
|
907
|
+
rotation = self.rotation # in radians
|
|
908
|
+
# padstack = self._pedb.padstacks.definitions[self.padstack_def.name]
|
|
909
|
+
try:
|
|
910
|
+
padstack_pad = PadstackDef(self._pedb, self.padstack_def).pad_by_layer[layer_name]
|
|
911
|
+
except KeyError: # pragma: no cover
|
|
912
|
+
try:
|
|
913
|
+
padstack_pad = PadstackDef(self._pedb, self.padstack_def).pad_by_layer[
|
|
914
|
+
PadstackDef(self._pedb, self.padstack_def).start_layer
|
|
915
|
+
]
|
|
916
|
+
except KeyError: # pragma: no cover
|
|
917
|
+
return False
|
|
918
|
+
|
|
919
|
+
try:
|
|
920
|
+
pad_shape = padstack_pad.geometry_type
|
|
921
|
+
params = padstack_pad.parameters_values
|
|
922
|
+
polygon_data = padstack_pad.polygon_data
|
|
923
|
+
except:
|
|
924
|
+
self._pedb.logger.warning(f"No pad defined on padstack definition {self.padstack_def.name}")
|
|
925
|
+
return False
|
|
926
|
+
|
|
927
|
+
def _rotate(p):
|
|
928
|
+
x = p[0] * math.cos(rotation) - p[1] * math.sin(rotation)
|
|
929
|
+
y = p[0] * math.sin(rotation) + p[1] * math.cos(rotation)
|
|
930
|
+
return [x, y]
|
|
931
|
+
|
|
932
|
+
def _translate(p):
|
|
933
|
+
x = p[0] + padstack_center[0]
|
|
934
|
+
y = p[1] + padstack_center[1]
|
|
935
|
+
return [x, y]
|
|
936
|
+
|
|
937
|
+
rect = None
|
|
938
|
+
|
|
939
|
+
if pad_shape == 1:
|
|
940
|
+
# Circle
|
|
941
|
+
diameter = params[0]
|
|
942
|
+
r = diameter * 0.5
|
|
943
|
+
p1 = [r, 0.0]
|
|
944
|
+
p2 = [0.0, r]
|
|
945
|
+
p3 = [-r, 0.0]
|
|
946
|
+
p4 = [0.0, -r]
|
|
947
|
+
rect = [_translate(p1), _translate(p2), _translate(p3), _translate(p4)]
|
|
948
|
+
elif pad_shape == 2:
|
|
949
|
+
# Square
|
|
950
|
+
square_size = params[0]
|
|
951
|
+
s2 = square_size * 0.5
|
|
952
|
+
p1 = [s2, s2]
|
|
953
|
+
p2 = [-s2, s2]
|
|
954
|
+
p3 = [-s2, -s2]
|
|
955
|
+
p4 = [s2, -s2]
|
|
956
|
+
rect = [
|
|
957
|
+
_translate(_rotate(p1)),
|
|
958
|
+
_translate(_rotate(p2)),
|
|
959
|
+
_translate(_rotate(p3)),
|
|
960
|
+
_translate(_rotate(p4)),
|
|
961
|
+
]
|
|
962
|
+
elif pad_shape == 3:
|
|
963
|
+
# Rectangle
|
|
964
|
+
x_size = float(params[0])
|
|
965
|
+
y_size = float(params[1])
|
|
966
|
+
sx2 = x_size * 0.5
|
|
967
|
+
sy2 = y_size * 0.5
|
|
968
|
+
p1 = [sx2, sy2]
|
|
969
|
+
p2 = [-sx2, sy2]
|
|
970
|
+
p3 = [-sx2, -sy2]
|
|
971
|
+
p4 = [sx2, -sy2]
|
|
972
|
+
rect = [
|
|
973
|
+
_translate(_rotate(p1)),
|
|
974
|
+
_translate(_rotate(p2)),
|
|
975
|
+
_translate(_rotate(p3)),
|
|
976
|
+
_translate(_rotate(p4)),
|
|
977
|
+
]
|
|
978
|
+
elif pad_shape == 4:
|
|
979
|
+
# Oval
|
|
980
|
+
x_size = params[0]
|
|
981
|
+
y_size = params[1]
|
|
982
|
+
corner_radius = float(params[2])
|
|
983
|
+
if corner_radius >= min(x_size, y_size):
|
|
984
|
+
r = min(x_size, y_size)
|
|
985
|
+
else:
|
|
986
|
+
r = corner_radius
|
|
987
|
+
sx = x_size * 0.5 - r
|
|
988
|
+
sy = y_size * 0.5 - r
|
|
989
|
+
k = r / math.sqrt(2)
|
|
990
|
+
p1 = [sx + k, sy + k]
|
|
991
|
+
p2 = [-sx - k, sy + k]
|
|
992
|
+
p3 = [-sx - k, -sy - k]
|
|
993
|
+
p4 = [sx + k, -sy - k]
|
|
994
|
+
rect = [
|
|
995
|
+
_translate(_rotate(p1)),
|
|
996
|
+
_translate(_rotate(p2)),
|
|
997
|
+
_translate(_rotate(p3)),
|
|
998
|
+
_translate(_rotate(p4)),
|
|
999
|
+
]
|
|
1000
|
+
elif pad_shape == 5:
|
|
1001
|
+
# Bullet
|
|
1002
|
+
x_size = params[0]
|
|
1003
|
+
y_size = params[1]
|
|
1004
|
+
corner_radius = params[2]
|
|
1005
|
+
if corner_radius >= min(x_size, y_size):
|
|
1006
|
+
r = min(x_size, y_size)
|
|
1007
|
+
else:
|
|
1008
|
+
r = corner_radius
|
|
1009
|
+
sx = x_size * 0.5 - r
|
|
1010
|
+
sy = y_size * 0.5 - r
|
|
1011
|
+
k = r / math.sqrt(2)
|
|
1012
|
+
p1 = [sx + k, sy + k]
|
|
1013
|
+
p2 = [-x_size * 0.5, sy + k]
|
|
1014
|
+
p3 = [-x_size * 0.5, -sy - k]
|
|
1015
|
+
p4 = [sx + k, -sy - k]
|
|
1016
|
+
rect = [
|
|
1017
|
+
_translate(_rotate(p1)),
|
|
1018
|
+
_translate(_rotate(p2)),
|
|
1019
|
+
_translate(_rotate(p3)),
|
|
1020
|
+
_translate(_rotate(p4)),
|
|
1021
|
+
]
|
|
1022
|
+
elif pad_shape == 6:
|
|
1023
|
+
# N-Sided Polygon
|
|
1024
|
+
size = params[0]
|
|
1025
|
+
num_sides = params[1]
|
|
1026
|
+
ext_radius = size * 0.5
|
|
1027
|
+
apothem = ext_radius * math.cos(math.pi / num_sides)
|
|
1028
|
+
p1 = [apothem, 0.0]
|
|
1029
|
+
p2 = [0.0, apothem]
|
|
1030
|
+
p3 = [-apothem, 0.0]
|
|
1031
|
+
p4 = [0.0, -apothem]
|
|
1032
|
+
rect = [
|
|
1033
|
+
_translate(_rotate(p1)),
|
|
1034
|
+
_translate(_rotate(p2)),
|
|
1035
|
+
_translate(_rotate(p3)),
|
|
1036
|
+
_translate(_rotate(p4)),
|
|
1037
|
+
]
|
|
1038
|
+
elif pad_shape == 7 and polygon_data is not None:
|
|
1039
|
+
# Polygon
|
|
1040
|
+
points = []
|
|
1041
|
+
i = 0
|
|
1042
|
+
while i < len(polygon_data.points):
|
|
1043
|
+
point = polygon_data.points[i]
|
|
1044
|
+
i += 1
|
|
1045
|
+
if point.is_arc:
|
|
1046
|
+
continue
|
|
1047
|
+
else:
|
|
1048
|
+
points.append([point.x.value, point.y.value])
|
|
1049
|
+
xpoly, ypoly = zip(*points)
|
|
1050
|
+
polygon = [list(xpoly), list(ypoly)]
|
|
1051
|
+
rectangles = GeometryOperators.find_largest_rectangle_inside_polygon(
|
|
1052
|
+
polygon, partition_max_order=partition_max_order
|
|
1053
|
+
)
|
|
1054
|
+
rect = rectangles[0]
|
|
1055
|
+
for i in range(4):
|
|
1056
|
+
rect[i] = _translate(_rotate(rect[i]))
|
|
1057
|
+
|
|
1058
|
+
# if rect is None or len(rect) != 4:
|
|
1059
|
+
# return False
|
|
1060
|
+
rect = [GrpcPointData(pt) for pt in rect]
|
|
1061
|
+
path = GrpcPolygonData(rect)
|
|
1062
|
+
new_rect = []
|
|
1063
|
+
for point in path.points:
|
|
1064
|
+
if self.component:
|
|
1065
|
+
p_transf = self.component.transform.transform_point(point)
|
|
1066
|
+
new_rect.append([p_transf.x.value, p_transf.y.value])
|
|
1067
|
+
if return_points:
|
|
1068
|
+
return new_rect
|
|
1069
|
+
else:
|
|
1070
|
+
created_polygon = self._pedb.modeler.create_polygon(path, layer_name)
|
|
1071
|
+
return created_polygon
|
|
1072
|
+
|
|
1073
|
+
def get_reference_pins(self, reference_net="GND", search_radius=5e-3, max_limit=0, component_only=True):
|
|
1074
|
+
"""Search for reference pins using given criteria.
|
|
1075
|
+
|
|
1076
|
+
Parameters
|
|
1077
|
+
----------
|
|
1078
|
+
reference_net : str, optional
|
|
1079
|
+
Reference net. The default is ``"GND"``.
|
|
1080
|
+
search_radius : float, optional
|
|
1081
|
+
Search radius for finding padstack instances. The default is ``5e-3``.
|
|
1082
|
+
max_limit : int, optional
|
|
1083
|
+
Maximum limit for the padstack instances found. The default is ``0``, in which
|
|
1084
|
+
case no limit is applied. The maximum limit value occurs on the nearest
|
|
1085
|
+
reference pins from the positive one that is found.
|
|
1086
|
+
component_only : bool, optional
|
|
1087
|
+
Whether to limit the search to component padstack instances only. The
|
|
1088
|
+
default is ``True``. When ``False``, the search is extended to the entire layout.
|
|
1089
|
+
|
|
1090
|
+
Returns
|
|
1091
|
+
-------
|
|
1092
|
+
List[:class:`PadstackInstance <pyedb.grpc.database.primitive.padstack_instance.PadstackInstance>`]
|
|
1093
|
+
|
|
1094
|
+
Examples
|
|
1095
|
+
--------
|
|
1096
|
+
>>> edbapp = Edb("target_path")
|
|
1097
|
+
>>> pin = edbapp.components.instances["J5"].pins["19"]
|
|
1098
|
+
>>> reference_pins = pin.get_reference_pins(reference_net="GND", search_radius=5e-3, max_limit=0,
|
|
1099
|
+
>>> component_only=True)
|
|
1100
|
+
"""
|
|
1101
|
+
return self._pedb.padstacks.get_reference_pins(
|
|
1102
|
+
positive_pin=self,
|
|
1103
|
+
reference_net=reference_net,
|
|
1104
|
+
search_radius=search_radius,
|
|
1105
|
+
max_limit=max_limit,
|
|
1106
|
+
component_only=component_only,
|
|
1107
|
+
)
|
|
1108
|
+
|
|
1109
|
+
def get_connected_objects(self):
|
|
1110
|
+
"""Get connected objects.
|
|
1111
|
+
|
|
1112
|
+
Returns
|
|
1113
|
+
-------
|
|
1114
|
+
List[:class:`LayoutObjInstance <ansys.edb.core.layout_instance.layout_obj_instance.LayoutObjInstance>`]
|
|
1115
|
+
"""
|
|
1116
|
+
return self._pedb.get_connected_objects(self.object_instance)
|