ansys-fluent-core 0.28.2__py3-none-any.whl → 0.29.dev1__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 (103) hide show
  1. ansys/fluent/core/__init__.py +1 -1
  2. ansys/fluent/core/_version.py +2 -2
  3. ansys/fluent/core/codegen/tuigen.py +1 -1
  4. ansys/fluent/core/codegen/walk_api.py +45 -18
  5. ansys/fluent/core/generated/api_tree/api_objects.json +1 -1
  6. ansys/fluent/core/generated/datamodel_252/meshing.py +1 -0
  7. ansys/fluent/core/generated/fluent_version_252.py +3 -3
  8. ansys/fluent/core/generated/solver/settings_252.py +251 -160
  9. ansys/fluent/core/generated/solver/settings_252.pyi +146 -54
  10. ansys/fluent/core/generated/solver/tui_252.py +48 -24
  11. ansys/fluent/core/launcher/container_launcher.py +4 -3
  12. ansys/fluent/core/launcher/fluent_container.py +22 -19
  13. ansys/fluent/core/launcher/launcher.py +2 -2
  14. ansys/fluent/core/launcher/pim_launcher.py +2 -2
  15. ansys/fluent/core/launcher/slurm_launcher.py +2 -2
  16. ansys/fluent/core/launcher/standalone_launcher.py +2 -2
  17. ansys/fluent/core/logging.py +2 -0
  18. ansys/fluent/core/logging_config.yaml +3 -0
  19. ansys/fluent/core/services/datamodel_se.py +4 -1
  20. ansys/fluent/core/services/field_data.py +252 -0
  21. ansys/fluent/core/services/interceptors.py +28 -2
  22. ansys/fluent/core/session.py +7 -2
  23. ansys/fluent/core/session_solver.py +21 -0
  24. {ansys_fluent_core-0.28.2.dist-info → ansys_fluent_core-0.29.dev1.dist-info}/LICENSE +1 -1
  25. {ansys_fluent_core-0.28.2.dist-info → ansys_fluent_core-0.29.dev1.dist-info}/METADATA +53 -32
  26. {ansys_fluent_core-0.28.2.dist-info → ansys_fluent_core-0.29.dev1.dist-info}/RECORD +61 -137
  27. {ansys_fluent_core-0.28.2.dist-info → ansys_fluent_core-0.29.dev1.dist-info}/WHEEL +1 -1
  28. ansys/fluent/core/docs/README.rst +0 -155
  29. ansys/fluent/tests/conftest.py +0 -415
  30. ansys/fluent/tests/fluent_fixtures.py +0 -195
  31. ansys/fluent/tests/integration/test_optislang/test_optislang_integration.py +0 -263
  32. ansys/fluent/tests/parametric/test_local_parametric_run.py +0 -36
  33. ansys/fluent/tests/parametric/test_local_parametric_setup.py +0 -34
  34. ansys/fluent/tests/parametric/test_parametric_workflow.py +0 -279
  35. ansys/fluent/tests/test_aero_session.py +0 -88
  36. ansys/fluent/tests/test_batch_ops.py +0 -39
  37. ansys/fluent/tests/test_builtin_settings.py +0 -761
  38. ansys/fluent/tests/test_cad_to_post_ftm.py +0 -525
  39. ansys/fluent/tests/test_cad_to_post_wtm.py +0 -250
  40. ansys/fluent/tests/test_casereader.py +0 -324
  41. ansys/fluent/tests/test_codegen.py +0 -783
  42. ansys/fluent/tests/test_creatable.py +0 -31
  43. ansys/fluent/tests/test_data_model_cache.py +0 -434
  44. ansys/fluent/tests/test_datamodel_api.py +0 -429
  45. ansys/fluent/tests/test_datamodel_service.py +0 -814
  46. ansys/fluent/tests/test_datareader.py +0 -103
  47. ansys/fluent/tests/test_error_handling.py +0 -24
  48. ansys/fluent/tests/test_events_manager.py +0 -214
  49. ansys/fluent/tests/test_field_data.py +0 -466
  50. ansys/fluent/tests/test_file_session.py +0 -355
  51. ansys/fluent/tests/test_file_transfer_service.py +0 -165
  52. ansys/fluent/tests/test_fix_doc.py +0 -29
  53. ansys/fluent/tests/test_flobject.py +0 -1235
  54. ansys/fluent/tests/test_fluent_fixes.py +0 -106
  55. ansys/fluent/tests/test_fluent_session.py +0 -270
  56. ansys/fluent/tests/test_fluent_version.py +0 -66
  57. ansys/fluent/tests/test_fluent_version_marker.py +0 -65
  58. ansys/fluent/tests/test_icing_session.py +0 -9
  59. ansys/fluent/tests/test_launcher.py +0 -529
  60. ansys/fluent/tests/test_launcher_remote.py +0 -272
  61. ansys/fluent/tests/test_lispy.py +0 -40
  62. ansys/fluent/tests/test_logging.py +0 -16
  63. ansys/fluent/tests/test_mapped_api.py +0 -766
  64. ansys/fluent/tests/test_meshing_utilities.py +0 -2436
  65. ansys/fluent/tests/test_meshing_workflow.py +0 -421
  66. ansys/fluent/tests/test_meshingmode/test_meshing_launch.py +0 -168
  67. ansys/fluent/tests/test_new_meshing_workflow.py +0 -1801
  68. ansys/fluent/tests/test_preferences.py +0 -89
  69. ansys/fluent/tests/test_pure_mesh_vs_mesh_workflow.py +0 -101
  70. ansys/fluent/tests/test_reduction.py +0 -484
  71. ansys/fluent/tests/test_rp_vars.py +0 -77
  72. ansys/fluent/tests/test_scheduler.py +0 -471
  73. ansys/fluent/tests/test_scheme_eval_222.py +0 -338
  74. ansys/fluent/tests/test_scheme_eval_231.py +0 -243
  75. ansys/fluent/tests/test_search.py +0 -344
  76. ansys/fluent/tests/test_session.py +0 -594
  77. ansys/fluent/tests/test_settings_api.py +0 -606
  78. ansys/fluent/tests/test_settings_reader.py +0 -85
  79. ansys/fluent/tests/test_slurm_future.py +0 -67
  80. ansys/fluent/tests/test_solution_variables.py +0 -241
  81. ansys/fluent/tests/test_solver_monitors.py +0 -83
  82. ansys/fluent/tests/test_solvermode/boundaries_periodic_expDict +0 -1712
  83. ansys/fluent/tests/test_solvermode/test_boundaries.py +0 -127
  84. ansys/fluent/tests/test_solvermode/test_calculationactivities.py +0 -20
  85. ansys/fluent/tests/test_solvermode/test_controls.py +0 -131
  86. ansys/fluent/tests/test_solvermode/test_general.py +0 -109
  87. ansys/fluent/tests/test_solvermode/test_initialization.py +0 -83
  88. ansys/fluent/tests/test_solvermode/test_materials.py +0 -40
  89. ansys/fluent/tests/test_solvermode/test_methods.py +0 -65
  90. ansys/fluent/tests/test_solvermode/test_models.py +0 -99
  91. ansys/fluent/tests/test_solvermode/test_named_expressions.py +0 -35
  92. ansys/fluent/tests/test_solvermode/test_post_vector.py +0 -22
  93. ansys/fluent/tests/test_solvermode/test_species_model.py +0 -67
  94. ansys/fluent/tests/test_streaming_services.py +0 -52
  95. ansys/fluent/tests/test_systemcoupling.py +0 -44
  96. ansys/fluent/tests/test_topy.py +0 -179
  97. ansys/fluent/tests/test_tui_api.py +0 -70
  98. ansys/fluent/tests/test_type_stub.py +0 -37
  99. ansys/fluent/tests/test_utils.py +0 -82
  100. ansys/fluent/tests/util/__init__.py +0 -36
  101. ansys/fluent/tests/util/meshing_workflow.py +0 -33
  102. ansys/fluent/tests/util/solver.py +0 -72
  103. ansys_fluent_core-0.28.2.dist-info/AUTHORS +0 -12
