Skip to content

Commit 4c6f66a

Browse files
authored
feat: get mesh by zone name or id (#3638)
* feat: Add field_data.get_zones_info() * feat: get mesh by zone name * feat: get mesh by zone name * feat: get mesh by zone name * feat: fix circular reference issue
1 parent dc4f02f commit 4c6f66a

File tree

4 files changed

+118
-9
lines changed

4 files changed

+118
-9
lines changed

src/ansys/fluent/core/services/field_data.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from functools import reduce
66
import io
77
from typing import Callable, Dict, List, Tuple
8+
import weakref
89

910
import grpc
1011
import numpy as np
@@ -954,6 +955,36 @@ def _extract_field(field_datatype, field_size, chunk_iterator):
954955
return fields_data
955956

956957

958+
# Root domain id in Fluent.
959+
ROOT_DOMAIN_ID = 1
960+
961+
962+
class ZoneType(Enum):
963+
"""Zone types for mesh."""
964+
965+
CELL = 1
966+
FACE = 2
967+
968+
969+
@dataclass
970+
class ZoneInfo:
971+
"""Zone information for mesh.
972+
973+
Attributes:
974+
-----------
975+
_id : int
976+
Zone ID.
977+
name : str
978+
Name of the zone.
979+
zone_type : ZoneType
980+
Type of the zone for mesh.
981+
"""
982+
983+
_id: int
984+
name: str
985+
zone_type: ZoneType
986+
987+
957988
@dataclass
958989
class Node:
959990
"""Node class for mesh.
@@ -1062,12 +1093,14 @@ def __init__(
10621093
field_info: FieldInfo,
10631094
is_data_valid: Callable[[], bool],
10641095
scheme_eval=None,
1096+
get_zones_info: weakref.WeakMethod[Callable[[], list[ZoneInfo]]] | None = None,
10651097
):
10661098
"""__init__ method of FieldData class."""
10671099
self._service = service
10681100
self._field_info = field_info
10691101
self.is_data_valid = is_data_valid
10701102
self.scheme_eval = scheme_eval
1103+
self.get_zones_info = lambda: get_zones_info()()
10711104

10721105
self._allowed_surface_names = _AllowedSurfaceNames(field_info)
10731106

@@ -1417,28 +1450,45 @@ def get_pathlines_field_data(
14171450
}
14181451
return path_lines_dict
14191452

1420-
def get_mesh(self, zone_id: int) -> Mesh:
1453+
def get_mesh(self, zone: str | int) -> Mesh:
14211454
"""Get mesh for a zone.
14221455
14231456
Parameters
14241457
----------
1425-
zone_id : int
1426-
Zone ID.
1458+
zone : str | int
1459+
Zone name or id. Currently, only cell zones are supported.
14271460
14281461
Returns
14291462
-------
14301463
Mesh
14311464
Mesh object containing nodes and elements.
1465+
1466+
Raises
1467+
------
1468+
ValueError
1469+
If the zone is not found.
1470+
NotImplementedError
1471+
If a face zone is provided.
14321472
"""
1473+
zone_info = None
1474+
for zone_info in self.get_zones_info():
1475+
if zone_info.name == zone or zone_info._id == zone:
1476+
break
1477+
if zone_info is None:
1478+
raise ValueError(f"Zone {zone} not found.")
1479+
if zone_info.zone_type == ZoneType.FACE:
1480+
raise NotImplementedError("Face zone mesh is not supported.")
1481+
1482+
# Mesh data is retrieved from the root domain in Fluent
14331483
request = FieldDataProtoModule.GetSolverMeshNodesRequest(
1434-
domain_id=1, thread_id=zone_id
1484+
domain_id=ROOT_DOMAIN_ID, thread_id=zone_info._id
14351485
)
14361486
response = self._service.get_solver_mesh_nodes(request)
14371487
nodes = response.nodes
14381488
nodes = [Node(_id=node.id, x=node.x, y=node.y, z=node.z) for node in nodes]
14391489
nodes = sorted(nodes, key=lambda x: x._id)
14401490
request = FieldDataProtoModule.GetSolverMeshElementsRequest(
1441-
domain_id=1, thread_id=zone_id
1491+
domain_id=ROOT_DOMAIN_ID, thread_id=zone_info._id
14421492
)
14431493
response = self._service.get_solver_mesh_elements(request)
14441494
elements_pb = response.elements

src/ansys/fluent/core/session.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
from enum import Enum
44
import json
55
import logging
6-
from typing import Any, Dict
6+
from typing import Any, Callable, Dict
77
import warnings
88
import weakref
99

1010
from ansys.fluent.core.fluent_connection import FluentConnection
1111
from ansys.fluent.core.journaling import Journal
1212
from ansys.fluent.core.services import service_creator
1313
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
1515
from ansys.fluent.core.services.scheme_eval import SchemeEval
1616
from ansys.fluent.core.streaming_services.datamodel_event_streaming import (
1717
DatamodelEvents,
@@ -85,6 +85,7 @@ class BaseSession:
8585
Close the Fluent connection and exit Fluent.
8686
"""
8787

88+
# We are passing around an WeakMethod to avoid circular references
8889
def __init__(
8990
self,
9091
fluent_connection: FluentConnection,
@@ -93,6 +94,7 @@ def __init__(
9394
start_transcript: bool = True,
9495
launcher_args: Dict[str, Any] | None = None,
9596
event_type: Enum | None = None,
97+
get_zones_info: weakref.WeakMethod[Callable[[], list[ZoneInfo]]] | None = None,
9698
):
9799
"""BaseSession.
98100
@@ -120,6 +122,7 @@ def __init__(
120122
scheme_eval,
121123
file_transfer_service,
122124
event_type,
125+
get_zones_info,
123126
)
124127

125128
def _build_from_fluent_connection(
@@ -128,6 +131,7 @@ def _build_from_fluent_connection(
128131
scheme_eval: SchemeEval,
129132
file_transfer_service: Any | None = None,
130133
event_type=None,
134+
get_zones_info: weakref.WeakMethod[Callable[[], list[ZoneInfo]]] | None = None,
131135
):
132136
"""Build a BaseSession object from fluent_connection object."""
133137
self._fluent_connection = fluent_connection
@@ -205,6 +209,7 @@ def __init__(self, _session):
205209
self.field_info,
206210
self._is_solution_data_valid,
207211
_session.scheme_eval,
212+
get_zones_info,
208213
)
209214
self.field_data_streaming = FieldDataStreaming(
210215
_session._fluent_connection._id, _session._field_data_service

src/ansys/fluent/core/session_solver.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
import threading
77
from typing import Any, Dict
88
import warnings
9+
import weakref
910

11+
from ansys.api.fluent.v0 import svar_pb2 as SvarProtoModule
1012
import ansys.fluent.core as pyfluent
1113
from ansys.fluent.core.services import SchemeEval, service_creator
14+
from ansys.fluent.core.services.field_data import ZoneInfo, ZoneType
1215
from ansys.fluent.core.services.reduction import ReductionService
1316
from ansys.fluent.core.services.solution_variables import (
1417
SolutionVariableData,
@@ -106,6 +109,7 @@ def __init__(
106109
start_transcript=start_transcript,
107110
launcher_args=launcher_args,
108111
event_type=SolverEvent,
112+
get_zones_info=weakref.WeakMethod(self._get_zones_info),
109113
)
110114
self._build_from_fluent_connection(fluent_connection, scheme_eval)
111115

@@ -176,6 +180,23 @@ def svar_info(self):
176180
)
177181
return self.fields.solution_variable_info
178182

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+
179200
@property
180201
def reduction(self):
181202
"""``Reduction`` handle."""

tests/test_field_data.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
CellElementType,
1010
FieldUnavailable,
1111
SurfaceDataType,
12+
ZoneType,
1213
)
1314

1415
HOT_INLET_TEMPERATURE = 313.15
@@ -472,9 +473,41 @@ def plot_mesh(index, field_name, data):
472473

473474

474475
@pytest.mark.fluent_version(">=25.2")
475-
def test_mesh_data(static_mixer_case_session):
476+
def test_mesh_data_2d_standard(disk_case_session):
477+
solver = disk_case_session
478+
zones_info = solver.fields.field_data.get_zones_info()
479+
cell_zone_names = [z.name for z in zones_info if z.zone_type == ZoneType.CELL]
480+
face_zone_names = [z.name for z in zones_info if z.zone_type == ZoneType.FACE]
481+
assert cell_zone_names == ["fluid-7"]
482+
assert face_zone_names == [
483+
"velocity-inlet-2",
484+
"pressure-outlet-3",
485+
"interior-4",
486+
"axis-5",
487+
"wall-6",
488+
]
489+
mesh = solver.fields.field_data.get_mesh(zone="fluid-7")
490+
assert len(mesh.nodes) == 6351
491+
assert len(mesh.elements) == 6192
492+
assert mesh.elements[0].element_type == CellElementType.QUADRILATERAL
493+
assert len(mesh.elements[0].node_indices) == 4
494+
assert min(mesh.nodes, key=lambda x: x.x).x == pytest_approx(0.0)
495+
assert max(mesh.nodes, key=lambda x: x.x).x == pytest_approx(0.06202)
496+
assert min(mesh.nodes, key=lambda x: x.y).y == pytest_approx(0.0)
497+
assert max(mesh.nodes, key=lambda x: x.y).y == pytest_approx(0.443)
498+
assert min(mesh.nodes, key=lambda x: x.z).z == pytest_approx(0.0)
499+
assert max(mesh.nodes, key=lambda x: x.z).z == pytest_approx(0.0)
500+
501+
502+
@pytest.mark.fluent_version(">=25.2")
503+
def test_mesh_data_3d_poly(static_mixer_case_session):
476504
solver = static_mixer_case_session
477-
mesh = solver.fields.field_data.get_mesh(zone_id=97)
505+
zones_info = solver.fields.field_data.get_zones_info()
506+
cell_zone_names = [z.name for z in zones_info if z.zone_type == ZoneType.CELL]
507+
face_zone_names = [z.name for z in zones_info if z.zone_type == ZoneType.FACE]
508+
assert cell_zone_names == ["fluid"]
509+
assert face_zone_names == ["inlet1", "inlet2", "outlet", "wall", "interior--fluid"]
510+
mesh = solver.fields.field_data.get_mesh(zone="fluid")
478511
assert len(mesh.nodes) == 82247
479512
assert len(mesh.elements) == 22771
480513
assert mesh.elements[0].element_type == CellElementType.POLYHEDRON

0 commit comments

Comments
 (0)