From 4ab7475ca3ea6cc30378019227dca739d69eb610 Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro <77293250+maxcapodi78@users.noreply.github.com> Date: Wed, 11 Dec 2024 14:44:57 +0100 Subject: [PATCH] FEAT: Use GetDataModel to improve the way information are cached into the API (#5488) Co-authored-by: Samuelopez-ansys --- src/ansys/aedt/core/application/__init__.py | 33 + src/ansys/aedt/core/application/analysis.py | 14 +- .../aedt/core/application/analysis_nexxim.py | 16 +- src/ansys/aedt/core/application/design.py | 15 +- src/ansys/aedt/core/application/variables.py | 2 +- src/ansys/aedt/core/circuit.py | 14 +- src/ansys/aedt/core/generic/configurations.py | 8 +- src/ansys/aedt/core/generic/data_handlers.py | 55 - src/ansys/aedt/core/hfss.py | 62 +- src/ansys/aedt/core/hfss3dlayout.py | 16 +- src/ansys/aedt/core/icepak.py | 24 +- src/ansys/aedt/core/maxwell.py | 15 +- src/ansys/aedt/core/mechanical.py | 2 +- .../aedt/core/modeler/cad/elements_3d.py | 65 +- src/ansys/aedt/core/modeler/cad/polylines.py | 60 +- src/ansys/aedt/core/modeler/cad/primitives.py | 48 +- .../aedt/core/modeler/cad/primitives_3d.py | 2 +- .../modeler/circuits/primitives_nexxim.py | 2 +- src/ansys/aedt/core/modules/boundary.py | 5991 ----------------- .../aedt/core/modules/boundary/__init__.py | 23 + .../core/modules/boundary/circuit_boundary.py | 2630 ++++++++ .../aedt/core/modules/boundary/common.py | 715 ++ .../core/modules/boundary/hfss_boundary.py | 521 ++ .../core/modules/boundary/icepak_boundary.py | 263 + .../core/modules/boundary/layout_boundary.py | 1342 ++++ .../core/modules/boundary/maxwell_boundary.py | 352 + .../core/modules/boundary/q3d_boundary.py | 267 + src/ansys/aedt/core/modules/mesh.py | 150 +- src/ansys/aedt/core/modules/mesh_icepak.py | 75 +- src/ansys/aedt/core/modules/solve_setup.py | 183 +- src/ansys/aedt/core/q3d.py | 6 +- src/ansys/aedt/core/rmxprt.py | 17 +- .../aedt/core/visualization/post/common.py | 46 +- .../aedt/core/visualization/report/common.py | 381 +- .../aedt/core/visualization/report/emi.py | 16 +- .../aedt/core/visualization/report/eye.py | 143 +- .../aedt/core/visualization/report/field.py | 20 +- .../core/visualization/report/standard.py | 54 +- src/pyaedt/modules/Boundary.py | 2 +- tests/system/general/conftest.py | 5 + tests/system/general/test_04_SBR.py | 2 +- tests/system/general/test_07_Object3D.py | 58 +- .../general/test_12_1_PostProcessing.py | 2 +- tests/system/general/test_20_HFSS.py | 4 +- tests/system/general/test_28_Maxwell3D.py | 18 +- tests/system/general/test_30_Q2D.py | 4 +- .../general/test_41_3dlayout_modeler.py | 13 +- tests/system/general/test_98_Icepak.py | 14 +- 48 files changed, 6993 insertions(+), 6777 deletions(-) delete mode 100644 src/ansys/aedt/core/modules/boundary.py create mode 100644 src/ansys/aedt/core/modules/boundary/__init__.py create mode 100644 src/ansys/aedt/core/modules/boundary/circuit_boundary.py create mode 100644 src/ansys/aedt/core/modules/boundary/common.py create mode 100644 src/ansys/aedt/core/modules/boundary/hfss_boundary.py create mode 100644 src/ansys/aedt/core/modules/boundary/icepak_boundary.py create mode 100644 src/ansys/aedt/core/modules/boundary/layout_boundary.py create mode 100644 src/ansys/aedt/core/modules/boundary/maxwell_boundary.py create mode 100644 src/ansys/aedt/core/modules/boundary/q3d_boundary.py diff --git a/src/ansys/aedt/core/application/__init__.py b/src/ansys/aedt/core/application/__init__.py index 9c4476773da..a83f6a966d8 100644 --- a/src/ansys/aedt/core/application/__init__.py +++ b/src/ansys/aedt/core/application/__init__.py @@ -21,3 +21,36 @@ # 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 ansys.aedt.core.generic.general_methods import pyaedt_function_handler + + +@pyaedt_function_handler() +def _get_data_model(child_object, level=-1): + import json + + def _fix_dict(p_list, p_out): + for p, val in p_list.items(): + if p == "properties": + for prop in val: + if "value" in prop: + p_out[prop["name"]] = prop["value"] + elif "values" in prop: + p_out[prop["name"]] = prop["values"] + else: + p_out[prop["name"]] = None + elif isinstance(val, dict): + _fix_dict(val, p_out[p]) + elif isinstance(val, list): + p_out[p] = [] + for el in val: + children = {} + _fix_dict(el, children) + p_out[p].append(children) + else: + p_out[p] = val + + input_str = child_object.GetDataModel(level, 1, 1) + props_list = json.loads(input_str) + props = {} + _fix_dict(props_list, props) + return props diff --git a/src/ansys/aedt/core/application/analysis.py b/src/ansys/aedt/core/application/analysis.py index 63f7fe13a60..6f7460d032e 100644 --- a/src/ansys/aedt/core/application/analysis.py +++ b/src/ansys/aedt/core/application/analysis.py @@ -55,9 +55,9 @@ from ansys.aedt.core.generic.general_methods import open_file from ansys.aedt.core.generic.general_methods import pyaedt_function_handler from ansys.aedt.core.generic.settings import settings -from ansys.aedt.core.modules.boundary import MaxwellParameters -from ansys.aedt.core.modules.boundary import NativeComponentObject -from ansys.aedt.core.modules.boundary import NativeComponentPCB +from ansys.aedt.core.modules.boundary.layout_boundary import NativeComponentObject +from ansys.aedt.core.modules.boundary.layout_boundary import NativeComponentPCB +from ansys.aedt.core.modules.boundary.maxwell_boundary import MaxwellParameters from ansys.aedt.core.modules.design_xploration import OptimizationSetups from ansys.aedt.core.modules.design_xploration import ParametricSetups from ansys.aedt.core.modules.solve_setup import Setup @@ -1537,14 +1537,16 @@ def get_setup(self, name): setup = SetupSBR(self, setuptype, name, is_new_setup=False) elif self.design_type in ["Q3D Extractor", "2D Extractor", "HFSS"]: setup = SetupHFSS(self, setuptype, name, is_new_setup=False) - if setup.props and setup.props.get("SetupType", "") == "HfssDrivenAuto": + if setup.properties: + if "Auto Solver Setting" in setup.properties: + setup = SetupHFSSAuto(self, 0, name, is_new_setup=False) + elif setup.props and setup.props.get("SetupType", "") == "HfssDrivenAuto": setup = SetupHFSSAuto(self, 0, name, is_new_setup=False) elif self.design_type in ["Maxwell 2D", "Maxwell 3D"]: setup = SetupMaxwell(self, setuptype, name, is_new_setup=False) else: setup = Setup(self, setuptype, name, is_new_setup=False) - if setup.props: - self.active_setup = name + self.active_setup = name return setup @pyaedt_function_handler() diff --git a/src/ansys/aedt/core/application/analysis_nexxim.py b/src/ansys/aedt/core/application/analysis_nexxim.py index 08f9d93a8a1..ace967c2427 100644 --- a/src/ansys/aedt/core/application/analysis_nexxim.py +++ b/src/ansys/aedt/core/application/analysis_nexxim.py @@ -26,14 +26,14 @@ from ansys.aedt.core.generic.general_methods import pyaedt_function_handler from ansys.aedt.core.generic.settings import settings from ansys.aedt.core.modeler.circuits.object_3d_circuit import CircuitComponent -from ansys.aedt.core.modules.boundary import CurrentSinSource -from ansys.aedt.core.modules.boundary import Excitations -from ansys.aedt.core.modules.boundary import PowerIQSource -from ansys.aedt.core.modules.boundary import PowerSinSource -from ansys.aedt.core.modules.boundary import Sources -from ansys.aedt.core.modules.boundary import VoltageDCSource -from ansys.aedt.core.modules.boundary import VoltageFrequencyDependentSource -from ansys.aedt.core.modules.boundary import VoltageSinSource +from ansys.aedt.core.modules.boundary.circuit_boundary import CurrentSinSource +from ansys.aedt.core.modules.boundary.circuit_boundary import Excitations +from ansys.aedt.core.modules.boundary.circuit_boundary import PowerIQSource +from ansys.aedt.core.modules.boundary.circuit_boundary import PowerSinSource +from ansys.aedt.core.modules.boundary.circuit_boundary import Sources +from ansys.aedt.core.modules.boundary.circuit_boundary import VoltageDCSource +from ansys.aedt.core.modules.boundary.circuit_boundary import VoltageFrequencyDependentSource +from ansys.aedt.core.modules.boundary.circuit_boundary import VoltageSinSource from ansys.aedt.core.modules.setup_templates import SetupKeys from ansys.aedt.core.modules.solve_setup import SetupCircuit diff --git a/src/ansys/aedt/core/application/design.py b/src/ansys/aedt/core/application/design.py index 5ac17103e10..e56b0ae77e7 100644 --- a/src/ansys/aedt/core/application/design.py +++ b/src/ansys/aedt/core/application/design.py @@ -77,9 +77,9 @@ from ansys.aedt.core.generic.general_methods import settings from ansys.aedt.core.generic.general_methods import write_csv from ansys.aedt.core.generic.load_aedt_file import load_entire_aedt_file -from ansys.aedt.core.modules.boundary import BoundaryObject -from ansys.aedt.core.modules.boundary import MaxwellParameters -from ansys.aedt.core.modules.boundary import NetworkObject +from ansys.aedt.core.modules.boundary.circuit_boundary import NetworkObject +from ansys.aedt.core.modules.boundary.common import BoundaryObject +from ansys.aedt.core.modules.boundary.maxwell_boundary import MaxwellParameters if sys.version_info.major > 2: import base64 @@ -376,6 +376,12 @@ def boundaries(self): current_excitation_types = ee[1::2] ff = [i.split(":")[0] for i in ee] bb.extend(ff) + for i in set(current_excitation_types): + if "GetExcitationsOfType" in self.oboundary.__dir__(): + ports = list(self.oboundary.GetExcitationsOfType(i)) + for p in ports: + bb.append(p) + bb.append(i) elif ( self.oboundary and "Excitations" in self.get_oo_name(self.odesign) @@ -431,6 +437,7 @@ def boundaries(self): del self._boundaries[k] for boundary, boundarytype in zip(current_boundaries, current_types): if boundary in self._boundaries: + self._boundaries[boundary]._initialize_bynary_tree() continue if boundarytype == "MaxwellParameters": maxwell_parameter_type = self.get_oo_property_value(self.odesign, f"Parameters\\{boundary}", "Type") @@ -3796,7 +3803,7 @@ def save_project(self, file_name=None, overwrite=True, refresh_ids=False): self.oproject.Save() if refresh_ids: self.modeler.refresh_all_ids() - self.modeler._refresh_all_ids_from_aedt_file() + self.modeler._refresh_all_ids_wrapper() self.mesh._refresh_mesh_operations() self._project_name = None self._project_path = None diff --git a/src/ansys/aedt/core/application/variables.py b/src/ansys/aedt/core/application/variables.py index f15dcb46b09..e3e73e487f2 100644 --- a/src/ansys/aedt/core/application/variables.py +++ b/src/ansys/aedt/core/application/variables.py @@ -1353,7 +1353,7 @@ def _find_used_variable_history(self, history, var_name): """ used = False - for _, v in history.props.items(): + for _, v in history.properties.items(): if isinstance(v, str) and var_name in re.findall("[a-zA-Z0-9_]+", v): return True for el in history.children.values(): diff --git a/src/ansys/aedt/core/circuit.py b/src/ansys/aedt/core/circuit.py index caee9b0ea2e..beb3fbb4b97 100644 --- a/src/ansys/aedt/core/circuit.py +++ b/src/ansys/aedt/core/circuit.py @@ -47,13 +47,13 @@ from ansys.aedt.core.generic.general_methods import read_configuration_file from ansys.aedt.core.generic.settings import settings from ansys.aedt.core.hfss3dlayout import Hfss3dLayout -from ansys.aedt.core.modules.boundary import CurrentSinSource -from ansys.aedt.core.modules.boundary import PowerIQSource -from ansys.aedt.core.modules.boundary import PowerSinSource -from ansys.aedt.core.modules.boundary import Sources -from ansys.aedt.core.modules.boundary import VoltageDCSource -from ansys.aedt.core.modules.boundary import VoltageFrequencyDependentSource -from ansys.aedt.core.modules.boundary import VoltageSinSource +from ansys.aedt.core.modules.boundary.circuit_boundary import CurrentSinSource +from ansys.aedt.core.modules.boundary.circuit_boundary import PowerIQSource +from ansys.aedt.core.modules.boundary.circuit_boundary import PowerSinSource +from ansys.aedt.core.modules.boundary.circuit_boundary import Sources +from ansys.aedt.core.modules.boundary.circuit_boundary import VoltageDCSource +from ansys.aedt.core.modules.boundary.circuit_boundary import VoltageFrequencyDependentSource +from ansys.aedt.core.modules.boundary.circuit_boundary import VoltageSinSource from ansys.aedt.core.modules.circuit_templates import SourceKeys diff --git a/src/ansys/aedt/core/generic/configurations.py b/src/ansys/aedt/core/generic/configurations.py index 1ebb0ae56cc..ed36cf50666 100644 --- a/src/ansys/aedt/core/generic/configurations.py +++ b/src/ansys/aedt/core/generic/configurations.py @@ -43,10 +43,10 @@ from ansys.aedt.core.modeler.cad.components_3d import UserDefinedComponent from ansys.aedt.core.modeler.cad.modeler import CoordinateSystem from ansys.aedt.core.modeler.geometry_operators import GeometryOperators -from ansys.aedt.core.modules.boundary import BoundaryObject -from ansys.aedt.core.modules.boundary import BoundaryProps -from ansys.aedt.core.modules.boundary import NativeComponentObject -from ansys.aedt.core.modules.boundary import NativeComponentPCB +from ansys.aedt.core.modules.boundary.common import BoundaryObject +from ansys.aedt.core.modules.boundary.common import BoundaryProps +from ansys.aedt.core.modules.boundary.layout_boundary import NativeComponentObject +from ansys.aedt.core.modules.boundary.layout_boundary import NativeComponentPCB from ansys.aedt.core.modules.design_xploration import SetupOpti from ansys.aedt.core.modules.design_xploration import SetupParam from ansys.aedt.core.modules.material_lib import Material diff --git a/src/ansys/aedt/core/generic/data_handlers.py b/src/ansys/aedt/core/generic/data_handlers.py index adc72547b78..095c7482c0c 100644 --- a/src/ansys/aedt/core/generic/data_handlers.py +++ b/src/ansys/aedt/core/generic/data_handlers.py @@ -205,61 +205,6 @@ def _arg2dict(arg, dict_out): raise ValueError("Incorrect data argument format") -@pyaedt_function_handler() -def create_list_for_csharp(input_list, return_strings=False): - """ - - Parameters - ---------- - input_list : - - return_strings : - (Default value = False) - - Returns - ------- - - """ - from ansys.aedt.core.generic.clr_module import Double - from ansys.aedt.core.generic.clr_module import List - - if return_strings: - col = List[str]() - else: - col = List[Double]() - - for el in input_list: - if return_strings: - col.Add(str(el)) - else: - col.Add(el) - return col - - -@pyaedt_function_handler() -def create_table_for_csharp(input_list_of_list, return_strings=True): - """ - - Parameters - ---------- - input_list_of_list : - - return_strings : - (Default value = True) - - Returns - ------- - - """ - from ansys.aedt.core.generic.clr_module import List - - new_table = List[List[str]]() - for col in input_list_of_list: - newcol = create_list_for_csharp(col, return_strings) - new_table.Add(newcol) - return new_table - - @pyaedt_function_handler() def format_decimals(el): """ diff --git a/src/ansys/aedt/core/hfss.py b/src/ansys/aedt/core/hfss.py index 95a0539c398..9dc8dc09b1d 100644 --- a/src/ansys/aedt/core/hfss.py +++ b/src/ansys/aedt/core/hfss.py @@ -22,7 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -"""This module contains these classes: ``Hfss`` and ``BoundaryType``.""" +"""This module contains the ``Hfss`` class.""" from __future__ import absolute_import # noreorder @@ -49,10 +49,10 @@ from ansys.aedt.core.modeler.cad.component_array import ComponentArray from ansys.aedt.core.modeler.cad.components_3d import UserDefinedComponent from ansys.aedt.core.modeler.geometry_operators import GeometryOperators -from ansys.aedt.core.modules.boundary import BoundaryObject -from ansys.aedt.core.modules.boundary import FarFieldSetup -from ansys.aedt.core.modules.boundary import NativeComponentObject -from ansys.aedt.core.modules.boundary import NearFieldSetup +from ansys.aedt.core.modules.boundary.common import BoundaryObject +from ansys.aedt.core.modules.boundary.hfss_boundary import FarFieldSetup +from ansys.aedt.core.modules.boundary.hfss_boundary import NearFieldSetup +from ansys.aedt.core.modules.boundary.layout_boundary import NativeComponentObject from ansys.aedt.core.modules.setup_templates import SetupKeys @@ -249,10 +249,17 @@ def field_setups(self): Returns ------- List of :class:`ansys.aedt.core.modules.boundary.FarFieldSetup` and - :class:`ansys.aedt.core.modules.boundary.NearFieldSetup` + :class:`ansys.aedt.core.modules.hfss_boundary.NearFieldSetup` """ - if not self._field_setups: - self._field_setups = self._get_rad_fields() + for field in self.field_setup_names: + obj_field = self.odesign.GetChildObject("Radiation").GetChildObject(field) + type_field = obj_field.GetPropValue("Type") + if type_field == "Infinite Sphere": + self._field_setups.append(FarFieldSetup(self, field, {}, "FarFieldSphere")) + else: + self._field_setups.append(NearFieldSetup(self, field, {}, f"NearField{type_field}")) + # if not self._field_setups: + # self._field_setups = self._get_rad_fields() return self._field_setups @property @@ -372,31 +379,6 @@ def _get_unique_source_name(self, source_name, root_name): source_name = generate_unique_name(source_name) return source_name - @pyaedt_function_handler() - def _get_rad_fields(self): - if not self.design_properties: - return [] - fields = [] - if self.design_properties.get("RadField"): - if self.design_properties["RadField"].get("FarFieldSetups"): - for val in self.design_properties["RadField"]["FarFieldSetups"]: - p = self.design_properties["RadField"]["FarFieldSetups"][val] - if isinstance(p, dict) and p.get("Type") == "Infinite Sphere": - fields.append(FarFieldSetup(self, val, p, "FarFieldSphere")) - if self.design_properties["RadField"].get("NearFieldSetups"): - for val in self.design_properties["RadField"]["NearFieldSetups"]: - p = self.design_properties["RadField"]["NearFieldSetups"][val] - if isinstance(p, dict): - if p["Type"] == "Near Rectangle": - fields.append(NearFieldSetup(self, val, p, "NearFieldRectangle")) - elif p["Type"] == "Near Line": - fields.append(NearFieldSetup(self, val, p, "NearFieldLine")) - elif p["Type"] == "Near Box": - fields.append(NearFieldSetup(self, val, p, "NearFieldBox")) - elif p["Type"] == "Near Sphere": - fields.append(NearFieldSetup(self, val, p, "NearFieldSphere")) - return fields - @pyaedt_function_handler() def _create_boundary(self, name, props, boundary_type): """Create a boundary. @@ -1482,7 +1464,7 @@ def create_sbr_antenna( Returns ------- - :class:`ansys.aedt.core.modules.boundary.NativeComponentObject` + :class:`ansys.aedt.core.modules.layout_boundary.NativeComponentObject` NativeComponentObject object. References @@ -1608,7 +1590,7 @@ def create_sbr_file_based_antenna( Returns ------- - :class:`ansys.aedt.core.modules.boundary.NativeComponentObject` + :class:`ansys.aedt.core.modules.layout_boundary.NativeComponentObject` References ---------- @@ -5139,7 +5121,7 @@ def insert_infinite_sphere( Returns ------- - :class:`ansys.aedt.core.modules.boundary.FarFieldSetup` + :class:`ansys.aedt.core.modules.hfss_boundary.FarFieldSetup` """ if not self.oradfield: self.logger.error("Radiation Field not available in this solution.") @@ -5231,7 +5213,7 @@ def insert_near_field_sphere( Returns ------- - :class:`ansys.aedt.core.modules.boundary.NearFieldSetup` + :class:`ansys.aedt.core.modules.hfss_boundary.NearFieldSetup` """ if not self.oradfield: self.logger.error("Radiation Field not available in this solution.") @@ -5308,7 +5290,7 @@ def insert_near_field_box( Returns ------- - :class:`ansys.aedt.core.modules.boundary.NearFieldSetup` + :class:`ansys.aedt.core.modules.hfss_boundary.NearFieldSetup` """ if not self.oradfield: self.logger.error("Radiation Field not available in this solution.") @@ -5377,7 +5359,7 @@ def insert_near_field_rectangle( Returns ------- - :class:`ansys.aedt.core.modules.boundary.NearFieldSetup` + :class:`ansys.aedt.core.modules.hfss_boundary.NearFieldSetup` """ if not self.oradfield: self.logger.error("Radiation Field not available in this solution.") @@ -5432,7 +5414,7 @@ def insert_near_field_line( Returns ------- - :class:`ansys.aedt.core.modules.boundary.NearFieldSetup` + :class:`ansys.aedt.core.modules.hfss_boundary.NearFieldSetup` """ if not self.oradfield: self.logger.error("Radiation Field not available in this solution.") diff --git a/src/ansys/aedt/core/hfss3dlayout.py b/src/ansys/aedt/core/hfss3dlayout.py index 9c2a7065103..4f0b9e62211 100644 --- a/src/ansys/aedt/core/hfss3dlayout.py +++ b/src/ansys/aedt/core/hfss3dlayout.py @@ -41,7 +41,7 @@ from ansys.aedt.core.generic.general_methods import tech_to_control_file from ansys.aedt.core.generic.settings import settings from ansys.aedt.core.modeler.pcb.object_3d_layout import Line3dLayout # noqa: F401 -from ansys.aedt.core.modules.boundary import BoundaryObject3dLayout +from ansys.aedt.core.modules.boundary.layout_boundary import BoundaryObject3dLayout class Hfss3dLayout(FieldAnalysis3DLayout, ScatteringMethods): @@ -254,7 +254,7 @@ def create_edge_port( Returns ------- - :class:`ansys.aedt.core.modules.boundary.BoundaryObject3dLayout` + :class:`ansys.aedt.core.modules.layout_boundary.BoundaryObject3dLayout` Port objcet port when successful, ``False`` when failed. References @@ -354,7 +354,7 @@ def create_wave_port( Returns ------- - :class:`ansys.aedt.core.modules.boundary.BoundaryObject3dLayout` + :class:`ansys.aedt.core.modules.layout_boundary.BoundaryObject3dLayout` Port objcet port when successful, ``False`` when failed. References @@ -395,7 +395,7 @@ def create_wave_port_from_two_conductors(self, assignment=None, edge_numbers=Non Returns ------- - :class:`ansys.aedt.core.modules.boundary.BoundaryObject3dLayout` + :class:`ansys.aedt.core.modules.layout_boundary.BoundaryObject3dLayout` Port objcet port when successful, ``False`` when failed. References @@ -473,7 +473,7 @@ def create_ports_on_component_by_nets( Returns ------- - list of :class:`ansys.aedt.core.modules.boundary.BoundaryObject3dLayout` + list of :class:`ansys.aedt.core.modules.layout_boundary.BoundaryObject3dLayout` Port Objects when successful. References @@ -550,7 +550,7 @@ def create_differential_port(self, via_signal, via_reference, name, deembed=True Returns ------- - :class:`ansys.aedt.core.modules.boundary.BoundaryObject3dLayout` + :class:`ansys.aedt.core.modules.layout_boundary.BoundaryObject3dLayout` Port Object when successful, ``False`` when failed. References @@ -599,7 +599,7 @@ def create_coax_port(self, via, radial_extent=0.1, layer=None, alignment="lower" Returns ------- - :class:`ansys.aedt.core.modules.boundary.BoundaryObject3dLayout` + :class:`ansys.aedt.core.modules.layout_boundary.BoundaryObject3dLayout` Port Object when successful, ``False`` when failed. References @@ -652,7 +652,7 @@ def create_pin_port(self, name, x=0, y=0, rotation=0, top_layer=None, bottom_lay Returns ------- - :class:`ansys.aedt.core.modules.boundary.BoundaryObject3dLayout` + :class:`ansys.aedt.core.modules.layout_boundary.BoundaryObject3dLayout` ``True`` when successful, ``False`` when failed. diff --git a/src/ansys/aedt/core/icepak.py b/src/ansys/aedt/core/icepak.py index 81e899944fd..f65e6f34ed7 100644 --- a/src/ansys/aedt/core/icepak.py +++ b/src/ansys/aedt/core/icepak.py @@ -48,18 +48,18 @@ from ansys.aedt.core.modeler.cad.elements_3d import FacePrimitive from ansys.aedt.core.modeler.geometry_operators import GeometryOperators from ansys.aedt.core.modeler.geometry_operators import GeometryOperators as go -from ansys.aedt.core.modules.boundary import BoundaryDictionary -from ansys.aedt.core.modules.boundary import BoundaryObject -from ansys.aedt.core.modules.boundary import ExponentialDictionary -from ansys.aedt.core.modules.boundary import LinearDictionary -from ansys.aedt.core.modules.boundary import NativeComponentObject -from ansys.aedt.core.modules.boundary import NativeComponentPCB -from ansys.aedt.core.modules.boundary import NetworkObject -from ansys.aedt.core.modules.boundary import PieceWiseLinearDictionary -from ansys.aedt.core.modules.boundary import PowerLawDictionary -from ansys.aedt.core.modules.boundary import SinusoidalDictionary -from ansys.aedt.core.modules.boundary import SquareWaveDictionary -from ansys.aedt.core.modules.boundary import _create_boundary +from ansys.aedt.core.modules.boundary.circuit_boundary import NetworkObject +from ansys.aedt.core.modules.boundary.common import BoundaryObject +from ansys.aedt.core.modules.boundary.icepak_boundary import BoundaryDictionary +from ansys.aedt.core.modules.boundary.icepak_boundary import ExponentialDictionary +from ansys.aedt.core.modules.boundary.icepak_boundary import LinearDictionary +from ansys.aedt.core.modules.boundary.icepak_boundary import PieceWiseLinearDictionary +from ansys.aedt.core.modules.boundary.icepak_boundary import PowerLawDictionary +from ansys.aedt.core.modules.boundary.icepak_boundary import SinusoidalDictionary +from ansys.aedt.core.modules.boundary.icepak_boundary import SquareWaveDictionary +from ansys.aedt.core.modules.boundary.icepak_boundary import _create_boundary +from ansys.aedt.core.modules.boundary.layout_boundary import NativeComponentObject +from ansys.aedt.core.modules.boundary.layout_boundary import NativeComponentPCB from ansys.aedt.core.modules.setup_templates import SetupKeys from ansys.aedt.core.visualization.post.monitor_icepak import Monitor diff --git a/src/ansys/aedt/core/maxwell.py b/src/ansys/aedt/core/maxwell.py index 80f7a8590db..2c692d79067 100644 --- a/src/ansys/aedt/core/maxwell.py +++ b/src/ansys/aedt/core/maxwell.py @@ -43,8 +43,8 @@ from ansys.aedt.core.generic.settings import settings from ansys.aedt.core.modeler.cad.elements_3d import FacePrimitive from ansys.aedt.core.modeler.geometry_operators import GeometryOperators -from ansys.aedt.core.modules.boundary import BoundaryObject -from ansys.aedt.core.modules.boundary import MaxwellParameters +from ansys.aedt.core.modules.boundary.common import BoundaryObject +from ansys.aedt.core.modules.boundary.maxwell_boundary import MaxwellParameters from ansys.aedt.core.modules.setup_templates import SetupKeys @@ -3053,9 +3053,14 @@ def assign_resistive_sheet( if not name: boundary = generate_unique_name("ResistiveSheet") - props = { - "Faces": assignment, - } + listobj = self.modeler.convert_to_selections(assignment, True) + + props = {"Objects": [], "Faces": []} + for sel in listobj: + if isinstance(sel, str): + props["Objects"].append(sel) + elif isinstance(sel, int): + props["Faces"].append(sel) if self.solution_type in ["EddyCurrent", "Transient"]: props["Resistance"] = resistance diff --git a/src/ansys/aedt/core/mechanical.py b/src/ansys/aedt/core/mechanical.py index 318898ec1ac..95bb740cd21 100644 --- a/src/ansys/aedt/core/mechanical.py +++ b/src/ansys/aedt/core/mechanical.py @@ -29,7 +29,7 @@ from ansys.aedt.core.application.analysis_3d import FieldAnalysis3D from ansys.aedt.core.generic.general_methods import generate_unique_name from ansys.aedt.core.generic.general_methods import pyaedt_function_handler -from ansys.aedt.core.modules.boundary import BoundaryObject +from ansys.aedt.core.modules.boundary.common import BoundaryObject from ansys.aedt.core.modules.setup_templates import SetupKeys diff --git a/src/ansys/aedt/core/modeler/cad/elements_3d.py b/src/ansys/aedt/core/modeler/cad/elements_3d.py index ef457b915c5..85030f3110a 100644 --- a/src/ansys/aedt/core/modeler/cad/elements_3d.py +++ b/src/ansys/aedt/core/modeler/cad/elements_3d.py @@ -1418,7 +1418,10 @@ def __init__(self, node, child_object, first_level=False, get_child_obj_arg=None if i == "OperandPart_" + saved_root_name or i == "OperandPart_" + saved_root_name.split("_")[0]: continue elif not i.startswith("OperandPart_"): - self.children[i] = BinaryTreeNode(i, self.child_object.GetChildObject(i), root_name=saved_root_name) + try: + self.children[i] = BinaryTreeNode(i, self.child_object.GetChildObject(i), root_name=saved_root_name) + except Exception: # nosec + pass else: names = self.child_object.GetChildObject(i).GetChildNames() for name in names: @@ -1433,36 +1436,37 @@ def __init__(self, node, child_object, first_level=False, get_child_obj_arg=None del self.children[name] @property - def props(self): + def properties(self): """Properties data. Returns ------- :class:``ansys.aedt.coree.modeler.cad.elements_3d.HistoryProps`` """ - if self._props is None: - self._props = {} - if settings.aedt_version >= "2024.2": - try: - props = self._get_data_model() - for p in self.child_object.GetPropNames(): - if p in props: - self._props[p] = props[p] - else: - self._props[p] = None - except Exception: - for p in self.child_object.GetPropNames(): - try: - self._props[p] = self.child_object.GetPropValue(p) - except Exception: - self._props[p] = None - else: + self._props = {} + if settings.aedt_version >= "2024.2": + try: + from ansys.aedt.core.application import _get_data_model + + props = _get_data_model(self.child_object) + for p in self.child_object.GetPropNames(): + if p in props: + self._props[p] = props[p] + else: + self._props[p] = None + except Exception: for p in self.child_object.GetPropNames(): try: self._props[p] = self.child_object.GetPropValue(p) except Exception: self._props[p] = None - self._props = HistoryProps(self, self._props) + else: + for p in self.child_object.GetPropNames(): + try: + self._props[p] = self.child_object.GetPropValue(p) + except Exception: + self._props[p] = None + self._props = HistoryProps(self, self._props) return self._props @property @@ -1473,22 +1477,7 @@ def command(self): ------- str """ - return self.props.get("Command", "") - - def _get_data_model(self): - import ast - - input_str = self.child_object.GetDataModel(-1, 1, 1).replace("false", "False").replace("true", "True") - props_list = ast.literal_eval(input_str) - props = {} - for prop in props_list["properties"]: - if "value" in prop: - props[prop["name"]] = prop["value"] - elif "values" in prop: - props[prop["name"]] = prop["values"] - else: - props[prop["name"]] = None - return props + return self.properties.get("Command", "") def update_property(self, prop_name, prop_value): """Update the property of the binary tree node. @@ -1516,7 +1505,7 @@ def _jsonalize_tree(self, binary_tree_node): childrend_dict = {} for _, node in binary_tree_node.children.items(): childrend_dict.update(self._jsonalize_tree(node)) - return {binary_tree_node.node: {"Props": binary_tree_node.props, "Children": childrend_dict}} + return {binary_tree_node.node: {"Props": binary_tree_node.properties, "Children": childrend_dict}} @pyaedt_function_handler def jsonalize_tree(self): @@ -1531,7 +1520,7 @@ def jsonalize_tree(self): @pyaedt_function_handler def _suppress(self, node, app, suppress): - if not node.command.startswith("Duplicate") and "Suppress Command" in node.props: + if not node.command.startswith("Duplicate") and "Suppress Command" in node.properties: app.oeditor.ChangeProperty( [ "NAME:AllTabs", diff --git a/src/ansys/aedt/core/modeler/cad/polylines.py b/src/ansys/aedt/core/modeler/cad/polylines.py index 914c1269376..82283513241 100644 --- a/src/ansys/aedt/core/modeler/cad/polylines.py +++ b/src/ansys/aedt/core/modeler/cad/polylines.py @@ -420,47 +420,47 @@ def _convert_points(p_in, dest_unit): if h_segments: for i, c in enumerate(h_segments.values()): # evaluate the number of points in the segment - attrb = list(c.props.keys()) + attrb = list(c.properties.keys()) n_points = 0 for j in range(1, len(attrb) + 1): if "Point" + str(j) in attrb: n_points += 1 # get the segment type - s_type = c.props["Segment Type"] + s_type = c.properties["Segment Type"] if i == 0: # append the first point only for the first segment if s_type != "Center Point Arc": p = [ - c.props["Point1/X"], - c.props["Point1/Y"], - c.props["Point1/Z"], + c.properties["Point1/X"], + c.properties["Point1/Y"], + c.properties["Point1/Z"], ] points.append(_convert_points(p, self._primitives.model_units)) else: p = [ - c.props["Start Point/X"], - c.props["Start Point/Y"], - c.props["Start Point/Z"], + c.properties["Start Point/X"], + c.properties["Start Point/Y"], + c.properties["Start Point/Z"], ] points.append(_convert_points(p, self._primitives.model_units)) if s_type == "Line": segments.append(PolylineSegment("Line")) p = [ - c.props["Point2/X"], - c.props["Point2/Y"], - c.props["Point2/Z"], + c.properties["Point2/X"], + c.properties["Point2/Y"], + c.properties["Point2/Z"], ] points.append(_convert_points(p, self._primitives.model_units)) elif s_type == "3 Point Arc": segments.append(PolylineSegment("Arc")) p2 = [ - c.props["Point2/X"], - c.props["Point2/Y"], - c.props["Point2/Z"], + c.properties["Point2/X"], + c.properties["Point2/Y"], + c.properties["Point2/Z"], ] p3 = [ - c.props["Point3/X"], - c.props["Point3/Y"], - c.props["Point3/Z"], + c.properties["Point3/X"], + c.properties["Point3/Y"], + c.properties["Point3/Z"], ] points.append(_convert_points(p2, self._primitives.model_units)) @@ -470,27 +470,27 @@ def _convert_points(p_in, dest_unit): for p in range(2, n_points + 1): point_attr = "Point" + str(p) p2 = [ - c.props[f"{point_attr}/X"], - c.props[f"{point_attr}/Y"], - c.props[f"{point_attr}/Z"], + c.properties[f"{point_attr}/X"], + c.properties[f"{point_attr}/Y"], + c.properties[f"{point_attr}/Z"], ] points.append(_convert_points(p2, self._primitives.model_units)) elif s_type == "Center Point Arc": p2 = [ - c.props["Start Point/X"], - c.props["Start Point/Y"], - c.props["Start Point/Z"], + c.properties["Start Point/X"], + c.properties["Start Point/Y"], + c.properties["Start Point/Z"], ] p3 = [ - c.props["Center Point/X"], - c.props["Center Point/Y"], - c.props["Center Point/Z"], + c.properties["Center Point/X"], + c.properties["Center Point/Y"], + c.properties["Center Point/Z"], ] start = _convert_points(p2, self._primitives.model_units) center = _convert_points(p3, self._primitives.model_units) - plane = c.props["Plane"] - angle = c.props["Angle"] + plane = c.properties["Plane"] + angle = c.properties["Angle"] arc_seg = PolylineSegment("AngularArc", arc_angle=angle, arc_center=center, arc_plane=plane) segments.append(arc_seg) self._evaluate_arc_angle_extra_points(arc_seg, start) @@ -498,8 +498,8 @@ def _convert_points(p_in, dest_unit): # perform validation if history: - nn_segments = int(history.props["Number of curves"]) - nn_points = int(history.props["Number of points"]) + nn_segments = int(history.properties["Number of curves"]) + nn_points = int(history.properties["Number of points"]) else: nn_segments = None nn_points = None diff --git a/src/ansys/aedt/core/modeler/cad/primitives.py b/src/ansys/aedt/core/modeler/cad/primitives.py index 836eab46178..16f4e0af589 100644 --- a/src/ansys/aedt/core/modeler/cad/primitives.py +++ b/src/ansys/aedt/core/modeler/cad/primitives.py @@ -90,7 +90,7 @@ def _parse_objs(self): if self.__obj_type == "o": self.__parent.logger.info("Parsing design objects. This operation can take time") self.__parent.logger.reset_timer() - self.__parent._refresh_all_ids_from_aedt_file() + self.__parent._refresh_all_ids_wrapper() self.__parent.add_new_solids() self.__parent.cleanup_solids() self.__parent.logger.info_timer("3D Modeler objects parsed.") @@ -510,7 +510,9 @@ def geometry_mode(self): References ---------- >>> oDesign.GetGeometryMode""" - return self._odesign.GetGeometryMode() + if "GetGeometryMode" in dir(self._odesign): + return self._odesign.GetGeometryMode() + return @property def solid_bodies(self): @@ -820,7 +822,7 @@ def refresh(self): self.user_defined_components = Objects(self, "u") self._refresh_object_types() if not settings.objects_lazy_load: - self._refresh_all_ids_from_aedt_file() + self._refresh_all_ids_wrapper() self.refresh_all_ids() @pyaedt_function_handler() @@ -851,8 +853,46 @@ def _create_point(self, name): return point + @pyaedt_function_handler() + def _refresh_all_ids_wrapper(self): + if settings.aedt_version >= "2025.1": + return self._refresh_all_ids_from_data_model() + else: + return self._refresh_all_ids_from_aedt_file() + + @pyaedt_function_handler() + def _refresh_all_ids_from_data_model(self): + self._app.logger.info("Refreshing objects from Data Model") + from ansys.aedt.core.application import _get_data_model + + dm = _get_data_model(self.oeditor, 2) + + for attribs in dm.get("children", []): + if attribs["type"] == "Part": + pid = 0 + is_polyline = False + try: + if attribs["children"][0]["Command"] == "CreatePolyline": + is_polyline = True + except Exception: + is_polyline = False + + o = self._create_object(name=attribs["Name"], pid=pid, use_cached=True, is_polyline=is_polyline) + o._part_coordinate_system = attribs["Orientation"] + o._model = attribs["Model"] + o._wireframe = attribs["Display Wireframe"] + o._m_groupName = attribs["Model"] + o._color = (attribs["Color/Red"], attribs["Color/Green"], attribs["Color/Blue"]) + o._material_name = attribs.get("Material", None) + o._surface_material = attribs.get("Surface Material", None) + o._solve_inside = attribs.get("Solve Inside", False) + o._is_updated = True + # pid+=1 + return len(self.objects) + @pyaedt_function_handler() def _refresh_all_ids_from_aedt_file(self): + self._app.logger.info("Refreshing objects from AEDT file") dp = copy.deepcopy(self._app.design_properties) if not dp or "ModelSetup" not in dp: @@ -964,7 +1004,7 @@ def cleanup_solids(self): new_object_id_dict = {} all_objects = self.object_names - all_unclassified = self.unclassified_names + all_unclassified = self._unclassified all_objs = all_objects + all_unclassified if sorted(all_objs) != sorted(list(self._object_names_to_ids.keys())): for old_id, obj in self.objects.items(): diff --git a/src/ansys/aedt/core/modeler/cad/primitives_3d.py b/src/ansys/aedt/core/modeler/cad/primitives_3d.py index f96c19ecada..ca4d9cd8d3f 100644 --- a/src/ansys/aedt/core/modeler/cad/primitives_3d.py +++ b/src/ansys/aedt/core/modeler/cad/primitives_3d.py @@ -36,7 +36,6 @@ import os from ansys.aedt.core import Edb -from ansys.aedt.core import Icepak from ansys.aedt.core.generic import load_aedt_file from ansys.aedt.core.generic.desktop_sessions import _edb_sessions from ansys.aedt.core.generic.general_methods import generate_unique_name @@ -1467,6 +1466,7 @@ def _create_reference_cs_from_3dcomp(self, assignment, password): @staticmethod def __create_temp_project(app): """Create temporary project with a duplicated design.""" + from ansys.aedt.core import Icepak temp_proj_name = generate_unique_project_name() ipkapp_temp = Icepak(project=os.path.join(app.toolkit_directory, temp_proj_name)) ipkapp_temp.delete_design(ipkapp_temp.design_name) diff --git a/src/ansys/aedt/core/modeler/circuits/primitives_nexxim.py b/src/ansys/aedt/core/modeler/circuits/primitives_nexxim.py index 92a63f76de0..a176e257dcb 100644 --- a/src/ansys/aedt/core/modeler/circuits/primitives_nexxim.py +++ b/src/ansys/aedt/core/modeler/circuits/primitives_nexxim.py @@ -39,7 +39,7 @@ from ansys.aedt.core.modeler.circuits.object_3d_circuit import CircuitComponent from ansys.aedt.core.modeler.circuits.primitives_circuit import CircuitComponents from ansys.aedt.core.modeler.circuits.primitives_circuit import ComponentCatalog -from ansys.aedt.core.modules.boundary import Excitations +from ansys.aedt.core.modules.boundary.circuit_boundary import Excitations class NexximComponents(CircuitComponents): diff --git a/src/ansys/aedt/core/modules/boundary.py b/src/ansys/aedt/core/modules/boundary.py deleted file mode 100644 index 3a35e69637e..00000000000 --- a/src/ansys/aedt/core/modules/boundary.py +++ /dev/null @@ -1,5991 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. -# SPDX-License-Identifier: MIT -# -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# 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. - -""" -This module contains these classes: ``BoundaryCommon`` and ``BoundaryObject``. -""" - -from abc import abstractmethod -import copy -import re - -from ansys.aedt.core.application.variables import decompose_variable_value -from ansys.aedt.core.generic.constants import CATEGORIESQ3D -from ansys.aedt.core.generic.data_handlers import _dict2arg -from ansys.aedt.core.generic.data_handlers import random_string -from ansys.aedt.core.generic.general_methods import GrpcApiError -from ansys.aedt.core.generic.general_methods import PropsManager -from ansys.aedt.core.generic.general_methods import _dim_arg -from ansys.aedt.core.generic.general_methods import filter_tuple -from ansys.aedt.core.generic.general_methods import generate_unique_name -from ansys.aedt.core.generic.general_methods import pyaedt_function_handler -from ansys.aedt.core.modeler.cad.elements_3d import EdgePrimitive -from ansys.aedt.core.modeler.cad.elements_3d import FacePrimitive -from ansys.aedt.core.modeler.cad.elements_3d import VertexPrimitive -from ansys.aedt.core.modules.circuit_templates import SourceKeys - - -class BoundaryProps(dict): - """AEDT Boundary Component Internal Parameters.""" - - def __setitem__(self, key, value): - dict.__setitem__(self, key, value) - if self._pyaedt_boundary.auto_update: - if key in ["Edges", "Faces", "Objects"]: - res = self._pyaedt_boundary.update_assignment() - else: - res = self._pyaedt_boundary.update() - if not res: - self._pyaedt_boundary._app.logger.warning("Update of %s Failed. Check needed arguments", key) - - def __init__(self, boundary, props): - dict.__init__(self) - if props: - for key, value in props.items(): - if isinstance(value, dict): - dict.__setitem__(self, key, BoundaryProps(boundary, value)) - elif isinstance(value, list): - list_els = [] - for el in value: - if isinstance(el, dict): - list_els.append(BoundaryProps(boundary, el)) - else: - list_els.append(el) - dict.__setitem__(self, key, list_els) - else: - dict.__setitem__(self, key, value) - self._pyaedt_boundary = boundary - - def _setitem_without_update(self, key, value): - dict.__setitem__(self, key, value) - - -class BoundaryCommon(PropsManager): - """ """ - - @pyaedt_function_handler() - def _get_args(self, props=None): - """Retrieve boundary properties. - - Parameters - ---------- - props : dict, optional - The default is ``None``. - - Returns - ------- - dict - Dictionary of boundary properties. - - """ - if not props: - props = self.props - arg = ["NAME:" + self.name] - _dict2arg(props, arg) - return arg - - @pyaedt_function_handler() - def delete(self): - """Delete the boundary. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - if self.type == "Matrix" or self.type == "Force" or self.type == "Torque": - self._app.o_maxwell_parameters.DeleteParameters([self.name]) - else: - self._app.oboundary.DeleteBoundaries([self.name]) - if self.name in self._app.excitation_objects.keys(): - self._app.excitation_objects.pop(self.name) - self._app.boundaries - return True - - def _get_boundary_data(self, ds): - try: - if "MaxwellParameterSetup" in self._app.design_properties: - param = "MaxwellParameters" - setup = "MaxwellParameterSetup" - if isinstance(self._app.design_properties[setup][param][ds], dict): - return [ - self._app.design_properties["MaxwellParameterSetup"]["MaxwellParameters"][ds], - self._app.design_properties["MaxwellParameterSetup"]["MaxwellParameters"][ds][ - "MaxwellParameterType" - ], - ] - except Exception: - self._app.logger.debug( - "An error occurred while getting boundary data for MaxwellParameterSetup." - ) # pragma: no cover - try: - if ( - "ModelSetup" in self._app.design_properties - and "MotionSetupList" in self._app.design_properties["ModelSetup"] - ): - motion_list = "MotionSetupList" - setup = "ModelSetup" - # check moving part - if isinstance(self._app.design_properties[setup][motion_list][ds], dict): - return [ - self._app.design_properties["ModelSetup"]["MotionSetupList"][ds], - self._app.design_properties["ModelSetup"]["MotionSetupList"][ds]["MotionType"], - ] - except Exception: - self._app.logger.debug("An error occurred while getting boundary data for ModelSetup.") # pragma: no cover - try: - if ds in self._app.design_properties["BoundarySetup"]["Boundaries"]: - if ( - self._app.design_properties["BoundarySetup"]["Boundaries"][ds]["BoundType"] == "Network" - and self._app.design_type == "Icepak" - ): - return [self._app.design_properties["BoundarySetup"]["Boundaries"][ds], ""] - else: - return [ - self._app.design_properties["BoundarySetup"]["Boundaries"][ds], - self._app.design_properties["BoundarySetup"]["Boundaries"][ds]["BoundType"], - ] - except Exception: - self._app.logger.debug( - "An error occurred while getting boundary data for BoundarySetup." - ) # pragma: no cover - return [] - - -class NativeComponentObject(BoundaryCommon, object): - """Manages Native Component data and execution. - - Parameters - ---------- - app : object - An AEDT application from ``ansys.aedt.core.application``. - component_type : str - Type of the component. - component_name : str - Name of the component. - props : dict - Properties of the boundary. - - Examples - -------- - in this example the par_beam returned object is a ``ansys.aedt.core.modules.boundary.NativeComponentObject`` - >>> from ansys.aedt.core import Hfss - >>> hfss = Hfss(solution_type="SBR+") - >>> ffd_file ="path/to/ffdfile.ffd" - >>> par_beam = hfss.create_sbr_file_based_antenna(ffd_file) - >>> par_beam.native_properties["Size"] = "0.1mm" - >>> par_beam.update() - >>> par_beam.delete() - """ - - def __init__(self, app, component_type, component_name, props): - self.auto_update = False - self._app = app - self._name = component_name - - self.props = BoundaryProps( - self, - { - "TargetCS": "Global", - "SubmodelDefinitionName": self.name, - "ComponentPriorityLists": {}, - "NextUniqueID": 0, - "MoveBackwards": False, - "DatasetType": "ComponentDatasetType", - "DatasetDefinitions": {}, - "BasicComponentInfo": { - "ComponentName": self.name, - "Company": "", - "Company URL": "", - "Model Number": "", - "Help URL": "", - "Version": "1.0", - "Notes": "", - "IconType": "", - }, - "GeometryDefinitionParameters": {"VariableOrders": {}}, - "DesignDefinitionParameters": {"VariableOrders": {}}, - "MaterialDefinitionParameters": {"VariableOrders": {}}, - "DefReferenceCSID": 1, - "MapInstanceParameters": "DesignVariable", - "UniqueDefinitionIdentifier": "89d26167-fb77-480e-a7ab-" - + random_string(12, char_set="abcdef0123456789"), - "OriginFilePath": "", - "IsLocal": False, - "ChecksumString": "", - "ChecksumHistory": [], - "VersionHistory": [], - "NativeComponentDefinitionProvider": {"Type": component_type}, - "InstanceParameters": {"GeometryParameters": "", "MaterialParameters": "", "DesignParameters": ""}, - }, - ) - if props: - self._update_props(self.props, props) - self.native_properties = self.props["NativeComponentDefinitionProvider"] - self.auto_update = True - - @property - def name(self): - """Name of the object. - - Returns - ------- - str - Name of the object. - - """ - return self._name - - @name.setter - def name(self, component_name): - if component_name != self._name: - if component_name not in self._app.native_component_names: - self.object_properties.props["Name"] = component_name - self._app.native_components.update({component_name: self}) - del self._app.native_components[self._name] - del self._app.modeler.user_defined_components[self._name] - self._name = component_name - else: # pragma: no cover - self._app._logger.warning("Name %s already assigned in the design", component_name) - - @property - def definition_name(self): - """Definition name of the native component. - - Returns - ------- - str - Name of the native component. - - """ - definition_name = None - if self.props and "SubmodelDefinitionName" in self.props: - definition_name = self.props["SubmodelDefinitionName"] - return definition_name - - @property - def targetcs(self): - """Native Component Coordinate System. - - Returns - ------- - str - Native Component Coordinate System. - """ - if "TargetCS" in list(self.props.keys()): - return self.props["TargetCS"] - else: - return "Global" - - @targetcs.setter - def targetcs(self, cs): - self.props["TargetCS"] = cs - - @property - def object_properties(self): - """Object-oriented properties. - - Returns - ------- - class:`ansys.aedt.core.modeler.cad.elements_3d.BinaryTreeNode` - - """ - - from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode - - child_object = self._app.get_oo_object(self._app.oeditor, self.name) - if child_object: - return BinaryTreeNode(self.name, child_object, False) - return False - - def _update_props(self, d, u): - for k, v in u.items(): - if isinstance(v, dict): - if k not in d: - d[k] = {} - d[k] = self._update_props(d[k], v) - else: - d[k] = v - return d - - @pyaedt_function_handler() - def _get_args(self, props=None): - if props is None: - props = self.props - arg = ["NAME:InsertNativeComponentData"] - _dict2arg(props, arg) - return arg - - @pyaedt_function_handler() - def create(self): - """Create a Native Component in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - try: - names = [i for i in self._app.excitations] - except GrpcApiError: # pragma: no cover - names = [] - self._name = self._app.modeler.oeditor.InsertNativeComponent(self._get_args()) - try: - a = [i for i in self._app.excitations if i not in names] - self.excitation_name = a[0].split(":")[0] - except (GrpcApiError, IndexError): - self.excitation_name = self.name - return True - - @pyaedt_function_handler() - def update(self): - """Update the Native Component in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - - self.update_props = {} - self.update_props["DefinitionName"] = self.props["SubmodelDefinitionName"] - self.update_props["GeometryDefinitionParameters"] = self.props["GeometryDefinitionParameters"] - self.update_props["DesignDefinitionParameters"] = self.props["DesignDefinitionParameters"] - self.update_props["MaterialDefinitionParameters"] = self.props["MaterialDefinitionParameters"] - self.update_props["NextUniqueID"] = self.props["NextUniqueID"] - self.update_props["MoveBackwards"] = self.props["MoveBackwards"] - self.update_props["DatasetType"] = self.props["DatasetType"] - self.update_props["DatasetDefinitions"] = self.props["DatasetDefinitions"] - self.update_props["NativeComponentDefinitionProvider"] = self.props["NativeComponentDefinitionProvider"] - self.update_props["ComponentName"] = self.props["BasicComponentInfo"]["ComponentName"] - self.update_props["Company"] = self.props["BasicComponentInfo"]["Company"] - self.update_props["Model Number"] = self.props["BasicComponentInfo"]["Model Number"] - self.update_props["Help URL"] = self.props["BasicComponentInfo"]["Help URL"] - self.update_props["Version"] = self.props["BasicComponentInfo"]["Version"] - self.update_props["Notes"] = self.props["BasicComponentInfo"]["Notes"] - self.update_props["IconType"] = self.props["BasicComponentInfo"]["IconType"] - self._app.modeler.oeditor.EditNativeComponentDefinition(self._get_args(self.update_props)) - - return True - - @pyaedt_function_handler() - def delete(self): - """Delete the Native Component in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - self._app.modeler.oeditor.Delete(["NAME:Selections", "Selections:=", self.name]) - for el in self._app._native_components: - if el.name == self.name: - self._app._native_components.remove(el) - del self._app.modeler.user_defined_components[self.name] - self._app.modeler.cleanup_objects() - return True - - -def disable_auto_update(func): - """Decorator used to disable automatic update.""" - - def wrapper(self, *args, **kwargs): - """Inner wrapper function.""" - obj = self - if not hasattr(self, "auto_update"): - obj = self.pcb - auto_update = obj.auto_update - obj.auto_update = False - out = func(self, *args, **kwargs) - if auto_update: - obj.update() - obj.auto_update = auto_update - return out - - return wrapper - - -class PCBSettingsPackageParts(object): - """Handle package part settings of the PCB component. - - Parameters - ---------- - pcb_obj : :class:`pyaedt.modules.Boundary.NativeComponentPCB` - Inherited pcb object. - app : :class:`pyaedt.Icepak` - Inherited application object. - """ - - def __init__(self, pcb_obj, app): - self._app = app - self.pcb = pcb_obj - self._solderbumps_map = {"Lumped": "SbLumped", "Cylinders": "SbCylinder", "Boxes": "SbBlock"} - - def __eq__(self, other): - if isinstance(other, str): - return other == "Package" - elif isinstance(other, self.__class__): - return self.__dict__ == other.__dict__ - else: - return False - - def __ne__(self, other): - return not self.__eq__(other) - - @pyaedt_function_handler() - @disable_auto_update - def set_solderballs_modeling(self, modeling=None): - """Set how to model solderballs. - - Parameters - ---------- - modeling : str, optional - Method for modeling solderballs located below the stackup. The default is - ``None``, in which case they are not modeled. Options for modeling are - ``"Boxes"``, ``"Cylinders"``, and ``"Lumped"``. - - Returns - ------- - bool - ``True`` if successful, ``False`` otherwise. - """ - update_properties = { - "CreateBottomSolderballs": modeling is not None, - "BottomSolderballsModelType": self._solderbumps_map[modeling], - } - - self.pcb.props["NativeComponentDefinitionProvider"].update(update_properties) - return True - - @pyaedt_function_handler() - @disable_auto_update - def set_connectors_modeling( - self, - modeling=None, - solderbumps_modeling="Boxes", - bondwire_material="Au-Typical", - bondwire_diameter="0.05mm", - ): - """Set how to model connectors. - - Parameters - ---------- - modeling : str, optional - Method for modeling connectors located above the stackup. The default is - ``None``, in which case they are not modeled. Options for modeling are - ``"Bondwire"`` and ``"Solderbump"``. - solderbumps_modeling : str, optional - Method for modeling solderbumps if ``modeling="Solderbump"``. - The default is ```"Boxes"``. Options are ``"Boxes"``, ``"Cylinders"``, - and ``"Lumped"``. - bondwire_material : str, optional - Bondwire material if ``modeling="Bondwire"``. The default is - ``"Au-Typical"``. - bondwire_diameter : str, optional - Bondwires diameter if ``modeling="Bondwire". - The default is ``"0.05mm"``. - - Returns - ------- - bool - ``True`` if successful, ``False`` otherwise. - """ - valid_connectors = ["Solderbump", "Bondwire"] - if modeling is not None and modeling not in valid_connectors: - self._app.logger.error( - f"{modeling} option is not supported. Use one of the following: {', '.join(valid_connectors)}" - ) - return False - if bondwire_material not in self._app.materials.mat_names_aedt: - self._app.logger.error(f"{bondwire_material} material is not present in the library.") - return False - if self._solderbumps_map.get(solderbumps_modeling, None) is None: - self._app.logger.error( - f"Solderbumps modeling option {solderbumps_modeling} is not valid. " - f"Available options are: {', '.join(list(self._solderbumps_map.keys()))}." - ) - return False - - update_properties = { - "CreateTopSolderballs": modeling is not None, - "TopConnectorType": modeling, - "TopSolderballsModelType": self._solderbumps_map[solderbumps_modeling], - "BondwireMaterial": bondwire_material, - "BondwireDiameter": bondwire_diameter, - } - - self.pcb.props["NativeComponentDefinitionProvider"].update(update_properties) - return True - - def __repr__(self): - return "Package" - - -class PCBSettingsDeviceParts(object): - """Handle device part settings of the PCB component. - - Parameters - ---------- - pcb_obj : :class:`pyaedt.modules.Boundary.NativeComponentPCB` - Inherited pcb object. - app : :class:`pyaedt.Icepak` - Inherited application object. - """ - - def __init__(self, pcb_obj, app): - self._app = app - self.pcb = pcb_obj - self._filter_map2name = {"Cap": "Capacitors", "Ind": "Inductors", "Res": "Resistors"} - - def __eq__(self, other): - if isinstance(other, str): - return other == "Device" - elif isinstance(other, self.__class__): - return self.__dict__ == other.__dict__ - else: - return False - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "Device" - - @property - @pyaedt_function_handler() - def simplify_parts(self): - """Get whether parts are simplified as cuboid.""" - return self.pcb.props["NativeComponentDefinitionProvider"]["ModelDeviceAsRect"] - - @simplify_parts.setter - @pyaedt_function_handler() - def simplify_parts(self, value): - """Set whether parts are simplified as cuboid. - - Parameters - ---------- - value : bool - Whether parts are simplified as cuboid. - """ - self.pcb.props["NativeComponentDefinitionProvider"]["ModelDeviceAsRect"] = value - - @property - @pyaedt_function_handler() - def surface_material(self): - """Surface material to apply to parts.""" - return self.pcb.props["NativeComponentDefinitionProvider"]["DeviceSurfaceMaterial"] - - @surface_material.setter - @pyaedt_function_handler() - def surface_material(self, value): - """Set surface material to apply to parts. - - Parameters - ---------- - value : str - Surface material to apply to parts. - """ - self.pcb.props["NativeComponentDefinitionProvider"]["DeviceSurfaceMaterial"] = value - - @property - @pyaedt_function_handler() - def footprint_filter(self): - """Minimum component footprint for filtering.""" - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return None - if self._app.settings.aedt_version < "2024.2": - return None - return self.filters.get("FootPrint", {}).get("Value", None) - - @footprint_filter.setter - @pyaedt_function_handler() - @disable_auto_update - def footprint_filter(self, minimum_footprint): - """Set minimum component footprint for filtering. - - Parameters - ---------- - minimum_footprint : str - Value with unit of the minimum component footprint for filtering. - """ - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return - if self._app.settings.aedt_version < "2024.2": - return - new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) - if "FootPrint" in new_filters: - new_filters.remove("FootPrint") - if minimum_footprint is not None: - new_filters.append("FootPrint") - self.pcb.props["NativeComponentDefinitionProvider"]["FootPrint"] = minimum_footprint - self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters - - @property - @pyaedt_function_handler() - def power_filter(self): - """Minimum component power for filtering.""" - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return None - return self.filters.get("Power", {}).get("Value") - - @power_filter.setter - @pyaedt_function_handler() - @disable_auto_update - def power_filter(self, minimum_power): - """Set minimum component power for filtering. - - Parameters - ---------- - minimum_power : str - Value with unit of the minimum component power for filtering. - """ - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return - new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) - if "Power" in new_filters: - new_filters.remove("Power") - if minimum_power is not None: - new_filters.append("Power") - self.pcb.props["NativeComponentDefinitionProvider"]["PowerVal"] = minimum_power - self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters - - @property - @pyaedt_function_handler() - def type_filters(self): - """Types of component that are filtered.""" - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return None - return self.filters.get("Types") - - @type_filters.setter - @pyaedt_function_handler() - @disable_auto_update - def type_filters(self, object_type): - """Set types of component to filter. - - Parameters - ---------- - object_type : str or list - Types of object to filter. Options are ``"Capacitors"``, ``"Inductors"``, and ``"Resistors"``. - """ - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return - if not isinstance(object_type, list): - object_type = [object_type] - if not all(o in self._filter_map2name.values() for o in object_type): - self._app.logger.error( - f"Accepted elements of the list are: {', '.join(list(self._filter_map2name.values()))}" - ) - else: - new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) - map2arg = {v: k for k, v in self._filter_map2name.items()} - for f in self._filter_map2name.keys(): - if f in new_filters: - new_filters.remove(f) - new_filters += [map2arg[o] for o in object_type] - self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters - - @property - @pyaedt_function_handler() - def height_filter(self): - """Minimum component height for filtering.""" - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return None - return self.filters.get("Height", {}).get("Value", None) - - @height_filter.setter - @pyaedt_function_handler() - @disable_auto_update - def height_filter(self, minimum_height): - """Set minimum component height for filtering and whether to filter 2D objects. - - Parameters - ---------- - minimum_height : str - Value with unit of the minimum component power for filtering. - """ - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return - new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) - if "Height" in new_filters: - new_filters.remove("Height") - if minimum_height is not None: - new_filters.append("Height") - self.pcb.props["NativeComponentDefinitionProvider"]["HeightVal"] = minimum_height - self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters - - @property - @pyaedt_function_handler() - def objects_2d_filter(self): - """Whether 2d objects are filtered.""" - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return None - return self.filters.get("Exclude2DObjects", False) - - @objects_2d_filter.setter - @pyaedt_function_handler() - @disable_auto_update - def objects_2d_filter(self, filter): - """Set whether 2d objects are filtered. - - Parameters - ---------- - filter : bool - Whether 2d objects are filtered - """ - if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return - new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) - if "HeightExclude2D" in new_filters: - new_filters.remove("HeightExclude2D") - if filter: - new_filters.append("HeightExclude2D") - self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters - - @property - @pyaedt_function_handler() - def filters(self): - """All active filters.""" - if self.pcb.props["NativeComponentDefinitionProvider"].get("PartsChoice", None) != 1: - self._app.logger.error( - "Device parts modeling is not active. No filtering or override option is available." - ) - return None - out_filters = {"Type": {"Capacitors": False, "Inductors": False, "Resistors": False}} - filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) - filter_map2type = { - "Cap": "Type", - "FootPrint": "FootPrint", - "Height": "Height", - "HeightExclude2D": None, - "Ind": "Type", - "Power": "Power", - "Res": "Type", - } - filter_map2val = {"FootPrint": "FootPrint", "Height": "HeightVal", "Power": "PowerVal"} - for f in filters: - if filter_map2type[f] == "Type": - out_filters["Type"][self._filter_map2name[f]] = True - elif filter_map2type[f] is not None: - out_filters[f] = {"Value": filter_map2val[f]} - if "HeightExclude2D" in filters: - out_filters["Exclude2DObjects"] = True - return out_filters - - @property - @pyaedt_function_handler() - def overridden_components(self): - """All overridden components.""" - override_component = ( - self.pcb.props["NativeComponentDefinitionProvider"] - .get("instanceOverridesMap", {}) - .get("oneOverrideBlk", []) - ) - return {o["overrideName"]: o["overrideProps"] for o in override_component} - - @pyaedt_function_handler() - def _override_common( - self, - map_name, - package=None, - part=None, - reference_designator=None, - filter_component=False, - power=None, - r_jb=None, - r_jc=None, - height=None, - ): - override_component = ( - self.pcb.props["NativeComponentDefinitionProvider"] - .get(map_name, {}) # "instanceOverridesMap" - .get("oneOverrideBlk", []) - ) - if map_name == "instanceOverridesMap": - for o in override_component: - if o["overrideName"] == reference_designator: - override_component.remove(o) - elif map_name == "definitionOverridesMap": # pragma: no cover - for o in override_component: - if o["overridePartNumberName"] == part: - override_component.remove(o) - new_filter = {} - if filter_component or any(override_val is not None for override_val in [power, r_jb, r_jc, height]): - if map_name == "instanceOverridesMap": - new_filter.update({"overrideName": reference_designator}) - elif map_name == "definitionOverridesMap": # pragma: no cover - new_filter.update({"overridePartNumberName": part, "overrideGeometryName": package}) - new_filter.update( - { - "overrideProps": { - "isFiltered": filter_component, - "isOverridePower": power is not None, - "isOverrideThetaJb": r_jb is not None, - "isOverrideThetaJc": r_jc is not None, - "isOverrideHeight": height is not None, - "powerOverride": power if power is not None else "nan", - "thetaJbOverride": r_jb if r_jb is not None else "nan", - "thetaJcOverride": r_jc if r_jc is not None else "nan", - "heightOverride": height if height is not None else "nan", - }, - } - ) - override_component.append(new_filter) - self.pcb.props["NativeComponentDefinitionProvider"][map_name] = {"oneOverrideBlk": override_component} - return True - - @pyaedt_function_handler() - @disable_auto_update - def override_definition(self, package, part, filter_component=False, power=None, r_jb=None, r_jc=None, height=None): - """Set component override. - - Parameters - ---------- - package : str - Package name of the definition to override. - part : str - Part name of the definition to override. - filter_component : bool, optional - Whether to filter out the component. The default is ``False``. - power : str, optional - Override component power. Default is ``None``, in which case the power is not overridden. - r_jb : str, optional - Override component r_jb value. Default is ``None``, in which case the resistance is not overridden. - r_jc : str, optional - Override component r_jc value. Default is ``None``, in which case the resistance is not overridden. - height : str, optional - Override component height value. Default is ``None``, in which case the height is not overridden. - - Returns - ------- - bool - ``True`` if successful, ``False`` otherwise. - """ - if self._app.settings.aedt_version < "2024.2": - self._app.logger.error( - "This method is available only with AEDT 2024 R2 or later. Use 'override_instance()' method instead." - ) - return False - return self._override_common( # pragma : no cover - "definitionOverridesMap", - package=package, - part=part, - filter_component=filter_component, - power=power, - r_jb=r_jb, - r_jc=r_jc, - height=height, - ) - - @pyaedt_function_handler() - @disable_auto_update - def override_instance( - self, reference_designator, filter_component=False, power=None, r_jb=None, r_jc=None, height=None - ): - """Set instance override. - - Parameters - ---------- - reference_designator : str - Reference designator of the instance to override. - filter_component : bool, optional - Whether to filter out the component. The default is ``False``. - power : str, optional - Override component power. The default is ``None``, in which case the power is not overridden. - r_jb : str, optional - Override component r_jb value. The default is ``None``, in which case the resistance is not overridden. - r_jc : str, optional - Override component r_jc value. The default is ``None``, in which case the resistance is not overridden. - height : str, optional - Override component height value. The default is ``None``, in which case the height is not overridden. - - Returns - ------- - bool - ``True`` if successful, ``False`` otherwise. - """ - return self._override_common( - "instanceOverridesMap", - reference_designator=reference_designator, - filter_component=filter_component, - power=power, - r_jb=r_jb, - r_jc=r_jc, - height=height, - ) - - -class NativeComponentPCB(NativeComponentObject, object): - """Manages native component PCB data and execution. - - Parameters - ---------- - app : object - AEDT application from the ``pyaedt.application`` class. - component_type : str - Type of the component. - component_name : str - Name of the component. - props : dict - Properties of the boundary. - """ - - def __init__(self, app, component_type, component_name, props): - NativeComponentObject.__init__(self, app, component_type, component_name, props) - - @pyaedt_function_handler() - @disable_auto_update - def set_resolution(self, resolution): - """Set metal fraction mapping resolution. - - Parameters - ------- - resolution : int - Resolution level. Accepted variables between 1 and 5. - - Returns - ------- - bool - True if successful, else False. - """ - if resolution < 1 or resolution > 5: - self._app.logger.error("Valid resolution values are between 1 and 5.") - return False - self.props["NativeComponentDefinitionProvider"]["Resolution"] = resolution - self.props["NativeComponentDefinitionProvider"]["CustomResolution"] = False - return True - - @pyaedt_function_handler() - @disable_auto_update - def set_custom_resolution(self, row, col): - """Set custom metal fraction mapping resolution. - - Parameters - ---------- - row : int - Resolution level in rows direction. - col : int - Resolution level in columns direction. - - Returns - ------- - bool - True if successful, else False. - """ - self.props["NativeComponentDefinitionProvider"]["CustomResolutionRow"] = row - self.props["NativeComponentDefinitionProvider"]["CustomResolutionCol"] = col - self.props["NativeComponentDefinitionProvider"]["CustomResolution"] = True - return True - - @property - def power(self): - """Power dissipation assigned to the PCB.""" - return self.props["NativeComponentDefinitionProvider"].get("Power", "0W") - - @pyaedt_function_handler() - @disable_auto_update - def set_high_side_radiation( - self, - enabled, - surface_material="Steel-oxidised-surface", - radiate_to_ref_temperature=False, - view_factor=1, - ref_temperature="AmbientTemp", - ): - """Set high side radiation properties. - - Parameters - ---------- - enabled : bool - Whether high side radiation is enabled. - surface_material : str, optional - Surface material to apply. Default is ``"Steel-oxidised-surface"``. - radiate_to_ref_temperature : bool, optional - Whether to radiate to a reference temperature instead of objects in the model. - Default is ``False``. - view_factor : float, optional - View factor to use for radiation computation if ``radiate_to_ref_temperature`` - is set to ``True``. Default is 1. - ref_temperature : str, optional - Reference temperature to use for radiation computation if - ``radiate_to_ref_temperature`` is set to True. Default is ``"AmbientTemp"``. - - Returns - ------- - bool - ``True`` if successful, else ``False``. - """ - high_rad = { - "Radiate": enabled, - "RadiateTo - High": "RefTemperature - High" if radiate_to_ref_temperature else "AllObjects - High", - "Surface Material - High": surface_material, - } - if radiate_to_ref_temperature: - high_rad["Ref. Temperature - High"] = (ref_temperature,) - high_rad["View Factor - High"] = view_factor - self.props["NativeComponentDefinitionProvider"]["HighSide"] = high_rad - return True - - @pyaedt_function_handler() - @disable_auto_update - def set_low_side_radiation( - self, - enabled, - surface_material="Steel-oxidised-surface", - radiate_to_ref_temperature=False, - view_factor=1, - ref_temperature="AmbientTemp", - ): - """Set low side radiation properties. - - Parameters - ---------- - enabled : bool - Whether high side radiation is enabled. - surface_material : str, optional - Surface material to apply. Default is ``"Steel-oxidised-surface"``. - radiate_to_ref_temperature : bool, optional - Whether to radiate to a reference temperature instead of objects in the model. - Default is ``False``. - view_factor : float, optional - View factor to use for radiation computation if ``radiate_to_ref_temperature`` - is set to True. Default is 1. - ref_temperature : str, optional - Reference temperature to use for radiation computation if - ``radiate_to_ref_temperature`` is set to ``True``. Default is ``"AmbientTemp"``. - - Returns - ------- - bool - ``True`` if successful, else ``False``. - """ - low_side = { - "Radiate": enabled, - "RadiateTo": "RefTemperature - High" if radiate_to_ref_temperature else "AllObjects", - "Surface Material": surface_material, - } - if radiate_to_ref_temperature: - low_side["Ref. Temperature"] = (ref_temperature,) - low_side["View Factor"] = view_factor - self.props["NativeComponentDefinitionProvider"]["LowSide"] = low_side - return True - - @power.setter - @disable_auto_update - def power(self, value): - """Assign power dissipation to the PCB. - - Parameters - ---------- - value : str - Power to apply to the PCB. - """ - self.props["NativeComponentDefinitionProvider"]["Power"] = value - - @property - def force_source_solve(self): - """Force source solution option.""" - return self.props["NativeComponentDefinitionProvider"].get("DefnLink", {}).get("ForceSourceToSolve", False) - - @force_source_solve.setter - @disable_auto_update - def force_source_solve(self, val): - """Set Whether to force source solution. - - Parameters - ---------- - value : bool - Whether to force source solution. - """ - if not isinstance(val, bool): - self._app.logger.error("Only Boolean value can be accepted.") - return - return self.props["NativeComponentDefinitionProvider"]["DefnLink"].update({"ForceSourceToSolve": val}) - - @property - def preserve_partner_solution(self): - """Preserve parner solution option.""" - return self.props["NativeComponentDefinitionProvider"].get("DefnLink", {}).get("PreservePartnerSoln", False) - - @preserve_partner_solution.setter - @disable_auto_update - def preserve_partner_solution(self, val): - """Set Whether to preserve partner solution. - - Parameters - ---------- - val : bool - Whether to preserve partner solution. - """ - if not isinstance(val, bool): - self._app.logger.error("Only boolean can be accepted.") - return - return self.props["NativeComponentDefinitionProvider"]["DefnLink"].update({"PreservePartnerSoln": val}) - - @property - def included_parts(self): - """Parts options.""" - p = self.props["NativeComponentDefinitionProvider"].get("PartsChoice", 0) - if p == 0: - return None - elif p == 1: - return PCBSettingsDeviceParts(self, self._app) - elif p == 2: - return PCBSettingsPackageParts(self, self._app) - - @included_parts.setter - @disable_auto_update - def included_parts(self, value): - """Set PCB parts incusion option. - - Parameters - ---------- - value : str or int - Valid options are ``"None"``, ``"Device"``, and ``"Package"`` (or 0, 1, and 2 respectivaly) - """ - if value is None: - value = "None" - part_map = {"None": 0, "Device": 1, "Package": 2} - if not isinstance(value, int): - value = part_map.get(value, None) - if value is not None: - self.props["NativeComponentDefinitionProvider"]["PartsChoice"] = value - else: - self._app.logger.error( - 'Invalid part choice. Valid options are "None", "Device", and "Package" (or 0, 1, and 2 respectively).' - ) - - @pyaedt_function_handler() - @disable_auto_update - def set_low_side_radiation( - self, - enabled, - surface_material="Steel-oxidised-surface", - radiate_to_ref_temperature=False, - view_factor=1, - ref_temperature="AmbientTemp", - ): - """Set low side radiation properties. - - Parameters - ---------- - enabled : bool - Whether high side radiation is enabled. - surface_material : str, optional - Surface material to apply. Default is ``"Steel-oxidised-surface"``. - radiate_to_ref_temperature : bool, optional - Whether to radiate to a reference temperature instead of objects in the model. - Default is ``False``. - view_factor : float, optional - View factor to use for radiation computation if ``radiate_to_ref_temperature`` - is set to True. Default is 1. - ref_temperature : str, optional - Reference temperature to use for radiation computation if - ``radiate_to_ref_temperature`` is set to ``True``. Default is ``"AmbientTemp"``. - - Returns - ------- - bool - ``True`` if successful, else ``False``. - """ - low_side = { - "Radiate": enabled, - "RadiateTo": "RefTemperature - High" if radiate_to_ref_temperature else "AllObjects", - "Surface Material": surface_material, - } - if radiate_to_ref_temperature: - low_side["Ref. Temperature"] = (ref_temperature,) - low_side["View Factor"] = view_factor - self.props["NativeComponentDefinitionProvider"]["LowSide"] = low_side - return True - - @power.setter - @disable_auto_update - def power(self, value): - """Assign power dissipation to the PCB. - - Parameters - ---------- - value : str - Power to apply to the PCB. - """ - self.props["NativeComponentDefinitionProvider"]["Power"] = value - - @property - def force_source_solve(self): - """Force source solution option.""" - return self.props["NativeComponentDefinitionProvider"].get("DefnLink", {}).get("ForceSourceToSolve", False) - - @force_source_solve.setter - @disable_auto_update - def force_source_solve(self, val): - """Set Whether to force source solution. - - Parameters - ---------- - value : bool - Whether to force source solution. - """ - if not isinstance(val, bool): - self._app.logger.error("Only Boolean value can be accepted.") - return - return self.props["NativeComponentDefinitionProvider"]["DefnLink"].update({"ForceSourceToSolve": val}) - - @property - def preserve_partner_solution(self): - """Preserve parner solution option.""" - return self.props["NativeComponentDefinitionProvider"].get("DefnLink", {}).get("PreservePartnerSoln", False) - - @preserve_partner_solution.setter - @disable_auto_update - def preserve_partner_solution(self, val): - """Set Whether to preserve partner solution. - - Parameters - ---------- - val : bool - Whether to preserve partner solution. - """ - if not isinstance(val, bool): - self._app.logger.error("Only boolean can be accepted.") - return - return self.props["NativeComponentDefinitionProvider"]["DefnLink"].update({"PreservePartnerSoln": val}) - - @property - def included_parts(self): - """Parts options.""" - p = self.props["NativeComponentDefinitionProvider"].get("PartsChoice", 0) - if p == 0: - return None - elif p == 1: - return PCBSettingsDeviceParts(self, self._app) - elif p == 2: - return PCBSettingsPackageParts(self, self._app) - - @included_parts.setter - @disable_auto_update - def included_parts(self, value): - """Set PCB parts incusion option. - - Parameters - ---------- - value : str or int - Valid options are ``"None"``, ``"Device"``, and ``"Package"`` (or 0, 1, and 2 respectivaly) - """ - if value is None: - value = "None" - part_map = {"None": 0, "Device": 1, "Package": 2} - if not isinstance(value, int): - value = part_map.get(value, None) - if value is not None: - self.props["NativeComponentDefinitionProvider"]["PartsChoice"] = value - else: - self._app.logger.error( - 'Invalid part choice. Valid options are "None", "Device", and "Package" (or 0, 1, and 2 respectively).' - ) - - @pyaedt_function_handler() - def identify_extent_poly(self): - """Get polygon that defines board extent. - - Returns - ------- - str - Name of the polygon to include. - """ - from ansys.aedt.core import Hfss3dLayout - - prj = self.props["NativeComponentDefinitionProvider"]["DefnLink"]["Project"] - if prj == "This Project*": - prj = self._app.project_name - layout = Hfss3dLayout(project=prj, design=self.props["NativeComponentDefinitionProvider"]["DefnLink"]["Design"]) - layer = [o for o in layout.modeler.stackup.drawing_layers if o.type == "outline"][0] - outlines = [p for p in layout.modeler.polygons.values() if p.placement_layer == layer.name] - if len(outlines) > 1: - self._app.logger.info( - f"{outlines[0].name} automatically selected as ``extent_polygon``, " - f"pass ``extent_polygon`` argument explixitly to select a different one. " - f"Available choices are: {', '.join([o.name for o in outlines])}" - ) - elif len(outlines) == 0: - self._app.logger.error("No polygon found in the Outline layer.") - return False - return outlines[0].name - - @property - def board_cutout_material(self): - """Material applied to cutout regions.""" - return self.props["NativeComponentDefinitionProvider"].get("BoardCutoutMaterial", "air ") - - @property - def via_holes_material(self): - """Material applied to via hole regions.""" - return self.props["NativeComponentDefinitionProvider"].get("ViaHoleMaterial", "copper") - - @board_cutout_material.setter - @disable_auto_update - def board_cutout_material(self, value): - """Set material to apply to cutout regions. - - Parameters - ---------- - value : str - Material to apply to cutout regions. - """ - self.props["NativeComponentDefinitionProvider"]["BoardCutoutMaterial"] = value - - @via_holes_material.setter - @disable_auto_update - def via_holes_material(self, value): - """Set material to apply to via hole regions. - - Parameters - ---------- - value : str - Material to apply to via hole regions. - """ - self.props["NativeComponentDefinitionProvider"]["ViaHoleMaterial"] = value - - @pyaedt_function_handler() - @disable_auto_update - def set_board_extents(self, extent_type=None, extent_polygon=None): - """Set board extent. - - Parameters - ---------- - extent_type : str, optional - Extent definition of the PCB. Default is ``None`` in which case the 3D Layout extent - will be used. Other possible options are: ``"Bounding Box"`` or ``"Polygon"``. - extent_polygon : str, optional - Polygon name to use in the extent definition of the PCB. Default is ``None``. This - argument is mandatory if ``extent_type`` is ``"Polygon"``. - - Returns - ------- - bool - ``True`` if successful. ``False`` otherwise. - """ - if extent_type is None: - self.props["NativeComponentDefinitionProvider"]["Use3DLayoutExtents"] = True - else: - allowed_extent_types = ["Bounding Box", "Polygon"] - if extent_type not in allowed_extent_types: - self._app.logger.error( - f"Accepted argument for ``extent_type`` are:" - f" {', '.join(allowed_extent_types)}. {extent_type} provided" - ) - return False - self.props["NativeComponentDefinitionProvider"]["ExtentsType"] = extent_type - if extent_type == "Polygon": - if extent_polygon is None: - extent_polygon = self.identify_extent_poly() - if not extent_polygon: - return False - self.props["NativeComponentDefinitionProvider"]["OutlinePolygon"] = extent_polygon - return True - - -class BoundaryObject(BoundaryCommon, object): - """Manages boundary data and execution. - - Parameters - ---------- - app : object - An AEDT application from ``ansys.aedt.core.application``. - name : str - Name of the boundary. - props : dict, optional - Properties of the boundary. - boundarytype : str, optional - Type of the boundary. - - Examples - -------- - - Create a cylinder at the XY working plane and assign a copper coating of 0.2 mm to it. The Coating is a boundary - operation and coat will return a ``ansys.aedt.core.modules.boundary.BoundaryObject`` - - >>> from ansys.aedt.core import Hfss - >>> hfss =Hfss() - >>> origin = hfss.modeler.Position(0, 0, 0) - >>> inner = hfss.modeler.create_cylinder(hfss.PLANE.XY,origin,3,200,0,"inner") - >>> inner_id = hfss.modeler.get_obj_id("inner",) - >>> coat = hfss.assign_coating([inner_id],"copper",use_thickness=True,thickness="0.2mm") - """ - - def __init__(self, app, name, props=None, boundarytype=None, auto_update=True): - self.auto_update = False - self._app = app - self._name = name - self._props = None - if props: - self._props = BoundaryProps(self, props) - self._type = boundarytype - self._boundary_name = self.name - self.auto_update = auto_update - - @property - def object_properties(self): - """Object-oriented properties. - - Returns - ------- - class:`ansys.aedt.core.modeler.cad.elements_3d.BinaryTreeNode` - - """ - from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode - - child_object = None - design_childs = self._app.get_oo_name(self._app.odesign) - - if "Thermal" in design_childs: - cc = self._app.get_oo_object(self._app.odesign, "Thermal") - cc_names = self._app.get_oo_name(cc) - if self.name in cc_names: - child_object = cc_names - if child_object: - return BinaryTreeNode(self.name, child_object, False) - elif "Boundaries" in design_childs: - cc = self._app.get_oo_object(self._app.odesign, "Boundaries") - if self.name in cc.GetChildNames(): - child_object = cc.GetChildObject(self.name) - elif "Excitations" in design_childs and self.name in self._app.get_oo_name( - self._app.odesign, "Excitations" - ): - child_object = self._app.get_oo_object(self._app.odesign, "Excitations").GetChildObject(self.name) - elif self._app.design_type in ["Maxwell 3D", "Maxwell 2D"] and "Model" in design_childs: - model = self._app.get_oo_object(self._app.odesign, "Model") - if self.name in model.GetChildNames(): - child_object = model.GetChildObject(self.name) - elif "Excitations" in design_childs and self._app.get_oo_name(self._app.odesign, "Excitations"): - for port in self._app.get_oo_name(self._app.odesign, "Excitations"): - terminals = self._app.get_oo_name(self._app.odesign, f"Excitations\\{port}") - if self.name in terminals: - child_object = self._app.get_oo_object(self._app.odesign, f"Excitations\\{port}\\{self.name}") - elif "Conductors" in design_childs and self._app.get_oo_name(self._app.odesign, "Conductors"): - for port in self._app.get_oo_name(self._app.odesign, "Conductors"): - if self.name == port: - child_object = self._app.get_oo_object(self._app.odesign, f"Conductors\\{port}") - - if child_object: - return BinaryTreeNode(self.name, child_object, False) - - return False - - @property - def props(self): - """Boundary data. - - Returns - ------- - :class:BoundaryProps - """ - if self._props: - return self._props - props = self._get_boundary_data(self.name) - - if props: - self._props = BoundaryProps(self, props[0]) - self._type = props[1] - return self._props - - @property - def type(self): - """Boundary type. - - Returns - ------- - str - Returns the type of the boundary. - """ - if not self._type: - if self.available_properties: - if "Type" in self.available_properties: - self._type = self.props["Type"] - elif "BoundType" in self.available_properties: - self._type = self.props["BoundType"] - elif self.object_properties and self.object_properties.props["Type"]: - self._type = self.object_properties.props["Type"] - - if self._app.design_type == "Icepak" and self._type == "Source": - return "SourceIcepak" - else: - return self._type - - @type.setter - def type(self, value): - self._type = value - - @property - def name(self): - """Boundary Name.""" - return self._name - - @name.setter - def name(self, value): - self._name = value - self.update() - - @pyaedt_function_handler() - def _get_args(self, props=None): - """Retrieve arguments. - - Parameters - ---------- - props : - The default is ``None``. - - Returns - ------- - list - List of boundary properties. - - """ - if props is None: - props = self.props - arg = ["NAME:" + self.name] - _dict2arg(props, arg) - return arg - - @pyaedt_function_handler() - def create(self): - """Create a boundary. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - bound_type = self.type - if bound_type == "Perfect E": - self._app.oboundary.AssignPerfectE(self._get_args()) - elif bound_type == "Perfect H": - self._app.oboundary.AssignPerfectH(self._get_args()) - elif bound_type == "Aperture": - self._app.oboundary.AssignAperture(self._get_args()) - elif bound_type == "Radiation": - self._app.oboundary.AssignRadiation(self._get_args()) - elif bound_type == "FE-BI": - self._app.oboundary.AssignFEBI(self._get_args()) - elif bound_type == "Finite Conductivity": - self._app.oboundary.AssignFiniteCond(self._get_args()) - elif bound_type == "Lumped RLC": - self._app.oboundary.AssignLumpedRLC(self._get_args()) - elif bound_type == "Impedance": - self._app.oboundary.AssignImpedance(self._get_args()) - elif bound_type == "Layered Impedance": - self._app.oboundary.AssignLayeredImp(self._get_args()) - elif bound_type == "Anisotropic Impedance": - self._app.oboundary.AssignAnisotropicImpedance(self._get_args()) - elif bound_type == "Primary": - self._app.oboundary.AssignPrimary(self._get_args()) - elif bound_type == "Secondary": - self._app.oboundary.AssignSecondary(self._get_args()) - elif bound_type == "Lattice Pair": - self._app.oboundary.AssignLatticePair(self._get_args()) - elif bound_type == "HalfSpace": - self._app.oboundary.AssignHalfSpace(self._get_args()) - elif bound_type == "Multipaction SEE": - self._app.oboundary.AssignMultipactionSEE(self._get_args()) - elif bound_type == "Fresnel": - self._app.oboundary.AssignFresnel(self._get_args()) - elif bound_type == "Symmetry": - self._app.oboundary.AssignSymmetry(self._get_args()) - elif bound_type == "Zero Tangential H Field": - self._app.oboundary.AssignZeroTangentialHField(self._get_args()) - elif bound_type == "Zero Integrated Tangential H Field": - self._app.oboundary.AssignIntegratedZeroTangentialHField(self._get_args()) - elif bound_type == "Tangential H Field": - self._app.oboundary.AssignTangentialHField(self._get_args()) - elif bound_type == "Insulating": - self._app.oboundary.AssignInsulating(self._get_args()) - elif bound_type == "Independent": - self._app.oboundary.AssignIndependent(self._get_args()) - elif bound_type == "Dependent": - self._app.oboundary.AssignDependent(self._get_args()) - elif bound_type == "Band": - self._app.omodelsetup.AssignBand(self._get_args()) - elif bound_type == "InfiniteGround": - self._app.oboundary.AssignInfiniteGround(self._get_args()) - elif bound_type == "ThinConductor": - self._app.oboundary.AssignThinConductor(self._get_args()) - elif bound_type == "Stationary Wall": - self._app.oboundary.AssignStationaryWallBoundary(self._get_args()) - elif bound_type == "Symmetry Wall": - self._app.oboundary.AssignSymmetryWallBoundary(self._get_args()) - elif bound_type == "Recirculating": - self._app.oboundary.AssignRecircBoundary(self._get_args()) - elif bound_type == "Resistance": - self._app.oboundary.AssignResistanceBoundary(self._get_args()) - elif bound_type == "Conducting Plate": - self._app.oboundary.AssignConductingPlateBoundary(self._get_args()) - elif bound_type == "Adiabatic Plate": - self._app.oboundary.AssignAdiabaticPlateBoundary(self._get_args()) - elif bound_type == "Network": - self._app.oboundary.AssignNetworkBoundary(self._get_args()) - elif bound_type == "Grille": - self._app.oboundary.AssignGrilleBoundary(self._get_args()) - elif bound_type == "Block": - self._app.oboundary.AssignBlockBoundary(self._get_args()) - elif bound_type == "Blower": - self._app.oboundary.AssignBlowerBoundary(self._get_args()) - elif bound_type == "SourceIcepak": - self._app.oboundary.AssignSourceBoundary(self._get_args()) - elif bound_type == "Opening": - self._app.oboundary.AssignOpeningBoundary(self._get_args()) - elif bound_type == "EMLoss": - self._app.oboundary.AssignEMLoss(self._get_args()) - elif bound_type == "ThermalCondition": - self._app.oboundary.AssignThermalCondition(self._get_args()) - elif bound_type == "Convection": - self._app.oboundary.AssignConvection(self._get_args()) - elif bound_type == "HeatFlux": - self._app.oboundary.AssignHeatFlux(self._get_args()) - elif bound_type == "HeatGeneration": - self._app.oboundary.AssignHeatGeneration(self._get_args()) - elif bound_type == "Temperature": - self._app.oboundary.AssignTemperature(self._get_args()) - elif bound_type == "RotatingFluid": - self._app.oboundary.AssignRotatingFluid(self._get_args()) - elif bound_type == "Frictionless": - self._app.oboundary.AssignFrictionlessSupport(self._get_args()) - elif bound_type == "FixedSupport": - self._app.oboundary.AssignFixedSupport(self._get_args()) - elif bound_type == "Voltage": - self._app.oboundary.AssignVoltage(self._get_args()) - elif bound_type == "VoltageDrop": - self._app.oboundary.AssignVoltageDrop(self._get_args()) - elif bound_type == "Floating": - self._app.oboundary.AssignFloating(self._get_args()) - elif bound_type == "Current": - self._app.oboundary.AssignCurrent(self._get_args()) - elif bound_type == "CurrentDensity": - self._app.oboundary.AssignCurrentDensity(self._get_args()) - elif bound_type == "CurrentDensityGroup": - self._app.oboundary.AssignCurrentDensityGroup(self._get_args()[2], self._get_args()[3]) - elif bound_type == "CurrentDensityTerminal": - self._app.oboundary.AssignCurrentDensityTerminal(self._get_args()) - elif bound_type == "CurrentDensityTerminalGroup": - self._app.oboundary.AssignCurrentDensityTerminalGroup(self._get_args()[2], self._get_args()[3]) - elif bound_type == "Balloon": - self._app.oboundary.AssignBalloon(self._get_args()) - elif bound_type == "Winding" or bound_type == "Winding Group": - self._app.oboundary.AssignWindingGroup(self._get_args()) - elif bound_type == "Vector Potential": - self._app.oboundary.AssignVectorPotential(self._get_args()) - elif bound_type == "CoilTerminal" or bound_type == "Coil Terminal": - self._app.oboundary.AssignCoilTerminal(self._get_args()) - elif bound_type == "Coil": - self._app.oboundary.AssignCoil(self._get_args()) - elif bound_type == "Source": - self._app.oboundary.AssignSource(self._get_args()) - elif bound_type == "Sink": - self._app.oboundary.AssignSink(self._get_args()) - elif bound_type == "SignalNet": - self._app.oboundary.AssignSignalNet(self._get_args()) - elif bound_type == "GroundNet": - self._app.oboundary.AssignGroundNet(self._get_args()) - elif bound_type == "FloatingNet": - self._app.oboundary.AssignFloatingNet(self._get_args()) - elif bound_type == "SignalLine": - self._app.oboundary.AssignSingleSignalLine(self._get_args()) - elif bound_type == "ReferenceGround": - self._app.oboundary.AssignSingleReferenceGround(self._get_args()) - elif bound_type == "Circuit Port": - self._app.oboundary.AssignCircuitPort(self._get_args()) - elif bound_type == "Lumped Port": - self._app.oboundary.AssignLumpedPort(self._get_args()) - elif bound_type == "Wave Port": - self._app.oboundary.AssignWavePort(self._get_args()) - elif bound_type == "Floquet Port": - self._app.oboundary.AssignFloquetPort(self._get_args()) - elif bound_type == "AutoIdentify": - # Build reference conductor argument as a list of strings - # ref_cond_arg should be a list. - ref_cond_arg = ["NAME:ReferenceConductors"] + self.props["ReferenceConductors"] - self._app.oboundary.AutoIdentifyPorts( - ["NAME:Faces", self.props["Faces"]], - self.props["IsWavePort"], - ref_cond_arg, - self.name, - self.props["RenormalizeModes"], - ) - elif bound_type == "SBRTxRxSettings": - self._app.oboundary.SetSBRTxRxSettings(self._get_args()) - elif bound_type == "EndConnection": - self._app.oboundary.AssignEndConnection(self._get_args()) - elif bound_type == "Hybrid": - self._app.oboundary.AssignHybridRegion(self._get_args()) - elif bound_type == "FluxTangential": - self._app.oboundary.AssignFluxTangential(self._get_args()) - elif bound_type == "Plane Incident Wave": - self._app.oboundary.AssignPlaneWave(self._get_args()) - elif bound_type == "ResistiveSheet": - self._app.oboundary.AssignResistiveSheet(self._get_args()) - else: - return False - return True - - @pyaedt_function_handler() - def update(self): - """Update the boundary. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - bound_type = self.type - if bound_type == "Perfect E": - self._app.oboundary.EditPerfectE(self._boundary_name, self._get_args()) - elif bound_type == "Perfect H": - self._app.oboundary.EditPerfectH(self._boundary_name, self._get_args()) - elif bound_type == "Aperture": - self._app.oboundary.EditAperture(self._boundary_name, self._get_args()) - elif bound_type == "Radiation": - self._app.oboundary.EditRadiation(self._boundary_name, self._get_args()) - elif bound_type == "Finite Conductivity": - self._app.oboundary.EditFiniteCond(self._boundary_name, self._get_args()) - elif bound_type == "Lumped RLC": - self._app.oboundary.EditLumpedRLC(self._boundary_name, self._get_args()) - elif bound_type == "Impedance": - self._app.oboundary.EditImpedance(self._boundary_name, self._get_args()) - elif bound_type == "Layered Impedance": - self._app.oboundary.EditLayeredImpedance(self._boundary_name, self._get_args()) - elif bound_type == "Anisotropic Impedance": - self._app.oboundary.EditAssignAnisotropicImpedance( - self._boundary_name, self._get_args() - ) # pragma: no cover - elif bound_type == "Primary": - self._app.oboundary.EditPrimary(self._boundary_name, self._get_args()) - elif bound_type == "Secondary": - self._app.oboundary.EditSecondary(self._boundary_name, self._get_args()) - elif bound_type == "Lattice Pair": - self._app.oboundary.EditLatticePair(self._boundary_name, self._get_args()) - elif bound_type == "HalfSpace": - self._app.oboundary.EditHalfSpace(self._boundary_name, self._get_args()) - elif bound_type == "Multipaction SEE": - self._app.oboundary.EditMultipactionSEE(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Fresnel": - self._app.oboundary.EditFresnel(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Symmetry": - self._app.oboundary.EditSymmetry(self._boundary_name, self._get_args()) - elif bound_type == "Zero Tangential H Field": - self._app.oboundary.EditZeroTangentialHField(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Zero Integrated Tangential H Field": - self._app.oboundary.EditIntegratedZeroTangentialHField( - self._boundary_name, self._get_args() - ) # pragma: no cover - elif bound_type == "Tangential H Field": - self._app.oboundary.EditTangentialHField(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Insulating": - self._app.oboundary.EditInsulating(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Independent": - self._app.oboundary.EditIndependent(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Dependent": - self._app.oboundary.EditDependent(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Band": - self._app.omodelsetup.EditMotionSetup(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "InfiniteGround": - self._app.oboundary.EditInfiniteGround(self._boundary_name, self._get_args()) - elif bound_type == "ThinConductor": - self._app.oboundary.EditThinConductor(self._boundary_name, self._get_args()) - elif bound_type == "Stationary Wall": - self._app.oboundary.EditStationaryWallBoundary(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Symmetry Wall": - self._app.oboundary.EditSymmetryWallBoundary(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Recirculating": - self._app.oboundary.EditRecircBoundary(self._boundary_name, self._get_args()) - elif bound_type == "Resistance": - self._app.oboundary.EditResistanceBoundary(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Conducting Plate": - self._app.oboundary.EditConductingPlateBoundary(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Adiabatic Plate": - self._app.oboundary.EditAdiabaticPlateBoundary(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Network": - self._app.oboundary.EditNetworkBoundary(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Grille": - self._app.oboundary.EditGrilleBoundary(self._boundary_name, self._get_args()) - elif bound_type == "Opening": - self._app.oboundary.EditOpeningBoundary(self._boundary_name, self._get_args()) - elif bound_type == "EMLoss": - self._app.oboundary.EditEMLoss(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Block": - self._app.oboundary.EditBlockBoundary(self._boundary_name, self._get_args()) - elif bound_type == "Blower": - self._app.oboundary.EditBlowerBoundary(self._boundary_name, self._get_args()) - elif bound_type == "SourceIcepak": - self._app.oboundary.EditSourceBoundary(self._boundary_name, self._get_args()) - elif bound_type == "HeatFlux": - self._app.oboundary.EditHeatFlux(self._boundary_name, self._get_args()) - elif bound_type == "HeatGeneration": - self._app.oboundary.EditHeatGeneration(self._boundary_name, self._get_args()) - elif bound_type == "Voltage": - self._app.oboundary.EditVoltage(self._boundary_name, self._get_args()) - elif bound_type == "VoltageDrop": - self._app.oboundary.EditVoltageDrop(self._boundary_name, self._get_args()) - elif bound_type == "Current": - self._app.oboundary.EditCurrent(self._boundary_name, self._get_args()) - elif bound_type == "CurrentDensity": - self._app.oboundary.AssignCurrentDensity(self._get_args()) - elif bound_type == "CurrentDensityGroup": - self._app.oboundary.AssignCurrentDensityGroup(self._get_args()[2], self._get_args()[3]) - elif bound_type == "CurrentDensityTerminal": - self._app.oboundary.AssignCurrentDensityTerminal(self._get_args()) - elif bound_type == "CurrentDensityTerminalGroup": - self._app.oboundary.AssignCurrentDensityTerminalGroup(self._get_args()[2], self._get_args()[3]) - elif bound_type == "Winding" or bound_type == "Winding Group": - self._app.oboundary.EditWindingGroup(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Vector Potential": - self._app.oboundary.EditVectorPotential(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "CoilTerminal" or bound_type == "Coil Terminal": - self._app.oboundary.EditCoilTerminal(self._boundary_name, self._get_args()) - elif bound_type == "Coil": - self._app.oboundary.EditCoil(self._boundary_name, self._get_args()) - elif bound_type == "Source": - self._app.oboundary.EditTerminal(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "Sink": - self._app.oboundary.EditTerminal(self._boundary_name, self._get_args()) - elif bound_type == "SignalNet" or bound_type == "GroundNet" or bound_type == "FloatingNet": - self._app.oboundary.EditTerminal(self._boundary_name, self._get_args()) - elif bound_type in "Circuit Port": - self._app.oboundary.EditCircuitPort(self._boundary_name, self._get_args()) - elif bound_type in "Lumped Port": - self._app.oboundary.EditLumpedPort(self._boundary_name, self._get_args()) - elif bound_type in "Wave Port": - self._app.oboundary.EditWavePort(self._boundary_name, self._get_args()) - elif bound_type == "SetSBRTxRxSettings": - self._app.oboundary.SetSBRTxRxSettings(self._get_args()) # pragma: no cover - elif bound_type == "Floquet Port": - self._app.oboundary.EditFloquetPort(self._boundary_name, self._get_args()) # pragma: no cover - elif bound_type == "End Connection": - self._app.oboundary.EditEndConnection(self._boundary_name, self._get_args()) - elif bound_type == "Hybrid": - self._app.oboundary.EditHybridRegion(self._boundary_name, self._get_args()) - elif bound_type == "Terminal": - self._app.oboundary.EditTerminal(self._boundary_name, self._get_args()) - elif bound_type == "Plane Incident Wave": - self._app.oboundary.EditIncidentWave(self._boundary_name, self._get_args()) - else: - return False # pragma: no cover - - self._app._boundaries[self.name] = self._app._boundaries.pop(self._boundary_name) - self._boundary_name = self.name - - return True - - @pyaedt_function_handler() - def update_assignment(self): - """Update the boundary assignment. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - - out = ["Name:" + self.name] - - if "Faces" in self.props: - faces = self.props["Faces"] - faces_out = [] - if not isinstance(faces, list): - faces = [faces] - for f in faces: - if isinstance(f, (EdgePrimitive, FacePrimitive, VertexPrimitive)): - faces_out.append(f.id) - else: - faces_out.append(f) - out += ["Faces:=", faces_out] - - if "Objects" in self.props: - pr = [] - for el in self.props["Objects"]: - try: - pr.append(self._app.modeler[el].name) - except (KeyError, AttributeError): - pass - out += ["Objects:=", pr] - - if len(out) == 1: - return False - - self._app.oboundary.ReassignBoundary(out) - - return True - - -class MaxwellParameters(BoundaryCommon, object): - """Manages parameters data and execution. - - Parameters - ---------- - app : :class:`ansys.aedt.core.maxwell.Maxwell3d`, :class:`ansys.aedt.core.maxwell.Maxwell2d` - Either ``Maxwell3d`` or ``Maxwell2d`` application. - name : str - Name of the boundary. - props : dict, optional - Properties of the boundary. - boundarytype : str, optional - Type of the boundary. - - Examples - -------- - - Create a matrix in Maxwell3D return a ``ansys.aedt.core.modules.boundary.BoundaryObject`` - - >>> from ansys.aedt.core import Maxwell2d - >>> maxwell_2d = Maxwell2d() - >>> coil1 = maxwell_2d.modeler.create_rectangle([8.5,1.5, 0],[8, 3],True,"Coil_1","vacuum") - >>> coil2 = maxwell_2d.modeler.create_rectangle([8.5,1.5, 0],[8, 3],True,"Coil_2","vacuum") - >>> maxwell_2d.assign_matrix(["Coil_1", "Coil_2"]) - """ - - def __init__(self, app, name, props=None, boundarytype=None): - self.auto_update = False - self._app = app - self._name = name - self._props = None - if props: - self._props = BoundaryProps(self, props) - self.type = boundarytype - self._boundary_name = self.name - self.auto_update = True - self.__reduced_matrices = None - self.matrix_assignment = None - - @property - def reduced_matrices(self): - """List of reduced matrix groups for the parent matrix. - - Returns - ------- - dict - Dictionary of reduced matrices where the key is the name of the parent matrix - and the values are in a list of reduced matrix groups. - """ - if self._app.solution_type == "EddyCurrent": - self.__reduced_matrices = {} - cc = self._app.odesign.GetChildObject("Parameters") - parents = cc.GetChildNames() - if self.name in parents: - parent_object = self._app.odesign.GetChildObject("Parameters").GetChildObject(self.name) - parent_type = parent_object.GetPropValue("Type") - if parent_type == "Matrix": - self.matrix_assignment = parent_object.GetPropValue("Selection").split(",") - child_names = parent_object.GetChildNames() - self.__reduced_matrices = [] - for r in child_names: - self.__reduced_matrices.append(MaxwellMatrix(self._app, self.name, r)) - return self.__reduced_matrices - - @property - def object_properties(self): - """Object-oriented properties. - - Returns - ------- - class:`ansys.aedt.core.modeler.cad.elements_3d.BinaryTreeNode` - - """ - - from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode - - cc = self._app.odesign.GetChildObject("Parameters") - child_object = None - if self.name in cc.GetChildNames(): - child_object = self._app.odesign.GetChildObject("Parameters").GetChildObject(self.name) - elif self.name in self._app.odesign.GetChildObject("Parameters").GetChildNames(): - child_object = self._app.odesign.GetChildObject("Parameters").GetChildObject(self.name) - if child_object: - return BinaryTreeNode(self.name, child_object, False) - return False - - @property - def props(self): - """Maxwell parameter data. - - Returns - ------- - :class:BoundaryProps - """ - if self._props: - return self._props - props = self._get_boundary_data(self.name) - - if props: - self._props = BoundaryProps(self, props[0]) - self._type = props[1] - return self._props - - @property - def name(self): - """Boundary name.""" - return self._name - - @name.setter - def name(self, value): - self._name = value - self.update() - - @pyaedt_function_handler() - def _get_args(self, props=None): - """Retrieve arguments. - - Parameters - ---------- - props : - The default is ``None``. - - Returns - ------- - list - List of boundary properties. - - """ - if props is None: - props = self.props - arg = ["NAME:" + self.name] - _dict2arg(props, arg) - return arg - - @pyaedt_function_handler() - def create(self): - """Create a boundary. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - if self.type == "Matrix": - self._app.o_maxwell_parameters.AssignMatrix(self._get_args()) - elif self.type == "Torque": - self._app.o_maxwell_parameters.AssignTorque(self._get_args()) - elif self.type == "Force": - self._app.o_maxwell_parameters.AssignForce(self._get_args()) - elif self.type == "LayoutForce": - self._app.o_maxwell_parameters.AssignLayoutForce(self._get_args()) - else: - return False - return True - - @pyaedt_function_handler() - def update(self): - """Update the boundary. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - if self.type == "Matrix": - self._app.o_maxwell_parameters.EditMatrix(self._boundary_name, self._get_args()) - elif self.type == "Force": - self._app.o_maxwell_parameters.EditForce(self._boundary_name, self._get_args()) - elif self.type == "Torque": - self._app.o_maxwell_parameters.EditTorque(self._boundary_name, self._get_args()) - else: - return False - self._boundary_name = self.name - return True - - @pyaedt_function_handler() - def _create_matrix_reduction(self, red_type, sources, matrix_name=None, join_name=None): - if not self._app.solution_type == "EddyCurrent": - self._app.logger.error("Matrix reduction is possible only in Eddy current solvers.") - return False, False - if not matrix_name: - matrix_name = generate_unique_name("ReducedMatrix", n=3) - if not join_name: - join_name = generate_unique_name("Join" + red_type, n=3) - try: - self._app.o_maxwell_parameters.AddReduceOp( - self.name, - matrix_name, - ["NAME:" + join_name, "Type:=", "Join in " + red_type, "Sources:=", ",".join(sources)], - ) - return matrix_name, join_name - except Exception: - self._app.logger.error("Failed to create Matrix Reduction") - return False, False - - @pyaedt_function_handler() - def join_series(self, sources, matrix_name=None, join_name=None): - """ - - Parameters - ---------- - sources : list - Sources to be included in matrix reduction. - matrix_name : str, optional - name of the string to create. - join_name : str, optional - Name of the Join operation. - - Returns - ------- - (str, str) - Matrix name and Joint name. - - """ - return self._create_matrix_reduction( - red_type="Series", sources=sources, matrix_name=matrix_name, join_name=join_name - ) - - @pyaedt_function_handler() - def join_parallel(self, sources, matrix_name=None, join_name=None): - """ - - Parameters - ---------- - sources : list - Sources to be included in matrix reduction. - matrix_name : str, optional - name of the string to create. - join_name : str, optional - Name of the Join operation. - - Returns - ------- - (str, str) - Matrix name and Joint name. - - """ - return self._create_matrix_reduction( - red_type="Parallel", sources=sources, matrix_name=matrix_name, join_name=join_name - ) - - -class MaxwellMatrix(object): - def __init__(self, app, parent_name, reduced_name): - self._app = app - self.parent_matrix = parent_name - self.name = reduced_name - self.__sources = None - - @property - def sources(self): - """List of matrix sources.""" - if self._app.solution_type == "EddyCurrent": - sources = ( - self._app.odesign.GetChildObject("Parameters") - .GetChildObject(self.parent_matrix) - .GetChildObject(self.name) - .GetChildNames() - ) - self.__sources = {} - for s in sources: - excitations = ( - self._app.odesign.GetChildObject("Parameters") - .GetChildObject(self.parent_matrix) - .GetChildObject(self.name) - .GetChildObject(s) - .GetPropValue("Source") - ) - self.__sources[s] = excitations - return self.__sources - - @pyaedt_function_handler() - def update(self, old_source, source_type, new_source=None, new_excitations=None): - """Update the reduced matrix. - - Parameters - ---------- - old_source : str - Original name of the source to update. - source_type : str - Source type, which can be ``Series`` or ``Parallel``. - new_source : str, optional - New name of the source to update. - The default value is the old source name. - new_excitations : str, optional - List of excitations to include in the matrix reduction. - The default values are excitations included in the source to update. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - """ - if old_source not in self.sources.keys(): - self._app.logger.error("Source does not exist.") - return False - else: - new_excitations = self.sources[old_source] if not new_excitations else new_excitations - if source_type.lower() not in ["series", "parallel"]: - self._app.logger.error("Join type not valid.") - return False - if not new_source: - new_source = old_source - args = ["NAME:" + new_source, "Type:=", "Join in " + source_type, "Sources:=", new_excitations] - self._app.o_maxwell_parameters.EditReduceOp(self.parent_matrix, self.name, old_source, args) - return True - - @pyaedt_function_handler() - def delete(self, source): - """Delete a specified source in a reduced matrix. - - Parameters - ---------- - source : string - Name of the source to delete. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - """ - if source not in self.sources.keys(): - self._app.logger.error("Invalid source name.") - return False - self._app.o_maxwell_parameters.DeleteReduceOp(self.parent_matrix, self.name, source) - return True - - -class FieldSetup(BoundaryCommon, object): - """Manages far field and near field component data and execution. - - Examples - -------- - In this example the sphere1 returned object is a ``ansys.aedt.core.modules.boundary.FarFieldSetup`` - >>> from ansys.aedt.core import Hfss - >>> hfss = Hfss() - >>> sphere1 = hfss.insert_infinite_sphere() - >>> sphere1.props["ThetaStart"] = "-90deg" - >>> sphere1.props["ThetaStop"] = "90deg" - >>> sphere1.props["ThetaStep"] = "2deg" - >>> sphere1.delete() - """ - - def __init__(self, app, component_name, props, component_type): - self.auto_update = False - self._app = app - self.type = component_type - self._name = component_name - self.props = BoundaryProps(self, props) - self.auto_update = True - - @property - def name(self): - """Variable name.""" - return self._name - - @name.setter - def name(self, value): - self._app.oradfield.RenameSetup(self._name, value) - self._name = value - - @pyaedt_function_handler() - def _get_args(self, props=None): - if props is None: - props = self.props - arg = ["NAME:" + self.name] - _dict2arg(props, arg) - return arg - - @pyaedt_function_handler() - def create(self): - """Create a Field Setup Component in HFSS. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - - if self.type == "FarFieldSphere": - self._app.oradfield.InsertInfiniteSphereSetup(self._get_args()) - elif self.type == "NearFieldBox": - self._app.oradfield.InsertBoxSetup(self._get_args()) - elif self.type == "NearFieldSphere": - self._app.oradfield.InsertSphereSetup(self._get_args()) - elif self.type == "NearFieldRectangle": - self._app.oradfield.InsertRectangleSetup(self._get_args()) - elif self.type == "NearFieldLine": - self._app.oradfield.InsertLineSetup(self._get_args()) - elif self.type == "AntennaOverlay": - self._app.oradfield.AddAntennaOverlay(self._get_args()) - elif self.type == "FieldSourceGroup": - self._app.oradfield.AddRadFieldSourceGroup(self._get_args()) - return True - - @pyaedt_function_handler() - def update(self): - """Update the Field Setup in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - - if self.type == "FarFieldSphere": - self._app.oradfield.EditInfiniteSphereSetup(self.name, self._get_args()) - elif self.type == "NearFieldBox": - self._app.oradfield.EditBoxSetup(self.name, self._get_args()) - elif self.type == "NearFieldSphere": - self._app.oradfield.EditSphereSetup(self.name, self._get_args()) - elif self.type == "NearFieldRectangle": - self._app.oradfield.EditRectangleSetup(self.name, self._get_args()) - elif self.type == "NearFieldLine": - self._app.oradfield.EditLineSetup(self.name, self._get_args()) - elif self.type == "AntennaOverlay": - self._app.oradfield.EditAntennaOverlay(self.name, self._get_args()) - elif self.type == "FieldSourceGroup": - self._app.oradfield.EditRadFieldSourceGroup(self._get_args()) - return True - - @pyaedt_function_handler() - def delete(self): - """Delete the Field Setup in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - self._app.oradfield.DeleteSetup([self.name]) - for el in self._app.field_setups: - if el.name == self.name: - self._app.field_setups.remove(el) - return True - - -class FarFieldSetup(FieldSetup, object): - """Manages Far Field Component data and execution. - - Examples - -------- - in this example the sphere1 returned object is a ``ansys.aedt.core.modules.boundary.FarFieldSetup`` - >>> from ansys.aedt.core import Hfss - >>> hfss = Hfss() - >>> sphere1 = hfss.insert_infinite_sphere() - >>> sphere1.props["ThetaStart"] = "-90deg" - >>> sphere1.props["ThetaStop"] = "90deg" - >>> sphere1.props["ThetaStep"] = "2deg" - >>> sphere1.delete() - """ - - def __init__(self, app, component_name, props, component_type, units="deg"): - FieldSetup.__init__(self, app, component_name, props, component_type) - self.units = units - - @property - def definition(self): - """Set/Get the Far Field Angle Definition.""" - return self.props["CSDefinition"] - - @definition.setter - def definition(self, value): - actual_value = self.props["CSDefinition"] - self.props["CSDefinition"] = value - actual_defs = None - defs = None - if actual_value != value and value == "Theta-Phi": - defs = ["ThetaStart", "ThetaStop", "ThetaStep", "PhiStart", "PhiStop", "PhiStep"] - actual_defs = [ - "AzimuthStart", - "AzimuthStop", - "AzimuthStep", - "ElevationStart", - "ElevationStop", - "ElevationStep", - ] - elif actual_value != value and value == "El Over Az": - defs = ["AzimuthStart", "AzimuthStop", "AzimuthStep", "ElevationStart", "ElevationStop", "ElevationStep"] - if actual_value == "Theta-Phi": - actual_defs = ["ThetaStart", "ThetaStop", "ThetaStep", "PhiStart", "PhiStop", "PhiStep"] - else: - actual_defs = [ - "AzimuthStart", - "AzimuthStop", - "AzimuthStep", - "ElevationStart", - "ElevationStop", - "ElevationStep", - ] - elif actual_value != value: - defs = ["ElevationStart", "ElevationStop", "ElevationStep", "AzimuthStart", "AzimuthStop", "AzimuthStep"] - if actual_value == "Theta-Phi": - actual_defs = ["ThetaStart", "ThetaStop", "ThetaStep", "PhiStart", "PhiStop", "PhiStep"] - else: - actual_defs = [ - "ElevationStart", - "ElevationStop", - "ElevationStep", - "AzimuthStart", - "AzimuthStop", - "AzimuthStep", - ] - if actual_defs != defs: - self.props[defs[0]] = self.props[actual_defs[0]] - self.props[defs[1]] = self.props[actual_defs[1]] - self.props[defs[2]] = self.props[actual_defs[2]] - self.props[defs[3]] = self.props[actual_defs[3]] - self.props[defs[4]] = self.props[actual_defs[4]] - self.props[defs[5]] = self.props[actual_defs[5]] - del self.props[actual_defs[0]] - del self.props[actual_defs[1]] - del self.props[actual_defs[2]] - del self.props[actual_defs[3]] - del self.props[actual_defs[4]] - del self.props[actual_defs[5]] - self.update() - - @property - def use_custom_radiation_surface(self): - """Set/Get the Far Field Radiation Surface Enable.""" - return self.props["UseCustomRadiationSurface"] - - @use_custom_radiation_surface.setter - def use_custom_radiation_surface(self, value): - self.props["UseCustomRadiationSurface"] = value - self.update() - - @property - def custom_radiation_surface(self): - """Set/Get the Far Field Radiation Surface FaceList.""" - return self.props["CustomRadiationSurface"] - - @custom_radiation_surface.setter - def custom_radiation_surface(self, value): - if value: - self.props["UseCustomRadiationSurface"] = True - self.props["CustomRadiationSurface"] = value - else: - self.props["UseCustomRadiationSurface"] = False - self.props["CustomRadiationSurface"] = "" - self.update() - - @property - def use_local_coordinate_system(self): - """Set/Get the usage of a custom Coordinate System.""" - return self.props["UseLocalCS"] - - @use_local_coordinate_system.setter - def use_local_coordinate_system(self, value): - self.props["UseLocalCS"] = value - self.update() - - @property - def local_coordinate_system(self): - """Set/Get the custom Coordinate System name.""" - return self.props["CoordSystem"] - - @local_coordinate_system.setter - def local_coordinate_system(self, value): - if value: - self.props["UseLocalCS"] = True - self.props["CoordSystem"] = value - else: - self.props["UseLocalCS"] = False - self.props["CoordSystem"] = "" - self.update() - - @property - def polarization(self): - """Set/Get the Far Field Polarization.""" - return self.props["Polarization"] - - @polarization.setter - def polarization(self, value): - self.props["Polarization"] = value - self.update() - - @property - def slant_angle(self): - """Set/Get the Far Field Slant Angle if Polarization is Set to `Slant`.""" - - if self.props["Polarization"] == "Slant": - return self.props["SlantAngle"] - else: - return - - @slant_angle.setter - def slant_angle(self, value): - self.props["Polarization"] = "Slant" - self.props["SlantAngle"] = value - self.update() - - @property - def theta_start(self): - """Set/Get the Far Field Theta Start Angle if Definition is Set to `Theta-Phi`.""" - - if "ThetaStart" in self.props: - return self.props["ThetaStart"] - else: - return - - @property - def theta_stop(self): - """Set/Get the Far Field Theta Stop Angle if Definition is Set to `Theta-Phi`.""" - - if "ThetaStop" in self.props: - return self.props["ThetaStop"] - else: - return - - @property - def theta_step(self): - """Set/Get the Far Field Theta Step Angle if Definition is Set to `Theta-Phi`.""" - - if "ThetaStep" in self.props: - return self.props["ThetaStep"] - else: - return - - @property - def phi_start(self): - """Set/Get the Far Field Phi Start Angle if Definition is Set to `Theta-Phi`.""" - - if "PhiStart" in self.props: - return self.props["PhiStart"] - else: - return - - @property - def phi_stop(self): - """Set/Get the Far Field Phi Stop Angle if Definition is Set to `Theta-Phi`.""" - - if "PhiStop" in self.props: - return self.props["PhiStop"] - else: - return - - @property - def phi_step(self): - """Set/Get the Far Field Phi Step Angle if Definition is Set to `Theta-Phi`.""" - - if "PhiStep" in self.props: - return self.props["PhiStep"] - else: - return - - @property - def azimuth_start(self): - """Set/Get the Far Field Azimuth Start Angle if Definition is Set to `Az Over El` or `El Over Az`.""" - - if "AzimuthStart" in self.props: - return self.props["AzimuthStart"] - else: - return - - @property - def azimuth_stop(self): - """Set/Get the Far Field Azimuth Stop Angle if Definition is Set to `Az Over El` or `El Over Az`.""" - - if "AzimuthStop" in self.props: - return self.props["AzimuthStop"] - else: - return - - @property - def azimuth_step(self): - """Set/Get the Far Field Azimuth Step Angle if Definition is Set to `Az Over El` or `El Over Az`.""" - - if "AzimuthStep" in self.props: - return self.props["AzimuthStep"] - else: - return - - @property - def elevation_start(self): - """Set/Get the Far Field Elevation Start Angle if Definition is Set to `Az Over El` or `El Over Az`.""" - - if "ElevationStart" in self.props: - return self.props["ElevationStart"] - else: - return - - @property - def elevation_stop(self): - """Set/Get the Far Field Elevation Stop Angle if Definition is Set to `Az Over El` or `El Over Az`.""" - - if "ElevationStop" in self.props: - return self.props["ElevationStop"] - else: - return - - @property - def elevation_step(self): - """Set/Get the Far Field Elevation Step Angle if Definition is Set to `Az Over El` or `El Over Az`.""" - - if "ElevationStep" in self.props: - return self.props["ElevationStep"] - else: - return - - @theta_start.setter - def theta_start(self, value): - if "ThetaStart" in self.props: - self.props["ThetaStart"] = _dim_arg(value, self.units) - self.update() - - @theta_stop.setter - def theta_stop(self, value): - if "ThetaStop" in self.props: - self.props["ThetaStop"] = _dim_arg(value, self.units) - self.update() - - @theta_step.setter - def theta_step(self, value): - if "ThetaStep" in self.props: - self.props["ThetaStep"] = _dim_arg(value, self.units) - self.update() - - @phi_start.setter - def phi_start(self, value): - if "PhiStart" in self.props: - self.props["PhiStart"] = _dim_arg(value, self.units) - self.update() - - @phi_stop.setter - def phi_stop(self, value): - if "PhiStop" in self.props: - self.props["PhiStop"] = _dim_arg(value, self.units) - self.update() - - @phi_step.setter - def phi_step(self, value): - if "PhiStep" in self.props: - self.props["PhiStep"] = _dim_arg(value, self.units) - self.update() - - @azimuth_start.setter - def azimuth_start(self, value): - if "AzimuthStart" in self.props: - self.props["AzimuthStart"] = _dim_arg(value, self.units) - self.update() - - @azimuth_stop.setter - def azimuth_stop(self, value): - if "AzimuthStop" in self.props: - self.props["AzimuthStop"] = _dim_arg(value, self.units) - self.update() - - @azimuth_step.setter - def azimuth_step(self, value): - if "AzimuthStep" in self.props: - self.props["AzimuthStep"] = _dim_arg(value, self.units) - self.update() - - @elevation_start.setter - def elevation_start(self, value): - if "ElevationStart" in self.props: - self.props["ElevationStart"] = _dim_arg(value, self.units) - self.update() - - @elevation_stop.setter - def elevation_stop(self, value): - if "ElevationStop" in self.props: - self.props["ElevationStop"] = _dim_arg(value, self.units) - self.update() - - @elevation_step.setter - def elevation_step(self, value): - if "ElevationStep" in self.props: - self.props["ElevationStep"] = _dim_arg(value, self.units) - self.update() - - -class NearFieldSetup(FieldSetup, object): - """Manages Near Field Component data and execution. - - Examples - -------- - in this example the rectangle1 returned object is a ``ansys.aedt.core.modules.boundary.NearFieldSetup`` - >>> from ansys.aedt.core import Hfss - >>> hfss = Hfss() - >>> rectangle1 = hfss.insert_near_field_rectangle() - """ - - def __init__(self, app, component_name, props, component_type): - FieldSetup.__init__(self, app, component_name, props, component_type) - - -class Matrix(object): - """Manages Matrix in Q3d and Q2d Projects. - - Examples - -------- - - - """ - - def __init__(self, app, name, operations=None): - self._app = app - self.omatrix = self._app.omatrix - self.name = name - self._sources = [] - if operations: - if isinstance(operations, list): - self._operations = operations - else: - self._operations = [operations] - self.CATEGORIES = CATEGORIESQ3D() - - @pyaedt_function_handler() - def sources(self, is_gc_sources=True): - """List of matrix sources. - - Parameters - ---------- - is_gc_sources : bool, - In Q3d, define if to return GC sources or RL sources. Default `True`. - - Returns - ------- - List - """ - if self.name in list(self._app.omatrix.ListReduceMatrixes()): - if self._app.design_type == "Q3D Extractor": - self._sources = list(self._app.omatrix.ListReduceMatrixReducedSources(self.name, is_gc_sources)) - else: - self._sources = list(self._app.omatrix.ListReduceMatrixReducedSources(self.name)) - return self._sources - - @pyaedt_function_handler() - def get_sources_for_plot( - self, - get_self_terms=True, - get_mutual_terms=True, - first_element_filter=None, - second_element_filter=None, - category="C", - ): - """Return a list of source of specified matrix ready to be used in plot reports. - - Parameters - ---------- - get_self_terms : bool - Either if self terms have to be returned or not. - get_mutual_terms : bool - Either if mutual terms have to be returned or not. - first_element_filter : str, optional - Filter to apply to first element of equation. It accepts `*` and `?` as special characters. - second_element_filter : str, optional - Filter to apply to second element of equation. It accepts `*` and `?` as special characters. - category : str - Plot category name as in the report. Eg. "C" is category Capacitance. - Matrix `CATEGORIES` property can be used to map available categories. - - Returns - ------- - list - - Examples - -------- - >>> from ansys.aedt.core import Q3d - >>> q3d = Q3d(project_path) - >>> q3d.matrices[0].get_sources_for_plot(first_element_filter="Bo?1", - ... second_element_filter="GND*", category="DCL") - """ - if not first_element_filter: - first_element_filter = "*" - if not second_element_filter: - second_element_filter = "*" - is_cg = False - if category in [self.CATEGORIES.Q3D.C, self.CATEGORIES.Q3D.G]: - is_cg = True - list_output = [] - if get_self_terms: - for el in self.sources(is_gc_sources=is_cg): - value = f"{category}({el},{el})" - if filter_tuple(value, first_element_filter, second_element_filter): - list_output.append(value) - if get_mutual_terms: - for el1 in self.sources(is_gc_sources=is_cg): - for el2 in self.sources(is_gc_sources=is_cg): - if el1 != el2: - value = f"{category}({el1},{el2})" - if filter_tuple(value, first_element_filter, second_element_filter): - list_output.append(value) - return list_output - - @property - def operations(self): - """List of matrix operations. - - Returns - ------- - List - """ - if self.name in list(self._app.omatrix.ListReduceMatrixes()): - self._operations = self._app.omatrix.ListReduceMatrixOperations(self.name) - return self._operations - - @pyaedt_function_handler() - def create( - self, - source_names=None, - new_net_name=None, - new_source_name=None, - new_sink_name=None, - ): - """Create a new matrix. - - Parameters - ---------- - source_names : str, list - List or str containing the content of the matrix reduction (eg. source name). - new_net_name : str, optional - Name of the new net. The default is ``None``. - new_source_name : str, optional - Name of the new source. The default is ``None``. - new_sink_name : str, optional - Name of the new sink. The default is ``None``. - - Returns - ------- - bool - `True` if succeeded. - """ - if not isinstance(source_names, list) and source_names: - source_names = [source_names] - - command = self._write_command(source_names, new_net_name, new_source_name, new_sink_name) - self.omatrix.InsertRM(self.name, command) - return True - - @pyaedt_function_handler() - def delete(self): - """Delete current matrix. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - """ - self.omatrix.DeleteRM(self.name) - for el in self._app.matrices: - if el.name == self.name: - self._app.matrices.remove(el) - return True - - @pyaedt_function_handler() - def add_operation( - self, - operation_type, - source_names=None, - new_net_name=None, - new_source_name=None, - new_sink_name=None, - ): - """Add a new operation to existing matrix. - - Parameters - ---------- - operation_type : str - Operation to perform - source_names : str, list - List or str containing the content of the matrix reduction (eg. source name). - new_net_name : str, optional - Name of the new net. The default is ``None``. - new_source_name : str, optional - Name of the new source. The default is ``None``. - new_sink_name : str, optional - Name of the new sink. The default is ``None``. - - Returns - ------- - bool - `True` if succeeded. - """ - self._operations.append(operation_type) - if not isinstance(source_names, list) and source_names: - source_names = [source_names] - - if not new_net_name: - new_net_name = generate_unique_name("Net") - - if not new_source_name: - new_source_name = generate_unique_name("Source") - - if not new_sink_name: - new_sink_name = generate_unique_name("Sink") - - command = self._write_command(source_names, new_net_name, new_source_name, new_sink_name) - self.omatrix.RMAddOp(self.name, command) - return True - - @pyaedt_function_handler() - def _write_command(self, source_names, new_name, new_source, new_sink): - if self._operations[-1] == "JoinSeries": - command = f"""{self._operations[-1]}('{new_name}', '{"', '".join(source_names)}')""" - elif self._operations[-1] == "JoinParallel": - command = ( - f"""{self._operations[-1]}('{new_name}', '{new_source}', '{new_sink}', '{"', '".join(source_names)}')""" - ) - elif self._operations[-1] == "JoinSelectedTerminals": - command = f"""{self._operations[-1]}('', '{"', '".join(source_names)}')""" - elif self._operations[-1] == "FloatInfinity": - command = "FloatInfinity()" - elif self._operations[-1] == "AddGround": - command = f"""{self._operations[-1]}(SelectionArray[{len(source_names)}: '{"', '".join(source_names)}'], - OverrideInfo())""" - elif ( - self._operations[-1] == "SetReferenceGround" - or self._operations[-1] == "SetReferenceGround" - or self._operations[-1] == "Float" - ): - command = f"""{self._operations[-1]}(SelectionArray[{len(source_names)}: '{"', '".join(source_names)}'], - OverrideInfo())""" - elif self._operations[-1] == "Parallel" or self._operations[-1] == "DiffPair": - id_ = 0 - for el in self._app.boundaries: - if el.name == source_names[0]: - id_ = self._app.modeler[el.props["Objects"][0]].id - command = f"""{self._operations[-1]}(SelectionArray[{len(source_names)}: '{"', '".join(source_names)}'], - OverrideInfo({id_}, '{new_name}'))""" - else: - command = f"""{self._operations[-1]}('{"', '".join(source_names)}')""" - return command - - -class BoundaryObject3dLayout(BoundaryCommon, object): - """Manages boundary data and execution for Hfss3dLayout. - - Parameters - ---------- - app : object - An AEDT application from ``ansys.aedt.core.application``. - name : str - Name of the boundary. - props : dict - Properties of the boundary. - boundarytype : str - Type of the boundary. - """ - - def __init__(self, app, name, props, boundarytype): - self.auto_update = False - self._app = app - self._name = name - self._props = None - if props: - self._props = BoundaryProps(self, props) - self.type = boundarytype - self._boundary_name = self.name - self.auto_update = True - - @property - def object_properties(self): - """Object-oriented properties. - - Returns - ------- - class:`ansys.aedt.core.modeler.cad.elements_3d.BinaryTreeNode` - - """ - from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode - - cc = self._app.odesign.GetChildObject("Excitations") - child_object = None - if self.name in cc.GetChildNames(): - child_object = self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) - elif self.name in self._app.odesign.GetChildObject("Excitations").GetChildNames(): - child_object = self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) - if child_object: - return BinaryTreeNode(self.name, child_object, False) - - if "Boundaries" in self._app.odesign.GetChildNames(): - cc = self._app.odesign.GetChildObject("Boundaries") - if self.name in cc.GetChildNames(): - child_object = self._app.odesign.GetChildObject("Boundaries").GetChildObject(self.name) - elif self.name in self._app.odesign.GetChildObject("Boundaries").GetChildNames(): - child_object = self._app.odesign.GetChildObject("Boundaries").GetChildObject(self.name) - if child_object: - return BinaryTreeNode(self.name, child_object, False) - return False - - @property - def name(self): - """Boundary Name.""" - return self._name - - @name.setter - def name(self, value): - if "Port" in self.props: - self.auto_update = False - self.props["Port"] = value - self.auto_update = True - self.update() - self._name = value - - @property - def props(self): - """Excitation data. - - Returns - ------- - :class:BoundaryProps - """ - if self._props: - return self._props - props = self._get_boundary_data(self.name) - - if props: - self._props = BoundaryProps(self, props[0]) - self._type = props[1] - return self._props - - @pyaedt_function_handler() - def _get_args(self, props=None): - """Retrieve arguments. - - Parameters - ---------- - props : - The default is ``None``. - - Returns - ------- - list - List of boundary properties. - - """ - if props is None: - props = self.props - arg = ["NAME:" + self.name] - _dict2arg(props, arg) - return arg - - @pyaedt_function_handler() - def _refresh_properties(self): - if len(self._app.oeditor.GetProperties("EM Design", f"Excitations:{self.name}")) != len(self.props): - propnames = self._app.oeditor.GetProperties("EM Design", f"Excitations:{self.name}") - props = {} - for prop in propnames: - props[prop] = self._app.oeditor.GetPropertyValue("EM Design", f"Excitations:{self.name}", prop) - self._props = BoundaryProps(self, props) - - @pyaedt_function_handler() - def update(self): - """Update the boundary. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - updated = False - for el in list(self.props.keys()): - if el == "Port" and self._name != self.props[el]: - self._app.oeditor.SetPropertyValue("EM Design", "Excitations:" + self.name, el, self.props[el]) - self._name = self.props[el] - elif el in self._app.oeditor.GetProperties("EM Design", f"Excitations:{self.name}") and self.props[ - el - ] != self._app.oeditor.GetPropertyValue("EM Design", "Excitations:" + self.name, el): - self._app.oeditor.SetPropertyValue("EM Design", "Excitations:" + self.name, el, self.props[el]) - updated = True - - if updated: - self._refresh_properties() - - return True - - -class Sources(object): - """Manages sources in Circuit projects.""" - - def __init__(self, app, name, source_type=None): - self._app = app - self._name = name - self._props = self._source_props(name, source_type) - self.source_type = source_type - if not source_type: - self.source_type = self._source_type_by_key() - self._auto_update = True - - @property - def name(self): - """Source name. - - Returns - ------- - str - """ - return self._name - - @name.setter - def name(self, source_name): - if source_name not in self._app.source_names: - if source_name != self._name: - original_name = self._name - self._name = source_name - for port in self._app.excitations: - if original_name in self._app.excitation_objects[port].props["EnabledPorts"]: - self._app.excitation_objects[port].props["EnabledPorts"] = [ - w.replace(original_name, source_name) - for w in self._app.excitation_objects[port].props["EnabledPorts"] - ] - if original_name in self._app.excitation_objects[port].props["EnabledAnalyses"]: - self._app.excitation_objects[port].props["EnabledAnalyses"][source_name] = ( - self._app.excitation_objects[port].props["EnabledAnalyses"].pop(original_name) - ) - self.update(original_name) - else: - self._logger.warning("Name %s already assigned in the design", source_name) - - @property - def _logger(self): - """Logger.""" - return self._app.logger - - @pyaedt_function_handler() - def _source_props(self, source, source_type=None): - source_prop_dict = {} - if source in self._app.source_names: - source_aedt_props = self._app.odesign.GetChildObject("Excitations").GetChildObject(source) - for el in source_aedt_props.GetPropNames(): - if el == "CosimDefinition": - source_prop_dict[el] = None - elif el == "FreqDependentSourceData": - data = self._app.design_properties["NexximSources"]["Data"][source]["FDSFileName"] - freqs = re.findall(r"freqs=\[(.*?)\]", data) - magnitude = re.findall(r"magnitude=\[(.*?)\]", data) - angle = re.findall(r"angle=\[(.*?)\]", data) - vreal = re.findall(r"vreal=\[(.*?)\]", data) - vimag = re.findall(r"vimag=\[(.*?)\]", data) - source_file = re.findall("voltage_source_file=", data) - source_prop_dict["frequencies"] = None - source_prop_dict["vmag"] = None - source_prop_dict["vang"] = None - source_prop_dict["vreal"] = None - source_prop_dict["vimag"] = None - source_prop_dict["fds_filename"] = None - source_prop_dict["magnitude_angle"] = False - source_prop_dict["FreqDependentSourceData"] = data - if freqs: - source_prop_dict["frequencies"] = [float(i) for i in freqs[0].split()] - if magnitude: - source_prop_dict["vmag"] = [float(i) for i in magnitude[0].split()] - if angle: - source_prop_dict["vang"] = [float(i) for i in angle[0].split()] - if vreal: - source_prop_dict["vreal"] = [float(i) for i in vreal[0].split()] - if vimag: - source_prop_dict["vimag"] = [float(i) for i in vimag[0].split()] - if source_file: - source_prop_dict["fds_filename"] = data[len(re.findall("voltage_source_file=", data)[0]) :] - else: - if freqs and magnitude and angle: - source_prop_dict["magnitude_angle"] = True - elif freqs and vreal and vimag: - source_prop_dict["magnitude_angle"] = False - - elif el != "Name" and el != "Noise": - source_prop_dict[el] = source_aedt_props.GetPropValue(el) - if not source_prop_dict[el]: - source_prop_dict[el] = "" - else: - if source_type in SourceKeys.SourceNames: - command_template = SourceKeys.SourceTemplates[source_type] - commands = copy.deepcopy(command_template) - props = [value for value in commands if type(value) == list] - for el in props[0]: - if isinstance(el, list): - if el[0] == "CosimDefinition": - source_prop_dict[el[0]] = None - elif el[0] == "FreqDependentSourceData": - source_prop_dict["frequencies"] = None - source_prop_dict["vmag"] = None - source_prop_dict["vang"] = None - source_prop_dict["vreal"] = None - source_prop_dict["vimag"] = None - source_prop_dict["fds_filename"] = None - source_prop_dict["magnitude_angle"] = True - source_prop_dict["FreqDependentSourceData"] = "" - - elif el[0] != "ModelName" and el[0] != "LabelID": - source_prop_dict[el[0]] = el[3] - - return source_prop_dict - - @pyaedt_function_handler() - def _update_command(self, name, source_prop_dict, source_type, fds_filename=None): - command_template = SourceKeys.SourceTemplates[source_type] - commands = copy.deepcopy(command_template) - commands[0] = "NAME:" + name - commands[10] = source_prop_dict["Netlist"] - if fds_filename: - commands[14] = fds_filename - cont = 0 - props = [value for value in commands if type(value) == list] - for command in props[0]: - if isinstance(command, list) and command[0] in source_prop_dict.keys() and command[0] != "CosimDefinition": - if command[0] == "Netlist": - props[0].pop(cont) - elif command[0] == "file" and source_prop_dict[command[0]]: - props[0][cont][3] = source_prop_dict[command[0]] - props[0][cont][4] = source_prop_dict[command[0]] - elif command[0] == "FreqDependentSourceData" and fds_filename: - props[0][cont][3] = fds_filename - props[0][cont][4] = fds_filename - else: - props[0][cont][3] = source_prop_dict[command[0]] - cont += 1 - - return commands - - @pyaedt_function_handler() - def _source_type_by_key(self): - for source_name in SourceKeys.SourceNames: - template = SourceKeys.SourceProps[source_name] - if list(self._props.keys()) == template: - return source_name - return None - - @pyaedt_function_handler() - def update(self, original_name=None, new_source=None): - """Update the source in AEDT. - - Parameters - ---------- - original_name : str, optional - Original name of the source. The default value is ``None``. - new_source : str, optional - New name of the source. The default value is ``None``. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - - arg0 = ["NAME:Data"] - if self.source_type != "VoltageFrequencyDependent": - fds_filename = None - else: - fds_filename = self._props["FreqDependentSourceData"] - - for source in self._app.sources: - if "FreqDependentSourceData" in self._app.sources[source]._props.keys(): - fds_filename_source = self._app.sources[source]._props["FreqDependentSourceData"] - else: - fds_filename_source = None - if source == self.name: - arg0.append(list(self._update_command(source, self._props, self.source_type, fds_filename))) - elif source != self.name and original_name == source: - arg0.append( - list( - self._update_command( - self.name, self._props, self._app.sources[source].source_type, fds_filename - ) - ) - ) - else: - arg0.append( - list( - self._update_command( - source, - self._app.sources[source]._props, - self._app.sources[source].source_type, - fds_filename_source, - ) - ) - ) - - if new_source and new_source not in self._app.sources: - arg0.append(list(self._update_command(self.name, self._props, self.source_type, fds_filename))) - - arg1 = ["NAME:NexximSources", ["NAME:NexximSources", arg0]] - arg2 = ["NAME:ComponentConfigurationData"] - - # Check Ports with Sources - arg3 = ["NAME:EnabledPorts"] - for source_name in self._app.sources: - excitation_source = [] - for port in self._app.excitations: - if source_name in self._app.excitation_objects[port]._props["EnabledPorts"]: - excitation_source.append(port) - arg3.append(source_name + ":=") - arg3.append(excitation_source) - - if new_source and new_source not in self._app.sources: - arg3.append(new_source + ":=") - arg3.append([]) - - arg4 = ["NAME:EnabledMultipleComponents"] - for source_name in self._app.sources: - arg4.append(source_name + ":=") - arg4.append([]) - - if new_source and new_source not in self._app.sources: - arg4.append(new_source + ":=") - arg4.append([]) - - arg5 = ["NAME:EnabledAnalyses"] - for source_name in self._app.sources: - arg6 = ["NAME:" + source_name] - for port in self._app.excitations: - if source_name in self._app.excitation_objects[port]._props["EnabledAnalyses"]: - arg6.append(port + ":=") - arg6.append(self._app.excitation_objects[port]._props["EnabledAnalyses"][source_name]) - else: - arg6.append(port + ":=") - arg6.append([]) - arg5.append(arg6) - - if new_source and new_source not in self._app.sources: - arg6 = ["NAME:" + new_source] - for port in self._app.excitations: - arg6.append(port + ":=") - arg6.append([]) - arg5.append(arg6) - - arg7 = ["NAME:ComponentConfigurationData", arg3, arg4, arg5] - arg2.append(arg7) - - self._app.odesign.UpdateSources(arg1, arg2) - return True - - @pyaedt_function_handler() - def delete(self): - """Delete the source in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - self._app.modeler._odesign.DeleteSource(self.name) - for port in self._app.excitations: - if self.name in self._app.excitation_objects[port].props["EnabledPorts"]: - self._app.excitation_objects[port].props["EnabledPorts"].remove(self.name) - if self.name in self._app.excitation_objects[port].props["EnabledAnalyses"]: - del self._app.excitation_objects[port].props["EnabledAnalyses"][self.name] - return True - - @pyaedt_function_handler() - def create(self): - """Create a new source in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - self.update(original_name=None, new_source=self.name) - return True - - -class PowerSinSource(Sources, object): - """Power Sinusoidal Class.""" - - def __init__(self, app, name, source_type=None): - Sources.__init__(self, app, name, source_type) - - @property - def _child(self): - return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) - - @property - def ac_magnitude(self): - """AC magnitude value. - - Returns - ------- - str - """ - return self._props["ACMAG"] - - @ac_magnitude.setter - def ac_magnitude(self, value): - self._props["ACMAG"] = value - self._child.SetPropValue("ACMAG", value) - - @property - def ac_phase(self): - """AC phase value. - - Returns - ------- - str - """ - return self._props["ACPHASE"] - - @ac_phase.setter - def ac_phase(self, value): - self._props["ACPHASE"] = value - self._child.SetPropValue("ACPHASE", value) - - @property - def dc_magnitude(self): - """DC voltage value. - - Returns - ------- - str - """ - return self._props["DC"] - - @dc_magnitude.setter - def dc_magnitude(self, value): - self._props["DC"] = value - self._child.SetPropValue("DC", value) - - @property - def power_offset(self): - """Power offset from zero watts. - - Returns - ------- - str - """ - return self._props["VO"] - - @power_offset.setter - def power_offset(self, value): - self._props["VO"] = value - self._child.SetPropValue("VO", value) - - @property - def power_magnitude(self): - """Available power of the source above power offset. - - Returns - ------- - str - """ - return self._props["POWER"] - - @power_magnitude.setter - def power_magnitude(self, value): - self._props["POWER"] = value - self._child.SetPropValue("POWER", value) - - @property - def frequency(self): - """Frequency. - - Returns - ------- - str - """ - return self._props["FREQ"] - - @frequency.setter - def frequency(self, value): - self._props["FREQ"] = value - self._child.SetPropValue("FREQ", value) - - @property - def delay(self): - """Delay to start of sine wave. - - Returns - ------- - str - """ - return self._props["TD"] - - @delay.setter - def delay(self, value): - self._props["TD"] = value - self._child.SetPropValue("TD", value) - - @property - def damping_factor(self): - """Damping factor. - - Returns - ------- - str - """ - return self._props["ALPHA"] - - @damping_factor.setter - def damping_factor(self, value): - self._props["ALPHA"] = value - self._child.SetPropValue("ALPHA", value) - - @property - def phase_delay(self): - """Phase delay. - - Returns - ------- - str - """ - return self._props["THETA"] - - @phase_delay.setter - def phase_delay(self, value): - self._props["THETA"] = value - self._child.SetPropValue("THETA", value) - - @property - def tone(self): - """Frequency to use for harmonic balance. - - Returns - ------- - str - """ - return self._props["TONE"] - - @tone.setter - def tone(self, value): - self._props["TONE"] = value - self._child.SetPropValue("TONE", value) - - -class PowerIQSource(Sources, object): - """Power IQ Class.""" - - def __init__(self, app, name, source_type=None): - Sources.__init__(self, app, name, source_type) - - @property - def _child(self): - return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) - - @property - def carrier_frequency(self): - """Carrier frequency value. - - Returns - ------- - str - """ - return self._props["FC"] - - @carrier_frequency.setter - def carrier_frequency(self, value): - self._props["FC"] = value - self._child.SetPropValue("FC", value) - - @property - def sampling_time(self): - """Sampling time value. - - Returns - ------- - str - """ - return self._props["TS"] - - @sampling_time.setter - def sampling_time(self, value): - self._props["TS"] = value - self._child.SetPropValue("TS", value) - - @property - def dc_magnitude(self): - """DC voltage value. - - Returns - ------- - str - """ - return self._props["DC"] - - @dc_magnitude.setter - def dc_magnitude(self, value): - self._props["DC"] = value - self._child.SetPropValue("DC", value) - - @property - def repeat_from(self): - """Repeat from time. - - Returns - ------- - str - """ - return self._props["R"] - - @repeat_from.setter - def repeat_from(self, value): - self._props["R"] = value - self._child.SetPropValue("R", value) - - @property - def delay(self): - """Delay to start of sine wave. - - Returns - ------- - str - """ - return self._props["TD"] - - @delay.setter - def delay(self, value): - self._props["TD"] = value - self._child.SetPropValue("TD", value) - - @property - def carrier_amplitude_voltage(self): - """Carrier amplitude value, voltage-based. - - Returns - ------- - str - """ - return self._props["V"] - - @carrier_amplitude_voltage.setter - def carrier_amplitude_voltage(self, value): - self._props["V"] = value - self._child.SetPropValue("V", value) - - @property - def carrier_amplitude_power(self): - """Carrier amplitude value, power-based. - - Returns - ------- - str - """ - return self._props["VA"] - - @carrier_amplitude_power.setter - def carrier_amplitude_power(self, value): - self._props["VA"] = value - self._child.SetPropValue("VA", value) - - @property - def carrier_offset(self): - """Carrier offset. - - Returns - ------- - str - """ - return self._props["VO"] - - @carrier_offset.setter - def carrier_offset(self, value): - self._props["VO"] = value - self._child.SetPropValue("VO", value) - - @property - def real_impedance(self): - """Real carrier impedance. - - Returns - ------- - str - """ - return self._props["RZ"] - - @real_impedance.setter - def real_impedance(self, value): - self._props["RZ"] = value - self._child.SetPropValue("RZ", value) - - @property - def imaginary_impedance(self): - """Imaginary carrier impedance. - - Returns - ------- - str - """ - return self._props["IZ"] - - @imaginary_impedance.setter - def imaginary_impedance(self, value): - self._props["IZ"] = value - self._child.SetPropValue("IZ", value) - - @property - def damping_factor(self): - """Damping factor. - - Returns - ------- - str - """ - return self._props["ALPHA"] - - @damping_factor.setter - def damping_factor(self, value): - self._props["ALPHA"] = value - self._child.SetPropValue("ALPHA", value) - - @property - def phase_delay(self): - """Phase delay. - - Returns - ------- - str - """ - return self._props["THETA"] - - @phase_delay.setter - def phase_delay(self, value): - self._props["THETA"] = value - self._child.SetPropValue("THETA", value) - - @property - def tone(self): - """Frequency to use for harmonic balance. - - Returns - ------- - str - """ - return self._props["TONE"] - - @tone.setter - def tone(self, value): - self._props["TONE"] = value - self._child.SetPropValue("TONE", value) - - @property - def i_q_values(self): - """I and Q value at each timepoint. - - Returns - ------- - str - """ - i_q = [] - for cont in range(1, 20): - i_q.append( - [self._props["time" + str(cont)], self._props["ival" + str(cont)], self._props["qval" + str(cont)]] - ) - return i_q - - @i_q_values.setter - def i_q_values(self, value): - cont = 0 - for point in value: - self._props["time" + str(cont + 1)] = point[0] - self._child.SetPropValue("time" + str(cont + 1), point[0]) - self._props["ival" + str(cont + 1)] = point[1] - self._child.SetPropValue("ival" + str(cont + 1), point[1]) - self._props["qval" + str(cont + 1)] = point[2] - self._child.SetPropValue("qval" + str(cont + 1), point[2]) - cont += 1 - - @property - def file( - self, - ): - """File path with I and Q values. - - Returns - ------- - str - """ - return self._props["file"] - - @file.setter - def file(self, value): - self._props["file"] = value - self.update() - - -class VoltageFrequencyDependentSource(Sources, object): - """Voltage Frequency Dependent Class.""" - - def __init__(self, app, name, source_type=None): - Sources.__init__(self, app, name, source_type) - - @property - def _child(self): - return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) - - @property - def frequencies(self): - """List of frequencies in ``Hz``. - - Returns - ------- - list - """ - return self._props["frequencies"] - - @frequencies.setter - def frequencies(self, value): - self._props["frequencies"] = [float(i) for i in value] - self._update_prop() - - @property - def vmag(self): - """List of magnitudes in ``V``. - - Returns - ------- - list - """ - return self._props["vmag"] - - @vmag.setter - def vmag(self, value): - self._props["vmag"] = [float(i) for i in value] - self._update_prop() - - @property - def vang(self): - """List of angles in ``rad``. - - Returns - ------- - list - """ - return self._props["vang"] - - @vang.setter - def vang(self, value): - self._props["vang"] = [float(i) for i in value] - self._update_prop() - - @property - def vreal(self): - """List of real values in ``V``. - - Returns - ------- - list - """ - return self._props["vreal"] - - @vreal.setter - def vreal(self, value): - self._props["vreal"] = [float(i) for i in value] - self._update_prop() - - @property - def vimag(self): - """List of imaginary values in ``V``. - - Returns - ------- - list - """ - return self._props["vimag"] - - @vimag.setter - def vimag(self, value): - self._props["vimag"] = [float(i) for i in value] - self._update_prop() - - @property - def magnitude_angle(self): - """Enable magnitude and angle data. - - Returns - ------- - bool - """ - return self._props["magnitude_angle"] - - @magnitude_angle.setter - def magnitude_angle(self, value): - self._props["magnitude_angle"] = value - self._update_prop() - - @property - def fds_filename(self): - """FDS file path. - - Returns - ------- - bool - """ - return self._props["fds_filename"] - - @fds_filename.setter - def fds_filename(self, name): - if not name: - self._props["fds_filename"] = None - self._update_prop() - else: - self._props["fds_filename"] = name - self._props["FreqDependentSourceData"] = "voltage_source_file=" + name - self.update() - - @pyaedt_function_handler() - def _update_prop(self): - if ( - self._props["vmag"] - and self._props["vang"] - and self._props["frequencies"] - and self._props["magnitude_angle"] - and not self._props["fds_filename"] - ): - if len(self._props["vmag"]) == len(self._props["vang"]) == len(self._props["frequencies"]): - self._props["FreqDependentSourceData"] = ( - "freqs=" - + str(self._props["frequencies"]).replace(",", "") - + " vmag=" - + str(self._props["vmag"]).replace(",", "") - + " vang=" - + str(self._props["vang"]).replace(",", "") - ) - self.update() - elif ( - self._props["vreal"] - and self._props["vimag"] - and self._props["frequencies"] - and not self._props["magnitude_angle"] - and not self._props["fds_filename"] - ): - if len(self._props["vreal"]) == len(self._props["vimag"]) == len(self._props["frequencies"]): - self._props["FreqDependentSourceData"] = ( - "freqs=" - + str(self._props["frequencies"]).replace(",", "") - + " vreal=" - + str(self._props["vreal"]).replace(",", "") - + " vimag=" - + str(self._props["vimag"]).replace(",", "") - ) - self.update() - else: - self._props["FreqDependentSourceData"] = "" - self.update() - return True - - -class VoltageDCSource(Sources, object): - """Power Sinusoidal Class.""" - - def __init__(self, app, name, source_type=None): - Sources.__init__(self, app, name, source_type) - - @property - def _child(self): - return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) - - @property - def ac_magnitude(self): - """AC magnitude value. - - Returns - ------- - str - """ - return self._props["ACMAG"] - - @ac_magnitude.setter - def ac_magnitude(self, value): - self._props["ACMAG"] = value - self._child.SetPropValue("ACMAG", value) - - @property - def ac_phase(self): - """AC phase value. - - Returns - ------- - str - """ - return self._props["ACPHASE"] - - @ac_phase.setter - def ac_phase(self, value): - self._props["ACPHASE"] = value - self._child.SetPropValue("ACPHASE", value) - - @property - def dc_magnitude(self): - """DC voltage value. - - Returns - ------- - str - """ - return self._props["DC"] - - @dc_magnitude.setter - def dc_magnitude(self, value): - self._props["DC"] = value - self._child.SetPropValue("DC", value) - - -class VoltageSinSource(Sources, object): - """Power Sinusoidal Class.""" - - def __init__(self, app, name, source_type=None): - Sources.__init__(self, app, name, source_type) - - @property - def _child(self): - return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) - - @property - def ac_magnitude(self): - """AC magnitude value. - - Returns - ------- - str - """ - return self._props["ACMAG"] - - @ac_magnitude.setter - def ac_magnitude(self, value): - self._props["ACMAG"] = value - self._child.SetPropValue("ACMAG", value) - - @property - def ac_phase(self): - """AC phase value. - - Returns - ------- - str - """ - return self._props["ACPHASE"] - - @ac_phase.setter - def ac_phase(self, value): - self._props["ACPHASE"] = value - self._child.SetPropValue("ACPHASE", value) - - @property - def dc_magnitude(self): - """DC voltage value. - - Returns - ------- - str - """ - return self._props["DC"] - - @dc_magnitude.setter - def dc_magnitude(self, value): - self._props["DC"] = value - self._child.SetPropValue("DC", value) - - @property - def voltage_amplitude(self): - """Voltage amplitude. - - Returns - ------- - str - """ - return self._props["VA"] - - @voltage_amplitude.setter - def voltage_amplitude(self, value): - self._props["VA"] = value - self._child.SetPropValue("VA", value) - - @property - def voltage_offset(self): - """Voltage offset from zero watts. - - Returns - ------- - str - """ - return self._props["VO"] - - @voltage_offset.setter - def voltage_offset(self, value): - self._props["VO"] = value - self._child.SetPropValue("VO", value) - - @property - def frequency(self): - """Frequency. - - Returns - ------- - str - """ - return self._props["FREQ"] - - @frequency.setter - def frequency(self, value): - self._props["FREQ"] = value - self._child.SetPropValue("FREQ", value) - - @property - def delay(self): - """Delay to start of sine wave. - - Returns - ------- - str - """ - return self._props["TD"] - - @delay.setter - def delay(self, value): - self._props["TD"] = value - self._child.SetPropValue("TD", value) - - @property - def damping_factor(self): - """Damping factor. - - Returns - ------- - str - """ - return self._props["ALPHA"] - - @damping_factor.setter - def damping_factor(self, value): - self._props["ALPHA"] = value - self._child.SetPropValue("ALPHA", value) - - @property - def phase_delay(self): - """Phase delay. - - Returns - ------- - str - """ - return self._props["THETA"] - - @phase_delay.setter - def phase_delay(self, value): - self._props["THETA"] = value - self._child.SetPropValue("THETA", value) - - @property - def tone(self): - """Frequency to use for harmonic balance. - - Returns - ------- - str - """ - return self._props["TONE"] - - @tone.setter - def tone(self, value): - self._props["TONE"] = value - self._child.SetPropValue("TONE", value) - - -class CurrentSinSource(Sources, object): - """Current Sinusoidal Class.""" - - def __init__(self, app, name, source_type=None): - Sources.__init__(self, app, name, source_type) - - @property - def _child(self): - return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) - - @property - def ac_magnitude(self): - """AC magnitude value. - - Returns - ------- - str - """ - return self._props["ACMAG"] - - @ac_magnitude.setter - def ac_magnitude(self, value): - self._props["ACMAG"] = value - self._child.SetPropValue("ACMAG", value) - - @property - def ac_phase(self): - """AC phase value. - - Returns - ------- - str - """ - return self._props["ACPHASE"] - - @ac_phase.setter - def ac_phase(self, value): - self._props["ACPHASE"] = value - self._child.SetPropValue("ACPHASE", value) - - @property - def dc_magnitude(self): - """DC current value. - - Returns - ------- - str - """ - return self._props["DC"] - - @dc_magnitude.setter - def dc_magnitude(self, value): - self._props["DC"] = value - self._child.SetPropValue("DC", value) - - @property - def current_amplitude(self): - """Current amplitude. - - Returns - ------- - str - """ - return self._props["VA"] - - @current_amplitude.setter - def current_amplitude(self, value): - self._props["VA"] = value - self._child.SetPropValue("VA", value) - - @property - def current_offset(self): - """Current offset. - - Returns - ------- - str - """ - return self._props["VO"] - - @current_offset.setter - def current_offset(self, value): - self._props["VO"] = value - self._child.SetPropValue("VO", value) - - @property - def frequency(self): - """Frequency. - - Returns - ------- - str - """ - return self._props["FREQ"] - - @frequency.setter - def frequency(self, value): - self._props["FREQ"] = value - self._child.SetPropValue("FREQ", value) - - @property - def delay(self): - """Delay to start of sine wave. - - Returns - ------- - str - """ - return self._props["TD"] - - @delay.setter - def delay(self, value): - self._props["TD"] = value - self._child.SetPropValue("TD", value) - - @property - def damping_factor(self): - """Damping factor. - - Returns - ------- - str - """ - return self._props["ALPHA"] - - @damping_factor.setter - def damping_factor(self, value): - self._props["ALPHA"] = value - self._child.SetPropValue("ALPHA", value) - - @property - def phase_delay(self): - """Phase delay. - - Returns - ------- - str - """ - return self._props["THETA"] - - @phase_delay.setter - def phase_delay(self, value): - self._props["THETA"] = value - self._child.SetPropValue("THETA", value) - - @property - def multiplier(self): - """Multiplier for simulating multiple parallel current sources. - - Returns - ------- - str - """ - return self._props["M"] - - @multiplier.setter - def multiplier(self, value): - self._props["M"] = value - self._child.SetPropValue("M", value) - - @property - def tone(self): - """Frequency to use for harmonic balance. - - Returns - ------- - str - """ - return self._props["TONE"] - - @tone.setter - def tone(self, value): - self._props["TONE"] = value - self._child.SetPropValue("TONE", value) - - -class Excitations(object): - """Manages Excitations in Circuit Projects. - - Examples - -------- - - """ - - def __init__(self, app, name): - self._app = app - self._name = name - for comp in self._app.modeler.schematic.components: - if ( - "PortName" in self._app.modeler.schematic.components[comp].parameters.keys() - and self._app.modeler.schematic.components[comp].parameters["PortName"] == self.name - ): - self.schematic_id = comp - self.id = self._app.modeler.schematic.components[comp].id - self._angle = self._app.modeler.schematic.components[comp].angle - self.levels = self._app.modeler.schematic.components[comp].levels - self._location = self._app.modeler.schematic.components[comp].location - self._mirror = self._app.modeler.schematic.components[comp].mirror - self.pins = self._app.modeler.schematic.components[comp].pins - self._use_symbol_color = self._app.modeler.schematic.components[comp].usesymbolcolor - break - self._props = self._excitation_props(name) - self._auto_update = True - - @property - def name(self): - """Excitation name. - - Returns - ------- - str - """ - return self._name - - @name.setter - def name(self, port_name): - if port_name not in self._app.excitations: - if port_name != self._name: - # Take previous properties - self._app.odesign.RenamePort(self._name, port_name) - self._name = port_name - self._app.modeler.schematic.components[self.schematic_id].name = "IPort@" + port_name - self.pins[0].name = "IPort@" + port_name + ";" + str(self.schematic_id) - else: - self._logger.warning("Name %s already assigned in the design", port_name) - - @property - def angle(self): - """Symbol angle. - - Returns - ------- - float - """ - return self._angle - - @angle.setter - def angle(self, angle): - self._app.modeler.schematic.components[self.schematic_id].angle = angle - - @property - def mirror(self): - """Enable port mirror. - - Returns - ------- - bool - """ - return self._mirror - - @mirror.setter - def mirror(self, mirror_value=True): - self._app.modeler.schematic.components[self.schematic_id].mirror = mirror_value - self._mirror = mirror_value - - @property - def location(self): - """Port location. - - Returns - ------- - list - """ - return self._location - - @location.setter - def location(self, location_xy): - # The command must be called two times. - self._app.modeler.schematic.components[self.schematic_id].location = location_xy - self._app.modeler.schematic.components[self.schematic_id].location = location_xy - self._location = location_xy - - @property - def use_symbol_color(self): - """Use symbol color. - - Returns - ------- - list - """ - return self._use_symbol_color - - @use_symbol_color.setter - def use_symbol_color(self, use_color=True): - self._app.modeler.schematic.components[self.schematic_id].usesymbolcolor = use_color - self._app.modeler.schematic.components[self.schematic_id].set_use_symbol_color(use_color) - self._use_symbol_color = use_color - - @property - def impedance(self): - """Port termination. - - Returns - ------- - list - """ - return [self._props["rz"], self._props["iz"]] - - @impedance.setter - def impedance(self, termination=None): - if termination and len(termination) == 2: - self._app.modeler.schematic.components[self.schematic_id].change_property( - ["NAME:rz", "Value:=", termination[0]] - ) - self._app.modeler.schematic.components[self.schematic_id].change_property( - ["NAME:iz", "Value:=", termination[1]] - ) - self._props["rz"] = termination[0] - self._props["iz"] = termination[1] - - @property - def enable_noise(self): - """Enable noise. - - Returns - ------- - bool - """ - - return self._props["EnableNoise"] - - @enable_noise.setter - def enable_noise(self, enable=False): - self._app.modeler.schematic.components[self.schematic_id].change_property( - ["NAME:EnableNoise", "Value:=", enable] - ) - self._props["EnableNoise"] = enable - - @property - def noise_temperature(self): - """Enable noise. - - Returns - ------- - str - """ - - return self._props["noisetemp"] - - @noise_temperature.setter - def noise_temperature(self, noise=None): - if noise: - self._app.modeler.schematic.components[self.schematic_id].change_property( - ["NAME:noisetemp", "Value:=", noise] - ) - self._props["noisetemp"] = noise - - @property - def microwave_symbol(self): - """Enable microwave symbol. - - Returns - ------- - bool - """ - if self._props["SymbolType"] == 1: - return True - else: - return False - - @microwave_symbol.setter - def microwave_symbol(self, enable=False): - if enable: - self._props["SymbolType"] = 1 - else: - self._props["SymbolType"] = 0 - self.update() - - @property - def reference_node(self): - """Reference node. - - Returns - ------- - str - """ - if self._props["RefNode"] == "Z": - return "Ground" - return self._props["RefNode"] - - @reference_node.setter - def reference_node(self, ref_node=None): - if ref_node: - self._logger.warning("Set reference node only working with gRPC") - if ref_node == "Ground": - ref_node = "Z" - self._props["RefNode"] = ref_node - self.update() - - @property - def enabled_sources(self): - """Enabled sources. - - Returns - ------- - list - """ - return self._props["EnabledPorts"] - - @enabled_sources.setter - def enabled_sources(self, sources=None): - if sources: - self._props["EnabledPorts"] = sources - self.update() - - @property - def enabled_analyses(self): - """Enabled analyses. - - Returns - ------- - dict - """ - return self._props["EnabledAnalyses"] - - @enabled_analyses.setter - def enabled_analyses(self, analyses=None): - if analyses: - self._props["EnabledAnalyses"] = analyses - self.update() - - @pyaedt_function_handler() - def _excitation_props(self, port): - excitation_prop_dict = {} - for comp in self._app.modeler.schematic.components: - if ( - "PortName" in self._app.modeler.schematic.components[comp].parameters.keys() - and self._app.modeler.schematic.components[comp].parameters["PortName"] == port - ): - excitation_prop_dict["rz"] = "50ohm" - excitation_prop_dict["iz"] = "0ohm" - excitation_prop_dict["term"] = None - excitation_prop_dict["TerminationData"] = None - excitation_prop_dict["RefNode"] = "Z" - excitation_prop_dict["EnableNoise"] = False - excitation_prop_dict["noisetemp"] = "16.85cel" - - if "RefNode" in self._app.modeler.schematic.components[comp].parameters: - excitation_prop_dict["RefNode"] = self._app.modeler.schematic.components[comp].parameters["RefNode"] - if "term" in self._app.modeler.schematic.components[comp].parameters: - excitation_prop_dict["term"] = self._app.modeler.schematic.components[comp].parameters["term"] - excitation_prop_dict["TerminationData"] = self._app.modeler.schematic.components[comp].parameters[ - "TerminationData" - ] - else: - if "rz" in self._app.modeler.schematic.components[comp].parameters: - excitation_prop_dict["rz"] = self._app.modeler.schematic.components[comp].parameters["rz"] - excitation_prop_dict["iz"] = self._app.modeler.schematic.components[comp].parameters["iz"] - - if "EnableNoise" in self._app.modeler.schematic.components[comp].parameters: - if self._app.modeler.schematic.components[comp].parameters["EnableNoise"] == "true": - excitation_prop_dict["EnableNoise"] = True - else: - excitation_prop_dict["EnableNoise"] = False - - excitation_prop_dict["noisetemp"] = self._app.modeler.schematic.components[comp].parameters[ - "noisetemp" - ] - - if not self._app.design_properties or not self._app.design_properties["NexximPorts"]["Data"]: - excitation_prop_dict["SymbolType"] = 0 - else: - excitation_prop_dict["SymbolType"] = self._app.design_properties["NexximPorts"]["Data"][port][ - "SymbolType" - ] - - if "pnum" in self._app.modeler.schematic.components[comp].parameters: - excitation_prop_dict["pnum"] = self._app.modeler.schematic.components[comp].parameters["pnum"] - else: - excitation_prop_dict["pnum"] = None - source_port = [] - if not self._app.design_properties: - enabled_ports = None - else: - enabled_ports = self._app.design_properties["ComponentConfigurationData"]["EnabledPorts"] - if isinstance(enabled_ports, dict): - for source in enabled_ports: - if enabled_ports[source] and port in enabled_ports[source]: - source_port.append(source) - excitation_prop_dict["EnabledPorts"] = source_port - - components_port = [] - if not self._app.design_properties: - multiple = None - else: - multiple = self._app.design_properties["ComponentConfigurationData"]["EnabledMultipleComponents"] - if isinstance(multiple, dict): - for source in multiple: - if multiple[source] and port in multiple[source]: - components_port.append(source) - excitation_prop_dict["EnabledMultipleComponents"] = components_port - - port_analyses = {} - if not self._app.design_properties: - enabled_analyses = None - else: - enabled_analyses = self._app.design_properties["ComponentConfigurationData"]["EnabledAnalyses"] - if isinstance(enabled_analyses, dict): - for source in enabled_analyses: - if ( - enabled_analyses[source] - and port in enabled_analyses[source] - and source in excitation_prop_dict["EnabledPorts"] - ): - port_analyses[source] = enabled_analyses[source][port] - excitation_prop_dict["EnabledAnalyses"] = port_analyses - return excitation_prop_dict - - @pyaedt_function_handler() - def update(self): - """Update the excitation in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - - # self._logger.warning("Property port update only working with GRPC") - - if self._props["RefNode"] == "Ground": - self._props["RefNode"] = "Z" - - arg0 = [ - "NAME:" + self.name, - "IIPortName:=", - self.name, - "SymbolType:=", - self._props["SymbolType"], - "DoPostProcess:=", - False, - ] - - arg1 = ["NAME:ChangedProps"] - arg2 = [] - - # Modify RefNode - if self._props["RefNode"] != "Z": - arg2 = [ - "NAME:NewProps", - ["NAME:RefNode", "PropType:=", "TextProp", "OverridingDef:=", True, "Value:=", self._props["RefNode"]], - ] - - # Modify Termination - if self._props["term"] and self._props["TerminationData"]: - arg2 = [ - "NAME:NewProps", - ["NAME:term", "PropType:=", "TextProp", "OverridingDef:=", True, "Value:=", self._props["term"]], - ] - - for prop in self._props: - skip1 = (prop == "rz" or prop == "iz") and isinstance(self._props["term"], str) - skip2 = prop == "EnabledPorts" or prop == "EnabledMultipleComponents" or prop == "EnabledAnalyses" - skip3 = prop == "SymbolType" - skip4 = prop == "TerminationData" and not isinstance(self._props["term"], str) - if not skip1 and not skip2 and not skip3 and not skip4 and self._props[prop] is not None: - command = ["NAME:" + prop, "Value:=", self._props[prop]] - arg1.append(command) - - arg1 = [["NAME:Properties", arg2, arg1]] - self._app.odesign.ChangePortProperty(self.name, arg0, arg1) - - for source in self._app.sources: - self._app.sources[source].update() - return True - - @pyaedt_function_handler() - def delete(self): - """Delete the port in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - self._app.modeler._odesign.DeletePort(self.name) - return True - - @property - def _logger(self): - """Logger.""" - return self._app.logger - - -class NetworkObject(BoundaryObject): - """Manages networks in Icepak projects.""" - - def __init__(self, app, name=None, props=None, create=False): - if not app.design_type == "Icepak": # pragma: no cover - raise NotImplementedError("Networks object works only with Icepak projects ") - if name is None: - self._name = generate_unique_name("Network") - else: - self._name = name - super(NetworkObject, self).__init__(app, self._name, props, "Network", False) - if self.props is None: - self._props = {} - self._nodes = [] - self._links = [] - self._schematic_data = {} - self._update_from_props() - if create: - self.create() - - def _clean_list(self, arg): - new_list = [] - for item in arg: - if isinstance(item, list): - if item[0] == "NAME:PageNet": - page_net_list = [] - for i in item: - if isinstance(i, list): - name = page_net_list[-1] - page_net_list.pop(-1) - for j in i: - page_net_list.append(name) - page_net_list.append(j) - else: - page_net_list.append(i) - new_list.append(page_net_list) - else: - new_list.append(self._clean_list(item)) - else: - new_list.append(item) - return new_list - - @pyaedt_function_handler() - def create(self): - """ - Create network in AEDT. - - Returns - ------- - bool: - True if successful. - """ - if not self.props.get("Faces", None): - self.props["Faces"] = [node.props["FaceID"] for _, node in self.face_nodes.items()] - if not self.props.get("SchematicData", None): - self.props["SchematicData"] = {} - - if self.props.get("Links", None): - self.props["Links"] = {link_name: link_values.props for link_name, link_values in self.links.items()} - else: # pragma : no cover - raise KeyError("Links information is missing.") - if self.props.get("Nodes", None): - self.props["Nodes"] = {node_name: node_values.props for node_name, node_values in self.nodes.items()} - else: # pragma : no cover - raise KeyError("Nodes information is missing.") - - args = self._get_args() - - clean_args = self._clean_list(args) - self._app.oboundary.AssignNetworkBoundary(clean_args) - return True - - @pyaedt_function_handler() - def _update_from_props(self): - nodes = self.props.get("Nodes", None) - if nodes is not None: - nd_name_list = [node.name for node in self._nodes] - for node_name, node_dict in nodes.items(): - if node_name not in nd_name_list: - nd_type = node_dict.get("NodeType", None) - if nd_type == "InternalNode": - self.add_internal_node( - node_name, - node_dict.get("Power", node_dict.get("Power Variation Data", None)), - mass=node_dict.get("Mass", None), - specific_heat=node_dict.get("SpecificHeat", None), - ) - elif nd_type == "BoundaryNode": - self.add_boundary_node( - node_name, - assignment_type=node_dict["ValueType"].replace("Value", ""), - value=node_dict[node_dict["ValueType"].replace("Value", "")], - ) - else: - if ( - node_dict["ThermalResistance"] == "NoResistance" - or node_dict["ThermalResistance"] == "Specified" - ): - node_material, node_thickness = None, None - node_resistance = node_dict["Resistance"] - else: - node_thickness, node_material = node_dict["Thickness"], node_dict["Material"] - node_resistance = None - self.add_face_node( - node_dict["FaceID"], - name=node_name, - thermal_resistance=node_dict["ThermalResistance"], - material=node_material, - thickness=node_thickness, - resistance=node_resistance, - ) - links = self.props.get("Links", None) - if links is not None: - l_name_list = [l.name for l in self._links] - for link_name, link_dict in links.items(): - if link_name not in l_name_list: - self.add_link(link_dict[0], link_dict[1], link_dict[-1], link_name) - - @property - def auto_update(self): - """ - Get if auto-update is enabled. - - Returns - ------- - bool: - Whether auto-update is enabled. - """ - return False - - @auto_update.setter - def auto_update(self, b): - """ - Set auto-update on or off. - - Parameters - ---------- - b : bool - Whether to enable auto-update. - - """ - if b: - self._app.logger.warning( - "Network objects auto_update property is False by default" " and cannot be set to True." - ) - - @property - def links(self): - """ - Get links of the network. - - Returns - ------- - dict: - Links dictionary. - - """ - self._update_from_props() - return {link.name: link for link in self._links} - - @property - def r_links(self): - """ - Get r-links of the network. - - Returns - ------- - dict: - R-links dictionary. - - """ - self._update_from_props() - return {link.name: link for link in self._links if link._link_type[0] == "R-Link"} - - @property - def c_links(self): - """ - Get c-links of the network. - - Returns - ------- - dict: - C-links dictionary. - - """ - self._update_from_props() - return {link.name: link for link in self._links if link._link_type[0] == "C-Link"} - - @property - def nodes(self): - """ - Get nodes of the network. - - Returns - ------- - dict: - Nodes dictionary. - - """ - self._update_from_props() - return {node.name: node for node in self._nodes} - - @property - def face_nodes(self): - """ - Get face nodes of the network. - - Returns - ------- - dict: - Face nodes dictionary. - - """ - self._update_from_props() - return {node.name: node for node in self._nodes if node.node_type == "FaceNode"} - - @property - def faces_ids_in_network(self): - """ - Get ID of faces included in the network. - - Returns - ------- - list: - Face IDs. - - """ - out_arr = [] - for _, node_dict in self.face_nodes.items(): - out_arr.append(node_dict.props["FaceID"]) - return out_arr - - @property - def objects_in_network(self): - """ - Get objects included in the network. - - Returns - ------- - list: - Objects names. - - """ - out_arr = [] - for face_id in self.faces_ids_in_network: - out_arr.append(self._app.oeditor.GetObjectNameByFaceID(face_id)) - return out_arr - - @property - def internal_nodes(self): - """ - Get internal nodes. - - Returns - ------- - dict: - Internal nodes. - - """ - self._update_from_props() - return {node.name: node for node in self._nodes if node.node_type == "InternalNode"} - - @property - def boundary_nodes(self): - """ - Get boundary nodes. - - Returns - ------- - dict: - Boundary nodes. - - """ - self._update_from_props() - return {node.name: node for node in self._nodes if node.node_type == "BoundaryNode"} - - @property - def name(self): - """ - Get network name. - - Returns - ------- - str - Network name. - """ - return self._name - - @name.setter - def name(self, new_network_name): - """ - Set new name of the network. - - Parameters - ---------- - new_network_name : str - New name of the network. - """ - bound_names = [b.name for b in self._app.boundaries] - if self.name in bound_names: - if new_network_name not in bound_names: - if new_network_name != self._name: - self._app._oboundary.RenameBoundary(self._name, new_network_name) - self._name = new_network_name - else: - self._app.logger.warning("Name %s already assigned in the design", new_network_name) - else: - self._name = new_network_name - - @pyaedt_function_handler() - def add_internal_node(self, name, power, mass=None, specific_heat=None): - """Add an internal node to the network. - - Parameters - ---------- - name : str - Name of the node. - power : str or float or dict - String, float, or dictionary containing the value of the assignment. - If a float is passed, the ``"W"`` unit is used. A dictionary can be - passed to use temperature-dependent or transient - assignments. - mass : str or float, optional - Value of the mass assignment. This parameter is relevant only - if the solution is transient. If a float is passed, the ``"Kg"`` unit - is used. The default is ``None``, in which case ``"0.001kg"`` is used. - specific_heat : str or float, optional - Value of the specific heat assignment. This parameter is - relevant only if the solution is transient. If a float is passed, - the ``"J_per_Kelkg"`` unit is used. The default is ``None`, in - which case ``"1000J_per_Kelkg"`` is used. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - Examples - -------- - - >>> import ansys.aedt.core - >>> app = ansys.aedt.core.Icepak() - >>> network = ansys.aedt.core.modules.boundary.Network(app) - >>> network.add_internal_node("TestNode", {"Type": "Transient", - >>> "Function": "Linear", "Values": ["0.01W", "1"]}) - """ - if self._app.solution_type != "SteadyState" and mass is None and specific_heat is None: - self._app.logger.warning("The solution is transient but neither mass nor specific heat is assigned.") - if self._app.solution_type == "SteadyState" and ( - mass is not None or specific_heat is not None - ): # pragma: no cover - self._app.logger.warning( - "Because the solution is steady state, neither mass nor specific heat assignment is relevant." - ) - if isinstance(power, (int, float)): - power = str(power) + "W" - props_dict = {"Power": power} - if mass is not None: - if isinstance(mass, (int, float)): - mass = str(mass) + "kg" - props_dict.update({"Mass": mass}) - if specific_heat is not None: - if isinstance(specific_heat, (int, float)): - specific_heat = str(specific_heat) + "J_per_Kelkg" - props_dict.update({"SpecificHeat": specific_heat}) - new_node = self._Node(name, self._app, node_type="InternalNode", props=props_dict, network=self) - self._nodes.append(new_node) - self._add_to_props(new_node) - return new_node - - @pyaedt_function_handler() - def add_boundary_node(self, name, assignment_type, value): - """ - Add a boundary node to the network. - - Parameters - ---------- - name : str - Name of the node. - assignment_type : str - Type assignment. Options are ``"Power"`` and ``"Temperature"``. - value : str or float or dict - String, float, or dictionary containing the value of the assignment. - If a float is passed the ``"W"`` or ``"cel"`` unit is used, depending on - the selection for the ``assignment_type`` parameter. If ``"Power"`` - is selected for the type, a dictionary can be passed to use - temperature-dependent or transient assignment. - - Returns - ------- - bool - ``True`` if successful. - - Examples - -------- - - >>> import ansys.aedt.core - >>> app = ansys.aedt.core.Icepak() - >>> network = ansys.aedt.core.modules.boundary.Network(app) - >>> network.add_boundary_node("TestNode", "Temperature", 2) - >>> ds = app.create_dataset1d_design("Test_DataSet",[1, 2, 3],[3, 4, 5]) - >>> network.add_boundary_node("TestNode", "Power", {"Type": "Temp Dep", - >>> "Function": "Piecewise Linear", - >>> "Values": "Test_DataSet"}) - """ - if assignment_type not in ["Power", "Temperature", "PowerValue", "TemperatureValue"]: # pragma: no cover - raise AttributeError('``type`` can be only ``"Power"`` or ``"Temperature"``.') - if isinstance(value, (float, int)): - if assignment_type == "Power" or assignment_type == "PowerValue": - value = str(value) + "W" - else: - value = str(value) + "cel" - if isinstance(value, dict) and ( - assignment_type == "Temperature" or assignment_type == "TemperatureValue" - ): # pragma: no cover - raise AttributeError( - "Temperature-dependent or transient assignment is not supported in a temperature boundary node." - ) - if not assignment_type.endswith("Value"): - assignment_type += "Value" - new_node = self._Node( - name, - self._app, - node_type="BoundaryNode", - props={"ValueType": assignment_type, assignment_type.removesuffix("Value"): value}, - network=self, - ) - self._nodes.append(new_node) - self._add_to_props(new_node) - return new_node - - @pyaedt_function_handler() - def _add_to_props(self, new_node, type_dict="Nodes"): - try: - self.props[type_dict].update({new_node.name: new_node.props}) - except KeyError: - self.props[type_dict] = {new_node.name: new_node.props} - - @pyaedt_function_handler(face_id="assignment") - def add_face_node( - self, assignment, name=None, thermal_resistance="NoResistance", material=None, thickness=None, resistance=None - ): - """ - Create a face node in the network. - - Parameters - ---------- - assignment : int - Face ID. - name : str, optional - Name of the node. Default is ``None``. - thermal_resistance : str - Thermal resistance value and unit. Default is ``"NoResistance"``. - material : str, optional - Material specification (required if ``thermal_resistance="Compute"``). - Default is ``None``. - thickness : str or float, optional - Thickness value and unit (required if ``thermal_resistance="Compute"``). - If a float is passed, ``"mm"`` unit is automatically used. Default is ``None``. - resistance : str or float, optional - Resistance value and unit (required if ``thermal_resistance="Specified"``). - If a float is passed, ``"cel_per_w"`` unit is automatically used. Default is ``None``. - - Returns - ------- - bool - True if successful. - - Examples - -------- - - >>> import ansys.aedt.core - >>> app = ansys.aedt.core.Icepak() - >>> network = ansys.aedt.core.modules.boundary.Network(app) - >>> box = app.modeler.create_box([5, 5, 5],[20, 50, 80]) - >>> faces_ids = [face.id for face in box.faces] - >>> network.add_face_node(faces_ids[0]) - >>> network.add_face_node(faces_ids[1],name="TestNode",thermal_resistance="Compute", - ... material="Al-Extruded",thickness="2mm") - >>> network.add_face_node(faces_ids[2],name="TestNode",thermal_resistance="Specified",resistance=2) - """ - props_dict = {} - props_dict["FaceID"] = assignment - if thermal_resistance is not None: - if thermal_resistance == "Compute": - if resistance is not None: - self._app.logger.info( - '``resistance`` assignment is incompatible with ``thermal_resistance="Compute"``' - "and it is ignored." - ) - if material is not None or thickness is not None: - props_dict["ThermalResistance"] = thermal_resistance - props_dict["Material"] = material - if not isinstance(thickness, str): - thickness = str(thickness) + "mm" - props_dict["Thickness"] = thickness - else: # pragma: no cover - raise AttributeError( - 'If ``thermal_resistance="Compute"`` both ``material`` and ``thickness``' - "arguments must be prescribed." - ) - if thermal_resistance == "Specified": - if material is not None or thickness is not None: - self._app.logger.warning( - "Because ``material`` and ``thickness`` assignments are incompatible with" - '``thermal_resistance="Specified"``, they are ignored.' - ) - if resistance is not None: - props_dict["ThermalResistance"] = thermal_resistance - if not isinstance(resistance, str): - resistance = str(resistance) + "cel_per_w" - props_dict["Resistance"] = resistance - else: # pragma : no cover - raise AttributeError( - 'If ``thermal_resistance="Specified"``, ``resistance`` argument must be prescribed.' - ) - - if name is None: - name = "FaceID" + str(assignment) - new_node = self._Node(name, self._app, node_type="FaceNode", props=props_dict, network=self) - self._nodes.append(new_node) - self._add_to_props(new_node) - return new_node - - @pyaedt_function_handler(nodes_dict="nodes") - def add_nodes_from_dictionaries(self, nodes): - """ - Add nodes to the network from dictionary. - - Parameters - ---------- - nodes : list or dict - A dictionary or list of dictionaries containing nodes to add to the network. Different - node types require different key and value pairs: - - - Face nodes must contain the ``"ID"`` key associated with an integer containing the face ID. - Optional keys and values pairs are: - - - ``"ThermalResistance"``: a string specifying the type of thermal resistance. - Options are ``"NoResistance"`` (default), ``"Compute"``, and ``"Specified"``. - - ``"Thickness"``: a string with the thickness value and unit (required if ``"Compute"`` - is selected for ``"ThermalResistance"``). - - ``"Material"``: a string with the name of the material (required if ``"Compute"`` is - selected for ``"ThermalResistance"``). - - ``"Resistance"``: a string with the resistance value and unit (required if - ``"Specified"`` is selected for ``"ThermalResistance"``). - - ``"Name"``: a string with the name of the node. If not - specified, a name is generated automatically. - - - - Internal nodes must contain the following keys and values pairs: - - - ``"Name"``: a string with the node name - - ``"Power"``: a string with the assigned power or a dictionary for transient or - temperature-dependent assignment - Optional keys and values pairs: - - ``"Mass"``: a string with the mass value and unit - - ``"SpecificHeat"``: a string with the specific heat value and unit - - - Boundary nodes must contain the following keys and values pairs: - - - ``"Name"``: a string with the node name - - ``"ValueType"``: a string specifying the type of value (``"Power"`` or - ``"Temperature"``) - Depending on the ``"ValueType"`` choice, one of the following keys and values pairs must - be used: - - ``"Power"``: a string with the power value and unit or a dictionary for transient or - temperature-dependent assignment - - ``"Temperature"``: a string with the temperature value and unit or a dictionary for - transient or temperature-dependent assignment - According to the ``"ValueType"`` choice, ``"Power"`` or ``"Temperature"`` key must be - used. Their associated value a string with the value and unit of the quantity prescribed or - a dictionary for transient or temperature dependent assignment. - - - All the temperature dependent or thermal dictionaries should contain three keys: - ``"Type"``, ``"Function"``, and ``"Values"``. Accepted ``"Type"`` values are: - ``"Temp Dep"`` and ``"Transient"``. Accepted ``"Function"`` are: ``"Linear"``, - ``"Power Law"``, ``"Exponential"``, ``"Sinusoidal"``, ``"Square Wave"``, and - ``"Piecewise Linear"``. ``"Temp Dep"`` only support the latter. ``"Values"`` - contains a list of strings containing the parameters required by the ``"Function"`` - selection (e.g. ``"Linear"`` requires two parameters: the value of the variable at t=0 - and the slope of the line). The parameters required by each ``Function`` option is in - Icepak documentation. The parameters must contain the units where needed. - - Returns - ------- - bool - ``True`` if successful. ``False`` otherwise. - - Examples - -------- - - >>> import ansys.aedt.core - >>> app = ansys.aedt.core.Icepak() - >>> network = ansys.aedt.core.modules.boundary.Network(app) - >>> box = app.modeler.create_box([5, 5, 5],[20, 50, 80]) - >>> faces_ids = [face.id for face in box.faces] - >>> nodes_dict = [ - >>> {"FaceID": faces_ids[0]}, - >>> {"Name": "TestNode", "FaceID": faces_ids[1], - >>> "ThermalResistance": "Compute", "Thickness": "2mm"}, - >>> {"FaceID": faces_ids[2], "ThermalResistance": "Specified", "Resistance": "2cel_per_w"}, - >>> {"Name": "Junction", "Power": "1W"}] - >>> network.add_nodes_from_dictionaries(nodes_dict) - - """ - if isinstance(nodes, dict): - nodes = [nodes] - for node_dict in nodes: - if "FaceID" in node_dict.keys(): - self.add_face_node( - assignment=node_dict["FaceID"], - name=node_dict.get("Name", None), - thermal_resistance=node_dict.get("ThermalResistance", None), - material=node_dict.get("Material", None), - thickness=node_dict.get("Thickness", None), - resistance=node_dict.get("Resistance", None), - ) - elif "ValueType" in node_dict.keys(): - if node_dict["ValueType"].endswith("Value"): - value = node_dict[node_dict["ValueType"].removesuffix("Value")] - else: - value = node_dict[node_dict["ValueType"]] - self.add_boundary_node(name=node_dict["Name"], assignment_type=node_dict["ValueType"], value=value) - else: - self.add_internal_node( - name=node_dict["Name"], - power=node_dict.get("Power", None), - mass=node_dict.get("Mass", None), - specific_heat=node_dict.get("SpecificHeat", None), - ) - return True - - @pyaedt_function_handler() - def add_link(self, node1, node2, value, name=None): - """Create links in the network object. - - Parameters - ---------- - node1 : str or int - String containing one of the node names that the link is connecting or an integer - containing the ID of the face. If an ID is used and the node associated with the - corresponding face is not created yet, it is added automatically. - node2 : str or int - String containing one of the node names that the link is connecting or an integer - containing the ID of the face. If an ID is used and the node associated with the - corresponding face is not created yet, it is added atuomatically. - value : str or float - String containing the value and unit of the connection. If a float is passed, an - R-Link is added to the network and the ``"cel_per_w"`` unit is used. - name : str, optional - Name of the link. The default is ``None``, in which case a name is - automatically generated. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - Examples - -------- - - >>> import ansys.aedt.core - >>> app = ansys.aedt.core.Icepak() - >>> network = ansys.aedt.core.modules.boundary.Network(app) - >>> box = app.modeler.create_box([5, 5, 5],[20, 50, 80]) - >>> faces_ids = [face.id for face in box.faces] - >>> connection = {"Name": "LinkTest", "Connection": [faces_ids[1], faces_ids[0]], "Value": "1cel_per_w"} - >>> network.add_links_from_dictionaries(connection) - - """ - if name is None: - new_name = True - while new_name: - name = generate_unique_name("Link") - if name not in self.links.keys(): - new_name = False - new_link = self._Link(node1, node2, value, name, self) - self._links.append(new_link) - self._add_to_props(new_link, "Links") - return True - - @pyaedt_function_handler() - def add_links_from_dictionaries(self, connections): - """Create links in the network object. - - Parameters - ---------- - connections : dict or list of dict - Dictionary or list of dictionaries containing the links between nodes. Each dictionary - consists of these elements: - - - ``"Link"``: a three-item list consisting of the two nodes that the link is connecting and - the value with unit of the link. The node of the connection can be referred to with the - name (str) or face ID (int). The link type (resistance, heat transfer coefficient, or - mass flow) is determined automatically from the unit. - - ``"Name"`` (optional): a string specifying the name of the link. - - - Returns - ------- - bool - ``True`` if successful. - - Examples - -------- - >>> import ansys.aedt.core - >>> app = ansys.aedt.core.Icepak() - >>> network = ansys.aedt.core.modules.boundary.Network(app) - >>> box = app.modeler.create_box([5, 5, 5],[20, 50, 80]) - >>> faces_ids = [face.id for face in box.faces] - >>> [network.add_face_node(faces_ids[i]) for i in range(2)] - >>> connection = {"Name": "LinkTest", "Link": [faces_ids[1], faces_ids[0], "1cel_per_w"]} - >>> network.add_links_from_dictionaries(connection) - """ - if isinstance(connections, dict): - connections = [connections] - for connection in connections: - name = connection.get("Name", None) - try: - self.add_link(connection["Link"][0], connection["Link"][1], connection["Link"][2], name) - except Exception: # pragma : no cover - if name: - self._app.logger.error("Failed to add " + name + " link.") - else: - self._app.logger.error( - "Failed to add link associated with the following dictionary:\n" + str(connection) - ) - return True - - @pyaedt_function_handler() - def update(self): - """Update the network in AEDT. - - Returns - ------- - bool - ``True`` when successful, ``False`` when failed. - - """ - if self.name in [b.name for b in self._app.boundaries]: - self.delete() - try: - self.create() - self._app._boundaries[self.name] = self - return True - except Exception: # pragma : no cover - self._app.odesign.Undo() - self._app.logger.error("Update of network object failed.") - return False - else: # pragma : no cover - self._app.logger.warning("Network object not yet created in design.") - return False - - @pyaedt_function_handler() - def update_assignment(self): - """Update assignments of the network.""" - return self.update() - - class _Link: - def __init__(self, node_1, node_2, value, name, network): - self.name = name - if not isinstance(node_1, str): - node_1 = "FaceID" + str(node_1) - if not isinstance(node_2, str): - node_2 = "FaceID" + str(node_2) - if not isinstance(value, str): - value = str(value) + "cel_per_w" - self.node_1 = node_1 - self.node_2 = node_2 - self.value = value - self._network = network - - @property - def _link_type(self): - unit2type_conversion = { - "g_per_s": ["C-Link", "Node1ToNode2"], - "kg_per_s": ["C-Link", "Node1ToNode2"], - "lbm_per_min": ["C-Link", "Node1ToNode2"], - "lbm_per_s": ["C-Link", "Node1ToNode2"], - "Kel_per_W": ["R-Link", "R"], - "cel_per_w": ["R-Link", "R"], - "FahSec_per_btu": ["R-Link", "R"], - "Kels_per_J": ["R-Link", "R"], - "w_per_m2kel": ["R-Link", "HTC"], - "w_per_m2Cel": ["R-Link", "HTC"], - "btu_per_rankHrFt2": ["R-Link", "HTC"], - "btu_per_fahHrFt2": ["R-Link", "HTC"], - "btu_per_rankSecFt2": ["R-Link", "HTC"], - "btu_per_fahSecFt2": ["R-Link", "HTC"], - "w_per_cm2kel": ["R-Link", "HTC"], - } - _, unit = decompose_variable_value(self.value) - return unit2type_conversion[unit] - - @property - def props(self): - """ - Get link properties. - - Returns - ------- - list - First two elements of the list are the node names that the link connects, - the third element is the link type while the fourth contains the value - associated with the link. - """ - return [self.node_1, self.node_2] + self._link_type + [self.value] - - @pyaedt_function_handler() - def delete_link(self): - """ - Delete link from network. - """ - self._network.props["Links"].pop(self.name) - self._network._links.remove(self) - - class _Node: - def __init__(self, name, app, network, node_type=None, props=None): - self.name = name - self._type = node_type - self._app = app - self._props = props - self._node_props() - self._network = network - - @pyaedt_function_handler() - def delete_node(self): - """Delete node from network.""" - self._network.props["Nodes"].pop(self.name) - self._network._nodes.remove(self) - - @property - def node_type(self): - """Get node type. - - Returns - ------- - str - Node type. - """ - if self._type is None: # pragma: no cover - if self.props is None: - self._app.logger.error( - "Cannot define node_type. Both its assignment and properties assignment are missing." - ) - return None - else: - type_in_dict = self.props.get("NodeType", None) - if type_in_dict is None: - self._type = "FaceNode" - else: - self._type = type_in_dict - return self._type - - @property - def props(self): - """Get properties of the node. - - Returns - ------- - dict - Node properties. - """ - return self._props - - @props.setter - def props(self, props): - """Set properties of the node. - - Parameters - ---------- - props : dict - Node properties. - """ - self._props = props - self._node_props() - - def _node_props(self): - face_node_default_dict = { - "FaceID": None, - "ThermalResistance": "NoResistance", - "Thickness": "1mm", - "Material": "Al-Extruded", - "Resistance": "0cel_per_w", - } - boundary_node_default_dict = { - "NodeType": "BoundaryNode", - "ValueType": "PowerValue", - "Power": "0W", - "Temperature": "25cel", - } - internal_node_default_dict = { - "NodeType": "InternalNode", - "Power": "0W", - "Mass": "0.001kg", - "SpecificHeat": "1000J_per_Kelkg", - } - if self.props is None: - if self.node_type == "InternalNode": - self._props = internal_node_default_dict - elif self.node_type == "FaceNode": - self._props = face_node_default_dict - elif self.node_type == "BoundaryNode": - self._props = boundary_node_default_dict - else: - if self.node_type == "InternalNode": - self._props = self._create_node_dict(internal_node_default_dict) - elif self.node_type == "FaceNode": - self._props = self._create_node_dict(face_node_default_dict) - elif self.node_type == "BoundaryNode": - self._props = self._create_node_dict(boundary_node_default_dict) - - @pyaedt_function_handler() - def _create_node_dict(self, default_dict): - node_dict = self.props - node_name = node_dict.get("Name", self.name) - if not node_name: - try: - self.name = "Face" + str(node_dict["FaceID"]) - except KeyError: # pragma: no cover - raise KeyError('"Name" key is needed for "BoundaryNodes" and "InternalNodes" dictionaries.') - else: - self.name = node_name - node_dict.pop("Name", None) - node_args = copy.deepcopy(default_dict) - for k in node_dict.keys(): - val = node_dict[k] - if isinstance(val, dict): # pragma : no cover - val = self._app._parse_variation_data( - k, val["Type"], variation_value=val["Values"], function=val["Function"] - ) - node_args.pop(k) - node_args.update(val) - else: - node_args[k] = val - - return node_args - - -def _create_boundary(bound): - try: - if bound.create(): - bound._app._boundaries[bound.name] = bound - return bound - else: # pragma : no cover - raise Exception - except Exception: # pragma: no cover - return None - - -class BoundaryDictionary: - """Handles Icepak transient and temperature-dependent boundary condition assignments. - - Parameters - ---------- - assignment_type : str - Type of assignment represented by the class. Options are `"Temp Dep"`` - and ``"Transient"``. - function_type : str - Variation function to assign. If ``assignment_type=="Temp Dep"``, - the function can only be ``"Piecewise Linear"``. Otherwise, the function can be - ``"Exponential"``, ``"Linear"``, ``"Piecewise Linear"``, ``"Power Law"``, - ``"Sinusoidal"``, and ``"Square Wave"``. - """ - - def __init__(self, assignment_type, function_type): - if assignment_type not in ["Temp Dep", "Transient"]: # pragma : no cover - raise AttributeError(f"The argument {assignment_type} for ``assignment_type`` is not valid.") - if assignment_type == "Temp Dep" and function_type != "Piecewise Linear": # pragma : no cover - raise AttributeError( - "Temperature dependent assignments only support" - ' ``"Piecewise Linear"`` as ``function_type`` argument.' - ) - self.assignment_type = assignment_type - self.function_type = function_type - - @property - def props(self): - """Dictionary that defines all the boundary condition properties.""" - return { - "Type": self.assignment_type, - "Function": self.function_type, - "Values": self._parse_value(), - } - - @abstractmethod - def _parse_value(self): - pass # pragma : no cover - - @pyaedt_function_handler() - def __getitem__(self, k): - return self.props.get(k) - - -class LinearDictionary(BoundaryDictionary): - """Manages linear conditions assignments, which are children of the ``BoundaryDictionary`` class. - - This class applies a condition ``y`` dependent on the time ``t``: - ``y=a+b*t`` - - Parameters - ---------- - intercept : str - Value of the assignment condition at the initial time, which - corresponds to the coefficient ``a`` in the formula. - slope : str - Slope of the assignment condition, which - corresponds to the coefficient ``b`` in the formula. - """ - - def __init__(self, intercept, slope): - super().__init__("Transient", "Linear") - self.intercept = intercept - self.slope = slope - - @pyaedt_function_handler() - def _parse_value(self): - return [self.slope, self.intercept] - - -class PowerLawDictionary(BoundaryDictionary): - """Manages power law condition assignments, which are children of the ``BoundaryDictionary`` class. - - This class applies a condition ``y`` dependent on the time ``t``: - ``y=a+b*t^c`` - - Parameters - ---------- - intercept : str - Value of the assignment condition at the initial time, which - corresponds to the coefficient ``a`` in the formula. - coefficient : str - Coefficient that multiplies the power term, which - corresponds to the coefficient ``b`` in the formula. - scaling_exponent : str - Exponent of the power term, which - corresponds to the coefficient ``c`` in the formula. - """ - - def __init__(self, intercept, coefficient, scaling_exponent): - super().__init__("Transient", "Power Law") - self.intercept = intercept - self.coefficient = coefficient - self.scaling_exponent = scaling_exponent - - @pyaedt_function_handler() - def _parse_value(self): - return [self.intercept, self.coefficient, self.scaling_exponent] - - -class ExponentialDictionary(BoundaryDictionary): - """Manages exponential condition assignments, which are children of the ``BoundaryDictionary`` class. - - This class applies a condition ``y`` dependent on the time ``t``: - ``y=a+b*exp(c*t)`` - - Parameters - ---------- - vertical_offset : str - Vertical offset summed to the exponential law, which - corresponds to the coefficient ``a`` in the formula. - coefficient : str - Coefficient that multiplies the exponential term, which - corresponds to the coefficient ``b`` in the formula. - exponent_coefficient : str - Coefficient in the exponential term, which - corresponds to the coefficient ``c`` in the formula. - """ - - def __init__(self, vertical_offset, coefficient, exponent_coefficient): - super().__init__("Transient", "Exponential") - self.vertical_offset = vertical_offset - self.coefficient = coefficient - self.exponent_coefficient = exponent_coefficient - - @pyaedt_function_handler() - def _parse_value(self): - return [self.vertical_offset, self.coefficient, self.exponent_coefficient] - - -class SinusoidalDictionary(BoundaryDictionary): - """Manages sinusoidal condition assignments, which are children of the ``BoundaryDictionary`` class. - - This class applies a condition ``y`` dependent on the time ``t``: - ``y=a+b*sin(2*pi(t-t0)/T)`` - - Parameters - ---------- - vertical_offset : str - Vertical offset summed to the sinusoidal law, which - corresponds to the coefficient ``a`` in the formula. - vertical_scaling : str - Coefficient that multiplies the sinusoidal term, which - corresponds to the coefficient ``b`` in the formula. - period : str - Period of the sinusoid, which - corresponds to the coefficient ``T`` in the formula. - period_offset : str - Offset of the sinusoid, which - corresponds to the coefficient ``t0`` in the formula. - """ - - def __init__(self, vertical_offset, vertical_scaling, period, period_offset): - super().__init__("Transient", "Sinusoidal") - self.vertical_offset = vertical_offset - self.vertical_scaling = vertical_scaling - self.period = period - self.period_offset = period_offset - - @pyaedt_function_handler() - def _parse_value(self): - return [self.vertical_offset, self.vertical_scaling, self.period, self.period_offset] - - -class SquareWaveDictionary(BoundaryDictionary): - """Manages square wave condition assignments, which are children of the ``BoundaryDictionary`` class. - - Parameters - ---------- - on_value : str - Maximum value of the square wave. - initial_time_off : str - Time after which the square wave assignment starts. - on_time : str - Time for which the square wave keeps the maximum value during one period. - off_time : str - Time for which the square wave keeps the minimum value during one period. - off_value : str - Minimum value of the square wave. - """ - - def __init__(self, on_value, initial_time_off, on_time, off_time, off_value): - super().__init__("Transient", "Square Wave") - self.on_value = on_value - self.initial_time_off = initial_time_off - self.on_time = on_time - self.off_time = off_time - self.off_value = off_value - - @pyaedt_function_handler() - def _parse_value(self): - return [self.on_value, self.initial_time_off, self.on_time, self.off_time, self.off_value] - - -class PieceWiseLinearDictionary(BoundaryDictionary): - """ - Manages dataset condition assignments, which are children of the ``BoundaryDictionary`` class. - - Parameters - ---------- - assignment_type : str - Type of assignment represented by the class. - Options are ``"Temp Dep"`` and ``"Transient"``. - ds : str - Dataset name to assign. - scale : str - Scaling factor for the y values of the dataset. - """ - - def __init__(self, assignment_type, ds, scale): - super().__init__(assignment_type, "Piecewise Linear") - self.scale = scale - self._assignment_type = assignment_type - self.dataset = ds - - @pyaedt_function_handler() - def _parse_value(self): - return [self.scale, self.dataset.name] - - @property - def dataset_name(self): - """Dataset name that defines the piecewise assignment.""" - return self.dataset.name diff --git a/src/ansys/aedt/core/modules/boundary/__init__.py b/src/ansys/aedt/core/modules/boundary/__init__.py new file mode 100644 index 00000000000..9c4476773da --- /dev/null +++ b/src/ansys/aedt/core/modules/boundary/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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. diff --git a/src/ansys/aedt/core/modules/boundary/circuit_boundary.py b/src/ansys/aedt/core/modules/boundary/circuit_boundary.py new file mode 100644 index 00000000000..d01259dc8c1 --- /dev/null +++ b/src/ansys/aedt/core/modules/boundary/circuit_boundary.py @@ -0,0 +1,2630 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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. + +import copy +import re + +from ansys.aedt.core.application.variables import decompose_variable_value +from ansys.aedt.core.generic.general_methods import generate_unique_name +from ansys.aedt.core.generic.general_methods import pyaedt_function_handler +from ansys.aedt.core.modules.boundary.common import BoundaryObject +from ansys.aedt.core.modules.circuit_templates import SourceKeys + + +class Sources(object): + """Manages sources in Circuit projects.""" + + def __init__(self, app, name, source_type=None): + self._app = app + self._name = name + self._props = self._source_props(name, source_type) + self.source_type = source_type + if not source_type: + self.source_type = self._source_type_by_key() + self._auto_update = True + + @property + def name(self): + """Source name. + + Returns + ------- + str + """ + return self._name + + @name.setter + def name(self, source_name): + if source_name not in self._app.source_names: + if source_name != self._name: + original_name = self._name + self._name = source_name + for port in self._app.excitations: + if original_name in self._app.excitation_objects[port].props["EnabledPorts"]: + self._app.excitation_objects[port].props["EnabledPorts"] = [ + w.replace(original_name, source_name) + for w in self._app.excitation_objects[port].props["EnabledPorts"] + ] + if original_name in self._app.excitation_objects[port].props["EnabledAnalyses"]: + self._app.excitation_objects[port].props["EnabledAnalyses"][source_name] = ( + self._app.excitation_objects[port].props["EnabledAnalyses"].pop(original_name) + ) + self.update(original_name) + else: + self._logger.warning("Name %s already assigned in the design", source_name) + + @property + def _logger(self): + """Logger.""" + return self._app.logger + + @pyaedt_function_handler() + def _source_props(self, source, source_type=None): + source_prop_dict = {} + if source in self._app.source_names: + source_aedt_props = self._app.odesign.GetChildObject("Excitations").GetChildObject(source) + for el in source_aedt_props.GetPropNames(): + if el == "CosimDefinition": + source_prop_dict[el] = None + elif el == "FreqDependentSourceData": + data = self._app.design_properties["NexximSources"]["Data"][source]["FDSFileName"] + freqs = re.findall(r"freqs=\[(.*?)\]", data) + magnitude = re.findall(r"magnitude=\[(.*?)\]", data) + angle = re.findall(r"angle=\[(.*?)\]", data) + vreal = re.findall(r"vreal=\[(.*?)\]", data) + vimag = re.findall(r"vimag=\[(.*?)\]", data) + source_file = re.findall("voltage_source_file=", data) + source_prop_dict["frequencies"] = None + source_prop_dict["vmag"] = None + source_prop_dict["vang"] = None + source_prop_dict["vreal"] = None + source_prop_dict["vimag"] = None + source_prop_dict["fds_filename"] = None + source_prop_dict["magnitude_angle"] = False + source_prop_dict["FreqDependentSourceData"] = data + if freqs: + source_prop_dict["frequencies"] = [float(i) for i in freqs[0].split()] + if magnitude: + source_prop_dict["vmag"] = [float(i) for i in magnitude[0].split()] + if angle: + source_prop_dict["vang"] = [float(i) for i in angle[0].split()] + if vreal: + source_prop_dict["vreal"] = [float(i) for i in vreal[0].split()] + if vimag: + source_prop_dict["vimag"] = [float(i) for i in vimag[0].split()] + if source_file: + source_prop_dict["fds_filename"] = data[len(re.findall("voltage_source_file=", data)[0]) :] + else: + if freqs and magnitude and angle: + source_prop_dict["magnitude_angle"] = True + elif freqs and vreal and vimag: + source_prop_dict["magnitude_angle"] = False + + elif el != "Name" and el != "Noise": + source_prop_dict[el] = source_aedt_props.GetPropValue(el) + if not source_prop_dict[el]: + source_prop_dict[el] = "" + else: + if source_type in SourceKeys.SourceNames: + command_template = SourceKeys.SourceTemplates[source_type] + commands = copy.deepcopy(command_template) + props = [value for value in commands if isinstance(value, list)] + for el in props[0]: + if isinstance(el, list): + if el[0] == "CosimDefinition": + source_prop_dict[el[0]] = None + elif el[0] == "FreqDependentSourceData": + source_prop_dict["frequencies"] = None + source_prop_dict["vmag"] = None + source_prop_dict["vang"] = None + source_prop_dict["vreal"] = None + source_prop_dict["vimag"] = None + source_prop_dict["fds_filename"] = None + source_prop_dict["magnitude_angle"] = True + source_prop_dict["FreqDependentSourceData"] = "" + + elif el[0] != "ModelName" and el[0] != "LabelID": + source_prop_dict[el[0]] = el[3] + + return source_prop_dict + + @pyaedt_function_handler() + def _update_command(self, name, source_prop_dict, source_type, fds_filename=None): + command_template = SourceKeys.SourceTemplates[source_type] + commands = copy.deepcopy(command_template) + commands[0] = "NAME:" + name + commands[10] = source_prop_dict["Netlist"] + if fds_filename: + commands[14] = fds_filename + cont = 0 + props = [value for value in commands if isinstance(value, list)] + for command in props[0]: + if isinstance(command, list) and command[0] in source_prop_dict.keys() and command[0] != "CosimDefinition": + if command[0] == "Netlist": + props[0].pop(cont) + elif command[0] == "file" and source_prop_dict[command[0]]: + props[0][cont][3] = source_prop_dict[command[0]] + props[0][cont][4] = source_prop_dict[command[0]] + elif command[0] == "FreqDependentSourceData" and fds_filename: + props[0][cont][3] = fds_filename + props[0][cont][4] = fds_filename + else: + props[0][cont][3] = source_prop_dict[command[0]] + cont += 1 + + return commands + + @pyaedt_function_handler() + def _source_type_by_key(self): + for source_name in SourceKeys.SourceNames: + template = SourceKeys.SourceProps[source_name] + if list(self._props.keys()) == template: + return source_name + return None + + @pyaedt_function_handler() + def update(self, original_name=None, new_source=None): + """Update the source in AEDT. + + Parameters + ---------- + original_name : str, optional + Original name of the source. The default value is ``None``. + new_source : str, optional + New name of the source. The default value is ``None``. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + + arg0 = ["NAME:Data"] + if self.source_type != "VoltageFrequencyDependent": + fds_filename = None + else: + fds_filename = self._props["FreqDependentSourceData"] + + for source in self._app.sources: + if "FreqDependentSourceData" in self._app.sources[source]._props.keys(): + fds_filename_source = self._app.sources[source]._props["FreqDependentSourceData"] + else: + fds_filename_source = None + if source == self.name: + arg0.append(list(self._update_command(source, self._props, self.source_type, fds_filename))) + elif source != self.name and original_name == source: + arg0.append( + list( + self._update_command( + self.name, self._props, self._app.sources[source].source_type, fds_filename + ) + ) + ) + else: + arg0.append( + list( + self._update_command( + source, + self._app.sources[source]._props, + self._app.sources[source].source_type, + fds_filename_source, + ) + ) + ) + + if new_source and new_source not in self._app.sources: + arg0.append(list(self._update_command(self.name, self._props, self.source_type, fds_filename))) + + arg1 = ["NAME:NexximSources", ["NAME:NexximSources", arg0]] + arg2 = ["NAME:ComponentConfigurationData"] + + # Check Ports with Sources + arg3 = ["NAME:EnabledPorts"] + for source_name in self._app.sources: + excitation_source = [] + for port in self._app.excitations: + # self._app.excitation_objects[port]._props + if source_name in self._app.excitation_objects[port]._props["EnabledPorts"]: + excitation_source.append(port) + arg3.append(source_name + ":=") + arg3.append(excitation_source) + + if new_source and new_source not in self._app.sources: + arg3.append(new_source + ":=") + arg3.append([]) + + arg4 = ["NAME:EnabledMultipleComponents"] + for source_name in self._app.sources: + arg4.append(source_name + ":=") + arg4.append([]) + + if new_source and new_source not in self._app.sources: + arg4.append(new_source + ":=") + arg4.append([]) + + arg5 = ["NAME:EnabledAnalyses"] + for source_name in self._app.sources: + arg6 = ["NAME:" + source_name] + for port in self._app.excitations: + if source_name in self._app.excitation_objects[port]._props["EnabledAnalyses"]: + arg6.append(port + ":=") + arg6.append(self._app.excitation_objects[port]._props["EnabledAnalyses"][source_name]) + else: + arg6.append(port + ":=") + arg6.append([]) + arg5.append(arg6) + + if new_source and new_source not in self._app.sources: + arg6 = ["NAME:" + new_source] + for port in self._app.excitations: + arg6.append(port + ":=") + arg6.append([]) + arg5.append(arg6) + + arg7 = ["NAME:ComponentConfigurationData", arg3, arg4, arg5] + arg2.append(arg7) + + self._app.odesign.UpdateSources(arg1, arg2) + return True + + @pyaedt_function_handler() + def delete(self): + """Delete the source in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + self._app.modeler._odesign.DeleteSource(self.name) + for port in self._app.excitations: + if self.name in self._app.excitation_objects[port].props["EnabledPorts"]: + self._app.excitation_objects[port].props["EnabledPorts"].remove(self.name) + if self.name in self._app.excitation_objects[port].props["EnabledAnalyses"]: + del self._app.excitation_objects[port].props["EnabledAnalyses"][self.name] + return True + + @pyaedt_function_handler() + def create(self): + """Create a new source in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + self.update(original_name=None, new_source=self.name) + return True + + +class PowerSinSource(Sources, object): + """Power Sinusoidal Class.""" + + def __init__(self, app, name, source_type=None): + Sources.__init__(self, app, name, source_type) + + @property + def _child(self): + return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) + + @property + def ac_magnitude(self): + """AC magnitude value. + + Returns + ------- + str + """ + return self._props["ACMAG"] + + @ac_magnitude.setter + def ac_magnitude(self, value): + self._props["ACMAG"] = value + self._child.SetPropValue("ACMAG", value) + + @property + def ac_phase(self): + """AC phase value. + + Returns + ------- + str + """ + return self._props["ACPHASE"] + + @ac_phase.setter + def ac_phase(self, value): + self._props["ACPHASE"] = value + self._child.SetPropValue("ACPHASE", value) + + @property + def dc_magnitude(self): + """DC voltage value. + + Returns + ------- + str + """ + return self._props["DC"] + + @dc_magnitude.setter + def dc_magnitude(self, value): + self._props["DC"] = value + self._child.SetPropValue("DC", value) + + @property + def power_offset(self): + """Power offset from zero watts. + + Returns + ------- + str + """ + return self._props["VO"] + + @power_offset.setter + def power_offset(self, value): + self._props["VO"] = value + self._child.SetPropValue("VO", value) + + @property + def power_magnitude(self): + """Available power of the source above power offset. + + Returns + ------- + str + """ + return self._props["POWER"] + + @power_magnitude.setter + def power_magnitude(self, value): + self._props["POWER"] = value + self._child.SetPropValue("POWER", value) + + @property + def frequency(self): + """Frequency. + + Returns + ------- + str + """ + return self._props["FREQ"] + + @frequency.setter + def frequency(self, value): + self._props["FREQ"] = value + self._child.SetPropValue("FREQ", value) + + @property + def delay(self): + """Delay to start of sine wave. + + Returns + ------- + str + """ + return self._props["TD"] + + @delay.setter + def delay(self, value): + self._props["TD"] = value + self._child.SetPropValue("TD", value) + + @property + def damping_factor(self): + """Damping factor. + + Returns + ------- + str + """ + return self._props["ALPHA"] + + @damping_factor.setter + def damping_factor(self, value): + self._props["ALPHA"] = value + self._child.SetPropValue("ALPHA", value) + + @property + def phase_delay(self): + """Phase delay. + + Returns + ------- + str + """ + return self._props["THETA"] + + @phase_delay.setter + def phase_delay(self, value): + self._props["THETA"] = value + self._child.SetPropValue("THETA", value) + + @property + def tone(self): + """Frequency to use for harmonic balance. + + Returns + ------- + str + """ + return self._props["TONE"] + + @tone.setter + def tone(self, value): + self._props["TONE"] = value + self._child.SetPropValue("TONE", value) + + +class PowerIQSource(Sources, object): + """Power IQ Class.""" + + def __init__(self, app, name, source_type=None): + Sources.__init__(self, app, name, source_type) + + @property + def _child(self): + return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) + + @property + def carrier_frequency(self): + """Carrier frequency value. + + Returns + ------- + str + """ + return self._props["FC"] + + @carrier_frequency.setter + def carrier_frequency(self, value): + self._props["FC"] = value + self._child.SetPropValue("FC", value) + + @property + def sampling_time(self): + """Sampling time value. + + Returns + ------- + str + """ + return self._props["TS"] + + @sampling_time.setter + def sampling_time(self, value): + self._props["TS"] = value + self._child.SetPropValue("TS", value) + + @property + def dc_magnitude(self): + """DC voltage value. + + Returns + ------- + str + """ + return self._props["DC"] + + @dc_magnitude.setter + def dc_magnitude(self, value): + self._props["DC"] = value + self._child.SetPropValue("DC", value) + + @property + def repeat_from(self): + """Repeat from time. + + Returns + ------- + str + """ + return self._props["R"] + + @repeat_from.setter + def repeat_from(self, value): + self._props["R"] = value + self._child.SetPropValue("R", value) + + @property + def delay(self): + """Delay to start of sine wave. + + Returns + ------- + str + """ + return self._props["TD"] + + @delay.setter + def delay(self, value): + self._props["TD"] = value + self._child.SetPropValue("TD", value) + + @property + def carrier_amplitude_voltage(self): + """Carrier amplitude value, voltage-based. + + Returns + ------- + str + """ + return self._props["V"] + + @carrier_amplitude_voltage.setter + def carrier_amplitude_voltage(self, value): + self._props["V"] = value + self._child.SetPropValue("V", value) + + @property + def carrier_amplitude_power(self): + """Carrier amplitude value, power-based. + + Returns + ------- + str + """ + return self._props["VA"] + + @carrier_amplitude_power.setter + def carrier_amplitude_power(self, value): + self._props["VA"] = value + self._child.SetPropValue("VA", value) + + @property + def carrier_offset(self): + """Carrier offset. + + Returns + ------- + str + """ + return self._props["VO"] + + @carrier_offset.setter + def carrier_offset(self, value): + self._props["VO"] = value + self._child.SetPropValue("VO", value) + + @property + def real_impedance(self): + """Real carrier impedance. + + Returns + ------- + str + """ + return self._props["RZ"] + + @real_impedance.setter + def real_impedance(self, value): + self._props["RZ"] = value + self._child.SetPropValue("RZ", value) + + @property + def imaginary_impedance(self): + """Imaginary carrier impedance. + + Returns + ------- + str + """ + return self._props["IZ"] + + @imaginary_impedance.setter + def imaginary_impedance(self, value): + self._props["IZ"] = value + self._child.SetPropValue("IZ", value) + + @property + def damping_factor(self): + """Damping factor. + + Returns + ------- + str + """ + return self._props["ALPHA"] + + @damping_factor.setter + def damping_factor(self, value): + self._props["ALPHA"] = value + self._child.SetPropValue("ALPHA", value) + + @property + def phase_delay(self): + """Phase delay. + + Returns + ------- + str + """ + return self._props["THETA"] + + @phase_delay.setter + def phase_delay(self, value): + self._props["THETA"] = value + self._child.SetPropValue("THETA", value) + + @property + def tone(self): + """Frequency to use for harmonic balance. + + Returns + ------- + str + """ + return self._props["TONE"] + + @tone.setter + def tone(self, value): + self._props["TONE"] = value + self._child.SetPropValue("TONE", value) + + @property + def i_q_values(self): + """I and Q value at each timepoint. + + Returns + ------- + str + """ + i_q = [] + for cont in range(1, 20): + i_q.append( + [self._props["time" + str(cont)], self._props["ival" + str(cont)], self._props["qval" + str(cont)]] + ) + return i_q + + @i_q_values.setter + def i_q_values(self, value): + cont = 0 + for point in value: + self._props["time" + str(cont + 1)] = point[0] + self._child.SetPropValue("time" + str(cont + 1), point[0]) + self._props["ival" + str(cont + 1)] = point[1] + self._child.SetPropValue("ival" + str(cont + 1), point[1]) + self._props["qval" + str(cont + 1)] = point[2] + self._child.SetPropValue("qval" + str(cont + 1), point[2]) + cont += 1 + + @property + def file( + self, + ): + """File path with I and Q values. + + Returns + ------- + str + """ + return self._props["file"] + + @file.setter + def file(self, value): + self._props["file"] = value + self.update() + + +class VoltageFrequencyDependentSource(Sources, object): + """Voltage Frequency Dependent Class.""" + + def __init__(self, app, name, source_type=None): + Sources.__init__(self, app, name, source_type) + + @property + def _child(self): + return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) + + @property + def frequencies(self): + """List of frequencies in ``Hz``. + + Returns + ------- + list + """ + return self._props["frequencies"] + + @frequencies.setter + def frequencies(self, value): + self._props["frequencies"] = [float(i) for i in value] + self._update_prop() + + @property + def vmag(self): + """List of magnitudes in ``V``. + + Returns + ------- + list + """ + return self._props["vmag"] + + @vmag.setter + def vmag(self, value): + self._props["vmag"] = [float(i) for i in value] + self._update_prop() + + @property + def vang(self): + """List of angles in ``rad``. + + Returns + ------- + list + """ + return self._props["vang"] + + @vang.setter + def vang(self, value): + self._props["vang"] = [float(i) for i in value] + self._update_prop() + + @property + def vreal(self): + """List of real values in ``V``. + + Returns + ------- + list + """ + return self._props["vreal"] + + @vreal.setter + def vreal(self, value): + self._props["vreal"] = [float(i) for i in value] + self._update_prop() + + @property + def vimag(self): + """List of imaginary values in ``V``. + + Returns + ------- + list + """ + return self._props["vimag"] + + @vimag.setter + def vimag(self, value): + self._props["vimag"] = [float(i) for i in value] + self._update_prop() + + @property + def magnitude_angle(self): + """Enable magnitude and angle data. + + Returns + ------- + bool + """ + return self._props["magnitude_angle"] + + @magnitude_angle.setter + def magnitude_angle(self, value): + self._props["magnitude_angle"] = value + self._update_prop() + + @property + def fds_filename(self): + """FDS file path. + + Returns + ------- + bool + """ + return self._props["fds_filename"] + + @fds_filename.setter + def fds_filename(self, name): + if not name: + self._props["fds_filename"] = None + self._update_prop() + else: + self._props["fds_filename"] = name + self._props["FreqDependentSourceData"] = "voltage_source_file=" + name + self.update() + + @pyaedt_function_handler() + def _update_prop(self): + if ( + self._props["vmag"] + and self._props["vang"] + and self._props["frequencies"] + and self._props["magnitude_angle"] + and not self._props["fds_filename"] + ): + if len(self._props["vmag"]) == len(self._props["vang"]) == len(self._props["frequencies"]): + self._props["FreqDependentSourceData"] = ( + "freqs=" + + str(self._props["frequencies"]).replace(",", "") + + " vmag=" + + str(self._props["vmag"]).replace(",", "") + + " vang=" + + str(self._props["vang"]).replace(",", "") + ) + self.update() + elif ( + self._props["vreal"] + and self._props["vimag"] + and self._props["frequencies"] + and not self._props["magnitude_angle"] + and not self._props["fds_filename"] + ): + if len(self._props["vreal"]) == len(self._props["vimag"]) == len(self._props["frequencies"]): + self._props["FreqDependentSourceData"] = ( + "freqs=" + + str(self._props["frequencies"]).replace(",", "") + + " vreal=" + + str(self._props["vreal"]).replace(",", "") + + " vimag=" + + str(self._props["vimag"]).replace(",", "") + ) + self.update() + else: + self._props["FreqDependentSourceData"] = "" + self.update() + return True + + +class VoltageDCSource(Sources, object): + """Power Sinusoidal Class.""" + + def __init__(self, app, name, source_type=None): + Sources.__init__(self, app, name, source_type) + + @property + def _child(self): + return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) + + @property + def ac_magnitude(self): + """AC magnitude value. + + Returns + ------- + str + """ + return self._props["ACMAG"] + + @ac_magnitude.setter + def ac_magnitude(self, value): + self._props["ACMAG"] = value + self._child.SetPropValue("ACMAG", value) + + @property + def ac_phase(self): + """AC phase value. + + Returns + ------- + str + """ + return self._props["ACPHASE"] + + @ac_phase.setter + def ac_phase(self, value): + self._props["ACPHASE"] = value + self._child.SetPropValue("ACPHASE", value) + + @property + def dc_magnitude(self): + """DC voltage value. + + Returns + ------- + str + """ + return self._props["DC"] + + @dc_magnitude.setter + def dc_magnitude(self, value): + self._props["DC"] = value + self._child.SetPropValue("DC", value) + + +class VoltageSinSource(Sources, object): + """Power Sinusoidal Class.""" + + def __init__(self, app, name, source_type=None): + Sources.__init__(self, app, name, source_type) + + @property + def _child(self): + return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) + + @property + def ac_magnitude(self): + """AC magnitude value. + + Returns + ------- + str + """ + return self._props["ACMAG"] + + @ac_magnitude.setter + def ac_magnitude(self, value): + self._props["ACMAG"] = value + self._child.SetPropValue("ACMAG", value) + + @property + def ac_phase(self): + """AC phase value. + + Returns + ------- + str + """ + return self._props["ACPHASE"] + + @ac_phase.setter + def ac_phase(self, value): + self._props["ACPHASE"] = value + self._child.SetPropValue("ACPHASE", value) + + @property + def dc_magnitude(self): + """DC voltage value. + + Returns + ------- + str + """ + return self._props["DC"] + + @dc_magnitude.setter + def dc_magnitude(self, value): + self._props["DC"] = value + self._child.SetPropValue("DC", value) + + @property + def voltage_amplitude(self): + """Voltage amplitude. + + Returns + ------- + str + """ + return self._props["VA"] + + @voltage_amplitude.setter + def voltage_amplitude(self, value): + self._props["VA"] = value + self._child.SetPropValue("VA", value) + + @property + def voltage_offset(self): + """Voltage offset from zero watts. + + Returns + ------- + str + """ + return self._props["VO"] + + @voltage_offset.setter + def voltage_offset(self, value): + self._props["VO"] = value + self._child.SetPropValue("VO", value) + + @property + def frequency(self): + """Frequency. + + Returns + ------- + str + """ + return self._props["FREQ"] + + @frequency.setter + def frequency(self, value): + self._props["FREQ"] = value + self._child.SetPropValue("FREQ", value) + + @property + def delay(self): + """Delay to start of sine wave. + + Returns + ------- + str + """ + return self._props["TD"] + + @delay.setter + def delay(self, value): + self._props["TD"] = value + self._child.SetPropValue("TD", value) + + @property + def damping_factor(self): + """Damping factor. + + Returns + ------- + str + """ + return self._props["ALPHA"] + + @damping_factor.setter + def damping_factor(self, value): + self._props["ALPHA"] = value + self._child.SetPropValue("ALPHA", value) + + @property + def phase_delay(self): + """Phase delay. + + Returns + ------- + str + """ + return self._props["THETA"] + + @phase_delay.setter + def phase_delay(self, value): + self._props["THETA"] = value + self._child.SetPropValue("THETA", value) + + @property + def tone(self): + """Frequency to use for harmonic balance. + + Returns + ------- + str + """ + return self._props["TONE"] + + @tone.setter + def tone(self, value): + self._props["TONE"] = value + self._child.SetPropValue("TONE", value) + + +class CurrentSinSource(Sources, object): + """Current Sinusoidal Class.""" + + def __init__(self, app, name, source_type=None): + Sources.__init__(self, app, name, source_type) + + @property + def _child(self): + return self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) + + @property + def ac_magnitude(self): + """AC magnitude value. + + Returns + ------- + str + """ + return self._props["ACMAG"] + + @ac_magnitude.setter + def ac_magnitude(self, value): + self._props["ACMAG"] = value + self._child.SetPropValue("ACMAG", value) + + @property + def ac_phase(self): + """AC phase value. + + Returns + ------- + str + """ + return self._props["ACPHASE"] + + @ac_phase.setter + def ac_phase(self, value): + self._props["ACPHASE"] = value + self._child.SetPropValue("ACPHASE", value) + + @property + def dc_magnitude(self): + """DC current value. + + Returns + ------- + str + """ + return self._props["DC"] + + @dc_magnitude.setter + def dc_magnitude(self, value): + self._props["DC"] = value + self._child.SetPropValue("DC", value) + + @property + def current_amplitude(self): + """Current amplitude. + + Returns + ------- + str + """ + return self._props["VA"] + + @current_amplitude.setter + def current_amplitude(self, value): + self._props["VA"] = value + self._child.SetPropValue("VA", value) + + @property + def current_offset(self): + """Current offset. + + Returns + ------- + str + """ + return self._props["VO"] + + @current_offset.setter + def current_offset(self, value): + self._props["VO"] = value + self._child.SetPropValue("VO", value) + + @property + def frequency(self): + """Frequency. + + Returns + ------- + str + """ + return self._props["FREQ"] + + @frequency.setter + def frequency(self, value): + self._props["FREQ"] = value + self._child.SetPropValue("FREQ", value) + + @property + def delay(self): + """Delay to start of sine wave. + + Returns + ------- + str + """ + return self._props["TD"] + + @delay.setter + def delay(self, value): + self._props["TD"] = value + self._child.SetPropValue("TD", value) + + @property + def damping_factor(self): + """Damping factor. + + Returns + ------- + str + """ + return self._props["ALPHA"] + + @damping_factor.setter + def damping_factor(self, value): + self._props["ALPHA"] = value + self._child.SetPropValue("ALPHA", value) + + @property + def phase_delay(self): + """Phase delay. + + Returns + ------- + str + """ + return self._props["THETA"] + + @phase_delay.setter + def phase_delay(self, value): + self._props["THETA"] = value + self._child.SetPropValue("THETA", value) + + @property + def multiplier(self): + """Multiplier for simulating multiple parallel current sources. + + Returns + ------- + str + """ + return self._props["M"] + + @multiplier.setter + def multiplier(self, value): + self._props["M"] = value + self._child.SetPropValue("M", value) + + @property + def tone(self): + """Frequency to use for harmonic balance. + + Returns + ------- + str + """ + return self._props["TONE"] + + @tone.setter + def tone(self, value): + self._props["TONE"] = value + self._child.SetPropValue("TONE", value) + + +class Excitations(object): + """Manages Excitations in Circuit Projects. + + Examples + -------- + + """ + + def __init__(self, app, name): + self._app = app + self._name = name + for comp in self._app.modeler.schematic.components: + if ( + "PortName" in self._app.modeler.schematic.components[comp].parameters.keys() + and self._app.modeler.schematic.components[comp].parameters["PortName"] == self.name + ): + self.schematic_id = comp + self.id = self._app.modeler.schematic.components[comp].id + self._angle = self._app.modeler.schematic.components[comp].angle + self.levels = self._app.modeler.schematic.components[comp].levels + self._location = self._app.modeler.schematic.components[comp].location + self._mirror = self._app.modeler.schematic.components[comp].mirror + self.pins = self._app.modeler.schematic.components[comp].pins + self._use_symbol_color = self._app.modeler.schematic.components[comp].usesymbolcolor + break + self._props = self._excitation_props(name) + self._auto_update = True + + @property + def name(self): + """Excitation name. + + Returns + ------- + str + """ + return self._name + + @name.setter + def name(self, port_name): + if port_name not in self._app.excitations: + if port_name != self._name: + # Take previous properties + self._app.odesign.RenamePort(self._name, port_name) + self._name = port_name + self._app.modeler.schematic.components[self.schematic_id].name = "IPort@" + port_name + self.pins[0].name = "IPort@" + port_name + ";" + str(self.schematic_id) + else: + self._logger.warning("Name %s already assigned in the design", port_name) + + @property + def angle(self): + """Symbol angle. + + Returns + ------- + float + """ + return self._angle + + @angle.setter + def angle(self, angle): + self._app.modeler.schematic.components[self.schematic_id].angle = angle + + @property + def mirror(self): + """Enable port mirror. + + Returns + ------- + bool + """ + return self._mirror + + @mirror.setter + def mirror(self, mirror_value=True): + self._app.modeler.schematic.components[self.schematic_id].mirror = mirror_value + self._mirror = mirror_value + + @property + def location(self): + """Port location. + + Returns + ------- + list + """ + return self._location + + @location.setter + def location(self, location_xy): + # The command must be called two times. + self._app.modeler.schematic.components[self.schematic_id].location = location_xy + self._app.modeler.schematic.components[self.schematic_id].location = location_xy + self._location = location_xy + + @property + def use_symbol_color(self): + """Use symbol color. + + Returns + ------- + list + """ + return self._use_symbol_color + + @use_symbol_color.setter + def use_symbol_color(self, use_color=True): + self._app.modeler.schematic.components[self.schematic_id].usesymbolcolor = use_color + self._app.modeler.schematic.components[self.schematic_id].set_use_symbol_color(use_color) + self._use_symbol_color = use_color + + @property + def impedance(self): + """Port termination. + + Returns + ------- + list + """ + return [self._props["rz"], self._props["iz"]] + + @impedance.setter + def impedance(self, termination=None): + if termination and len(termination) == 2: + self._app.modeler.schematic.components[self.schematic_id].change_property( + ["NAME:rz", "Value:=", termination[0]] + ) + self._app.modeler.schematic.components[self.schematic_id].change_property( + ["NAME:iz", "Value:=", termination[1]] + ) + self._props["rz"] = termination[0] + self._props["iz"] = termination[1] + + @property + def enable_noise(self): + """Enable noise. + + Returns + ------- + bool + """ + + return self._props["EnableNoise"] + + @enable_noise.setter + def enable_noise(self, enable=False): + self._app.modeler.schematic.components[self.schematic_id].change_property( + ["NAME:EnableNoise", "Value:=", enable] + ) + self._props["EnableNoise"] = enable + + @property + def noise_temperature(self): + """Enable noise. + + Returns + ------- + str + """ + + return self._props["noisetemp"] + + @noise_temperature.setter + def noise_temperature(self, noise=None): + if noise: + self._app.modeler.schematic.components[self.schematic_id].change_property( + ["NAME:noisetemp", "Value:=", noise] + ) + self._props["noisetemp"] = noise + + @property + def microwave_symbol(self): + """Enable microwave symbol. + + Returns + ------- + bool + """ + if self._props["SymbolType"] == 1: + return True + else: + return False + + @microwave_symbol.setter + def microwave_symbol(self, enable=False): + if enable: + self._props["SymbolType"] = 1 + else: + self._props["SymbolType"] = 0 + self.update() + + @property + def reference_node(self): + """Reference node. + + Returns + ------- + str + """ + if self._props["RefNode"] == "Z": + return "Ground" + return self._props["RefNode"] + + @reference_node.setter + def reference_node(self, ref_node=None): + if ref_node: + self._logger.warning("Set reference node only working with gRPC") + if ref_node == "Ground": + ref_node = "Z" + self._props["RefNode"] = ref_node + self.update() + + @property + def enabled_sources(self): + """Enabled sources. + + Returns + ------- + list + """ + return self._props["EnabledPorts"] + + @enabled_sources.setter + def enabled_sources(self, sources=None): + if sources: + self._props["EnabledPorts"] = sources + self.update() + + @property + def enabled_analyses(self): + """Enabled analyses. + + Returns + ------- + dict + """ + return self._props["EnabledAnalyses"] + + @enabled_analyses.setter + def enabled_analyses(self, analyses=None): + if analyses: + self._props["EnabledAnalyses"] = analyses + self.update() + + @pyaedt_function_handler() + def _excitation_props(self, port): + excitation_prop_dict = {} + for comp in self._app.modeler.schematic.components: + if ( + "PortName" in self._app.modeler.schematic.components[comp].parameters.keys() + and self._app.modeler.schematic.components[comp].parameters["PortName"] == port + ): + excitation_prop_dict["rz"] = "50ohm" + excitation_prop_dict["iz"] = "0ohm" + excitation_prop_dict["term"] = None + excitation_prop_dict["TerminationData"] = None + excitation_prop_dict["RefNode"] = "Z" + excitation_prop_dict["EnableNoise"] = False + excitation_prop_dict["noisetemp"] = "16.85cel" + + if "RefNode" in self._app.modeler.schematic.components[comp].parameters: + excitation_prop_dict["RefNode"] = self._app.modeler.schematic.components[comp].parameters["RefNode"] + if "term" in self._app.modeler.schematic.components[comp].parameters: + excitation_prop_dict["term"] = self._app.modeler.schematic.components[comp].parameters["term"] + excitation_prop_dict["TerminationData"] = self._app.modeler.schematic.components[comp].parameters[ + "TerminationData" + ] + else: + if "rz" in self._app.modeler.schematic.components[comp].parameters: + excitation_prop_dict["rz"] = self._app.modeler.schematic.components[comp].parameters["rz"] + excitation_prop_dict["iz"] = self._app.modeler.schematic.components[comp].parameters["iz"] + + if "EnableNoise" in self._app.modeler.schematic.components[comp].parameters: + if self._app.modeler.schematic.components[comp].parameters["EnableNoise"] == "true": + excitation_prop_dict["EnableNoise"] = True + else: + excitation_prop_dict["EnableNoise"] = False + + excitation_prop_dict["noisetemp"] = self._app.modeler.schematic.components[comp].parameters[ + "noisetemp" + ] + + if not self._app.design_properties or not self._app.design_properties["NexximPorts"]["Data"]: + excitation_prop_dict["SymbolType"] = 0 + else: + excitation_prop_dict["SymbolType"] = self._app.design_properties["NexximPorts"]["Data"][port][ + "SymbolType" + ] + + if "pnum" in self._app.modeler.schematic.components[comp].parameters: + excitation_prop_dict["pnum"] = self._app.modeler.schematic.components[comp].parameters["pnum"] + else: + excitation_prop_dict["pnum"] = None + source_port = [] + if not self._app.design_properties: + enabled_ports = None + else: + enabled_ports = self._app.design_properties["ComponentConfigurationData"]["EnabledPorts"] + if isinstance(enabled_ports, dict): + for source in enabled_ports: + if enabled_ports[source] and port in enabled_ports[source]: + source_port.append(source) + excitation_prop_dict["EnabledPorts"] = source_port + + components_port = [] + if not self._app.design_properties: + multiple = None + else: + multiple = self._app.design_properties["ComponentConfigurationData"]["EnabledMultipleComponents"] + if isinstance(multiple, dict): + for source in multiple: + if multiple[source] and port in multiple[source]: + components_port.append(source) + excitation_prop_dict["EnabledMultipleComponents"] = components_port + + port_analyses = {} + if not self._app.design_properties: + enabled_analyses = None + else: + enabled_analyses = self._app.design_properties["ComponentConfigurationData"]["EnabledAnalyses"] + if isinstance(enabled_analyses, dict): + for source in enabled_analyses: + if ( + enabled_analyses[source] + and port in enabled_analyses[source] + and source in excitation_prop_dict["EnabledPorts"] + ): + port_analyses[source] = enabled_analyses[source][port] + excitation_prop_dict["EnabledAnalyses"] = port_analyses + return excitation_prop_dict + + @pyaedt_function_handler() + def update(self): + """Update the excitation in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + + # self._logger.warning("Property port update only working with GRPC") + + if self._props["RefNode"] == "Ground": + self._props["RefNode"] = "Z" + + arg0 = [ + "NAME:" + self.name, + "IIPortName:=", + self.name, + "SymbolType:=", + self._props["SymbolType"], + "DoPostProcess:=", + False, + ] + + arg1 = ["NAME:ChangedProps"] + arg2 = [] + + # Modify RefNode + if self._props["RefNode"] != "Z": + arg2 = [ + "NAME:NewProps", + ["NAME:RefNode", "PropType:=", "TextProp", "OverridingDef:=", True, "Value:=", self._props["RefNode"]], + ] + + # Modify Termination + if self._props["term"] and self._props["TerminationData"]: + arg2 = [ + "NAME:NewProps", + ["NAME:term", "PropType:=", "TextProp", "OverridingDef:=", True, "Value:=", self._props["term"]], + ] + + for prop in self._props: + skip1 = (prop == "rz" or prop == "iz") and isinstance(self._props["term"], str) + skip2 = prop == "EnabledPorts" or prop == "EnabledMultipleComponents" or prop == "EnabledAnalyses" + skip3 = prop == "SymbolType" + skip4 = prop == "TerminationData" and not isinstance(self._props["term"], str) + if not skip1 and not skip2 and not skip3 and not skip4 and self._props[prop] is not None: + command = ["NAME:" + prop, "Value:=", self._props[prop]] + arg1.append(command) + + arg1 = [["NAME:Properties", arg2, arg1]] + self._app.odesign.ChangePortProperty(self.name, arg0, arg1) + + for source in self._app.sources: + self._app.sources[source].update() + return True + + @pyaedt_function_handler() + def delete(self): + """Delete the port in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + self._app.modeler._odesign.DeletePort(self.name) + return True + + @property + def _logger(self): + """Logger.""" + return self._app.logger + + +class NetworkObject(BoundaryObject): + """Manages networks in Icepak projects.""" + + def __init__(self, app, name=None, props=None, create=False): + if not app.design_type == "Icepak": # pragma: no cover + raise NotImplementedError("Networks object works only with Icepak projects ") + if name is None: + self._name = generate_unique_name("Network") + else: + self._name = name + super(NetworkObject, self).__init__(app, self._name, props, "Network", False) + + self._nodes = [] + self._links = [] + self._schematic_data = {} + self._update_from_props() + if create: + self.create() + + def _clean_list(self, arg): + new_list = [] + for item in arg: + if isinstance(item, list): + if item[0] == "NAME:PageNet": + page_net_list = [] + for i in item: + if isinstance(i, list): + name = page_net_list[-1] + page_net_list.pop(-1) + for j in i: + page_net_list.append(name) + page_net_list.append(j) + else: + page_net_list.append(i) + new_list.append(page_net_list) + else: + new_list.append(self._clean_list(item)) + else: + new_list.append(item) + return new_list + + @pyaedt_function_handler() + def create(self): + """ + Create network in AEDT. + + Returns + ------- + bool: + True if successful. + """ + if not self.props.get("Faces", None): + self.props["Faces"] = [node.props["FaceID"] for _, node in self.face_nodes.items()] + if not self.props.get("SchematicData", None): + self.props["SchematicData"] = {} + + if self.props.get("Links", None): + self.props["Links"] = {link_name: link_values.props for link_name, link_values in self.links.items()} + else: # pragma : no cover + raise KeyError("Links information is missing.") + if self.props.get("Nodes", None): + self.props["Nodes"] = {node_name: node_values.props for node_name, node_values in self.nodes.items()} + else: # pragma : no cover + raise KeyError("Nodes information is missing.") + + args = self._get_args() + + clean_args = self._clean_list(args) + self._app.oboundary.AssignNetworkBoundary(clean_args) + return True + + @pyaedt_function_handler() + def _update_from_props(self): + nodes = self.props.get("Nodes", None) + if nodes is not None: + nd_name_list = [node.name for node in self._nodes] + for node_name, node_dict in nodes.items(): + if node_name not in nd_name_list: + nd_type = node_dict.get("NodeType", None) + if nd_type == "InternalNode": + self.add_internal_node( + node_name, + node_dict.get("Power", node_dict.get("Power Variation Data", None)), + mass=node_dict.get("Mass", None), + specific_heat=node_dict.get("SpecificHeat", None), + ) + elif nd_type == "BoundaryNode": + self.add_boundary_node( + node_name, + assignment_type=node_dict["ValueType"].replace("Value", ""), + value=node_dict[node_dict["ValueType"].replace("Value", "")], + ) + else: + if ( + node_dict["ThermalResistance"] == "NoResistance" + or node_dict["ThermalResistance"] == "Specified" + ): + node_material, node_thickness = None, None + node_resistance = node_dict["Resistance"] + else: + node_thickness, node_material = node_dict["Thickness"], node_dict["Material"] + node_resistance = None + self.add_face_node( + node_dict["FaceID"], + name=node_name, + thermal_resistance=node_dict["ThermalResistance"], + material=node_material, + thickness=node_thickness, + resistance=node_resistance, + ) + links = self.props.get("Links", None) + if links is not None: + l_name_list = [l.name for l in self._links] + for link_name, link_dict in links.items(): + if link_name not in l_name_list: + self.add_link(link_dict[0], link_dict[1], link_dict[-1], link_name) + + @property + def auto_update(self): + """ + Get if auto-update is enabled. + + Returns + ------- + bool: + Whether auto-update is enabled. + """ + return False + + @auto_update.setter + def auto_update(self, b): + """ + Set auto-update on or off. + + Parameters + ---------- + b : bool + Whether to enable auto-update. + + """ + if b: + self._app.logger.warning( + "Network objects auto_update property is False by default" " and cannot be set to True." + ) + + @property + def links(self): + """ + Get links of the network. + + Returns + ------- + dict: + Links dictionary. + + """ + self._update_from_props() + return {link.name: link for link in self._links} + + @property + def r_links(self): + """ + Get r-links of the network. + + Returns + ------- + dict: + R-links dictionary. + + """ + self._update_from_props() + return {link.name: link for link in self._links if link._link_type[0] == "R-Link"} + + @property + def c_links(self): + """ + Get c-links of the network. + + Returns + ------- + dict: + C-links dictionary. + + """ + self._update_from_props() + return {link.name: link for link in self._links if link._link_type[0] == "C-Link"} + + @property + def nodes(self): + """ + Get nodes of the network. + + Returns + ------- + dict: + Nodes dictionary. + + """ + self._update_from_props() + return {node.name: node for node in self._nodes} + + @property + def face_nodes(self): + """ + Get face nodes of the network. + + Returns + ------- + dict: + Face nodes dictionary. + + """ + self._update_from_props() + return {node.name: node for node in self._nodes if node.node_type == "FaceNode"} + + @property + def faces_ids_in_network(self): + """ + Get ID of faces included in the network. + + Returns + ------- + list: + Face IDs. + + """ + out_arr = [] + for _, node_dict in self.face_nodes.items(): + out_arr.append(node_dict.props["FaceID"]) + return out_arr + + @property + def objects_in_network(self): + """ + Get objects included in the network. + + Returns + ------- + list: + Objects names. + + """ + out_arr = [] + for face_id in self.faces_ids_in_network: + out_arr.append(self._app.oeditor.GetObjectNameByFaceID(face_id)) + return out_arr + + @property + def internal_nodes(self): + """ + Get internal nodes. + + Returns + ------- + dict: + Internal nodes. + + """ + self._update_from_props() + return {node.name: node for node in self._nodes if node.node_type == "InternalNode"} + + @property + def boundary_nodes(self): + """ + Get boundary nodes. + + Returns + ------- + dict: + Boundary nodes. + + """ + self._update_from_props() + return {node.name: node for node in self._nodes if node.node_type == "BoundaryNode"} + + @property + def name(self): + """ + Get network name. + + Returns + ------- + str + Network name. + """ + return self._name + + @name.setter + def name(self, new_network_name): + """ + Set new name of the network. + + Parameters + ---------- + new_network_name : str + New name of the network. + """ + bound_names = [b.name for b in self._app.boundaries] + if self.name in bound_names: + if new_network_name not in bound_names: + if new_network_name != self._name: + self._app._oboundary.RenameBoundary(self._name, new_network_name) + self._name = new_network_name + else: + self._app.logger.warning("Name %s already assigned in the design", new_network_name) + else: + self._name = new_network_name + + @pyaedt_function_handler() + def add_internal_node(self, name, power, mass=None, specific_heat=None): + """Add an internal node to the network. + + Parameters + ---------- + name : str + Name of the node. + power : str or float or dict + String, float, or dictionary containing the value of the assignment. + If a float is passed, the ``"W"`` unit is used. A dictionary can be + passed to use temperature-dependent or transient + assignments. + mass : str or float, optional + Value of the mass assignment. This parameter is relevant only + if the solution is transient. If a float is passed, the ``"Kg"`` unit + is used. The default is ``None``, in which case ``"0.001kg"`` is used. + specific_heat : str or float, optional + Value of the specific heat assignment. This parameter is + relevant only if the solution is transient. If a float is passed, + the ``"J_per_Kelkg"`` unit is used. The default is ``None`, in + which case ``"1000J_per_Kelkg"`` is used. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + Examples + -------- + >>> import ansys.aedt.core + >>> app = ansys.aedt.core.Icepak() + >>> network = ansys.aedt.core.modules.boundary.Network(app) + >>> network.add_internal_node("TestNode", {"Type": "Transient", + >>> "Function": "Linear", "Values": ["0.01W", "1"]}) + """ + if self._app.solution_type != "SteadyState" and mass is None and specific_heat is None: + self._app.logger.warning("The solution is transient but neither mass nor specific heat is assigned.") + if self._app.solution_type == "SteadyState" and ( + mass is not None or specific_heat is not None + ): # pragma: no cover + self._app.logger.warning( + "Because the solution is steady state, neither mass nor specific heat assignment is relevant." + ) + if isinstance(power, (int, float)): + power = str(power) + "W" + props_dict = {"Power": power} + if mass is not None: + if isinstance(mass, (int, float)): + mass = str(mass) + "kg" + props_dict.update({"Mass": mass}) + if specific_heat is not None: + if isinstance(specific_heat, (int, float)): + specific_heat = str(specific_heat) + "J_per_Kelkg" + props_dict.update({"SpecificHeat": specific_heat}) + new_node = self._Node(name, self._app, node_type="InternalNode", props=props_dict, network=self) + self._nodes.append(new_node) + self._add_to_props(new_node) + return new_node + + @pyaedt_function_handler() + def add_boundary_node(self, name, assignment_type, value): + """ + Add a boundary node to the network. + + Parameters + ---------- + name : str + Name of the node. + assignment_type : str + Type assignment. Options are ``"Power"`` and ``"Temperature"``. + value : str or float or dict + String, float, or dictionary containing the value of the assignment. + If a float is passed the ``"W"`` or ``"cel"`` unit is used, depending on + the selection for the ``assignment_type`` parameter. If ``"Power"`` + is selected for the type, a dictionary can be passed to use + temperature-dependent or transient assignment. + + Returns + ------- + bool + ``True`` if successful. + + Examples + -------- + >>> import ansys.aedt.core + >>> app = ansys.aedt.core.Icepak() + >>> network = ansys.aedt.core.modules.boundary.Network(app) + >>> network.add_boundary_node("TestNode", "Temperature", 2) + >>> ds = app.create_dataset1d_design("Test_DataSet",[1, 2, 3],[3, 4, 5]) + >>> network.add_boundary_node("TestNode", "Power", {"Type": "Temp Dep", + >>> "Function": "Piecewise Linear", + >>> "Values": "Test_DataSet"}) + """ + if assignment_type not in ["Power", "Temperature", "PowerValue", "TemperatureValue"]: # pragma: no cover + raise AttributeError('``type`` can be only ``"Power"`` or ``"Temperature"``.') + if isinstance(value, (float, int)): + if assignment_type == "Power" or assignment_type == "PowerValue": + value = str(value) + "W" + else: + value = str(value) + "cel" + if isinstance(value, dict) and ( + assignment_type == "Temperature" or assignment_type == "TemperatureValue" + ): # pragma: no cover + raise AttributeError( + "Temperature-dependent or transient assignment is not supported in a temperature boundary node." + ) + if not assignment_type.endswith("Value"): + assignment_type += "Value" + new_node = self._Node( + name, + self._app, + node_type="BoundaryNode", + props={"ValueType": assignment_type, assignment_type.removesuffix("Value"): value}, + network=self, + ) + self._nodes.append(new_node) + self._add_to_props(new_node) + return new_node + + @pyaedt_function_handler() + def _add_to_props(self, new_node, type_dict="Nodes"): + try: + self.props[type_dict].update({new_node.name: new_node.props}) + except KeyError: + self.props[type_dict] = {new_node.name: new_node.props} + + @pyaedt_function_handler(face_id="assignment") + def add_face_node( + self, assignment, name=None, thermal_resistance="NoResistance", material=None, thickness=None, resistance=None + ): + """ + Create a face node in the network. + + Parameters + ---------- + assignment : int + Face ID. + name : str, optional + Name of the node. Default is ``None``. + thermal_resistance : str + Thermal resistance value and unit. Default is ``"NoResistance"``. + material : str, optional + Material specification (required if ``thermal_resistance="Compute"``). + Default is ``None``. + thickness : str or float, optional + Thickness value and unit (required if ``thermal_resistance="Compute"``). + If a float is passed, ``"mm"`` unit is automatically used. Default is ``None``. + resistance : str or float, optional + Resistance value and unit (required if ``thermal_resistance="Specified"``). + If a float is passed, ``"cel_per_w"`` unit is automatically used. Default is ``None``. + + Returns + ------- + bool + True if successful. + + Examples + -------- + >>> import ansys.aedt.core + >>> app = ansys.aedt.core.Icepak() + >>> network = ansys.aedt.core.modules.boundary.Network(app) + >>> box = app.modeler.create_box([5, 5, 5],[20, 50, 80]) + >>> faces_ids = [face.id for face in box.faces] + >>> network.add_face_node(faces_ids[0]) + >>> network.add_face_node(faces_ids[1],name="TestNode",thermal_resistance="Compute", + ... material="Al-Extruded",thickness="2mm") + >>> network.add_face_node(faces_ids[2],name="TestNode",thermal_resistance="Specified",resistance=2) + """ + props_dict = {} + props_dict["FaceID"] = assignment + if thermal_resistance is not None: + if thermal_resistance == "Compute": + if resistance is not None: + self._app.logger.info( + '``resistance`` assignment is incompatible with ``thermal_resistance="Compute"``' + "and it is ignored." + ) + if material is not None or thickness is not None: + props_dict["ThermalResistance"] = thermal_resistance + props_dict["Material"] = material + if not isinstance(thickness, str): + thickness = str(thickness) + "mm" + props_dict["Thickness"] = thickness + else: # pragma: no cover + raise AttributeError( + 'If ``thermal_resistance="Compute"`` both ``material`` and ``thickness``' + "arguments must be prescribed." + ) + if thermal_resistance == "Specified": + if material is not None or thickness is not None: + self._app.logger.warning( + "Because ``material`` and ``thickness`` assignments are incompatible with" + '``thermal_resistance="Specified"``, they are ignored.' + ) + if resistance is not None: + props_dict["ThermalResistance"] = thermal_resistance + if not isinstance(resistance, str): + resistance = str(resistance) + "cel_per_w" + props_dict["Resistance"] = resistance + else: # pragma : no cover + raise AttributeError( + 'If ``thermal_resistance="Specified"``, ``resistance`` argument must be prescribed.' + ) + + if name is None: + name = "FaceID" + str(assignment) + new_node = self._Node(name, self._app, node_type="FaceNode", props=props_dict, network=self) + self._nodes.append(new_node) + self._add_to_props(new_node) + return new_node + + @pyaedt_function_handler(nodes_dict="nodes") + def add_nodes_from_dictionaries(self, nodes): + """ + Add nodes to the network from dictionary. + + Parameters + ---------- + nodes : list or dict + A dictionary or list of dictionaries containing nodes to add to the network. Different + node types require different key and value pairs: + + - Face nodes must contain the ``"ID"`` key associated with an integer containing the face ID. + Optional keys and values pairs are: + + - ``"ThermalResistance"``: a string specifying the type of thermal resistance. + Options are ``"NoResistance"`` (default), ``"Compute"``, and ``"Specified"``. + - ``"Thickness"``: a string with the thickness value and unit (required if ``"Compute"`` + is selected for ``"ThermalResistance"``). + - ``"Material"``: a string with the name of the material (required if ``"Compute"`` is + selected for ``"ThermalResistance"``). + - ``"Resistance"``: a string with the resistance value and unit (required if + ``"Specified"`` is selected for ``"ThermalResistance"``). + - ``"Name"``: a string with the name of the node. If not + specified, a name is generated automatically. + + + - Internal nodes must contain the following keys and values pairs: + + - ``"Name"``: a string with the node name + - ``"Power"``: a string with the assigned power or a dictionary for transient or + temperature-dependent assignment + Optional keys and values pairs: + - ``"Mass"``: a string with the mass value and unit + - ``"SpecificHeat"``: a string with the specific heat value and unit + + - Boundary nodes must contain the following keys and values pairs: + + - ``"Name"``: a string with the node name + - ``"ValueType"``: a string specifying the type of value (``"Power"`` or + ``"Temperature"``) + Depending on the ``"ValueType"`` choice, one of the following keys and values pairs must + be used: + - ``"Power"``: a string with the power value and unit or a dictionary for transient or + temperature-dependent assignment + - ``"Temperature"``: a string with the temperature value and unit or a dictionary for + transient or temperature-dependent assignment + According to the ``"ValueType"`` choice, ``"Power"`` or ``"Temperature"`` key must be + used. Their associated value a string with the value and unit of the quantity prescribed or + a dictionary for transient or temperature dependent assignment. + + + All the temperature dependent or thermal dictionaries should contain three keys: + ``"Type"``, ``"Function"``, and ``"Values"``. Accepted ``"Type"`` values are: + ``"Temp Dep"`` and ``"Transient"``. Accepted ``"Function"`` are: ``"Linear"``, + ``"Power Law"``, ``"Exponential"``, ``"Sinusoidal"``, ``"Square Wave"``, and + ``"Piecewise Linear"``. ``"Temp Dep"`` only support the latter. ``"Values"`` + contains a list of strings containing the parameters required by the ``"Function"`` + selection (e.g. ``"Linear"`` requires two parameters: the value of the variable at t=0 + and the slope of the line). The parameters required by each ``Function`` option is in + Icepak documentation. The parameters must contain the units where needed. + + Returns + ------- + bool + ``True`` if successful. ``False`` otherwise. + + Examples + -------- + >>> import ansys.aedt.core + >>> app = ansys.aedt.core.Icepak() + >>> network = ansys.aedt.core.modules.boundary.Network(app) + >>> box = app.modeler.create_box([5, 5, 5],[20, 50, 80]) + >>> faces_ids = [face.id for face in box.faces] + >>> nodes_dict = [ + >>> {"FaceID": faces_ids[0]}, + >>> {"Name": "TestNode", "FaceID": faces_ids[1], + >>> "ThermalResistance": "Compute", "Thickness": "2mm"}, + >>> {"FaceID": faces_ids[2], "ThermalResistance": "Specified", "Resistance": "2cel_per_w"}, + >>> {"Name": "Junction", "Power": "1W"}] + >>> network.add_nodes_from_dictionaries(nodes_dict) + """ + if isinstance(nodes, dict): + nodes = [nodes] + for node_dict in nodes: + if "FaceID" in node_dict.keys(): + self.add_face_node( + assignment=node_dict["FaceID"], + name=node_dict.get("Name", None), + thermal_resistance=node_dict.get("ThermalResistance", None), + material=node_dict.get("Material", None), + thickness=node_dict.get("Thickness", None), + resistance=node_dict.get("Resistance", None), + ) + elif "ValueType" in node_dict.keys(): + if node_dict["ValueType"].endswith("Value"): + value = node_dict[node_dict["ValueType"].removesuffix("Value")] + else: + value = node_dict[node_dict["ValueType"]] + self.add_boundary_node(name=node_dict["Name"], assignment_type=node_dict["ValueType"], value=value) + else: + self.add_internal_node( + name=node_dict["Name"], + power=node_dict.get("Power", None), + mass=node_dict.get("Mass", None), + specific_heat=node_dict.get("SpecificHeat", None), + ) + return True + + @pyaedt_function_handler() + def add_link(self, node1, node2, value, name=None): + """Create links in the network object. + + Parameters + ---------- + node1 : str or int + String containing one of the node names that the link is connecting or an integer + containing the ID of the face. If an ID is used and the node associated with the + corresponding face is not created yet, it is added automatically. + node2 : str or int + String containing one of the node names that the link is connecting or an integer + containing the ID of the face. If an ID is used and the node associated with the + corresponding face is not created yet, it is added atuomatically. + value : str or float + String containing the value and unit of the connection. If a float is passed, an + R-Link is added to the network and the ``"cel_per_w"`` unit is used. + name : str, optional + Name of the link. The default is ``None``, in which case a name is + automatically generated. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + Examples + -------- + >>> import ansys.aedt.core + >>> app = ansys.aedt.core.Icepak() + >>> network = ansys.aedt.core.modules.boundary.Network(app) + >>> box = app.modeler.create_box([5, 5, 5],[20, 50, 80]) + >>> faces_ids = [face.id for face in box.faces] + >>> connection = {"Name": "LinkTest", "Connection": [faces_ids[1], faces_ids[0]], "Value": "1cel_per_w"} + >>> network.add_links_from_dictionaries(connection) + """ + if name is None: + new_name = True + while new_name: + name = generate_unique_name("Link") + if name not in self.links.keys(): + new_name = False + new_link = self._Link(node1, node2, value, name, self) + self._links.append(new_link) + self._add_to_props(new_link, "Links") + return True + + @pyaedt_function_handler() + def add_links_from_dictionaries(self, connections): + """Create links in the network object. + + Parameters + ---------- + connections : dict or list of dict + Dictionary or list of dictionaries containing the links between nodes. Each dictionary + consists of these elements: + + - ``"Link"``: a three-item list consisting of the two nodes that the link is connecting and + the value with unit of the link. The node of the connection can be referred to with the + name (str) or face ID (int). The link type (resistance, heat transfer coefficient, or + mass flow) is determined automatically from the unit. + - ``"Name"`` (optional): a string specifying the name of the link. + + + Returns + ------- + bool + ``True`` if successful. + + Examples + -------- + >>> import ansys.aedt.core + >>> app = ansys.aedt.core.Icepak() + >>> network = ansys.aedt.core.modules.boundary.Network(app) + >>> box = app.modeler.create_box([5, 5, 5],[20, 50, 80]) + >>> faces_ids = [face.id for face in box.faces] + >>> [network.add_face_node(faces_ids[i]) for i in range(2)] + >>> connection = {"Name": "LinkTest", "Link": [faces_ids[1], faces_ids[0], "1cel_per_w"]} + >>> network.add_links_from_dictionaries(connection) + """ + if isinstance(connections, dict): + connections = [connections] + for connection in connections: + name = connection.get("Name", None) + try: + self.add_link(connection["Link"][0], connection["Link"][1], connection["Link"][2], name) + except Exception: # pragma : no cover + if name: + self._app.logger.error("Failed to add " + name + " link.") + else: + self._app.logger.error( + "Failed to add link associated with the following dictionary:\n" + str(connection) + ) + return True + + @pyaedt_function_handler() + def update(self): + """Update the network in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + if self.name in [b.name for b in self._app.boundaries]: + self.delete() + try: + self.create() + self._app._boundaries[self.name] = self + return True + except Exception: # pragma : no cover + self._app.odesign.Undo() + self._app.logger.error("Update of network object failed.") + return False + else: # pragma : no cover + self._app.logger.warning("Network object not yet created in design.") + return False + + @pyaedt_function_handler() + def update_assignment(self): + """Update assignments of the network.""" + return self.update() + + class _Link: + def __init__(self, node_1, node_2, value, name, network): + self.name = name + if not isinstance(node_1, str): + node_1 = "FaceID" + str(node_1) + if not isinstance(node_2, str): + node_2 = "FaceID" + str(node_2) + if not isinstance(value, str): + value = str(value) + "cel_per_w" + self.node_1 = node_1 + self.node_2 = node_2 + self.value = value + self._network = network + + @property + def _link_type(self): + unit2type_conversion = { + "g_per_s": ["C-Link", "Node1ToNode2"], + "kg_per_s": ["C-Link", "Node1ToNode2"], + "lbm_per_min": ["C-Link", "Node1ToNode2"], + "lbm_per_s": ["C-Link", "Node1ToNode2"], + "Kel_per_W": ["R-Link", "R"], + "cel_per_w": ["R-Link", "R"], + "FahSec_per_btu": ["R-Link", "R"], + "Kels_per_J": ["R-Link", "R"], + "w_per_m2kel": ["R-Link", "HTC"], + "w_per_m2Cel": ["R-Link", "HTC"], + "btu_per_rankHrFt2": ["R-Link", "HTC"], + "btu_per_fahHrFt2": ["R-Link", "HTC"], + "btu_per_rankSecFt2": ["R-Link", "HTC"], + "btu_per_fahSecFt2": ["R-Link", "HTC"], + "w_per_cm2kel": ["R-Link", "HTC"], + } + _, unit = decompose_variable_value(self.value) + return unit2type_conversion[unit] + + @property + def props(self): + """ + Get link properties. + + Returns + ------- + list + First two elements of the list are the node names that the link connects, + the third element is the link type while the fourth contains the value + associated with the link. + """ + return [self.node_1, self.node_2] + self._link_type + [self.value] + + @pyaedt_function_handler() + def delete_link(self): + """ + Delete link from network. + """ + self._network.props["Links"].pop(self.name) + self._network._links.remove(self) + + class _Node: + def __init__(self, name, app, network, node_type=None, props=None): + self.name = name + self._type = node_type + self._app = app + self._props = props + self._node_props() + self._network = network + + @pyaedt_function_handler() + def delete_node(self): + """Delete node from network.""" + self._network.props["Nodes"].pop(self.name) + self._network._nodes.remove(self) + + @property + def node_type(self): + """Get node type. + + Returns + ------- + str + Node type. + """ + if self._type is None: # pragma: no cover + if self.props is None: + self._app.logger.error( + "Cannot define node_type. Both its assignment and properties assignment are missing." + ) + return None + else: + type_in_dict = self.props.get("NodeType", None) + if type_in_dict is None: + self._type = "FaceNode" + else: + self._type = type_in_dict + return self._type + + @property + def props(self): + """Get properties of the node. + + Returns + ------- + dict + Node properties. + """ + return self._props + + @props.setter + def props(self, props): + """Set properties of the node. + + Parameters + ---------- + props : dict + Node properties. + """ + self._props = props + self._node_props() + + def _node_props(self): + face_node_default_dict = { + "FaceID": None, + "ThermalResistance": "NoResistance", + "Thickness": "1mm", + "Material": "Al-Extruded", + "Resistance": "0cel_per_w", + } + boundary_node_default_dict = { + "NodeType": "BoundaryNode", + "ValueType": "PowerValue", + "Power": "0W", + "Temperature": "25cel", + } + internal_node_default_dict = { + "NodeType": "InternalNode", + "Power": "0W", + "Mass": "0.001kg", + "SpecificHeat": "1000J_per_Kelkg", + } + if self.props is None: + if self.node_type == "InternalNode": + self._props = internal_node_default_dict + elif self.node_type == "FaceNode": + self._props = face_node_default_dict + elif self.node_type == "BoundaryNode": + self._props = boundary_node_default_dict + else: + if self.node_type == "InternalNode": + self._props = self._create_node_dict(internal_node_default_dict) + elif self.node_type == "FaceNode": + self._props = self._create_node_dict(face_node_default_dict) + elif self.node_type == "BoundaryNode": + self._props = self._create_node_dict(boundary_node_default_dict) + + @pyaedt_function_handler() + def _create_node_dict(self, default_dict): + node_dict = self.props + node_name = node_dict.get("Name", self.name) + if not node_name: + try: + self.name = "Face" + str(node_dict["FaceID"]) + except KeyError: # pragma: no cover + raise KeyError('"Name" key is needed for "BoundaryNodes" and "InternalNodes" dictionaries.') + else: + self.name = node_name + node_dict.pop("Name", None) + node_args = copy.deepcopy(default_dict) + for k in node_dict.keys(): + val = node_dict[k] + if isinstance(val, dict): # pragma : no cover + val = self._app._parse_variation_data( + k, val["Type"], variation_value=val["Values"], function=val["Function"] + ) + node_args.pop(k) + node_args.update(val) + else: + node_args[k] = val + + return node_args diff --git a/src/ansys/aedt/core/modules/boundary/common.py b/src/ansys/aedt/core/modules/boundary/common.py new file mode 100644 index 00000000000..9bbbc2147ad --- /dev/null +++ b/src/ansys/aedt/core/modules/boundary/common.py @@ -0,0 +1,715 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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. + +""" +This module contains these classes: ``BoundaryCommon`` and ``BoundaryObject``. +""" + +from ansys.aedt.core.generic.data_handlers import _dict2arg +from ansys.aedt.core.generic.general_methods import PropsManager +from ansys.aedt.core.generic.general_methods import pyaedt_function_handler +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode +from ansys.aedt.core.modeler.cad.elements_3d import EdgePrimitive +from ansys.aedt.core.modeler.cad.elements_3d import FacePrimitive +from ansys.aedt.core.modeler.cad.elements_3d import VertexPrimitive + + +class BoundaryProps(dict): + """AEDT Boundary Component Internal Parameters.""" + + def __setitem__(self, key, value): + dict.__setitem__(self, key, value) + if self._pyaedt_boundary.auto_update: + if key in ["Edges", "Faces", "Objects"]: + res = self._pyaedt_boundary.update_assignment() + else: + res = self._pyaedt_boundary.update() + if not res: + self._pyaedt_boundary._app.logger.warning("Update of %s Failed. Check needed arguments", key) + + def __init__(self, boundary, props): + dict.__init__(self) + if props: + for key, value in props.items(): + if isinstance(value, dict): + dict.__setitem__(self, key, BoundaryProps(boundary, value)) + elif isinstance(value, list): + list_els = [] + for el in value: + if isinstance(el, dict): + list_els.append(BoundaryProps(boundary, el)) + else: + list_els.append(el) + dict.__setitem__(self, key, list_els) + else: + dict.__setitem__(self, key, value) + self._pyaedt_boundary = boundary + + def _setitem_without_update(self, key, value): + dict.__setitem__(self, key, value) + + +class BoundaryCommon(PropsManager): + """ """ + + @pyaedt_function_handler() + def _get_args(self, props=None): + """Retrieve boundary properties. + + Parameters + ---------- + props : dict, optional + The default is ``None``. + + Returns + ------- + dict + Dictionary of boundary properties. + + """ + if not props: + props = self.props + arg = ["NAME:" + self.name] + _dict2arg(props, arg) + return arg + + @pyaedt_function_handler() + def _initialize_bynary_tree(self): + if self._child_object: + BinaryTreeNode.__init__(self, self._name, self._child_object, False) + + @pyaedt_function_handler() + def delete(self): + """Delete the boundary. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + if self.type == "Matrix" or self.type == "Force" or self.type == "Torque": + self._app.o_maxwell_parameters.DeleteParameters([self.name]) + else: + self._app.oboundary.DeleteBoundaries([self.name]) + if self.name in self._app.excitation_objects.keys(): + self._app.excitation_objects.pop(self.name) + return True + + def _get_boundary_data(self, ds): + try: + if "MaxwellParameterSetup" in self._app.design_properties: + param = "MaxwellParameters" + setup = "MaxwellParameterSetup" + if isinstance(self._app.design_properties[setup][param][ds], dict): + return [ + self._app.design_properties["MaxwellParameterSetup"]["MaxwellParameters"][ds], + self._app.design_properties["MaxwellParameterSetup"]["MaxwellParameters"][ds][ + "MaxwellParameterType" + ], + ] + except Exception: + self._app.logger.debug( + "An error occurred while getting boundary data for MaxwellParameterSetup." + ) # pragma: no cover + try: + if ( + "ModelSetup" in self._app.design_properties + and "MotionSetupList" in self._app.design_properties["ModelSetup"] + ): + motion_list = "MotionSetupList" + setup = "ModelSetup" + # check moving part + if isinstance(self._app.design_properties[setup][motion_list][ds], dict): + return [ + self._app.design_properties["ModelSetup"]["MotionSetupList"][ds], + self._app.design_properties["ModelSetup"]["MotionSetupList"][ds]["MotionType"], + ] + except Exception: + self._app.logger.debug("An error occurred while getting boundary data for ModelSetup.") # pragma: no cover + try: + if ds in self._app.design_properties["BoundarySetup"]["Boundaries"]: + if ( + self._app.design_properties["BoundarySetup"]["Boundaries"][ds]["BoundType"] == "Network" + and self._app.design_type == "Icepak" + ): + return [self._app.design_properties["BoundarySetup"]["Boundaries"][ds], ""] + else: + return [ + self._app.design_properties["BoundarySetup"]["Boundaries"][ds], + self._app.design_properties["BoundarySetup"]["Boundaries"][ds]["BoundType"], + ] + except Exception: + self._app.logger.debug( + "An error occurred while getting boundary data for BoundarySetup." + ) # pragma: no cover + return [] + + +def disable_auto_update(func): + """Decorator used to disable automatic update.""" + + def wrapper(self, *args, **kwargs): + """Inner wrapper function.""" + obj = self + if not hasattr(self, "auto_update"): + obj = self.pcb + auto_update = obj.auto_update + obj.auto_update = False + out = func(self, *args, **kwargs) + if auto_update: + obj.update() + obj.auto_update = auto_update + return out + + return wrapper + + +class BoundaryObject(BoundaryCommon, BinaryTreeNode): + """Manages boundary data and execution. + + Parameters + ---------- + app : object + An AEDT application from ``ansys.aedt.core.application``. + name : str + Name of the boundary. + props : dict, optional + Properties of the boundary. + boundarytype : str, optional + Type of the boundary. + + Examples + -------- + + Create a cylinder at the XY working plane and assign a copper coating of 0.2 mm to it. The Coating is a boundary + operation and coat will return a ``ansys.aedt.core.modules.boundary.BoundaryObject`` + + >>> from ansys.aedt.core import Hfss + >>> hfss =Hfss() + >>> origin = hfss.modeler.Position(0, 0, 0) + >>> inner = hfss.modeler.create_cylinder(hfss.PLANE.XY,origin,3,200,0,"inner") + >>> inner_id = hfss.modeler.get_obj_id("inner",) + >>> coat = hfss.assign_coating([inner_id],"copper",use_thickness=True,thickness="0.2mm") + """ + + def __init__(self, app, name, props=None, boundarytype=None, auto_update=True): + self.auto_update = False + self._app = app + self._name = name + self.__props = None + self.__props = BoundaryProps(self, props) if props else {} + self._type = boundarytype + self._boundary_name = self.name + self.auto_update = auto_update + self._initialize_bynary_tree() + + @property + def _child_object(self): + """Object-oriented properties. + + Returns + ------- + class:`ansys.aedt.core.modeler.cad.elements_3d.BinaryTreeNode` + + """ + child_object = None + design_childs = self._app.get_oo_name(self._app.odesign) + + if "Thermal" in design_childs: + cc = self._app.get_oo_object(self._app.odesign, "Thermal") + cc_names = self._app.get_oo_name(cc) + if self.name in cc_names: + child_object = cc.GetChildObject(self.name) + elif "Boundaries" in design_childs: + cc = self._app.get_oo_object(self._app.odesign, "Boundaries") + if self.name in cc.GetChildNames(): + child_object = cc.GetChildObject(self.name) + elif "Excitations" in design_childs and self.name in self._app.get_oo_name( + self._app.odesign, "Excitations" + ): + child_object = self._app.get_oo_object(self._app.odesign, "Excitations").GetChildObject(self.name) + elif self._app.design_type in ["Maxwell 3D", "Maxwell 2D"] and "Model" in design_childs: + model = self._app.get_oo_object(self._app.odesign, "Model") + if self.name in model.GetChildNames(): + child_object = model.GetChildObject(self.name) + elif "Excitations" in design_childs and self._app.get_oo_name(self._app.odesign, "Excitations"): + for port in self._app.get_oo_name(self._app.odesign, "Excitations"): + terminals = self._app.get_oo_name(self._app.odesign, f"Excitations\\{port}") + if self.name in terminals: + child_object = self._app.get_oo_object(self._app.odesign, f"Excitations\\{port}\\{self.name}") + elif "Conductors" in design_childs and self._app.get_oo_name(self._app.odesign, "Conductors"): + for port in self._app.get_oo_name(self._app.odesign, "Conductors"): + if self.name == port: + child_object = self._app.get_oo_object(self._app.odesign, f"Conductors\\{port}") + return child_object + + @property + def props(self): + """Boundary data. + + Returns + ------- + :class:BoundaryProps + """ + if self.__props: + return self.__props + props = self._get_boundary_data(self.name) + + if props: + self.__props = BoundaryProps(self, props[0]) + self._type = props[1] + return self.__props + + @property + def type(self): + """Boundary type. + + Returns + ------- + str + Returns the type of the boundary. + """ + if not self._type: + if self.available_properties: + if "Type" in self.available_properties: + self._type = self.props["Type"] + elif "BoundType" in self.available_properties: + self._type = self.props["BoundType"] + elif self.properties and self.properties["Type"]: + self._type = self.properties["Type"] + + if self._app.design_type == "Icepak" and self._type == "Source": + return "SourceIcepak" + else: + return self._type + + @type.setter + def type(self, value): + self._type = value + + @property + def name(self): + """Boundary Name.""" + return self._name + + @name.setter + def name(self, value): + self._name = value + self.update() + + @pyaedt_function_handler() + def _get_args(self, props=None): + """Retrieve arguments. + + Parameters + ---------- + props : + The default is ``None``. + + Returns + ------- + list + List of boundary properties. + + """ + if props is None: + props = self.props + arg = ["NAME:" + self.name] + _dict2arg(props, arg) + return arg + + @pyaedt_function_handler() + def create(self): + """Create a boundary. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + bound_type = self.type + if bound_type == "Perfect E": + self._app.oboundary.AssignPerfectE(self._get_args()) + elif bound_type == "Perfect H": + self._app.oboundary.AssignPerfectH(self._get_args()) + elif bound_type == "Aperture": + self._app.oboundary.AssignAperture(self._get_args()) + elif bound_type == "Radiation": + self._app.oboundary.AssignRadiation(self._get_args()) + elif bound_type == "FE-BI": + self._app.oboundary.AssignFEBI(self._get_args()) + elif bound_type == "Finite Conductivity": + self._app.oboundary.AssignFiniteCond(self._get_args()) + elif bound_type == "Lumped RLC": + self._app.oboundary.AssignLumpedRLC(self._get_args()) + elif bound_type == "Impedance": + self._app.oboundary.AssignImpedance(self._get_args()) + elif bound_type == "Layered Impedance": + self._app.oboundary.AssignLayeredImp(self._get_args()) + elif bound_type == "Anisotropic Impedance": + self._app.oboundary.AssignAnisotropicImpedance(self._get_args()) + elif bound_type == "Primary": + self._app.oboundary.AssignPrimary(self._get_args()) + elif bound_type == "Secondary": + self._app.oboundary.AssignSecondary(self._get_args()) + elif bound_type == "Lattice Pair": + self._app.oboundary.AssignLatticePair(self._get_args()) + elif bound_type == "HalfSpace": + self._app.oboundary.AssignHalfSpace(self._get_args()) + elif bound_type == "Multipaction SEE": + self._app.oboundary.AssignMultipactionSEE(self._get_args()) + elif bound_type == "Fresnel": + self._app.oboundary.AssignFresnel(self._get_args()) + elif bound_type == "Symmetry": + self._app.oboundary.AssignSymmetry(self._get_args()) + elif bound_type == "Zero Tangential H Field": + self._app.oboundary.AssignZeroTangentialHField(self._get_args()) + elif bound_type == "Zero Integrated Tangential H Field": + self._app.oboundary.AssignIntegratedZeroTangentialHField(self._get_args()) + elif bound_type == "Tangential H Field": + self._app.oboundary.AssignTangentialHField(self._get_args()) + elif bound_type == "Insulating": + self._app.oboundary.AssignInsulating(self._get_args()) + elif bound_type == "Independent": + self._app.oboundary.AssignIndependent(self._get_args()) + elif bound_type == "Dependent": + self._app.oboundary.AssignDependent(self._get_args()) + elif bound_type == "Band": + self._app.omodelsetup.AssignBand(self._get_args()) + elif bound_type == "InfiniteGround": + self._app.oboundary.AssignInfiniteGround(self._get_args()) + elif bound_type == "ThinConductor": + self._app.oboundary.AssignThinConductor(self._get_args()) + elif bound_type == "Stationary Wall": + self._app.oboundary.AssignStationaryWallBoundary(self._get_args()) + elif bound_type == "Symmetry Wall": + self._app.oboundary.AssignSymmetryWallBoundary(self._get_args()) + elif bound_type == "Recirculating": + self._app.oboundary.AssignRecircBoundary(self._get_args()) + elif bound_type == "Resistance": + self._app.oboundary.AssignResistanceBoundary(self._get_args()) + elif bound_type == "Conducting Plate": + self._app.oboundary.AssignConductingPlateBoundary(self._get_args()) + elif bound_type == "Adiabatic Plate": + self._app.oboundary.AssignAdiabaticPlateBoundary(self._get_args()) + elif bound_type == "Network": + self._app.oboundary.AssignNetworkBoundary(self._get_args()) + elif bound_type == "Grille": + self._app.oboundary.AssignGrilleBoundary(self._get_args()) + elif bound_type == "Block": + self._app.oboundary.AssignBlockBoundary(self._get_args()) + elif bound_type == "Blower": + self._app.oboundary.AssignBlowerBoundary(self._get_args()) + elif bound_type == "SourceIcepak": + self._app.oboundary.AssignSourceBoundary(self._get_args()) + elif bound_type == "Opening": + self._app.oboundary.AssignOpeningBoundary(self._get_args()) + elif bound_type == "EMLoss": + self._app.oboundary.AssignEMLoss(self._get_args()) + elif bound_type == "ThermalCondition": + self._app.oboundary.AssignThermalCondition(self._get_args()) + elif bound_type == "Convection": + self._app.oboundary.AssignConvection(self._get_args()) + elif bound_type == "HeatFlux": + self._app.oboundary.AssignHeatFlux(self._get_args()) + elif bound_type == "HeatGeneration": + self._app.oboundary.AssignHeatGeneration(self._get_args()) + elif bound_type == "Temperature": + self._app.oboundary.AssignTemperature(self._get_args()) + elif bound_type == "RotatingFluid": + self._app.oboundary.AssignRotatingFluid(self._get_args()) + elif bound_type == "Frictionless": + self._app.oboundary.AssignFrictionlessSupport(self._get_args()) + elif bound_type == "FixedSupport": + self._app.oboundary.AssignFixedSupport(self._get_args()) + elif bound_type == "Voltage": + self._app.oboundary.AssignVoltage(self._get_args()) + elif bound_type == "VoltageDrop": + self._app.oboundary.AssignVoltageDrop(self._get_args()) + elif bound_type == "Floating": + self._app.oboundary.AssignFloating(self._get_args()) + elif bound_type == "Current": + self._app.oboundary.AssignCurrent(self._get_args()) + elif bound_type == "CurrentDensity": + self._app.oboundary.AssignCurrentDensity(self._get_args()) + elif bound_type == "CurrentDensityGroup": + self._app.oboundary.AssignCurrentDensityGroup(self._get_args()[2], self._get_args()[3]) + elif bound_type == "CurrentDensityTerminal": + self._app.oboundary.AssignCurrentDensityTerminal(self._get_args()) + elif bound_type == "CurrentDensityTerminalGroup": + self._app.oboundary.AssignCurrentDensityTerminalGroup(self._get_args()[2], self._get_args()[3]) + elif bound_type == "Balloon": + self._app.oboundary.AssignBalloon(self._get_args()) + elif bound_type == "Winding" or bound_type == "Winding Group": + self._app.oboundary.AssignWindingGroup(self._get_args()) + elif bound_type == "Vector Potential": + self._app.oboundary.AssignVectorPotential(self._get_args()) + elif bound_type == "CoilTerminal" or bound_type == "Coil Terminal": + self._app.oboundary.AssignCoilTerminal(self._get_args()) + elif bound_type == "Coil": + self._app.oboundary.AssignCoil(self._get_args()) + elif bound_type == "Source": + self._app.oboundary.AssignSource(self._get_args()) + elif bound_type == "Sink": + self._app.oboundary.AssignSink(self._get_args()) + elif bound_type == "SignalNet": + self._app.oboundary.AssignSignalNet(self._get_args()) + elif bound_type == "GroundNet": + self._app.oboundary.AssignGroundNet(self._get_args()) + elif bound_type == "FloatingNet": + self._app.oboundary.AssignFloatingNet(self._get_args()) + elif bound_type == "SignalLine": + self._app.oboundary.AssignSingleSignalLine(self._get_args()) + elif bound_type == "ReferenceGround": + self._app.oboundary.AssignSingleReferenceGround(self._get_args()) + elif bound_type == "Circuit Port": + self._app.oboundary.AssignCircuitPort(self._get_args()) + elif bound_type == "Lumped Port": + self._app.oboundary.AssignLumpedPort(self._get_args()) + elif bound_type == "Wave Port": + self._app.oboundary.AssignWavePort(self._get_args()) + elif bound_type == "Floquet Port": + self._app.oboundary.AssignFloquetPort(self._get_args()) + elif bound_type == "AutoIdentify": + # Build reference conductor argument as a list of strings + # ref_cond_arg should be a list. + ref_cond_arg = ["NAME:ReferenceConductors"] + self.props["ReferenceConductors"] + self._app.oboundary.AutoIdentifyPorts( + ["NAME:Faces", self.props["Faces"]], + self.props["IsWavePort"], + ref_cond_arg, + self.name, + self.props["RenormalizeModes"], + ) + elif bound_type == "SBRTxRxSettings": + self._app.oboundary.SetSBRTxRxSettings(self._get_args()) + elif bound_type == "EndConnection": + self._app.oboundary.AssignEndConnection(self._get_args()) + elif bound_type == "Hybrid": + self._app.oboundary.AssignHybridRegion(self._get_args()) + elif bound_type == "FluxTangential": + self._app.oboundary.AssignFluxTangential(self._get_args()) + elif bound_type == "Plane Incident Wave": + self._app.oboundary.AssignPlaneWave(self._get_args()) + elif bound_type == "ResistiveSheet": + self._app.oboundary.AssignResistiveSheet(self._get_args()) + else: + return False + self._initialize_bynary_tree() + + return True + + @pyaedt_function_handler() + def update(self): + """Update the boundary. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + bound_type = self.type + if bound_type == "Perfect E": + self._app.oboundary.EditPerfectE(self._boundary_name, self._get_args()) + elif bound_type == "Perfect H": + self._app.oboundary.EditPerfectH(self._boundary_name, self._get_args()) + elif bound_type == "Aperture": + self._app.oboundary.EditAperture(self._boundary_name, self._get_args()) + elif bound_type == "Radiation": + self._app.oboundary.EditRadiation(self._boundary_name, self._get_args()) + elif bound_type == "Finite Conductivity": + self._app.oboundary.EditFiniteCond(self._boundary_name, self._get_args()) + elif bound_type == "Lumped RLC": + self._app.oboundary.EditLumpedRLC(self._boundary_name, self._get_args()) + elif bound_type == "Impedance": + self._app.oboundary.EditImpedance(self._boundary_name, self._get_args()) + elif bound_type == "Layered Impedance": + self._app.oboundary.EditLayeredImpedance(self._boundary_name, self._get_args()) + elif bound_type == "Anisotropic Impedance": + self._app.oboundary.EditAssignAnisotropicImpedance( + self._boundary_name, self._get_args() + ) # pragma: no cover + elif bound_type == "Primary": + self._app.oboundary.EditPrimary(self._boundary_name, self._get_args()) + elif bound_type == "Secondary": + self._app.oboundary.EditSecondary(self._boundary_name, self._get_args()) + elif bound_type == "Lattice Pair": + self._app.oboundary.EditLatticePair(self._boundary_name, self._get_args()) + elif bound_type == "HalfSpace": + self._app.oboundary.EditHalfSpace(self._boundary_name, self._get_args()) + elif bound_type == "Multipaction SEE": + self._app.oboundary.EditMultipactionSEE(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Fresnel": + self._app.oboundary.EditFresnel(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Symmetry": + self._app.oboundary.EditSymmetry(self._boundary_name, self._get_args()) + elif bound_type == "Zero Tangential H Field": + self._app.oboundary.EditZeroTangentialHField(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Zero Integrated Tangential H Field": + self._app.oboundary.EditIntegratedZeroTangentialHField( + self._boundary_name, self._get_args() + ) # pragma: no cover + elif bound_type == "Tangential H Field": + self._app.oboundary.EditTangentialHField(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Insulating": + self._app.oboundary.EditInsulating(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Independent": + self._app.oboundary.EditIndependent(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Dependent": + self._app.oboundary.EditDependent(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Band": + self._app.omodelsetup.EditMotionSetup(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "InfiniteGround": + self._app.oboundary.EditInfiniteGround(self._boundary_name, self._get_args()) + elif bound_type == "ThinConductor": + self._app.oboundary.EditThinConductor(self._boundary_name, self._get_args()) + elif bound_type == "Stationary Wall": + self._app.oboundary.EditStationaryWallBoundary(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Symmetry Wall": + self._app.oboundary.EditSymmetryWallBoundary(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Recirculating": + self._app.oboundary.EditRecircBoundary(self._boundary_name, self._get_args()) + elif bound_type == "Resistance": + self._app.oboundary.EditResistanceBoundary(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Conducting Plate": + self._app.oboundary.EditConductingPlateBoundary(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Adiabatic Plate": + self._app.oboundary.EditAdiabaticPlateBoundary(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Network": + self._app.oboundary.EditNetworkBoundary(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Grille": + self._app.oboundary.EditGrilleBoundary(self._boundary_name, self._get_args()) + elif bound_type == "Opening": + self._app.oboundary.EditOpeningBoundary(self._boundary_name, self._get_args()) + elif bound_type == "EMLoss": + self._app.oboundary.EditEMLoss(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Block": + self._app.oboundary.EditBlockBoundary(self._boundary_name, self._get_args()) + elif bound_type == "Blower": + self._app.oboundary.EditBlowerBoundary(self._boundary_name, self._get_args()) + elif bound_type == "SourceIcepak": + self._app.oboundary.EditSourceBoundary(self._boundary_name, self._get_args()) + elif bound_type == "HeatFlux": + self._app.oboundary.EditHeatFlux(self._boundary_name, self._get_args()) + elif bound_type == "HeatGeneration": + self._app.oboundary.EditHeatGeneration(self._boundary_name, self._get_args()) + elif bound_type == "Voltage": + self._app.oboundary.EditVoltage(self._boundary_name, self._get_args()) + elif bound_type == "VoltageDrop": + self._app.oboundary.EditVoltageDrop(self._boundary_name, self._get_args()) + elif bound_type == "Current": + self._app.oboundary.EditCurrent(self._boundary_name, self._get_args()) + elif bound_type == "CurrentDensity": + self._app.oboundary.AssignCurrentDensity(self._get_args()) + elif bound_type == "CurrentDensityGroup": + self._app.oboundary.AssignCurrentDensityGroup(self._get_args()[2], self._get_args()[3]) + elif bound_type == "CurrentDensityTerminal": + self._app.oboundary.AssignCurrentDensityTerminal(self._get_args()) + elif bound_type == "CurrentDensityTerminalGroup": + self._app.oboundary.AssignCurrentDensityTerminalGroup(self._get_args()[2], self._get_args()[3]) + elif bound_type == "Winding" or bound_type == "Winding Group": + self._app.oboundary.EditWindingGroup(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Vector Potential": + self._app.oboundary.EditVectorPotential(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "CoilTerminal" or bound_type == "Coil Terminal": + self._app.oboundary.EditCoilTerminal(self._boundary_name, self._get_args()) + elif bound_type == "Coil": + self._app.oboundary.EditCoil(self._boundary_name, self._get_args()) + elif bound_type == "Source": + self._app.oboundary.EditTerminal(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "Sink": + self._app.oboundary.EditTerminal(self._boundary_name, self._get_args()) + elif bound_type == "SignalNet" or bound_type == "GroundNet" or bound_type == "FloatingNet": + self._app.oboundary.EditTerminal(self._boundary_name, self._get_args()) + elif bound_type in "Circuit Port": + self._app.oboundary.EditCircuitPort(self._boundary_name, self._get_args()) + elif bound_type in "Lumped Port": + self._app.oboundary.EditLumpedPort(self._boundary_name, self._get_args()) + elif bound_type in "Wave Port": + self._app.oboundary.EditWavePort(self._boundary_name, self._get_args()) + elif bound_type == "SetSBRTxRxSettings": + self._app.oboundary.SetSBRTxRxSettings(self._get_args()) # pragma: no cover + elif bound_type == "Floquet Port": + self._app.oboundary.EditFloquetPort(self._boundary_name, self._get_args()) # pragma: no cover + elif bound_type == "End Connection": + self._app.oboundary.EditEndConnection(self._boundary_name, self._get_args()) + elif bound_type == "Hybrid": + self._app.oboundary.EditHybridRegion(self._boundary_name, self._get_args()) + elif bound_type == "Terminal": + self._app.oboundary.EditTerminal(self._boundary_name, self._get_args()) + elif bound_type == "Plane Incident Wave": + self._app.oboundary.EditIncidentWave(self._boundary_name, self._get_args()) + elif bound_type == "ResistiveSheet": + self._app.oboundary.EditResistiveSheet(self._boundary_name, self._get_args()) + else: + return False # pragma: no cover + + self._app._boundaries[self.name] = self._app._boundaries.pop(self._boundary_name) + self._boundary_name = self.name + + return True + + @pyaedt_function_handler() + def update_assignment(self): + """Update the boundary assignment. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + + out = ["Name:" + self.name] + + if "Faces" in self.props: + faces = self.props["Faces"] + faces_out = [] + if not isinstance(faces, list): + faces = [faces] + for f in faces: + if isinstance(f, (EdgePrimitive, FacePrimitive, VertexPrimitive)): + faces_out.append(f.id) + else: + faces_out.append(f) + out += ["Faces:=", faces_out] + + if "Objects" in self.props: + pr = [] + for el in self.props["Objects"]: + try: + pr.append(self._app.modeler[el].name) + except (KeyError, AttributeError): + pass + out += ["Objects:=", pr] + + if len(out) == 1: + return False + + self._app.oboundary.ReassignBoundary(out) + + return True diff --git a/src/ansys/aedt/core/modules/boundary/hfss_boundary.py b/src/ansys/aedt/core/modules/boundary/hfss_boundary.py new file mode 100644 index 00000000000..c1323e978d2 --- /dev/null +++ b/src/ansys/aedt/core/modules/boundary/hfss_boundary.py @@ -0,0 +1,521 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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 ansys.aedt.core.generic.data_handlers import _dict2arg +from ansys.aedt.core.generic.general_methods import _dim_arg +from ansys.aedt.core.generic.general_methods import pyaedt_function_handler +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode +from ansys.aedt.core.modules.boundary.common import BoundaryCommon +from ansys.aedt.core.modules.boundary.common import BoundaryProps + + +class FieldSetup(BoundaryCommon, BinaryTreeNode): + """Manages far field and near field component data and execution. + + Examples + -------- + In this example the sphere1 returned object is a ``ansys.aedt.core.modules.boundary.FarFieldSetup`` + >>> from ansys.aedt.core import Hfss + >>> hfss = Hfss() + >>> sphere1 = hfss.insert_infinite_sphere() + >>> sphere1.props["ThetaStart"] = "-90deg" + >>> sphere1.props["ThetaStop"] = "90deg" + >>> sphere1.props["ThetaStep"] = "2deg" + >>> sphere1.delete() + """ + + def __init__(self, app, component_name, props, component_type): + self.auto_update = False + self._app = app + self.type = component_type + self._name = component_name + self.__props = BoundaryProps(self, props) if props else {} + self.auto_update = True + child_object = self._app.get_oo_object(self._app.odesign, f"Radiation/{self._name}") + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) + + @property + def props(self): + if not self.__props and self._app.design_properties: + if ( + self.type == "FarFieldSphere" + and self._app.design_properties.get("RadField") + and self._app.design_properties["RadField"].get("FarFieldSetups") + ): + for val in self._app.design_properties["RadField"]["FarFieldSetups"]: + if val == self.name: + self.__props = self._app.design_properties["RadField"]["FarFieldSetups"][val] + elif self.type != "FarFieldSphere" and self._app.design_properties["RadField"].get("NearFieldSetups"): + for val in self._app.design_properties["RadField"]["NearFieldSetups"]: + if val == self.name: + self.__props = self._app.design_properties["RadField"]["NearFieldSetups"][val] + self.__props = BoundaryProps(self, self.__props) + return self.__props + + @property + def name(self): + """Variable name.""" + return self._name + + @name.setter + def name(self, value): + self._app.oradfield.RenameSetup(self._name, value) + self._name = value + + @pyaedt_function_handler() + def _get_args(self, props=None): + if props is None: + props = self.props + arg = ["NAME:" + self.name] + _dict2arg(props, arg) + return arg + + @pyaedt_function_handler() + def create(self): + """Create a Field Setup Component in HFSS. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + + if self.type == "FarFieldSphere": + self._app.oradfield.InsertInfiniteSphereSetup(self._get_args()) + elif self.type == "NearFieldBox": + self._app.oradfield.InsertBoxSetup(self._get_args()) + elif self.type == "NearFieldSphere": + self._app.oradfield.InsertSphereSetup(self._get_args()) + elif self.type == "NearFieldRectangle": + self._app.oradfield.InsertRectangleSetup(self._get_args()) + elif self.type == "NearFieldLine": + self._app.oradfield.InsertLineSetup(self._get_args()) + elif self.type == "AntennaOverlay": + self._app.oradfield.AddAntennaOverlay(self._get_args()) + elif self.type == "FieldSourceGroup": + self._app.oradfield.AddRadFieldSourceGroup(self._get_args()) + child_object = self._app.get_oo_object(self._app.odesign, f"Radiation/{self._name}") + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) + return True + + @pyaedt_function_handler() + def update(self): + """Update the Field Setup in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + + if self.type == "FarFieldSphere": + self._app.oradfield.EditInfiniteSphereSetup(self.name, self._get_args()) + elif self.type == "NearFieldBox": + self._app.oradfield.EditBoxSetup(self.name, self._get_args()) + elif self.type == "NearFieldSphere": + self._app.oradfield.EditSphereSetup(self.name, self._get_args()) + elif self.type == "NearFieldRectangle": + self._app.oradfield.EditRectangleSetup(self.name, self._get_args()) + elif self.type == "NearFieldLine": + self._app.oradfield.EditLineSetup(self.name, self._get_args()) + elif self.type == "AntennaOverlay": + self._app.oradfield.EditAntennaOverlay(self.name, self._get_args()) + elif self.type == "FieldSourceGroup": + self._app.oradfield.EditRadFieldSourceGroup(self._get_args()) + return True + + @pyaedt_function_handler() + def delete(self): + """Delete the Field Setup in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + self._app.oradfield.DeleteSetup([self.name]) + for el in self._app.field_setups: + if el.name == self.name: + self._app.field_setups.remove(el) + return True + + +class FarFieldSetup(FieldSetup, object): + """Manages Far Field Component data and execution. + + Examples + -------- + in this example the sphere1 returned object is a ``ansys.aedt.core.modules.boundary.FarFieldSetup`` + >>> from ansys.aedt.core import Hfss + >>> hfss = Hfss() + >>> sphere1 = hfss.insert_infinite_sphere() + >>> sphere1.props["ThetaStart"] = "-90deg" + >>> sphere1.props["ThetaStop"] = "90deg" + >>> sphere1.props["ThetaStep"] = "2deg" + >>> sphere1.delete() + """ + + def __init__(self, app, component_name, props, component_type, units="deg"): + FieldSetup.__init__(self, app, component_name, props, component_type) + self.units = units + + @property + def definition(self): + """Set/Get the Far Field Angle Definition.""" + return self.props["CSDefinition"] + + @definition.setter + def definition(self, value): + actual_value = self.props["CSDefinition"] + self.props["CSDefinition"] = value + actual_defs = None + defs = None + if actual_value != value and value == "Theta-Phi": + defs = ["ThetaStart", "ThetaStop", "ThetaStep", "PhiStart", "PhiStop", "PhiStep"] + actual_defs = [ + "AzimuthStart", + "AzimuthStop", + "AzimuthStep", + "ElevationStart", + "ElevationStop", + "ElevationStep", + ] + elif actual_value != value and value == "El Over Az": + defs = ["AzimuthStart", "AzimuthStop", "AzimuthStep", "ElevationStart", "ElevationStop", "ElevationStep"] + if actual_value == "Theta-Phi": + actual_defs = ["ThetaStart", "ThetaStop", "ThetaStep", "PhiStart", "PhiStop", "PhiStep"] + else: + actual_defs = [ + "AzimuthStart", + "AzimuthStop", + "AzimuthStep", + "ElevationStart", + "ElevationStop", + "ElevationStep", + ] + elif actual_value != value: + defs = ["ElevationStart", "ElevationStop", "ElevationStep", "AzimuthStart", "AzimuthStop", "AzimuthStep"] + if actual_value == "Theta-Phi": + actual_defs = ["ThetaStart", "ThetaStop", "ThetaStep", "PhiStart", "PhiStop", "PhiStep"] + else: + actual_defs = [ + "ElevationStart", + "ElevationStop", + "ElevationStep", + "AzimuthStart", + "AzimuthStop", + "AzimuthStep", + ] + if actual_defs != defs: + self.props[defs[0]] = self.props[actual_defs[0]] + self.props[defs[1]] = self.props[actual_defs[1]] + self.props[defs[2]] = self.props[actual_defs[2]] + self.props[defs[3]] = self.props[actual_defs[3]] + self.props[defs[4]] = self.props[actual_defs[4]] + self.props[defs[5]] = self.props[actual_defs[5]] + del self.props[actual_defs[0]] + del self.props[actual_defs[1]] + del self.props[actual_defs[2]] + del self.props[actual_defs[3]] + del self.props[actual_defs[4]] + del self.props[actual_defs[5]] + self.update() + + @property + def use_custom_radiation_surface(self): + """Set/Get the Far Field Radiation Surface Enable.""" + return self.props["UseCustomRadiationSurface"] + + @use_custom_radiation_surface.setter + def use_custom_radiation_surface(self, value): + self.props["UseCustomRadiationSurface"] = value + self.update() + + @property + def custom_radiation_surface(self): + """Set/Get the Far Field Radiation Surface FaceList.""" + return self.props["CustomRadiationSurface"] + + @custom_radiation_surface.setter + def custom_radiation_surface(self, value): + if value: + self.props["UseCustomRadiationSurface"] = True + self.props["CustomRadiationSurface"] = value + else: + self.props["UseCustomRadiationSurface"] = False + self.props["CustomRadiationSurface"] = "" + self.update() + + @property + def use_local_coordinate_system(self): + """Set/Get the usage of a custom Coordinate System.""" + return self.props["UseLocalCS"] + + @use_local_coordinate_system.setter + def use_local_coordinate_system(self, value): + self.props["UseLocalCS"] = value + self.update() + + @property + def local_coordinate_system(self): + """Set/Get the custom Coordinate System name.""" + try: + return self.properties["Coordinate System"] + except Exception: # pragma: no cover + return None + + @local_coordinate_system.setter + def local_coordinate_system(self, value): + if value: + self.props["UseLocalCS"] = True + self.props["CoordSystem"] = value + else: + self.props["UseLocalCS"] = False + self.props["CoordSystem"] = "" + self.update() + + @property + def polarization(self): + """Set/Get the Far Field Polarization.""" + return self.props["Polarization"] + + @polarization.setter + def polarization(self, value): + self.props["Polarization"] = value + self.update() + + @property + def slant_angle(self): + """Set/Get the Far Field Slant Angle if Polarization is Set to `Slant`.""" + + if self.props["Polarization"] == "Slant": + return self.props["SlantAngle"] + else: + return + + @slant_angle.setter + def slant_angle(self, value): + self.props["Polarization"] = "Slant" + self.props["SlantAngle"] = value + self.update() + + @property + def theta_start(self): + """Set/Get the Far Field Theta Start Angle if Definition is Set to `Theta-Phi`.""" + + if "ThetaStart" in self.props: + return self.props["ThetaStart"] + else: + return + + @property + def theta_stop(self): + """Set/Get the Far Field Theta Stop Angle if Definition is Set to `Theta-Phi`.""" + + if "ThetaStop" in self.props: + return self.props["ThetaStop"] + else: + return + + @property + def theta_step(self): + """Set/Get the Far Field Theta Step Angle if Definition is Set to `Theta-Phi`.""" + + if "ThetaStep" in self.props: + return self.props["ThetaStep"] + else: + return + + @property + def phi_start(self): + """Set/Get the Far Field Phi Start Angle if Definition is Set to `Theta-Phi`.""" + + if "PhiStart" in self.props: + return self.props["PhiStart"] + else: + return + + @property + def phi_stop(self): + """Set/Get the Far Field Phi Stop Angle if Definition is Set to `Theta-Phi`.""" + + if "PhiStop" in self.props: + return self.props["PhiStop"] + else: + return + + @property + def phi_step(self): + """Set/Get the Far Field Phi Step Angle if Definition is Set to `Theta-Phi`.""" + + if "PhiStep" in self.props: + return self.props["PhiStep"] + else: + return + + @property + def azimuth_start(self): + """Set/Get the Far Field Azimuth Start Angle if Definition is Set to `Az Over El` or `El Over Az`.""" + + if "AzimuthStart" in self.props: + return self.props["AzimuthStart"] + else: + return + + @property + def azimuth_stop(self): + """Set/Get the Far Field Azimuth Stop Angle if Definition is Set to `Az Over El` or `El Over Az`.""" + + if "AzimuthStop" in self.props: + return self.props["AzimuthStop"] + else: + return + + @property + def azimuth_step(self): + """Set/Get the Far Field Azimuth Step Angle if Definition is Set to `Az Over El` or `El Over Az`.""" + + if "AzimuthStep" in self.props: + return self.props["AzimuthStep"] + else: + return + + @property + def elevation_start(self): + """Set/Get the Far Field Elevation Start Angle if Definition is Set to `Az Over El` or `El Over Az`.""" + + if "ElevationStart" in self.props: + return self.props["ElevationStart"] + else: + return + + @property + def elevation_stop(self): + """Set/Get the Far Field Elevation Stop Angle if Definition is Set to `Az Over El` or `El Over Az`.""" + + if "ElevationStop" in self.props: + return self.props["ElevationStop"] + else: + return + + @property + def elevation_step(self): + """Set/Get the Far Field Elevation Step Angle if Definition is Set to `Az Over El` or `El Over Az`.""" + + if "ElevationStep" in self.props: + return self.props["ElevationStep"] + else: + return + + @theta_start.setter + def theta_start(self, value): + if "ThetaStart" in self.props: + self.props["ThetaStart"] = _dim_arg(value, self.units) + self.update() + + @theta_stop.setter + def theta_stop(self, value): + if "ThetaStop" in self.props: + self.props["ThetaStop"] = _dim_arg(value, self.units) + self.update() + + @theta_step.setter + def theta_step(self, value): + if "ThetaStep" in self.props: + self.props["ThetaStep"] = _dim_arg(value, self.units) + self.update() + + @phi_start.setter + def phi_start(self, value): + if "PhiStart" in self.props: + self.props["PhiStart"] = _dim_arg(value, self.units) + self.update() + + @phi_stop.setter + def phi_stop(self, value): + if "PhiStop" in self.props: + self.props["PhiStop"] = _dim_arg(value, self.units) + self.update() + + @phi_step.setter + def phi_step(self, value): + if "PhiStep" in self.props: + self.props["PhiStep"] = _dim_arg(value, self.units) + self.update() + + @azimuth_start.setter + def azimuth_start(self, value): + if "AzimuthStart" in self.props: + self.props["AzimuthStart"] = _dim_arg(value, self.units) + self.update() + + @azimuth_stop.setter + def azimuth_stop(self, value): + if "AzimuthStop" in self.props: + self.props["AzimuthStop"] = _dim_arg(value, self.units) + self.update() + + @azimuth_step.setter + def azimuth_step(self, value): + if "AzimuthStep" in self.props: + self.props["AzimuthStep"] = _dim_arg(value, self.units) + self.update() + + @elevation_start.setter + def elevation_start(self, value): + if "ElevationStart" in self.props: + self.props["ElevationStart"] = _dim_arg(value, self.units) + self.update() + + @elevation_stop.setter + def elevation_stop(self, value): + if "ElevationStop" in self.props: + self.props["ElevationStop"] = _dim_arg(value, self.units) + self.update() + + @elevation_step.setter + def elevation_step(self, value): + if "ElevationStep" in self.props: + self.props["ElevationStep"] = _dim_arg(value, self.units) + self.update() + + +class NearFieldSetup(FieldSetup, object): + """Manages Near Field Component data and execution. + + Examples + -------- + in this example the rectangle1 returned object is a ``ansys.aedt.core.modules.boundary.NearFieldSetup`` + >>> from ansys.aedt.core import Hfss + >>> hfss = Hfss() + >>> rectangle1 = hfss.insert_near_field_rectangle() + """ + + def __init__(self, app, component_name, props, component_type): + FieldSetup.__init__(self, app, component_name, props, component_type) diff --git a/src/ansys/aedt/core/modules/boundary/icepak_boundary.py b/src/ansys/aedt/core/modules/boundary/icepak_boundary.py new file mode 100644 index 00000000000..0ec6e021afd --- /dev/null +++ b/src/ansys/aedt/core/modules/boundary/icepak_boundary.py @@ -0,0 +1,263 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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 abc import abstractmethod + +from ansys.aedt.core.generic.general_methods import pyaedt_function_handler + + +class BoundaryDictionary: + """Handles Icepak transient and temperature-dependent boundary condition assignments. + + Parameters + ---------- + assignment_type : str + Type of assignment represented by the class. Options are `"Temp Dep"`` + and ``"Transient"``. + function_type : str + Variation function to assign. If ``assignment_type=="Temp Dep"``, + the function can only be ``"Piecewise Linear"``. Otherwise, the function can be + ``"Exponential"``, ``"Linear"``, ``"Piecewise Linear"``, ``"Power Law"``, + ``"Sinusoidal"``, and ``"Square Wave"``. + """ + + def __init__(self, assignment_type, function_type): + if assignment_type not in ["Temp Dep", "Transient"]: # pragma : no cover + raise AttributeError(f"The argument {assignment_type} for ``assignment_type`` is not valid.") + if assignment_type == "Temp Dep" and function_type != "Piecewise Linear": # pragma : no cover + raise AttributeError( + "Temperature dependent assignments only support" + ' ``"Piecewise Linear"`` as ``function_type`` argument.' + ) + self.assignment_type = assignment_type + self.function_type = function_type + + @property + def props(self): + """Dictionary that defines all the boundary condition properties.""" + return { + "Type": self.assignment_type, + "Function": self.function_type, + "Values": self._parse_value(), + } + + @abstractmethod + def _parse_value(self): + pass # pragma : no cover + + @pyaedt_function_handler() + def __getitem__(self, k): + return self.props.get(k) + + +class LinearDictionary(BoundaryDictionary): + """Manages linear conditions assignments, which are children of the ``BoundaryDictionary`` class. + + This class applies a condition ``y`` dependent on the time ``t``: + ``y=a+b*t`` + + Parameters + ---------- + intercept : str + Value of the assignment condition at the initial time, which + corresponds to the coefficient ``a`` in the formula. + slope : str + Slope of the assignment condition, which + corresponds to the coefficient ``b`` in the formula. + """ + + def __init__(self, intercept, slope): + super().__init__("Transient", "Linear") + self.intercept = intercept + self.slope = slope + + @pyaedt_function_handler() + def _parse_value(self): + return [self.slope, self.intercept] + + +class PowerLawDictionary(BoundaryDictionary): + """Manages power law condition assignments, which are children of the ``BoundaryDictionary`` class. + + This class applies a condition ``y`` dependent on the time ``t``: + ``y=a+b*t^c`` + + Parameters + ---------- + intercept : str + Value of the assignment condition at the initial time, which + corresponds to the coefficient ``a`` in the formula. + coefficient : str + Coefficient that multiplies the power term, which + corresponds to the coefficient ``b`` in the formula. + scaling_exponent : str + Exponent of the power term, which + corresponds to the coefficient ``c`` in the formula. + """ + + def __init__(self, intercept, coefficient, scaling_exponent): + super().__init__("Transient", "Power Law") + self.intercept = intercept + self.coefficient = coefficient + self.scaling_exponent = scaling_exponent + + @pyaedt_function_handler() + def _parse_value(self): + return [self.intercept, self.coefficient, self.scaling_exponent] + + +class ExponentialDictionary(BoundaryDictionary): + """Manages exponential condition assignments, which are children of the ``BoundaryDictionary`` class. + + This class applies a condition ``y`` dependent on the time ``t``: + ``y=a+b*exp(c*t)`` + + Parameters + ---------- + vertical_offset : str + Vertical offset summed to the exponential law, which + corresponds to the coefficient ``a`` in the formula. + coefficient : str + Coefficient that multiplies the exponential term, which + corresponds to the coefficient ``b`` in the formula. + exponent_coefficient : str + Coefficient in the exponential term, which + corresponds to the coefficient ``c`` in the formula. + """ + + def __init__(self, vertical_offset, coefficient, exponent_coefficient): + super().__init__("Transient", "Exponential") + self.vertical_offset = vertical_offset + self.coefficient = coefficient + self.exponent_coefficient = exponent_coefficient + + @pyaedt_function_handler() + def _parse_value(self): + return [self.vertical_offset, self.coefficient, self.exponent_coefficient] + + +class SinusoidalDictionary(BoundaryDictionary): + """Manages sinusoidal condition assignments, which are children of the ``BoundaryDictionary`` class. + + This class applies a condition ``y`` dependent on the time ``t``: + ``y=a+b*sin(2*pi(t-t0)/T)`` + + Parameters + ---------- + vertical_offset : str + Vertical offset summed to the sinusoidal law, which + corresponds to the coefficient ``a`` in the formula. + vertical_scaling : str + Coefficient that multiplies the sinusoidal term, which + corresponds to the coefficient ``b`` in the formula. + period : str + Period of the sinusoid, which + corresponds to the coefficient ``T`` in the formula. + period_offset : str + Offset of the sinusoid, which + corresponds to the coefficient ``t0`` in the formula. + """ + + def __init__(self, vertical_offset, vertical_scaling, period, period_offset): + super().__init__("Transient", "Sinusoidal") + self.vertical_offset = vertical_offset + self.vertical_scaling = vertical_scaling + self.period = period + self.period_offset = period_offset + + @pyaedt_function_handler() + def _parse_value(self): + return [self.vertical_offset, self.vertical_scaling, self.period, self.period_offset] + + +class SquareWaveDictionary(BoundaryDictionary): + """Manages square wave condition assignments, which are children of the ``BoundaryDictionary`` class. + + Parameters + ---------- + on_value : str + Maximum value of the square wave. + initial_time_off : str + Time after which the square wave assignment starts. + on_time : str + Time for which the square wave keeps the maximum value during one period. + off_time : str + Time for which the square wave keeps the minimum value during one period. + off_value : str + Minimum value of the square wave. + """ + + def __init__(self, on_value, initial_time_off, on_time, off_time, off_value): + super().__init__("Transient", "Square Wave") + self.on_value = on_value + self.initial_time_off = initial_time_off + self.on_time = on_time + self.off_time = off_time + self.off_value = off_value + + @pyaedt_function_handler() + def _parse_value(self): + return [self.on_value, self.initial_time_off, self.on_time, self.off_time, self.off_value] + + +class PieceWiseLinearDictionary(BoundaryDictionary): + """ + Manages dataset condition assignments, which are children of the ``BoundaryDictionary`` class. + + Parameters + ---------- + assignment_type : str + Type of assignment represented by the class. + Options are ``"Temp Dep"`` and ``"Transient"``. + ds : str + Dataset name to assign. + scale : str + Scaling factor for the y values of the dataset. + """ + + def __init__(self, assignment_type, ds, scale): + super().__init__(assignment_type, "Piecewise Linear") + self.scale = scale + self._assignment_type = assignment_type + self.dataset = ds + + @pyaedt_function_handler() + def _parse_value(self): + return [self.scale, self.dataset.name] + + @property + def dataset_name(self): + """Dataset name that defines the piecewise assignment.""" + return self.dataset.name + + +def _create_boundary(bound): + try: + if bound.create(): + bound._app._boundaries[bound.name] = bound + return bound + else: # pragma : no cover + raise Exception + except Exception: # pragma: no cover + return None diff --git a/src/ansys/aedt/core/modules/boundary/layout_boundary.py b/src/ansys/aedt/core/modules/boundary/layout_boundary.py new file mode 100644 index 00000000000..e5c3088b45f --- /dev/null +++ b/src/ansys/aedt/core/modules/boundary/layout_boundary.py @@ -0,0 +1,1342 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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 ansys.aedt.core.generic.data_handlers import _dict2arg +from ansys.aedt.core.generic.data_handlers import random_string +from ansys.aedt.core.generic.general_methods import GrpcApiError +from ansys.aedt.core.generic.general_methods import pyaedt_function_handler +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode +from ansys.aedt.core.modules.boundary.common import BoundaryCommon +from ansys.aedt.core.modules.boundary.common import BoundaryProps +from ansys.aedt.core.modules.boundary.common import disable_auto_update + + +class NativeComponentObject(BoundaryCommon, BinaryTreeNode): + """Manages Native Component data and execution. + + Parameters + ---------- + app : object + An AEDT application from ``ansys.aedt.core.application``. + component_type : str + Type of the component. + component_name : str + Name of the component. + props : dict + Properties of the boundary. + + Examples + -------- + in this example the par_beam returned object is a ``ansys.aedt.core.modules.boundary.NativeComponentObject`` + >>> from ansys.aedt.core import Hfss + >>> hfss = Hfss(solution_type="SBR+") + >>> ffd_file ="path/to/ffdfile.ffd" + >>> par_beam = hfss.create_sbr_file_based_antenna(ffd_file) + >>> par_beam.native_properties["Size"] = "0.1mm" + >>> par_beam.update() + >>> par_beam.delete() + """ + + def __init__(self, app, component_type, component_name, props): + self.auto_update = False + self._app = app + self._name = component_name + + self.__props = BoundaryProps( + self, + { + "TargetCS": "Global", + "SubmodelDefinitionName": self.name, + "ComponentPriorityLists": {}, + "NextUniqueID": 0, + "MoveBackwards": False, + "DatasetType": "ComponentDatasetType", + "DatasetDefinitions": {}, + "BasicComponentInfo": { + "ComponentName": self.name, + "Company": "", + "Company URL": "", + "Model Number": "", + "Help URL": "", + "Version": "1.0", + "Notes": "", + "IconType": "", + }, + "GeometryDefinitionParameters": {"VariableOrders": {}}, + "DesignDefinitionParameters": {"VariableOrders": {}}, + "MaterialDefinitionParameters": {"VariableOrders": {}}, + "DefReferenceCSID": 1, + "MapInstanceParameters": "DesignVariable", + "UniqueDefinitionIdentifier": "89d26167-fb77-480e-a7ab-" + + random_string(12, char_set="abcdef0123456789"), + "OriginFilePath": "", + "IsLocal": False, + "ChecksumString": "", + "ChecksumHistory": [], + "VersionHistory": [], + "NativeComponentDefinitionProvider": {"Type": component_type}, + "InstanceParameters": {"GeometryParameters": "", "MaterialParameters": "", "DesignParameters": ""}, + }, + ) + if props: + self._update_props(self.__props, props) + self.native_properties = self.__props["NativeComponentDefinitionProvider"] + self.auto_update = True + child_object = self._app.get_oo_object(self._app.oeditor, self._name) + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) + + @property + def props(self): + return self.__props + + @property + def name(self): + """Name of the object. + + Returns + ------- + str + Name of the object. + + """ + return self._name + + @name.setter + def name(self, component_name): + if component_name != self._name: + if component_name not in self._app.native_component_names: + self.properties["Name"] = component_name + self._app.native_components.update({component_name: self}) + del self._app.native_components[self._name] + del self._app.modeler.user_defined_components[self._name] + self._name = component_name + else: # pragma: no cover + self._app._logger.warning("Name %s already assigned in the design", component_name) + + @property + def definition_name(self): + """Definition name of the native component. + + Returns + ------- + str + Name of the native component. + + """ + definition_name = None + if self.props and "SubmodelDefinitionName" in self.props: + definition_name = self.props["SubmodelDefinitionName"] + return definition_name + + @property + def targetcs(self): + """Native Component Coordinate System. + + Returns + ------- + str + Native Component Coordinate System. + """ + if "TargetCS" in list(self.props.keys()): + return self.props["TargetCS"] + else: + return "Global" + + @targetcs.setter + def targetcs(self, cs): + self.props["TargetCS"] = cs + + def _update_props(self, d, u): + for k, v in u.items(): + if isinstance(v, dict): + if k not in d: + d[k] = {} + d[k] = self._update_props(d[k], v) + else: + d[k] = v + return d + + @pyaedt_function_handler() + def _get_args(self, props=None): + if props is None: + props = self.props + arg = ["NAME:InsertNativeComponentData"] + _dict2arg(props, arg) + return arg + + @pyaedt_function_handler() + def create(self): + """Create a Native Component in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + try: + names = [i for i in self._app.excitations] + except GrpcApiError: # pragma: no cover + names = [] + self._name = self._app.modeler.oeditor.InsertNativeComponent(self._get_args()) + try: + a = [i for i in self._app.excitations if i not in names] + self.excitation_name = a[0].split(":")[0] + except (GrpcApiError, IndexError): + self.excitation_name = self.name + child_object = self._app.get_oo_object(self._app.oeditor, self._name) + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) + return True + + @pyaedt_function_handler() + def update(self): + """Update the Native Component in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + + self.update_props = {} + self.update_props["DefinitionName"] = self.props["SubmodelDefinitionName"] + self.update_props["GeometryDefinitionParameters"] = self.props["GeometryDefinitionParameters"] + self.update_props["DesignDefinitionParameters"] = self.props["DesignDefinitionParameters"] + self.update_props["MaterialDefinitionParameters"] = self.props["MaterialDefinitionParameters"] + self.update_props["NextUniqueID"] = self.props["NextUniqueID"] + self.update_props["MoveBackwards"] = self.props["MoveBackwards"] + self.update_props["DatasetType"] = self.props["DatasetType"] + self.update_props["DatasetDefinitions"] = self.props["DatasetDefinitions"] + self.update_props["NativeComponentDefinitionProvider"] = self.props["NativeComponentDefinitionProvider"] + self.update_props["ComponentName"] = self.props["BasicComponentInfo"]["ComponentName"] + self.update_props["Company"] = self.props["BasicComponentInfo"]["Company"] + self.update_props["Model Number"] = self.props["BasicComponentInfo"]["Model Number"] + self.update_props["Help URL"] = self.props["BasicComponentInfo"]["Help URL"] + self.update_props["Version"] = self.props["BasicComponentInfo"]["Version"] + self.update_props["Notes"] = self.props["BasicComponentInfo"]["Notes"] + self.update_props["IconType"] = self.props["BasicComponentInfo"]["IconType"] + self._app.modeler.oeditor.EditNativeComponentDefinition(self._get_args(self.update_props)) + + return True + + @pyaedt_function_handler() + def delete(self): + """Delete the Native Component in AEDT. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + self._app.modeler.oeditor.Delete(["NAME:Selections", "Selections:=", self.name]) + for el in self._app._native_components: + if el.name == self.name: + self._app._native_components.remove(el) + del self._app.modeler.user_defined_components[self.name] + self._app.modeler.cleanup_objects() + return True + + +class BoundaryObject3dLayout(BoundaryCommon, BinaryTreeNode): + """Manages boundary data and execution for Hfss3dLayout. + + Parameters + ---------- + app : object + An AEDT application from ``ansys.aedt.core.application``. + name : str + Name of the boundary. + props : dict + Properties of the boundary. + boundarytype : str + Type of the boundary. + """ + + def __init__(self, app, name, props, boundarytype): + self.auto_update = False + self._app = app + self._name = name + self.__props = None + if props: + self.__props = BoundaryProps(self, props) + self.type = boundarytype + self._boundary_name = self.name + self.auto_update = True + if self._child_object: + BinaryTreeNode.__init__(self, self.name, self._child_object, False) + + @property + def _child_object(self): + + cc = self._app.odesign.GetChildObject("Excitations") + child_object = None + if self.name in cc.GetChildNames(): + child_object = self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) + elif self.name in self._app.odesign.GetChildObject("Excitations").GetChildNames(): + child_object = self._app.odesign.GetChildObject("Excitations").GetChildObject(self.name) + + if "Boundaries" in self._app.odesign.GetChildNames(): + cc = self._app.odesign.GetChildObject("Boundaries") + if self.name in cc.GetChildNames(): + child_object = self._app.odesign.GetChildObject("Boundaries").GetChildObject(self.name) + elif self.name in self._app.odesign.GetChildObject("Boundaries").GetChildNames(): + child_object = self._app.odesign.GetChildObject("Boundaries").GetChildObject(self.name) + + return child_object + + @property + def name(self): + """Boundary Name.""" + return self._name + + @name.setter + def name(self, value): + if "Port" in self.props: + self.auto_update = False + self.props["Port"] = value + self.auto_update = True + self.update() + self._name = value + + @property + def props(self): + """Excitation data. + + Returns + ------- + :class:BoundaryProps + """ + if self.__props: + return self.__props + props = self._get_boundary_data(self.name) + + if props: + self.__props = BoundaryProps(self, props[0]) + self._type = props[1] + return self.__props + + @pyaedt_function_handler() + def _get_args(self, props=None): + """Retrieve arguments. + + Parameters + ---------- + props : + The default is ``None``. + + Returns + ------- + list + List of boundary properties. + + """ + if props is None: + props = self.props + arg = ["NAME:" + self.name] + _dict2arg(props, arg) + return arg + + @pyaedt_function_handler() + def _refresh_properties(self): + if len(self._app.oeditor.GetProperties("EM Design", f"Excitations:{self.name}")) != len(self.props): + propnames = self._app.oeditor.GetProperties("EM Design", f"Excitations:{self.name}") + props = {} + for prop in propnames: + props[prop] = self._app.oeditor.GetPropertyValue("EM Design", f"Excitations:{self.name}", prop) + self.__props = BoundaryProps(self, props) + + @pyaedt_function_handler() + def update(self): + """Update the boundary. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + updated = False + for el in list(self.props.keys()): + if el == "Port" and self._name != self.props[el]: + self._app.oeditor.SetPropertyValue("EM Design", "Excitations:" + self.name, el, self.props[el]) + self._name = self.props[el] + elif el in self._app.oeditor.GetProperties("EM Design", f"Excitations:{self.name}") and self.props[ + el + ] != self._app.oeditor.GetPropertyValue("EM Design", "Excitations:" + self.name, el): + self._app.oeditor.SetPropertyValue("EM Design", "Excitations:" + self.name, el, self.props[el]) + updated = True + + if updated: + self._refresh_properties() + + return True + + +class NativeComponentPCB(NativeComponentObject, object): + """Manages native component PCB data and execution. + + Parameters + ---------- + app : object + AEDT application from the ``pyaedt.application`` class. + component_type : str + Type of the component. + component_name : str + Name of the component. + props : dict + Properties of the boundary. + """ + + def __init__(self, app, component_type, component_name, props): + NativeComponentObject.__init__(self, app, component_type, component_name, props) + + @pyaedt_function_handler() + @disable_auto_update + def set_resolution(self, resolution): + """Set metal fraction mapping resolution. + + Parameters + ------- + resolution : int + Resolution level. Accepted variables between 1 and 5. + + Returns + ------- + bool + True if successful, else False. + """ + if resolution < 1 or resolution > 5: + self._app.logger.error("Valid resolution values are between 1 and 5.") + return False + self.props["NativeComponentDefinitionProvider"]["Resolution"] = resolution + self.props["NativeComponentDefinitionProvider"]["CustomResolution"] = False + return True + + @pyaedt_function_handler() + @disable_auto_update + def set_custom_resolution(self, row, col): + """Set custom metal fraction mapping resolution. + + Parameters + ---------- + row : int + Resolution level in rows direction. + col : int + Resolution level in columns direction. + + Returns + ------- + bool + True if successful, else False. + """ + self.props["NativeComponentDefinitionProvider"]["CustomResolutionRow"] = row + self.props["NativeComponentDefinitionProvider"]["CustomResolutionCol"] = col + self.props["NativeComponentDefinitionProvider"]["CustomResolution"] = True + return True + + @property + def power(self): + """Power dissipation assigned to the PCB.""" + return self.props["NativeComponentDefinitionProvider"].get("Power", "0W") + + @pyaedt_function_handler() + @disable_auto_update + def set_high_side_radiation( + self, + enabled, + surface_material="Steel-oxidised-surface", + radiate_to_ref_temperature=False, + view_factor=1, + ref_temperature="AmbientTemp", + ): + """Set high side radiation properties. + + Parameters + ---------- + enabled : bool + Whether high side radiation is enabled. + surface_material : str, optional + Surface material to apply. Default is ``"Steel-oxidised-surface"``. + radiate_to_ref_temperature : bool, optional + Whether to radiate to a reference temperature instead of objects in the model. + Default is ``False``. + view_factor : float, optional + View factor to use for radiation computation if ``radiate_to_ref_temperature`` + is set to ``True``. Default is 1. + ref_temperature : str, optional + Reference temperature to use for radiation computation if + ``radiate_to_ref_temperature`` is set to True. Default is ``"AmbientTemp"``. + + Returns + ------- + bool + ``True`` if successful, else ``False``. + """ + high_rad = { + "Radiate": enabled, + "RadiateTo - High": "RefTemperature - High" if radiate_to_ref_temperature else "AllObjects - High", + "Surface Material - High": surface_material, + } + if radiate_to_ref_temperature: + high_rad["Ref. Temperature - High"] = (ref_temperature,) + high_rad["View Factor - High"] = view_factor + self.props["NativeComponentDefinitionProvider"]["HighSide"] = high_rad + return True + + @power.setter + @disable_auto_update + def power(self, value): + """Assign power dissipation to the PCB. + + Parameters + ---------- + value : str + Power to apply to the PCB. + """ + self.props["NativeComponentDefinitionProvider"]["Power"] = value + + @property + def force_source_solve(self): + """Force source solution option.""" + return self.props["NativeComponentDefinitionProvider"].get("DefnLink", {}).get("ForceSourceToSolve", False) + + @force_source_solve.setter + @disable_auto_update + def force_source_solve(self, val): + """Set Whether to force source solution. + + Parameters + ---------- + value : bool + Whether to force source solution. + """ + if not isinstance(val, bool): + self._app.logger.error("Only Boolean value can be accepted.") + return + return self.props["NativeComponentDefinitionProvider"]["DefnLink"].update({"ForceSourceToSolve": val}) + + @property + def preserve_partner_solution(self): + """Preserve parner solution option.""" + return self.props["NativeComponentDefinitionProvider"].get("DefnLink", {}).get("PreservePartnerSoln", False) + + @preserve_partner_solution.setter + @disable_auto_update + def preserve_partner_solution(self, val): + """Set Whether to preserve partner solution. + + Parameters + ---------- + val : bool + Whether to preserve partner solution. + """ + if not isinstance(val, bool): + self._app.logger.error("Only boolean can be accepted.") + return + return self.props["NativeComponentDefinitionProvider"]["DefnLink"].update({"PreservePartnerSoln": val}) + + @property + def included_parts(self): + """Parts options.""" + p = self.props["NativeComponentDefinitionProvider"].get("PartsChoice", 0) + if p == 0: + return None + elif p == 1: + return PCBSettingsDeviceParts(self, self._app) + elif p == 2: + return PCBSettingsPackageParts(self, self._app) + + @included_parts.setter + @disable_auto_update + def included_parts(self, value): + """Set PCB parts incusion option. + + Parameters + ---------- + value : str or int + Valid options are ``"None"``, ``"Device"``, and ``"Package"`` (or 0, 1, and 2 respectivaly) + """ + if value is None: + value = "None" + part_map = {"None": 0, "Device": 1, "Package": 2} + if not isinstance(value, int): + value = part_map.get(value, None) + if value is not None: + self.props["NativeComponentDefinitionProvider"]["PartsChoice"] = value + else: + self._app.logger.error( + 'Invalid part choice. Valid options are "None", "Device", and "Package" (or 0, 1, and 2 respectively).' + ) + + @pyaedt_function_handler() + @disable_auto_update + def set_low_side_radiation( + self, + enabled, + surface_material="Steel-oxidised-surface", + radiate_to_ref_temperature=False, + view_factor=1, + ref_temperature="AmbientTemp", + ): + """Set low side radiation properties. + + Parameters + ---------- + enabled : bool + Whether high side radiation is enabled. + surface_material : str, optional + Surface material to apply. Default is ``"Steel-oxidised-surface"``. + radiate_to_ref_temperature : bool, optional + Whether to radiate to a reference temperature instead of objects in the model. + Default is ``False``. + view_factor : float, optional + View factor to use for radiation computation if ``radiate_to_ref_temperature`` + is set to True. Default is 1. + ref_temperature : str, optional + Reference temperature to use for radiation computation if + ``radiate_to_ref_temperature`` is set to ``True``. Default is ``"AmbientTemp"``. + + Returns + ------- + bool + ``True`` if successful, else ``False``. + """ + low_side = { + "Radiate": enabled, + "RadiateTo": "RefTemperature - High" if radiate_to_ref_temperature else "AllObjects", + "Surface Material": surface_material, + } + if radiate_to_ref_temperature: + low_side["Ref. Temperature"] = (ref_temperature,) + low_side["View Factor"] = view_factor + self.props["NativeComponentDefinitionProvider"]["LowSide"] = low_side + return True + + @power.setter + @disable_auto_update + def power(self, value): + """Assign power dissipation to the PCB. + + Parameters + ---------- + value : str + Power to apply to the PCB. + """ + self.props["NativeComponentDefinitionProvider"]["Power"] = value + + @force_source_solve.setter + @disable_auto_update + def force_source_solve(self, val): + """Set Whether to force source solution. + + Parameters + ---------- + value : bool + Whether to force source solution. + """ + if not isinstance(val, bool): + self._app.logger.error("Only Boolean value can be accepted.") + return + return self.props["NativeComponentDefinitionProvider"]["DefnLink"].update({"ForceSourceToSolve": val}) + + @preserve_partner_solution.setter + @disable_auto_update + def preserve_partner_solution(self, val): + """Set Whether to preserve partner solution. + + Parameters + ---------- + val : bool + Whether to preserve partner solution. + """ + if not isinstance(val, bool): + self._app.logger.error("Only boolean can be accepted.") + return + return self.props["NativeComponentDefinitionProvider"]["DefnLink"].update({"PreservePartnerSoln": val}) + + @included_parts.setter + @disable_auto_update + def included_parts(self, value): + """Set PCB parts incusion option. + + Parameters + ---------- + value : str or int + Valid options are ``"None"``, ``"Device"``, and ``"Package"`` (or 0, 1, and 2 respectivaly) + """ + if value is None: + value = "None" + part_map = {"None": 0, "Device": 1, "Package": 2} + if not isinstance(value, int): + value = part_map.get(value, None) + if value is not None: + self.props["NativeComponentDefinitionProvider"]["PartsChoice"] = value + else: + self._app.logger.error( + 'Invalid part choice. Valid options are "None", "Device", and "Package" (or 0, 1, and 2 respectively).' + ) + + @pyaedt_function_handler() + def identify_extent_poly(self): + """Get polygon that defines board extent. + + Returns + ------- + str + Name of the polygon to include. + """ + from ansys.aedt.core import Hfss3dLayout + + prj = self.props["NativeComponentDefinitionProvider"]["DefnLink"]["Project"] + if prj == "This Project*": + prj = self._app.project_name + layout = Hfss3dLayout(project=prj, design=self.props["NativeComponentDefinitionProvider"]["DefnLink"]["Design"]) + layer = [o for o in layout.modeler.stackup.drawing_layers if o.type == "outline"][0] + outlines = [p for p in layout.modeler.polygons.values() if p.placement_layer == layer.name] + if len(outlines) > 1: + self._app.logger.info( + f"{outlines[0].name} automatically selected as ``extent_polygon``, " + f"pass ``extent_polygon`` argument explixitly to select a different one. " + f"Available choices are: {', '.join([o.name for o in outlines])}" + ) + elif len(outlines) == 0: + self._app.logger.error("No polygon found in the Outline layer.") + return False + return outlines[0].name + + @property + def board_cutout_material(self): + """Material applied to cutout regions.""" + return self.props["NativeComponentDefinitionProvider"].get("BoardCutoutMaterial", "air ") + + @property + def via_holes_material(self): + """Material applied to via hole regions.""" + return self.props["NativeComponentDefinitionProvider"].get("ViaHoleMaterial", "copper") + + @board_cutout_material.setter + @disable_auto_update + def board_cutout_material(self, value): + """Set material to apply to cutout regions. + + Parameters + ---------- + value : str + Material to apply to cutout regions. + """ + self.props["NativeComponentDefinitionProvider"]["BoardCutoutMaterial"] = value + + @via_holes_material.setter + @disable_auto_update + def via_holes_material(self, value): + """Set material to apply to via hole regions. + + Parameters + ---------- + value : str + Material to apply to via hole regions. + """ + self.props["NativeComponentDefinitionProvider"]["ViaHoleMaterial"] = value + + @pyaedt_function_handler() + @disable_auto_update + def set_board_extents(self, extent_type=None, extent_polygon=None): + """Set board extent. + + Parameters + ---------- + extent_type : str, optional + Extent definition of the PCB. Default is ``None`` in which case the 3D Layout extent + will be used. Other possible options are: ``"Bounding Box"`` or ``"Polygon"``. + extent_polygon : str, optional + Polygon name to use in the extent definition of the PCB. Default is ``None``. This + argument is mandatory if ``extent_type`` is ``"Polygon"``. + + Returns + ------- + bool + ``True`` if successful. ``False`` otherwise. + """ + if extent_type is None: + self.props["NativeComponentDefinitionProvider"]["Use3DLayoutExtents"] = True + else: + allowed_extent_types = ["Bounding Box", "Polygon"] + if extent_type not in allowed_extent_types: + self._app.logger.error( + f"Accepted argument for ``extent_type`` are:" + f" {', '.join(allowed_extent_types)}. {extent_type} provided" + ) + return False + self.props["NativeComponentDefinitionProvider"]["ExtentsType"] = extent_type + if extent_type == "Polygon": + if extent_polygon is None: + extent_polygon = self.identify_extent_poly() + if not extent_polygon: + return False + self.props["NativeComponentDefinitionProvider"]["OutlinePolygon"] = extent_polygon + return True + + +class PCBSettingsPackageParts(object): + """Handle package part settings of the PCB component. + + Parameters + ---------- + pcb_obj : :class:`ansys.aedt.core.modules.layout_boundary.NativeComponentPCB` + Inherited pcb object. + app : :class:`pyaedt.Icepak` + Inherited application object. + """ + + def __init__(self, pcb_obj, app): + self._app = app + self.pcb = pcb_obj + self._solderbumps_map = {"Lumped": "SbLumped", "Cylinders": "SbCylinder", "Boxes": "SbBlock"} + + def __eq__(self, other): + if isinstance(other, str): + return other == "Package" + elif isinstance(other, self.__class__): + return self.__dict__ == other.__dict__ + else: + return False + + def __ne__(self, other): + return not self.__eq__(other) + + @pyaedt_function_handler() + @disable_auto_update + def set_solderballs_modeling(self, modeling=None): + """Set how to model solderballs. + + Parameters + ---------- + modeling : str, optional + Method for modeling solderballs located below the stackup. The default is + ``None``, in which case they are not modeled. Options for modeling are + ``"Boxes"``, ``"Cylinders"``, and ``"Lumped"``. + + Returns + ------- + bool + ``True`` if successful, ``False`` otherwise. + """ + update_properties = { + "CreateBottomSolderballs": modeling is not None, + "BottomSolderballsModelType": self._solderbumps_map[modeling], + } + + self.pcb.props["NativeComponentDefinitionProvider"].update(update_properties) + return True + + @pyaedt_function_handler() + @disable_auto_update + def set_connectors_modeling( + self, + modeling=None, + solderbumps_modeling="Boxes", + bondwire_material="Au-Typical", + bondwire_diameter="0.05mm", + ): + """Set how to model connectors. + + Parameters + ---------- + modeling : str, optional + Method for modeling connectors located above the stackup. The default is + ``None``, in which case they are not modeled. Options for modeling are + ``"Bondwire"`` and ``"Solderbump"``. + solderbumps_modeling : str, optional + Method for modeling solderbumps if ``modeling="Solderbump"``. + The default is ```"Boxes"``. Options are ``"Boxes"``, ``"Cylinders"``, + and ``"Lumped"``. + bondwire_material : str, optional + Bondwire material if ``modeling="Bondwire"``. The default is + ``"Au-Typical"``. + bondwire_diameter : str, optional + Bondwires diameter if ``modeling="Bondwire". + The default is ``"0.05mm"``. + + Returns + ------- + bool + ``True`` if successful, ``False`` otherwise. + """ + valid_connectors = ["Solderbump", "Bondwire"] + if modeling is not None and modeling not in valid_connectors: + self._app.logger.error( + f"{modeling} option is not supported. Use one of the following: {', '.join(valid_connectors)}" + ) + return False + if bondwire_material not in self._app.materials.mat_names_aedt: + self._app.logger.error(f"{bondwire_material} material is not present in the library.") + return False + if self._solderbumps_map.get(solderbumps_modeling, None) is None: + self._app.logger.error( + f"Solderbumps modeling option {solderbumps_modeling} is not valid. " + f"Available options are: {', '.join(list(self._solderbumps_map.keys()))}." + ) + return False + + update_properties = { + "CreateTopSolderballs": modeling is not None, + "TopConnectorType": modeling, + "TopSolderballsModelType": self._solderbumps_map[solderbumps_modeling], + "BondwireMaterial": bondwire_material, + "BondwireDiameter": bondwire_diameter, + } + + self.pcb.props["NativeComponentDefinitionProvider"].update(update_properties) + return True + + def __repr__(self): + return "Package" + + +class PCBSettingsDeviceParts(object): + """Handle device part settings of the PCB component. + + Parameters + ---------- + pcb_obj : :class:`ansys.aedt.core.modules.layout_boundary.NativeComponentPCB` + Inherited pcb object. + app : :class:`pyaedt.Icepak` + Inherited application object. + """ + + def __init__(self, pcb_obj, app): + self._app = app + self.pcb = pcb_obj + self._filter_map2name = {"Cap": "Capacitors", "Ind": "Inductors", "Res": "Resistors"} + + def __eq__(self, other): + if isinstance(other, str): + return other == "Device" + elif isinstance(other, self.__class__): + return self.__dict__ == other.__dict__ + else: + return False + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return "Device" + + @property + @pyaedt_function_handler() + def simplify_parts(self): + """Get whether parts are simplified as cuboid.""" + return self.pcb.props["NativeComponentDefinitionProvider"]["ModelDeviceAsRect"] + + @simplify_parts.setter + @pyaedt_function_handler() + def simplify_parts(self, value): + """Set whether parts are simplified as cuboid. + + Parameters + ---------- + value : bool + Whether parts are simplified as cuboid. + """ + self.pcb.props["NativeComponentDefinitionProvider"]["ModelDeviceAsRect"] = value + + @property + @pyaedt_function_handler() + def surface_material(self): + """Surface material to apply to parts.""" + return self.pcb.props["NativeComponentDefinitionProvider"]["DeviceSurfaceMaterial"] + + @surface_material.setter + @pyaedt_function_handler() + def surface_material(self, value): + """Set surface material to apply to parts. + + Parameters + ---------- + value : str + Surface material to apply to parts. + """ + self.pcb.props["NativeComponentDefinitionProvider"]["DeviceSurfaceMaterial"] = value + + @property + @pyaedt_function_handler() + def footprint_filter(self): + """Minimum component footprint for filtering.""" + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return None + if self._app.settings.aedt_version < "2024.2": + return None + return self.filters.get("FootPrint", {}).get("Value", None) + + @footprint_filter.setter + @pyaedt_function_handler() + @disable_auto_update + def footprint_filter(self, minimum_footprint): + """Set minimum component footprint for filtering. + + Parameters + ---------- + minimum_footprint : str + Value with unit of the minimum component footprint for filtering. + """ + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return + if self._app.settings.aedt_version < "2024.2": + return + new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) + if "FootPrint" in new_filters: + new_filters.remove("FootPrint") + if minimum_footprint is not None: + new_filters.append("FootPrint") + self.pcb.props["NativeComponentDefinitionProvider"]["FootPrint"] = minimum_footprint + self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters + + @property + @pyaedt_function_handler() + def power_filter(self): + """Minimum component power for filtering.""" + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return None + return self.filters.get("Power", {}).get("Value") + + @power_filter.setter + @pyaedt_function_handler() + @disable_auto_update + def power_filter(self, minimum_power): + """Set minimum component power for filtering. + + Parameters + ---------- + minimum_power : str + Value with unit of the minimum component power for filtering. + """ + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return + new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) + if "Power" in new_filters: + new_filters.remove("Power") + if minimum_power is not None: + new_filters.append("Power") + self.pcb.props["NativeComponentDefinitionProvider"]["PowerVal"] = minimum_power + self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters + + @property + @pyaedt_function_handler() + def type_filters(self): + """Types of component that are filtered.""" + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return None + return self.filters.get("Types") + + @type_filters.setter + @pyaedt_function_handler() + @disable_auto_update + def type_filters(self, object_type): + """Set types of component to filter. + + Parameters + ---------- + object_type : str or list + Types of object to filter. Options are ``"Capacitors"``, ``"Inductors"``, and ``"Resistors"``. + """ + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return + if not isinstance(object_type, list): + object_type = [object_type] + if not all(o in self._filter_map2name.values() for o in object_type): + self._app.logger.error( + f"Accepted elements of the list are: {', '.join(list(self._filter_map2name.values()))}" + ) + else: + new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) + map2arg = {v: k for k, v in self._filter_map2name.items()} + for f, _ in self._filter_map2name.items(): + if f in new_filters: + new_filters.remove(f) + new_filters += [map2arg[o] for o in object_type] + self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters + + @property + @pyaedt_function_handler() + def height_filter(self): + """Minimum component height for filtering.""" + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return None + return self.filters.get("Height", {}).get("Value", None) + + @height_filter.setter + @pyaedt_function_handler() + @disable_auto_update + def height_filter(self, minimum_height): + """Set minimum component height for filtering and whether to filter 2D objects. + + Parameters + ---------- + minimum_height : str + Value with unit of the minimum component power for filtering. + """ + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return + new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) + if "Height" in new_filters: + new_filters.remove("Height") + if minimum_height is not None: + new_filters.append("Height") + self.pcb.props["NativeComponentDefinitionProvider"]["HeightVal"] = minimum_height + self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters + + @property + @pyaedt_function_handler() + def objects_2d_filter(self): + """Whether 2d objects are filtered.""" + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return None + return self.filters.get("Exclude2DObjects", False) + + @objects_2d_filter.setter + @pyaedt_function_handler(filter="enable") + @disable_auto_update + def objects_2d_filter(self, enable): + """Set whether 2d objects are filtered. + + Parameters + ---------- + enable : bool + Whether 2d objects are filtered. + """ + if self.pcb.props["NativeComponentDefinitionProvider"]["PartsChoice"] != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return + new_filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) + if "HeightExclude2D" in new_filters: + new_filters.remove("HeightExclude2D") + if enable: + new_filters.append("HeightExclude2D") + self.pcb.props["NativeComponentDefinitionProvider"]["Filters"] = new_filters + + @property + @pyaedt_function_handler() + def filters(self): + """All active filters.""" + if self.pcb.props["NativeComponentDefinitionProvider"].get("PartsChoice", None) != 1: + self._app.logger.error( + "Device parts modeling is not active. No filtering or override option is available." + ) + return None + out_filters = {"Type": {"Capacitors": False, "Inductors": False, "Resistors": False}} + filters = self.pcb.props["NativeComponentDefinitionProvider"].get("Filters", []) + filter_map2type = { + "Cap": "Type", + "FootPrint": "FootPrint", + "Height": "Height", + "HeightExclude2D": None, + "Ind": "Type", + "Power": "Power", + "Res": "Type", + } + filter_map2val = {"FootPrint": "FootPrint", "Height": "HeightVal", "Power": "PowerVal"} + for f in filters: + if filter_map2type[f] == "Type": + out_filters["Type"][self._filter_map2name[f]] = True + elif filter_map2type[f] is not None: + out_filters[f] = {"Value": filter_map2val[f]} + if "HeightExclude2D" in filters: + out_filters["Exclude2DObjects"] = True + return out_filters + + @property + @pyaedt_function_handler() + def overridden_components(self): + """All overridden components.""" + override_component = ( + self.pcb.props["NativeComponentDefinitionProvider"] + .get("instanceOverridesMap", {}) + .get("oneOverrideBlk", []) + ) + return {o["overrideName"]: o["overrideProps"] for o in override_component} + + @pyaedt_function_handler() + def _override_common( + self, + map_name, + package=None, + part=None, + reference_designator=None, + filter_component=False, + power=None, + r_jb=None, + r_jc=None, + height=None, + ): + override_component = ( + self.pcb.props["NativeComponentDefinitionProvider"] + .get(map_name, {}) # "instanceOverridesMap" + .get("oneOverrideBlk", []) + ) + if map_name == "instanceOverridesMap": + for o in override_component: + if o["overrideName"] == reference_designator: + override_component.remove(o) + elif map_name == "definitionOverridesMap": # pragma: no cover + for o in override_component: + if o["overridePartNumberName"] == part: + override_component.remove(o) + new_filter = {} + if filter_component or any(override_val is not None for override_val in [power, r_jb, r_jc, height]): + if map_name == "instanceOverridesMap": + new_filter.update({"overrideName": reference_designator}) + elif map_name == "definitionOverridesMap": # pragma: no cover + new_filter.update({"overridePartNumberName": part, "overrideGeometryName": package}) + new_filter.update( + { + "overrideProps": { + "isFiltered": filter_component, + "isOverridePower": power is not None, + "isOverrideThetaJb": r_jb is not None, + "isOverrideThetaJc": r_jc is not None, + "isOverrideHeight": height is not None, + "powerOverride": power if power is not None else "nan", + "thetaJbOverride": r_jb if r_jb is not None else "nan", + "thetaJcOverride": r_jc if r_jc is not None else "nan", + "heightOverride": height if height is not None else "nan", + }, + } + ) + override_component.append(new_filter) + self.pcb.props["NativeComponentDefinitionProvider"][map_name] = {"oneOverrideBlk": override_component} + return True + + @pyaedt_function_handler() + @disable_auto_update + def override_definition(self, package, part, filter_component=False, power=None, r_jb=None, r_jc=None, height=None): + """Set component override. + + Parameters + ---------- + package : str + Package name of the definition to override. + part : str + Part name of the definition to override. + filter_component : bool, optional + Whether to filter out the component. The default is ``False``. + power : str, optional + Override component power. Default is ``None``, in which case the power is not overridden. + r_jb : str, optional + Override component r_jb value. Default is ``None``, in which case the resistance is not overridden. + r_jc : str, optional + Override component r_jc value. Default is ``None``, in which case the resistance is not overridden. + height : str, optional + Override component height value. Default is ``None``, in which case the height is not overridden. + + Returns + ------- + bool + ``True`` if successful, ``False`` otherwise. + """ + if self._app.settings.aedt_version < "2024.2": + self._app.logger.error( + "This method is available only with AEDT 2024 R2 or later. Use 'override_instance()' method instead." + ) + return False + return self._override_common( # pragma : no cover + "definitionOverridesMap", + package=package, + part=part, + filter_component=filter_component, + power=power, + r_jb=r_jb, + r_jc=r_jc, + height=height, + ) + + @pyaedt_function_handler() + @disable_auto_update + def override_instance( + self, reference_designator, filter_component=False, power=None, r_jb=None, r_jc=None, height=None + ): + """Set instance override. + + Parameters + ---------- + reference_designator : str + Reference designator of the instance to override. + filter_component : bool, optional + Whether to filter out the component. The default is ``False``. + power : str, optional + Override component power. The default is ``None``, in which case the power is not overridden. + r_jb : str, optional + Override component r_jb value. The default is ``None``, in which case the resistance is not overridden. + r_jc : str, optional + Override component r_jc value. The default is ``None``, in which case the resistance is not overridden. + height : str, optional + Override component height value. The default is ``None``, in which case the height is not overridden. + + Returns + ------- + bool + ``True`` if successful, ``False`` otherwise. + """ + return self._override_common( + "instanceOverridesMap", + reference_designator=reference_designator, + filter_component=filter_component, + power=power, + r_jb=r_jb, + r_jc=r_jc, + height=height, + ) diff --git a/src/ansys/aedt/core/modules/boundary/maxwell_boundary.py b/src/ansys/aedt/core/modules/boundary/maxwell_boundary.py new file mode 100644 index 00000000000..28feac03a5c --- /dev/null +++ b/src/ansys/aedt/core/modules/boundary/maxwell_boundary.py @@ -0,0 +1,352 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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 ansys.aedt.core.generic.data_handlers import _dict2arg +from ansys.aedt.core.generic.general_methods import generate_unique_name +from ansys.aedt.core.generic.general_methods import pyaedt_function_handler +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode +from ansys.aedt.core.modules.boundary.common import BoundaryCommon +from ansys.aedt.core.modules.boundary.common import BoundaryProps + + +class MaxwellParameters(BoundaryCommon, BinaryTreeNode): + """Manages parameters data and execution. + + Parameters + ---------- + app : :class:`ansys.aedt.core.maxwell.Maxwell3d`, :class:`ansys.aedt.core.maxwell.Maxwell2d` + Either ``Maxwell3d`` or ``Maxwell2d`` application. + name : str + Name of the boundary. + props : dict, optional + Properties of the boundary. + boundarytype : str, optional + Type of the boundary. + + Examples + -------- + + Create a matrix in Maxwell3D return a ``ansys.aedt.core.modules.boundary.BoundaryObject`` + + >>> from ansys.aedt.core import Maxwell2d + >>> maxwell_2d = Maxwell2d() + >>> coil1 = maxwell_2d.modeler.create_rectangle([8.5,1.5, 0],[8, 3],True,"Coil_1","vacuum") + >>> coil2 = maxwell_2d.modeler.create_rectangle([8.5,1.5, 0],[8, 3],True,"Coil_2","vacuum") + >>> maxwell_2d.assign_matrix(["Coil_1", "Coil_2"]) + """ + + def __init__(self, app, name, props=None, boundarytype=None): + self.auto_update = False + self._app = app + self._name = name + self.__props = BoundaryProps(self, props) if props else {} + self.type = boundarytype + self._boundary_name = self.name + self.auto_update = True + self.__reduced_matrices = None + self.matrix_assignment = None + if self._child_object: + BinaryTreeNode.__init__(self, self.name, self._child_object, False) + + @property + def reduced_matrices(self): + """List of reduced matrix groups for the parent matrix. + + Returns + ------- + dict + Dictionary of reduced matrices where the key is the name of the parent matrix + and the values are in a list of reduced matrix groups. + """ + if self._app.solution_type == "EddyCurrent": + self.__reduced_matrices = {} + cc = self._app.odesign.GetChildObject("Parameters") + parents = cc.GetChildNames() + if self.name in parents: + parent_object = self._app.odesign.GetChildObject("Parameters").GetChildObject(self.name) + parent_type = parent_object.GetPropValue("Type") + if parent_type == "Matrix": + self.matrix_assignment = parent_object.GetPropValue("Selection").split(",") + child_names = parent_object.GetChildNames() + self.__reduced_matrices = [] + for r in child_names: + self.__reduced_matrices.append(MaxwellMatrix(self._app, self.name, r)) + return self.__reduced_matrices + + @property + def _child_object(self): + + cc = self._app.odesign.GetChildObject("Parameters") + child_object = None + if self.name in cc.GetChildNames(): + child_object = self._app.odesign.GetChildObject("Parameters").GetChildObject(self.name) + elif self.name in self._app.odesign.GetChildObject("Parameters").GetChildNames(): + child_object = self._app.odesign.GetChildObject("Parameters").GetChildObject(self.name) + + return child_object + + @property + def props(self): + """Maxwell parameter data. + + Returns + ------- + :class:BoundaryProps + """ + if self.__props: + return self.__props + props = self._get_boundary_data(self.name) + + if props: + self.__props = BoundaryProps(self, props[0]) + self._type = props[1] + return self.__props + + @property + def name(self): + """Boundary name.""" + return self._name + + @name.setter + def name(self, value): + self._name = value + self.update() + + @pyaedt_function_handler() + def _get_args(self, props=None): + """Retrieve arguments. + + Parameters + ---------- + props : + The default is ``None``. + + Returns + ------- + list + List of boundary properties. + + """ + if props is None: + props = self.props + arg = ["NAME:" + self.name] + _dict2arg(props, arg) + return arg + + @pyaedt_function_handler() + def create(self): + """Create a boundary. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + if self.type == "Matrix": + self._app.o_maxwell_parameters.AssignMatrix(self._get_args()) + elif self.type == "Torque": + self._app.o_maxwell_parameters.AssignTorque(self._get_args()) + elif self.type == "Force": + self._app.o_maxwell_parameters.AssignForce(self._get_args()) + elif self.type == "LayoutForce": + self._app.o_maxwell_parameters.AssignLayoutForce(self._get_args()) + else: + return False + if self._child_object: + BinaryTreeNode.__init__(self, self.name, self._child_object, False) + return True + + @pyaedt_function_handler() + def update(self): + """Update the boundary. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + """ + if self.type == "Matrix": + self._app.o_maxwell_parameters.EditMatrix(self._boundary_name, self._get_args()) + elif self.type == "Force": + self._app.o_maxwell_parameters.EditForce(self._boundary_name, self._get_args()) + elif self.type == "Torque": + self._app.o_maxwell_parameters.EditTorque(self._boundary_name, self._get_args()) + else: + return False + self._boundary_name = self.name + return True + + @pyaedt_function_handler() + def _create_matrix_reduction(self, red_type, sources, matrix_name=None, join_name=None): + if not self._app.solution_type == "EddyCurrent": + self._app.logger.error("Matrix reduction is possible only in Eddy current solvers.") + return False, False + if not matrix_name: + matrix_name = generate_unique_name("ReducedMatrix", n=3) + if not join_name: + join_name = generate_unique_name("Join" + red_type, n=3) + try: + self._app.o_maxwell_parameters.AddReduceOp( + self.name, + matrix_name, + ["NAME:" + join_name, "Type:=", "Join in " + red_type, "Sources:=", ",".join(sources)], + ) + return matrix_name, join_name + except Exception: + self._app.logger.error("Failed to create Matrix Reduction") + return False, False + + @pyaedt_function_handler() + def join_series(self, sources, matrix_name=None, join_name=None): + """ + + Parameters + ---------- + sources : list + Sources to be included in matrix reduction. + matrix_name : str, optional + name of the string to create. + join_name : str, optional + Name of the Join operation. + + Returns + ------- + (str, str) + Matrix name and Joint name. + + """ + return self._create_matrix_reduction( + red_type="Series", sources=sources, matrix_name=matrix_name, join_name=join_name + ) + + @pyaedt_function_handler() + def join_parallel(self, sources, matrix_name=None, join_name=None): + """ + + Parameters + ---------- + sources : list + Sources to be included in matrix reduction. + matrix_name : str, optional + name of the string to create. + join_name : str, optional + Name of the Join operation. + + Returns + ------- + (str, str) + Matrix name and Joint name. + + """ + return self._create_matrix_reduction( + red_type="Parallel", sources=sources, matrix_name=matrix_name, join_name=join_name + ) + + +class MaxwellMatrix(object): + def __init__(self, app, parent_name, reduced_name): + self._app = app + self.parent_matrix = parent_name + self.name = reduced_name + self.__sources = None + + @property + def sources(self): + """List of matrix sources.""" + if self._app.solution_type == "EddyCurrent": + sources = ( + self._app.odesign.GetChildObject("Parameters") + .GetChildObject(self.parent_matrix) + .GetChildObject(self.name) + .GetChildNames() + ) + self.__sources = {} + for s in sources: + excitations = ( + self._app.odesign.GetChildObject("Parameters") + .GetChildObject(self.parent_matrix) + .GetChildObject(self.name) + .GetChildObject(s) + .GetPropValue("Source") + ) + self.__sources[s] = excitations + return self.__sources + + @pyaedt_function_handler() + def update(self, old_source, source_type, new_source=None, new_excitations=None): + """Update the reduced matrix. + + Parameters + ---------- + old_source : str + Original name of the source to update. + source_type : str + Source type, which can be ``Series`` or ``Parallel``. + new_source : str, optional + New name of the source to update. + The default value is the old source name. + new_excitations : str, optional + List of excitations to include in the matrix reduction. + The default values are excitations included in the source to update. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + """ + if old_source not in self.sources: + self._app.logger.error("Source does not exist.") + return False + else: + new_excitations = self.sources[old_source] if not new_excitations else new_excitations + if source_type.lower() not in ["series", "parallel"]: + self._app.logger.error("Join type not valid.") + return False + if not new_source: + new_source = old_source + args = ["NAME:" + new_source, "Type:=", "Join in " + source_type, "Sources:=", new_excitations] + self._app.o_maxwell_parameters.EditReduceOp(self.parent_matrix, self.name, old_source, args) + return True + + @pyaedt_function_handler() + def delete(self, source): + """Delete a specified source in a reduced matrix. + + Parameters + ---------- + source : string + Name of the source to delete. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + """ + if source not in self.sources: + self._app.logger.error("Invalid source name.") + return False + self._app.o_maxwell_parameters.DeleteReduceOp(self.parent_matrix, self.name, source) + return True diff --git a/src/ansys/aedt/core/modules/boundary/q3d_boundary.py b/src/ansys/aedt/core/modules/boundary/q3d_boundary.py new file mode 100644 index 00000000000..5745ca23a28 --- /dev/null +++ b/src/ansys/aedt/core/modules/boundary/q3d_boundary.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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 ansys.aedt.core.generic.constants import CATEGORIESQ3D +from ansys.aedt.core.generic.general_methods import filter_tuple +from ansys.aedt.core.generic.general_methods import generate_unique_name +from ansys.aedt.core.generic.general_methods import pyaedt_function_handler + + +class Matrix(object): + """Manages Matrix in Q3d and Q2d Projects. + + Examples + -------- + + + """ + + def __init__(self, app, name, operations=None): + self._app = app + self.omatrix = self._app.omatrix + self.name = name + self._sources = [] + if operations: + if isinstance(operations, list): + self._operations = operations + else: + self._operations = [operations] + self.CATEGORIES = CATEGORIESQ3D() + + @pyaedt_function_handler() + def sources(self, is_gc_sources=True): + """List of matrix sources. + + Parameters + ---------- + is_gc_sources : bool, + In Q3d, define if to return GC sources or RL sources. Default `True`. + + Returns + ------- + List + """ + if self.name in list(self._app.omatrix.ListReduceMatrixes()): + if self._app.design_type == "Q3D Extractor": + self._sources = list(self._app.omatrix.ListReduceMatrixReducedSources(self.name, is_gc_sources)) + else: + self._sources = list(self._app.omatrix.ListReduceMatrixReducedSources(self.name)) + return self._sources + + @pyaedt_function_handler() + def get_sources_for_plot( + self, + get_self_terms=True, + get_mutual_terms=True, + first_element_filter=None, + second_element_filter=None, + category="C", + ): + """Return a list of source of specified matrix ready to be used in plot reports. + + Parameters + ---------- + get_self_terms : bool + Either if self terms have to be returned or not. + get_mutual_terms : bool + Either if mutual terms have to be returned or not. + first_element_filter : str, optional + Filter to apply to first element of equation. It accepts `*` and `?` as special characters. + second_element_filter : str, optional + Filter to apply to second element of equation. It accepts `*` and `?` as special characters. + category : str + Plot category name as in the report. Eg. "C" is category Capacitance. + Matrix `CATEGORIES` property can be used to map available categories. + + Returns + ------- + list + + Examples + -------- + >>> from ansys.aedt.core import Q3d + >>> q3d = Q3d(project_path) + >>> q3d.matrices[0].get_sources_for_plot(first_element_filter="Bo?1", + ... second_element_filter="GND*", category="DCL") + """ + if not first_element_filter: + first_element_filter = "*" + if not second_element_filter: + second_element_filter = "*" + is_cg = False + if category in [self.CATEGORIES.Q3D.C, self.CATEGORIES.Q3D.G]: + is_cg = True + list_output = [] + if get_self_terms: + for el in self.sources(is_gc_sources=is_cg): + value = f"{category}({el},{el})" + if filter_tuple(value, first_element_filter, second_element_filter): + list_output.append(value) + if get_mutual_terms: + for el1 in self.sources(is_gc_sources=is_cg): + for el2 in self.sources(is_gc_sources=is_cg): + if el1 != el2: + value = f"{category}({el1},{el2})" + if filter_tuple(value, first_element_filter, second_element_filter): + list_output.append(value) + return list_output + + @property + def operations(self): + """List of matrix operations. + + Returns + ------- + List + """ + if self.name in list(self._app.omatrix.ListReduceMatrixes()): + self._operations = self._app.omatrix.ListReduceMatrixOperations(self.name) + return self._operations + + @pyaedt_function_handler() + def create( + self, + source_names=None, + new_net_name=None, + new_source_name=None, + new_sink_name=None, + ): + """Create a new matrix. + + Parameters + ---------- + source_names : str, list + List or str containing the content of the matrix reduction (eg. source name). + new_net_name : str, optional + Name of the new net. The default is ``None``. + new_source_name : str, optional + Name of the new source. The default is ``None``. + new_sink_name : str, optional + Name of the new sink. The default is ``None``. + + Returns + ------- + bool + `True` if succeeded. + """ + if not isinstance(source_names, list) and source_names: + source_names = [source_names] + + command = self._write_command(source_names, new_net_name, new_source_name, new_sink_name) + self.omatrix.InsertRM(self.name, command) + return True + + @pyaedt_function_handler() + def delete(self): + """Delete current matrix. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + """ + self.omatrix.DeleteRM(self.name) + for el in self._app.matrices: + if el.name == self.name: + self._app.matrices.remove(el) + return True + + @pyaedt_function_handler() + def add_operation( + self, + operation_type, + source_names=None, + new_net_name=None, + new_source_name=None, + new_sink_name=None, + ): + """Add a new operation to existing matrix. + + Parameters + ---------- + operation_type : str + Operation to perform + source_names : str, list + List or str containing the content of the matrix reduction (eg. source name). + new_net_name : str, optional + Name of the new net. The default is ``None``. + new_source_name : str, optional + Name of the new source. The default is ``None``. + new_sink_name : str, optional + Name of the new sink. The default is ``None``. + + Returns + ------- + bool + `True` if succeeded. + """ + self._operations.append(operation_type) + if not isinstance(source_names, list) and source_names: + source_names = [source_names] + + if not new_net_name: + new_net_name = generate_unique_name("Net") + + if not new_source_name: + new_source_name = generate_unique_name("Source") + + if not new_sink_name: + new_sink_name = generate_unique_name("Sink") + + command = self._write_command(source_names, new_net_name, new_source_name, new_sink_name) + self.omatrix.RMAddOp(self.name, command) + return True + + @pyaedt_function_handler() + def _write_command(self, source_names, new_name, new_source, new_sink): + if self._operations[-1] == "JoinSeries": + command = f"""{self._operations[-1]}('{new_name}', '{"', '".join(source_names)}')""" + elif self._operations[-1] == "JoinParallel": + command = ( + f"""{self._operations[-1]}('{new_name}', '{new_source}', '{new_sink}', '{"', '".join(source_names)}')""" + ) + elif self._operations[-1] == "JoinSelectedTerminals": + command = f"""{self._operations[-1]}('', '{"', '".join(source_names)}')""" + elif self._operations[-1] == "FloatInfinity": + command = "FloatInfinity()" + elif self._operations[-1] == "AddGround": + command = f"""{self._operations[-1]}(SelectionArray[{len(source_names)}: '{"', '".join(source_names)}'], + OverrideInfo())""" + elif ( + self._operations[-1] == "SetReferenceGround" + or self._operations[-1] == "SetReferenceGround" + or self._operations[-1] == "Float" + ): + command = f"""{self._operations[-1]}(SelectionArray[{len(source_names)}: '{"', '".join(source_names)}'], + OverrideInfo())""" + elif self._operations[-1] == "Parallel" or self._operations[-1] == "DiffPair": + id_ = 0 + for el in self._app.boundaries: + if el.name == source_names[0]: + id_ = self._app.modeler[el.props["Objects"][0]].id + command = f"""{self._operations[-1]}(SelectionArray[{len(source_names)}: '{"', '".join(source_names)}'], + OverrideInfo({id_}, '{new_name}'))""" + else: + command = f"""{self._operations[-1]}('{"', '".join(source_names)}')""" + return command diff --git a/src/ansys/aedt/core/modules/mesh.py b/src/ansys/aedt/core/modules/mesh.py index 214f081d517..5121f2857d9 100644 --- a/src/ansys/aedt/core/modules/mesh.py +++ b/src/ansys/aedt/core/modules/mesh.py @@ -36,6 +36,7 @@ from ansys.aedt.core.generic.general_methods import pyaedt_function_handler from ansys.aedt.core.generic.general_methods import settings from ansys.aedt.core.generic.load_aedt_file import load_keyword_in_aedt_file +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode from ansys.aedt.core.modeler.cad.elements_3d import EdgePrimitive from ansys.aedt.core.modeler.cad.elements_3d import FacePrimitive from ansys.aedt.core.modeler.cad.elements_3d import VertexPrimitive @@ -106,7 +107,7 @@ def _setitem_without_update(self, key, value): dict.__setitem__(self, key, value) -class MeshOperation(object): +class MeshOperation(BinaryTreeNode): """MeshOperation class. Parameters @@ -118,16 +119,70 @@ class MeshOperation(object): def __init__(self, mesh, name, props, meshoptype): self._mesh = mesh self._app = self._mesh._app - self.props = MeshProps(self, props) - self.type = meshoptype + self._legacy_props = None + if props is not None: + self._legacy_props = MeshProps(self, props) + self._type = meshoptype self._name = name self.auto_update = True + child_object = self._app.get_oo_object(self._app.odesign, f"Mesh/{self._name}") + + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) + + @property + def type(self): + if not self._type: + self._type = self.props.get("Type", None) + return self._type + + @property + def props(self): + if not self._legacy_props: + props = {} + for k, v in self.properties.items(): + props[k] = v + if "Assignment" in props: + assignment = props["Assignment"] + if "Face_" in assignment: + props["Faces"] = [ + int(i.replace("Face_", "")) for i in assignment.split("(")[1].split(")")[0].split(",") + ] + elif "Edge_" in assignment: + props["Edges"] = [ + int(i.replace("Edge_", "")) for i in assignment.split("(")[1].split(")")[0].split(",") + ] + else: + props["Objects"] = assignment + else: + props["Objects"] = [] + props["Faces"] = [] + props["Edges"] = [] + assigned_id = self._mesh.omeshmodule.GetMeshOpAssignment(self.name) + for comp_id in assigned_id: + if int(comp_id) in self._app.modeler.objects.keys(): + props["Objects"].append(self._app.oeditor.GetObjectNameByID(comp_id)) + continue + for comp in self._app.modeler.object_list: + faces = comp.faces + face_ids = [face.id for face in faces] + if int(comp_id) in face_ids: + props["Faces"].append(int(comp_id)) + continue + edges = comp.edges + edge_ids = [edge.id for edge in edges] + if int(comp_id) in edge_ids: + props["Edges"].append(int(comp_id)) + continue + self._legacy_props = MeshProps(self, props) + return self._legacy_props + @pyaedt_function_handler() def _get_args(self): """Retrieve arguments.""" props = self.props - arg = ["NAME:" + self.name] + arg = ["NAME:" + self._name] _dict2arg(props, arg) return arg @@ -141,14 +196,17 @@ def name(self): Name of the mesh operation. """ + try: + self._name = self.properties["Name"] + except KeyError: + pass return self._name @name.setter def name(self, meshop_name): - if meshop_name not in self._mesh._app.odesign.GetChildObject("Mesh").GetChildNames(): - self._mesh._app.odesign.GetChildObject("Mesh").GetChildObject(self.name).SetPropValue("Name", meshop_name) - self._name = meshop_name - else: + try: + self.properties["Name"] = meshop_name + except KeyError: self._mesh.logger.warning("Name %s already assigned in the design", meshop_name) @pyaedt_function_handler() @@ -187,6 +245,10 @@ def create(self): self._mesh.omeshmodule.AssignCylindricalGapOp(self._get_args()) else: return False + child_object = self._app.get_oo_object(self._app.odesign, f"Mesh/{self._name}") + + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) return True @pyaedt_function_handler() @@ -355,13 +417,16 @@ def __init__(self, app): app.logger.reset_timer() self._app = app self._odesign = self._app.odesign - self.modeler = self._app.modeler self.logger = self._app.logger self.id = 0 self._meshoperations = None self._globalmesh = None app.logger.info_timer("Mesh class has been initialized!") + @property + def _modeler(self): + return self._app.modeler + @pyaedt_function_handler() def __getitem__(self, part_id): """Get the object ``Mesh`` for a given mesh operation name. @@ -516,47 +581,8 @@ def _get_design_global_mesh(self): def _get_design_mesh_operations(self): """ """ meshops = [] - try: - for ds in self.meshoperation_names: - props = {} - design_mesh = self._app.odesign.GetChildObject("Mesh") - for i in design_mesh.GetChildObject(ds).GetPropNames(): - props[i] = design_mesh.GetChildObject(ds).GetPropValue(i) - if self._app._desktop.GetVersion()[0:6] < "2023.1": - if self._app.design_properties: - props_parsed = self._app.design_properties["MeshSetup"]["MeshOperations"][ds] - if "Edges" in props_parsed.keys(): - props["Edges"] = props_parsed["Edges"] - if "Faces" in props_parsed.keys(): - props["Faces"] = props_parsed["Faces"] - if "Objects" in props_parsed.keys(): - props["Objects"] = [] - for comp in props_parsed["Objects"]: - props["Objects"].append(comp) - else: - props["Objects"] = [] - props["Faces"] = [] - props["Edges"] = [] - assigned_id = self.omeshmodule.GetMeshOpAssignment(ds) - for comp_id in assigned_id: - if int(comp_id) in self._app.modeler.objects.keys(): - props["Objects"].append(self._app.modeler.oeditor.GetObjectNameByID(comp_id)) - continue - for comp in self._app.modeler.object_list: - faces = comp.faces - face_ids = [face.id for face in faces] - if int(comp_id) in face_ids: - props["Faces"].append(int(comp_id)) - continue - edges = comp.edges - edge_ids = [edge.id for edge in edges] - if int(comp_id) in edge_ids: - props["Edges"].append(int(comp_id)) - continue - - meshops.append(MeshOperation(self, ds, props, props["Type"])) - except Exception: - self.logger.debug("An error occurred while accessing design mesh operations.") # pragma: no cover + for ds in self.meshoperation_names: + meshops.append(MeshOperation(self, ds, {}, "")) return meshops @pyaedt_function_handler(names="assignment", meshop_name="name") @@ -591,7 +617,7 @@ def assign_surface_mesh(self, assignment, level, name=None): >>> o = hfss.modeler.create_cylinder(0,[0, 0, 0],3,20,0) >>> surface = hfss.mesh.assign_surface_mesh(o.id,3,"Surface") """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if name: for m in self.meshoperations: if name == m.name: @@ -656,7 +682,7 @@ def assign_surface_mesh_manual( >>> o = hfss.modeler.create_cylinder(0,[0, 0, 0],3,20,0) >>> surface = hfss.mesh.assign_surface_mesh_manual(o.id,1e-6,aspect_ratio=3,name="Surface_Manual") """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if name: for m in self.meshoperations: if name == m.name: @@ -733,7 +759,7 @@ def assign_model_resolution(self, assignment, defeature_length=None, name=None): >>> o = hfss.modeler.create_cylinder(0,[0, 0, 0],3,20,0) >>> surface = hfss.mesh.assign_model_resolution(o,1e-4,"ModelRes1") """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if name: for m in self.meshoperations: if name == m.name: @@ -984,7 +1010,7 @@ def assign_length_mesh(self, assignment, inside_selection=True, maximum_length=1 >>> oModule.AssignLengthOp """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if name: for m in self.meshoperations: if name == m.name: @@ -996,7 +1022,7 @@ def assign_length_mesh(self, assignment, inside_selection=True, maximum_length=1 restrictlength = False else: restrictlength = True - length = self.modeler.modeler_variable(maximum_length) + length = self._modeler.modeler_variable(maximum_length) if maximum_elements is None: restrictel = False @@ -1085,7 +1111,7 @@ def assign_skin_depth( >>> oModule.AssignSkinDepthOp """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if self._app.design_type not in ["HFSS", "Maxwell 3D", "Maxwell 2D"]: raise MethodNotSupportedError @@ -1157,7 +1183,7 @@ def assign_curvilinear_elements(self, assignment, enable=True, name=None): >>> oModule.AssignApplyCurvlinearElementsOp """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if self._app.design_type != "HFSS" and self._app.design_type != "Maxwell 3D": raise MethodNotSupportedError @@ -1210,7 +1236,7 @@ def assign_curvature_extraction(self, assignment, disabled_for_faceted=True, nam >>> oModule.AssignCurvatureExtractionOp """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if self._app.solution_type != "SBR+": raise MethodNotSupportedError @@ -1264,7 +1290,7 @@ def assign_rotational_layer(self, assignment, layers_number=3, total_thickness=" >>> oModule.AssignRotationalLayerOp """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if self._app.design_type != "Maxwell 3D": raise MethodNotSupportedError @@ -1313,7 +1339,7 @@ def assign_edge_cut(self, assignment, layer_thickness="1mm", name=None): >>> oModule.AssignRotationalLayerOp """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if self._app.design_type != "Maxwell 3D": raise MethodNotSupportedError @@ -1365,7 +1391,7 @@ def assign_density_control( >>> oModule.AssignDensityControlOp """ - assignment = self.modeler.convert_to_selections(assignment, True) + assignment = self._modeler.convert_to_selections(assignment, True) if self._app.design_type != "Maxwell 3D": raise MethodNotSupportedError @@ -1462,7 +1488,7 @@ def assign_cylindrical_gap( if self._app.design_type != "Maxwell 2D" and self._app.design_type != "Maxwell 3D": raise MethodNotSupportedError - entity = self.modeler.convert_to_selections(entity, True) + entity = self._modeler.convert_to_selections(entity, True) if len(entity) > 1: self.logger.error("Cylindrical gap treatment cannot be assigned to multiple objects.") raise ValueError diff --git a/src/ansys/aedt/core/modules/mesh_icepak.py b/src/ansys/aedt/core/modules/mesh_icepak.py index c58b9778459..c66d6f55f2b 100644 --- a/src/ansys/aedt/core/modules/mesh_icepak.py +++ b/src/ansys/aedt/core/modules/mesh_icepak.py @@ -32,6 +32,7 @@ from ansys.aedt.core.generic.general_methods import pyaedt_function_handler from ansys.aedt.core.generic.settings import settings from ansys.aedt.core.modeler.cad.components_3d import UserDefinedComponent +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode from ansys.aedt.core.modeler.cad.object_3d import Object3d from ansys.aedt.core.modules.mesh import MeshOperation from ansys.aedt.core.modules.mesh import meshers @@ -312,7 +313,7 @@ def _set_region_data(self, value, direction=None, padding_type=True): region = self.object create_region = region.history() set_type = ["Data", "Type"][int(padding_type)] - create_region.props[f"{direction} Padding {set_type}"] = value + create_region.properties[f"{direction} Padding {set_type}"] = value def _update_region_data(self): region = self.object @@ -320,9 +321,9 @@ def _update_region_data(self): self._padding_type = [] self._padding_value = [] for padding_direction in ["+X", "-X", "+Y", "-Y", "+Z", "-Z"]: - self._padding_type.append(create_region.props[f"{padding_direction} Padding Type"]) - self._padding_value.append(create_region.props[f"{padding_direction} Padding Data"]) - self._coordinate_system = create_region.props["Coordinate System"] + self._padding_type.append(create_region.properties[f"{padding_direction} Padding Type"]) + self._padding_value.append(create_region.properties[f"{padding_direction} Padding Data"]) + self._coordinate_system = create_region.properties["Coordinate System"] def _get_region_data(self, direction=None, padding_type=True): self._update_region_data() @@ -419,10 +420,8 @@ def parts(self): Dictionary with the part names as keys and ::class::modeler.cad.object_3d.Object3d as values. """ if self.object: - return { - obj_name: self._app.modeler[obj_name] - for obj_name in self.object.history().props["Part Names"].split(",") - } + history = self.object.history().properties + return {obj_name: self._app.modeler[obj_name] for obj_name in history["Part Names"].split(",")} else: return {} @@ -627,6 +626,10 @@ def __init__(self, units, app, name): self._name = name self._model_units = units self._app = app + child_object = self._app.get_oo_object(self._app.odesign, f"Mesh/{self._name}") + + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) @abstractmethod def update(self): @@ -724,6 +727,10 @@ def create(self): self.delete() self.global_region = Region(self._app) self.global_region.create(self.padding_types, self.padding_values) + child_object = self._app.get_oo_object(self._app.odesign, f"Mesh/{self._name}") + + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) class MeshRegion(MeshRegionCommon): @@ -881,16 +888,18 @@ def assignment(self): for sr in sub_regions: p1 = [] p2 = [] - if "Part Names" in self._app.modeler[sr].history().props: - p1 = self._app.modeler[sr].history().props.get("Part Names", None) + history = self._app.modeler[sr].history() + history_props = history.properties + if "Part Names" in history_props: + p1 = history_props.get("Part Names", None) if not isinstance(p1, list): p1 = [p1] - elif "Submodel Names" in self._app.modeler[sr].history().props: - p2 = self._app.modeler[sr].history().props.get("Submodel Names", None) + elif "Submodel Names" in history_props: + p2 = history_props.get("Submodel Names", None) if not isinstance(p2, list): p2 = [p2] p1 += p2 - if "CreateSubRegion" == self._app.modeler[sr].history().command and all(p in p1 for p in parts): + if "CreateSubRegion" == history.command and all(p in p1 for p in parts): self._assignment.name = sr return self._assignment elif isinstance(self._assignment, list): @@ -931,6 +940,11 @@ def create(self): self._app.mesh.meshregions.append(self) self._app.modeler.refresh_all_ids() self._assignment = self.assignment + child_object = self._app.get_oo_object(self._app.odesign, f"Mesh/{self._name}") + + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) + return True # backward compatibility @@ -1016,13 +1030,10 @@ def __init__(self, app): self._app = app self._odesign = self._app._odesign - self.modeler = self._app.modeler design_type = self._odesign.GetDesignType() if design_type not in meshers: raise RuntimeError(f"Invalid design type {design_type}") # pragma: no cover self.id = 0 - self._oeditor = self.modeler.oeditor - self._model_units = self.modeler.model_units self.meshoperations = self._get_design_mesh_operations() self.meshregions = self._get_design_mesh_regions() try: @@ -1031,6 +1042,18 @@ def __init__(self, app): self.global_mesh_region = GlobalMeshRegion(app) self._priorities_args = [] + @property + def _modeler(self): + return self._app.modeler + + @property + def _oeditor(self): + return self._app.oeditor + + @property + def _model_units(self): + return self._modeler.model_units + @property def meshregions_dict(self): """ @@ -1063,7 +1086,7 @@ def omeshmodule(self): @property def boundingdimension(self): """Bounding dimension.""" - return self.modeler.get_bounding_dimension() + return self._modeler.get_bounding_dimension() @pyaedt_function_handler() def _get_design_mesh_operations(self): @@ -1339,12 +1362,12 @@ def assign_priorities(self, assignment): raise AttributeError("``assignment`` input must be a list of lists.") props = {"PriorityListParameters": []} self._app.logger.info("Parsing input objects information for priority assignment. This operation can take time") - udc = self.modeler.user_defined_components + udc = self._modeler.user_defined_components udc._parse_objs() for level, objects in enumerate(assignment): level += 1 if isinstance(objects[0], str): - objects = [self.modeler.objects_by_name.get(o, udc.get(o, None)) for o in objects] + objects = [self._modeler.objects_by_name.get(o, udc.get(o, None)) for o in objects] obj_3d = [ o for o in objects @@ -1387,7 +1410,7 @@ def assign_priorities(self, assignment): self._app.logger.info("Input objects information for priority assignment completed.") args = [] _dict2arg(props, args) - self.modeler.oeditor.UpdatePriorityList(args[0]) + self._modeler.oeditor.UpdatePriorityList(args[0]) return True @pyaedt_function_handler(obj_list="assignment", comp_name="component") @@ -1453,7 +1476,7 @@ def add_priority(self, entity_type, assignment=None, component=None, priority=3) self._priorities_args.append(prio) args += self._priorities_args elif entity_type == 2: - o = self.modeler.user_defined_components[component] + o = self._modeler.user_defined_components[component] if (all(part.is3d for part in o.parts.values()) is False) and ( any(part.is3d for part in o.parts.values()) is True ): @@ -1509,8 +1532,8 @@ def add_priority(self, entity_type, assignment=None, component=None, priority=3) self._priorities_args.append(prio_2d) args += self._priorities_args - self.modeler.oeditor.UpdatePriorityList(["NAME:UpdatePriorityListData"]) - self.modeler.oeditor.UpdatePriorityList(args) + self._modeler.oeditor.UpdatePriorityList(["NAME:UpdatePriorityListData"]) + self._modeler.oeditor.UpdatePriorityList(args) return True @pyaedt_function_handler(objectlist="assignment") @@ -1540,15 +1563,15 @@ def assign_mesh_region(self, assignment=None, level=5, name=None, **kwargs): if not name: name = generate_unique_name("MeshRegion") if assignment is None: - assignment = [i for i in self.modeler.object_names] + assignment = [i for i in self._modeler.object_names] meshregion = MeshRegion(self._app, assignment, name) meshregion.manual_settings = False meshregion.settings["MeshRegionResolution"] = level - all_objs = [i for i in self.modeler.object_names] + all_objs = [i for i in self._modeler.object_names] created = bool(meshregion) if created: if settings.aedt_version < "2024.1": - objectlist2 = self.modeler.object_names + objectlist2 = self._modeler.object_names added_obj = [i for i in objectlist2 if i not in all_objs] if not added_obj: added_obj = [i for i in objectlist2 if i not in all_objs or i in assignment] diff --git a/src/ansys/aedt/core/modules/solve_setup.py b/src/ansys/aedt/core/modules/solve_setup.py index 37527d4d2a9..8e0d590accc 100644 --- a/src/ansys/aedt/core/modules/solve_setup.py +++ b/src/ansys/aedt/core/modules/solve_setup.py @@ -43,6 +43,7 @@ from ansys.aedt.core.generic.general_methods import generate_unique_name from ansys.aedt.core.generic.general_methods import pyaedt_function_handler from ansys.aedt.core.generic.settings import settings +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode from ansys.aedt.core.modules.setup_templates import SetupKeys from ansys.aedt.core.modules.solve_sweeps import SetupProps from ansys.aedt.core.modules.solve_sweeps import SweepHFSS @@ -51,7 +52,7 @@ from ansys.aedt.core.modules.solve_sweeps import identify_setup -class CommonSetup(PropsManager, object): +class CommonSetup(PropsManager, BinaryTreeNode): def __init__(self, app, solution_type, name="MySetupAuto", is_new_setup=True): self.auto_update = False self._app = None @@ -65,10 +66,45 @@ def __init__(self, app, solution_type, name="MySetupAuto", is_new_setup=True): else: self.setuptype = self.p_app.design_solutions._solution_options[solution_type]["default_setup"] self._name = name - self.props = {} - self.sweeps = [] - self._init_props(is_new_setup) + self._legacy_props = {} + self._sweeps = [] + self._is_new_setup = is_new_setup + # self._init_props(is_new_setup) self.auto_update = True + child_object = self._app.get_oo_object(self._app.odesign, f"Analysis/{self._name}") + + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) + + @property + def sweeps(self): + if self._sweeps: + return self._sweeps + try: + setups_data = self.p_app.design_properties["AnalysisSetup"]["SolveSetups"] + if self.name in setups_data: + setup_data = setups_data[self.name] + if "Sweeps" in setup_data and self.setuptype not in [ + 0, + 7, + ]: # 0 and 7 represent setup HFSSDrivenAuto + if self.setuptype <= 4: + app = setup_data["Sweeps"] + app.pop("NextUniqueID", None) + app.pop("MoveBackForward", None) + app.pop("MoveBackwards", None) + for el in app: + if isinstance(app[el], dict): + self._sweeps.append(SweepHFSS(self, el, props=app[el])) + else: + app = setup_data["Sweeps"] + for el in app: + if isinstance(app[el], dict): + self._sweeps.append(SweepMatrix(self, el, props=app[el])) + setup_data.pop("Sweeps", None) + except (TypeError, KeyError): + pass + return self._sweeps @property def default_intrinsics(self): @@ -170,38 +206,32 @@ def analyze( blocking=blocking, ) - @pyaedt_function_handler() - def _init_props(self, is_new_setup=False): - if is_new_setup: + @property + def props(self): + if self._legacy_props: + return self._legacy_props + if self._is_new_setup: setup_template = SetupKeys.get_setup_templates()[self.setuptype] - self.props = SetupProps(self, setup_template) + setup_template["Name"] = self._name + self._legacy_props = SetupProps(self, setup_template) + self._is_new_setup = False else: try: if "AnalysisSetup" in self.p_app.design_properties.keys(): setups_data = self.p_app.design_properties["AnalysisSetup"]["SolveSetups"] if self.name in setups_data: setup_data = setups_data[self.name] - if "Sweeps" in setup_data and self.setuptype != 0: # 0 represents setup HFSSDrivenAuto - if self.setuptype <= 4: - app = setup_data["Sweeps"] - app.pop("NextUniqueID", None) - app.pop("MoveBackForward", None) - app.pop("MoveBackwards", None) - for el in app: - if isinstance(app[el], dict): - self.sweeps.append(SweepHFSS(self, el, props=app[el])) - else: - app = setup_data["Sweeps"] - for el in app: - if isinstance(app[el], dict): - self.sweeps.append(SweepMatrix(self, el, props=app[el])) - setup_data.pop("Sweeps", None) - self.props = SetupProps(self, setup_data) + self._legacy_props = SetupProps(self, setup_data) elif "SimSetups" in self.p_app.design_properties.keys(): setup_data = self.p_app.design_properties["SimSetups"]["SimSetup"] - self.props = SetupProps(self, setup_data) + self._legacy_props = SetupProps(self, setup_data) except Exception: - self.props = SetupProps(self, {}) + self._legacy_props = SetupProps(self, {}) + return self._legacy_props + + @props.setter + def props(self, value): + self._legacy_props = SetupProps(self, value) @property def is_solved(self): @@ -515,9 +545,14 @@ def create(self): >>> oModule.InsertSetup """ soltype = SetupKeys.SetupNames[self.setuptype] - arg = ["NAME:" + self.name] + arg = ["NAME:" + self._name] _dict2arg(self.props, arg) self.omodule.InsertSetup(soltype, arg) + child_object = self._app.get_oo_object(self._app.odesign, f"Analysis/{self._name}") + + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) + return arg @pyaedt_function_handler(update_dictionary="properties") @@ -1076,14 +1111,17 @@ class SetupCircuit(CommonSetup): def __init__(self, app, solution_type, name="MySetupAuto", is_new_setup=True): CommonSetup.__init__(self, app, solution_type, name, is_new_setup) - @pyaedt_function_handler(isnewsetup="is_new_setup") - def _init_props(self, is_new_setup=False): - props = {} - if is_new_setup: + @property + def props(self): + if self._legacy_props: + return self._legacy_props + if self._is_new_setup: setup_template = SetupKeys.get_setup_templates()[self.setuptype] - self.props = SetupProps(self, setup_template) + setup_template["Name"] = self.name + self._legacy_props = SetupProps(self, setup_template) + self._is_new_setup = False else: - self.props = SetupProps(self, {}) + self._legacy_props = SetupProps(self, {}) try: setups_data = self.p_app.design_properties["SimSetups"]["SimSetup"] if not isinstance(setups_data, list): @@ -1092,10 +1130,10 @@ def _init_props(self, is_new_setup=False): if self.name == setup["Name"]: setup_data = setup setup_data.pop("Sweeps", None) - self.props = SetupProps(self, setup_data) + self._legacy_props = SetupProps(self, setup_data) except Exception: - self.props = SetupProps(self, {}) - self.props["Name"] = self.name + self._legacy_props = SetupProps(self, {}) + return self._legacy_props @property def _odesign(self): @@ -1125,6 +1163,10 @@ def create(self): arg = ["NAME:SimSetup"] _dict2arg(self.props, arg) self._setup(soltype, arg) + child_object = self._app.get_oo_object(self._app.odesign, f"Analysis/{self._name}") + + if child_object: + BinaryTreeNode.__init__(self, self._name, child_object, False) return arg @pyaedt_function_handler() @@ -1765,26 +1807,47 @@ class Setup3DLayout(CommonSetup): def __init__(self, app, solution_type, name="MySetupAuto", is_new_setup=True): CommonSetup.__init__(self, app, solution_type, name, is_new_setup) - @pyaedt_function_handler(isnewsetup="is_new_setup") - def _init_props(self, is_new_setup=False): - if is_new_setup: + @property + def sweeps(self): + if self._sweeps: + return self._sweeps + try: + setups_data = self._app.design_properties["Setup"]["Data"] + if self.name in setups_data: + setup_data = setups_data[self.name] + if "Data" in setup_data: # 0 and 7 represent setup HFSSDrivenAuto + app = setup_data["Data"] + for el in app: + if isinstance(app[el], dict): + self._sweeps.append(SweepHFSS3DLayout(self, el, props=app[el])) + except (KeyError, TypeError): + pass + return self._sweeps + + @property + def props(self): + if self._legacy_props: + return self._legacy_props + if self._is_new_setup: setup_template = SetupKeys.get_setup_templates()[self.setuptype] - self.props = SetupProps(self, setup_template) + setup_template["Name"] = self.name + self._legacy_props = SetupProps(self, setup_template) + self._is_new_setup = False + else: try: setups_data = self._app.design_properties["Setup"]["Data"] if self.name in setups_data: setup_data = setups_data[self.name] - if "Data" in setup_data: # 0 and 7 represent setup HFSSDrivenAuto - app = setup_data["Data"] - for el in app: - if isinstance(app[el], dict): - self.sweeps.append(SweepHFSS3DLayout(self, el, props=app[el])) - - self.props = SetupProps(self, setup_data) + self._legacy_props = SetupProps(self, setup_data) except Exception: - self.props = SetupProps(self, {}) + self._legacy_props = SetupProps(self, {}) settings.logger.error("Unable to set props.") + return self._legacy_props + + @props.setter + def props(self, value): + self._legacy_props = SetupProps(self, value) @property def is_solved(self): @@ -1795,22 +1858,28 @@ def is_solved(self): bool `True` if solutions are available. """ - if self.props.get("SolveSetupType", "HFSS") == "HFSS": + if self.properties: + props = self.properties + key = "Solver" + else: + props = self.props + key = "SolveSetupType" + if props.get(key, "HFSS") == "HFSS": combined_name = f"{self.name} : Last Adaptive" expressions = [i for i in self.p_app.post.available_report_quantities(solution=combined_name)] sol = self._app.post.reports_by_category.standard(expressions=expressions[0], setup=combined_name) - elif self.props.get("SolveSetupType", "HFSS") == "SIwave": + elif props.get(key, "HFSS") == "SIwave": combined_name = f"{self.name} : {self.sweeps[0].name}" expressions = [i for i in self.p_app.post.available_report_quantities(solution=combined_name)] sol = self._app.post.reports_by_category.standard(expressions=expressions[0], setup=combined_name) - elif self.props.get("SolveSetupType", "HFSS") == "SIwaveDCIR": + elif props.get(key, "HFSS") == "SIwaveDCIR": expressions = self.p_app.post.available_report_quantities(solution=self.name, is_siwave_dc=True) sol = self._app.post.reports_by_category.standard(expressions=expressions[0], setup=self.name) else: expressions = [i for i in self.p_app.post.available_report_quantities(solution=self.name)] sol = self._app.post.reports_by_category.standard(expressions=expressions[0], setup=self.name) - if identify_setup(self.props): + if identify_setup(props): sol.domain = "Time" return True if sol.get_solution_data() else False @@ -1823,9 +1892,13 @@ def solver_type(self): type Setup type. """ - if "SolveSetupType" in self.props: + try: + return self.properties["Solver"] + except Exception: + pass + try: return self.props["SolveSetupType"] - else: + except Exception: return None @pyaedt_function_handler() @@ -2936,7 +3009,7 @@ def delete_sweep(self, name): >>> setup1.delete_sweep("Sweep1") """ if name in self.get_sweep_names(): - self.sweeps = [sweep for sweep in self.sweeps if sweep.name != name] + self._sweeps = [sweep for sweep in self._sweeps if sweep.name != name] self.omodule.DeleteSweep(self.name, name) return True return False diff --git a/src/ansys/aedt/core/q3d.py b/src/ansys/aedt/core/q3d.py index 01b42c8aed7..bffbb98be48 100644 --- a/src/ansys/aedt/core/q3d.py +++ b/src/ansys/aedt/core/q3d.py @@ -38,8 +38,8 @@ from ansys.aedt.core.generic.general_methods import pyaedt_function_handler from ansys.aedt.core.generic.settings import settings from ansys.aedt.core.modeler.geometry_operators import GeometryOperators as go -from ansys.aedt.core.modules.boundary import BoundaryObject -from ansys.aedt.core.modules.boundary import Matrix +from ansys.aedt.core.modules.boundary.common import BoundaryObject +from ansys.aedt.core.modules.boundary.q3d_boundary import Matrix from ansys.aedt.core.modules.setup_templates import SetupKeys try: @@ -157,7 +157,7 @@ def insert_reduced_matrix( Returns ------- - :class:`ansys.aedt.core.modules.boundary.Matrix` + :class:`ansys.aedt.core.modules.q3d_boundary.Matrix` Matrix object. """ if not reduced_matrix: diff --git a/src/ansys/aedt/core/rmxprt.py b/src/ansys/aedt/core/rmxprt.py index f8275315932..9187023884d 100644 --- a/src/ansys/aedt/core/rmxprt.py +++ b/src/ansys/aedt/core/rmxprt.py @@ -63,16 +63,17 @@ def properties(self): @pyaedt_function_handler() def __setitem__(self, parameter_name, value): def _apply_val(dict_in, name, value): - if name in dict_in.props: + if name in dict_in.properties: if ( - isinstance(dict_in.props[name], list) - and ":=" in dict_in.props[name][0] + isinstance(dict_in.properties[name], list) + and isinstance(dict_in.properties[name][0], str) + and ":=" in dict_in.properties[name][0] and not isinstance(value, list) ): - prps = dict_in.props[name][::] + prps = dict_in.properties[name][::] prps[1] = value value = prps - dict_in.props[name] = value + dict_in.properties[name] = value return True else: for _, child in dict_in.children.items(): @@ -88,8 +89,8 @@ def _apply_val(dict_in, name, value): @pyaedt_function_handler() def __getitem__(self, parameter_name): def _get_val(dict_in, name): - if name in dict_in.props: - return dict_in.props[name] + if name in dict_in.properties: + return dict_in.properties[name] else: for _, child in dict_in.children.items(): return _get_val(child, name) @@ -347,7 +348,7 @@ def export_configuration(self, output_file): def jsonalize(dict_in, dict_out): dict_out[dict_in.node] = {} - for k, v in dict_in.props.items(): + for k, v in dict_in.properties.items(): if not k.endswith("/Choices"): dict_out[dict_in.node][k] = v for _, c in dict_in.children.items(): diff --git a/src/ansys/aedt/core/visualization/post/common.py b/src/ansys/aedt/core/visualization/post/common.py index fcf5d98d2b7..4fecd217008 100644 --- a/src/ansys/aedt/core/visualization/post/common.py +++ b/src/ansys/aedt/core/visualization/post/common.py @@ -515,7 +515,7 @@ def _get_plot_inputs(self): report = TEMPLATES_BY_NAME.get(report_type, TEMPLATES_BY_NAME["Standard"]) plots.append(report(self, report_type, None)) - plots[-1]._props["plot_name"] = name + plots[-1]._legacy_props["plot_name"] = name plots[-1]._is_created = True plots[-1].report_type = obj.GetPropValue("Display Type") return plots @@ -1694,15 +1694,15 @@ def _update_props(prop_in, props_out): props.get("context", {"context": {}}).get("secondary_sweep", "") == "" and props.get("report_type", "") != "Rectangular Contour Plot" ): - report._props["context"]["secondary_sweep"] = "" - _update_props(props, report._props) + report._legacy_props["context"]["secondary_sweep"] = "" + _update_props(props, report._legacy_props) for el, k in self._app.available_variations.nominal_w_values_dict.items(): if ( - report._props.get("context", None) - and report._props["context"].get("variations", None) - and el not in report._props["context"]["variations"] + report._legacy_props.get("context", None) + and report._legacy_props["context"].get("variations", None) + and el not in report._legacy_props["context"]["variations"] ): - report._props["context"]["variations"][el] = k + report._legacy_props["context"]["variations"][el] = k _ = report.expressions if matplotlib: if props.get("report_type", "").lower() in ["eye diagram", "statistical eye"]: # pragma: no cover @@ -1722,40 +1722,42 @@ def _update_props(prop_in, props_out): def _report_plotter(self, report): sols = report.get_solution_data() report_plotter = ReportPlotter() - report_plotter.title = report._props.get("plot_name", "PyAEDT Report") + report_plotter.title = report._legacy_props.get("plot_name", "PyAEDT Report") try: report_plotter.general_back_color = [ - i / 255 for i in report._props["general"]["appearance"]["background_color"] + i / 255 for i in report._legacy_props["general"]["appearance"]["background_color"] ] except KeyError: pass try: - report_plotter.general_plot_color = [i / 255 for i in report._props["general"]["appearance"]["plot_color"]] + report_plotter.general_plot_color = [ + i / 255 for i in report._legacy_props["general"]["appearance"]["plot_color"] + ] except KeyError: pass try: - report_plotter.grid_enable_major_x = report._props["general"]["grid"]["major_x"] + report_plotter.grid_enable_major_x = report._legacy_props["general"]["grid"]["major_x"] except KeyError: pass try: - report_plotter.grid_enable_minor_x = report._props["general"]["grid"]["minor_x"] + report_plotter.grid_enable_minor_x = report._legacy_props["general"]["grid"]["minor_x"] except KeyError: pass try: - report_plotter.grid_enable_major_y = report._props["general"]["grid"]["major_y"] + report_plotter.grid_enable_major_y = report._legacy_props["general"]["grid"]["major_y"] except KeyError: pass try: - report_plotter.grid_enable_minor_yi = report._props["general"]["grid"]["minor_y"] + report_plotter.grid_enable_minor_yi = report._legacy_props["general"]["grid"]["minor_y"] except KeyError: pass try: - report_plotter.grid_color = [i / 255 for i in report._props["general"]["grid"]["major_color"]] + report_plotter.grid_color = [i / 255 for i in report._legacy_props["general"]["grid"]["major_color"]] except KeyError: pass try: - report_plotter.show_legend = True if report._props["general"]["legend"] else False + report_plotter.show_legend = True if report._legacy_props["general"]["legend"] else False except KeyError: pass sw = sols.primary_sweep_values @@ -1764,7 +1766,7 @@ def _report_plotter(self, report): "x_label": sols.primary_sweep, "y_label": curve, } - pp = [i for i in report._props["expressions"] if i["name"] == curve] + pp = [i for i in report._legacy_props["expressions"] if i["name"] == curve] if pp: pp = pp[0] try: @@ -1801,7 +1803,7 @@ def _report_plotter(self, report): except KeyError: pass report_plotter.add_trace([sw, sols.data_real(curve)], 0, properties=props, name=curve) - for name, line in report._props.get("limitLines", {}).items(): + for name, line in report._legacy_props.get("limitLines", {}).items(): props = {} try: props["trace_width"] = line["width"] @@ -1815,16 +1817,16 @@ def _report_plotter(self, report): report_plotter.add_limit_line([line["xpoints"], line["ypoints"]], 0, properties=props, name=name) except KeyError: self.logger.warning("Equation lines not supported yet.") - if report._props.get("report_type", "Rectangular Plot") == "Rectangular Plot": + if report._legacy_props.get("report_type", "Rectangular Plot") == "Rectangular Plot": _ = report_plotter.plot_2d() return report_plotter - elif report._props.get("report_type", "Rectangular Plot") == "Polar Plot": + elif report._legacy_props.get("report_type", "Rectangular Plot") == "Polar Plot": _ = report_plotter.plot_polar() return report_plotter - elif report._props.get("report_type", "Rectangular Plot") == "Rectangular Contour Plot": + elif report._legacy_props.get("report_type", "Rectangular Plot") == "Rectangular Contour Plot": _ = report_plotter.plot_contour() return report_plotter - elif report._props.get("report_type", "Rectangular Plot") in ["3D Polar Plot", "3D Spherical Plot"]: + elif report._legacy_props.get("report_type", "Rectangular Plot") in ["3D Polar Plot", "3D Spherical Plot"]: _ = report_plotter.plot_3d() return report_plotter diff --git a/src/ansys/aedt/core/visualization/report/common.py b/src/ansys/aedt/core/visualization/report/common.py index 2d4aa353ed6..5386b6d3f1e 100644 --- a/src/ansys/aedt/core/visualization/report/common.py +++ b/src/ansys/aedt/core/visualization/report/common.py @@ -32,10 +32,11 @@ from ansys.aedt.core.generic.general_methods import generate_unique_name from ansys.aedt.core.generic.general_methods import pyaedt_function_handler from ansys.aedt.core.generic.general_methods import write_configuration_file +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode from ansys.aedt.core.modeler.geometry_operators import GeometryOperators -class LimitLine(object): +class LimitLine(BinaryTreeNode): """Line Limit Management Class.""" def __init__(self, report_setup, trace_name, oo=None): @@ -43,24 +44,11 @@ def __init__(self, report_setup, trace_name, oo=None): self._oreport_setup = report_setup self.line_name = trace_name self.LINESTYLE = LineStyle() + self._initialize_tree_node() - @property - def properties(self): - """Line properties. - - Returns - ------- - :class:`ansys.aedt.core.modeler.cad.elements_3d.BinaryTree` when successful, - ``False`` when failed. - - """ - from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode - - try: - parent = BinaryTreeNode(self.line_name, self._oo, False) - return parent.props - except Exception: - return [] + @pyaedt_function_handler() + def _initialize_tree_node(self): + BinaryTreeNode.__init__(self, self.line_name, self._oo, False) @pyaedt_function_handler() def _change_property(self, props_value): @@ -113,31 +101,14 @@ def set_line_properties( return self._change_property(props) -class Note(object): +class Note(BinaryTreeNode): """Note Management Class.""" def __init__(self, report_setup, plot_note_name, oo=None): self._oo = oo self._oreport_setup = report_setup self.plot_note_name = plot_note_name - - @property - def properties(self): - """Note properties. - - Returns - ------- - :class:`ansys.aedt.core.modeler.cad.elements_3d.BinaryTree` when successful, - ``False`` when failed. - - """ - from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode - - try: - parent = BinaryTreeNode(self.plot_note_name, self._oo, False) - return parent.props - except Exception: - return [] + BinaryTreeNode.__init__(self, self.plot_note_name, self._oo, False) @pyaedt_function_handler() def _change_property(self, props_value): @@ -257,7 +228,7 @@ def set_note_properties( return self._change_property(props) -class Trace(object): +class Trace(BinaryTreeNode): """Provides trace management.""" def __init__( @@ -283,31 +254,11 @@ def __init__( self._symbol_color = None self._show_symbol = False self._available_props = [] + self._initialize_tree_node() - @property - def __all_props(self): - from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode - - try: - parent = BinaryTreeNode(self.aedt_name, self._oo, False) - return parent - except Exception: - return [] - - @property - def properties(self): - """All available properties. - - Returns - ------- - :class:`ansys.aedt.core.modeler.cad.elements_3d.BinaryTree` when successful, - ``False`` when failed. - - """ - try: - return self.__all_props.props - except Exception: - return {} + @pyaedt_function_handler() + def _initialize_tree_node(self): + BinaryTreeNode.__init__(self, self.aedt_name, self._oo, False) @property def curve_properties(self): @@ -319,8 +270,8 @@ def curve_properties(self): ``False`` when failed. """ - if self.aedt_name.split(":")[-1] in self.__all_props.children: - return self.__all_props.children[self.aedt_name.split(":")[-1]].props + if self.aedt_name.split(":")[-1] in self.children: + return self.children[self.aedt_name.split(":")[-1]].properties return {} @property @@ -435,64 +386,54 @@ def set_symbol_properties(self, show=True, style=None, show_arrows=None, fill=No return self._change_property(props) -class CommonReport(object): +class CommonReport(BinaryTreeNode): """Provides common reports.""" def __init__(self, app, report_category, setup_name, expressions=None): self._post = app - self._props = {} - self._props["report_category"] = report_category + self._legacy_props = {} + self._legacy_props["report_category"] = report_category self.setup = setup_name - self._props["report_type"] = "Rectangular Plot" - self._props["context"] = {} - self._props["context"]["domain"] = "Sweep" - self._props["context"]["primary_sweep"] = "Freq" - self._props["context"]["primary_sweep_range"] = ["All"] - self._props["context"]["secondary_sweep_range"] = ["All"] - self._props["context"]["variations"] = {"Freq": ["All"]} + self._legacy_props["report_type"] = "Rectangular Plot" + self._legacy_props["context"] = {} + self._legacy_props["context"]["domain"] = "Sweep" + self._legacy_props["context"]["primary_sweep"] = "Freq" + self._legacy_props["context"]["primary_sweep_range"] = ["All"] + self._legacy_props["context"]["secondary_sweep_range"] = ["All"] + self._legacy_props["context"]["variations"] = {"Freq": ["All"]} if hasattr(self._post._app, "available_variations") and self._post._app.available_variations: for el, k in self._post._app.available_variations.nominal_w_values_dict.items(): - self._props["context"]["variations"][el] = k - self._props["expressions"] = None - self._props["plot_name"] = None + self._legacy_props["context"]["variations"][el] = k + self._legacy_props["expressions"] = None + self._legacy_props["plot_name"] = None if expressions: self.expressions = expressions self._is_created = False self.siwave_dc_category = 0 self._traces = [] - self._child_object = None + self._initialize_tree_node() + + @pyaedt_function_handler() + def _initialize_tree_node(self): + if self._is_created: + oo = self._post.oreportsetup.GetChildObject(self._legacy_props["plot_name"]) + if oo: + BinaryTreeNode.__init__(self, self.plot_name, oo, False) @property - def _all_props(self): - if self._child_object: - return self._child_object + def __all_props(self): from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode try: - oo = self._post.oreportsetup.GetChildObject(self._props["plot_name"]) - self._child_object = BinaryTreeNode(self.plot_name, oo, False) - for var in [i.split(" ,")[-1] for i in list(self._child_object.props.values())[4:]]: - if var in self._child_object.children: - del self._child_object.children[var] - els = [i for i in self._child_object.children.keys() if i.startswith("LimitLine") or i.startswith("Note")] + oo = self._post.oreportsetup.GetChildObject(self._legacy_props["plot_name"]) + _child_object = BinaryTreeNode(self.plot_name, oo, False) + for var in [i.split(" ,")[-1] for i in list(_child_object.properties.values())[4:]]: + if var in _child_object.children: + del _child_object.children[var] + els = [i for i in _child_object.children.keys() if i.startswith("LimitLine") or i.startswith("Note")] for var in els: - del self._child_object.children[var] - return self._child_object - except Exception: - return {} - - @property - def properties(self): - """Report properties. - - Returns - ------- - :class:`ansys.aedt.core.modeler.cad.elements_3d.BinaryTree` when successful, - ``False`` when failed. - - """ - try: - return self._all_props + del _child_object.children[var] + return _child_object except Exception: return {} @@ -515,11 +456,11 @@ def differential_pairs(self): bool ``True`` when differential pairs is enabled, ``False`` otherwise. """ - return self._props["context"].get("differential_pairs", False) + return self._legacy_props["context"].get("differential_pairs", False) @differential_pairs.setter def differential_pairs(self, value): - self._props["context"]["differential_pairs"] = value + self._legacy_props["context"]["differential_pairs"] = value @property def matrix(self): @@ -539,16 +480,16 @@ def matrix(self): ): try: if "Parameter" in self.traces[0].properties: - self._props["context"]["matrix"] = self.traces[0].properties["Parameter"] + self._legacy_props["context"]["matrix"] = self.traces[0].properties["Parameter"] elif "Matrix" in self.traces[0].properties: - self._props["context"]["matrix"] = self.traces[0].properties["Matrix"] + self._legacy_props["context"]["matrix"] = self.traces[0].properties["Matrix"] except Exception: self._post._app.logger.warning("Property `matrix` not found.") - return self._props["context"].get("matrix", None) + return self._legacy_props["context"].get("matrix", None) @matrix.setter def matrix(self, value): - self._props["context"]["matrix"] = value + self._legacy_props["context"]["matrix"] = value @property def reduced_matrix(self): @@ -559,11 +500,11 @@ def reduced_matrix(self): str Reduced matrix name. """ - return self._props["context"].get("reduced_matrix", None) + return self._legacy_props["context"].get("reduced_matrix", None) @reduced_matrix.setter def reduced_matrix(self, value): - self._props["context"]["reduced_matrix"] = value + self._legacy_props["context"]["reduced_matrix"] = value @property def polyline(self): @@ -576,14 +517,14 @@ def polyline(self): """ if self._is_created and self.report_category != "Far Fields" and self.report_category.endswith("Fields"): try: - self._props["context"]["polyline"] = self.traces[0].properties["Geometry"] + self._legacy_props["context"]["polyline"] = self.traces[0].properties["Geometry"] except Exception: self._post._app.logger.debug("Something went wrong while processing polyline.") - return self._props["context"].get("polyline", None) + return self._legacy_props["context"].get("polyline", None) @polyline.setter def polyline(self, value): - self._props["context"]["polyline"] = value + self._legacy_props["context"]["polyline"] = value @property def expressions(self): @@ -594,28 +535,29 @@ def expressions(self): list Expressions. """ + self._initialize_tree_node() if self._is_created: - return [i.split(" ,")[-1] for i in list(self.properties.props.values())[4:]] - if self._props.get("expressions", None) is None: + return [i.split(" ,")[-1] for i in list(self.properties.values())[4:]] + if self._legacy_props.get("expressions", None) is None: return [] - return [k.get("name", None) for k in self._props["expressions"] if k.get("name", None) is not None] + return [k.get("name", None) for k in self._legacy_props["expressions"] if k.get("name", None) is not None] @expressions.setter def expressions(self, value): if isinstance(value, dict): - self._props["expressions"].append(value) + self._legacy_props["expressions"].append(value) elif isinstance(value, list): - self._props["expressions"] = [] + self._legacy_props["expressions"] = [] for el in value: if isinstance(el, dict): - self._props["expressions"].append(el) + self._legacy_props["expressions"].append(el) else: - self._props["expressions"].append({"name": el}) + self._legacy_props["expressions"].append({"name": el}) elif isinstance(value, str): - if isinstance(self._props["expressions"], list): - self._props["expressions"].append({"name": value}) + if isinstance(self._legacy_props["expressions"], list): + self._legacy_props["expressions"].append({"name": value}) else: - self._props["expressions"] = [{"name": value}] + self._legacy_props["expressions"] = [{"name": value}] @property def report_category(self): @@ -628,15 +570,15 @@ def report_category(self): """ if self._is_created: try: - return self.properties.props["Report Type"] + return self.properties["Report Type"] except Exception: - return self._props["report_category"] - return self._props["report_category"] + return self._legacy_props["report_category"] + return self._legacy_props["report_category"] @report_category.setter def report_category(self, value): if not self._is_created: - self._props["report_category"] = value + self._legacy_props["report_category"] = value @property def report_type(self): @@ -651,20 +593,20 @@ def report_type(self): """ if self._is_created: try: - return self.properties.props["Display Type"] + return self.properties["Display Type"] except Exception: - return self._props["report_type"] - return self._props["report_type"] + return self._legacy_props["report_type"] + return self._legacy_props["report_type"] @report_type.setter def report_type(self, report): if not self._is_created: - self._props["report_type"] = report + self._legacy_props["report_type"] = report if not self.primary_sweep: - if self._props["report_type"] in ["3D Polar Plot", "3D Spherical Plot"]: + if self._legacy_props["report_type"] in ["3D Polar Plot", "3D Spherical Plot"]: self.primary_sweep = "Phi" self.secondary_sweep = "Theta" - elif self._props["report_type"] == "Radiation Pattern": + elif self._legacy_props["report_type"] == "Radiation Pattern": self.primary_sweep = "Phi" elif self.domain == "Sweep": self.primary_sweep = "Freq" @@ -711,7 +653,7 @@ def traces(self): def _update_traces(self): for trace in self.traces[::]: trace_name = trace.name - for trace_val in self._props["expressions"]: + for trace_val in self._legacy_props["expressions"]: if trace_val["name"] == trace_name: trace_style = self.__props_with_default(trace_val, "trace_style") trace_width = self.__props_with_default(trace_val, "width") @@ -724,7 +666,7 @@ def _update_traces(self): ) for trace in self.traces[::]: trace_name = trace.name - for trace_val in self._props["expressions"]: + for trace_val in self._legacy_props["expressions"]: if trace_val["name"] == trace_name: if self.report_category in ["Eye Diagram", "Spectrum"]: continue @@ -743,7 +685,7 @@ def _update_traces(self): ) for trace in self.traces[::]: trace_name = trace.name - for trace_val in self._props["expressions"]: + for trace_val in self._legacy_props["expressions"]: if trace_val["name"] == trace_name: y_axis = self.__props_with_default(trace_val, "y_axis", "Y1") if y_axis != "Y1": @@ -761,61 +703,61 @@ def _update_traces(self): ) if ( - "eye_mask" in self._props + "eye_mask" in self._legacy_props and self.report_category in ["Eye Diagram", "Statistical Eye"] - or ("quantity_type" in self._props and self.report_type == "Rectangular Contour Plot") + or ("quantity_type" in self._legacy_props and self.report_type == "Rectangular Contour Plot") ): - eye_xunits = self.__props_with_default(self._props["eye_mask"], "xunits", "ns") - eye_yunits = self.__props_with_default(self._props["eye_mask"], "yunits", "mV") - eye_points = self.__props_with_default(self._props["eye_mask"], "points") - eye_enable = self.__props_with_default(self._props["eye_mask"], "enable_limits", False) - eye_upper = self.__props_with_default(self._props["eye_mask"], "upper_limit", 500) - eye_lower = self.__props_with_default(self._props["eye_mask"], "lower_limit", 0.3) - eye_transparency = self.__props_with_default(self._props["eye_mask"], "transparency", 0.3) - eye_color = self.__props_with_default(self._props["eye_mask"], "color", (0, 128, 0)) - eye_xoffset = self.__props_with_default(self._props["eye_mask"], "X Offset", "0ns") - eye_yoffset = self.__props_with_default(self._props["eye_mask"], "Y Offset", "0V") - if "quantity_type" in self._props and self.report_type == "Rectangular Contour Plot": - if "contours_number" in self._props.get("general", {}): + eye_xunits = self.__props_with_default(self._legacy_props["eye_mask"], "xunits", "ns") + eye_yunits = self.__props_with_default(self._legacy_props["eye_mask"], "yunits", "mV") + eye_points = self.__props_with_default(self._legacy_props["eye_mask"], "points") + eye_enable = self.__props_with_default(self._legacy_props["eye_mask"], "enable_limits", False) + eye_upper = self.__props_with_default(self._legacy_props["eye_mask"], "upper_limit", 500) + eye_lower = self.__props_with_default(self._legacy_props["eye_mask"], "lower_limit", 0.3) + eye_transparency = self.__props_with_default(self._legacy_props["eye_mask"], "transparency", 0.3) + eye_color = self.__props_with_default(self._legacy_props["eye_mask"], "color", (0, 128, 0)) + eye_xoffset = self.__props_with_default(self._legacy_props["eye_mask"], "X Offset", "0ns") + eye_yoffset = self.__props_with_default(self._legacy_props["eye_mask"], "Y Offset", "0V") + if "quantity_type" in self._legacy_props and self.report_type == "Rectangular Contour Plot": + if "contours_number" in self._legacy_props.get("general", {}): self._change_property( "Contour", f" Plot {self.traces[0].name}", [ "NAME:ChangedProps", - ["NAME:Num. Contours", "Value:=", str(self._props["general"]["contours_number"])], + ["NAME:Num. Contours", "Value:=", str(self._legacy_props["general"]["contours_number"])], ], ) - if "contours_scale" in self._props.get("general", {}): + if "contours_scale" in self._legacy_props.get("general", {}): self._change_property( "Contour", f" Plot {self.traces[0].name}", [ "NAME:ChangedProps", - ["NAME:Axis Scale", "Value:=", str(self._props["general"]["contours_scale"])], + ["NAME:Axis Scale", "Value:=", str(self._legacy_props["general"]["contours_scale"])], ], ) - if "enable_contours_auto_limit" in self._props.get("general", {}): + if "enable_contours_auto_limit" in self._legacy_props.get("general", {}): self._change_property( "Contour", f" Plot {self.traces[0].name}", ["NAME:ChangedProps", ["NAME:Scale Type", "Value:=", "Auto Limits"]], ) - elif "contours_min_limit" in self._props.get("general", {}): + elif "contours_min_limit" in self._legacy_props.get("general", {}): self._change_property( "Contour", f" Plot {self.traces[0].name}", [ "NAME:ChangedProps", - ["NAME:Min", "Value:=", str(self._props["general"]["contours_min_limit"])], + ["NAME:Min", "Value:=", str(self._legacy_props["general"]["contours_min_limit"])], ], ) - elif "contours_max_limit" in self._props.get("general", {}): + elif "contours_max_limit" in self._legacy_props.get("general", {}): self._change_property( "Contour", f" Plot {self.traces[0].name}", [ "NAME:ChangedProps", - ["NAME:Max", "Value:=", str(self._props["general"]["contours_max_limit"])], + ["NAME:Max", "Value:=", str(self._legacy_props["general"]["contours_max_limit"])], ], ) self.eye_mask( @@ -830,8 +772,8 @@ def _update_traces(self): x_offset=eye_xoffset, y_offset=eye_yoffset, ) - if "limitLines" in self._props and self.report_category not in ["Eye Diagram", "Statistical Eye"]: - for line in self._props["limitLines"].values(): + if "limitLines" in self._legacy_props and self.report_category not in ["Eye Diagram", "Statistical Eye"]: + for line in self._legacy_props["limitLines"].values(): if "equation" in line: line_start = self.__props_with_default(line, "start") line_stop = self.__props_with_default(line, "stop") @@ -867,8 +809,8 @@ def _update_traces(self): hatch_pixels=line_hatchpix, color=line_color, ) - if "notes" in self._props: - for note in self._props["notes"].values(): + if "notes" in self._legacy_props: + for note in self._legacy_props["notes"].values(): note_text = self.__props_with_default(note, "text") note_position = self.__props_with_default(note, "position", [0, 0]) self.add_note(note_text, note_position[0], note_position[1]) @@ -895,12 +837,14 @@ def _update_traces(self): bold=note_bold, color=note_color, ) - if "general" in self._props: - if "show_rectangular_plot" in self._props["general"] and self.report_category in ["Eye Diagram"]: - eye_rectangular = self.__props_with_default(self._props["general"], "show_rectangular_plot", True) + if "general" in self._legacy_props: + if "show_rectangular_plot" in self._legacy_props["general"] and self.report_category in ["Eye Diagram"]: + eye_rectangular = self.__props_with_default( + self._legacy_props["general"], "show_rectangular_plot", True + ) self.rectangular_plot(eye_rectangular) - if "legend" in self._props["general"] and self.report_type != "Rectangular Contour Plot": - legend = self._props["general"]["legend"] + if "legend" in self._legacy_props["general"] and self.report_type != "Rectangular Contour Plot": + legend = self._legacy_props["general"]["legend"] legend_sol_name = self.__props_with_default(legend, "show_solution_name", True) legend_var_keys = self.__props_with_default(legend, "show_variation_key", True) leend_trace_names = self.__props_with_default(legend, "show_trace_name", True) @@ -913,8 +857,8 @@ def _update_traces(self): back_color=legend_color, font_color=legend_font_color, ) - if "grid" in self._props["general"]: - grid = self._props["general"]["grid"] + if "grid" in self._legacy_props["general"]: + grid = self._legacy_props["general"]["grid"] grid_major_color = self.__props_with_default(grid, "major_color", (200, 200, 200)) grid_minor_color = self.__props_with_default(grid, "minor_color", (230, 230, 230)) grid_enable_major_x = self.__props_with_default(grid, "major_x", True) @@ -933,12 +877,12 @@ def _update_traces(self): style_minor=grid_style_minor, style_major=grid_style_major, ) - if "appearance" in self._props["general"]: - general = self._props["general"]["appearance"] + if "appearance" in self._legacy_props["general"]: + general = self._legacy_props["general"]["appearance"] general_back_color = self.__props_with_default(general, "background_color", (255, 255, 255)) general_plot_color = self.__props_with_default(general, "plot_color", (255, 255, 255)) enable_y_stripes = self.__props_with_default(general, "enable_y_stripes", True) - if self._props["report_type"] == "Radiation Pattern": + if self._legacy_props["report_type"] == "Radiation Pattern": enable_y_stripes = None general_field_width = self.__props_with_default(general, "field_width", 4) general_precision = self.__props_with_default(general, "precision", 4) @@ -951,8 +895,8 @@ def _update_traces(self): precision=general_precision, use_scientific_notation=general_use_scientific_notation, ) - if "header" in self._props["general"]: - header = self._props["general"]["header"] + if "header" in self._legacy_props["general"]: + header = self._legacy_props["general"]["header"] company_name = self.__props_with_default(header, "company_name", "") show_design_name = self.__props_with_default(header, "show_design_name", True) header_font = self.__props_with_default(header, "font", "Arial") @@ -972,9 +916,9 @@ def _update_traces(self): color=header_color, ) - for i in list(self._props["general"].keys()): + for i in list(self._legacy_props["general"].keys()): if "axis" in i: - axis = self._props["general"][i] + axis = self._legacy_props["general"][i] axis_font = self.__props_with_default(axis, "font", "Arial") axis_size = self.__props_with_default(axis, "font_size", 12) axis_italic = self.__props_with_default(axis, "italic", False) @@ -1095,14 +1039,14 @@ def plot_name(self): str Plot name. """ - return self._props["plot_name"] + return self._legacy_props["plot_name"] @plot_name.setter def plot_name(self, name): if self._is_created: if name not in self._post.oreportsetup.GetAllReportNames(): - self._post.oreportsetup.RenameReport(self._props["plot_name"], name) - self._props["plot_name"] = name + self._post.oreportsetup.RenameReport(self._legacy_props["plot_name"], name) + self._legacy_props["plot_name"] = name @property def variations(self): @@ -1131,15 +1075,15 @@ def variations(self): variations[tr.properties["Primary Sweep"]] = ["All"] if tr.properties.get("Secondary Sweep", None): variations[tr.properties["Secondary Sweep"]] = ["All"] - self._props["context"]["variations"] = variations + self._legacy_props["context"]["variations"] = variations except Exception: self._post._app.logger.debug("Something went wrong while processing variations.") - return self._props["context"]["variations"] + return self._legacy_props["context"]["variations"] @variations.setter def variations(self, value): - self._props["context"]["variations"] = value + self._legacy_props["context"]["variations"] = value @property def primary_sweep(self): @@ -1151,14 +1095,14 @@ def primary_sweep(self): Primary sweep. """ if self._is_created: - return list(self.properties.props.values())[4].split(" ,")[0] - return self._props["context"]["primary_sweep"] + return list(self.properties.values())[4].split(" ,")[0] + return self._legacy_props["context"]["primary_sweep"] @primary_sweep.setter def primary_sweep(self, value): - if value == self._props["context"].get("secondary_sweep", None): - self._props["context"]["secondary_sweep"] = self._props["context"]["primary_sweep"] - self._props["context"]["primary_sweep"] = value + if value == self._legacy_props["context"].get("secondary_sweep", None): + self._legacy_props["context"]["secondary_sweep"] = self._legacy_props["context"]["primary_sweep"] + self._legacy_props["context"]["primary_sweep"] = value if value == "Time": self.variations.pop("Freq", None) self.variations["Time"] = ["All"] @@ -1176,16 +1120,16 @@ def secondary_sweep(self): Secondary sweep. """ if self._is_created: - els = list(self.properties.props.values())[4].split(" ,") + els = list(self.properties.values())[4].split(" ,") return els[1] if len(els) == 3 else None - return self._props["context"].get("secondary_sweep", None) + return self._legacy_props["context"].get("secondary_sweep", None) @secondary_sweep.setter def secondary_sweep(self, value): - if value == self._props["context"]["primary_sweep"]: - self._props["context"]["primary_sweep"] = self._props["context"]["secondary_sweep"] - self._props["context"]["secondary_sweep"] = value + if value == self._legacy_props["context"]["primary_sweep"]: + self._legacy_props["context"]["primary_sweep"] = self._legacy_props["context"]["secondary_sweep"] + self._legacy_props["context"]["secondary_sweep"] = value if value == "Time": self.variations.pop("Freq", None) self.variations["Time"] = ["All"] @@ -1202,11 +1146,11 @@ def primary_sweep_range(self): str Primary sweep range. """ - return self._props["context"]["primary_sweep_range"] + return self._legacy_props["context"]["primary_sweep_range"] @primary_sweep_range.setter def primary_sweep_range(self, value): - self._props["context"]["primary_sweep_range"] = value + self._legacy_props["context"]["primary_sweep_range"] = value @property def secondary_sweep_range(self): @@ -1217,11 +1161,11 @@ def secondary_sweep_range(self): str Secondary sweep range. """ - return self._props["context"]["secondary_sweep_range"] + return self._legacy_props["context"]["secondary_sweep_range"] @secondary_sweep_range.setter def secondary_sweep_range(self, value): - self._props["context"]["secondary_sweep_range"] = value + self._legacy_props["context"]["secondary_sweep_range"] = value @property def _context(self): @@ -1293,11 +1237,11 @@ def domain(self): return self.traces[0].properties["Domain"] except Exception: self._post._app.logger.debug("Something went wrong while accessing trace's Domain property.") - return self._props["context"]["domain"] + return self._legacy_props["context"]["domain"] @domain.setter def domain(self, domain): - self._props["context"]["domain"] = domain + self._legacy_props["context"]["domain"] = domain if self._post._app.design_type in ["Maxwell 3D", "Maxwell 2D"]: return if self.primary_sweep == "Freq" and domain == "Time": @@ -1318,11 +1262,11 @@ def use_pulse_in_tdr(self): bool ``True`` when option is enabled, ``False`` otherwise. """ - return self._props["context"].get("use_pulse_in_tdr", False) + return self._legacy_props["context"].get("use_pulse_in_tdr", False) @use_pulse_in_tdr.setter def use_pulse_in_tdr(self, val): - self._props["context"]["use_pulse_in_tdr"] = val + self._legacy_props["context"]["use_pulse_in_tdr"] = val @pyaedt_function_handler() def _convert_dict_to_report_sel(self, sweeps): @@ -1388,6 +1332,7 @@ def create(self, name=None): ) self._post.plots.append(self) self._is_created = True + self._initialize_tree_node() return True @pyaedt_function_handler() @@ -1458,7 +1403,7 @@ def _export_context(self, output_dict): def _export_expressions(self, output_dict): output_dict["expressions"] = {} for expr in self.traces: - name = self.properties.props[expr.name].split(" ,")[-1] + name = self.properties[expr.name].split(" ,")[-1] pr = expr.curve_properties output_dict["expressions"][name] = {} if "Trace Type" in pr: @@ -1485,8 +1430,8 @@ def _export_graphical_objects(self, output_dict): from ansys.aedt.core.visualization.report.eye import AMIEyeDiagram from ansys.aedt.core.visualization.report.eye import EyeDiagram - if isinstance(self, (AMIEyeDiagram, EyeDiagram)) and "EyeDisplayTypeProperty" in self.properties.children: - pr = self.properties.children["EyeDisplayTypeProperty"].props + if isinstance(self, (AMIEyeDiagram, EyeDiagram)) and "EyeDisplayTypeProperty" in self.children: + pr = self.children["EyeDisplayTypeProperty"].properties if pr.get("Mask/MaskPoints", None) and len(pr["Mask/MaskPoints"]) > 1: pts_x = pr["Mask/MaskPoints"][1::2] pts_y = pr["Mask/MaskPoints"][2::2] @@ -1553,8 +1498,8 @@ def _export_general_appearance(self, output_dict): from ansys.aedt.core.visualization.report.eye import EyeDiagram output_dict["general"] = {} - if "AxisX" in self.properties.children: - axis = self.properties.children["AxisX"].props + if "AxisX" in self.children: + axis = self.children["AxisX"].properties output_dict["general"]["axisx"] = { "label": axis["Name"], "font": axis["Text Font/FaceName"], @@ -1569,9 +1514,9 @@ def _export_general_appearance(self, output_dict): } if not isinstance(self, (AMIEyeDiagram, EyeDiagram)): output_dict["general"]["axisx"]["linear_scaling"] = True if axis["Axis Scaling"] == "Linear" else False - y_axis_available = [i for i in self.properties.children.keys() if i.startswith("AxisY")] + y_axis_available = [i for i in self.children.keys() if i.startswith("AxisY")] for yaxis in y_axis_available: - axis = self.properties.children[yaxis].props + axis = self.children[yaxis].properties output_dict["general"][yaxis.lower()] = { "label": axis["Name"], "font": axis["Text Font/FaceName"], @@ -1587,8 +1532,8 @@ def _export_general_appearance(self, output_dict): output_dict["general"][yaxis.lower()]["linear_scaling"] = ( True if axis["Axis Scaling"] == "Linear" else False ) - if "General" in self.properties.children: - props = self.properties.children["General"].props + if "General" in self.children: + props = self.children["General"].properties output_dict["general"]["appearance"] = { "background_color": [ props["Back Color/Red"], @@ -1607,8 +1552,8 @@ def _export_general_appearance(self, output_dict): "use_scientific_notation": props["Use Scientific Notation"], } - if "Grid" in self.properties.children: - props = self.properties.children["Grid"].props + if "Grid" in self.children: + props = self.children["Grid"].properties output_dict["general"]["grid"] = { "major_color": [ props["Major grid line color/Red"], @@ -1627,8 +1572,8 @@ def _export_general_appearance(self, output_dict): "style_major": props["Major grid line style"], "style_minor": props["Minor grid line style"], } - if "Legend" in self.properties.children: - props = self.properties.children["Legend"].props + if "Legend" in self.children: + props = self.children["Legend"].properties output_dict["general"]["legend"] = { "back_color": [ props["Back Color/Red"], @@ -1644,8 +1589,8 @@ def _export_general_appearance(self, output_dict): "show_variation_key": props["Show Variation Key"], "show_trace_name": props["Show Trace Name"], } - if "Header" in self.properties.children: - props = self.properties.children["Header"].props + if "Header" in self.children: + props = self.children["Header"].properties output_dict["general"]["header"] = { "font": props["Title Font/FaceName"], "title_size": props["Title Font/Height"], @@ -2548,6 +2493,7 @@ def delete_traces(self, plot_name, traces_list): props = [f"{plot_name}:=", traces_list] try: self._post.oreportsetup.DeleteTraces(props) + self._initialize_tree_node() return True except Exception: return False @@ -2585,6 +2531,7 @@ def add_trace_to_report(self, traces, setup_name=None, variations=None, context= self._convert_dict_to_report_sel(variations if variations else self.variations), self._trace_info, ) + self._initialize_tree_node() return True except Exception: return False diff --git a/src/ansys/aedt/core/visualization/report/emi.py b/src/ansys/aedt/core/visualization/report/emi.py index c4f9d643518..f43b14905fb 100644 --- a/src/ansys/aedt/core/visualization/report/emi.py +++ b/src/ansys/aedt/core/visualization/report/emi.py @@ -82,11 +82,11 @@ def band(self): str Band name. """ - return self._props["context"].get("band", None) + return self._legacy_props["context"].get("band", None) @band.setter def band(self, value): - self._props["context"]["band"] = value + self._legacy_props["context"]["band"] = value @property def emission(self): @@ -105,10 +105,10 @@ def emission(self): def emission(self, value): if value == "CE": self._emission = value - self._props["context"]["emission"] = "0" + self._legacy_props["context"]["emission"] = "0" elif value == "RE": self._emission = value - self._props["context"]["emission"] = "1" + self._legacy_props["context"]["emission"] = "1" else: self.logger.error(f"Emission must be 'CE' or 'RE', value '{value}' is not valid.") @@ -121,11 +121,11 @@ def time_start(self): str Time start. """ - return self._props["context"].get("time_start", None) + return self._legacy_props["context"].get("time_start", None) @time_start.setter def time_start(self, value): - self._props["context"]["time_start"] = value + self._legacy_props["context"]["time_start"] = value @property def time_stop(self): @@ -136,11 +136,11 @@ def time_stop(self): str Time stop. """ - return self._props["context"].get("time_stop", None) + return self._legacy_props["context"].get("time_stop", None) @time_stop.setter def time_stop(self, value): - self._props["context"]["time_stop"] = value + self._legacy_props["context"]["time_stop"] = value @property def _context(self): diff --git a/src/ansys/aedt/core/visualization/report/eye.py b/src/ansys/aedt/core/visualization/report/eye.py index da06cd5b26d..e9c7bc4c434 100644 --- a/src/ansys/aedt/core/visualization/report/eye.py +++ b/src/ansys/aedt/core/visualization/report/eye.py @@ -32,6 +32,7 @@ from ansys.aedt.core import generate_unique_name from ansys.aedt.core import pyaedt_function_handler +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode from ansys.aedt.core.visualization.report.common import CommonReport @@ -41,13 +42,13 @@ class AMIConturEyeDiagram(CommonReport): def __init__(self, app, report_category, setup_name, expressions=None): CommonReport.__init__(self, app, report_category, setup_name, expressions) self.domain = "Time" - self._props["report_type"] = "Rectangular Contour Plot" + self._legacy_props["report_type"] = "Rectangular Contour Plot" self.variations.pop("Time", None) - self._props["context"]["variations"]["__UnitInterval"] = ["All"] - self._props["context"]["variations"]["__Amplitude"] = ["All"] - self._props["context"]["variations"]["__EyeOpening"] = ["0"] - self._props["context"]["primary_sweep"] = "__UnitInterval" - self._props["context"]["secondary_sweep"] = "__Amplitude" + self._legacy_props["context"]["variations"]["__UnitInterval"] = ["All"] + self._legacy_props["context"]["variations"]["__Amplitude"] = ["All"] + self._legacy_props["context"]["variations"]["__EyeOpening"] = ["0"] + self._legacy_props["context"]["primary_sweep"] = "__UnitInterval" + self._legacy_props["context"]["secondary_sweep"] = "__Amplitude" self.quantity_type = 0 self.min_latch_overlay = "0" self.noise_floor = "1e-16" @@ -69,12 +70,12 @@ def expressions(self): Expressions. """ if self._is_created: - return [i.split(" ,")[-1] for i in list(self.properties.props.values())[4:]] - if self._props.get("expressions", None) is None: + return [i.split(" ,")[-1] for i in list(self.properties.values())[4:]] + if self._legacy_props.get("expressions", None) is None: return [] expr_head = "Eye" new_exprs = [] - for expr_dict in self._props["expressions"]: + for expr_dict in self._legacy_props["expressions"]: expr = expr_dict["name"] if not ".int_ami" in expr: qtype = int(self.quantity_type) @@ -93,19 +94,19 @@ def expressions(self): @expressions.setter def expressions(self, value): if isinstance(value, dict): - self._props["expressions"].append = value + self._legacy_props["expressions"].append = value elif isinstance(value, list): - self._props["expressions"] = [] + self._legacy_props["expressions"] = [] for el in value: if isinstance(el, dict): - self._props["expressions"].append(el) + self._legacy_props["expressions"].append(el) else: - self._props["expressions"].append({"name": el}) + self._legacy_props["expressions"].append({"name": el}) elif isinstance(value, str): - if isinstance(self._props["expressions"], list): - self._props["expressions"].append({"name": value}) + if isinstance(self._legacy_props["expressions"], list): + self._legacy_props["expressions"].append({"name": value}) else: - self._props["expressions"] = [{"name": value}] + self._legacy_props["expressions"] = [{"name": value}] @property def quantity_type(self): @@ -116,11 +117,11 @@ def quantity_type(self): int Quantity type. """ - return self._props.get("quantity_type", 0) + return self._legacy_props.get("quantity_type", 0) @quantity_type.setter def quantity_type(self, value): - self._props["quantity_type"] = value + self._legacy_props["quantity_type"] = value @property def _context(self): @@ -307,7 +308,9 @@ def create(self, name=None): ) self._post.plots.append(self) self._is_created = True - + oo = self._post.oreportsetup.GetChildObject(self._legacy_props["plot_name"]) + if oo: + BinaryTreeNode.__init__(self, self.plot_name, oo, False) return True @pyaedt_function_handler(xunits="x_units", yunits="y_units", xoffset="x_offset", yoffset="y_offset") @@ -499,7 +502,7 @@ def __init__(self, app, report_category, setup_name, expressions=None): CommonReport.__init__(self, app, report_category, setup_name, expressions) self.domain = "Time" if report_category == "Statistical Eye": - self._props["report_type"] = "Statistical Eye Plot" + self._legacy_props["report_type"] = "Statistical Eye Plot" self.variations.pop("Time", None) self.variations["__UnitInterval"] = "All" self.variations["__Amplitude"] = "All" @@ -523,14 +526,14 @@ def expressions(self): Expressions. """ if self._is_created: - return [i.split(" ,")[-1] for i in list(self.properties.props.values())[4:]] - if self._props.get("expressions", None) is None: + return [i.split(" ,")[-1] for i in list(self.properties.values())[4:]] + if self._legacy_props.get("expressions", None) is None: return [] expr_head = "Wave" if self.report_category == "Statistical Eye": expr_head = "Eye" new_exprs = [] - for expr_dict in self._props["expressions"]: + for expr_dict in self._legacy_props["expressions"]: expr = expr_dict["name"] if not ".int_ami" in expr: qtype = int(self.quantity_type) @@ -557,11 +560,11 @@ def quantity_type(self): int Quantity type. """ - return self._props.get("quantity_type", 0) + return self._legacy_props.get("quantity_type", 0) @quantity_type.setter def quantity_type(self, value): - self._props["quantity_type"] = value + self._legacy_props["quantity_type"] = value @property def report_category(self): @@ -576,19 +579,19 @@ def report_category(self): try: return self.properties.props["Report Type"] except Exception: - return self._props["report_category"] - return self._props["report_category"] + return self._legacy_props["report_category"] + return self._legacy_props["report_category"] @report_category.setter def report_category(self, value): - self._props["report_category"] = value - if self._props["report_category"] == "Statistical Eye" and self.report_type == "Rectangular Plot": - self._props["report_type"] = "Statistical Eye Plot" + self._legacy_props["report_category"] = value + if self._legacy_props["report_category"] == "Statistical Eye" and self.report_type == "Rectangular Plot": + self._legacy_props["report_type"] = "Statistical Eye Plot" self.variations.pop("Time", None) self.variations["__UnitInterval"] = "All" self.variations["__Amplitude"] = "All" - elif self._props["report_category"] == "Eye Diagram" and self.report_type == "Statistical Eye Plot": - self._props["report_type"] = "Rectangular Plot" + elif self._legacy_props["report_category"] == "Eye Diagram" and self.report_type == "Statistical Eye Plot": + self._legacy_props["report_type"] = "Rectangular Plot" self.variations.pop("__UnitInterval", None) self.variations.pop("__Amplitude", None) self.variations["Time"] = "All" @@ -602,11 +605,11 @@ def unit_interval(self): str Unit interval. """ - return self._props["context"].get("unit_interval", None) + return self._legacy_props["context"].get("unit_interval", None) @unit_interval.setter def unit_interval(self, value): - self._props["context"]["unit_interval"] = value + self._legacy_props["context"]["unit_interval"] = value @property def offset(self): @@ -617,11 +620,11 @@ def offset(self): str Offset value. """ - return self._props["context"].get("offset", None) + return self._legacy_props["context"].get("offset", None) @offset.setter def offset(self, value): - self._props["context"]["offset"] = value + self._legacy_props["context"]["offset"] = value @property def auto_delay(self): @@ -632,11 +635,11 @@ def auto_delay(self): bool ``True`` if auto-delay is enabled, ``False`` otherwise. """ - return self._props["context"].get("auto_delay", None) + return self._legacy_props["context"].get("auto_delay", None) @auto_delay.setter def auto_delay(self, value): - self._props["context"]["auto_delay"] = value + self._legacy_props["context"]["auto_delay"] = value @property def manual_delay(self): @@ -647,11 +650,11 @@ def manual_delay(self): str ``True`` if manual-delay is enabled, ``False`` otherwise. """ - return self._props["context"].get("manual_delay", None) + return self._legacy_props["context"].get("manual_delay", None) @manual_delay.setter def manual_delay(self, value): - self._props["context"]["manual_delay"] = value + self._legacy_props["context"]["manual_delay"] = value @property def auto_cross_amplitude(self): @@ -662,11 +665,11 @@ def auto_cross_amplitude(self): bool ``True`` if auto-cross amplitude is enabled, ``False`` otherwise. """ - return self._props["context"].get("auto_cross_amplitude", None) + return self._legacy_props["context"].get("auto_cross_amplitude", None) @auto_cross_amplitude.setter def auto_cross_amplitude(self, value): - self._props["context"]["auto_cross_amplitude"] = value + self._legacy_props["context"]["auto_cross_amplitude"] = value @property def cross_amplitude(self): @@ -677,11 +680,11 @@ def cross_amplitude(self): str Cross-amplitude. """ - return self._props["context"].get("cross_amplitude", None) + return self._legacy_props["context"].get("cross_amplitude", None) @cross_amplitude.setter def cross_amplitude(self, value): - self._props["context"]["cross_amplitude"] = value + self._legacy_props["context"]["cross_amplitude"] = value @property def auto_compute_eye_meas(self): @@ -692,11 +695,11 @@ def auto_compute_eye_meas(self): bool ``True`` to compute eye measurements, ``False`` otherwise. """ - return self._props["context"].get("auto_compute_eye_meas", None) + return self._legacy_props["context"].get("auto_compute_eye_meas", None) @auto_compute_eye_meas.setter def auto_compute_eye_meas(self, value): - self._props["context"]["auto_compute_eye_meas"] = value + self._legacy_props["context"]["auto_compute_eye_meas"] = value @property def eye_measurement_point(self): @@ -707,11 +710,11 @@ def eye_measurement_point(self): str Eye measurement point. """ - return self._props["context"].get("eye_measurement_point", None) + return self._legacy_props["context"].get("eye_measurement_point", None) @eye_measurement_point.setter def eye_measurement_point(self, value): - self._props["context"]["eye_measurement_point"] = value + self._legacy_props["context"]["eye_measurement_point"] = value @property def _context(self): @@ -900,7 +903,9 @@ def create(self, name=None): ) self._post.plots.append(self) self._is_created = True - + oo = self._post.oreportsetup.GetChildObject(self._legacy_props["plot_name"]) + if oo: + BinaryTreeNode.__init__(self, self.plot_name, oo, False) return True @pyaedt_function_handler(xunits="x_units", yunits="y_units", xoffset="x_offset", yoffset="y_offset") @@ -1098,27 +1103,27 @@ def expressions(self): Expressions. """ if self._is_created: - return [i.split(" ,")[-1] for i in list(self.properties.props.values())[4:]] - if self._props.get("expressions", None) is None: + return [i.split(" ,")[-1] for i in list(self.properties.values())[4:]] + if self._legacy_props.get("expressions", None) is None: return [] - return [k.get("name", None) for k in self._props["expressions"] if k.get("name", None) is not None] + return [k.get("name", None) for k in self._legacy_props["expressions"] if k.get("name", None) is not None] @expressions.setter def expressions(self, value): if isinstance(value, dict): - self._props["expressions"].append = value + self._legacy_props["expressions"].append = value elif isinstance(value, list): - self._props["expressions"] = [] + self._legacy_props["expressions"] = [] for el in value: if isinstance(el, dict): - self._props["expressions"].append(el) + self._legacy_props["expressions"].append(el) else: - self._props["expressions"].append({"name": el}) + self._legacy_props["expressions"].append({"name": el}) elif isinstance(value, str): - if isinstance(self._props["expressions"], list): - self._props["expressions"].append({"name": value}) + if isinstance(self._legacy_props["expressions"], list): + self._legacy_props["expressions"].append({"name": value}) else: - self._props["expressions"] = [{"name": value}] + self._legacy_props["expressions"] = [{"name": value}] @property def time_start(self): @@ -1129,11 +1134,11 @@ def time_start(self): str Time start. """ - return self._props["context"].get("time_start", None) + return self._legacy_props["context"].get("time_start", None) @time_start.setter def time_start(self, value): - self._props["context"]["time_start"] = value + self._legacy_props["context"]["time_start"] = value @property def time_stop(self): @@ -1144,11 +1149,11 @@ def time_stop(self): str Time stop. """ - return self._props["context"].get("time_stop", None) + return self._legacy_props["context"].get("time_stop", None) @time_stop.setter def time_stop(self, value): - self._props["context"]["time_stop"] = value + self._legacy_props["context"]["time_stop"] = value @property def thinning(self): @@ -1159,11 +1164,11 @@ def thinning(self): bool ``True`` if thinning is enabled, ``False`` otherwise. """ - return self._props["context"].get("thinning", None) + return self._legacy_props["context"].get("thinning", None) @thinning.setter def thinning(self, value): - self._props["context"]["thinning"] = value + self._legacy_props["context"]["thinning"] = value @property def dy_dx_tolerance(self): @@ -1174,11 +1179,11 @@ def dy_dx_tolerance(self): float DY DX tolerance. """ - return self._props["context"].get("dy_dx_tolerance", None) + return self._legacy_props["context"].get("dy_dx_tolerance", None) @dy_dx_tolerance.setter def dy_dx_tolerance(self, value): - self._props["context"]["dy_dx_tolerance"] = value + self._legacy_props["context"]["dy_dx_tolerance"] = value @property def thinning_points(self): @@ -1189,11 +1194,11 @@ def thinning_points(self): int Number of thinning points. """ - return self._props["context"].get("thinning_points", None) + return self._legacy_props["context"].get("thinning_points", None) @thinning_points.setter def thinning_points(self, value): - self._props["context"]["thinning_points"] = value + self._legacy_props["context"]["thinning_points"] = value @property def _context(self): diff --git a/src/ansys/aedt/core/visualization/report/field.py b/src/ansys/aedt/core/visualization/report/field.py index a85b7571603..ee1cc945b10 100644 --- a/src/ansys/aedt/core/visualization/report/field.py +++ b/src/ansys/aedt/core/visualization/report/field.py @@ -51,15 +51,15 @@ def far_field_sphere(self): """ if self._is_created: try: - self._props["context"]["far_field_sphere"] = self.traces[0].properties["Geometry"] + self._legacy_props["context"]["far_field_sphere"] = self.traces[0].properties["Geometry"] except Exception: self._post._app.logger.warning("Property `far_field_sphere` not found.") - return self._props["context"].get("far_field_sphere", None) + return self._legacy_props["context"].get("far_field_sphere", None) @far_field_sphere.setter def far_field_sphere(self, value): - self._props["context"]["far_field_sphere"] = value + self._legacy_props["context"]["far_field_sphere"] = value @property def _context(self): @@ -87,11 +87,11 @@ def point_number(self): str Point number. """ - return self._props["context"].get("point_number", 1001) + return self._legacy_props["context"].get("point_number", 1001) @point_number.setter def point_number(self, value): - self._props["context"]["point_number"] = value + self._legacy_props["context"]["point_number"] = value @property def _context(self): @@ -121,11 +121,11 @@ def near_field(self): str Field name. """ - return self._props["context"].get("near_field", None) + return self._legacy_props["context"].get("near_field", None) @near_field.setter def near_field(self, value): - self._props["context"]["near_field"] = value + self._legacy_props["context"]["near_field"] = value class FarField(CommonReport): @@ -156,14 +156,14 @@ def far_field_sphere(self): """ if self._is_created: try: - self._props["context"]["far_field_sphere"] = self.traces[0].properties["Geometry"] + self._legacy_props["context"]["far_field_sphere"] = self.traces[0].properties["Geometry"] except Exception: self._post._app.logger.warning("Property `far_field_sphere` not found.") - return self._props["context"].get("far_field_sphere", None) + return self._legacy_props["context"].get("far_field_sphere", None) @far_field_sphere.setter def far_field_sphere(self, value): - self._props["context"]["far_field_sphere"] = value + self._legacy_props["context"]["far_field_sphere"] = value @property def _context(self): diff --git a/src/ansys/aedt/core/visualization/report/standard.py b/src/ansys/aedt/core/visualization/report/standard.py index 7d5a1ae5930..515545aee65 100644 --- a/src/ansys/aedt/core/visualization/report/standard.py +++ b/src/ansys/aedt/core/visualization/report/standard.py @@ -32,6 +32,7 @@ from ansys.aedt.core import generate_unique_name from ansys.aedt.core import pyaedt_function_handler +from ansys.aedt.core.modeler.cad.elements_3d import BinaryTreeNode from ansys.aedt.core.visualization.report.common import CommonReport @@ -50,11 +51,11 @@ def sub_design_id(self): int Number of the sub design ID. """ - return self._props["context"].get("Sub Design ID", None) + return self._legacy_props["context"].get("Sub Design ID", None) @sub_design_id.setter def sub_design_id(self, value): - self._props["context"]["Sub Design ID"] = value + self._legacy_props["context"]["Sub Design ID"] = value @property def time_start(self): @@ -65,11 +66,11 @@ def time_start(self): str Time start value. """ - return self._props["context"].get("time_start", None) + return self._legacy_props["context"].get("time_start", None) @time_start.setter def time_start(self, value): - self._props["context"]["time_start"] = value + self._legacy_props["context"]["time_start"] = value @property def time_stop(self): @@ -80,11 +81,11 @@ def time_stop(self): str Time stop value. """ - return self._props["context"].get("time_stop", None) + return self._legacy_props["context"].get("time_stop", None) @time_stop.setter def time_stop(self, value): - self._props["context"]["time_stop"] = value + self._legacy_props["context"]["time_stop"] = value @property def _did(self): @@ -110,11 +111,11 @@ def pulse_rise_time(self): float Pulse rise time. """ - return self._props["context"].get("pulse_rise_time", 0) if self.domain == "Time" else 0 + return self._legacy_props["context"].get("pulse_rise_time", 0) if self.domain == "Time" else 0 @pulse_rise_time.setter def pulse_rise_time(self, val): - self._props["context"]["pulse_rise_time"] = val + self._legacy_props["context"]["pulse_rise_time"] = val @property def time_windowing(self): @@ -136,7 +137,7 @@ def time_windowing(self): int Time windowing. """ - _time_windowing = self._props["context"].get("time_windowing", 0) + _time_windowing = self._legacy_props["context"].get("time_windowing", 0) return _time_windowing if self.domain == "Time" and self.pulse_rise_time != 0 else 0 @time_windowing.setter @@ -153,9 +154,9 @@ def time_windowing(self, val): "lanzcos": 8, } if isinstance(val, int): - self._props["context"]["time_windowing"] = val + self._legacy_props["context"]["time_windowing"] = val elif isinstance(val, str) and val.lower in available_values: - self._props["context"]["time_windowing"] = available_values[val.lower()] + self._legacy_props["context"]["time_windowing"] = available_values[val.lower()] @property def _context(self): @@ -444,11 +445,11 @@ def time_start(self): str Time start. """ - return self._props["context"].get("time_start", "0s") + return self._legacy_props["context"].get("time_start", "0s") @time_start.setter def time_start(self, value): - self._props["context"]["time_start"] = value + self._legacy_props["context"]["time_start"] = value @property def time_stop(self): @@ -459,11 +460,11 @@ def time_stop(self): str Time stop. """ - return self._props["context"].get("time_stop", "100ns") + return self._legacy_props["context"].get("time_stop", "100ns") @time_stop.setter def time_stop(self, value): - self._props["context"]["time_stop"] = value + self._legacy_props["context"]["time_stop"] = value @property def window(self): @@ -474,11 +475,11 @@ def window(self): str Window. """ - return self._props["context"].get("window", "Rectangular") + return self._legacy_props["context"].get("window", "Rectangular") @window.setter def window(self, value): - self._props["context"]["window"] = value + self._legacy_props["context"]["window"] = value @property def kaiser_coeff(self): @@ -489,11 +490,11 @@ def kaiser_coeff(self): str Kaiser coefficient. """ - return self._props["context"].get("kaiser_coeff", 0) + return self._legacy_props["context"].get("kaiser_coeff", 0) @kaiser_coeff.setter def kaiser_coeff(self, value): - self._props["context"]["kaiser_coeff"] = value + self._legacy_props["context"]["kaiser_coeff"] = value @property def adjust_coherent_gain(self): @@ -504,11 +505,11 @@ def adjust_coherent_gain(self): bool ``True`` if coherent gain is enabled, ``False`` otherwise. """ - return self._props["context"].get("adjust_coherent_gain", False) + return self._legacy_props["context"].get("adjust_coherent_gain", False) @adjust_coherent_gain.setter def adjust_coherent_gain(self, value): - self._props["context"]["adjust_coherent_gain"] = value + self._legacy_props["context"]["adjust_coherent_gain"] = value @property def plot_continous_spectrum(self): @@ -519,11 +520,11 @@ def plot_continous_spectrum(self): bool ``True`` if continuous spectrum is enabled, ``False`` otherwise. """ - return self._props["context"].get("plot_continous_spectrum", False) + return self._legacy_props["context"].get("plot_continous_spectrum", False) @plot_continous_spectrum.setter def plot_continous_spectrum(self, value): - self._props["context"]["plot_continous_spectrum"] = value + self._legacy_props["context"]["plot_continous_spectrum"] = value @property def max_frequency(self): @@ -534,11 +535,11 @@ def max_frequency(self): str Maximum spectrum frequency. """ - return self._props["context"].get("max_frequency", "10GHz") + return self._legacy_props["context"].get("max_frequency", "10GHz") @max_frequency.setter def max_frequency(self, value): - self._props["context"]["max_frequency"] = value + self._legacy_props["context"]["max_frequency"] = value @property def _context(self): @@ -654,4 +655,7 @@ def create(self, name=None): ) self._post.plots.append(self) self._is_created = True + oo = self._post.oreportsetup.GetChildObject(self._legacy_props["plot_name"]) + if oo: + BinaryTreeNode.__init__(self, self.plot_name, oo, False) return True diff --git a/src/pyaedt/modules/Boundary.py b/src/pyaedt/modules/Boundary.py index ed3420aeced..8b137891791 100644 --- a/src/pyaedt/modules/Boundary.py +++ b/src/pyaedt/modules/Boundary.py @@ -1 +1 @@ -from ansys.aedt.core.modules.boundary import * + diff --git a/tests/system/general/conftest.py b/tests/system/general/conftest.py index e874c99a1dd..f36c07515f2 100644 --- a/tests/system/general/conftest.py +++ b/tests/system/general/conftest.py @@ -168,8 +168,13 @@ def desktop(): d.odesktop.SetDesktopConfiguration("All") d.odesktop.SetSchematicEnvironment(0) yield d + pid = d.aedt_process_id d.release_desktop(True, True) time.sleep(1) + try: + os.kill(pid, 9) + except OSError: + pass @pytest.fixture(scope="module") diff --git a/tests/system/general/test_04_SBR.py b/tests/system/general/test_04_SBR.py index 0a361c2a651..34b61ce98bb 100644 --- a/tests/system/general/test_04_SBR.py +++ b/tests/system/general/test_04_SBR.py @@ -161,7 +161,7 @@ def test_02_add_antennas(self): array.native_properties["Array Length In Wavelength"] = "10" assert array.update() - assert array.object_properties.props["Name"] == array.name + assert array.properties["Name"] == array.name native_components = len(self.aedtapp.native_component_names) array.name = "new_name" diff --git a/tests/system/general/test_07_Object3D.py b/tests/system/general/test_07_Object3D.py index 15ff00f711a..3cd578af1f7 100644 --- a/tests/system/general/test_07_Object3D.py +++ b/tests/system/general/test_07_Object3D.py @@ -632,7 +632,7 @@ def test_26_unclassified_object(self): vArg2 = ["NAME:IntersectParameters", "KeepOriginals:=", False] self.aedtapp.modeler.oeditor.Intersect(vArg1, vArg2) - assert box1 in self.aedtapp.modeler.unclassified_objects + assert box1.name in self.aedtapp.modeler.unclassified_names def test_26a_delete_unclassified_object(self): unclassified = self.aedtapp.modeler.unclassified_objects @@ -662,17 +662,17 @@ def test_27_get_object_history_properties(self): box_clone_history = box_clone.history() assert box_history.node == "box_history" assert box_history.command == "CreateBox" - assert box_history.props["Command"] == "CreateBox" + assert box_history.properties["Command"] == "CreateBox" assert box_history.children == {} assert box_clone_history.node == "box_history1" assert box_clone_history.command == box_history.command - assert box_clone_history.props["Command"] == box_history.props["Command"] - assert box_clone_history.props["Position/X"] == box_history.props["Position/X"] - assert box_clone_history.props["Position/Y"] == box_history.props["Position/Y"] - assert box_clone_history.props["Position/Z"] == box_history.props["Position/Z"] - assert box_clone_history.props["XSize"] == box_history.props["XSize"] - assert box_clone_history.props["YSize"] == box_history.props["YSize"] - assert box_clone_history.props["ZSize"] == box_history.props["ZSize"] + assert box_clone_history.properties["Command"] == box_history.properties["Command"] + assert box_clone_history.properties["Position/X"] == box_history.properties["Position/X"] + assert box_clone_history.properties["Position/Y"] == box_history.properties["Position/Y"] + assert box_clone_history.properties["Position/Z"] == box_history.properties["Position/Z"] + assert box_clone_history.properties["XSize"] == box_history.properties["XSize"] + assert box_clone_history.properties["YSize"] == box_history.properties["YSize"] + assert box_clone_history.properties["ZSize"] == box_history.properties["ZSize"] assert len(box_clone_history.children) == 3 assert "Subtract:1" in box_clone_history.children.keys() assert "Rotate:1" in box_clone_history.children.keys() @@ -686,10 +686,10 @@ def test_27_get_object_history_properties(self): subtract = self.aedtapp.modeler["box_history1"].history().children["Subtract:1"].children assert len(subtract) == 1 for key in subtract.keys(): - assert subtract[key].command == subtract[key].props["Command"] + assert subtract[key].command == subtract[key].properties["Command"] subtract_child = subtract[key].children for child in subtract_child.keys(): - assert subtract_child[child].command == subtract_child[child].props["Command"] + assert subtract_child[child].command == subtract_child[child].properties["Command"] assert len(subtract_child[child].children) == 0 def test_27b_object_suppress(self): @@ -703,29 +703,29 @@ def test_27c_object_jsonalize(self): def test_28_set_object_history_properties(self): history = self.aedtapp.modeler["box_history1"].history() - assert history.props["Position/X"] == "10meter" - history.props["Position/X"] = "15meter" - assert history.props["Position/X"] == "15meter" - assert history.props["ZSize"] == "15meter" - history.props["ZSize"] = "10meter" - assert history.props["ZSize"] == "10meter" + assert history.properties["Position/X"] == "10meter" + history.properties["Position/X"] = "15meter" + assert history.properties["Position/X"] == "15meter" + assert history.properties["ZSize"] == "15meter" + history.properties["ZSize"] = "10meter" + assert history.properties["ZSize"] == "10meter" subtract = history.children["Subtract:1"].children for key in subtract.keys(): subtract_child = subtract[key].children for child in subtract_child.keys(): if "CreateCylinder" in child: - assert subtract_child[child].props["Center Position/X"] == "10meter" - subtract_child[child].props["Center Position/X"] = "15meter" - assert subtract_child[child].props["Center Position/X"] == "15meter" - assert subtract_child[child].props["Axis"] == "Y" - subtract_child[child].props["Axis"] = "Z" - assert subtract_child[child].props["Axis"] == "Z" - assert subtract_child[child].props["Radius"] == "5meter" - subtract_child[child].props["Radius"] = "8meter" - assert subtract_child[child].props["Radius"] == "8meter" - assert subtract_child[child].props["Height"] == "20meter" - subtract_child[child].props["Height"] = "24meter" - assert subtract_child[child].props["Height"] == "24meter" + assert subtract_child[child].properties["Center Position/X"] == "10meter" + subtract_child[child].properties["Center Position/X"] = "15meter" + assert subtract_child[child].properties["Center Position/X"] == "15meter" + assert subtract_child[child].properties["Axis"] == "Y" + subtract_child[child].properties["Axis"] = "Z" + assert subtract_child[child].properties["Axis"] == "Z" + assert subtract_child[child].properties["Radius"] == "5meter" + subtract_child[child].properties["Radius"] = "8meter" + assert subtract_child[child].properties["Radius"] == "8meter" + assert subtract_child[child].properties["Height"] == "20meter" + subtract_child[child].properties["Height"] = "24meter" + assert subtract_child[child].properties["Height"] == "24meter" def test_29_test_nets(self): self.aedtapp.insert_design("nets") diff --git a/tests/system/general/test_12_1_PostProcessing.py b/tests/system/general/test_12_1_PostProcessing.py index 642f5302bfb..7a65c67fbbc 100644 --- a/tests/system/general/test_12_1_PostProcessing.py +++ b/tests/system/general/test_12_1_PostProcessing.py @@ -418,7 +418,7 @@ def test_09e_add_traces_to_report(self): assert not new_report.add_trace_to_report(traces, setup, variations) def test_09f_update_trace_name(self): - report = [plot for plot in self.aedtapp.post.plots if plot.plot_name == "add_traces_test"][0] + report = self.aedtapp.create_scattering("add_traces_test_2") old_trace_name = report.traces[0].name assert old_trace_name in report.traces[0].aedt_name new_name = "update_trace_name_test" diff --git a/tests/system/general/test_20_HFSS.py b/tests/system/general/test_20_HFSS.py index 71ae88ba839..9df72ef1b5c 100644 --- a/tests/system/general/test_20_HFSS.py +++ b/tests/system/general/test_20_HFSS.py @@ -139,7 +139,7 @@ def test_04_assign_coating(self): coat = self.aedtapp.assign_coating([id, "inner_1", 41], **args) coat.name = "Coating1inner" assert coat.update() - assert coat.object_properties + assert coat.properties material = coat.props.get("Material", "") assert material == "aluminum" assert not self.aedtapp.assign_coating(["insulator2", 45]) @@ -164,7 +164,7 @@ def test_05_create_wave_port_from_sheets(self): terminals_rename=False, ) - assert port.object_properties + assert port.properties assert port.name == "sheet1_Port" assert port.name in [i.name for i in self.aedtapp.boundaries] assert port.props["RenormalizeAllTerminals"] is False diff --git a/tests/system/general/test_28_Maxwell3D.py b/tests/system/general/test_28_Maxwell3D.py index 0ba29f6b845..9a61868f337 100644 --- a/tests/system/general/test_28_Maxwell3D.py +++ b/tests/system/general/test_28_Maxwell3D.py @@ -1131,17 +1131,19 @@ def test_59_assign_floating(self): def test_60_resistive_sheet(self): self.aedtapp.insert_design("ResistiveSheet") self.aedtapp.solution_type = SOLUTIONS.Maxwell3d.EddyCurrent - my_box = self.aedtapp.modeler.create_box( - origin=[0, 0, 0], sizes=[0.4, -1, 0.8], name="my_box", material="copper" + self.aedtapp.modeler.create_box(origin=[0, 0, 0], sizes=[0.4, -1, 0.8], name="my_box", material="copper") + my_rectangle = self.aedtapp.modeler.create_rectangle( + orientation=1, origin=[0, 0, 0.8], sizes=[-1, 0.4], name="my_rect" ) - resistive_face = my_box.faces[0] - bound = self.aedtapp.assign_resistive_sheet(assignment=resistive_face, resistance="3ohm") + + # From 2025.1, this boundary can only be assigned to Sheets that touch conductor Solids. + bound = self.aedtapp.assign_resistive_sheet(assignment=my_rectangle.faces[0], resistance="3ohm") assert bound - assert bound.props["Faces"][0] == resistive_face.id + assert bound.props["Faces"][0] == my_rectangle.faces[0].id assert bound.props["Resistance"] == "3ohm" self.aedtapp.solution_type = SOLUTIONS.Maxwell3d.Magnetostatic - bound = self.aedtapp.assign_resistive_sheet(assignment=resistive_face, non_linear=True) + bound = self.aedtapp.assign_resistive_sheet(assignment=my_rectangle.name, non_linear=True) assert bound.props["Nonlinear"] - assert bound.props["Faces"][0] == resistive_face.id + assert bound.props["Objects"][0] == my_rectangle.name self.aedtapp.solution_type = SOLUTIONS.Maxwell3d.ACConduction - assert not self.aedtapp.assign_resistive_sheet(assignment=resistive_face, resistance="3ohm") + assert not self.aedtapp.assign_resistive_sheet(assignment=my_rectangle, resistance="3ohm") diff --git a/tests/system/general/test_30_Q2D.py b/tests/system/general/test_30_Q2D.py index 38c57f29c4a..75f959bb85c 100644 --- a/tests/system/general/test_30_Q2D.py +++ b/tests/system/general/test_30_Q2D.py @@ -103,7 +103,7 @@ def test_09_auto_assign(self): o = self.aedtapp.create_rectangle([6, 6], [5, 3], name="Rectangle1", material="Copper") o = self.aedtapp.create_rectangle([0, 0], [5, 3], name="Rectangle2", material="Copper") assert self.aedtapp.auto_assign_conductors() - assert self.aedtapp.boundaries[0].object_properties + assert self.aedtapp.boundaries[0].properties assert len(self.aedtapp.boundaries) == 2 def test_10_toggle_conductor(self): @@ -221,7 +221,7 @@ def test_14_export_matrix_data(self, add_app): def test_15_export_equivalent_circuit(self, add_app): q2d = add_app(application=Q2d, project_name=self.test_matrix, just_open=True) q2d.insert_reduced_matrix(q2d.MATRIXOPERATIONS.Float, "Circle2", "Test4") - assert q2d.matrices[4].name == "Test4" + assert q2d.matrices[-1].name == "Test4" assert len(q2d.setups[0].sweeps[0].frequencies) > 0 assert q2d.setups[0].sweeps[0].basis_frequencies == [] assert q2d.export_equivalent_circuit(os.path.join(self.local_scratch.path, "test_export_circuit.cir")) diff --git a/tests/system/general/test_41_3dlayout_modeler.py b/tests/system/general/test_41_3dlayout_modeler.py index 8dbf26bd4c6..be902599c7d 100644 --- a/tests/system/general/test_41_3dlayout_modeler.py +++ b/tests/system/general/test_41_3dlayout_modeler.py @@ -336,7 +336,7 @@ def test_13a_create_edge_port(self): assert self.aedtapp.create_edge_port("line1", 3, False) assert len(self.aedtapp.excitations) > 0 time_domain = os.path.join(TESTS_GENERAL_PATH, "example_models", test_subfolder, "Sinusoidal.csv") - assert self.aedtapp.boundaries[0].object_properties.props["Magnitude"] == "1V" + assert self.aedtapp.boundaries[0].properties["Magnitude"] == "1V" assert self.aedtapp.edit_source_from_file( source=port_wave.name, input_file=time_domain, @@ -345,8 +345,9 @@ def test_13a_create_edge_port(self): y_scale=1e-3, data_format="Voltage", ) - assert self.aedtapp.boundaries[0].object_properties.props["Magnitude"] != "1V" - self.aedtapp.boundaries[0].object_properties.props["Boundary Type"] = "PEC" + assert self.aedtapp.boundaries[0].properties["Magnitude"] != "1V" + self.aedtapp.boundaries[0].properties["Boundary Type"] = "PEC" + assert self.aedtapp.boundaries[0].properties["Boundary Type"] == "PEC" assert list(self.aedtapp.oboundary.GetAllBoundariesList())[0] == self.aedtapp.boundaries[0].name def test_14a_create_coaxial_port(self): @@ -607,9 +608,9 @@ def test_27_create_pin_port(self): assert port.name == "PinPort1" port.props["Magnitude"] = "2V" assert port.props["Magnitude"] == "2V" - assert port.object_properties.props["Magnitude"] == "2V" - port.object_properties.props["Magnitude"] = "5V" - assert port.object_properties.props["Magnitude"] == "5V" + assert port.properties["Magnitude"] == "2V" + port.properties["Magnitude"] = "5V" + assert port.properties["Magnitude"] == "5V" def test_28_create_scattering(self): assert self.aedtapp.create_scattering() diff --git a/tests/system/general/test_98_Icepak.py b/tests/system/general/test_98_Icepak.py index e1184c056e1..8cb40a365ce 100644 --- a/tests/system/general/test_98_Icepak.py +++ b/tests/system/general/test_98_Icepak.py @@ -26,10 +26,10 @@ from ansys.aedt.core import Icepak from ansys.aedt.core.generic.settings import settings -from ansys.aedt.core.modules.boundary import NativeComponentObject -from ansys.aedt.core.modules.boundary import NetworkObject -from ansys.aedt.core.modules.boundary import PCBSettingsDeviceParts -from ansys.aedt.core.modules.boundary import PCBSettingsPackageParts +from ansys.aedt.core.modules.boundary.circuit_boundary import NetworkObject +from ansys.aedt.core.modules.boundary.layout_boundary import NativeComponentObject +from ansys.aedt.core.modules.boundary.layout_boundary import PCBSettingsDeviceParts +from ansys.aedt.core.modules.boundary.layout_boundary import PCBSettingsPackageParts from ansys.aedt.core.modules.mesh_icepak import MeshRegion from ansys.aedt.core.modules.setup_templates import SetupKeys from ansys.aedt.core.visualization.post.field_data import FolderPlotSettings @@ -1228,10 +1228,12 @@ def test_55_native_components_history(self): self.aedtapp.modeler.user_defined_components[fan.name].duplicate_along_line([4, 5, 6]) fan_1_history = self.aedtapp.modeler.user_defined_components[fan.name].history() assert fan_1_history.command == "Move" - assert all(fan_1_history.props["Move Vector/" + i] == j + "mm" for i, j in [("X", "1"), ("Y", "2"), ("Z", "3")]) + assert all( + fan_1_history.properties["Move Vector/" + i] == j + "mm" for i, j in [("X", "1"), ("Y", "2"), ("Z", "3")] + ) assert fan_1_history.children["DuplicateAlongLine:1"].command == "DuplicateAlongLine" assert all( - fan_1_history.children["DuplicateAlongLine:1"].props["Vector/" + i] == j + "mm" + fan_1_history.children["DuplicateAlongLine:1"].properties["Vector/" + i] == j + "mm" for i, j in [("X", "4"), ("Y", "5"), ("Z", "6")] )