Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/1680.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Place layout component enhancement
155 changes: 136 additions & 19 deletions src/pyedb/grpc/database/modeler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1533,11 +1533,12 @@ def insert_layout_instance_on_layer(
self,
cell_name: str,
placement_layer: str,
scale: Union[float] = 1,
rotation: Union[float, str] = 0,
x: Union[float, str] = 0,
y: Union[float, str] = 0,
mirror: bool = False,
place_on_bottom: bool = False,
local_origin_x: Optional[Union[float, str]] = 0,
local_origin_y: Optional[Union[float, str]] = 0,
) -> Any:
"""Insert a layout instance into the active layout.

Expand All @@ -1547,7 +1548,7 @@ def insert_layout_instance_on_layer(
Name of the layout to insert.
placement_layer: str
Placement Layer.
scale : float
scaling : float
Scale parameter.
rotation : float or str
Rotation angle, specified counter-clockwise in radians.
Expand All @@ -1557,24 +1558,39 @@ def insert_layout_instance_on_layer(
X offset.
y : float or str
Y offset.
place_on_bottom : bool
Whether to place the layout instance on the bottom of the layer.
local_origin_x: float or str
Local origin X coordinate.
local_origin_y: float or str
Local origin Y coordinate.
"""

from ansys.edb.core.hierarchy.cell_instance import CellInstance
from ansys.edb.core.layout.cell import Cell, CellType

from pyedb.generic.general_methods import generate_unique_name

instance_name = generate_unique_name(cell_name, n=2)
cell = Cell.find(self._pedb._db, CellType.CIRCUIT_CELL, cell_name)
cell_inst = CellInstance.create(self._pedb.active_layout, instance_name, cell.layout)
cell_inst.placement_layer = self._pedb.stackup.layers[placement_layer]._edb_object
transform = cell_inst.transform
transform.scale = scale
transform.rotation = rotation
transform.offset_x = x
transform.offset_y = y
transform.mirror = mirror
cell_inst.transform = transform
placement_layer = self._pedb.stackup.layers[placement_layer]
if not place_on_bottom:
cell_inst = self.insert_layout_instance_placement_3d(
cell_name=cell_name,
x=x,
y=y,
z=placement_layer.upper_elevation,
rotation_x="0deg",
rotation_y=0,
rotation_z=rotation,
local_origin_x=local_origin_x,
local_origin_y=local_origin_y,
)
else:
cell_inst = self.insert_layout_instance_placement_3d(
cell_name=cell_name,
x=x,
y=y,
z=placement_layer.lower_elevation,
rotation_x="180deg",
rotation_y=0,
rotation_z=rotation,
local_origin_x=local_origin_x,
local_origin_y=local_origin_y,
)
return cell_inst

