ansys-fluent-core 0.33.0__py3-none-any.whl → 0.34.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansys-fluent-core might be problematic. Click here for more details.

Files changed (78) hide show
  1. ansys/fluent/core/__init__.py +3 -3
  2. ansys/fluent/core/codegen/builtin_settingsgen.py +25 -19
  3. ansys/fluent/core/codegen/settingsgen.py +17 -5
  4. ansys/fluent/core/codegen/tuigen.py +2 -1
  5. ansys/fluent/core/docker/docker_compose.py +4 -1
  6. ansys/fluent/core/docker/utils.py +35 -0
  7. ansys/fluent/core/exceptions.py +13 -1
  8. ansys/fluent/core/field_data_interfaces.py +239 -38
  9. ansys/fluent/core/file_session.py +139 -59
  10. ansys/fluent/core/fluent_connection.py +23 -16
  11. ansys/fluent/core/generated/api_tree/api_objects.json +1 -1
  12. ansys/fluent/core/generated/datamodel_231/flicing.py +30 -30
  13. ansys/fluent/core/generated/datamodel_231/meshing.py +171 -171
  14. ansys/fluent/core/generated/datamodel_232/flicing.py +35 -35
  15. ansys/fluent/core/generated/datamodel_232/meshing.py +223 -223
  16. ansys/fluent/core/generated/datamodel_241/flicing.py +35 -35
  17. ansys/fluent/core/generated/datamodel_241/meshing.py +264 -264
  18. ansys/fluent/core/generated/datamodel_242/flicing.py +30 -30
  19. ansys/fluent/core/generated/datamodel_242/meshing.py +369 -369
  20. ansys/fluent/core/generated/datamodel_251/flicing.py +35 -35
  21. ansys/fluent/core/generated/datamodel_251/meshing.py +331 -331
  22. ansys/fluent/core/generated/datamodel_251/part_management.py +3 -3
  23. ansys/fluent/core/generated/datamodel_252/flicing.py +50 -50
  24. ansys/fluent/core/generated/datamodel_252/meshing.py +398 -398
  25. ansys/fluent/core/generated/datamodel_252/part_management.py +5 -5
  26. ansys/fluent/core/generated/datamodel_261/flicing.py +40 -40
  27. ansys/fluent/core/generated/datamodel_261/meshing.py +416 -397
  28. ansys/fluent/core/generated/datamodel_261/part_management.py +10 -10
  29. ansys/fluent/core/generated/datamodel_261/preferences.py +7 -0
  30. ansys/fluent/core/generated/fluent_version_261.py +3 -3
  31. ansys/fluent/core/generated/meshing/tui_261.py +1186 -1180
  32. ansys/fluent/core/generated/solver/settings_231.py +1 -0
  33. ansys/fluent/core/generated/solver/settings_231.pyi +3025 -1
  34. ansys/fluent/core/generated/solver/settings_232.py +1 -0
  35. ansys/fluent/core/generated/solver/settings_232.pyi +3425 -1
  36. ansys/fluent/core/generated/solver/settings_241.py +1 -0
  37. ansys/fluent/core/generated/solver/settings_241.pyi +4423 -1
  38. ansys/fluent/core/generated/solver/settings_242.py +1 -0
  39. ansys/fluent/core/generated/solver/settings_242.pyi +5474 -1
  40. ansys/fluent/core/generated/solver/settings_251.py +11 -0
  41. ansys/fluent/core/generated/solver/settings_251.pyi +6006 -1
  42. ansys/fluent/core/generated/solver/settings_252.py +11 -1
  43. ansys/fluent/core/generated/solver/settings_252.pyi +6782 -2
  44. ansys/fluent/core/generated/solver/settings_261.py +5592 -2740
  45. ansys/fluent/core/generated/solver/settings_261.pyi +10335 -1994
  46. ansys/fluent/core/generated/solver/settings_builtin.py +56 -22
  47. ansys/fluent/core/generated/solver/settings_builtin.pyi +22 -0
  48. ansys/fluent/core/generated/solver/tui_261.py +2445 -2281
  49. ansys/fluent/core/launcher/container_launcher.py +6 -2
  50. ansys/fluent/core/launcher/error_handler.py +1 -1
  51. ansys/fluent/core/launcher/fluent_container.py +53 -10
  52. ansys/fluent/core/launcher/launcher.py +3 -0
  53. ansys/fluent/core/launcher/watchdog.py +6 -6
  54. ansys/fluent/core/launcher/watchdog_exec +1 -1
  55. ansys/fluent/core/pyfluent_warnings.py +7 -1
  56. ansys/fluent/core/report.py +2 -0
  57. ansys/fluent/core/search.py +11 -3
  58. ansys/fluent/core/services/__init__.py +2 -2
  59. ansys/fluent/core/services/app_utilities.py +39 -0
  60. ansys/fluent/core/services/deprecated_field_data.py +4 -4
  61. ansys/fluent/core/services/field_data.py +158 -41
  62. ansys/fluent/core/services/reduction.py +16 -5
  63. ansys/fluent/core/services/settings.py +1 -0
  64. ansys/fluent/core/session.py +16 -1
  65. ansys/fluent/core/session_pure_meshing.py +5 -5
  66. ansys/fluent/core/session_pure_meshing.pyi +1 -0
  67. ansys/fluent/core/session_solver.py +33 -8
  68. ansys/fluent/core/session_solver.pyi +1 -0
  69. ansys/fluent/core/solver/error_message.py +2 -2
  70. ansys/fluent/core/solver/flobject.py +187 -120
  71. ansys/fluent/core/solver/function/reduction.py +37 -9
  72. ansys/fluent/core/solver/settings_builtin_data.py +5 -3
  73. ansys/fluent/core/utils/fluent_version.py +1 -3
  74. ansys/fluent/core/utils/networking.py +18 -8
  75. {ansys_fluent_core-0.33.0.dist-info → ansys_fluent_core-0.34.0.dist-info}/METADATA +10 -11
  76. {ansys_fluent_core-0.33.0.dist-info → ansys_fluent_core-0.34.0.dist-info}/RECORD +78 -77
  77. {ansys_fluent_core-0.33.0.dist-info → ansys_fluent_core-0.34.0.dist-info}/WHEEL +1 -1
  78. {ansys_fluent_core-0.33.0.dist-info/licenses → ansys_fluent_core-0.34.0.dist-info}/LICENSE +0 -0
