Skip to content

Commit

Permalink
FEAT: Import materials from Workbench (#5186)
Browse files Browse the repository at this point in the history
Co-authored-by: samuel <[email protected]>
  • Loading branch information
Alberto-DM and Samuelopez-ansys authored Sep 23, 2024
1 parent 2874d68 commit 949d676
Show file tree
Hide file tree
Showing 17 changed files with 6,207 additions and 36 deletions.
1,452 changes: 1,452 additions & 0 deletions _unittest/example_models/T03/EngineeringData1.xml

Large diffs are not rendered by default.

2,660 changes: 2,660 additions & 0 deletions _unittest/example_models/T03/EngineeringData2.xml

Large diffs are not rendered by default.

1,491 changes: 1,491 additions & 0 deletions _unittest/example_models/T03/EngineeringData3.xml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions _unittest/test_01_GeometryOperators.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def is_vector_equal(v, r):

@pytest.fixture(scope="module", autouse=True)
def desktop():
"""Override the desktop fixture to DO NOT open the Desktop when running this test class"""
return


Expand Down
49 changes: 49 additions & 0 deletions _unittest/test_01_data_handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- 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 import data_handlers as dh
import pytest


@pytest.fixture(scope="module", autouse=True)
def desktop():
"""Override the desktop fixture to DO NOT open the Desktop when running this test class"""
return


class TestClass:

def test_str_to_bool(self):
test_list_1 = ["one", "two", "five"]
bool_values = list(map(dh.str_to_bool, test_list_1))
assert all(isinstance(b, str) for b in bool_values) # All strings
test_list_1.append("True")
assert True in list(map(dh.str_to_bool, test_list_1))
test_list_2 = ["Stop", "go", "run", "crawl", "False"]
assert False in list(map(dh.str_to_bool, test_list_2))

def test_normalize_string_format(self):
dirty = "-Hello Wòrld - Test---Strïng - With - Múltiple Spaces ç & Unsupport€d Ch@rachter$ £ike * " # codespell:ignore # noqa: E501
clean = "Hello_World_Test_String_With_Multiple_Spaces_c_and_UnsupportEd_ChatrachterS_Like" # codespell:ignore # noqa: E501
assert dh.normalize_string_format(dirty) == clean
1 change: 1 addition & 0 deletions _unittest/test_01_downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

@pytest.fixture(scope="module", autouse=True)
def desktop():
"""Override the desktop fixture to DO NOT open the Desktop when running this test class"""
return


Expand Down
11 changes: 1 addition & 10 deletions _unittest/test_01_general_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
# 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 str_to_bool
from ansys.aedt.core.generic.general_methods import number_aware_string_key
import pytest


@pytest.fixture(scope="module", autouse=True)
def desktop():
"""Override the desktop fixture to DO NOT open the Desktop when running this test class"""
return


Expand All @@ -44,12 +44,3 @@ def test_01_number_aware_string_key(self):
expected_sort_order = ["C1", "U2", "U10", "Y200", "Y1000"]
assert sorted(component_names, key=number_aware_string_key) == expected_sort_order
assert sorted(component_names + [""], key=number_aware_string_key) == [""] + expected_sort_order

def test_02_str_to_bool(self):
test_list_1 = ["one", "two", "five"]
bool_values = list(map(str_to_bool, test_list_1))
assert all(isinstance(b, str) for b in bool_values) # All strings
test_list_1.append("True")
assert True in list(map(str_to_bool, test_list_1))
test_list_2 = ["Stop", "go", "run", "crawl", "False"]
assert False in list(map(str_to_bool, test_list_2))
1 change: 1 addition & 0 deletions _unittest/test_01_report_file_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

@pytest.fixture(scope="module", autouse=True)
def desktop():
"""Override the desktop fixture to DO NOT open the Desktop when running this test class"""
return


Expand Down
1 change: 1 addition & 0 deletions _unittest/test_01_toolkit_icons.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

@pytest.fixture(scope="module", autouse=True)
def desktop():
"""Override the desktop fixture to DO NOT open the Desktop when running this test class"""
return


Expand Down
83 changes: 82 additions & 1 deletion _unittest/test_03_Materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,17 @@ def aedtapp(add_app):
return app


@pytest.fixture(scope="class")
def aedtapp2(add_app):
app = add_app(project_name="Test03", design_name="import_from_wb")
return app


class TestClass:
@pytest.fixture(autouse=True)
def init(self, aedtapp, local_scratch):
def init(self, aedtapp, aedtapp2, local_scratch):
self.aedtapp = aedtapp
self.testapp2 = aedtapp2
self.local_scratch = local_scratch

def test_01_vaacum(self):
Expand Down Expand Up @@ -446,3 +453,77 @@ def test_15_thermalmodifier_and_spatialmodifier(self):

self.aedtapp.materials["vacuum"].conductivity.thermalmodifier = None
self.aedtapp.materials["vacuum"].conductivity.spatialmodifier = None

def test_16_import_materials_from_workbench(self):

assert self.testapp2.materials.import_materials_from_workbench("not_existing.xml") is False

imported_mats = self.testapp2.materials.import_materials_from_workbench(
os.path.join(local_path, "example_models", test_subfolder, "EngineeringData1.xml")
)
for m in imported_mats:
assert m in self.testapp2.materials.material_keys.values()
assert self.testapp2.materials.material_keys["new_wb_material_aniso_wb"].permittivity.value == [1, 2, 3]
assert self.testapp2.materials.material_keys["new_wb_material_aniso_wb"].conductivity.value == [
0.012987012987012988,
0.011363636363636364,
0.010101010101010102,
]
assert self.testapp2.materials.material_keys["structural_steel_wb"].permeability.value == 10000
assert self.testapp2.materials.material_keys["wb_material_simple_thermal_wb"].conductivity.value == 18
assert (
self.testapp2.materials.material_keys["wb_material_simple_thermal_wb"].permittivity.thermalmodifier
== "pwl($TM_WB_MATERIAL_SIMPLE_thermal_wb_permittivity, Temp)"
)
assert self.testapp2.materials.material_keys["wb_material_simple_wb"].conductivity.value == 3
assert self.testapp2.materials.material_keys["wb_material_simple_wb"].thermal_expansion_coefficient.value == 23

imported_mats = self.testapp2.materials.import_materials_from_workbench(
os.path.join(local_path, "example_models", test_subfolder, "EngineeringData2.xml")
)
for m in imported_mats:
assert m in self.testapp2.materials.material_keys.values()
assert self.testapp2.materials.material_keys["aluminum_alloy_wb"].conductivity.value == 41152263.3744856
assert (
self.testapp2.materials.material_keys["aluminum_alloy_wb"].thermal_conductivity.thermalmodifier
== "pwl($TM_Aluminum_Alloy_wb_thermal_conductivity, Temp)"
)
assert self.testapp2.materials.material_keys["concrete_wb"].thermal_conductivity.value == 0.72
assert self.testapp2.materials.material_keys["fr_4_wb"].thermal_conductivity.value == [0.38, 0.38, 0.3]
assert self.testapp2.materials.material_keys["silicon_anisotropic_wb"].mass_density.value == 2330
assert (
self.testapp2.materials.material_keys[
"silicon_anisotropic_wb"
].thermal_expansion_coefficient.thermalmodifier
== "pwl($TM_Silicon_Anisotropic_wb_thermal_expansion_coefficient, Temp)"
)

imported_mats = self.testapp2.materials.import_materials_from_workbench(
os.path.join(local_path, "example_models", test_subfolder, "EngineeringData3.xml"), name_suffix="_imp"
)
for m in imported_mats:
assert m in self.testapp2.materials.material_keys.values()
assert (
self.testapp2.materials.material_keys["84zn_12ag_4au_imp"].thermal_conductivity.thermalmodifier
== "pwl($TM_84Zn_12Ag_4Au_imp_thermal_conductivity, Temp)"
)
assert (
self.testapp2.materials.material_keys["84zn_12ag_4au_imp"].mass_density.thermalmodifier
== "pwl($TM_84Zn_12Ag_4Au_imp_mass_density, Temp)"
)
assert (
self.testapp2.materials.material_keys["84zn_12ag_4au_imp"].specific_heat.thermalmodifier
== "pwl($TM_84Zn_12Ag_4Au_imp_specific_heat, Temp)"
)
assert (
self.testapp2.materials.material_keys["84zn_12ag_4au_imp"].youngs_modulus.thermalmodifier
== "pwl($TM_84Zn_12Ag_4Au_imp_youngs_modulus, Temp)"
)
assert (
self.testapp2.materials.material_keys["84zn_12ag_4au_imp"].poissons_ratio.thermalmodifier
== "pwl($TM_84Zn_12Ag_4Au_imp_poissons_ratio, Temp)"
)
assert (
self.testapp2.materials.material_keys["84zn_12ag_4au_imp"].thermal_expansion_coefficient.thermalmodifier
== "pwl($TM_84Zn_12Ag_4Au_imp_thermal_expansion_coefficient, Temp)"
)
1 change: 1 addition & 0 deletions _unittest/test_37_Genetic_Algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

@pytest.fixture(scope="module", autouse=True)
def desktop():
"""Override the desktop fixture to DO NOT open the Desktop when running this test class"""
return


Expand Down
1 change: 0 additions & 1 deletion src/ansys/aedt/core/generic/configurations.py
Original file line number Diff line number Diff line change
Expand Up @@ -1161,7 +1161,6 @@ def import_config(self, config_file, *args):
else:
newname = el
newmat = Material(self._app, el, val, material_update=True)
newmat._update_material()
if newmat:
self._app.materials.material_keys[newname] = newmat
else: # pragma: no cover
Expand Down
40 changes: 40 additions & 0 deletions src/ansys/aedt/core/generic/data_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import random
import re
import string
import unicodedata

from ansys.aedt.core.generic.general_methods import pyaedt_function_handler
from ansys.aedt.core.generic.general_methods import read_json
Expand Down Expand Up @@ -654,3 +655,42 @@ def float_units(val_str, units=""):

val = val / unit_val[units]
return val


@pyaedt_function_handler()
def normalize_string_format(text):

equivalence_table = {
"$": "S",
"€": "E",
"£": "L",
"@": "at",
"&": "and",
}

def _remove_accents(input_str):
# Normalize the input string to decompose accents and diacritics
nfkd_form = unicodedata.normalize("NFKD", input_str)
# Filter out diacritical marks (combining characters)
return "".join([c for c in nfkd_form if not unicodedata.combining(c)])

# Step 1: Remove accents and diacritics
text = _remove_accents(text)

# Step 2: Replace specific characters like " ", "-", " -", "- ", " - ", and multiple spaces with "_"
text = re.sub(r"[\s\-]+", "_", text)

# Step 3: Replace using the equivalence table
for a, b in equivalence_table.items():
text = text.replace(a, b)

# Step 4: Remove unsupported characters, keeping only letters, numbers, and underscores
text = re.sub(r"[^a-zA-Z0-9_]", "", text)

# Step 5: Replace multiple underscores with a single underscore
text = re.sub(r"_+", "_", text)

# Finally, remove leading or trailing underscores
text = text.strip("_")

return text
55 changes: 46 additions & 9 deletions src/ansys/aedt/core/modules/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class MatProperties(object):
"molecular_mass",
"viscosity",
]

