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
4 changes: 2 additions & 2 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@
intersphinx_mapping = {
"python": ("https://docs.python.org/dev", None),
"scipy": ("https://docs.scipy.org/doc/scipy/reference", None),
"numpy": ("https://numpy.org/devdocs", None),
"numpy": ("https://numpy.org/doc/stable/", None),
"matplotlib": ("https://matplotlib.org/stable", None),
"pandas": ("https://pandas.pydata.org/pandas-docs/stable", None),
"pandas": ("https://pandas.pydata.org/docs/", None),
"pyvista": ("https://docs.pyvista.org/", None),
}

Expand Down
9 changes: 5 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ vtk = [
{ url = "https://github.com/pyvista/pyvista-wheels/raw/main/vtk-9.1.0.dev0-cp310-cp310-win_amd64.whl" , markers = "python_version > '3.9' and sys_platform != 'linux'"},
{ version = "9.1.0", python = "<=3.9" },
]
pyvista = "0.33.2"
pyvistaqt = "0.7.0"
pyside6 = "6.2.3"
matplotlib = "3.5.1"
ipyvtklink = ">=0.2.2"
pyvista = ">=0.33.2"
pyvistaqt = ">=0.7.0"
pyside6 = ">=6.2.3"
matplotlib = ">=3.5.1"

[tool.black]
line-length = 88
Expand Down
4 changes: 3 additions & 1 deletion src/ansys/fluent/visualization/matplotlib/matplot_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def __init__(self, session, local_surfaces_provider=None):
)

def _init_module(self, obj, mod):
from ansys.fluent.visualization.post_helper import PostAPIHelper
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that PostAPIHelper can be patched.


for name, cls in mod.__dict__.items():