@@ -44,6 +44,7 @@ import collections
44
44
  from contextlib import contextmanager, nullcontext
45
45
  import fnmatch
46
46
  import hashlib
47
+ import inspect
47
48
  import keyword
48
49
  import logging
49
50
  import os
@@ -54,6 +55,7 @@ import sys
54
55
  import types
55
56
  from typing import (
56
57
  Any,
58
+ Callable,
57
59
  Dict,
58
60
  ForwardRef,
59
61
  Generic,
@@ -88,6 +90,14 @@ from .settings_external import expand_api_file_argument
88
90
  settings_logger = logging.getLogger("pyfluent.settings_api")
89
91
 
90
92
 
93
+ _static_class_attributes = [
94
+ "_version",
95
+ "_deprecated_version",
96
+ "_python_name",
97
+ "fluent_name",
98
+ ]
99
+
100
+
91
101
  class InactiveObjectError(RuntimeError):
92
102
  """Inactive object access."""
93
103
 
@@ -178,7 +188,7 @@ _ttable = str.maketrans(string.punctuation, "_" * len(string.punctuation), "?'")
178
188
  def to_python_name(fluent_name: str) -> str:
179
189
  """Convert a scheme string to a Python variable name.
180
190
 
181
- This function replaces symbols with _. Any ``?`` symbols are
191
+ This function replaces symbols with _. ``'`` and ``?`` symbols are
182
192
  ignored.
183
193
  """
184
194
  if not fluent_name:
@@ -189,6 +199,22 @@ def to_python_name(fluent_name: str) -> str:
189
199
  return name
190
200
 
191
201
 
202
+ def to_constant_name(fluent_name: str) -> str:
203
+ """Convert a scheme string to a Python constant name.
204
+
205
+ This function replaces symbols and spaces with _ and converts the name to uppercase.
206
+ ``'`` and ``?`` symbols are ignored.
207
+ """
208
+ fluent_name = fluent_name.replace(" ", "_")
209
+ name = fluent_name.translate(_ttable).upper()
210
+ if not name:
211
+ return "EMPTY_STRING"
212
+ if name[0].isdigit():
213
+ # If the first character is a digit, prepend "CASE_"
214
+ name = "CASE_" + name
215
+ return name
216
+
217
+
192
218
  _to_field_name_str = naming_strategy().to_string
193
219
 
194
220
 
@@ -236,9 +262,8 @@ def _is_deprecated(obj) -> bool | None:
236
262
  deprecated_version = (
237
263
  deprecated_version.get("deprecated-version") if deprecated_version else None
238
264
  )
239
- return deprecated_version and (
240
- FluentVersion(float(deprecated_version)) <= FluentVersion.v222
241
- or FluentVersion(obj._version) >= FluentVersion(deprecated_version)
265
+ return deprecated_version and FluentVersion(obj._version) >= FluentVersion(
266
+ deprecated_version
242
267
  )
243
268
 
244
269
 
@@ -496,6 +521,39 @@ class Base:
496
521
  return False
497
522
  return self.flproxy == other.flproxy and self.path == other.path
498
523
 
524
+ def get_completer_info(self, prefix="", excluded=None) -> List[List[str]]:
525
+ """Get completer info of all children.
526
+
527
+ Returns
528
+ -------
529
+ List[List[str]]
530
+ Name, type and docstring of all children.
531
+ """
532
+ excluded = excluded or []
533
+ ret = []
534
+ for k, v in inspect.getmembers(self):
535
+ if not k.startswith("_") and k not in excluded and k.startswith(prefix):
536
+ if isinstance(v, Base):
537
+ if not _is_deprecated(v):
538
+ ret.append(
539
+ [
540
+ k,
541
+ _get_type_for_completer_info(v.__class__),
542
+ v.__doc__,
543
+ ]
544
+ )
545
+ elif inspect.ismethod(v):
546
+ ret.append(
547
+ [
548
+ k,
549
+ "Method",
550
+ v.__doc__ or "",
551
+ ]
552
+ )
553
+ else:
554
+ ret.append([k, "Data", ""])
555
+ return ret
556
+
499
557
 
500
558
  StateT = TypeVar("StateT")
501
559
 
@@ -946,20 +1004,6 @@ class BooleanList(SettingsBase[BoolListType], Property):
946
1004
  _state_type = BoolListType
947
1005
 
948
1006
 
949
- def _command_query_name_filter(
950
- parent, list_attr: str, prefix: str, excluded: List[str]
951
- ) -> List:
952
- """Auto completer info of commands and queries."""
953
- ret = []
954
- names = getattr(parent, list_attr)
955
- for name in names:
956
- if name not in excluded and name.startswith(prefix):
957
- child = getattr(parent, name)
958
- if child.is_active() and not _is_deprecated(child):
959
- ret.append([name, child.__class__.__bases__[0].__name__, child.__doc__])
960
- return ret
961
-
962
-
963
1007
  def _get_type_for_completer_info(cls) -> str:
964
1008
  if issubclass(cls, (FileName, _InputFile)):
965
1009
  return "InputFilename"
@@ -1097,41 +1141,14 @@ class Group(SettingsBase[DictStateType]):
1097
1141
  [
1098
1142
  child
1099
1143
  for child in self.child_names + self.command_names + self.query_names
1100
- if getattr(self, child).is_active()
1101
- and _is_deprecated(getattr(self, child))
1144
+ if _is_deprecated(getattr(self, child))
1102
1145
  ]
1103
1146
  )
1104
1147
 
1105
- def get_completer_info(self, prefix="", excluded=None) -> List[List[str]]:
1106
- """Get completer info of all children.
1107
-
1108
- Returns
1109
- -------
1110
- List[List[str]]
1111
- Name, type and docstring of all children.
1112
- """
1113
- excluded = excluded or []
1114
- ret = []
1115
- for child_name in self.child_names:
1116
- if child_name not in excluded and child_name.startswith(prefix):
1117
- child = getattr(self, child_name)
1118
- if child.is_active() and not _is_deprecated(child):
1119
- ret.append(
1120
- [
1121
- child_name,
1122
- _get_type_for_completer_info(child.__class__),
1123
- child.__doc__,
1124
- ]
1125
- )
1126
- command_info = _command_query_name_filter(
1127
- self, "command_names", prefix, excluded
1128
- )
1129
- query_info = _command_query_name_filter(self, "query_names", prefix, excluded)
1130
- for items in [command_info, query_info]:
1131
- ret.extend(items)
1132
- return ret
1133
-
1134
1148
  def __getattribute__(self, name):
1149
+ # Avoiding server queries for static attributes
1150
+ if name in _static_class_attributes:
1151
+ return super().__getattribute__(name)
1135
1152
  if (
1136
1153
  name in super().__getattribute__("child_names")
1137
1154
  and self.is_active() is False
@@ -1153,9 +1170,7 @@ class Group(SettingsBase[DictStateType]):
1153
1170
  error_msg = allowed_name_error_message(
1154
1171
  trial_name=name,
1155
1172
  message=ex.args[0],
1156
- allowed_values=sorted(
1157
- set(self.get_active_child_names() + self.command_names)
1158
- ),
1173
+ allowed_values=sorted(set(self.child_names + self.command_names)),
1159
1174
  )
1160
1175
  ex.args = (error_msg,)
1161
1176
  raise
@@ -1402,24 +1417,6 @@ class NamedObject(SettingsBase[DictStateType], Generic[ChildTypeT]):
1402
1417
  obj_names_list = obj_names if isinstance(obj_names, list) else list(obj_names)
1403
1418
  return obj_names_list
1404
1419
 
1405
- def get_completer_info(self, prefix="", excluded=None) -> List[List[str]]:
1406
- """Get completer info of all children.
1407
-
1408
- Returns
1409
- -------
1410
- List[List[str]]
1411
- Name, type and docstring of all children.
1412
- """
1413
- excluded = excluded or []
1414
- ret = []
1415
- command_info = _command_query_name_filter(
1416
- self, "command_names", prefix, excluded
1417
- )
1418
- query_info = _command_query_name_filter(self, "query_names", prefix, excluded)
1419
- for items in [command_info, query_info]:
1420
- ret.extend(items)
1421
- return ret
1422
-
1423
1420
  def __getitem__(self, name: str) -> ChildTypeT:
1424
1421
  if name not in self.get_object_names():
1425
1422
  if self.flproxy.has_wildcard(name):
@@ -1475,7 +1472,54 @@ class NamedObject(SettingsBase[DictStateType], Generic[ChildTypeT]):
1475
1472
  )
1476
1473
  return alias_obj
1477
1474
  else:
1478
- return getattr(super(), name)
1475
+ try:
1476
+ return getattr(super(), name)
1477
+ except AttributeError as ex:
1478
+ raise AttributeError(
1479
+ f"'{self.__class__.__name__}' has no attribute '{name}'"
1480
+ ) from ex
1481
+
1482
+ def __add__(self, other):
1483
+ if not isinstance(other, NamedObject):
1484
+ raise TypeError(
1485
+ f"Can only add NamedObject to NamedObject, not {type(other).__name__}"
1486
+ )
1487
+ return CombinedNamedObject([self, other])
1488
+
1489
+
1490
+ class CombinedNamedObject:
1491
+ """A ``CombinedNamedObject`` contains the concatenated named-objects."""
1492
+
1493
+ def __init__(self, objects: list[NamedObject]):
1494
+ """__init__ of CombinedNamedObject."""
1495
+ self.objects = []
1496
+ self._items = []
1497
+ for obj in objects:
1498
+ if isinstance(obj, CombinedNamedObject):
1499
+ self.objects.extend(obj.objects)
1500
+ else:
1501
+ self.objects.append(obj)
1502
+ for obj in self.objects:
1503
+ self._items.extend(obj.items())
1504
+
1505
+ def items(self):
1506
+ """Return items like a dictionary."""
1507
+ return self._items
1508
+
1509
+ def __iter__(self):
1510
+ for obj in self.objects:
1511
+ yield from obj
1512
+
1513
+ def __add__(self, other):
1514
+ if not isinstance(other, NamedObject):
1515
+ raise TypeError(f"Cannot add {type(self)} to NamedObject")
1516
+ return CombinedNamedObject(self.objects + [other])
1517
+
1518
+ def __call__(self):
1519
+ temp_dict = {}
1520
+ for obj in self.objects:
1521
+ temp_dict.update(obj())
1522
+ return temp_dict
1479
1523
 
1480
1524
 
1481
1525
  def _rename(obj: NamedObject | _Alias, new: str, old: str):
@@ -1613,11 +1657,16 @@ def _get_new_keywords(obj, *args, **kwds):
1613
1657
  newkwds[argName] = arg
1614
1658
  if kwds:
1615
1659
  # Convert deprecated keywords through aliases
1616
- # We don't get arguments-aliases from static-info yet.
1617
- argument_aliases_scm = obj.get_attr("arguments-aliases") or {}
1618
- argument_aliases = {}
1619
- for k, v in argument_aliases_scm.items():
1620
- argument_aliases[to_python_name(k)] = to_python_name(v.removeprefix("'"))
1660
+ if FluentVersion(obj._version) >= FluentVersion.v252:
1661
+ argument_aliases = {k: v[0] for k, v in obj._child_aliases.items()}
1662
+ else:
1663
+ # Arguments-aliases was not statically available before v252.
1664
+ argument_aliases_scm = obj.get_attr("arguments-aliases") or {}
1665
+ argument_aliases = {}
1666
+ for k, v in argument_aliases_scm.items():
1667
+ argument_aliases[to_python_name(k)] = to_python_name(
1668
+ v.removeprefix("'")
1669
+ )
1621
1670
  for k, v in kwds.items():
1622
1671
  alias = argument_aliases.get(k)
1623
1672
  if alias:
@@ -1657,34 +1706,10 @@ class Action(Base):
1657
1706
  [
1658
1707
  child
1659
1708
  for child in self.argument_names
1660
- if getattr(self, child).is_active()
1661
- and _is_deprecated(getattr(self, child))
1709
+ if _is_deprecated(getattr(self, child))
1662
1710
  ]
1663
1711
  )
