Skip to content

Commit

Permalink
Documentation for matplotlib
Browse files Browse the repository at this point in the history
  • Loading branch information
otvam committed Jan 21, 2023
1 parent c0a1fe3 commit 20178d8
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 64 deletions.
4 changes: 2 additions & 2 deletions PyPEEC/lib_check/check_data_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ def _check_data_plotter_matplotlib(data_plot):
if data_plot not in ["convergence", "residuum"]:
raise CheckError("data_plot: specified data plot is invalid")


def _check_data_plotter_pyvista(data_plot):
"""
Check the data describing a PyVista plot (for the plotter).
Expand Down Expand Up @@ -272,7 +273,7 @@ def _check_data_plotter_item(data_plotter):
raise CheckError("plot_framework: plot framework should be a string")

# check the plot data for the framework
if plot_framework== "matplotlib":
if plot_framework == "matplotlib":
_check_data_plotter_matplotlib(data_plot)
elif plot_framework == "pyvista":
_check_data_plotter_pyvista(data_plot)
Expand Down Expand Up @@ -344,4 +345,3 @@ def check_data_viewer(data_viewer):
# check items
for dat_tmp in data_viewer:
_check_data_viewer_item(dat_tmp)

67 changes: 38 additions & 29 deletions PyPEEC/lib_utils/plotgui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
The Qt framework is used for the GUI.
WARNING: This module is using different more or less dirty hacks.
This module is likely to be non-portable.
This module is likely to break with some version of the dependencies.
This module is likely to be non-portable across platforms (tested on Linux x64).
This module is likely to break with newer/older versions of the dependencies.
"""

__author__ = "Thomas Guillod"
Expand All @@ -28,20 +28,21 @@
mpl.use('QtAgg')


def open_pyvista(data_window, is_blocking):
def open_pyvista(data_window, is_interactive):
"""
Get a PyVista plotter.
If the call is non-blocking, the window is not shown.
If the mode is set to interactive, the plotter is customized (title, size, and menu).
If the mode is set to non-interactive, a basic plotter is returned.
"""

# get the data
title = data_window["title"]
show_menu = data_window["show_menu"]
size = data_window["size"]