@@ -239,16 +239,23 @@ def configure_container_dict(
239
239
  logger.warning(
240
240
  f"Starting Fluent container mounted to {mount_source}, with this path available as {mount_target} for the Fluent session running inside the container."
241
241
  )
242
-
243
- if not port and "ports" in container_dict:
242
+ port_mapping = {port: port} if port else {}
243
+ if not port_mapping and "ports" in container_dict:
244
244
  # take the specified 'port', OR the first port value from the specified 'ports', for Fluent to use
245
- port = next(iter(container_dict["ports"].values()))
246
- if not port and pyfluent.LAUNCH_FLUENT_PORT:
245
+ port_mapping = container_dict["ports"]
246
+ if not port_mapping and pyfluent.LAUNCH_FLUENT_PORT:
247
247
  port = pyfluent.LAUNCH_FLUENT_PORT
248
- if not port:
248
+ port_mapping = {port: port}
249
+ if not port_mapping:
249
250
  port = get_free_port()
251
+ port_mapping = {port: port}
250
252
 
251
- container_dict.update(ports={str(port): port}) # container port : host port
253
+ container_dict.update(
254
+ ports={str(x): y for x, y in port_mapping.items()}
255
+ ) # container port : host port
256
+ container_grpc_port = next(
257
+ iter(port_mapping.values())
258
+ ) # the first port in the mapping is chosen as the gRPC port
252
259
 
253
260
  if "environment" not in container_dict:
254
261
  if not license_server:
@@ -259,7 +266,7 @@ def configure_container_dict(
259
266
  container_dict.update(
260
267
  environment={
261
268
  "ANSYSLMD_LICENSE_FILE": license_server,
262
- "REMOTING_PORTS": f"{port}/portspan=2",
269
+ "REMOTING_PORTS": f"{container_grpc_port}/portspan=2",
263
270
  }
264
271
  )
265
272
 
@@ -319,16 +326,10 @@ def configure_container_dict(
319
326
  container_dict["environment"] = {}
320
327
  container_dict["environment"]["FLUENT_NO_AUTOMATIC_TRANSCRIPT"] = "1"
321
328
 
322
- if (
323
- os.getenv("REMOTING_NEW_DM_API") == "1"
324
- or os.getenv("REMOTING_MAPPED_NEW_DM_API") == "1"
325
- ):
329
+ if os.getenv("REMOTING_NEW_DM_API") == "1":
326
330
  if "environment" not in container_dict:
327
331
  container_dict["environment"] = {}
328
- if os.getenv("REMOTING_NEW_DM_API") == "1":
329
- container_dict["environment"]["REMOTING_NEW_DM_API"] = "1"
330
- if os.getenv("REMOTING_MAPPED_NEW_DM_API") == "1":
331
- container_dict["environment"]["REMOTING_MAPPED_NEW_DM_API"] = "1"
332
+ container_dict["environment"]["REMOTING_NEW_DM_API"] = "1"
332
333
 
333
334
  if pyfluent.LAUNCH_FLUENT_IP or os.getenv("REMOTING_SERVER_ADDRESS"):
334
335
  if "environment" not in container_dict:
@@ -360,7 +361,7 @@ def configure_container_dict(
360
361
  return (
361
362
  container_dict,
362
363
  timeout,
363
- port,
364
+ container_grpc_port,
364
365
  host_server_info_file,
365
366
  remove_server_info_file,
366
367
  )
@@ -368,7 +369,7 @@ def configure_container_dict(
368
369
 
369
370
  def start_fluent_container(
370
371
  args: List[str], container_dict: dict | None = None
371
- ) -> (int, str):
372
+ ) -> tuple[int, str, Any]:
372
373
  """Start a Fluent container.
373
374
 
374
375
  Parameters
@@ -439,7 +440,9 @@ def start_fluent_container(
439
440
 
440
441
  logger.debug("Starting Fluent docker container...")
441
442
 
442
- docker_client.containers.run(config_dict.pop("fluent_image"), **config_dict)
443
+ container = docker_client.containers.run(
444
+ config_dict.pop("fluent_image"), **config_dict
445
+ )
443
446
 
444
447
  success = timeout_loop(
445
448
  lambda: host_server_info_file.stat().st_mtime > last_mtime, timeout
@@ -452,7 +455,7 @@ def start_fluent_container(
452
455
  else:
453
456
  _, _, password = _parse_server_info_file(str(host_server_info_file))
454
457
 
455
- return port, password
458
+ return port, password, container
456
459
  finally:
457
460
  if remove_server_info_file and host_server_info_file.exists():
458
461
  host_server_info_file.unlink()
@@ -130,8 +130,8 @@ def launch_fluent(
130
130
  Parameters
131
131
  ----------
132
132
  product_version : FluentVersion or str or float or int, optional
133
- Version of Ansys Fluent to launch. To use Fluent version 2024 R2, pass
134
- any of ``FluentVersion.v242``, ``"24.2.0"``, ``"24.2"``, ``24.2``or ``242``.
133
+ Version of Ansys Fluent to launch. To use Fluent version 2025 R1, pass
134
+ any of ``FluentVersion.v251``, ``"25.1.0"``, ``"25.1"``, ``25.1``or ``251``.
135
135
  The default is ``None``, in which case the newest installed version is used.
136
136
  dimension : Dimension or int, optional
137
137
  Geometric dimensionality of the Fluent simulation. The default is ``None``,
@@ -76,8 +76,8 @@ class PIMLauncher:
76
76
  ``FluentWindowsGraphicsDriver`` enum in Windows or the values of the
77
77
  ``FluentLinuxGraphicsDriver`` enum in Linux.
78
78
  product_version : FluentVersion or str or float or int, optional
79
- Version of Ansys Fluent to launch. To use Fluent version 2024 R2, pass
80
- ``FluentVersion.v242``, ``"24.2.0"``, ``"24.2"``, ``24.2``, or ``242``.
79
+ Version of Ansys Fluent to launch. To use Fluent version 2025 R1, pass
80
+ ``FluentVersion.v251``, ``"25.1.0"``, ``"25.1"``, ``25.1``, or ``251``.
81
81
  The default is ``None``, in which case the newest installed version is used.
82
82
  dimension : Dimension or int, optional
83
83
  Geometric dimensionality of the Fluent simulation. The default is ``None``,
@@ -301,8 +301,8 @@ class SlurmLauncher:
301
301
  ``FluentWindowsGraphicsDriver`` enum in Windows or the values of the
302
302
  ``FluentLinuxGraphicsDriver`` enum in Linux.
303
303
  product_version : FluentVersion or str or float or int, optional
304
- Version of Ansys Fluent to launch. To use Fluent version 2024 R2, pass
305
- ``FluentVersion.v242``, ``"24.2.0"``, ``"24.2"``, ``24.2``, or ``242``.
304
+ Version of Ansys Fluent to launch. To use Fluent version 2025 R1, pass
305
+ ``FluentVersion.v251``, ``"25.1.0"``, ``"25.1"``, ``25.1``, or ``251``.
306
306
  The default is ``None``, in which case the newest installed version is used.
307
307
  dimension : Dimension or int, optional
308
308
  Geometric dimensionality of the Fluent simulation. The default is ``None``,
@@ -96,8 +96,8 @@ class StandaloneLauncher:
96
96
  ``FluentWindowsGraphicsDriver`` enum in Windows or the values of the
97
97
  ``FluentLinuxGraphicsDriver`` enum in Linux.
98
98
  product_version : FluentVersion or str or float or int, optional
99
- Version of Ansys Fluent to launch. To use Fluent version 2024 R2, pass
100
- ``FluentVersion.v242``, ``"24.2.0"``, ``"24.2"``, ``24.2``, or ``242``.
99
+ Version of Ansys Fluent to launch. To use Fluent version 2025 R1, pass
100
+ ``FluentVersion.v251``, ``"25.1.0"``, ``"25.1"``, ``25.1``, or ``251``.
101
101
  The default is ``None``, in which case the newest installed version is used.
102
102
  dimension : Dimension or int, optional
103
103
  Geometric dimensionality of the Fluent simulation. The default is ``None``,
@@ -61,6 +61,8 @@ def get_default_config() -> dict:
61
61
  'maxBytes': 10485760}},
62
62
  'loggers': {'pyfluent.datamodel': {'handlers': ['pyfluent_file'],
63
63
  'level': 'DEBUG'},
64
+ 'pyfluent.field_data': {'handlers': ['pyfluent_file'],
65
+ 'level': 'DEBUG'},
64
66
  'pyfluent.general': {'handlers': ['pyfluent_file'],
65
67
  'level': 'DEBUG'},
66
68
  'pyfluent.launcher': {'handlers': ['pyfluent_file'],
@@ -43,3 +43,6 @@ loggers:
43
43
  pyfluent.post_objects:
44
44
  level: DEBUG
45
45
  handlers: [pyfluent_file]
46
+ pyfluent.field_data:
47
+ level: DEBUG
48
+ handlers: [pyfluent_file]
@@ -176,6 +176,8 @@ class DatamodelServiceImpl:
176
176
  self._stub = DataModelGrpcModule.DataModelStub(intercept_channel)
177
177
  self._metadata = metadata
178
178
  self.file_transfer_service = file_transfer_service
179
+ if os.getenv("REMOTING_MAPPED_NEW_DM_API") == "1":
180
+ self._metadata.append(("mapped", "1"))
179
181
 
180
182
  # TODO: Remove it from the proto interface
181
183
  def initialize_datamodel(
@@ -1969,7 +1971,8 @@ class PyCommand:
1969
1971
  id,
1970
1972
  static_info.get("args"),
1971
1973
  )
1972
- except RuntimeError:
1974
+ # Possible error thrown from the grpc layer
1975
+ except (RuntimeError, ValueError):
1973
1976
  logger.warning(
1974
1977
  "Create command arguments object is available from 23.1 onwards"
1975
1978
  )
@@ -1,8 +1,12 @@
1
1
  """Wrappers over FieldData gRPC service of Fluent."""
2
2
 
3
+ from dataclasses import dataclass, field
3
4
  from enum import Enum
4
5
  from functools import reduce
6
+ import logging
7
+ import time
5
8
  from typing import Callable, Dict, List, Tuple
9
+ import weakref
6
10
 
7
11
  import grpc
8
12
  import numpy as np
@@ -19,6 +23,8 @@ from ansys.fluent.core.services.interceptors import (
19
23
  from ansys.fluent.core.services.streaming import StreamingService
20
24
  from ansys.fluent.core.utils.deprecate import deprecate_argument, deprecate_arguments
21
25
 
26
+ logger = logging.getLogger("pyfluent.field_data")
27
+
22
28
 
23
29
  def override_help_text(func, func_to_be_wrapped):
24
30
  """Override function help text."""
@@ -82,6 +88,28 @@ class FieldDataService(StreamingService):
82
88
  )
83
89
  return chunk_iterator
84
90
 
91
+ def get_solver_mesh_nodes(
92
+ self, request: FieldDataProtoModule.GetSolverMeshNodesRequest
93
+ ):
94
+ """GetSolverMeshNodesDouble RPC of FieldData service."""
95
+ responses = self._stub.GetSolverMeshNodesDouble(
96
+ request, metadata=self._metadata
97
+ )
98
+ nested_nodes = []
99
+ for response in responses:
100
+ nested_nodes.append(response.nodes)
101
+ return nested_nodes
102
+
103
+ def get_solver_mesh_elements(
104
+ self, request: FieldDataProtoModule.GetSolverMeshElementsRequest
105
+ ):
106
+ """GetSolverMeshElements RPC of FieldData service."""
107
+ responses = self._stub.GetSolverMeshElements(request, metadata=self._metadata)
108
+ elementss = []
109
+ for response in responses:
110
+ elementss.append(response.elements)
111
+ return elementss
112
+
85
113
 
86
114
  class FieldInfo:
87
115
  """Provides access to Fluent field information.
@@ -922,6 +950,134 @@ class ChunkParser:
922
950
  return fields_data
923
951
 
924
952
 
953
+ # Root domain id in Fluent.
954
+ ROOT_DOMAIN_ID = 1
955
+
956
+
957
+ class ZoneType(Enum):
958
+ """Zone types for mesh."""
959
+
960
+ CELL = 1
961
+ FACE = 2
962
+
963
+
964
+ @dataclass
965
+ class ZoneInfo:
966
+ """Zone information for mesh.
967
+
968
+ Attributes:
969
+ -----------
970
+ _id : int
971
+ Zone ID.
972
+ name : str
973
+ Name of the zone.
974
+ zone_type : ZoneType
975
+ Type of the zone for mesh.
976
+ """
977
+
978
+ _id: int
979
+ name: str
980
+ zone_type: ZoneType
981
+
982
+
983
+ @dataclass
984
+ class Node:
985
+ """Node class for mesh.
986
+
987
+ Attributes:
988
+ -----------
989
+ x : float
990
+ x-coordinate of the node.
991
+ y : float
992
+ y-coordinate of the node.
993
+ z : float
994
+ z-coordinate of the node.
995
+ """
996
+
997
+ _id: int
998
+ x: float
999
+ y: float
1000
+ z: float
1001
+
1002
+
1003
+ class CellElementType(Enum):
1004
+ """Element types for a cell element."""
1005
+
1006
+ # 3 nodes, 3 faces
1007
+ TRIANGLE = 1
1008
+ # 4 nodes, 4 faces
1009
+ TETRAHEDRON = 2
1010
+ # 4 nodes, 4 faces
1011
+ QUADRILATERAL = 3
1012
+ # 8 nodes, 6 faces
1013
+ HEXAHEDRON = 4
1014
+ # 5 nodes, 5 faces
1015
+ PYRAMID = 5
1016
+ # 6 nodes, 5 faces
1017
+ WEDGE = 6
1018
+ # Arbitrary number of nodes and faces
1019
+ POLYHEDRON = 7
1020
+ # 2 nodes, 1 face (only in 2D)
1021
+ GHOST = 8
1022
+ # 10 nodes, 4 faces
1023
+ QUADRATIC_TETRAHEDRON = 9
1024
+ # 20 nodes, 6 faces
1025
+ QUADRATIC_HEXAHEDRON = 10
1026
+ # 13 nodes, 5 faces
1027
+ QUADRATIC_PYRAMID = 11
1028
+ # 15 nodes, 5 faces
1029
+ QUADRATIC_WEDGE = 12
1030
+
1031
+
1032
+ @dataclass
1033
+ class Facet:
1034
+ """Facet class within a mesh element.
1035
+
1036
+ Attributes:
1037
+ -----------
1038
+ node_indices : list[int]
1039
+ 0-based node indices of the facet.
1040
+ """
1041
+
1042
+ node_indices: list[int]
1043
+
1044
+
1045
+ @dataclass
1046
+ class Element:
1047
+ """Element class for mesh.
1048
+
1049
+ Attributes:
1050
+ -----------
1051
+ element_type : CellElementType
1052
+ Element type of the element.
1053
+ node_indices : list[int]
1054
+ 0-based node indices of the element. Populated for standard elements.
1055
+ facets : list[Facet]
1056
+ List of facets of the element. Populated for polyhedral elements.
1057
+ """
1058
+
1059
+ _id: int
1060
+ element_type: CellElementType
1061
+ node_indices: list[int] = field(default_factory=list)
1062
+ facets: list[Facet] = field(default_factory=list)
1063
+
1064
+
1065
+ @dataclass
1066
+ class Mesh:
1067
+ """Mesh class for Fluent field data.
1068
+
1069
+ Attributes:
1070
+ -----------
1071
+ nodes : list[Node]
1072
+ List of nodes in the mesh.
1073
+ elements : list[Element]
1074
+ List of elements in the mesh.
1075
+ """
1076
+
1077
+ nodes: list[Node]
1078
+ elements: list[Element]
1079
+
1080
+
925
1081
  class FieldData:
926
1082
  """Provides access to Fluent field data on surfaces."""
927
1083
 
@@ -931,12 +1087,14 @@ class FieldData:
931
1087
  field_info: FieldInfo,
932
1088
  is_data_valid: Callable[[], bool],
933
1089
  scheme_eval=None,
1090
+ get_zones_info: weakref.WeakMethod[Callable[[], list[ZoneInfo]]] | None = None,
934
1091
  ):
935
1092
  """__init__ method of FieldData class."""
936
1093
  self._service = service
937
1094
  self._field_info = field_info
938
1095
  self.is_data_valid = is_data_valid
939
1096
  self.scheme_eval = scheme_eval
1097
+ self.get_zones_info = lambda: get_zones_info()()
940
1098
 
941
1099
  self._allowed_surface_names = _AllowedSurfaceNames(field_info)
942
1100
 
@@ -1285,3 +1443,97 @@ class FieldData:
1285
1443
  field_name: pathlines_data[surface_ids[count]][field_name],
1286
1444
  }
1287
1445
  return path_lines_dict
1446
+
1447
+ def get_mesh(self, zone: str | int) -> Mesh:
1448
+ """Get mesh for a zone.
1449
+
1450
+ Parameters
1451
+ ----------
1452
+ zone : str | int
1453
+ Zone name or id. Currently, only cell zones are supported.
1454
+
1455
+ Returns
1456
+ -------
1457
+ Mesh
1458
+ Mesh object containing nodes and elements.
1459
+
1460
+ Raises
1461
+ ------
1462
+ ValueError
1463
+ If the zone is not found.
1464
+ NotImplementedError
1465
+ If a face zone is provided.
1466
+ """
1467
+ zone_info = None
1468
+ for zone_info in self.get_zones_info():
1469
+ if zone_info.name == zone or zone_info._id == zone:
1470
+ break
1471
+ if zone_info is None:
1472
+ raise ValueError(f"Zone {zone} not found.")
1473
+ if zone_info.zone_type == ZoneType.FACE:
1474
+ raise NotImplementedError("Face zone mesh is not supported.")
1475
+
1476
+ # Mesh data is retrieved from the root domain in Fluent
1477
+ logger.info(f"Getting nodes data for zone {zone_info._id}")
1478
+ start_time = time.time()
1479
+ nodes_request = FieldDataProtoModule.GetSolverMeshNodesRequest(
1480
+ domain_id=ROOT_DOMAIN_ID, thread_id=zone_info._id
1481
+ )
1482
+ nested_nodes = self._service.get_solver_mesh_nodes(nodes_request)
1483
+ logger.info(f"Nodes data received in {time.time() - start_time} seconds")
1484
+ logger.info(f"Getting elements for zone {zone_info._id}")
1485
+ start_time = time.time()
1486
+ elements_request = FieldDataProtoModule.GetSolverMeshElementsRequest(
1487
+ domain_id=ROOT_DOMAIN_ID, thread_id=zone_info._id
1488
+ )
1489
+ elementss_pb = self._service.get_solver_mesh_elements(elements_request)
1490
+ logger.info(f"Elements data received in {time.time() - start_time} seconds")
1491
+ logger.info("Constructing nodes structure in PyFluent")
1492
+ start_time = time.time()
1493
+ node_count = sum(len(nodes) for nodes in nested_nodes)
1494
+ nodes = np.empty(node_count, dtype=Node)
1495
+ node_index_by_id = {}
1496
+ i = 0
1497
+ for nodes_pb in nested_nodes:
1498
+ for node_pb in nodes_pb:
1499
+ nodes[i] = Node(_id=node_pb.id, x=node_pb.x, y=node_pb.y, z=node_pb.z)
1500
+ node_index_by_id[node_pb.id] = i
1501
+ i += 1
1502
+ logger.info(
1503
+ f"Nodes structure constructed in {time.time() - start_time} seconds"
1504
+ )
1505
+ logger.info("Constructing elements structure in PyFluent")
1506
+ start_time = time.time()
1507
+ element_count = sum(len(elements) for elements in elementss_pb)
1508
+ elements = np.empty(element_count, dtype=Element)
1509
+ i = 0
1510
+ for elements_pb in elementss_pb:
1511
+ for element_pb in elements_pb:
1512
+ element_type = CellElementType(element_pb.element_type)
1513
+ if element_type == CellElementType.POLYHEDRON:
1514
+ facets = []
1515
+ for facet_pb in element_pb.facets:
1516
+ facet = Facet(
1517
+ node_indices=[node_index_by_id[id] for id in facet_pb.node]
1518
+ )
1519
+ facets.append(facet)
1520
+ element = Element(
1521
+ _id=element_pb.id,
1522
+ element_type=element_type,
1523
+ facets=facets,
1524
+ )
1525
+ else:
1526
+ element = Element(
1527
+ _id=element_pb.id,
1528
+ element_type=element_type,
1529
+ node_indices=[
1530
+ node_index_by_id[id] for id in element_pb.node_ids
1531
+ ],
1532
+ )
1533
+ elements[i] = element
1534
+ i += 1
1535
+ logger.info(
1536
+ f"Elements structure constructed in {time.time() - start_time} seconds"
1537
+ )
1538
+ logger.info("Returning mesh")
1539
+ return Mesh(nodes=nodes, elements=elements)
@@ -1,11 +1,12 @@
1
1
  """Interceptor classes to use with gRPC services."""
2
2
 
3
+ import builtins
3
4
  import logging
4
5
  import os
5
6
  from typing import Any
6
7
 
7
8
  from google.protobuf.json_format import MessageToDict
8
- from google.protobuf.message import Message
9
+ from google.protobuf.message import DecodeError, Message
9
10
  import grpc
10
11
 
11
12
  from ansys.fluent.core.services.batch_ops import BatchOps
@@ -15,6 +16,10 @@ log_bytes_limit: int = int(os.getenv("PYFLUENT_GRPC_LOG_BYTES_LIMIT", 1000))
15
16
  truncate_len: int = log_bytes_limit // 5
16
17
 
17
18
 
19
+ def _upper_snake_case_to_camel_case(name: str) -> str:
20
+ return "".join([word.capitalize() for word in name.split("_") if word])
21
+
22
+
18
23
  def _truncate_grpc_str(message: Message) -> str:
19
24
  message_bytes = message.ByteSize()
20
25
  message_str = str(MessageToDict(message))
@@ -107,7 +112,28 @@ class GrpcErrorInterceptor(grpc.UnaryUnaryClientInterceptor):
107
112
  response = continuation(client_call_details, request)
108
113
  if response.exception() is not None and response.code() != grpc.StatusCode.OK:
109
114
  ex = response.exception()
110
- new_ex = RuntimeError(
115
+ new_ex_cls = RuntimeError
116
+ try:
117
+ from google.rpc import error_details_pb2
118
+ from grpc_status import rpc_status
119
+
120
+ status = rpc_status.from_call(ex)
121
+ if status:
122
+ for detail in status.details:
123
+ if detail.Is(error_details_pb2.ErrorInfo.DESCRIPTOR):
124
+ info = error_details_pb2.ErrorInfo()
125
+ detail.Unpack(info)
126
+ if info.domain == "Python":
127
+ reason = info.reason
128
+ ex_cls_name = _upper_snake_case_to_camel_case(reason)
129
+ if hasattr(builtins, ex_cls_name):
130
+ cls = getattr(builtins, ex_cls_name)
131
+ if issubclass(cls, Exception):
132
+ new_ex_cls = cls
133
+ break
134
+ except DecodeError:
135
+ pass
136
+ new_ex = new_ex_cls(
111
137
  ex.details() if isinstance(ex, grpc.RpcError) else str(ex)
112
138
  )
113
139
  new_ex.__context__ = ex
@@ -3,7 +3,7 @@
3
3
  from enum import Enum
4
4
  import json
5
5
  import logging
6
- from typing import Any, Dict
6
+ from typing import Any, Callable, Dict
7
7
  import warnings
8
8
  import weakref
9
9
 
@@ -11,7 +11,7 @@ from ansys.fluent.core.fluent_connection import FluentConnection
11
11
  from ansys.fluent.core.journaling import Journal
12
12
  from ansys.fluent.core.services import service_creator
13
13
  from ansys.fluent.core.services.app_utilities import AppUtilitiesOld
14
- from ansys.fluent.core.services.field_data import FieldDataService
14
+ from ansys.fluent.core.services.field_data import FieldDataService, ZoneInfo
15
15
  from ansys.fluent.core.services.scheme_eval import SchemeEval
16
16
  from ansys.fluent.core.streaming_services.datamodel_event_streaming import (
17
17
  DatamodelEvents,
@@ -85,6 +85,7 @@ class BaseSession:
85
85
  Close the Fluent connection and exit Fluent.
86
86
  """
87
87
 
88
+ # We are passing around an WeakMethod to avoid circular references
88
89
  def __init__(
89
90
  self,
90
91
  fluent_connection: FluentConnection,
@@ -93,6 +94,7 @@ class BaseSession:
93
94
  start_transcript: bool = True,
94
95
  launcher_args: Dict[str, Any] | None = None,
95
96
  event_type: Enum | None = None,
97
+ get_zones_info: weakref.WeakMethod[Callable[[], list[ZoneInfo]]] | None = None,
96
98
  ):
97
99
  """BaseSession.
98
100
 
@@ -120,6 +122,7 @@ class BaseSession:
120
122
  scheme_eval,
121
123
  file_transfer_service,
122
124
  event_type,
125
+ get_zones_info,
123
126
  )
124
127
 
125
128
  def _build_from_fluent_connection(
@@ -128,6 +131,7 @@ class BaseSession:
128
131
  scheme_eval: SchemeEval,
129
132
  file_transfer_service: Any | None = None,
130
133
  event_type=None,
134
+ get_zones_info: weakref.WeakMethod[Callable[[], list[ZoneInfo]]] | None = None,
131
135
  ):
132
136
  """Build a BaseSession object from fluent_connection object."""
133
137
  self._fluent_connection = fluent_connection
@@ -205,6 +209,7 @@ class BaseSession:
205
209
  self.field_info,
206
210
  self._is_solution_data_valid,
207
211
  _session.scheme_eval,
212
+ get_zones_info,
208
213
  )
209
214
  self.field_data_streaming = FieldDataStreaming(
210
215
  _session._fluent_connection._id, _session._field_data_service
@@ -6,9 +6,12 @@ import logging
6
6
  import threading
7
7
  from typing import Any, Dict
8
8
  import warnings
9
+ import weakref
9
10
 
11
+ from ansys.api.fluent.v0 import svar_pb2 as SvarProtoModule
10
12
  import ansys.fluent.core as pyfluent
11
13
  from ansys.fluent.core.services import SchemeEval, service_creator
14
+ from ansys.fluent.core.services.field_data import ZoneInfo, ZoneType
12
15
  from ansys.fluent.core.services.reduction import ReductionService
13
16
  from ansys.fluent.core.services.solution_variables import (
14
17
  SolutionVariableData,
@@ -106,6 +109,7 @@ class Solver(BaseSession):
106
109
  start_transcript=start_transcript,
107
110
  launcher_args=launcher_args,
108
111
  event_type=SolverEvent,
112
+ get_zones_info=weakref.WeakMethod(self._get_zones_info),
109
113
  )
110
114
  self._build_from_fluent_connection(fluent_connection, scheme_eval)
111
115
 
@@ -176,6 +180,23 @@ class Solver(BaseSession):
176
180
  )
177
181
  return self.fields.solution_variable_info
178
182
 
183
+ def _get_zones_info(self) -> list[ZoneInfo]:
184
+ zones_info = []
185
+ for (
186
+ zone_info
187
+ ) in self.fields.solution_variable_info.get_zones_info()._zones_info.values():
188
+ zone_type = (
189
+ ZoneType.CELL
190
+ if zone_info.thread_type == SvarProtoModule.ThreadType.CELL_THREAD
191
+ else ZoneType.FACE
192
+ )
193
+ zones_info.append(
194
+ ZoneInfo(
195
+ _id=zone_info.zone_id, name=zone_info.name, zone_type=zone_type
196
+ )
197
+ )
198
+ return zones_info
199
+
179
200
  @property
180
201
  def reduction(self):
181
202
  """``Reduction`` handle."""
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 ANSYS, Inc. All rights reserved.
3
+ Copyright (c) 2025 ANSYS, Inc. All rights reserved.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal