diff --git a/src/ansys/dpf/core/field.py b/src/ansys/dpf/core/field.py index c4a05dc9411..e65d2af6e47 100644 --- a/src/ansys/dpf/core/field.py +++ b/src/ansys/dpf/core/field.py @@ -30,6 +30,7 @@ from ansys import dpf from ansys.dpf.core import dimensionality, errors, meshed_region, scoping, time_freq_support +from ansys.dpf.core.available_result import Homogeneity from ansys.dpf.core.common import ( _get_size_of_list, locations, @@ -617,12 +618,18 @@ def unit(self): return self.field_definition.unit @unit.setter - def unit(self, value): + def unit(self, value: str | tuple[Homogeneity, str]): """Change the unit for the field. + A single string is interpreted as a known physical unit with an associated homogeneity. + + For DPF 11.0 (2026 R1) and above: A tuple of two strings is interpreted as a homogeneity and a unit name. + If the homogeneity is :py:attr:`Homogeneity.dimensionless`, then the unit string is kept as a name. + Otherwise, the homogeneity is ignored, and the unit string interpreted as a known physical unit with an associated homogeneity. + Parameters ---------- - value : str + value: Units for the field. Examples @@ -635,10 +642,24 @@ def unit(self, value): >>> my_field.unit 'm' + + Named dimensionless unit. + + >>> from ansys.dpf import core as dpf + >>> from ansys.dpf.core.available_result import Homogeneity + >>> my_field = dpf.Field(10) + >>> my_field.unit = (Homogeneity.dimensionless, "dollars") + >>> print(my_field.unit) + 'dollars' + + Notes + ----- + Setting a named dimensionless unit requires DPF 11.0 (2026 R1) or above. + """ - fielddef = self.field_definition - fielddef.unit = value - self.field_definition = fielddef + field_def = self.field_definition + field_def.unit = value + self.field_definition = field_def @property def dimensionality(self): diff --git a/src/ansys/dpf/core/field_definition.py b/src/ansys/dpf/core/field_definition.py index 2271c469f13..fb87d249812 100644 --- a/src/ansys/dpf/core/field_definition.py +++ b/src/ansys/dpf/core/field_definition.py @@ -22,11 +22,14 @@ """FieldDefinition.""" +from __future__ import annotations + import traceback import warnings from ansys.dpf.core import server as server_module -from ansys.dpf.core.check_version import version_requires +from ansys.dpf.core.available_result import Homogeneity +from ansys.dpf.core.check_version import server_meet_version_and_raise, version_requires from ansys.dpf.core.common import natures, shell_layers from ansys.dpf.core.dimensionality import Dimensionality from ansys.dpf.gate import ( @@ -204,8 +207,44 @@ def is_of_quantity_type(self, quantity_type): return is_of_quantity_type @unit.setter - def unit(self, value): - self._api.csfield_definition_set_unit(self, value, None, 0, 0, 0) + def unit(self, value: str | tuple[Homogeneity, str]): + """Change the unit for the field definition. + + A single string is interpreted as a known physical unit with an associated homogeneity. + + For DPF 11.0 (2026 R1) and above: A tuple of two strings is interpreted as a homogeneity and a unit name. + If the homogeneity is :py:attr:`Homogeneity.dimensionless`, then the unit string is kept as a name. + Otherwise, the homogeneity is ignored, and the unit string interpreted as a known physical unit with an associated homogeneity. + + Parameters + ---------- + value: + Units for the field. + + Notes + ----- + Setting a named dimensionless unit requires DPF 11.0 (2026 R1) or above. + + """ + # setter with explicit homogeneity: homogeneity is taken into account if it is dimensionless + if ( + isinstance(value, tuple) + and len(value) == 2 + and isinstance(value[0], Homogeneity) + and isinstance(value[1], str) + ): + server_meet_version_and_raise( + required_version="11.0", + server=self._server, + msg="Setting a named dimensionless unit requires DPF 11.0 (2026 R1) or above.", + ) + # csfield_definition_set_unit will ignore the homogeneity if it is not dimensionless + self._api.csfield_definition_set_unit(self, value[1], None, value[0].value, 0, 0) + # standard unit setter, using string interpreter + elif isinstance(value, str): + self._api.csfield_definition_set_unit(self, value, None, 0, 0, 0) + else: + raise ValueError("Unit setter supports either string or tuple(Homogeneity, str)") @location.setter def location(self, value): diff --git a/tests/test_field.py b/tests/test_field.py index fa77deec0b9..5ef760ee711 100644 --- a/tests/test_field.py +++ b/tests/test_field.py @@ -29,8 +29,10 @@ from ansys import dpf from ansys.dpf import core from ansys.dpf.core import FieldDefinition, operators as ops +from ansys.dpf.core.available_result import Homogeneity from ansys.dpf.core.check_version import server_meet_version from ansys.dpf.core.common import locations, shell_layers +from ansys.dpf.gate.errors import DPFServerException, DpfVersionNotSupported import conftest from conftest import SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_8_0, running_docker @@ -1417,3 +1419,28 @@ def test_deep_copy_big_field_remote(server_type, server_type_remote_process): out = dpf.core.core._deep_copy(field_a, server_type_remote_process) assert np.allclose(out.data, data) + + +def test_set_units(server_type): + data = np.random.random(100) + field = dpf.core.field_from_array(data) + # use string setter with recognized string + field.unit = "m" + assert field.unit == "m" + + if server_meet_version("11.0", server_type): + # use tuple(Homogeneity, string) setter + field.unit = (Homogeneity.dimensionless, "sones") + assert field.unit == "sones" + else: + with pytest.raises(DpfVersionNotSupported): + # use tuple(Homogeneity, string) setter + field.unit = (Homogeneity.dimensionless, "sones") + + # use unrecognized string + with pytest.raises(DPFServerException): + field.unit = "sones" + + # use wrong type of arguments + with pytest.raises(ValueError): + field.unit = 1.0