Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
install_requires = [
"ansys-api-fluent>=0.3.22",
"ansys-platform-instancemanagement~=1.0",
"ansys-units>=0.2.0",
"grpcio>=1.30.0",
"grpcio-health-checking>=1.30.0",
"numpy<2,>=1.21.5",
Expand Down
48 changes: 48 additions & 0 deletions src/ansys/fluent/core/solver/flobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
>>> r.setup.models.energy.enabled = True
>>> r.boundary_conditions.velocity_inlet['inlet'].vmag.constant = 20
"""
from __future__ import annotations

import collections
import fnmatch
import hashlib
Expand All @@ -26,8 +28,17 @@
import sys
import types
from typing import Any, Dict, Generic, List, NewType, Optional, Tuple, TypeVar, Union
import warnings
import weakref

try:
import ansys.units as ansys_units

from .flunits import get_si_unit_for_fluent_quantity
except ImportError:
get_unit_for_fl_quantity_attr = None
ansys_units = None

from .error_message import allowed_name_error_message, allowed_values_error

settings_logger = logging.getLogger("pyfluent.settings_api")
Expand Down Expand Up @@ -388,6 +399,43 @@ class Real(SettingsBase[RealType], Numerical):
expression values.
"""

def get_state_as_quantity(self) -> Optional[ansys_units.Quantity]:
"""Get the state of the object as a Quantity."""
error = None
if not ansys_units:
error = "Code not configured to support units."
if not error:
quantity = self.get_attr("units-quantity")
if not quantity:
error = f"{self.path} does not contain quantity information."
else:
try:
return ansys_units.Quantity(
value=self.get_state(),
units=get_si_unit_for_fluent_quantity(quantity),
)
except (TypeError, ValueError) as e:
error = e
warnings.warn(f"Unable to construct 'Quantity'. {error}")

def set_state(self, state: Optional[StateT] = None, **kwargs):
"""Set the state of the object.

Raises
------
TypeError
If the quantity attribute is not a string instance.
ValueError
If the quantity attribute is not present in this parameter.
"""
if ansys_units and isinstance(state, ansys_units.Quantity):
quantity = self.get_attr("units-quantity")
if not quantity:
raise ValueError(f"{self.path} does not contain quantity information.")
unit = get_si_unit_for_fluent_quantity(quantity)
state = state.to(unit).value
return super().set_state(state=state, **kwargs)

_state_type = RealType


Expand Down
187 changes: 187 additions & 0 deletions src/ansys/fluent/core/solver/flunits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
"""Module for accessing Fluent units labels per quantity type.

Example
-------
>>> units = get_si_unit_for_fluent_quantity("pressure")

The following code is employed to translate the source data
into Python data structures:

from ansys.fluent.core.filereader import lispy
import re
from pprint import pprint

fl_unit_subs = {'deg': 'radian', 'rad':'radian',}
def replace_units(match):
return fl_unit_subs[match.group(0)]

def substitute_fl_units_with_py_units(fl_units_dict):
subs = {}
for k, v in fl_units_dict.items():
new_val = re.sub('|'.join(r'\b%s\b' % re.escape(s) for s in fl_unit_subs), replace_units, v)
if new_val != v:
subs[k] = new_val
print("Substitutions:")
for k, v in subs.items():
print(f"'{fl_units_dict[k]}' -> '{v}' for '{k}'")
print("\n")
fl_units_dict.update(subs)
return fl_units_dict

def make_python_fl_unit_table(scheme_unit_table):
as_list = lispy.parse(scheme_unit_table)[1][1]
as_dict = { x[0]:x[1][3].split('"')[1] for x in as_list }
return substitute_fl_units_with_py_units(as_dict)
"""

_fl_unit_table = {
"acceleration": "m s^-2",
"angle": "radian",
"angular-velocity": "radian s^-1",
"area": "m^2",
"area-inverse": "m^-2",
"collision-rate": "m^-3 s^-1",
"concentration": "kmol m^-3",
"contact-resistance": "m^2 K W^-1",
"contact-resistance-vol": "Ohm m^3",
"crank-angle": "radian",
"crank-angular-velocity": "rev min^-1",
"current": "A",
"current-density": "A m^-2",
"current-vol-density": "A m^-3",
"density": "kg m^-3",
"density*specific-energy": "J m^-3",
"density*specific-heat": "J m^-3 K^-1",
"density*velocity": "kg m^-2 s^-1",
"density-gradient": "kg m^-4",
"density-inverse": "m^3 kg^-1",
"depth": "m",
"elec-charge": "A h",
"elec-charge-density": "A s m^-3",
"elec-conductivity": "S m^-1",
"elec-contact-resistance": "Ohm m^2",
"elec-field": "V m^-1",
"elec-permittivity": "farad m^-1",
"elec-resistance": "Ohm",
"elec-resistivity": "Ohm m",
"energy": "J",
"energy-density": "J/m2",
"force": "N",
"force*time-per-volume": "N s m^-3",
"force-per-area": "N m^-2",
"force-per-volume": "N m^-3",
"frequency": "Hz",
"gas-constant": "J kg^-1 K^-1",
"heat-flux": "W m^-2",
"heat-flux-resolved": "m K s^-1",
"heat-generation-rate": "W m^-3",
"heat-transfer-coefficient": "W m^-2 K^-1",
"ignition-energy": "J mol^-1",
"kinematic-viscosity": "m^2 s^-1",
"length": "m",
"length-inverse": "m^-1",
"length-time-inverse": "m^-1 s^-1",
"mag-permeability": "H m^-1",
"mass": "kg",
"mass-diffusivity": "m^2 s^-1",
"mass-flow": "kg s^-1",
"mass-flow-per-depth": "kg m^-1 s^-1",
"mass-flow-per-time": "kg s^-2",
"mass-flux": "kg m^-2 s^-1",
"mass-transfer-rate": "kg m^-3 s^-1",
"mole-con-henry-const": "Pa m^3 kgmol^-1",
"mole-specific-energy": "J kgmol^-1",
"mole-specific-entropy": "J kgmol^-1 K^-1",
"mole-transfer-rate": "kgmol m^-3 s^-1",
"molec-wt": "kg kmol^-1",
"moment": "N m",
"moment-of-inertia": "kg m^2",
"nucleation-rate": "m^-3 s^-1",
"number-density": "m^-3",
"particles-conc": "1.e15-particles/kg",
"particles-rate": "1.e15 m^-3 s^-1",
"percentage": "%",
"power": "W",
"power-per-time": "W s^-1",
"pressure": "Pa",
"pressure-2nd-time-derivative": "Pa s^-2",
"pressure-gradient": "Pa m^-1",
"pressure-time-deriv-sqr": "Pa^2 s^-2",
"pressure-time-derivative": "Pa s^-1",
"resistance": "m^-1",
"site-density": "kgmol m^-2",
"soot-formation-constant-unit": "kg N^-1 m^-1 s^-1",
"soot-limiting-nuclei-rate": "1e+15-particles/m3-s",
"soot-linear-termination": "m^3 s^-1",
"soot-oxidation-constant": "kg m kgmol^-1 K^-0.5 s^-1",
"soot-pre-exponential-constant": "1.e15 kg^-1 s^-1",
"soot-sitespecies-concentration": "kmol m^-3",
"soot-surface-growth-scale-factor": "kg m kgmol^-1 s^-1",
"source-elliptic-relaxation-function": "kg m^-3 s^-2",
"source-energy": "W m^-3",
"source-kinetic-energy": "kg m^-1 s^-3",
"source-mass": "kg m^-3 s^-1",
"source-momentum": "N m^-3",
"source-specific-dissipation-rate": "kg m^-3 s^-2",
"source-temperature-variance": "K^2 m^-3 s^-1",
"source-turbulent-dissipation-rate": "kg m^-1 s^-4",
"source-turbulent-viscosity": "kg m^-1 s^-2",
"specific-area": "m^2 kg^-1",
"specific-energy": "J kg^-1",
"specific-heat": "J kg^-1 K^-1",
"spring-constant": "N m^-1",
"spring-constant-angular": "N m radian^-1",
"stefan-boltzmann-constant": "W m^-2 K^-4",
"surface-density": "kg m^-2",
"surface-mole-transfer-rate": "kgmol m^-2 s^-1",
"surface-tension": "N m^-1",
"surface-tension-gradient": "N m^-1 K^-1",
"temperature": "K",
"temperature-difference": "K",
"temperature-gradient": "K m^-1",
"temperature-inverse": "K^-1",
"temperature-variance": "K^2",
"thermal-conductivity": "W m^-1 K^-1",
"thermal-resistance": "m^2 K W^-1",
"thermal-resistivity": "m K W^-1",
"thermophoretic-diffusivity": "kg m^2 s^-2",
"time": "s",
"time-inverse": "s^-1",
"time-inverse-cubed": "s^-3",
"time-inverse-squared": "s^-2",
"turb-kinetic-energy-production": "kg m^-1 s^-3",
"turbulent-energy-diss-rate": "m^2 s^-3",
"turbulent-energy-diss-rate-gradient": "m s^-3",
"turbulent-kinetic-energy": "m^2 s^-2",
"turbulent-kinetic-energy-gradient": "m s^-2",
"univ-gas-constant": "J K^-1 kgmol^-1",
"velocity": "m s^-1",
"viscosity": "kg m^-1 s^-1",
"viscosity-consistency-index": "kg s^n-2 m^-1",
"voltage": "V",
"volume": "m^3",
"volume-flow-rate": "m^3 s^-1",
"volume-flow-rate-per-depth": "m^3 s^-1 m^-1",
"volume-inverse": "m^-3",
"wave-length": "Angstrom",
"youngs-modulus": "N m^-2",
}


def get_si_unit_for_fluent_quantity(quantity: str, unit_table: dict = None):
"""Get the SI unit for the given Fluent quantity.

Raises
------
TypeError
If ``quantity`` is not a string instance.
"""
try:
if quantity.startswith("'"):
quantity = quantity[1:]
except AttributeError:
# not a string
raise TypeError(
"Invalid quantity argument type in get_si_unit_for_fluent_quantity."
)
return (unit_table or _fl_unit_table)[quantity]
30 changes: 30 additions & 0 deletions tests/test_flobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ansys.fluent.core.examples import download_file
from ansys.fluent.core.solver import flobject
from ansys.fluent.core.solver.flobject import InactiveObjectError, find_children
import ansys.units


class Setting:
Expand Down Expand Up @@ -943,3 +944,32 @@ def test_strings_with_allowed_values(load_static_mixer_settings_only):
"density-based-implicit",
"density-based-explicit",
]


@pytest.mark.fluent_version(">=24.1")
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]"

assert hot_inlet.turbulence.hydraulic_diameter() == "1 [in]"
# Could not convert string to float: '1 [in]'
# TEMPORARILY disable. pending units changes
# assert hot_inlet.turbulence.hydraulic_diameter.get_state_as_quantity() == None

# 'percentage' cannot be converted to a Quantity.
assert hot_inlet.turbulence.turbulent_intensity.get_state_as_quantity() == None

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

hot_inlet.turbulence.hydraulic_diameter = ansys.units.Quantity(1, "in")
assert (
hot_inlet.turbulence.hydraulic_diameter.get_state_as_quantity()
== ansys.units.Quantity(0.0254, "m")
)