pyedb 0.38.0__py3-none-any.whl → 0.39.1__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_components.py +1 -1
- pyedb/configuration/cfg_general.py +4 -2
- pyedb/configuration/cfg_modeler.py +1 -1
- pyedb/configuration/cfg_package_definition.py +1 -1
- pyedb/configuration/cfg_padstacks.py +1 -1
- pyedb/configuration/cfg_ports_sources.py +56 -23
- pyedb/configuration/configuration.py +18 -1
- 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 +17 -17
- 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 +16 -16
- 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 +15 -15
- 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 +3 -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 +1 -1
- 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 +3 -3
- pyedb/dotnet/{edb_core → database}/utilities/siwave_simulation_setup.py +6 -6
- pyedb/dotnet/edb.py +117 -112
- 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 +4152 -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 +53 -21
- pyedb/ipc2581/ipc2581.py +47 -49
- pyedb/modeler/geometry_operators.py +1 -1
- {pyedb-0.38.0.dist-info → pyedb-0.39.1.dist-info}/METADATA +5 -2
- pyedb-0.39.1.dist-info/RECORD +288 -0
- pyedb-0.38.0.dist-info/RECORD +0 -195
- /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.38.0.dist-info → pyedb-0.39.1.dist-info}/LICENSE +0 -0
- {pyedb-0.38.0.dist-info → pyedb-0.39.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,1468 @@
|
|
|
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
|
+
"""
|
|
24
|
+
This module contains these classes: `EdbLayout` and `Shape`.
|
|
25
|
+
"""
|
|
26
|
+
import math
|
|
27
|
+
|
|
28
|
+
from ansys.edb.core.geometry.arc_data import ArcData as GrpcArcData
|
|
29
|
+
from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
|
|
30
|
+
from ansys.edb.core.geometry.polygon_data import (
|
|
31
|
+
PolygonSenseType as GrpcPolygonSenseType,
|
|
32
|
+
)
|
|
33
|
+
from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
|
|
34
|
+
from ansys.edb.core.hierarchy.pin_group import PinGroup as GrpcPinGroup
|
|
35
|
+
from ansys.edb.core.inner.exceptions import InvalidArgumentException
|
|
36
|
+
from ansys.edb.core.primitive.primitive import (
|
|
37
|
+
RectangleRepresentationType as GrpcRectangleRepresentationType,
|
|
38
|
+
)
|
|
39
|
+
from ansys.edb.core.primitive.primitive import BondwireType as GrpcBondwireType
|
|
40
|
+
from ansys.edb.core.primitive.primitive import PathCornerType as GrpcPathCornerType
|
|
41
|
+
from ansys.edb.core.primitive.primitive import PathEndCapType as GrpcPathEndCapType
|
|
42
|
+
from ansys.edb.core.utility.value import Value as GrpcValue
|
|
43
|
+
|
|
44
|
+
from pyedb.grpc.database.primitive.bondwire import Bondwire
|
|
45
|
+
from pyedb.grpc.database.primitive.circle import Circle
|
|
46
|
+
from pyedb.grpc.database.primitive.path import Path
|
|
47
|
+
from pyedb.grpc.database.primitive.polygon import Polygon
|
|
48
|
+
from pyedb.grpc.database.primitive.primitive import Primitive
|
|
49
|
+
from pyedb.grpc.database.primitive.rectangle import Rectangle
|
|
50
|
+
from pyedb.grpc.database.utility.layout_statistics import LayoutStatistics
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Modeler(object):
|
|
54
|
+
"""Manages EDB methods for primitives management accessible from `Edb.modeler` property.
|
|
55
|
+
|
|
56
|
+
Examples
|
|
57
|
+
--------
|
|
58
|
+
>>> from pyedb import Edb
|
|
59
|
+
>>> edbapp = Edb("myaedbfolder", edbversion="2021.2")
|
|
60
|
+
>>> edb_layout = edbapp.modeler
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __getitem__(self, name):
|
|
64
|
+
"""Get a layout instance from the Edb project.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
name : str, int
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
:class:`pyedb.dotnet.database.cell.hierarchy.component.EDBComponent`
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
for i in self.primitives:
|
|
76
|
+
if (
|
|
77
|
+
(isinstance(name, str) and i.aedt_name == name)
|
|
78
|
+
or (isinstance(name, str) and i.aedt_name == name.replace("__", "_"))
|
|
79
|
+
or (isinstance(name, int) and i.id == name)
|
|
80
|
+
):
|
|
81
|
+
return i
|
|
82
|
+
self._pedb.logger.error("Primitive not found.")
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
def __init__(self, p_edb):
|
|
86
|
+
self._pedb = p_edb
|
|
87
|
+
self._primitives = []
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def _edb(self):
|
|
91
|
+
return self._pedb
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def _logger(self):
|
|
95
|
+
"""Logger."""
|
|
96
|
+
return self._pedb.logger
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def _active_layout(self):
|
|
100
|
+
return self._pedb.active_layout
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def _layout(self):
|
|
104
|
+
return self._pedb.layout
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def _cell(self):
|
|
108
|
+
return self._pedb.active_cell
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def db(self):
|
|
112
|
+
"""Db object."""
|
|
113
|
+
return self._pedb.active_db
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def layers(self):
|
|
117
|
+
"""Dictionary of layers.
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
dict
|
|
122
|
+
Dictionary of layers.
|
|
123
|
+
"""
|
|
124
|
+
return self._pedb.stackup.layers
|
|
125
|
+
|
|
126
|
+
def get_primitive(self, primitive_id):
|
|
127
|
+
"""Retrieve primitive from give id.
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
primitive_id : int
|
|
132
|
+
Primitive id.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
137
|
+
List of primitives.
|
|
138
|
+
"""
|
|
139
|
+
for p in self._layout.primitives:
|
|
140
|
+
if p.edb_uid == primitive_id:
|
|
141
|
+
return self.__mapping_primitive_type(p)
|
|
142
|
+
for p in self._layout.primitives:
|
|
143
|
+
for v in p.voids:
|
|
144
|
+
if v.edb_uid == primitive_id:
|
|
145
|
+
return self.__mapping_primitive_type(v)
|
|
146
|
+
|
|
147
|
+
def __mapping_primitive_type(self, primitive):
|
|
148
|
+
from ansys.edb.core.primitive.primitive import (
|
|
149
|
+
PrimitiveType as GrpcPrimitiveType,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if primitive.primitive_type == GrpcPrimitiveType.POLYGON:
|
|
153
|
+
return Polygon(self._pedb, primitive)
|
|
154
|
+
elif primitive.primitive_type == GrpcPrimitiveType.PATH:
|
|
155
|
+
return Path(self._pedb, primitive)
|
|
156
|
+
elif primitive.primitive_type == GrpcPrimitiveType.RECTANGLE:
|
|
157
|
+
return Rectangle(self._pedb, primitive)
|
|
158
|
+
elif primitive.primitive_type == GrpcPrimitiveType.CIRCLE:
|
|
159
|
+
return Circle(self._pedb, primitive)
|
|
160
|
+
elif primitive.primitive_type == GrpcPrimitiveType.BONDWIRE:
|
|
161
|
+
return Bondwire(self._pedb, primitive)
|
|
162
|
+
else:
|
|
163
|
+
return False
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def primitives(self):
|
|
167
|
+
"""Primitives.
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
172
|
+
List of primitives.
|
|
173
|
+
"""
|
|
174
|
+
return [self.__mapping_primitive_type(prim) for prim in self._pedb.layout.primitives]
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def polygons_by_layer(self):
|
|
178
|
+
"""Primitives with layer names as keys.
|
|
179
|
+
|
|
180
|
+
Returns
|
|
181
|
+
-------
|
|
182
|
+
dict
|
|
183
|
+
Dictionary of primitives with layer names as keys.
|
|
184
|
+
"""
|
|
185
|
+
_primitives_by_layer = {}
|
|
186
|
+
for lay in self.layers:
|
|
187
|
+
_primitives_by_layer[lay] = self.get_polygons_by_layer(lay)
|
|
188
|
+
return _primitives_by_layer
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def primitives_by_net(self):
|
|
192
|
+
"""Primitives with net names as keys.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
dict
|
|
197
|
+
Dictionary of primitives with nat names as keys.
|
|
198
|
+
"""
|
|
199
|
+
_prim_by_net = {}
|
|
200
|
+
for net, net_obj in self._pedb.nets.nets.items():
|
|
201
|
+
_prim_by_net[net] = [i for i in net_obj.primitives]
|
|
202
|
+
return _prim_by_net
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def primitives_by_layer(self):
|
|
206
|
+
"""Primitives with layer names as keys.
|
|
207
|
+
|
|
208
|
+
Returns
|
|
209
|
+
-------
|
|
210
|
+
dict
|
|
211
|
+
Dictionary of primitives with layer names as keys.
|
|
212
|
+
"""
|
|
213
|
+
_primitives_by_layer = {}
|
|
214
|
+
for lay in self.layers:
|
|
215
|
+
_primitives_by_layer[lay] = []
|
|
216
|
+
for lay in self._pedb.stackup.non_stackup_layers:
|
|
217
|
+
_primitives_by_layer[lay] = []
|
|
218
|
+
for i in self._layout.primitives:
|
|
219
|
+
try:
|
|
220
|
+
lay = i.layer.name
|
|
221
|
+
if lay in _primitives_by_layer:
|
|
222
|
+
_primitives_by_layer[lay].append(Primitive(self._pedb, i))
|
|
223
|
+
except (InvalidArgumentException, AttributeError):
|
|
224
|
+
pass
|
|
225
|
+
return _primitives_by_layer
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def rectangles(self):
|
|
229
|
+
"""Rectangles.
|
|
230
|
+
|
|
231
|
+
Returns
|
|
232
|
+
-------
|
|
233
|
+
list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
234
|
+
List of rectangles.
|
|
235
|
+
|
|
236
|
+
"""
|
|
237
|
+
return [Rectangle(self._pedb, i) for i in self.primitives if i.type == "rectangle"]
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def circles(self):
|
|
241
|
+
"""Circles.
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
246
|
+
List of circles.
|
|
247
|
+
|
|
248
|
+
"""
|
|
249
|
+
return [Circle(self._pedb, i) for i in self.primitives if i.type == "circle"]
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def paths(self):
|
|
253
|
+
"""Paths.
|
|
254
|
+
|
|
255
|
+
Returns
|
|
256
|
+
-------
|
|
257
|
+
list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
258
|
+
List of paths.
|
|
259
|
+
"""
|
|
260
|
+
return [Path(self._pedb, i) for i in self.primitives if i.type == "path"]
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def polygons(self):
|
|
264
|
+
"""Polygons.
|
|
265
|
+
|
|
266
|
+
Returns
|
|
267
|
+
-------
|
|
268
|
+
list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
269
|
+
List of polygons.
|
|
270
|
+
"""
|
|
271
|
+
return [Polygon(self._pedb, i) for i in self.primitives if i.type == "polygon"]
|
|
272
|
+
|
|
273
|
+
def get_polygons_by_layer(self, layer_name, net_list=None):
|
|
274
|
+
"""Retrieve polygons by a layer.
|
|
275
|
+
|
|
276
|
+
Parameters
|
|
277
|
+
----------
|
|
278
|
+
layer_name : str
|
|
279
|
+
Name of the layer.
|
|
280
|
+
net_list : list, optional
|
|
281
|
+
List of net names.
|
|
282
|
+
|
|
283
|
+
Returns
|
|
284
|
+
-------
|
|
285
|
+
list
|
|
286
|
+
List of primitive objects.
|
|
287
|
+
"""
|
|
288
|
+
objinst = []
|
|
289
|
+
for el in self.polygons:
|
|
290
|
+
if el.layer.name == layer_name:
|
|
291
|
+
if not el.net.is_null:
|
|
292
|
+
if net_list and el.net.name in net_list:
|
|
293
|
+
objinst.append(el)
|
|
294
|
+
else:
|
|
295
|
+
objinst.append(el)
|
|
296
|
+
return objinst
|
|
297
|
+
|
|
298
|
+
def get_primitive_by_layer_and_point(self, point=None, layer=None, nets=None):
|
|
299
|
+
"""Return primitive given coordinate point [x, y], layer name and nets.
|
|
300
|
+
|
|
301
|
+
Parameters
|
|
302
|
+
----------
|
|
303
|
+
point : list
|
|
304
|
+
Coordinate [x, y]
|
|
305
|
+
|
|
306
|
+
layer : list or str, optional
|
|
307
|
+
list of layer name or layer name applied on filter.
|
|
308
|
+
|
|
309
|
+
nets : list or str, optional
|
|
310
|
+
list of net name or single net name applied on filter
|
|
311
|
+
|
|
312
|
+
Returns
|
|
313
|
+
-------
|
|
314
|
+
list of :class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
315
|
+
List of primitives, polygons, paths and rectangles.
|
|
316
|
+
"""
|
|
317
|
+
from ansys.edb.core.primitive.primitive import Circle as GrpcCircle
|
|
318
|
+
from ansys.edb.core.primitive.primitive import Path as GrpcPath
|
|
319
|
+
from ansys.edb.core.primitive.primitive import Polygon as GrpcPolygon
|
|
320
|
+
from ansys.edb.core.primitive.primitive import Rectangle as GrpcRectangle
|
|
321
|
+
|
|
322
|
+
if isinstance(layer, str) and layer not in list(self._pedb.stackup.signal_layers.keys()):
|
|
323
|
+
layer = None
|
|
324
|
+
if not isinstance(point, list) and len(point) == 2:
|
|
325
|
+
self._logger.error("Provided point must be a list of two values")
|
|
326
|
+
return False
|
|
327
|
+
pt = GrpcPointData(point)
|
|
328
|
+
if isinstance(nets, str):
|
|
329
|
+
nets = [nets]
|
|
330
|
+
elif nets and not isinstance(nets, list) and len(nets) == len([net for net in nets if isinstance(net, str)]):
|
|
331
|
+
_nets = []
|
|
332
|
+
for net in nets:
|
|
333
|
+
if net not in self._pedb.nets:
|
|
334
|
+
self._logger.error(
|
|
335
|
+
f"Net {net} used to find primitive from layer point and net not found, skipping it."
|
|
336
|
+
)
|
|
337
|
+
else:
|
|
338
|
+
_nets.append(self._pedb.nets[net])
|
|
339
|
+
if _nets:
|
|
340
|
+
nets = _nets
|
|
341
|
+
if not isinstance(layer, list) and layer:
|
|
342
|
+
layer = [layer]
|
|
343
|
+
_obj_instances = self._pedb.layout_instance.query_layout_obj_instances(
|
|
344
|
+
layer_filter=layer, net_filter=nets, spatial_filter=pt
|
|
345
|
+
)
|
|
346
|
+
returned_obj = []
|
|
347
|
+
for inst in _obj_instances:
|
|
348
|
+
primitive = inst.layout_obj.cast()
|
|
349
|
+
if isinstance(primitive, GrpcPath):
|
|
350
|
+
returned_obj.append(Path(self._pedb, primitive))
|
|
351
|
+
elif isinstance(primitive, GrpcPolygon):
|
|
352
|
+
returned_obj.append(Polygon(self._pedb, primitive))
|
|
353
|
+
elif isinstance(primitive, GrpcRectangle):
|
|
354
|
+
returned_obj.append(Rectangle(self._pedb, primitive))
|
|
355
|
+
elif isinstance(primitive, GrpcCircle):
|
|
356
|
+
returned_obj.append(Circle(self._pedb, primitive))
|
|
357
|
+
return returned_obj
|
|
358
|
+
|
|
359
|
+
@staticmethod
|
|
360
|
+
def get_polygon_bounding_box(polygon):
|
|
361
|
+
"""Retrieve a polygon bounding box.
|
|
362
|
+
|
|
363
|
+
Parameters
|
|
364
|
+
----------
|
|
365
|
+
polygon :
|
|
366
|
+
Name of the polygon.
|
|
367
|
+
|
|
368
|
+
Returns
|
|
369
|
+
-------
|
|
370
|
+
list
|
|
371
|
+
List of bounding box coordinates in the format ``[-x, -y, +x, +y]``.
|
|
372
|
+
|
|
373
|
+
Examples
|
|
374
|
+
--------
|
|
375
|
+
>>> poly = database.modeler.get_polygons_by_layer("GND")
|
|
376
|
+
>>> bounding = database.modeler.get_polygon_bounding_box(poly[0])
|
|
377
|
+
"""
|
|
378
|
+
bounding_box = polygon.polygon_data.bbox()
|
|
379
|
+
return [
|
|
380
|
+
bounding_box[0].x.value,
|
|
381
|
+
bounding_box[0].y.value,
|
|
382
|
+
bounding_box[1].x.value,
|
|
383
|
+
bounding_box[1].y.value,
|
|
384
|
+
]
|
|
385
|
+
|
|
386
|
+
@staticmethod
|
|
387
|
+
def get_polygon_points(polygon):
|
|
388
|
+
"""Retrieve polygon points.
|
|
389
|
+
|
|
390
|
+
.. note::
|
|
391
|
+
For arcs, one point is returned.
|
|
392
|
+
|
|
393
|
+
Parameters
|
|
394
|
+
----------
|
|
395
|
+
polygon :
|
|
396
|
+
class: `dotnet.database.edb_data.primitives_data.Primitive`
|
|
397
|
+
|
|
398
|
+
Returns
|
|
399
|
+
-------
|
|
400
|
+
list
|
|
401
|
+
List of tuples. Each tuple provides x, y point coordinate. If the length of two consecutives tuples
|
|
402
|
+
from the list equals 2, a segment is defined. The first tuple defines the starting point while the second
|
|
403
|
+
tuple the ending one. If the length of one tuple equals one, that means a polyline is defined and the value
|
|
404
|
+
is giving the arc height. Therefore to polyline is defined as starting point for the tuple
|
|
405
|
+
before in the list, the current one the arc height and the tuple after the polyline ending point.
|
|
406
|
+
|
|
407
|
+
Examples
|
|
408
|
+
--------
|
|
409
|
+
|
|
410
|
+
>>> poly = database.modeler.get_polygons_by_layer("GND")
|
|
411
|
+
>>> points = database.modeler.get_polygon_points(poly[0])
|
|
412
|
+
|
|
413
|
+
"""
|
|
414
|
+
points = []
|
|
415
|
+
i = 0
|
|
416
|
+
continue_iterate = True
|
|
417
|
+
prev_point = None
|
|
418
|
+
while continue_iterate:
|
|
419
|
+
try:
|
|
420
|
+
point = polygon.polygon_data.points[i]
|
|
421
|
+
if prev_point != point:
|
|
422
|
+
if point.is_arc:
|
|
423
|
+
points.append([point.x.value])
|
|
424
|
+
else:
|
|
425
|
+
points.append([point.x.value, point.y.value])
|
|
426
|
+
prev_point = point
|
|
427
|
+
i += 1
|
|
428
|
+
else:
|
|
429
|
+
continue_iterate = False
|
|
430
|
+
except:
|
|
431
|
+
continue_iterate = False
|
|
432
|
+
return points
|
|
433
|
+
|
|
434
|
+
def parametrize_polygon(self, polygon, selection_polygon, offset_name="offsetx", origin=None):
|
|
435
|
+
"""Parametrize pieces of a polygon based on another polygon.
|
|
436
|
+
|
|
437
|
+
Parameters
|
|
438
|
+
----------
|
|
439
|
+
polygon :
|
|
440
|
+
Name of the polygon.
|
|
441
|
+
selection_polygon :
|
|
442
|
+
Polygon to use as a filter.
|
|
443
|
+
offset_name : str, optional
|
|
444
|
+
Name of the offset to create. The default is ``"offsetx"``.
|
|
445
|
+
origin : list, optional
|
|
446
|
+
List of the X and Y origins, which impacts the vector
|
|
447
|
+
computation and is needed to determine expansion direction.
|
|
448
|
+
The default is ``None``, in which case the vector is
|
|
449
|
+
computed from the polygon's center.
|
|
450
|
+
|
|
451
|
+
Returns
|
|
452
|
+
-------
|
|
453
|
+
bool
|
|
454
|
+
``True`` when successful, ``False`` when failed.
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
def calc_slope(point, origin):
|
|
458
|
+
if point[0] - origin[0] != 0:
|
|
459
|
+
slope = math.atan((point[1] - origin[1]) / (point[0] - origin[0]))
|
|
460
|
+
xcoeff = math.sin(slope)
|
|
461
|
+
ycoeff = math.cos(slope)
|
|
462
|
+
|
|
463
|
+
else:
|
|
464
|
+
if point[1] > 0:
|
|
465
|
+
xcoeff = 0
|
|
466
|
+
ycoeff = 1
|
|
467
|
+
else:
|
|
468
|
+
xcoeff = 0
|
|
469
|
+
ycoeff = -1
|
|
470
|
+
if ycoeff > 0:
|
|
471
|
+
ycoeff = "+" + str(ycoeff)
|
|
472
|
+
else:
|
|
473
|
+
ycoeff = str(ycoeff)
|
|
474
|
+
if xcoeff > 0:
|
|
475
|
+
xcoeff = "+" + str(xcoeff)
|
|
476
|
+
else:
|
|
477
|
+
xcoeff = str(xcoeff)
|
|
478
|
+
return xcoeff, ycoeff
|
|
479
|
+
|
|
480
|
+
selection_polygon_data = selection_polygon.polygon_data
|
|
481
|
+
polygon_data = polygon.polygon_data
|
|
482
|
+
bound_center = polygon_data.bounding_circle()[0]
|
|
483
|
+
bound_center2 = selection_polygon_data.bounding_circle()[0]
|
|
484
|
+
center = [bound_center.x.value, bound_center.y.value]
|
|
485
|
+
center2 = [bound_center2.x.value, bound_center2.y.value]
|
|
486
|
+
x1, y1 = calc_slope(center2, center)
|
|
487
|
+
|
|
488
|
+
if not origin:
|
|
489
|
+
origin = [center[0] + float(x1) * 10000, center[1] + float(y1) * 10000]
|
|
490
|
+
self._pedb.add_design_variable(offset_name, 0.0, is_parameter=True)
|
|
491
|
+
i = 0
|
|
492
|
+
continue_iterate = True
|
|
493
|
+
prev_point = None
|
|
494
|
+
while continue_iterate:
|
|
495
|
+
try:
|
|
496
|
+
point = polygon_data.points[i]
|
|
497
|
+
if prev_point != point:
|
|
498
|
+
check_inside = selection_polygon_data.is_inside(point)
|
|
499
|
+
if check_inside:
|
|
500
|
+
xcoeff, ycoeff = calc_slope([point.x.value, point.x.value], origin)
|
|
501
|
+
|
|
502
|
+
new_points = GrpcPointData(
|
|
503
|
+
[
|
|
504
|
+
GrpcValue(str(point.x.value) + f"{xcoeff}*{offset_name}"),
|
|
505
|
+
GrpcValue(str(point.y.value) + f"{ycoeff}*{offset_name}"),
|
|
506
|
+
]
|
|
507
|
+
)
|
|
508
|
+
polygon_data.points[i] = new_points
|
|
509
|
+
prev_point = point
|
|
510
|
+
i += 1
|
|
511
|
+
else:
|
|
512
|
+
continue_iterate = False
|
|
513
|
+
except:
|
|
514
|
+
continue_iterate = False
|
|
515
|
+
polygon.polygon_data = polygon_data
|
|
516
|
+
return True
|
|
517
|
+
|
|
518
|
+
def _create_path(
|
|
519
|
+
self,
|
|
520
|
+
points,
|
|
521
|
+
layer_name,
|
|
522
|
+
width=1,
|
|
523
|
+
net_name="",
|
|
524
|
+
start_cap_style="Round",
|
|
525
|
+
end_cap_style="Round",
|
|
526
|
+
corner_style="Round",
|
|
527
|
+
):
|
|
528
|
+
"""
|
|
529
|
+
Create a path based on a list of points.
|
|
530
|
+
|
|
531
|
+
Parameters
|
|
532
|
+
----------
|
|
533
|
+
points: .:class:`dotnet.database.layout.Shape`
|
|
534
|
+
List of points.
|
|
535
|
+
layer_name : str
|
|
536
|
+
Name of the layer on which to create the path.
|
|
537
|
+
width : float, optional
|
|
538
|
+
Width of the path. The default is ``1``.
|
|
539
|
+
net_name: str, optional
|
|
540
|
+
Name of the net. The default is ``""``.
|
|
541
|
+
start_cap_style : str, optional
|
|
542
|
+
Style of the cap at its start. Options are ``"Round"``,
|
|
543
|
+
``"Extended", `` and ``"Flat"``. The default is
|
|
544
|
+
``"Round".
|
|
545
|
+
end_cap_style : str, optional
|
|
546
|
+
Style of the cap at its end. Options are ``"Round"``,
|
|
547
|
+
``"Extended", `` and ``"Flat"``. The default is
|
|
548
|
+
``"Round".
|
|
549
|
+
corner_style : str, optional
|
|
550
|
+
Style of the corner. Options are ``"Round"``,
|
|
551
|
+
``"Sharp"`` and ``"Mitered"``. The default is ``"Round".
|
|
552
|
+
|
|
553
|
+
Returns
|
|
554
|
+
-------
|
|
555
|
+
:class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
556
|
+
``True`` when successful, ``False`` when failed.
|
|
557
|
+
"""
|
|
558
|
+
net = self._pedb.nets.find_or_create_net(net_name)
|
|
559
|
+
if start_cap_style.lower() == "round":
|
|
560
|
+
start_cap_style = GrpcPathEndCapType.ROUND
|
|
561
|
+
elif start_cap_style.lower() == "extended":
|
|
562
|
+
start_cap_style = GrpcPathEndCapType.EXTENDED
|
|
563
|
+
else:
|
|
564
|
+
start_cap_style = GrpcPathEndCapType.FLAT
|
|
565
|
+
if end_cap_style.lower() == "round":
|
|
566
|
+
end_cap_style = GrpcPathEndCapType.ROUND
|
|
567
|
+
elif end_cap_style.lower() == "extended":
|
|
568
|
+
end_cap_style = GrpcPathEndCapType.EXTENDED
|
|
569
|
+
else:
|
|
570
|
+
end_cap_style = GrpcPathEndCapType.FLAT
|
|
571
|
+
if corner_style.lower() == "round":
|
|
572
|
+
corner_style = GrpcPathEndCapType.ROUND
|
|
573
|
+
elif corner_style.lower() == "sharp":
|
|
574
|
+
corner_style = GrpcPathCornerType.SHARP
|
|
575
|
+
else:
|
|
576
|
+
corner_style = GrpcPathCornerType.MITER
|
|
577
|
+
_points = []
|
|
578
|
+
for pt in points:
|
|
579
|
+
_pt = []
|
|
580
|
+
for coord in pt:
|
|
581
|
+
coord = GrpcValue(coord, self._pedb.active_cell)
|
|
582
|
+
_pt.append(coord)
|
|
583
|
+
_points.append(_pt)
|
|
584
|
+
points = _points
|
|
585
|
+
|
|
586
|
+
width = GrpcValue(width, self._pedb.active_cell)
|
|
587
|
+
|
|
588
|
+
polygon_data = GrpcPolygonData(points=[GrpcPointData(i) for i in points])
|
|
589
|
+
path = Path.create(
|
|
590
|
+
layout=self._active_layout,
|
|
591
|
+
layer=layer_name,
|
|
592
|
+
net=net,
|
|
593
|
+
width=width,
|
|
594
|
+
end_cap1=start_cap_style,
|
|
595
|
+
end_cap2=end_cap_style,
|
|
596
|
+
corner_style=corner_style,
|
|
597
|
+
points=polygon_data,
|
|
598
|
+
)
|
|
599
|
+
if path.is_null: # pragma: no cover
|
|
600
|
+
self._logger.error("Null path created")
|
|
601
|
+
return False
|
|
602
|
+
return Path(self._pedb, path)
|
|
603
|
+
|
|
604
|
+
def create_trace(
|
|
605
|
+
self,
|
|
606
|
+
path_list,
|
|
607
|
+
layer_name,
|
|
608
|
+
width=1,
|
|
609
|
+
net_name="",
|
|
610
|
+
start_cap_style="Round",
|
|
611
|
+
end_cap_style="Round",
|
|
612
|
+
corner_style="Round",
|
|
613
|
+
):
|
|
614
|
+
"""
|
|
615
|
+
Create a trace based on a list of points.
|
|
616
|
+
|
|
617
|
+
Parameters
|
|
618
|
+
----------
|
|
619
|
+
path_list : list
|
|
620
|
+
List of points.
|
|
621
|
+
layer_name : str
|
|
622
|
+
Name of the layer on which to create the path.
|
|
623
|
+
width : float, optional
|
|
624
|
+
Width of the path. The default is ``1``.
|
|
625
|
+
net_name : str, optional
|
|
626
|
+
Name of the net. The default is ``""``.
|
|
627
|
+
start_cap_style : str, optional
|
|
628
|
+
Style of the cap at its start. Options are ``"Round"``,
|
|
629
|
+
``"Extended",`` and ``"Flat"``. The default is
|
|
630
|
+
``"Round"``.
|
|
631
|
+
end_cap_style : str, optional
|
|
632
|
+
Style of the cap at its end. Options are ``"Round"``,
|
|
633
|
+
``"Extended",`` and ``"Flat"``. The default is
|
|
634
|
+
``"Round"``.
|
|
635
|
+
corner_style : str, optional
|
|
636
|
+
Style of the corner. Options are ``"Round"``,
|
|
637
|
+
``"Sharp"`` and ``"Mitered"``. The default is ``"Round"``.
|
|
638
|
+
|
|
639
|
+
Returns
|
|
640
|
+
-------
|
|
641
|
+
:class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
642
|
+
"""
|
|
643
|
+
|
|
644
|
+
primitive = self._create_path(
|
|
645
|
+
points=path_list,
|
|
646
|
+
layer_name=layer_name,
|
|
647
|
+
net_name=net_name,
|
|
648
|
+
width=width,
|
|
649
|
+
start_cap_style=start_cap_style,
|
|
650
|
+
end_cap_style=end_cap_style,
|
|
651
|
+
corner_style=corner_style,
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
return primitive
|
|
655
|
+
|
|
656
|
+
def create_polygon(self, points, layer_name, voids=[], net_name=""):
|
|
657
|
+
"""Create a polygon based on a list of points and voids.
|
|
658
|
+
|
|
659
|
+
Parameters
|
|
660
|
+
----------
|
|
661
|
+
points : list of points or PolygonData.
|
|
662
|
+
- [x, y] coordinate
|
|
663
|
+
- [x, y, height] for an arc with specific height (between previous point and actual point)
|
|
664
|
+
- [x, y, rotation, xc, yc] for an arc given a point, rotation and center.
|
|
665
|
+
layer_name : str
|
|
666
|
+
Name of the layer on which to create the polygon.
|
|
667
|
+
voids : list, optional
|
|
668
|
+
List of shape objects for voids or points that creates the shapes. The default is``[]``.
|
|
669
|
+
net_name : str, optional
|
|
670
|
+
Name of the net. The default is ``""``.
|
|
671
|
+
|
|
672
|
+
Returns
|
|
673
|
+
-------
|
|
674
|
+
bool, :class:`dotnet.database.edb_data.primitives.Primitive`
|
|
675
|
+
Polygon when successful, ``False`` when failed.
|
|
676
|
+
"""
|
|
677
|
+
net = self._pedb.nets.find_or_create_net(net_name)
|
|
678
|
+
if isinstance(points, list):
|
|
679
|
+
new_points = []
|
|
680
|
+
for idx, i in enumerate(points):
|
|
681
|
+
new_points.append(
|
|
682
|
+
GrpcPointData([GrpcValue(i[0], self._pedb.active_cell), GrpcValue(i[1], self._pedb.active_cell)])
|
|
683
|
+
)
|
|
684
|
+
polygon_data = GrpcPolygonData(points=new_points)
|
|
685
|
+
|
|
686
|
+
elif isinstance(points, GrpcPolygonData):
|
|
687
|
+
polygon_data = points
|
|
688
|
+
else:
|
|
689
|
+
polygon_data = points
|
|
690
|
+
if not polygon_data.points:
|
|
691
|
+
self._logger.error("Failed to create main shape polygon data")
|
|
692
|
+
return False
|
|
693
|
+
for void in voids:
|
|
694
|
+
if isinstance(void, list):
|
|
695
|
+
void_polygon_data = GrpcPolygonData(points=void)
|
|
696
|
+
else:
|
|
697
|
+
void_polygon_data = void.polygon_data
|
|
698
|
+
if not void_polygon_data.points:
|
|
699
|
+
self._logger.error("Failed to create void polygon data")
|
|
700
|
+
return False
|
|
701
|
+
polygon_data.holes.append(void_polygon_data)
|
|
702
|
+
polygon = Polygon.create(layout=self._active_layout, layer=layer_name, net=net, polygon_data=polygon_data)
|
|
703
|
+
if polygon.is_null or polygon_data is False: # pragma: no cover
|
|
704
|
+
self._logger.error("Null polygon created")
|
|
705
|
+
return False
|
|
706
|
+
return Polygon(self._pedb, polygon)
|
|
707
|
+
|
|
708
|
+
def create_rectangle(
|
|
709
|
+
self,
|
|
710
|
+
layer_name,
|
|
711
|
+
net_name="",
|
|
712
|
+
lower_left_point="",
|
|
713
|
+
upper_right_point="",
|
|
714
|
+
center_point="",
|
|
715
|
+
width="",
|
|
716
|
+
height="",
|
|
717
|
+
representation_type="lower_left_upper_right",
|
|
718
|
+
corner_radius="0mm",
|
|
719
|
+
rotation="0deg",
|
|
720
|
+
):
|
|
721
|
+
"""Create rectangle.
|
|
722
|
+
|
|
723
|
+
Parameters
|
|
724
|
+
----------
|
|
725
|
+
layer_name : str
|
|
726
|
+
Name of the layer on which to create the rectangle.
|
|
727
|
+
net_name : str
|
|
728
|
+
Name of the net. The default is ``""``.
|
|
729
|
+
lower_left_point : list
|
|
730
|
+
Lower left point when ``representation_type="lower_left_upper_right"``. The default is ``""``.
|
|
731
|
+
upper_right_point : list
|
|
732
|
+
Upper right point when ``representation_type="lower_left_upper_right"``. The default is ``""``.
|
|
733
|
+
center_point : list
|
|
734
|
+
Center point when ``representation_type="center_width_height"``. The default is ``""``.
|
|
735
|
+
width : str
|
|
736
|
+
Width of the rectangle when ``representation_type="center_width_height"``. The default is ``""``.
|
|
737
|
+
height : str
|
|
738
|
+
Height of the rectangle when ``representation_type="center_width_height"``. The default is ``""``.
|
|
739
|
+
representation_type : str, optional
|
|
740
|
+
Type of the rectangle representation. The default is ``lower_left_upper_right``. Options are
|
|
741
|
+
``"lower_left_upper_right"`` and ``"center_width_height"``.
|
|
742
|
+
corner_radius : str, optional
|
|
743
|
+
Radius of the rectangle corner. The default is ``"0mm"``.
|
|
744
|
+
rotation : str, optional
|
|
745
|
+
Rotation of the rectangle. The default is ``"0deg"``.
|
|
746
|
+
|
|
747
|
+
Returns
|
|
748
|
+
-------
|
|
749
|
+
:class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
750
|
+
Rectangle when successful, ``False`` when failed.
|
|
751
|
+
"""
|
|
752
|
+
edb_net = self._pedb.nets.find_or_create_net(net_name)
|
|
753
|
+
if representation_type == "lower_left_upper_right":
|
|
754
|
+
rep_type = GrpcRectangleRepresentationType.LOWER_LEFT_UPPER_RIGHT
|
|
755
|
+
rect = Rectangle.create(
|
|
756
|
+
layout=self._active_layout,
|
|
757
|
+
layer=layer_name,
|
|
758
|
+
net=edb_net,
|
|
759
|
+
rep_type=rep_type,
|
|
760
|
+
param1=GrpcValue(lower_left_point[0]),
|
|
761
|
+
param2=GrpcValue(lower_left_point[1]),
|
|
762
|
+
param3=GrpcValue(upper_right_point[0]),
|
|
763
|
+
param4=GrpcValue(upper_right_point[1]),
|
|
764
|
+
corner_rad=GrpcValue(corner_radius),
|
|
765
|
+
rotation=GrpcValue(rotation),
|
|
766
|
+
)
|
|
767
|
+
else:
|
|
768
|
+
rep_type = GrpcRectangleRepresentationType.CENTER_WIDTH_HEIGHT
|
|
769
|
+
if isinstance(width, str):
|
|
770
|
+
if width in self._pedb.variables:
|
|
771
|
+
width = GrpcValue(width, self._pedb.active_cell)
|
|
772
|
+
else:
|
|
773
|
+
width = GrpcValue(width)
|
|
774
|
+
else:
|
|
775
|
+
width = GrpcValue(width)
|
|
776
|
+
if isinstance(height, str):
|
|
777
|
+
if height in self._pedb.variables:
|
|
778
|
+
height = GrpcValue(height, self._pedb.active_cell)
|
|
779
|
+
else:
|
|
780
|
+
height = GrpcValue(width)
|
|
781
|
+
else:
|
|
782
|
+
height = GrpcValue(width)
|
|
783
|
+
rect = Rectangle.create(
|
|
784
|
+
layout=self._active_layout,
|
|
785
|
+
layer=layer_name,
|
|
786
|
+
net=edb_net,
|
|
787
|
+
rep_type=rep_type,
|
|
788
|
+
param1=GrpcValue(center_point[0]),
|
|
789
|
+
param2=GrpcValue(center_point[1]),
|
|
790
|
+
param3=GrpcValue(width),
|
|
791
|
+
param4=GrpcValue(height),
|
|
792
|
+
corner_rad=GrpcValue(corner_radius),
|
|
793
|
+
rotation=GrpcValue(rotation),
|
|
794
|
+
)
|
|
795
|
+
if not rect.is_null:
|
|
796
|
+
return Rectangle(self._pedb, rect)
|
|
797
|
+
return False
|
|
798
|
+
|
|
799
|
+
def create_circle(self, layer_name, x, y, radius, net_name=""):
|
|
800
|
+
"""Create a circle on a specified layer.
|
|
801
|
+
|
|
802
|
+
Parameters
|
|
803
|
+
----------
|
|
804
|
+
layer_name : str
|
|
805
|
+
Name of the layer.
|
|
806
|
+
x : float
|
|
807
|
+
Position on the X axis.
|
|
808
|
+
y : float
|
|
809
|
+
Position on the Y axis.
|
|
810
|
+
radius : float
|
|
811
|
+
Radius of the circle.
|
|
812
|
+
net_name : str, optional
|
|
813
|
+
Name of the net. The default is ``None``, in which case the
|
|
814
|
+
default name is assigned.
|
|
815
|
+
|
|
816
|
+
Returns
|
|
817
|
+
-------
|
|
818
|
+
:class:`pyedb.dotnet.database.edb_data.primitives_data.Primitive`
|
|
819
|
+
Objects of the circle created when successful.
|
|
820
|
+
"""
|
|
821
|
+
edb_net = self._pedb.nets.find_or_create_net(net_name)
|
|
822
|
+
|
|
823
|
+
circle = Circle.create(
|
|
824
|
+
layout=self._active_layout,
|
|
825
|
+
layer=layer_name,
|
|
826
|
+
net=edb_net,
|
|
827
|
+
center_x=GrpcValue(x),
|
|
828
|
+
center_y=GrpcValue(y),
|
|
829
|
+
radius=GrpcValue(radius),
|
|
830
|
+
)
|
|
831
|
+
if not circle.is_null:
|
|
832
|
+
return Circle(self._pedb, circle)
|
|
833
|
+
return False
|
|
834
|
+
|
|
835
|
+
def delete_primitives(self, net_names):
|
|
836
|
+
"""Delete primitives by net names.
|
|
837
|
+
|
|
838
|
+
Parameters
|
|
839
|
+
----------
|
|
840
|
+
net_names : str, list
|
|
841
|
+
Names of the nets to delete.
|
|
842
|
+
|
|
843
|
+
Returns
|
|
844
|
+
-------
|
|
845
|
+
bool
|
|
846
|
+
``True`` when successful, ``False`` when failed.
|
|
847
|
+
|
|
848
|
+
References
|
|
849
|
+
----------
|
|
850
|
+
|
|
851
|
+
>>> Edb.modeler.delete_primitives(net_names=["GND"])
|
|
852
|
+
"""
|
|
853
|
+
if not isinstance(net_names, list): # pragma: no cover
|
|
854
|
+
net_names = [net_names]
|
|
855
|
+
|
|
856
|
+
for p in self.primitives[:]:
|
|
857
|
+
if p.net_name in net_names:
|
|
858
|
+
p.delete()
|
|
859
|
+
return True
|
|
860
|
+
|
|
861
|
+
def get_primitives(self, net_name=None, layer_name=None, prim_type=None, is_void=False):
|
|
862
|
+
"""Get primitives by conditions.
|
|
863
|
+
|
|
864
|
+
Parameters
|
|
865
|
+
----------
|
|
866
|
+
net_name : str, optional
|
|
867
|
+
Set filter on net_name. Default is `None`.
|
|
868
|
+
layer_name : str, optional
|
|
869
|
+
Set filter on layer_name. Default is `None`.
|
|
870
|
+
prim_type : str, optional
|
|
871
|
+
Set filter on primitive type. Default is `None`.
|
|
872
|
+
is_void : bool
|
|
873
|
+
Set filter on is_void. Default is 'False'
|
|
874
|
+
Returns
|
|
875
|
+
-------
|
|
876
|
+
list
|
|
877
|
+
List of filtered primitives
|
|
878
|
+
"""
|
|
879
|
+
prims = []
|
|
880
|
+
for el in self.primitives:
|
|
881
|
+
if not el.primitive_type:
|
|
882
|
+
continue
|
|
883
|
+
if net_name:
|
|
884
|
+
if not el.net.name == net_name:
|
|
885
|
+
continue
|
|
886
|
+
if layer_name:
|
|
887
|
+
if not el.layer.name == layer_name:
|
|
888
|
+
continue
|
|
889
|
+
if prim_type:
|
|
890
|
+
if not el.primitive_type.name.lower() == prim_type:
|
|
891
|
+
continue
|
|
892
|
+
if not el.is_void == is_void:
|
|
893
|
+
continue
|
|
894
|
+
prims.append(el)
|
|
895
|
+
return prims
|
|
896
|
+
|
|
897
|
+
def fix_circle_void_for_clipping(self):
|
|
898
|
+
"""Fix issues when circle void are clipped due to a bug in EDB.
|
|
899
|
+
|
|
900
|
+
Returns
|
|
901
|
+
-------
|
|
902
|
+
bool
|
|
903
|
+
``True`` when successful, ``False`` when no changes were applied.
|
|
904
|
+
"""
|
|
905
|
+
for void_circle in self.circles:
|
|
906
|
+
if not void_circle.is_void:
|
|
907
|
+
continue
|
|
908
|
+
circ_params = void_circle.get_parameters()
|
|
909
|
+
|
|
910
|
+
cloned_circle = Circle.create(
|
|
911
|
+
layout=self._active_layout,
|
|
912
|
+
layer=void_circle.layer_name,
|
|
913
|
+
net=void_circle.net,
|
|
914
|
+
center_x=GrpcValue(circ_params[0]),
|
|
915
|
+
center_y=GrpcValue(circ_params[1]),
|
|
916
|
+
radius=GrpcValue(circ_params[2]),
|
|
917
|
+
)
|
|
918
|
+
if not cloned_circle.is_null:
|
|
919
|
+
cloned_circle.is_negative = True
|
|
920
|
+
void_circle.delete()
|
|
921
|
+
return True
|
|
922
|
+
|
|
923
|
+
@staticmethod
|
|
924
|
+
def add_void(shape, void_shape):
|
|
925
|
+
"""Add a void into a shape.
|
|
926
|
+
|
|
927
|
+
Parameters
|
|
928
|
+
----------
|
|
929
|
+
shape : Polygon
|
|
930
|
+
Shape of the main object.
|
|
931
|
+
void_shape : list, Path
|
|
932
|
+
Shape of the voids.
|
|
933
|
+
"""
|
|
934
|
+
flag = False
|
|
935
|
+
if not isinstance(void_shape, list):
|
|
936
|
+
void_shape = [void_shape]
|
|
937
|
+
for void in void_shape:
|
|
938
|
+
if isinstance(void, Primitive):
|
|
939
|
+
shape._edb_object.add_void(void)
|
|
940
|
+
flag = True
|
|
941
|
+
else:
|
|
942
|
+
shape._edb_object.add_void(void)
|
|
943
|
+
flag = True
|
|
944
|
+
if not flag:
|
|
945
|
+
return flag
|
|
946
|
+
return True
|
|
947
|
+
|
|
948
|
+
def shape_to_polygon_data(self, shape):
|
|
949
|
+
"""Convert a shape to polygon data.
|
|
950
|
+
|
|
951
|
+
Parameters
|
|
952
|
+
----------
|
|
953
|
+
shape : :class:`pyedb.dotnet.database.modeler.Modeler.Shape`
|
|
954
|
+
Type of the shape to convert. Options are ``"rectangle"`` and ``"polygon"``.
|
|
955
|
+
"""
|
|
956
|
+
if shape.type == "polygon":
|
|
957
|
+
return self._createPolygonDataFromPolygon(shape)
|
|
958
|
+
elif shape.type == "rectangle":
|
|
959
|
+
return self._createPolygonDataFromRectangle(shape)
|
|
960
|
+
else:
|
|
961
|
+
self._logger.error(
|
|
962
|
+
"Unsupported shape type %s when creating a polygon primitive.",
|
|
963
|
+
shape.type,
|
|
964
|
+
)
|
|
965
|
+
return None
|
|
966
|
+
|
|
967
|
+
def _createPolygonDataFromPolygon(self, shape):
|
|
968
|
+
points = shape.points
|
|
969
|
+
if not self._validatePoint(points[0]):
|
|
970
|
+
self._logger.error("Error validating point.")
|
|
971
|
+
return None
|
|
972
|
+
arcs = []
|
|
973
|
+
is_parametric = False
|
|
974
|
+
for i in range(len(points) - 1):
|
|
975
|
+
if i == 0:
|
|
976
|
+
startPoint = points[-1]
|
|
977
|
+
endPoint = points[i]
|
|
978
|
+
else:
|
|
979
|
+
startPoint = points[i - 1]
|
|
980
|
+
endPoint = points[i]
|
|
981
|
+
|
|
982
|
+
if not self._validatePoint(endPoint):
|
|
983
|
+
return None
|
|
984
|
+
startPoint = [GrpcValue(i) for i in startPoint]
|
|
985
|
+
endPoint = [GrpcValue(i) for i in endPoint]
|
|
986
|
+
if len(endPoint) == 2:
|
|
987
|
+
is_parametric = (
|
|
988
|
+
is_parametric
|
|
989
|
+
or startPoint[0].is_parametric
|
|
990
|
+
or startPoint[1].is_parametric
|
|
991
|
+
or endPoint[0].is_parametric
|
|
992
|
+
or endPoint[1].is_parametric
|
|
993
|
+
)
|
|
994
|
+
arc = GrpcArcData(
|
|
995
|
+
GrpcPointData([startPoint[0], startPoint[1]]), GrpcPointData([endPoint[0], endPoint[1]])
|
|
996
|
+
)
|
|
997
|
+
arcs.append(arc)
|
|
998
|
+
elif len(endPoint) == 3:
|
|
999
|
+
is_parametric = (
|
|
1000
|
+
is_parametric
|
|
1001
|
+
or startPoint[0].is_parametric
|
|
1002
|
+
or startPoint[1].is_parametric
|
|
1003
|
+
or endPoint[0].is_parametric
|
|
1004
|
+
or endPoint[1].is_parametric
|
|
1005
|
+
or endPoint[2].is_parametric
|
|
1006
|
+
)
|
|
1007
|
+
arc = GrpcArcData(
|
|
1008
|
+
GrpcPointData([startPoint[0], startPoint[1]]),
|
|
1009
|
+
GrpcPointData([endPoint[0], endPoint[1]]),
|
|
1010
|
+
kwarg={"height": endPoint[2]},
|
|
1011
|
+
)
|
|
1012
|
+
arcs.append(arc)
|
|
1013
|
+
elif len(endPoint) == 5:
|
|
1014
|
+
is_parametric = (
|
|
1015
|
+
is_parametric
|
|
1016
|
+
or startPoint[0].is_parametric
|
|
1017
|
+
or startPoint[1].is_parametric
|
|
1018
|
+
or endPoint[0].is_parametric
|
|
1019
|
+
or endPoint[1].is_parametric
|
|
1020
|
+
or endPoint[3].is_parametric
|
|
1021
|
+
or endPoint[4].is_parametric
|
|
1022
|
+
)
|
|
1023
|
+
if endPoint[2].is_cw:
|
|
1024
|
+
rotationDirection = GrpcPolygonSenseType.SENSE_CW
|
|
1025
|
+
elif endPoint[2].is_ccw:
|
|
1026
|
+
rotationDirection = GrpcPolygonSenseType.SENSE_CCW
|
|
1027
|
+
else:
|
|
1028
|
+
self._logger.error("Invalid rotation direction %s is specified.", endPoint[2])
|
|
1029
|
+
return None
|
|
1030
|
+
arc = GrpcArcData(
|
|
1031
|
+
GrpcPointData(startPoint),
|
|
1032
|
+
GrpcPointData(endPoint),
|
|
1033
|
+
)
|
|
1034
|
+
# arc.direction = rotationDirection,
|
|
1035
|
+
# arc.center = GrpcPointData([endPoint[3], endPoint[4]]),
|
|
1036
|
+
arcs.append(arc)
|
|
1037
|
+
polygon = GrpcPolygonData(arcs=arcs)
|
|
1038
|
+
if not is_parametric:
|
|
1039
|
+
return polygon
|
|
1040
|
+
else:
|
|
1041
|
+
k = 0
|
|
1042
|
+
for pt in points:
|
|
1043
|
+
point = [GrpcValue(i) for i in pt]
|
|
1044
|
+
new_points = GrpcPointData(point)
|
|
1045
|
+
if len(point) > 2:
|
|
1046
|
+
k += 1
|
|
1047
|
+
polygon.set_point(k, new_points)
|
|
1048
|
+
k += 1
|
|
1049
|
+
return polygon
|
|
1050
|
+
|
|
1051
|
+
def _validatePoint(self, point, allowArcs=True):
|
|
1052
|
+
if len(point) == 2:
|
|
1053
|
+
if not isinstance(point[0], (int, float, str)):
|
|
1054
|
+
self._logger.error("Point X value must be a number.")
|
|
1055
|
+
return False
|
|
1056
|
+
if not isinstance(point[1], (int, float, str)):
|
|
1057
|
+
self._logger.error("Point Y value must be a number.")
|
|
1058
|
+
return False
|
|
1059
|
+
return True
|
|
1060
|
+
elif len(point) == 3:
|
|
1061
|
+
if not allowArcs: # pragma: no cover
|
|
1062
|
+
self._logger.error("Arc found but arcs are not allowed in _validatePoint.")
|
|
1063
|
+
return False
|
|
1064
|
+
if not isinstance(point[0], (int, float, str)): # pragma: no cover
|
|
1065
|
+
self._logger.error("Point X value must be a number.")
|
|
1066
|
+
return False
|
|
1067
|
+
if not isinstance(point[1], (int, float, str)): # pragma: no cover
|
|
1068
|
+
self._logger.error("Point Y value must be a number.")
|
|
1069
|
+
return False
|
|
1070
|
+
if not isinstance(point[1], (int, float, str)): # pragma: no cover
|
|
1071
|
+
self._logger.error("Invalid point height.")
|
|
1072
|
+
return False
|
|
1073
|
+
return True
|
|
1074
|
+
elif len(point) == 5:
|
|
1075
|
+
if not allowArcs: # pragma: no cover
|
|
1076
|
+
self._logger.error("Arc found but arcs are not allowed in _validatePoint.")
|
|
1077
|
+
return False
|
|
1078
|
+
if not isinstance(point[0], (int, float, str)): # pragma: no cover
|
|
1079
|
+
self._logger.error("Point X value must be a number.")
|
|
1080
|
+
return False
|
|
1081
|
+
if not isinstance(point[1], (int, float, str)): # pragma: no cover
|
|
1082
|
+
self._logger.error("Point Y value must be a number.")
|
|
1083
|
+
return False
|
|
1084
|
+
if not isinstance(point[2], str) or point[2] not in ["cw", "ccw"]:
|
|
1085
|
+
self._logger.error("Invalid rotation direction {} is specified.")
|
|
1086
|
+
return False
|
|
1087
|
+
if not isinstance(point[3], (int, float, str)): # pragma: no cover
|
|
1088
|
+
self._logger.error("Arc center point X value must be a number.")
|
|
1089
|
+
return False
|
|
1090
|
+
if not isinstance(point[4], (int, float, str)): # pragma: no cover
|
|
1091
|
+
self._logger.error("Arc center point Y value must be a number.")
|
|
1092
|
+
return False
|
|
1093
|
+
return True
|
|
1094
|
+
else: # pragma: no cover
|
|
1095
|
+
self._logger.error("Arc point descriptor has incorrect number of elements (%s)", len(point))
|
|
1096
|
+
return False
|
|
1097
|
+
|
|
1098
|
+
def _createPolygonDataFromRectangle(self, shape):
|
|
1099
|
+
# if not self._validatePoint(shape.pointA, False) or not self._validatePoint(shape.pointB, False):
|
|
1100
|
+
# return None
|
|
1101
|
+
# pointA = GrpcPointData(pointA[0]), self._get_edb_value(shape.pointA[1])
|
|
1102
|
+
# )
|
|
1103
|
+
# pointB = self._edb.geometry.point_data(
|
|
1104
|
+
# self._get_edb_value(shape.pointB[0]), self._get_edb_value(shape.pointB[1])
|
|
1105
|
+
# )
|
|
1106
|
+
# return self._edb.geometry.polygon_data.create_from_bbox((pointA, pointB))
|
|
1107
|
+
pass
|
|
1108
|
+
|
|
1109
|
+
def parametrize_trace_width(
|
|
1110
|
+
self,
|
|
1111
|
+
nets_name,
|
|
1112
|
+
layers_name=None,
|
|
1113
|
+
parameter_name="trace_width",
|
|
1114
|
+
variable_value=None,
|
|
1115
|
+
):
|
|
1116
|
+
"""Parametrize a Trace on specific layer or all stackup.
|
|
1117
|
+
|
|
1118
|
+
Parameters
|
|
1119
|
+
----------
|
|
1120
|
+
nets_name : str, list
|
|
1121
|
+
name of the net or list of nets to parametrize.
|
|
1122
|
+
layers_name : str, optional
|
|
1123
|
+
name of the layer or list of layers to which the net to parametrize has to be included.
|
|
1124
|
+
parameter_name : str, optional
|
|
1125
|
+
name of the parameter to create.
|
|
1126
|
+
variable_value : str, float, optional
|
|
1127
|
+
value with units of parameter to create.
|
|
1128
|
+
If None, the first trace width of Net will be used as parameter value.
|
|
1129
|
+
|
|
1130
|
+
Returns
|
|
1131
|
+
-------
|
|
1132
|
+
bool
|
|
1133
|
+
"""
|
|
1134
|
+
if isinstance(nets_name, str):
|
|
1135
|
+
nets_name = [nets_name]
|
|
1136
|
+
if isinstance(layers_name, str):
|
|
1137
|
+
layers_name = [layers_name]
|
|
1138
|
+
for net_name in nets_name:
|
|
1139
|
+
for p in self.paths:
|
|
1140
|
+
_parameter_name = f"{parameter_name}_{p.id}"
|
|
1141
|
+
if not p.net.is_null:
|
|
1142
|
+
if p.net.name == net_name:
|
|
1143
|
+
if not layers_name:
|
|
1144
|
+
if not variable_value:
|
|
1145
|
+
variable_value = p.width
|
|
1146
|
+
self._pedb.active_cell.add_variable(
|
|
1147
|
+
name=_parameter_name, value=GrpcValue(variable_value), is_param=True
|
|
1148
|
+
)
|
|
1149
|
+
p.width = GrpcValue(_parameter_name, self._pedb.active_cell)
|
|
1150
|
+
elif p.layer.name in layers_name:
|
|
1151
|
+
if not variable_value:
|
|
1152
|
+
variable_value = p.width
|
|
1153
|
+
self._pedb.add_design_variable(parameter_name, variable_value, True)
|
|
1154
|
+
p.width = GrpcValue(_parameter_name, self._pedb.active_cell)
|
|
1155
|
+
return True
|
|
1156
|
+
|
|
1157
|
+
def unite_polygons_on_layer(self, layer_name=None, delete_padstack_gemometries=False, net_names_list=[]):
|
|
1158
|
+
"""Try to unite all Polygons on specified layer.
|
|
1159
|
+
|
|
1160
|
+
Parameters
|
|
1161
|
+
----------
|
|
1162
|
+
layer_name : str, optional
|
|
1163
|
+
Name of layer name to unite objects on. The default is ``None``, in which case all layers are taken.
|
|
1164
|
+
delete_padstack_gemometries : bool, optional
|
|
1165
|
+
Whether to delete all padstack geometries. The default is ``False``.
|
|
1166
|
+
net_names_list : list[str] : optional
|
|
1167
|
+
Net names list filter. The default is ``[]``, in which case all nets are taken.
|
|
1168
|
+
|
|
1169
|
+
Returns
|
|
1170
|
+
-------
|
|
1171
|
+
bool
|
|
1172
|
+
``True`` is successful.
|
|
1173
|
+
"""
|
|
1174
|
+
if isinstance(layer_name, str):
|
|
1175
|
+
layer_name = [layer_name]
|
|
1176
|
+
if not layer_name:
|
|
1177
|
+
layer_name = list(self._pedb.stackup.signal_layers.keys())
|
|
1178
|
+
|
|
1179
|
+
for lay in layer_name:
|
|
1180
|
+
self._logger.info(f"Uniting Objects on layer {lay}.")
|
|
1181
|
+
poly_by_nets = {}
|
|
1182
|
+
all_voids = []
|
|
1183
|
+
list_polygon_data = []
|
|
1184
|
+
delete_list = []
|
|
1185
|
+
if lay in list(self.polygons_by_layer.keys()):
|
|
1186
|
+
for poly in self.polygons_by_layer[lay]:
|
|
1187
|
+
poly = poly
|
|
1188
|
+
if not poly.net.name in list(poly_by_nets.keys()):
|
|
1189
|
+
if poly.net.name:
|
|
1190
|
+
poly_by_nets[poly.net.name] = [poly]
|
|
1191
|
+
else:
|
|
1192
|
+
if poly.net.name:
|
|
1193
|
+
poly_by_nets[poly.net.name].append(poly)
|
|
1194
|
+
for net in poly_by_nets:
|
|
1195
|
+
if net in net_names_list or not net_names_list:
|
|
1196
|
+
for i in poly_by_nets[net]:
|
|
1197
|
+
list_polygon_data.append(i.polygon_data)
|
|
1198
|
+
delete_list.append(i)
|
|
1199
|
+
all_voids.append(i.voids)
|
|
1200
|
+
a = GrpcPolygonData.unite(list_polygon_data)
|
|
1201
|
+
for item in a:
|
|
1202
|
+
for v in all_voids:
|
|
1203
|
+
for void in v:
|
|
1204
|
+
if item.intersection_type(void.polygon_data) == 2:
|
|
1205
|
+
item.add_hole(void.polygon_data)
|
|
1206
|
+
self.create_polygon(item, layer_name=lay, voids=[], net_name=net)
|
|
1207
|
+
for v in all_voids:
|
|
1208
|
+
for void in v:
|
|
1209
|
+
for poly in poly_by_nets[net]: # pragma no cover
|
|
1210
|
+
if void.polygon_data.intersection_type(poly.polygon_data).value >= 2:
|
|
1211
|
+
try:
|
|
1212
|
+
id = delete_list.index(poly)
|
|
1213
|
+
except ValueError:
|
|
1214
|
+
id = -1
|
|
1215
|
+
if id >= 0:
|
|
1216
|
+
delete_list.pop(id)
|
|
1217
|
+
for poly in delete_list:
|
|
1218
|
+
poly.delete()
|
|
1219
|
+
|
|
1220
|
+
if delete_padstack_gemometries:
|
|
1221
|
+
self._logger.info("Deleting Padstack Definitions")
|
|
1222
|
+
for pad in self._pedb.padstacks.definitions:
|
|
1223
|
+
p1 = self._pedb.padstacks.definitions[pad].edb_padstack.data
|
|
1224
|
+
if len(p1.get_layer_names()) > 1:
|
|
1225
|
+
self._pedb.padstacks.remove_pads_from_padstack(pad)
|
|
1226
|
+
return True
|
|
1227
|
+
|
|
1228
|
+
def defeature_polygon(self, poly, tolerance=0.001):
|
|
1229
|
+
"""Defeature the polygon based on the maximum surface deviation criteria.
|
|
1230
|
+
|
|
1231
|
+
Parameters
|
|
1232
|
+
----------
|
|
1233
|
+
maximum_surface_deviation : float
|
|
1234
|
+
poly : Edb Polygon primitive
|
|
1235
|
+
Polygon to defeature.
|
|
1236
|
+
tolerance : float, optional
|
|
1237
|
+
Maximum tolerance criteria. The default is ``0.001``.
|
|
1238
|
+
|
|
1239
|
+
Returns
|
|
1240
|
+
-------
|
|
1241
|
+
bool
|
|
1242
|
+
``True`` when successful, ``False`` when failed.
|
|
1243
|
+
"""
|
|
1244
|
+
new_poly = poly.polygon_data.defeature(tol=tolerance)
|
|
1245
|
+
if not new_poly.points:
|
|
1246
|
+
self._pedb.logger.error(
|
|
1247
|
+
f"Defeaturing on polygon {poly.id} returned empty polygon, tolerance threshold " f"might too large. "
|
|
1248
|
+
)
|
|
1249
|
+
return False
|
|
1250
|
+
poly.polygon_data = new_poly
|
|
1251
|
+
return True
|
|
1252
|
+
|
|
1253
|
+
def get_layout_statistics(self, evaluate_area=False, net_list=None):
|
|
1254
|
+
"""Return EDBStatistics object from a layout.
|
|
1255
|
+
|
|
1256
|
+
Parameters
|
|
1257
|
+
----------
|
|
1258
|
+
|
|
1259
|
+
evaluate_area : optional bool
|
|
1260
|
+
When True evaluates the layout metal surface, can take time-consuming,
|
|
1261
|
+
avoid using this option on large design.
|
|
1262
|
+
|
|
1263
|
+
Returns
|
|
1264
|
+
-------
|
|
1265
|
+
|
|
1266
|
+
EDBStatistics object.
|
|
1267
|
+
|
|
1268
|
+
"""
|
|
1269
|
+
stat_model = LayoutStatistics()
|
|
1270
|
+
stat_model.num_layers = len(list(self._pedb.stackup.layers.values()))
|
|
1271
|
+
stat_model.num_capacitors = len(self._pedb.components.capacitors)
|
|
1272
|
+
stat_model.num_resistors = len(self._pedb.components.resistors)
|
|
1273
|
+
stat_model.num_inductors = len(self._pedb.components.inductors)
|
|
1274
|
+
bbox = self._pedb._hfss.get_layout_bounding_box(self._active_layout)
|
|
1275
|
+
stat_model._layout_size = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
|
1276
|
+
stat_model.num_discrete_components = (
|
|
1277
|
+
len(self._pedb.components.Others) + len(self._pedb.components.ICs) + len(self._pedb.components.IOs)
|
|
1278
|
+
)
|
|
1279
|
+
stat_model.num_inductors = len(self._pedb.components.inductors)
|
|
1280
|
+
stat_model.num_resistors = len(self._pedb.components.resistors)
|
|
1281
|
+
stat_model.num_capacitors = len(self._pedb.components.capacitors)
|
|
1282
|
+
stat_model.num_nets = len(self._pedb.nets.nets)
|
|
1283
|
+
stat_model.num_traces = len(self._pedb.modeler.paths)
|
|
1284
|
+
stat_model.num_polygons = len(self._pedb.modeler.polygons)
|
|
1285
|
+
stat_model.num_vias = len(self._pedb.padstacks.instances)
|
|
1286
|
+
stat_model.stackup_thickness = self._pedb.stackup.get_layout_thickness()
|
|
1287
|
+
if evaluate_area:
|
|
1288
|
+
outline_surface = stat_model.layout_size[0] * stat_model.layout_size[1]
|
|
1289
|
+
if net_list:
|
|
1290
|
+
netlist = list(self._pedb.nets.nets.keys())
|
|
1291
|
+
_poly = self._pedb.get_conformal_polygon_from_netlist(netlist)
|
|
1292
|
+
else:
|
|
1293
|
+
for layer in list(self._pedb.stackup.signal_layers.keys()):
|
|
1294
|
+
surface = 0.0
|
|
1295
|
+
primitives = self.primitives_by_layer[layer]
|
|
1296
|
+
for prim in primitives:
|
|
1297
|
+
if prim.primitive_type.name == "PATH":
|
|
1298
|
+
surface += Path(self._pedb, prim).length * prim.cast().width.value
|
|
1299
|
+
if prim.primitive_type.name == "POLYGON":
|
|
1300
|
+
surface += prim.polygon_data.area()
|
|
1301
|
+
stat_model.occupying_surface[layer] = surface
|
|
1302
|
+
stat_model.occupying_ratio[layer] = surface / outline_surface
|
|
1303
|
+
return stat_model
|
|
1304
|
+
|
|
1305
|
+
def create_bondwire(
|
|
1306
|
+
self,
|
|
1307
|
+
definition_name,
|
|
1308
|
+
placement_layer,
|
|
1309
|
+
width,
|
|
1310
|
+
material,
|
|
1311
|
+
start_layer_name,
|
|
1312
|
+
start_x,
|
|
1313
|
+
start_y,
|
|
1314
|
+
end_layer_name,
|
|
1315
|
+
end_x,
|
|
1316
|
+
end_y,
|
|
1317
|
+
net,
|
|
1318
|
+
start_cell_instance_name=None,
|
|
1319
|
+
end_cell_instance_name=None,
|
|
1320
|
+
bondwire_type="jedec4",
|
|
1321
|
+
):
|
|
1322
|
+
"""Create a bondwire object.
|
|
1323
|
+
|
|
1324
|
+
Parameters
|
|
1325
|
+
----------
|
|
1326
|
+
bondwire_type : :class:`BondwireType`
|
|
1327
|
+
Type of bondwire: kAPDBondWire or kJDECBondWire types.
|
|
1328
|
+
definition_name : str
|
|
1329
|
+
Bondwire definition name.
|
|
1330
|
+
placement_layer : str
|
|
1331
|
+
Layer name this bondwire will be on.
|
|
1332
|
+
width : :class:`Value <ansys.edb.utility.Value>`
|
|
1333
|
+
Bondwire width.
|
|
1334
|
+
material : str
|
|
1335
|
+
Bondwire material name.
|
|
1336
|
+
start_layer_name : str
|
|
1337
|
+
Name of start layer.
|
|
1338
|
+
start_x : :class:`Value <ansys.edb.utility.Value>`
|
|
1339
|
+
X value of start point.
|
|
1340
|
+
start_y : :class:`Value <ansys.edb.utility.Value>`
|
|
1341
|
+
Y value of start point.
|
|
1342
|
+
end_layer_name : str
|
|
1343
|
+
Name of end layer.
|
|
1344
|
+
end_x : :class:`Value <ansys.edb.utility.Value>`
|
|
1345
|
+
X value of end point.
|
|
1346
|
+
end_y : :class:`Value <ansys.edb.utility.Value>`
|
|
1347
|
+
Y value of end point.
|
|
1348
|
+
net : str or :class:`Net <ansys.edb.net.Net>` or None
|
|
1349
|
+
Net of the Bondwire.
|
|
1350
|
+
start_cell_instance_name : str, optional
|
|
1351
|
+
Cell instance name where the bondwire starts.
|
|
1352
|
+
end_cell_instance_name : str, optional
|
|
1353
|
+
Cell instance name where the bondwire ends.
|
|
1354
|
+
|
|
1355
|
+
Returns
|
|
1356
|
+
-------
|
|
1357
|
+
:class:`pyedb.dotnet.database.dotnet.primitive.BondwireDotNet`
|
|
1358
|
+
Bondwire object created.
|
|
1359
|
+
"""
|
|
1360
|
+
from ansys.edb.core.hierarchy.cell_instance import (
|
|
1361
|
+
CellInstance as GrpcCellInstance,
|
|
1362
|
+
)
|
|
1363
|
+
|
|
1364
|
+
start_cell_inst = None
|
|
1365
|
+
end_cell_inst = None
|
|
1366
|
+
cell_instances = {cell_inst.name: cell_inst for cell_inst in self._active_layout.cell_instances}
|
|
1367
|
+
if start_cell_instance_name:
|
|
1368
|
+
if start_cell_instance_name not in cell_instances:
|
|
1369
|
+
start_cell_inst = GrpcCellInstance.create(
|
|
1370
|
+
self._pedb.active_layout, start_cell_instance_name, ref=self._pedb.active_layout
|
|
1371
|
+
)
|
|
1372
|
+
else:
|
|
1373
|
+
start_cell_inst = cell_instances[start_cell_instance_name]
|
|
1374
|
+
cell_instances = {cell_inst.name: cell_inst for cell_inst in self._active_layout.cell_instances}
|
|
1375
|
+
if end_cell_instance_name:
|
|
1376
|
+
if end_cell_instance_name not in cell_instances:
|
|
1377
|
+
end_cell_inst = GrpcCellInstance.create(
|
|
1378
|
+
self._pedb.active_layout, end_cell_instance_name, ref=self._pedb.active_layout
|
|
1379
|
+
)
|
|
1380
|
+
else:
|
|
1381
|
+
end_cell_inst = cell_instances[end_cell_instance_name]
|
|
1382
|
+
|
|
1383
|
+
if bondwire_type == "jedec4":
|
|
1384
|
+
bondwire_type = GrpcBondwireType.JEDEC4
|
|
1385
|
+
elif bondwire_type == "jedec5":
|
|
1386
|
+
bondwire_type = GrpcBondwireType.JEDEC5
|
|
1387
|
+
elif bondwire_type == "apd":
|
|
1388
|
+
bondwire_type = GrpcBondwireType.APD
|
|
1389
|
+
else:
|
|
1390
|
+
bondwire_type = GrpcBondwireType.JEDEC4
|
|
1391
|
+
bw = Bondwire.create(
|
|
1392
|
+
layout=self._active_layout,
|
|
1393
|
+
bondwire_type=bondwire_type,
|
|
1394
|
+
definition_name=definition_name,
|
|
1395
|
+
placement_layer=placement_layer,
|
|
1396
|
+
width=GrpcValue(width),
|
|
1397
|
+
material=material,
|
|
1398
|
+
start_layer_name=start_layer_name,
|
|
1399
|
+
start_x=GrpcValue(start_x),
|
|
1400
|
+
start_y=GrpcValue(start_y),
|
|
1401
|
+
end_layer_name=end_layer_name,
|
|
1402
|
+
end_x=GrpcValue(end_x),
|
|
1403
|
+
end_y=GrpcValue(end_y),
|
|
1404
|
+
net=net,
|
|
1405
|
+
end_context=end_cell_inst,
|
|
1406
|
+
start_context=start_cell_inst,
|
|
1407
|
+
)
|
|
1408
|
+
return Bondwire(self._pedb, bw)
|
|
1409
|
+
|
|
1410
|
+
def create_pin_group(
|
|
1411
|
+
self,
|
|
1412
|
+
name: str,
|
|
1413
|
+
pins_by_id=None,
|
|
1414
|
+
pins_by_aedt_name=None,
|
|
1415
|
+
pins_by_name=None,
|
|
1416
|
+
):
|
|
1417
|
+
"""Create a PinGroup.
|
|
1418
|
+
|
|
1419
|
+
Parameters
|
|
1420
|
+
name : str,
|
|
1421
|
+
Name of the PinGroup.
|
|
1422
|
+
pins_by_id : list[int] or None
|
|
1423
|
+
List of pins by ID.
|
|
1424
|
+
pins_by_aedt_name : list[str] or None
|
|
1425
|
+
List of pins by AEDT name.
|
|
1426
|
+
pins_by_name : list[str] or None
|
|
1427
|
+
List of pins by name.
|
|
1428
|
+
"""
|
|
1429
|
+
# TODO move this method to components and merge with existing one
|
|
1430
|
+
pins = {}
|
|
1431
|
+
if pins_by_id:
|
|
1432
|
+
if isinstance(pins_by_id, int):
|
|
1433
|
+
pins_by_id = [pins_by_id]
|
|
1434
|
+
for p in pins_by_id:
|
|
1435
|
+
edb_pin = None
|
|
1436
|
+
if p in self._pedb.padstacks.instances:
|
|
1437
|
+
edb_pin = self._pedb.padstacks.instances[p]
|
|
1438
|
+
if edb_pin and not p in pins:
|
|
1439
|
+
pins[p] = edb_pin
|
|
1440
|
+
if not pins_by_aedt_name:
|
|
1441
|
+
pins_by_aedt_name = []
|
|
1442
|
+
if not pins_by_name:
|
|
1443
|
+
pins_by_name = []
|
|
1444
|
+
if pins_by_aedt_name or pins_by_name:
|
|
1445
|
+
if isinstance(pins_by_aedt_name, str):
|
|
1446
|
+
pins_by_aedt_name = [pins_by_aedt_name]
|
|
1447
|
+
if isinstance(pins_by_name, str):
|
|
1448
|
+
pins_by_name = [pins_by_name]
|
|
1449
|
+
p_inst = self._pedb.layout.padstack_instances
|
|
1450
|
+
_pins = {pin.id: pin for pin in p_inst if pin.aedt_name in pins_by_aedt_name or pin.name in pins_by_name}
|
|
1451
|
+
if not pins:
|
|
1452
|
+
pins = _pins
|
|
1453
|
+
else:
|
|
1454
|
+
for id, pin in _pins.items():
|
|
1455
|
+
if not id in pins:
|
|
1456
|
+
pins[id] = pin
|
|
1457
|
+
if not pins:
|
|
1458
|
+
self._logger.error("No pin found.")
|
|
1459
|
+
return False
|
|
1460
|
+
pins = list(pins.values())
|
|
1461
|
+
obj = GrpcPinGroup.create(layout=self._pedb.active_layout, name=name, padstack_instances=pins)
|
|
1462
|
+
if obj.is_null:
|
|
1463
|
+
raise RuntimeError(f"Failed to create pin group {name}.")
|
|
1464
|
+
else:
|
|
1465
|
+
net_obj = [i.net for i in pins if not i.net.is_null]
|
|
1466
|
+
if net_obj:
|
|
1467
|
+
obj.net = net_obj[0]
|
|
1468
|
+
return self._pedb.siwave.pin_groups[name]
|