1664
1712
 
1665
- def get_completer_info(self, prefix="", excluded=None) -> List[List[str]]:
1666
- """Get completer info of all arguments.
1667
-
1668
- Returns
1669
- -------
1670
- List[List[str]]
1671
- Name, type and docstring of all arguments.
1672
- """
1673
- excluded = excluded or []
1674
- ret = []
1675
- for argument_name in self.argument_names:
1676
- if argument_name not in excluded and argument_name.startswith(prefix):
1677
- argument = getattr(self, argument_name)
1678
- if argument.is_active() and not _is_deprecated(argument):
1679
- ret.append(
1680
- [
1681
- argument_name,
1682
- _get_type_for_completer_info(argument.__class__),
1683
- argument.__doc__,
1684
- ]
1685
- )
1686
- return ret
1687
-
1688
1713
  def __getattr__(self, name: str):
1689
1714
  alias = self._child_aliases.get(name)
1690
1715
  if alias:
@@ -1721,12 +1746,16 @@ class BaseCommand(Action):
1721
1746
  print("Please enter 'y[es]' or 'n[o]'.")
1722
1747
  with self._while_executing_command():
1723
1748
  ret = self.flproxy.execute_cmd(self._parent.path, self.obj_name, **kwds)
1724
- if os.getenv("PYFLUENT_NO_FIX_PARAMETER_LIST_RETURN") != "1":
1725
- if (self._parent.path, self.obj_name) in [
1726
- ("parameters/input-parameters", "list"),
1727
- ("parameters/output-parameters", "list"),
1728
- ]:
1729
- ret = _fix_parameter_list_return(ret)
1749
+ if (
1750
+ os.getenv("PYFLUENT_NO_FIX_PARAMETER_LIST_RETURN") != "1"
1751
+ and FluentVersion(self._version) <= FluentVersion.v252
1752
+ and self.path
1753
+ in [
1754
+ "parameters/input-parameters/list",
1755
+ "parameters/output-parameters/list",
1756
+ ]
1757
+ ):
1758
+ ret = _fix_parameter_list_return(ret)
1730
1759
  return ret
