Skip to content
1 change: 1 addition & 0 deletions doc/changelog.d/1659.miscellaneous.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Edb cfg boundaries
110 changes: 33 additions & 77 deletions src/pyedb/configuration/cfg_boundaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,87 +19,43 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from typing import Any, Optional, Union

from pyedb.configuration.cfg_common import CfgBase
from pydantic import BaseModel, Field


class CfgBoundaries(CfgBase):
def __init__(self, pedb, boundary_data):
self._pedb = pedb
self.boundary_data = boundary_data

self.open_region = self.boundary_data.get("open_region", None)
self.open_region_type = self.boundary_data.get("map_open_region_type", None)
self.pml_visible = self.boundary_data.get("pml_visible", None)
self.pml_operation_frequency = self.boundary_data.get("pml_operation_frequency", None)
self.pml_radiation_factor = self.boundary_data.get("pml_radiation_factor", None)
self.dielectric_extent_type = self.boundary_data.get("dielectric_extent_type", None)
self.horizontal_padding = self.boundary_data.get("horizontal_padding", None)
self.honor_primitives_on_dielectric_layers = self.boundary_data.get(
"honor_primitives_on_dielectric_layers", False
)
self.air_box_extent_type = self.boundary_data.get("air_box_extent_type", None)
self.air_box_base_polygon = self.boundary_data.get("air_box_base_polygon", None)
self.air_box_truncate_model_ground_layers = self.boundary_data.get("air_box_truncate_model_ground_layers", None)
self.air_box_horizontal_padding = self.boundary_data.get("air_box_horizontal_padding", None)
self.air_box_positive_vertical_padding = self.boundary_data.get("air_box_positive_vertical_padding", None)
self.air_box_negative_vertical_padding = self.boundary_data.get("air_box_negative_vertical_padding", None)
class CfgBase(BaseModel):
model_config = {
"populate_by_name": True,
"extra": "forbid",
}

def get_parameters_from_edb(self):
self.open_region = self._pedb.hfss.hfss_extent_info.use_open_region
self.open_region_type = self._pedb.hfss.hfss_extent_info.open_region_type
self.pml_visible = self._pedb.hfss.hfss_extent_info.is_pml_visible
self.pml_operation_frequency = self._pedb.hfss.hfss_extent_info.operating_freq.tostring
self.pml_radiation_factor = self._pedb.hfss.hfss_extent_info.radiation_level.tostring
self.dielectric_extent_type = self._pedb.hfss.hfss_extent_info.extent_type
self.horizontal_padding = self._pedb.hfss.hfss_extent_info.dielectric_extent_size
self.honor_primitives_on_dielectric_layers = self._pedb.hfss.hfss_extent_info.honor_user_dielectric
self.air_box_extent_type = self._pedb.hfss.hfss_extent_info.extent_type
self.air_box_truncate_model_ground_layers = self._pedb.hfss.hfss_extent_info.truncate_air_box_at_ground
self.air_box_horizontal_padding = self._pedb.hfss.hfss_extent_info.air_box_horizontal_extent
self.air_box_positive_vertical_padding = self._pedb.hfss.hfss_extent_info.air_box_positive_vertical_extent
self.air_box_negative_vertical_padding = self._pedb.hfss.hfss_extent_info.air_box_negative_vertical_extent
return self.get_attributes(exclude="boundary_data")

