Skip to content

Commit

Permalink
FEAT: Expose port properties on Components3dLayout (#5376)
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacwaldron authored Nov 6, 2024
1 parent 73be523 commit 51de3f1
Show file tree
Hide file tree
Showing 4 changed files with 1,537 additions and 864 deletions.
171 changes: 130 additions & 41 deletions src/ansys/aedt/core/modeler/pcb/object_3d_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

import math
import re
from typing import Optional
from typing import Tuple

from ansys.aedt.core.generic.constants import unit_converter
from ansys.aedt.core.generic.general_methods import _dim_arg
Expand All @@ -48,6 +50,7 @@ class Object3DLayout(object):

def __init__(self, primitives, prim_type=None):
self._primitives = primitives
self.logger = self._primitives.logger
self._oeditor = self._primitives.oeditor
self._n = 10
self.prim_type = prim_type
Expand Down Expand Up @@ -267,7 +270,7 @@ def create_clearance_on_component(self, extra_soldermask_clearance=5e-3):
bool
"""
if self.prim_type != "component":
self._primitives.logger.error("Clearance applies only to components.")
self.logger.error("Clearance applies only to components.")
return False
bbox = self.bounding_box
start_points = [bbox[0] - extra_soldermask_clearance, bbox[1] - extra_soldermask_clearance]
Expand Down Expand Up @@ -467,6 +470,37 @@ def __init__(self, primitives, name="", edb_object=None):
self.edb_object = edb_object
self._pins = {}

@staticmethod
def __get_die_properties(model_info):
s = r".+DieProp\(dt=(.+?), do=(.+?), dh='(.+?)', lid=(.+?)\)"
m = re.search(s, model_info)
dt = 0
do = 0
dh = "0"
lid = -100
if m:
dt = int(m.group(1))
do = int(m.group(2))
dh = str(m.group(3))
lid = int(m.group(4))
return dt, do, dh, lid

@staticmethod
def __get_port_properties(model_info):
s = r".+PortProp\(rh='(.+?)', rsa=(.+?), rsx='(.+?)', rsy='(.+?)'\)"
m = re.search(s, model_info)
rsx = "0"
rsy = "0"
rsa = True
rh = "0"
if m:
rh = m.group(1)
rsx = m.group(3)
rsy = m.group(4)
if m.group(2) == "false":
rsa = False
return rh, rsa, rsx, rsy

@property
def part(self):
"""Retrieve the component part.
Expand Down Expand Up @@ -499,8 +533,7 @@ def part_type(self):
"""
return self._oeditor.GetPropertyValue("BaseElementTab", self.name, "Part Type")

@property
def _part_type_id(self):
def __part_type_id(self):
parts = {"Other": 0, "Resistor": 1, "Inductor": 2, "Capacitor": 3, "IC": 4, "IO": 5}
if self.part_type in parts:
return parts[self.part_type]
Expand Down Expand Up @@ -530,10 +563,83 @@ def enabled(self):

@enabled.setter
def enabled(self, status):
if self._part_type_id in [0, 4, 5]:
if self.__part_type_id() in [0, 4, 5]:
return False
self._oeditor.EnableComponents(["NAME:Components", self.name], status)

@property
def die_properties(self) -> Optional[Tuple[int, int, str, int]]:
"""Get die properties from component.
Returns
-------
Tuple[int, int, str, int]
Tuple of die type (``0`` for None, ``1``, for FlipChip, or ``2`` for WireBond), die orientation (``0`` for
Chip Top or ``1`` for Chip Bottom), die height as a string, and a reserved property as an integer.
"""
if self.__part_type_id() != 4:
return None
return Components3DLayout.__get_die_properties(self.__get_model_info())

def __has_port_properties(self) -> bool:
return self.__part_type_id() in [0, 4, 5]

@property
def port_properties(self) -> Optional[Tuple[str, bool, str, str]]:
"""Get port properties from component.
Returns
-------
Tuple[str, bool, str, str]
Tuple of reference offset [str], reference size auto [bool], reference size X dimension [str], reference
size Y dimension [str].
"""
if not self.__has_port_properties():
return None
return Components3DLayout.__get_port_properties(self.__get_model_info())

@port_properties.setter
def port_properties(self, values: Tuple[str, bool, str, str]):
rh, rsa, rsx, rsy = values
if not self.__has_port_properties():
self.logger.warning(
f"Cannot set port_properties on {self.name!r} with part type ID {self.__part_type_id()!r}"
)
return
if self.__part_type_id() == 4:
prop_name = "ICProp:="
else:
prop_name = "IOProp:="
port_properties = ["rh:=", rh, "rsa:=", rsa, "rsx:=", rsx, "rsy:=", rsy]

args = [
"NAME:Model Info",
[
"NAME:Model",
prop_name,
[
"PortProp:=",
port_properties,
],
],
]
if self.__part_type_id() == 4:
dt, do, dh, lid = self.die_properties
args[1] = [
"NAME:Model",
prop_name,
[
"DieProp:=",
["dt:=", dt, "do:=", do, "dh:=", dh, "lid:=", lid],
"PortProp:=",
port_properties,
],
"CompType:=",
4,
]

self.change_property(args)

@property
def solderball_enabled(self):
"""Check if solderball is enabled.
Expand All @@ -543,7 +649,7 @@ def solderball_enabled(self):
bool
``True`` when successful, ``False`` when failed.
"""
if self._part_type_id not in [0, 4, 5]:
if not self.__has_port_properties():
return False
component_info = str(list(self._oeditor.GetComponentInfo(self.name))).replace("'", "").replace('"', "")
if "sbsh=Cyl" in component_info or "sbsh=Sph" in component_info:
Expand All @@ -559,7 +665,7 @@ def die_enabled(self):
bool
``True`` when successful, ``False`` when failed.
"""
if self._part_type_id not in [0, 4, 5]:
if not self.__has_port_properties():
return False
component_info = str(list(self._oeditor.GetComponentInfo(self.name))).replace("'", "").replace('"', "")
if "dt=1" in component_info or "dt=2" in component_info:
Expand All @@ -574,7 +680,7 @@ def die_type(self):
-------
str
"""
if self._part_type_id not in [0, 4, 5]:
if not self.__has_port_properties():
return False
component_info = str(list(self._oeditor.GetComponentInfo(self.name))).replace("'", "").replace('"', "")
if "dt=1" in component_info:
Expand Down Expand Up @@ -620,12 +726,12 @@ def set_die_type(
bool
``True`` when successful, ``False`` when failed.
"""
if self._part_type_id not in [0, 4, 5]:
if not self.__has_port_properties():
return False
if auto_reference:
reference_x = "0"
reference_y = "0"
if self._part_type_id == 4:
if self.__part_type_id() == 4:
prop_name = "ICProp:="
else:
prop_name = "IOProp:="
Expand Down Expand Up @@ -690,43 +796,17 @@ def set_solderball(
bool
``True`` when successful, ``False`` when failed or the wrong component type.
"""
if self._part_type_id not in [0, 4, 5]:
if not self.__has_port_properties():
return False
props = self._oeditor.GetComponentInfo(self.name)
model = ""
for p in props:
if "PortProp(" in p:
model = p
break
s = r".+PortProp\(rh='(.+?)', rsa=(.+?), rsx='(.+?)', rsy='(.+?)'\)"
m = re.search(s, model)
rsx = "0"
rsy = "0"
rsa = True
rh = "0"
if m:
rh = m.group(1)
rsx = m.group(3)
rsy = m.group(4)
if m.group(2) == "false":
rsa = False
model_info = self.__get_model_info()
rh, rsa, rsx, rsy = Components3DLayout.__get_port_properties(model_info)
if reference_offset:
rh = reference_offset
if self._part_type_id == 4:
if self.__part_type_id() == 4:
prop_name = "ICProp:="
if not self.die_enabled:
self.set_die_type()
s = r".+DieProp\(dt=(.+?), do=(.+?), dh='(.+?)', lid=(.+?)\)"
m = re.search(s, model)
dt = 0
do = 0
dh = "0"
lid = -100
if m:
dt = int(m.group(1))
do = int(m.group(2))
dh = str(m.group(3))
lid = int(m.group(4))
dt, do, dh, lid = Components3DLayout.__get_die_properties(model_info)

args = [
"NAME:Model Info",
Expand Down Expand Up @@ -784,6 +864,15 @@ def set_solderball(
]
return self.change_property(args)

def __get_model_info(self):
props = self._oeditor.GetComponentInfo(self.name)
model_info = ""
for p in props:
if "PortProp(" in p:
model_info = p
break
return model_info

@property
def pins(self):
"""Component pins.
Expand All @@ -806,7 +895,7 @@ def model(self):
-------
:class:`ansys.aedt.core.modeler.cad.object_3dlayout.ModelInfoRlc`
"""
if self._part_type_id in [1, 2, 3]:
if self.__part_type_id() in [1, 2, 3]:
return ModelInfoRlc(self, self.name)


Expand Down
Binary file not shown.
Loading

0 comments on commit 51de3f1

Please sign in to comment.