defaultvalue = [
1.0,
1.0,
Expand Down Expand Up @@ -101,6 +102,7 @@ class MatProperties(object):
0,
0,
]

defaultunit = [
None,
None,
Expand All @@ -122,9 +124,48 @@ class MatProperties(object):
None,
None,
]

diel_order = [3, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 1]

cond_order = [2, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 3]

workbench_name = [
"Relative Permittivity",
"Relative Permeability",
"Electrical Conductivity",
"Electric Loss Tangent",
"Magnetic Loss Tangent",
"Magnetic Coercivity",
"Thermal Conductivity",
"Density",
"Specific Heat",
"Coefficient of Thermal Expansion",
"Young's Modulus",
"Poisson's Ratio",
None, # diffusivity
"Molecular Weight",
"Viscosity",
]

@classmethod
def wb_to_aedt_name(cls, wb_name):
"""Retrieve the corresponding AEDT property name for the specified Workbench property name.
The workbench names are specified in ``MatProperties.workbench_name``.
The AEDT names are specified in ``MatProperties.aedtname``.
Parameters
----------
wb_name : str
Workbench name of the property.
Returns
-------
str
AEDT name of the property.
"""
return cls.aedtname[cls.workbench_name.index(wb_name)]

@classmethod
def get_defaultunit(cls, aedtname=None):
"""Retrieve the default unit for a full name or a category name.
Expand Down Expand Up @@ -412,19 +453,15 @@ def value(self, val):

self._property_value[i].value = el
i += 1
if self._material._material_update:
self._material._update_props(self.name, val)

self._material._update_props(self.name, val, update_aedt=self._material._material_update)
elif isinstance(val, list) and self.type == "vector":
if len(val) == 4:
self._property_value[0].value = val
if self._material._material_update:
self._material._update_props(self.name, val)
self._material._update_props(self.name, val, update_aedt=self._material._material_update)
else:
self.type = "simple"
self._property_value[0].value = val
if self._material._material_update:
self._material._update_props(self.name, val)
self._material._update_props(self.name, val, update_aedt=self._material._material_update)

@property
def unit(self):
Expand Down Expand Up @@ -1230,7 +1267,7 @@ def __init__(self, materials, name, props=None):
self._coordinate_system = ""
self.is_sweep_material = False
if props:
self._props = props.copy()
self._props = copy.deepcopy(props)
else:
self._props = {}
if "CoordinateSystemType" in self._props:
Expand Down Expand Up @@ -1441,7 +1478,7 @@ def __init__(self, materiallib, name, props=None, material_update=True):
if "wire_type" in self._props:
self.wire_type = self._props["wire_type"]["Choice"]

def _update_material(self):
# Update the material properties
for property in MatProperties.aedtname:
tmods = None
smods = None
Expand Down
Loading

0 comments on commit 949d676

Please sign in to comment.