def set_parameters_to_edb(self):
"""Imports boundary information from JSON."""
if self.open_region is not None:
self._pedb.hfss.hfss_extent_info.use_open_region = self.open_region
if self.open_region_type:
self._pedb.hfss.hfss_extent_info.open_region_type = self.open_region_type.lower()
if self.pml_visible is not None:
self._pedb.hfss.hfss_extent_info.is_pml_visible = self.pml_visible
if self.pml_operation_frequency:
self._pedb.hfss.hfss_extent_info.operating_freq = self.pml_operation_frequency
if self.pml_radiation_factor:
if self._pedb.grpc:
self._pedb.hfss.hfss_extent_info.pml_radiation_factor = self.pml_radiation_factor
else:
self._pedb.hfss.hfss_extent_info.radiation_level = self.pml_radiation_factor
if self.dielectric_extent_type:
self._pedb.hfss.hfss_extent_info.extent_type = self.dielectric_extent_type.lower()
if self.horizontal_padding:
self._pedb.hfss.hfss_extent_info.dielectric_extent_size = float(self.horizontal_padding)
if self.honor_primitives_on_dielectric_layers is not None:
self._pedb.hfss.hfss_extent_info.honor_user_dielectric = self.honor_primitives_on_dielectric_layers
if self.air_box_extent_type:
self._pedb.hfss.hfss_extent_info.extent_type = self.air_box_extent_type.lower()
if self.air_box_truncate_model_ground_layers is not None:
self._pedb.hfss.hfss_extent_info.truncate_air_box_at_ground = self.air_box_truncate_model_ground_layers
if self.air_box_horizontal_padding:
self._pedb.hfss.hfss_extent_info.air_box_horizontal_extent = float(self.air_box_horizontal_padding)
if self.air_box_positive_vertical_padding:
self._pedb.hfss.hfss_extent_info.air_box_positive_vertical_extent = float(
self.air_box_positive_vertical_padding
)
if self.air_box_negative_vertical_padding:
self._pedb.hfss.hfss_extent_info.air_box_negative_vertical_extent = float(
self.air_box_negative_vertical_padding
)
class PaddingData(CfgBase):
size: Union[float, str]
is_multiple: bool

def apply(self):
"""Imports boundary information from JSON."""
self.set_parameters_to_edb()

def get_data_from_db(self):
return self.get_parameters_from_edb()
class CfgBoundaries(CfgBase):
use_open_region: Optional[Any] = Field(default=None)
open_region_type: Optional[Any] = Field(default=None)
is_pml_visible: Optional[Any] = Field(default=None)
operating_freq: Optional[Any] = Field(default=None)
pml_radiation_factor: Optional[int] = Field(default=None)

dielectric_extent_type: Optional[str] = Field(default=None)
dielectric_base_polygon: Optional[str] = Field(default=None)
dielectric_extent_size: Optional[PaddingData] = Field(default=None)
honor_user_dielectric: bool = Field(default=False)

extent_type: Optional[Any] = Field(default=None)
base_polygon: Optional[Any] = Field(default=None)
truncate_air_box_at_ground: Optional[Any] = Field(default=None)
air_box_horizontal_extent: Optional[PaddingData] = Field(default=None)
air_box_positive_vertical_extent: Optional[PaddingData] = Field(default=None)
air_box_negative_vertical_extent: Optional[PaddingData] = Field(default=None)
sync_air_box_vertical_extent: Optional[bool] = Field(default=None)

@classmethod
def create(cls, **kwargs):
return cls(**kwargs)
2 changes: 1 addition & 1 deletion src/pyedb/configuration/cfg_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __init__(self, pedb, **kwargs):
self._pedb = pedb
self.general = CfgGeneral(self._pedb, kwargs.get("general", {}))

self.boundaries = CfgBoundaries(self._pedb, kwargs.get("boundaries", {}))
self.boundaries = CfgBoundaries.create(**kwargs.get("boundaries", {}))