def insert_layout_instance_placement_3d(
Expand All @@ -1586,6 +1602,9 @@ def insert_layout_instance_placement_3d(
rotation_x: Union[float, str] = 0.0,
rotation_y: Union[float, str] = 0.0,
rotation_z: Union[float, str] = 0.0,
local_origin_x: Union[float, str] = 0.0,
local_origin_y: Union[float, str] = 0.0,
local_origin_z: Union[float, str] = 0.0,
) -> Any:
"""Insert a 3D component placement into the active layout.

Expand All @@ -1605,6 +1624,12 @@ def insert_layout_instance_placement_3d(
Rotation angle around Y-axis, specified counter-clockwise in radians.
rotation_z: float or str
Rotation angle around Z-axis, specified counter-clockwise in radians.
local_origin_x: float or str
Local origin X coordinate.
local_origin_y: float or str
Local origin Y coordinate.
local_origin_z: float or str
Local origin Z coordinate.
"""

from ansys.edb.core.geometry.point3d_data import Point3DData as GrpcPoint3DData
Expand All @@ -1619,6 +1644,15 @@ def insert_layout_instance_placement_3d(
cell_inst.placement_3d = True
t3d = cell_inst.transform3d

# offsets
location = GrpcPoint3DData(
(self._pedb.value(local_origin_x) * -1)._edb_object,
(self._pedb.value(local_origin_y) * -1)._edb_object,
(self._pedb.value(local_origin_z) * -1)._edb_object,
)
t3d_offset = t3d.create_from_offset(offset=location)
t3d = t3d + t3d_offset

# Rotation X
t3d_rotation_x = t3d.create_from_axis_and_angle(
axis=GrpcPoint3DData(1.0, 0.0, 0.0), angle=self._pedb.value(rotation_x)
Expand Down Expand Up @@ -1657,6 +1691,9 @@ def insert_3d_component_placement_3d(
rotation_x: Union[float, str] = 0.0,
rotation_y: Union[float, str] = 0.0,
rotation_z: Union[float, str] = 0.0,
local_origin_x: Union[float, str] = 0.0,
local_origin_y: Union[float, str] = 0.0,
local_origin_z: Union[float, str] = 0.0,
) -> Any:
"""Insert a 3D component placement into the active layout.

Expand All @@ -1676,6 +1713,12 @@ def insert_3d_component_placement_3d(
Rotation angle around Y-axis, specified counter-clockwise in radians.
rotation_z: float or str
Rotation angle around Z-axis, specified counter-clockwise in radians.
local_origin_x: float or str
Local origin X coordinate.
local_origin_y: float or str
Local origin Y coordinate.
local_origin_z: float or str
Local origin Z coordinate.
"""
from ansys.edb.core.geometry.point3d_data import Point3DData as GrpcPoint3DData
from ansys.edb.core.layout.mcad_model import McadModel as GrpcMcadModel
Expand All @@ -1685,6 +1728,15 @@ def insert_3d_component_placement_3d(
cell_inst.placement_3d = True
t3d = cell_inst.transform3d

# offsets
location = GrpcPoint3DData(
(self._pedb.value(local_origin_x) * -1)._edb_object,
(self._pedb.value(local_origin_y) * -1)._edb_object,
(self._pedb.value(local_origin_z) * -1)._edb_object,
)
t3d_offset = t3d.create_from_offset(offset=location)
t3d = t3d + t3d_offset

# Rotation X
t3d_rotation_x = t3d.create_from_axis_and_angle(
axis=GrpcPoint3DData(1.0, 0.0, 0.0), angle=self._pedb.value(rotation_x)
Expand Down Expand Up @@ -1713,3 +1765,68 @@ def insert_3d_component_placement_3d(
# Set transform3d back into instance
cell_inst.transform3d = t3d
return cell_inst

def insert_3d_component_on_layer(
self,
a3dcomp_path: Union[str, Path],
placement_layer: str,
rotation: Union[float, str] = 0,
x: Union[float, str] = 0,
y: Union[float, str] = 0,
place_on_bottom: bool = False,
local_origin_x: Optional[Union[float, str]] = 0,
local_origin_y: Optional[Union[float, str]] = 0,
local_origin_z: Optional[Union[float, str]] = 0,
) -> Any:
"""Insert a layout instance into the active layout.

Parameters
----------
a3dcomp_path: str or Path
File path to the 3D component.
placement_layer: str
Placement Layer.
rotation : float or str
Rotation angle, specified counter-clockwise in radians.
x : float or str
X offset.
y : float or str
Y offset.
place_on_bottom : bool
Whether to place the layout instance on the bottom of the layer.
local_origin_x: float or str
Local origin X coordinate.
local_origin_y: float or str
Local origin Y coordinate.
local_origin_z: float or str
Local origin Z coordinate.
"""

placement_layer = self._pedb.stackup.layers[placement_layer]
if not place_on_bottom:
cell_inst = self.insert_3d_component_placement_3d(
a3dcomp_path=a3dcomp_path,
x=x,
y=y,
z=placement_layer.upper_elevation,
rotation_x=0,
rotation_y=0,
rotation_z=rotation,
local_origin_x=local_origin_x,
local_origin_y=local_origin_y,
local_origin_z=local_origin_z,
)
else:
cell_inst = self.insert_3d_component_placement_3d(
a3dcomp_path=a3dcomp_path,
x=x,
y=y,
z=placement_layer.lower_elevation,
rotation_x="180deg",
rotation_y=0,
rotation_z=rotation,
local_origin_x=local_origin_x,
local_origin_y=local_origin_y,
local_origin_z=local_origin_z,
)
return cell_inst
46 changes: 39 additions & 7 deletions tests/system/test_edb_modeler.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,20 +616,35 @@ def test_insert_layout_instance(self, edb_examples):
edbapp = edb_examples.get_si_verse()
edb2_path = edb_examples.get_package(edbapp=False)
edbapp.copy_cell_from_edb(edb2_path)
cell_inst = edbapp.modeler.insert_layout_instance_on_layer("analysis", "1_Top", 2, "180deg", "1mm", "2mm", True)
assert cell_inst.transform.rotation.value == pytest.approx(3.14159265358979)
assert cell_inst.transform.scale.value == pytest.approx(2)
assert cell_inst.transform.offset_x.value == pytest.approx(0.001)
assert cell_inst.transform.offset_y.value == pytest.approx(0.002)
assert cell_inst.transform.mirror
cell_inst = edbapp.modeler.insert_layout_instance_on_layer("analysis", "1_Top", "180deg", "1mm", "2mm", True)
assert cell_inst.transform3d.shift.x.value == pytest.approx(0.001)
assert cell_inst.transform3d.shift.y.value == pytest.approx(0.002)
assert cell_inst.transform3d.shift.z.value == pytest.approx(edbapp.stackup.layers["1_Top"].lower_elevation)
edbapp.close(terminate_rpc_session=False)

@pytest.mark.skipif(not config.get("use_grpc"), reason="only implemented in gRPC")
def test_insert_layout_instance_place_on_bottom(self, edb_examples):
edbapp = edb_examples.get_si_verse()
edb2_path = edb_examples.get_package(edbapp=False)
edbapp.copy_cell_from_edb(edb2_path)
cell_inst = edbapp.modeler.insert_layout_instance_on_layer(
"analysis", "16_Bottom", 2, "180deg", "32mm", "-1mm", True, True
)
assert not cell_inst.is_null
edbapp.close(terminate_rpc_session=False)

@pytest.mark.skipif(not config.get("use_grpc"), reason="bug in dotnet core")
def test_insert_layout_instance_placement_3d(self, edb_examples):
edbapp = edb_examples.get_si_verse()
edb2_path = edb_examples.get_package(edbapp=False)
edbapp.copy_cell_from_edb(edb2_path)
cell_inst = edbapp.modeler.insert_layout_instance_placement_3d("analysis", rotation_x="180deg", z="-0.33mm")
cell_inst = edbapp.modeler.insert_layout_instance_placement_3d(
"analysis",
rotation_z="30deg",
z="-0.33mm",
local_origin_x="4.4mm",
local_origin_y="4.4mm",
)
assert not cell_inst.is_null
edbapp.close(terminate_rpc_session=False)

Expand All @@ -648,3 +663,20 @@ def test_insert_3d_component_placement_3d(self, edb_examples):
assert cell_inst_1.transform3d.shift.y.value == pytest.approx(0.002)
assert cell_inst_1.transform3d.shift.z.value == pytest.approx(0.003)
edbapp.close(terminate_rpc_session=False)

@pytest.mark.skipif(not config.get("use_grpc"), reason="bug in dotnet core")
def test_insert_3d_component_on_layer(self, edb_examples):
edbapp = edb_examples.get_si_board(additional_files_folders=["si_board/SMA.a3dcomp"])
cell_inst_1 = edbapp.modeler.insert_3d_component_on_layer(
a3dcomp_path=Path(edbapp.edbpath).with_name("SMA.a3dcomp"), x="1mm", y="2mm", placement_layer="s1"
)
assert not cell_inst_1.is_null
cell_inst_2 = edbapp.modeler.insert_3d_component_on_layer(
a3dcomp_path=Path(edbapp.edbpath).with_name("SMA.a3dcomp"),
x="5mm",
y="2mm",
placement_layer="s3",
place_on_bottom=True,
)
assert not cell_inst_2.is_null
edbapp.close(terminate_rpc_session=False)
Loading