# create the figure (blocking or non-blocking)
if is_blocking:
# get Qt plotter if blocking
# create the figure
if is_interactive:
# get Qt plotter if interactive
pl = pvqt.BackgroundPlotter(
show=True,
toolbar=show_menu,
Expand All @@ -52,77 +53,85 @@ def open_pyvista(data_window, is_blocking):
# set icon
pl.set_icon(PATH_ROOT + "/icon.png")
else:
# get standard plotter if non-blocking
# get standard plotter if non-interactive
pl = pv.Plotter(off_screen=True)

return pl


def open_matplotlib(data_window, is_blocking):
def open_matplotlib(data_window, is_interactive):
"""
Get a Matplotlib figure.
If the mode is set to interactive, the figure is customized (title, size, and menu).
If the mode is set to non-interactive, a basic figure is returned.
"""

# get the data
title = data_window["title"]
show_menu = data_window["show_menu"]
size = data_window["size"]

# create the figure (blocking or non-blocking)
if is_blocking:
(fig, ax) = plt.subplots(tight_layout=True)
# get the figure
(fig, ax) = plt.subplots(tight_layout=True)

# set the Qt options
# create the figure
if is_interactive:
# get the window size and icon
(sizex, sizey) = size
icn = qtu.QIcon(PATH_ROOT + "/icon.png")

# set the Qt options
man = plt.get_current_fig_manager()
man.window.setWindowTitle(title)
man.window.setWindowIcon(icn)
man.window.resize(sizex, sizey)

if show_menu:
man.toolbar.show()
else:
man.toolbar.hide()
else:
# get a default plot is non-blocking
(fig, ax) = plt.subplots()

return fig


def close_pyvista(pl, is_blocking):
def close_pyvista(pl, is_interactive):
"""
Close a PyVista plotter (only if the call is non-blocking).
Close a PyVista plotter (only if the mode is set to non-interactive).
"""

# close plotter if non-blocking
if not is_blocking:
if not is_interactive:
pl.close()
pl.deep_clean()


def close_matplotlib(fig, is_blocking):
if not is_blocking:
def close_matplotlib(fig, is_interactive):
"""
Close a Matplotlib figure (only if the mode is set to non-interactive).
"""

if not is_interactive:
plt.close(fig)


def open_app(is_blocking):
def open_app(is_interactive):
"""
Create a master Qt app for all the plotter windows.
Create a master Qt app for all the GUI windows (only if the mode is set to interactive).
"""

if is_blocking:
if is_interactive:
app = qtw.QApplication([])
else:
app = None

return app


def run_app(app, is_blocking):
def run_app(app, is_interactive):
"""
Enter the event loop (only if the call is non-blocking).
Enter the event loop (only if the mode is set to interactive).
This will create a blocking call until all the windows are closed.
"""

if is_blocking:
if is_interactive:
plt.show(block=False)
exit_code = app.exec_()
return exit_code == 0
Expand Down
27 changes: 25 additions & 2 deletions PyPEEC/lib_visualization/manage_matplotlib.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
"""
Different functions for plotting solver results with Matplotlib.
For the plotter, the following plots are available:
- plot of the convergence of the matrix solver
- histogram of the final residuum of the solution
"""

__author__ = "Thomas Guillod"
__copyright__ = "(c) 2023 - Dartmouth College"

import numpy as np
import matplotlib.pyplot as plt


def _get_plot_residuum(fig, res_raw):
"""
Plot the final residuum (absolute value) with an histogram.
"""

# activate the figure
plt.figure(fig)

Expand All @@ -17,7 +32,11 @@ def _get_plot_residuum(fig, res_raw):
plt.title("Solver Residuum")


def _get_plot_congervence(fig, res_iter):
def _get_plot_convergence(fig, res_iter):
"""
Plot the convergence of the iterative matrix solver.
"""

# activate the figure
plt.figure(fig)

Expand All @@ -33,13 +52,17 @@ def _get_plot_congervence(fig, res_iter):


def get_plot_plotter(fig, solver_status, data_plot):
"""
Plot the solver status (for the plotter).
"""

# get the data
res_raw = solver_status["res_raw"]
res_iter = solver_status["res_iter"]

# get the main plot
if data_plot == "convergence":
_get_plot_congervence(fig, res_iter)
_get_plot_convergence(fig, res_iter)
elif data_plot == "residuum":
_get_plot_residuum(fig, res_raw)
else:
Expand Down
4 changes: 2 additions & 2 deletions PyPEEC/lib_visualization/manage_pyvista.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""
Different functions for plotting voxel structures with PyVista.
For the viewer and the plotter, the following object are plotter:
For the viewer and the plotter, the following object are shown:
- the complete voxel structure (as wireframe)
- the structure containing non-empty voxels (as wireframe)
- the defined point cloud (as points)
For the plotter, the following plots are available:
For the viewer, the following plots are available:
- the domains are shown for the non-empty voxels
- the connected components for the non-empty voxels
Expand Down
26 changes: 15 additions & 11 deletions PyPEEC/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- current density
- magnetic field
Two different mode are available for the plots:
- interactive mode: the plots are shown (blocking call at the end)
- silent mode: nothing is shown and the program exit (for debugging and testing)
The plotting is done with PyVista with the Qt framework.
"""

Expand Down Expand Up @@ -64,7 +68,7 @@ def _get_grid_voxel(data_solution, data_point):
return grid, voxel, point, solver_status


def _get_plot(grid, voxel, point, solver_status, data_plotter, is_blocking):
def _get_plot(grid, voxel, point, solver_status, data_plotter, is_interactive):
"""
Make a plot with the specified user settings.
The plot contains the following elements:
Expand All @@ -81,27 +85,27 @@ def _get_plot(grid, voxel, point, solver_status, data_plotter, is_blocking):
# make the plots
if plot_framework == "pyvista":
# get the plotter (with the Qt framework)
pl = plotgui.open_pyvista(data_window, is_blocking)
pl = plotgui.open_pyvista(data_window, is_interactive)

# make the plot
manage_pyvista.get_plot_plotter(pl, grid, voxel, point, data_plot)

# close plotter if non-blocking
plotgui.close_pyvista(pl, is_blocking)
# close plotter if non-interactive
plotgui.close_pyvista(pl, is_interactive)
elif plot_framework == "matplotlib":
# get the figure (with the Qt framework)
fig = plotgui.open_matplotlib(data_window, is_blocking)
fig = plotgui.open_matplotlib(data_window, is_interactive)

# make the plot
manage_matplotlib.get_plot_plotter(fig, solver_status, data_plot)

# close figure if non-blocking
plotgui.close_matplotlib(fig, is_blocking)
# close figure if non-interactive
plotgui.close_matplotlib(fig, is_interactive)
else:
raise ValueError("invalid plot framework")


def run(data_solution, data_point, data_plotter, is_blocking):
def run(data_solution, data_point, data_plotter, is_interactive):
"""
Main script for plotting the solution of a FFT-PEEC problem.
"""
Expand All @@ -119,7 +123,7 @@ def run(data_solution, data_point, data_plotter, is_blocking):

# create the Qt app (should be at the beginning)
logger.info("create the GUI application")
app = plotgui.open_app(is_blocking)
app = plotgui.open_app(is_interactive)

# handle the data
logger.info("parse the voxel geometry and the data")
Expand All @@ -129,7 +133,7 @@ def run(data_solution, data_point, data_plotter, is_blocking):
logger.info("generate the different plots")
for i, dat_tmp in enumerate(data_plotter):
logger.info("plotting %d / %d" % (i + 1, len(data_plotter)))
_get_plot(grid, voxel, point, solver_status, dat_tmp, is_blocking)
_get_plot(grid, voxel, point, solver_status, dat_tmp, is_interactive)
except CheckError as ex:
logger.error("check error : " + str(ex))
return False
Expand All @@ -141,6 +145,6 @@ def run(data_solution, data_point, data_plotter, is_blocking):
logger.info("successful termination")

# enter the event loop (should be at the end, blocking call)
status = plotgui.run_app(app, is_blocking)
status = plotgui.run_app(app, is_interactive)

return status
16 changes: 8 additions & 8 deletions PyPEEC/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def run_mesher(file_mesher, file_voxel):
return status


def run_viewer(file_voxel, file_point, file_viewer, is_blocking):
def run_viewer(file_voxel, file_point, file_viewer, is_interactive):
"""
Load the voxel structure and plot the results.
"""
Expand All @@ -61,7 +61,7 @@ def run_viewer(file_voxel, file_point, file_viewer, is_blocking):
data_viewer = fileio.load_json(file_viewer)

# call the viewer
status = viewer.run(data_voxel, data_point, data_viewer, is_blocking)
status = viewer.run(data_voxel, data_point, data_viewer, is_interactive)
except FileError as ex:
logger.error("check error : " + str(ex))
return False
Expand Down Expand Up @@ -93,7 +93,7 @@ def run_solver(file_voxel, file_problem, file_solution):
return status


def run_plotter(file_solution, file_point, file_plotter, is_blocking):
def run_plotter(file_solution, file_point, file_plotter, is_interactive):
"""
Load the solver solution and plot the results.
"""
Expand All @@ -106,7 +106,7 @@ def run_plotter(file_solution, file_point, file_plotter, is_blocking):
data_plotter = fileio.load_json(file_plotter)

# call the plotter
status = plotter.run(data_solution, data_point, data_plotter, is_blocking)
status = plotter.run(data_solution, data_point, data_plotter, is_interactive)
except FileError as ex:
logger.error("check error : " + str(ex))
return False
Expand Down Expand Up @@ -182,12 +182,12 @@ def main_viewer():
"--silent",
help="if set, do not display the plots",
action="store_false",
dest="is_blocking",
dest="is_interactive",
)

# parse and call
args = parser.parse_args()
status = run_viewer(args.file_voxel, args.file_point, args.file_viewer, args.is_blocking)
status = run_viewer(args.file_voxel, args.file_point, args.file_viewer, args.is_interactive)
sys.exit(int(not status))


Expand Down Expand Up @@ -266,10 +266,10 @@ def main_plotter():
"--silent",
help="if set, do not display the plots",
action="store_false",
dest="is_blocking",
dest="is_interactive",
)

# parse and call
args = parser.parse_args()
status = run_plotter(args.file_solution, args.file_point, args.file_plotter, args.is_blocking)
status = run_plotter(args.file_solution, args.file_point, args.file_plotter, args.is_interactive)
sys.exit(int(not status))
Loading

0 comments on commit 20178d8

Please sign in to comment.