1731
1760
 
1732
1761
  def execute_command(self, *args, **kwds):
@@ -1764,13 +1793,6 @@ class BaseCommand(Action):
1764
1793
  assert_type(ret, base_t._state_type)
1765
1794
  return ret
1766
1795
 
1767
- def __call__(self, *args, **kwds):
1768
- try:
1769
- return self.execute_command(*args, **kwds)
1770
- except KeyboardInterrupt:
1771
- self._root._on_interrupt(self)
1772
- raise KeyboardInterrupt
1773
-
1774
1796
 
1775
1797
  # TODO: Remove this after parameter list() method is fixed from Fluent side
1776
1798
  def _fix_parameter_list_return(val):
@@ -1807,6 +1829,8 @@ class Command(BaseCommand):
1807
1829
 
1808
1830
  def __call__(self, **kwds):
1809
1831
  """Call a command with the specified keyword arguments."""
1832
+ if not self.is_active():
1833
+ raise InactiveObjectError(self.python_path)
1810
1834
  try:
1811
1835
  return self.execute_command(**kwds)
1812
1836
  except KeyboardInterrupt:
@@ -1819,6 +1843,8 @@ class CommandWithPositionalArgs(BaseCommand):
1819
1843
 
1820
1844
  def __call__(self, *args, **kwds):
