Skip to content

Commit

Permalink
docs: Feat/units and doc (#2471)
Browse files Browse the repository at this point in the history
* update units code and test

* update units code and test

* update units code and test

* update units code and test

* update units doc

* update units doc

* update units code

* update units doc

* update units doc

* update test version
  • Loading branch information
seanpearsonuk authored Feb 19, 2024
1 parent 5f36c5f commit a10c316
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 44 deletions.
17 changes: 17 additions & 0 deletions doc/source/api/solver/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,23 @@ and ``NamedObject`` types, the state value is a dictionary. For the
You can also access the state of an object with the ``get_state`` method and
modify it with the ``set_state`` method.

Real parameter objects can have units as well as values. If a particular object
supports units then you can obtain its value and units as an ``ansys.units.Quantity``
object by calling the ``as_quantity`` method. You can obtain the same value
and units information as a tuple by calling the ``value_with_units`` method.
That is especially useful if you do not wish to install the ansys.units package.
Both ``ansys.units.Quantity`` objects and value-unit tuples can be passed to the
``set_state`` method.

.. code-block::
>>> diam_obj = hydraulic_diameter.as_quantity()
>>> diam_tup = hydraulic_diameter.value_with_units()
>>> assert diam_tup == (diam_obj.value, diam_obj.units.name)
>>> hydraulic_diameter.set_state(2.0 * diam_obj)
>>> assert hydraulic_diameter.units == diam_obj.units
You can print the current state in a simple text format with the
``print_state`` method. For example, assume you entered:

Expand Down
60 changes: 52 additions & 8 deletions src/ansys/fluent/core/solver/flobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,24 @@ class Real(SettingsBase[RealType], Numerical):
Some ``Real`` objects also accept string arguments representing
expression values.
Methods
-------
as_quantity()
Get the current state of the object as an ansys.units.Quantity.
set_state(state)
Set the state of the object.
value_with_units()
Get a tuple containing the current value and units string.
units()
Get the units string.
"""

def as_quantity(self) -> Optional[ansys_units.Quantity]:
"""Get the state of the object as a Quantity."""
"""Get the state of the object as an ansys.units.Quantity."""
error = None
if not ansys_units:
error = "Code not configured to support units."
Expand All @@ -521,22 +535,52 @@ def as_quantity(self) -> Optional[ansys_units.Quantity]:
def set_state(self, state: Optional[StateT] = None, **kwargs):
"""Set the state of the object.
Parameters
----------
state
The type of state can be float, str (representing either
an expression or a value with units), or an ansys.units.Quantity.
Raises
------
UnhandledQuantity
If the quantity object cannot be handled for the given path. This can
happen if the quantity attribute specifies an unsupported quantity, or if
the units specified for the quantity are not supported.
"""
if ansys_units and isinstance(state, ansys_units.Quantity):
try:
quantity = self.get_attr("units-quantity")
unit = get_si_unit_for_fluent_quantity(quantity)
state = state.to(unit).value
except Exception as ex:
raise UnhandledQuantity(self.path, state) from ex
try:
if ansys_units and isinstance(state, (ansys_units.Quantity, tuple)):
state = (
ansys_units.Quantity(*state) if isinstance(state, tuple) else state
)
state = state.to(self.units()).value
elif isinstance(state, tuple):
if state[1] == self.units():
state = state[0]
else:
raise UnhandledQuantity(self.path, state)
except Exception as ex:
raise UnhandledQuantity(self.path, state) from ex

return super().set_state(state=state, **kwargs)

def value_with_units(self) -> Optional[tuple]:
"""Get the value with physical units in a tuple."""
if ansys_units:
quantity = self.as_quantity()
if quantity is not None:
return (quantity.value, quantity.units.name)
units = self.units()
if units is not None:
value = self.get_state()
if isinstance(value, (float, int)):
return (value, units)

def units(self) -> Optional[str]:
"""Get the physical units of the object as a string."""
quantity = self.get_attr("units-quantity")
return get_si_unit_for_fluent_quantity(quantity)

_state_type = RealType


Expand Down
112 changes: 76 additions & 36 deletions tests/test_flobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -964,50 +964,90 @@ def test_ansys_units_integration(load_mixing_elbow_mesh):
solver = load_mixing_elbow_mesh

hot_inlet = solver.setup.boundary_conditions.velocity_inlet["hot-inlet"]
hot_inlet.turbulence.turbulent_specification = "Intensity and Hydraulic Diameter"
hot_inlet.turbulence.hydraulic_diameter = "1 [in]"
turbulence = hot_inlet.turbulence
turbulence.turbulent_specification = "Intensity and Hydraulic Diameter"

assert hot_inlet.turbulence.hydraulic_diameter() == "1 [in]"
assert hot_inlet.turbulence.hydraulic_diameter.as_quantity() == None
hydraulic_diameter = turbulence.hydraulic_diameter
hydraulic_diameter.set_state("1 [in]")
assert hydraulic_diameter() == "1 [in]"
assert hydraulic_diameter.as_quantity() == None
assert hydraulic_diameter.value_with_units() == None
assert hydraulic_diameter.units() == "m"

hot_inlet.turbulence.turbulent_intensity = 0.2
assert hot_inlet.turbulence.turbulent_intensity() == 0.2
turbulent_intensity = turbulence.turbulent_intensity
turbulent_intensity.set_state(0.2)
assert turbulent_intensity() == 0.2

# turbulent_intensity has a units-quantity attribute, 'percentage', but it
# is unsupported

# 'percentage' cannot be converted to a Quantity.
assert hot_inlet.turbulence.turbulent_intensity.as_quantity() == None
# is unsupported. So, 'percentage' cannot be converted to a Quantity.
assert turbulent_intensity.as_quantity() == None

# likewise, cannot set turbulent_intensity via a Quantity
with pytest.raises(UnhandledQuantity):
hot_inlet.turbulence.turbulent_intensity = ansys.units.Quantity(0.1, "")
turbulent_intensity.set_state(ansys.units.Quantity(0.1, ""))

hydraulic_diameter.set_state(1)
assert hydraulic_diameter.as_quantity() == ansys.units.Quantity(1, "m")
assert hydraulic_diameter.value_with_units() == (1.0, "m")
assert hydraulic_diameter.units() == "m"

hydraulic_diameter.set_state(ansys.units.Quantity(1, "in"))
assert hydraulic_diameter.as_quantity() == ansys.units.Quantity(0.0254, "m")
assert hydraulic_diameter.value_with_units() == (0.0254, "m")
assert hydraulic_diameter.units() == "m"
assert hydraulic_diameter() == 0.0254

# clip_factor has no units-quantity attribute because it is dimensionless
clip_factor = solver.setup.models.viscous.options.production_limiter.clip_factor
clip_factor.set_state(1.2)
assert clip_factor() == 1.2
assert clip_factor.as_quantity() == ansys.units.Quantity(1.2, "")
assert clip_factor.value_with_units() == (1.2, "")
assert clip_factor.units() == ""
clip_factor.set_state(ansys.units.Quantity(1.8, ""))
assert clip_factor.as_quantity() == ansys.units.Quantity(1.8, "")
assert clip_factor.value_with_units() == (1.8, "")
assert clip_factor.units() == ""

hot_inlet.turbulence.hydraulic_diameter = 1
assert (
hot_inlet.turbulence.hydraulic_diameter.as_quantity()
== ansys.units.Quantity(1, "m")
)

hot_inlet.turbulence.hydraulic_diameter = ansys.units.Quantity(1, "in")
assert (
hot_inlet.turbulence.hydraulic_diameter.as_quantity()
== ansys.units.Quantity(0.0254, "m")
)
@pytest.mark.fluent_version(">=24.1")
def test_ansys_units_integration_no_pyansys_units(load_mixing_elbow_mesh):
solver = load_mixing_elbow_mesh
ansys_units = flobject.ansys_units
flobject.ansys_units = None

hot_inlet = solver.setup.boundary_conditions.velocity_inlet["hot-inlet"]
turbulence = hot_inlet.turbulence
turbulence.turbulent_specification = "Intensity and Hydraulic Diameter"

assert hot_inlet.turbulence.hydraulic_diameter() == 0.0254
hydraulic_diameter = turbulence.hydraulic_diameter
hydraulic_diameter.set_state("1 [in]")
assert hydraulic_diameter() == "1 [in]"
assert hydraulic_diameter.as_quantity() == None
assert hydraulic_diameter.value_with_units() == None
assert hydraulic_diameter.units() == "m"

# clip_factor has no units-quantity attribute, because it is dimensionless
solver.setup.models.viscous.options.production_limiter.clip_factor.set_state(1.2)
assert solver.setup.models.viscous.options.production_limiter.clip_factor() == 1.2
assert (
solver.setup.models.viscous.options.production_limiter.clip_factor.as_quantity()
== ansys.units.Quantity(1.2, "")
)
solver.setup.models.viscous.options.production_limiter.clip_factor.set_state(
ansys.units.Quantity(1.8, "")
)
assert (
solver.setup.models.viscous.options.production_limiter.clip_factor.as_quantity()
== ansys.units.Quantity(1.8, "")
)
turbulent_intensity = turbulence.turbulent_intensity
turbulent_intensity.set_state(0.2)
assert turbulent_intensity() == 0.2

# turbulent_intensity has a units-quantity attribute, 'percentage', but it
# is unsupported. So, 'percentage' cannot be converted to a Quantity.
assert turbulent_intensity.as_quantity() == None

hydraulic_diameter.set_state(1)
assert hydraulic_diameter.as_quantity() == None
assert hydraulic_diameter.value_with_units() == (1.0, "m")
assert hydraulic_diameter.units() == "m"
hydraulic_diameter.set_state((2.0, "m"))
assert hydraulic_diameter.value_with_units() == (2.0, "m")

# clip_factor has no units-quantity attribute because it is dimensionless
clip_factor = solver.setup.models.viscous.options.production_limiter.clip_factor
clip_factor.set_state(1.2)
assert clip_factor() == 1.2
assert clip_factor.as_quantity() == None
assert clip_factor.value_with_units() == (1.2, "")
assert clip_factor.units() == ""

flobject.ansys_units = ansys_units

0 comments on commit a10c316

Please sign in to comment.