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
2 changes: 1 addition & 1 deletion .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
strategy:
matrix:
os: [windows-latest, ubuntu-latest]
python-version: ['3.7', '3.8', '3.9']
python-version: ['3.7', '3.8', '3.9', '3.10']
fail-fast: false

steps:
Expand Down
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ packages = [
python = ">=3.7,<4.0"
importlib-metadata = {version = "^4.0", python = "<3.8"}
ansys-fluent-solver = { git = "https://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@github.com/pyansys/pyfluent.git", branch = "main" }
vtk = "9.1.0"
vtk = [
{ url = "https://github.com/pyvista/pyvista-wheels/raw/main/vtk-9.1.0.dev0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" , markers = "python_version > '3.9' and sys_platform == 'linux'"},
{ 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"
Expand Down
18 changes: 15 additions & 3 deletions src/ansys/fluent/post/matplotlib/matplot_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from ansys.fluent.core.meta import PyLocalContainer

from ansys.fluent.post.matplotlib.matplot_windows_manager import matplot_windows_manager
from ansys.fluent.post.post_object_defns import XYPlotDefn
from ansys.fluent.post.post_object_defns import MonitorDefn, XYPlotDefn


class Plots:
Expand Down Expand Up @@ -60,6 +60,18 @@ def plot(self, window_id: Optional[str] = None):
window_id : str, optional
Window id. If not specified unique id is used.
"""
self._pre_display()
matplot_windows_manager.plot(self, window_id)
self._post_display()


class MonitorPlot(MonitorDefn):
"""Monitor Plot."""

def plot(self, window_id: Optional[str] = None):
"""Draw Monitor Plot.

Parameters
----------
window_id : str, optional
Window id. If not specified unique id is used.
"""
matplot_windows_manager.plot(self, window_id)
205 changes: 103 additions & 102 deletions src/ansys/fluent/post/matplotlib/matplot_windows_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
import multiprocessing as mp
from typing import List, Optional, Union

from ansys.api.fluent.v0.field_data_pb2 import PayloadTag
from ansys.fluent.core.session import Session
from ansys.fluent.core.utils.generic import AbstractSingletonMeta, in_notebook
import numpy as np

from ansys.fluent.post import get_config
from ansys.fluent.post.matplotlib.plotter_defns import Plotter, ProcessPlotter
from ansys.fluent.post.post_object_defns import GraphicsDefn, PlotDefn
from ansys.fluent.post.post_data_extractor import XYPlotDataExtractor
from ansys.fluent.post.post_object_defns import MonitorDefn, PlotDefn, XYPlotDefn
from ansys.fluent.post.post_windows_manager import PostWindow, PostWindowsManager


Expand Down Expand Up @@ -65,40 +64,31 @@ def close(self):
class MatplotWindow(PostWindow):
"""Class for MatplotWindow."""

def __init__(self, id: str, post_object: Union[GraphicsDefn, PlotDefn]):
def __init__(self, id: str, post_object: PlotDefn):
"""Instantiate a MatplotWindow.

Parameters
----------
id : str
Window id.
post_object : Union[GraphicsDefn, PlotDefn]
post_object : PlotDefn
Object to plot.
"""
self.post_object: Union[GraphicsDefn, PlotDefn] = post_object
self.id: str = id
self.properties: dict = None
self.post_object = None
self.plotter: Union[_ProcessPlotterHandle, Plotter] = self._get_plotter()
self.animate: bool = False
self.close: bool = False
self.refresh: bool = False

def plot(self):
"""Draw plot."""
if not self.post_object:
return
xy_data = self._get_xy_plot_data()
if in_notebook() or get_config()["blocking"]:
self.plotter.set_properties(self.properties)
else:
try:
self.plotter.set_properties(self.properties)
except BrokenPipeError:
self.plotter: Union[
_ProcessPlotterHandle, Plotter
] = self._get_plotter()
self.plotter.set_properties(self.properties)
self.plotter.plot(xy_data)
if self.post_object is not None:
plot = (
_XYPlot(self.post_object, self.plotter)
if self.post_object.__class__.__name__ == "XYPlot"
else _MonitorPlot(self.post_object, self.plotter)
)
plot()

# private methods
def _get_plotter(self):
Expand All @@ -108,86 +98,99 @@ def _get_plotter(self):
else _ProcessPlotterHandle(self.id)
)

def _get_xy_plot_data(self):
obj = self.post_object
field = obj.y_axis_function()
node_values = obj.node_values()
boundary_values = obj.boundary_values()
direction_vector = obj.direction_vector()
surfaces_list = obj.surfaces_list()
self.properties = {
"curves": surfaces_list,

class _XYPlot:
"""Class for XYPlot."""

def __init__(
self, post_object: XYPlotDefn, plotter: Union[_ProcessPlotterHandle, Plotter]
):
"""Instantiate XYPlot.

Parameters
----------
post_object : XYPlotDefn
Object to plot.
plotter: Union[_ProcessPlotterHandle, Plotter]
Plotter to plot data.
"""
self.post_object: XYPlotDefn = post_object
self.plotter: Union[_ProcessPlotterHandle, Plotter] = plotter

def __call__(self):
"""Draw XY plot."""
if not self.post_object:
return
properties = {
"curves": self.post_object.surfaces_list(),
"title": "XY Plot",
"xlabel": "position",
"ylabel": field,
"ylabel": self.post_object.y_axis_function(),
}
field_info = obj._data_extractor.field_info()
field_data = obj._data_extractor.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 id in surfaces_info[surf]["surface_id"]
]
xy_data = XYPlotDataExtractor(self.post_object).fetch_data()
if in_notebook() or get_config()["blocking"]:
self.plotter.set_properties(properties)
else:
try:
self.plotter.set_properties(properties)
except BrokenPipeError:
self.plotter: Union[
_ProcessPlotterHandle, Plotter
] = self._get_plotter()
self.plotter.set_properties(properties)
self.plotter.plot(xy_data)

# get scalar field data
field_data.add_get_surfaces_request(
surface_ids,
provide_faces=False,
provide_vertices=True if node_values else False,
provide_faces_centroid=False if node_values else True,
)
field_data.add_get_scalar_fields_request(
surface_ids,
field,
node_values,
boundary_values,
)

location_tag = (
field_data._payloadTags[PayloadTag.NODE_LOCATION]
if node_values
else field_data._payloadTags[PayloadTag.ELEMENT_LOCATION]
)
boundary_value_tag = (
field_data._payloadTags[PayloadTag.BOUNDARY_VALUES]
if boundary_values
else 0
class _MonitorPlot:
"""Class MonitorPlot."""

def __init__(
self, post_object: MonitorDefn, plotter: Union[_ProcessPlotterHandle, Plotter]
):
"""Instantiate MonitorPlot.

Parameters
----------
post_object : MonitorDefn
Object to plot.
plotter: Union[_ProcessPlotterHandle, Plotter]
Plotter to plot data.
"""
self.post_object: MonitorDefn = post_object
self.plotter: Union[_ProcessPlotterHandle, Plotter] = plotter

def __call__(self):
"""Draw Monitor plot."""
if not self.post_object:
return
monitors_manager = self.post_object._data_extractor.monitors_manager()
indices, columns_data = monitors_manager.get_monitor_set_data(
self.post_object.monitor_set_name()
)
surface_tag = 0
xyplot_payload_data = field_data.get_fields()
data_tag = location_tag | boundary_value_tag
xyplot_data = xyplot_payload_data[data_tag]
surface_data = xyplot_payload_data[surface_tag]

# loop over all meshes
xy_plots_data = {}
surfaces_list_iter = iter(surfaces_list)
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]
x_values = np.matmul(
mesh_data["vertices" if node_values else "centroid"],
direction_vector,
)
structured_data = np.empty(
x_values.size,
dtype={
"names": ("xvalues", "yvalues"),
"formats": ("f8", "f8"),
},
)
structured_data["xvalues"] = x_values
structured_data["yvalues"] = y_values
sort = np.argsort(structured_data, order=["xvalues"])
surface_name = next(surfaces_list_iter)
xy_plots_data[surface_name] = structured_data[sort]
return xy_plots_data
xy_data = {}
for column_name, column_data in columns_data.items():
xy_data[column_name] = {"xvalues": indices, "yvalues": column_data}
monitor_set_name = self.post_object.monitor_set_name()
properties = {
"curves": list(xy_data.keys()),
"title": monitor_set_name,
"xlabel": monitors_manager.get_monitor_set_prop(monitor_set_name, "xlabel"),
"ylabel": monitors_manager.get_monitor_set_prop(monitor_set_name, "ylabel"),
"yscale": "log" if monitor_set_name == "residual" else "linear",
}

if in_notebook() or get_config()["blocking"]:
self.plotter.set_properties(properties)
else:
try:
self.plotter.set_properties(properties)
except BrokenPipeError:
self.plotter: Union[
_ProcessPlotterHandle, Plotter
] = self._get_plotter()
self.plotter.set_properties(properties)
if xy_data:
self.plotter.plot(xy_data)


class MatplotWindowsManager(PostWindowsManager, metaclass=AbstractSingletonMeta):
Expand Down Expand Up @@ -215,14 +218,12 @@ def open_window(self, window_id: Optional[str] = None) -> str:
self._open_window(window_id)
return window_id

def set_object_for_window(
self, object: Union[PlotDefn, GraphicsDefn], window_id: str
) -> None:
def set_object_for_window(self, object: PlotDefn, window_id: str) -> None:
"""Associate post object with running window instance.

Parameters
----------
object : Union[GraphicsDefn, PlotDefn]
object : PlotDefn
Post object to associate with window.

window_id : str
Expand All @@ -241,14 +242,14 @@ def set_object_for_window(

def plot(
self,
object: Union[PlotDefn, GraphicsDefn],
object: PlotDefn,
window_id: Optional[str] = None,
) -> None:
"""Draw plot.

Parameters
----------
object: Union[GraphicsDefn, PlotDefn]
object: PlotDefn
Object to plot.

window_id : str, optional
Expand Down
17 changes: 12 additions & 5 deletions src/ansys/fluent/post/matplotlib/plotter_defns.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def __init__(
self._max_y = None
self._min_x = None
self._max_x = None
self._yscale = None
self._data = {}
self._closed = False
self._visible = False
Expand All @@ -72,8 +73,8 @@ def plot(self, data: dict) -> None:
max_y_value = np.amax(data[curve]["yvalues"])
min_x_value = np.amin(data[curve]["xvalues"])
max_x_value = np.amax(data[curve]["xvalues"])
self._data[curve]["xvalues"] += data[curve]["xvalues"].tolist()
self._data[curve]["yvalues"] += data[curve]["yvalues"].tolist()
self._data[curve]["xvalues"] = data[curve]["xvalues"].tolist()
self._data[curve]["yvalues"] = data[curve]["yvalues"].tolist()
self._min_y = min(self._min_y, min_y_value) if self._min_y else min_y_value
self._max_y = max(self._max_y, max_y_value) if self._max_y else max_y_value
self._min_x = min(self._min_x, min_x_value) if self._min_x else min_x_value
Expand All @@ -84,10 +85,13 @@ def plot(self, data: dict) -> None:
curve_line.set_data(
self._data[curve]["xvalues"], self._data[curve]["yvalues"]
)
x_range = max_x_value - min_x_value
y_range = max_y_value - min_y_value
self.ax.set_xlim(self._min_x, self._max_x)
if self._max_x > self._min_x:
self.ax.set_xlim(self._min_x, self._max_x)
y_range = self._max_y - self._min_y
if self._yscale == "log":
y_range = 0
self.ax.set_ylim(self._min_y - y_range * 0.2, self._max_y + y_range * 0.2)

if not self._visible:
self._visible = True
plt.show()
Expand Down Expand Up @@ -123,6 +127,7 @@ def set_properties(self, properties: dict):
self._title = properties.get("title", self._title)
self._xlabel = properties.get("xlabel", self._xlabel)
self._ylabel = properties.get("ylabel", self._ylabel)
self._yscale = properties.get("yscale", self._yscale)
self._data = {}
self._min_y = None
self._max_y = None
Expand All @@ -140,6 +145,8 @@ def __call__(self):
def _reset(self):
plt.figure(self.fig.number)
self.ax.cla()
if self._yscale:
self.ax.set_yscale(self._yscale)
for curve_name in self._curves:
self._data[curve_name] = {}
self._data[curve_name]["xvalues"] = []
Expand Down
Loading