1821
1845
  """Call a command with the specified positional and keyword arguments."""
1846
+ if not self.is_active():
1847
+ raise InactiveObjectError(self.python_path)
1822
1848
  try:
1823
1849
  return self.execute_command(*args, **kwds)
1824
1850
  except KeyboardInterrupt:
@@ -1831,6 +1857,8 @@ class Query(Action):
1831
1857
 
1832
1858
  def __call__(self, **kwds):
1833
1859
  """Call a query with the specified keyword arguments."""
1860
+ if not self.is_active():
1861
+ raise InactiveObjectError(self.python_path)
1834
1862
  kwds = _get_new_keywords(self, **kwds)
1835
1863
  scmKwds = {}
1836
1864
  for arg, value in kwds.items():
@@ -2027,6 +2055,33 @@ class AllowedValuesMixin:
2027
2055
  return []
2028
2056
 
2029
2057
 
2058
+ class _MaybeActiveString(str):
2059
+ """A string class with an is_active() method."""
2060
+
2061
+ def __new__(cls, value, is_active: Callable[[], bool]):
2062
+ return super().__new__(cls, value)
2063
+
2064
+ def __init__(self, value, is_active: Callable[[], bool]):
2065
+ super().__init__()
2066
+ self.is_active = is_active
2067
+
2068
+
2069
+ class _FlStringConstant:
2070
+ """A descriptor class to hold a constant string value."""
2071
+
2072
+ def __init__(self, value):
2073
+ self._value = value
2074
+
2075
+ def __get__(self, instance, owner):
2076
+ def is_active():
2077
+ return self._value in instance.allowed_values()
2078
+
2079
+ return _MaybeActiveString(self._value, is_active=is_active)
2080
+
2081
+ def __set__(self, instance, value):
2082
+ raise AttributeError("Cannot set a constant value.")
2083
+
2084
+
2030
2085
  _bases_by_class = {}