if cls.__class__.__name__ in (
Expand All @@ -47,7 +49,7 @@ def _init_module(self, obj, mod):
setattr(
obj,
cls.PLURAL,
PyLocalContainer(self, cls),
PyLocalContainer(self, cls, PostAPIHelper),
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,13 @@ def __call__(self):
"""Draw XY plot."""
if not self.post_object:
return
xy_data = XYPlotDataExtractor(self.post_object).fetch_data()
properties = {
"curves": self.post_object.surfaces_list(),
"curves": list(xy_data),
"title": "XY Plot",
"xlabel": "position",
"ylabel": self.post_object.y_axis_function(),
}
xy_data = XYPlotDataExtractor(self.post_object).fetch_data()
if in_notebook() or get_config()["blocking"]:
self.plotter.set_properties(properties)
else:
Expand Down Expand Up @@ -170,7 +170,7 @@ def __call__(self):
"""Draw Monitor plot."""
if not self.post_object:
return
monitors_manager = self.post_object._data_extractor.monitors_manager()
monitors_manager = self.post_object._api_helper.monitors_manager()
indices, columns_data = monitors_manager.get_monitor_set_data(
self.post_object.monitor_set_name()
)
Expand Down Expand Up @@ -405,8 +405,7 @@ def _get_windows_id(
for window_id, window in self._post_windows.items()
if not window.plotter.is_closed()
and (
not session_id
or session_id == window.post_object._data_extractor.id()
not session_id or session_id == window.post_object._api_helper.id()
)
]
if not windows_id or window_id in windows_id
Expand Down
63 changes: 41 additions & 22 deletions src/ansys/fluent/visualization/post_data_extractor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Module providing data extractor APIs."""

import itertools
from typing import Dict

from ansys.api.fluent.v0.field_data_pb2 import PayloadTag
Expand Down Expand Up @@ -46,14 +47,12 @@ def _fetch_mesh_data(self, obj, *args, **kwargs):
if not obj.surfaces_list():
raise RuntimeError("Mesh definition is incomplete.")
obj._pre_display()
field_info = obj._data_extractor.field_info()
field_data = obj._data_extractor.field_data()
field_info = obj._api_helper.field_info()
field_data = obj._api_helper.field_data()
surfaces_info = field_info.get_surfaces_info()
surface_ids = [
id
for surf in map(
obj._data_extractor.remote_surface_name, obj.surfaces_list()
)
for surf in map(obj._api_helper.remote_surface_name, obj.surfaces_list())
for id in surfaces_info[surf]["surface_id"]
]

Expand All @@ -64,7 +63,7 @@ def _fetch_mesh_data(self, obj, *args, **kwargs):
return surfaces_data

def _fetch_surface_data(self, obj, *args, **kwargs):
surface_api = obj._data_extractor.surface_api
surface_api = obj._api_helper.surface_api
surface_api.create_surface_on_server()
dummy_object = "dummy_object"
post_session = obj._get_top_most_parent()
Expand Down Expand Up @@ -100,14 +99,12 @@ def _fetch_contour_data(self, obj, *args, **kwargs):
node_values = obj.node_values()
boundary_values = obj.boundary_values()

field_info = obj._data_extractor.field_info()
field_data = obj._data_extractor.field_data()
field_info = obj._api_helper.field_info()
field_data = obj._api_helper.field_data()
surfaces_info = field_info.get_surfaces_info()
surface_ids = [
id
for surf in map(
obj._data_extractor.remote_surface_name, obj.surfaces_list()
)
for surf in map(obj._api_helper.remote_surface_name, obj.surfaces_list())
for id in surfaces_info[surf]["surface_id"]
]
# get scalar field data
Expand Down Expand Up @@ -144,16 +141,14 @@ def _fetch_vector_data(self, obj, *args, **kwargs):
raise RuntimeError("Vector definition is incomplete.")

obj._pre_display()
field_info = obj._data_extractor.field_info()
field_data = obj._data_extractor.field_data()
field_info = obj._api_helper.field_info()
field_data = obj._api_helper.field_data()

# surface ids
surfaces_info = field_info.get_surfaces_info()
surface_ids = [
id
for surf in map(
obj._data_extractor.remote_surface_name, obj.surfaces_list()
)
for surf in map(obj._api_helper.remote_surface_name, obj.surfaces_list())
for id in surfaces_info[surf]["surface_id"]
]

Expand Down Expand Up @@ -210,16 +205,36 @@ def _fetch_xy_data(self, obj):
boundary_values = obj.boundary_values()
direction_vector = obj.direction_vector()
surfaces_list = obj.surfaces_list()
field_info = obj._data_extractor.field_info()
field_data = obj._data_extractor.field_data()
field_info = obj._api_helper.field_info()
field_data = obj._api_helper.field_data()
surfaces_info = field_info.get_surfaces_info()
surface_ids = [
id
for surf in map(
obj._data_extractor.remote_surface_name, obj.surfaces_list()
)
for surf in map(obj._api_helper.remote_surface_name, obj.surfaces_list())
for id in surfaces_info[surf]["surface_id"]
]
# For group surfaces, expanded surf name is used.
# If group1 consists of id 3,4,5 then corresponding surface name will be
# group:3, group:4, group:5
surfaces_list_expanded = [
expanded_surf_name
for expanded_surf_name_list in itertools.starmap(
lambda local_surface_name, id_list: [local_surface_name]
if len(id_list) == 1
else [f"{local_surface_name}:{id}" for id in id_list],
[
(
local_surface_name,
surfaces_info[remote_surface_name]["surface_id"],
)
for remote_surface_name, local_surface_name in zip(
map(obj._api_helper.remote_surface_name, surfaces_list),
surfaces_list,
)
],
)
for expanded_surf_name in expanded_surf_name_list
]

# get scalar field data
field_data.add_get_surfaces_request(
Expand Down Expand Up @@ -248,18 +263,22 @@ def _fetch_xy_data(self, obj):
surface_tag = 0
xyplot_payload_data = field_data.get_fields()
data_tag = location_tag | boundary_value_tag
if data_tag not in xyplot_payload_data:
raise RuntimeError("Plot surface is not valid.")
xyplot_data = xyplot_payload_data[data_tag]
surface_data = xyplot_payload_data[surface_tag]

# loop over all surfaces
xy_plots_data = {}
surfaces_list_iter = iter(surfaces_list)
surfaces_list_iter = iter(surfaces_list_expanded)
for surface_id, mesh_data in surface_data.items():
mesh_data["vertices" if node_values else "centroid"].shape = (
mesh_data["vertices" if node_values else "centroid"].size // 3,
3,
)
y_values = xyplot_data[surface_id][field]
if y_values is None:
continue
x_values = np.matmul(
mesh_data["vertices" if node_values else "centroid"],
direction_vector,
Expand Down
142 changes: 142 additions & 0 deletions src/ansys/fluent/visualization/post_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import re


class PostAPIHelper:
"""Class providing helper API for post objects."""

class _SurfaceAPI:
"""Class providing APIs for surface operations."""

def __init__(self, obj):
self.obj = obj
self._surface_name_on_server = self.surface_name_in_server(obj._name)

@staticmethod
def surface_name_in_server(local_surface_name):
return "_dummy_surface_for_pyfluent:" + local_surface_name.lower()

def _get_api_handle(self):
return self.obj._get_top_most_parent().session.solver.tui.surface

def _delete_if_exist_on_server(self):
field_info = self.obj._api_helper.field_info()
surfaces_list = list(field_info.get_surfaces_info().keys())
if self._surface_name_on_server in surfaces_list:
self.delete_surface_on_server()

def create_surface_on_server(self):
if self.obj.surface.type() == "iso-surface":
iso_surface = self.obj.surface.iso_surface
field = iso_surface.field()
iso_value = iso_surface.iso_value()
if not field:
raise RuntimeError("Iso surface definition is incomplete.")
self._delete_if_exist_on_server()
phases = self.obj._api_helper._get_phases()
unit_quantity = self.obj._api_helper._field_unit_quantity(field)
unit_info = self.obj._api_helper._fluent_unit_info(unit_quantity)
if phases:
phases = list(filter(field.startswith, phases))
if phases:
domain, field = (
field[0 : len(phases[0])],
field[len(phases[0]) + 1 :],
)
else:
domain = "mixture"
self._get_api_handle().iso_surface(
domain,
field,
self._surface_name_on_server,
(),
(),
(iso_value / unit_info[1]) - unit_info[2],
(),
)
else:
self._get_api_handle().iso_surface(
field,
self._surface_name_on_server,
(),
(),
(iso_value / unit_info[1]) - unit_info[2],
(),
)
elif self.obj.surface.type() == "plane-surface":
plane_surface = self.obj.surface.plane_surface
xy_plane = plane_surface.xy_plane
yz_plane = plane_surface.yz_plane
zx_plane = plane_surface.zx_plane
self._delete_if_exist_on_server()
unit_info = self.obj._api_helper._fluent_unit_info("length")
self._get_api_handle().plane_surface(
self._surface_name_on_server,
"xy-plane" if xy_plane else "yz-plane" if yz_plane else "zx-plane",
(xy_plane.z() / unit_info[1]) - unit_info[2]
if xy_plane
else (yz_plane.x() / unit_info[1]) - unit_info[2]
if yz_plane
else (zx_plane.y() / unit_info[1]) - unit_info[2],
)
field_info = self.obj._api_helper.field_info()
surfaces_list = list(field_info.get_surfaces_info().keys())
if self._surface_name_on_server not in surfaces_list:
raise RuntimeError("Surface creation failed.")

def delete_surface_on_server(self):
self._get_api_handle().delete_surface(self._surface_name_on_server)

def __init__(self, obj):
self.obj = obj
self.field_info = lambda: obj._get_top_most_parent().session.field_info
self.field_data = lambda: obj._get_top_most_parent().session.field_data
self.monitors_manager = (
lambda: obj._get_top_most_parent().session.monitors_manager
)
self.id = lambda: obj._get_top_most_parent().session.id
if obj.__class__.__name__ == "Surface":
self.surface_api = PostAPIHelper._SurfaceAPI(obj)

def remote_surface_name(self, local_surface_name):
local_surfaces_provider = (
self.obj._get_top_most_parent()._local_surfaces_provider()
)
if local_surface_name in list(local_surfaces_provider):
return PostAPIHelper._SurfaceAPI.surface_name_in_server(local_surface_name)
else:
return local_surface_name

# Following functions will be deprecated in future.
def get_vector_fields(self):
scheme_eval_str = "(map car (apply append (map client-inquire-cell-vector-functions (inquire-domain-for-cell-functions))))" # noqa: E501
return self._scheme_str_to_py_list(scheme_eval_str)

def _get_phases(self):
scheme_eval_str = "(map domain-name (get-phase-domains))"
return self._scheme_str_to_py_list(scheme_eval_str)

def _field_unit_quantity(self, field):
scheme_eval_str = f"(cdr (assq 'units (%fill-render-info '{field})))"
return self._scheme_str_to_py_list(scheme_eval_str)[0]

def _fluent_unit_info(self, unit_quantity):
def to_float(number):
try:
return float(number)
except ValueError:
return number

scheme_eval_str = (
f"(units/inquire-label-scale-offset-for-quantity '{unit_quantity})"
)
unit_info = [
to_float(data) for data in self._scheme_str_to_py_list(scheme_eval_str)
]
if len(unit_info) == 2:
unit_info.insert(0, "")
return unit_info

def _scheme_str_to_py_list(self, scheme_eval_str):
session = self.obj._get_top_most_parent().session
str = session.scheme_eval.string_eval(scheme_eval_str)
return list(filter(None, re.split(r'[\s()"\']', str)))
Loading