self.nets = CfgNets(
self._pedb,
Expand Down
84 changes: 76 additions & 8 deletions src/pyedb/configuration/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,7 @@ def run(self, **kwargs):
if self.cfg_data.general:
self.cfg_data.general.apply()

# Configure boundary settings
if self.cfg_data.boundaries:
self.__apply_with_logging("Updating boundaries", self.cfg_data.boundaries.apply)
self.apply_boundaries()

if self.cfg_data.nets:
self.__apply_with_logging("Updating nets", self.cfg_data.nets.apply)
Expand Down Expand Up @@ -154,6 +152,75 @@ def run(self, **kwargs):

return True

def apply_boundaries(self):
boundaries = self.cfg_data.boundaries
info = self._pedb.hfss.hfss_extent_info

# Simple direct-assign attributes:
attr_map = {
"use_open_region": "use_open_region",
"open_region_type": "open_region_type",
"is_pml_visible": "is_pml_visible",
"operating_freq": "operating_freq",
"pml_radiation_factor": "pml_radiation_factor",
"dielectric_extent_type": "dielectric_extent_type",
"honor_user_dielectric": "honor_user_dielectric",
"extent_type": "extent_type",
"truncate_air_box_at_ground": "truncate_air_box_at_ground",
"base_polygon": "base_polygon",
"dielectric_base_polygon": "dielectric_base_polygon",
"sync_air_box_vertical_extent": "sync_air_box_vertical_extent",
}

for b_attr, info_attr in attr_map.items():
value = getattr(boundaries, b_attr, None)
if value is not None:
# Lowercase only string-based HFSS enum-like values
if b_attr in ("open_region_type", "dielectric_extent_type", "extent_type") and isinstance(value, str):
value = value.lower()
if hasattr(info, info_attr):
setattr(info, info_attr, value)
else: # pragma: no cover
raise AttributeError(f"Attribute {info_attr} not found in {info.__name__}")

# Attributes requiring specific setter functions
if boundaries.dielectric_extent_size:
info.set_dielectric_extent(**boundaries.dielectric_extent_size.model_dump())

if boundaries.air_box_horizontal_extent:
info.set_air_box_horizontal_extent(**boundaries.air_box_horizontal_extent.model_dump())

if boundaries.air_box_positive_vertical_extent:
info.set_air_box_positive_vertical_extent(**boundaries.air_box_positive_vertical_extent.model_dump())

if boundaries.air_box_negative_vertical_extent:
info.set_air_box_negative_vertical_extent(**boundaries.air_box_negative_vertical_extent.model_dump())

def get_boundaries(self):
boundaries = self.cfg_data.boundaries
edb_hfss_extent_info = self._pedb.hfss.hfss_extent_info

boundaries.use_open_region = edb_hfss_extent_info.use_open_region
boundaries.open_region_type = edb_hfss_extent_info.open_region_type
boundaries.is_pml_visible = edb_hfss_extent_info.is_pml_visible
boundaries.operating_freq = edb_hfss_extent_info.operating_freq
boundaries.pml_radiation_factor = edb_hfss_extent_info.pml_radiation_factor
boundaries.dielectric_extent_type = edb_hfss_extent_info.dielectric_extent_type
size, is_multiple = edb_hfss_extent_info.get_dielectric_extent()
boundaries.dielectric_extent_size = {"size": size, "is_multiple": is_multiple}
boundaries.honor_user_dielectric = edb_hfss_extent_info.honor_user_dielectric
boundaries.extent_type = edb_hfss_extent_info.extent_type
boundaries.truncate_air_box_at_ground = edb_hfss_extent_info.truncate_air_box_at_ground
size, is_multiple = edb_hfss_extent_info.get_air_box_horizontal_extent()
boundaries.air_box_horizontal_extent = {"size": size, "is_multiple": is_multiple}
size, is_multiple = edb_hfss_extent_info.get_air_box_positive_vertical_extent()
boundaries.air_box_positive_vertical_extent = {"size": size, "is_multiple": is_multiple}
size, is_multiple = edb_hfss_extent_info.get_air_box_negative_vertical_extent()
boundaries.air_box_negative_vertical_extent = {"size": size, "is_multiple": is_multiple}
boundaries.base_polygon = edb_hfss_extent_info.base_polygon
boundaries.dielectric_base_polygon = edb_hfss_extent_info.dielectric_base_polygon
boundaries.sync_air_box_vertical_extent = edb_hfss_extent_info.sync_air_box_vertical_extent

def apply_modeler(self):
modeler = self.cfg_data.modeler
if modeler.traces:
Expand Down Expand Up @@ -419,17 +486,17 @@ def get_data_from_db(self, **kwargs):

"""
self._pedb.logger.info("Getting data from layout database.")
self.get_variables()

self.get_materials()
self.get_stackup()
self.get_operations()

data = {}
if kwargs.get("general", False):
data["general"] = self.cfg_data.general.get_data_from_db()
if kwargs.get("variables", False):
self.get_variables()
data.update(self.cfg_data.variables.model_dump(exclude_none=True))
if kwargs.get("stackup", False):
self.get_stackup()
data["stackup"] = self.cfg_data.stackup.model_dump(exclude_none=True)
if kwargs.get("package_definitions", False):
data["package_definitions"] = self.cfg_data.package_definitions.get_data_from_db()
Expand Down Expand Up @@ -461,6 +528,7 @@ def get_data_from_db(self, **kwargs):
if kwargs.get("pin_groups", False):
data["pin_groups"] = self.cfg_data.pin_groups.get_data_from_db()
if kwargs.get("operations", False):
self.get_operations()
data["operations"] = self.cfg_data.operations.model_dump()
if kwargs.get("padstacks", False):
self.cfg_data.padstacks.retrieve_parameters_from_edb()
Expand All @@ -475,8 +543,8 @@ def get_data_from_db(self, **kwargs):
data["padstacks"]["instances"] = instances

if kwargs.get("boundaries", False):
data["boundaries"] = self.cfg_data.boundaries.get_data_from_db()

self.get_boundaries()
data["boundaries"] = self.cfg_data.boundaries.model_dump(exclude_none=True)
return data

@execution_timer("Applying operations")
Expand Down
60 changes: 54 additions & 6 deletions src/pyedb/dotnet/database/edb_data/hfss_extent_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from pyedb.dotnet.database.edb_data.edbvalue import EdbValue
from pyedb.dotnet.database.edb_data.primitives_data import cast
from pyedb.dotnet.database.general import convert_pytuple_to_nettuple, pascal_to_snake
from pyedb.dotnet.database.utilities.value import Value


class HfssExtentInfo:
Expand Down Expand Up @@ -85,6 +86,15 @@ def air_box_horizontal_extent(self, value):
info.AirBoxHorizontalExtent = convert_pytuple_to_nettuple((value, self.air_box_horizontal_extent_enabled))
self._update_hfss_extent_info(info)

def set_air_box_horizontal_extent(self, size: float, is_multiple: bool = True):
info = self._edb_hfss_extent_info
info.AirBoxHorizontalExtent = convert_pytuple_to_nettuple((size, is_multiple))
self._update_hfss_extent_info(info)

def get_air_box_horizontal_extent(self):
info = self._edb_hfss_extent_info
return info.AirBoxHorizontalExtent.Item1, info.AirBoxHorizontalExtent.Item2

@property
def air_box_positive_vertical_extent_enabled(self):
"""Whether positive vertical extent is enabled for the air box."""
Expand All @@ -110,6 +120,15 @@ def air_box_positive_vertical_extent(self, value):
)
self._update_hfss_extent_info(info)

def set_air_box_positive_vertical_extent(self, size: float, is_multiple: bool = True):
info = self._edb_hfss_extent_info
info.AirBoxPositiveVerticalExtent = convert_pytuple_to_nettuple((size, is_multiple))
self._update_hfss_extent_info(info)

def get_air_box_positive_vertical_extent(self):
info = self._edb_hfss_extent_info
return info.AirBoxPositiveVerticalExtent.Item1, info.AirBoxPositiveVerticalExtent.Item2

@property
def air_box_negative_vertical_extent_enabled(self):
"""Whether negative vertical extent is enabled for the air box."""
Expand All @@ -135,6 +154,15 @@ def air_box_negative_vertical_extent(self, value):
)
self._update_hfss_extent_info(info)

def set_air_box_negative_vertical_extent(self, size: float, is_multiple: bool = True):
info = self._edb_hfss_extent_info
info.AirBoxNegativeVerticalExtent = convert_pytuple_to_nettuple((size, is_multiple))
self._update_hfss_extent_info(info)

def get_air_box_negative_vertical_extent(self):
info = self._edb_hfss_extent_info
return info.AirBoxNegativeVerticalExtent.Item1, info.AirBoxNegativeVerticalExtent.Item2

@property
def base_polygon(self):
"""Base polygon.
Expand All @@ -143,12 +171,13 @@ def base_polygon(self):
-------
:class:`dotnet.database.edb_data.primitives_data.EDBPrimitive`
"""
return cast(self._edb_hfss_extent_info.BasePolygon, self._pedb)
return cast(self._edb_hfss_extent_info.BasePolygon, self._pedb).aedt_name

@base_polygon.setter
def base_polygon(self, value):
info = self._edb_hfss_extent_info
info.BasePolygon = value.primitive_object
obj = self._pedb.layout.find_primitive(name=value)[0]
info.BasePolygon = obj._edb_object
self._update_hfss_extent_info(info)

@property
Expand All @@ -159,12 +188,13 @@ def dielectric_base_polygon(self):
-------
:class:`dotnet.database.edb_data.primitives_data.EDBPrimitive`
"""
return cast(self._edb_hfss_extent_info.DielectricBasePolygon, self._pedb)
return cast(self._edb_hfss_extent_info.DielectricBasePolygon, self._pedb).aedt_name

@dielectric_base_polygon.setter
def dielectric_base_polygon(self, value):
info = self._edb_hfss_extent_info
info.DielectricBasePolygon = value.primitive_object
obj = self._pedb.layout.find_primitive(name=value)[0]
info.DielectricBasePolygon = obj.primitive_object
self._update_hfss_extent_info(info)

@property
Expand All @@ -189,6 +219,15 @@ def dielectric_extent_size(self, value):
info.DielectricExtentSize = convert_pytuple_to_nettuple((value, self.dielectric_extent_size_enabled))
self._update_hfss_extent_info(info)

def set_dielectric_extent(self, size: float, is_multiple: bool = True):
hfss_extent = self._edb_hfss_extent_info
hfss_extent.DielectricExtentSize = convert_pytuple_to_nettuple((size, is_multiple))
self._update_hfss_extent_info(hfss_extent)

def get_dielectric_extent(self):
hfss_extent = self._edb_hfss_extent_info
return hfss_extent.DielectricExtentSize.Item1, hfss_extent.DielectricExtentSize.Item2

@property
def dielectric_extent_type(self):
"""Dielectric extent type."""
Expand Down Expand Up @@ -253,7 +292,7 @@ def operating_freq(self):
-------
pyedb.dotnet.database.edb_data.edbvalue.EdbValue
"""
return EdbValue(self._edb_hfss_extent_info.OperatingFreq)
return Value(self._pedb, self._edb_hfss_extent_info.OperatingFreq)

@operating_freq.setter
def operating_freq(self, value):
Expand All @@ -265,7 +304,7 @@ def operating_freq(self, value):
@property
def radiation_level(self):
"""PML Radiation level to calculate the thickness of boundary."""
return EdbValue(self._edb_hfss_extent_info.RadiationLevel)
return Value(self._pedb, self._edb_hfss_extent_info.RadiationLevel)

@radiation_level.setter
def radiation_level(self, value):
Expand All @@ -274,6 +313,15 @@ def radiation_level(self, value):
info.RadiationLevel = value
self._update_hfss_extent_info(info)

@property
def pml_radiation_factor(self):
"""PML Radiation level to calculate the thickness of boundary."""
return self.radiation_level

@pml_radiation_factor.setter
def pml_radiation_factor(self, value):
self.radiation_level = value

@property
def sync_air_box_vertical_extent(self):
"""Vertical extent of the sync air box."""
Expand Down
Loading
Loading