2031
2086
 
2032
2087
 
@@ -2111,8 +2166,11 @@ def get_cls(name, info, parent=None, version=None, parent_taboo=None):
2111
2166
  dct["_child_classes"] = {}
2112
2167
  cls = type(pname, bases, dct)
2113
2168
 
2114
- deprecated_version = info.get("deprecated_version", "")
2115
- cls._deprecated_version = deprecated_version
2169
+ deprecated_version = info.get("deprecated_version", None)
2170
+ if deprecated_version and float(deprecated_version) >= 22.2:
2171
+ cls._deprecated_version = deprecated_version
2172
+ else:
2173
+ cls._deprecated_version = ""
2116
2174
 
2117
2175
  taboo = set(dir(cls))
2118
2176
  taboo |= set(
@@ -2156,7 +2214,6 @@ def get_cls(name, info, parent=None, version=None, parent_taboo=None):
2156
2214
  commands = info.get("commands")
2157
2215
  if commands:
2158
2216
  commands.pop("exit", None)
2159
- commands.pop("switch-to-meshing-mode", None)
2160
2217
  if commands and not user_creatable:
2161
2218
  commands.pop("create", None)
2162
2219
  if commands:
@@ -2195,14 +2252,14 @@ def get_cls(name, info, parent=None, version=None, parent_taboo=None):
2195
2252
  child_aliases = info.get("child-aliases") or info.get("child_aliases", {})
2196
2253
  command_aliases = info.get("command-aliases") or info.get("command_aliases", {})
2197
2254
  query_aliases = info.get("query-aliases") or info.get("query_aliases", {})
2198
- argument_aliases = info.get("arguments-aliases") or info.get(
2255
+ arguments_aliases = info.get("arguments-aliases") or info.get(
2199
2256
  "arguments_aliases", {}
2200
2257
  )
2201
- if child_aliases or command_aliases or query_aliases or argument_aliases:
2258
+ if child_aliases or command_aliases or query_aliases or arguments_aliases:
2202
2259
  cls._child_aliases = {}
2203
2260
  # No need to differentiate in the Python implementation
2204
2261
  for k, v in (
2205
- child_aliases | command_aliases | query_aliases | argument_aliases
2262
+ child_aliases | command_aliases | query_aliases | arguments_aliases
2206
2263
  ).items():
2207
2264
  # Storing the original name as we don't have any other way
2208
2265
  # to recover it at runtime.
@@ -2213,6 +2270,16 @@ def get_cls(name, info, parent=None, version=None, parent_taboo=None):
2213
2270
  k,
2214
2271
  )
2215
2272
 
2273
+ allowed_values = info.get("allowed-values") or info.get("allowed_values", [])
2274
+ if allowed_values:
2275
+ for allowed_value in allowed_values:
2276
+ setattr(
2277
+ cls,
2278
+ to_constant_name(allowed_value),
2279
+ _FlStringConstant(allowed_value),
2280
+ )
2281
+ cls._allowed_values = allowed_values
2282
+
2216
2283
  except Exception:
2217
2284
  print(
2218
2285
  f"Unable to construct class for '{name}' of "
@@ -74,16 +74,35 @@ Examples
74
74
  >>> vsquared.definition = "VelocityMagnitude ** 2"
75
75
  >>> reduction.minimum(
76
76
  ... expr = vsquared,
77
- ... locations = [
78
- ... solver1.setup.boundary_conditions.pressure_outlet,
79
- ... solver2.setup.boundary_conditions.pressure_outlet
80
- ... ])
77
+ ... locations = solver1.setup.boundary_conditions.pressure_outlet
78
+ ... + solver2.setup.boundary_conditions.pressure_outlet
79
+ ... )
81
80
  19.28151
82
81
  """
82
+ from collections.abc import Iterable
83
+ from enum import Enum
83
84
 
84
85
  import numpy as np
85
86
  from numpy import array
86
87
 
88
+ from ansys.fluent.core.exceptions import DisallowedValuesError
89
+ from ansys.fluent.core.variable_strategies import (
90
+ FluentExprNamingStrategy as naming_strategy,
91
+ )
92
+
93
+
94
+ class Weight(Enum):
95
+ """Weight for sum."""
96
+
97
+ AREA = "Area"
98
+ VOLUME = "Volume"
99
+ MASS = "Mass"
100
+ MASS_FLOW_RATE = "MassFlowRate"
101
+ ABS_MASS_FLOW_RATE = "AbsMassFlowRate"
102
+
103
+ def __str__(self):
104
+ return self.value
105
+
87
106
 
88
107
  class BadReductionRequest(Exception):
89
108
  """Raised on an attempt to make a bad reduction request."""
@@ -117,7 +136,12 @@ def _locn_name_and_obj(locn, locns):
117
136
  def _locn_names_and_objs(locns):
118
137
  if _is_iterable(locns):
119
138
  names_and_objs = []
139
+ if locns.__class__.__name__ == "CombinedNamedObject":
140
+ return locns.items()
141
+
120
142
  for locn in locns:
143
+ if isinstance(locn, Iterable) and not isinstance(locn, (str, bytes)):
144
+ raise DisallowedValuesError("location", locn, list(locn))
121
145
  name_and_obj = _locn_name_and_obj(locn, locns)
122
146
  if _is_iterable(name_and_obj):
123
147
  if isinstance(name_and_obj[0], str):
@@ -187,7 +211,7 @@ def _eval_reduction(
187
211
  weight = "Weight=" + str(weight)
188
212
  locations = str(locations) + ", " + weight
189
213
 
190
- expr_str = _expr_to_expr_str(expr)
214
+ expr_str = _expr_to_expr_str(naming_strategy().to_string(expr))
191
215
  if condition:
192
216
  expr_str = expr_str + ", " + condition
193
217
  return _eval_expr(
@@ -291,6 +315,10 @@ def _limit(limit, expr, locations, ctxt):
291
315
  return limit_val
292
316
 
293
317
 
318
+ # Weight for sum
319
+ weight = Weight
320
+
321
+
294
322
  def area_average(expression, locations, ctxt=None):
295
323
  """Compute the area averaged value of the specified expression over the specified
296
324
  locations.
@@ -579,14 +607,14 @@ def mass_flow(locations, ctxt=None):
579
607
  return _extent("MassFlow", locations, ctxt)
580
608
 
581
609
 
582
- def sum(expression, locations, weight, ctxt=None):
610
+ def sum(expression, locations, weight: str | Weight, ctxt=None):
583
611
  """Compute the sum of the specified expression over the specified locations.
584
612
 
585
613
  Parameters
586
614
  ----------
587
615
  expression : Any
588
616
  locations : Any
589
- weight: str
617
+ weight: str | Weight
590
618
  ctxt : Any, optional
591
619
  Returns
592
620
  -------
@@ -595,7 +623,7 @@ def sum(expression, locations, weight, ctxt=None):
595
623
  return _extent_expression("Sum", "Sum", expression, locations, ctxt, weight=weight)
596
624
 
597
625
 
598
- def sum_if(expression, condition, locations, weight, ctxt=None):
626
+ def sum_if(expression, condition, locations, weight: str | Weight, ctxt=None):
599
627
  """Compute the sum of the specified expression over the specified locations if a
600
628
  condition is satisfied.
601
629
 
@@ -604,7 +632,7 @@ def sum_if(expression, condition, locations, weight, ctxt=None):
604
632
  expression : Any
605
633
  condition: str
606
634
  locations : Any
607
- weight: str
635
+ weight: str | Weight
608
636
  ctxt : Any, optional
609
637
  Returns
610
638
  -------
@@ -729,23 +729,25 @@ DATA = {
729
729
  "ReadData": ("Command", "file.read_data"),
730
730
  "ReadCaseData": ("Command", "file.read_case_data"),
731
731
  "WriteCase": (
732
- "Singleton",
732
+ "Command",
733
733
  {
734
734
  since(FluentVersion.v241): "file.write_case",
735
735
  },
736
736
  ),
737
737
  "WriteData": (
738
- "Singleton",
738
+ "Command",
739
739
  {
740
740
  since(FluentVersion.v241): "file.write_data",
741
741
  },
742
742
  ),
743
743
  "WriteCaseData": (
744
- "Singleton",
744
+ "Command",
745
745
  {
746
746
  since(FluentVersion.v241): "file.write_case_data",
747
747
  },
748
748
  ),
749
749
  "Initialize": ("Command", "solution.initialization.initialize"),
750
750
  "Calculate": ("Command", "solution.run_calculation.calculate"),
751
+ "Iterate": ("Command", "solution.run_calculation.iterate"),
752
+ "DualTimeIterate": ("Command", "solution.run_calculation.dual_time_iterate"),
751
753
  }
@@ -201,9 +201,7 @@ class FluentVersion(Enum):
201
201
 
202
202
  def __str__(self) -> str:
203
203
  """String output for the Fluent version."""
204
- return (
205
- f"Fluent version 20{self.value.split('.')[0]} R{self.value.split('.')[1]}"
206
- )
204
+ return f"Ansys Fluent 20{self.value.split('.')[0]} R{self.value.split('.')[1]}"
207
205
 
208
206
 
209
207
  class FluentVersionSet(Set[FluentVersion]):
@@ -81,14 +81,24 @@ def find_remoting_ip() -> str:
81
81
  """
82
82
  from ansys.fluent.core import INFER_REMOTING_IP_TIMEOUT_PER_IP
83
83
 
84
- for addrinfo in socket.getaddrinfo(
85
- "localhost",
86
- 0,
87
- family=socket.AF_INET,
88
- type=socket.SOCK_STREAM,
89
- flags=socket.AI_PASSIVE,
90
- ):
91
- ip = addrinfo[-1][0]
84
+ all_ips = [
85
+ addrinfo[-1][0]
86
+ for addrinfo in socket.getaddrinfo(
87
+ "localhost",
88
+ 0,
89
+ family=socket.AF_INET,
90
+ type=socket.SOCK_STREAM,
91
+ flags=socket.AI_PASSIVE,
92
+ )
93
+ ]
94
+ # Check if we can establish a gRPC connection using localhost first
95
+ # before trying other IPs. It has been observed that in some systems,
96
+ # although we can establish a test gRPC connection using one of the
97
+ # resolved IP addresses in addrinfo, PyFluent fails to connect to Fluent
98
+ # using that IP address. Using localhost usually helps in such cases.
99
+ all_ips.insert(0, "localhost")
100
+
101
+ for ip in all_ips:
92
102
  port = get_free_port()
93
103
  address = f"{ip}:{port}"
94
104
  with _GrpcServer(address):