From 26305bc8ad52f1d3526566cb424b02fd047c6c1f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 00:25:59 +0000 Subject: [PATCH 1/5] Bump scipy, pytest and sphinx-autodoc-typehints Bumps [scipy](https://github.com/scipy/scipy), [pytest](https://github.com/pytest-dev/pytest) and [sphinx-autodoc-typehints](https://github.com/tox-dev/sphinx-autodoc-typehints). These dependencies needed to be updated together. Updates `scipy` from 1.10.1 to 1.11.0 - [Release notes](https://github.com/scipy/scipy/releases) - [Commits](https://github.com/scipy/scipy/compare/v1.10.1...v1.11.0) Updates `pytest` from 7.3.2 to 7.4.0 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.3.2...7.4.0) Updates `sphinx-autodoc-typehints` from 1.23.0 to 1.23.2 - [Release notes](https://github.com/tox-dev/sphinx-autodoc-typehints/releases) - [Changelog](https://github.com/tox-dev/sphinx-autodoc-typehints/blob/main/CHANGELOG.md) - [Commits](https://github.com/tox-dev/sphinx-autodoc-typehints/compare/1.23.0...1.23.2) --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 82c7b6c75b..4bde7d7b0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,9 +58,9 @@ tests = [ "ansys-dpf-core==0.8.1", "autopep8==2.0.2", "matplotlib==3.7.1", - "scipy==1.10.1", + "scipy==1.11.0", "pandas==2.0.2", - "pytest==7.3.2", + "pytest==7.4.0", "pytest-cov==4.1.0", "pyvista==0.39.1", "pyansys-tools-report==0.5.0", @@ -86,7 +86,7 @@ doc = [ "pythreejs==2.4.2", "pyvista==0.39.1", "sphinx-autobuild==2021.3.14", - "sphinx-autodoc-typehints==1.23.0", + "sphinx-autodoc-typehints==1.23.2", "sphinx-copybutton==0.5.2", "sphinx-gallery==0.13.0", "sphinx-notfound-page==0.8.3", From 6f2275692f874272f22c22baa588779ab5815a7e Mon Sep 17 00:00:00 2001 From: German Date: Tue, 27 Jun 2023 10:17:51 +0200 Subject: [PATCH 2/5] Revert "Bump scipy, pytest and sphinx-autodoc-typehints" This reverts commit 26305bc8ad52f1d3526566cb424b02fd047c6c1f. --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4bde7d7b0c..82c7b6c75b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,9 +58,9 @@ tests = [ "ansys-dpf-core==0.8.1", "autopep8==2.0.2", "matplotlib==3.7.1", - "scipy==1.11.0", + "scipy==1.10.1", "pandas==2.0.2", - "pytest==7.4.0", + "pytest==7.3.2", "pytest-cov==4.1.0", "pyvista==0.39.1", "pyansys-tools-report==0.5.0", @@ -86,7 +86,7 @@ doc = [ "pythreejs==2.4.2", "pyvista==0.39.1", "sphinx-autobuild==2021.3.14", - "sphinx-autodoc-typehints==1.23.2", + "sphinx-autodoc-typehints==1.23.0", "sphinx-copybutton==0.5.2", "sphinx-gallery==0.13.0", "sphinx-notfound-page==0.8.3", From b51f3fc38cb2ab092ac064a802405b981f69af4b Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Tue, 27 Jun 2023 10:43:06 +0200 Subject: [PATCH 3/5] Allowing savefig when vtk=false (#2143) * Fix having to plot twice to get a file with size bigger than 3kb * changing var name * Implementing savefig argument * Fixing tests * Apply suggestions from code review * Adding more tests --- src/ansys/mapdl/core/mapdl.py | 174 ++++++++++++++++++++--------- src/ansys/mapdl/core/mapdl_grpc.py | 2 +- tests/test_mapdl.py | 47 ++++++++ 3 files changed, 172 insertions(+), 51 deletions(-) diff --git a/src/ansys/mapdl/core/mapdl.py b/src/ansys/mapdl/core/mapdl.py index 067c835548..432b239433 100644 --- a/src/ansys/mapdl/core/mapdl.py +++ b/src/ansys/mapdl/core/mapdl.py @@ -63,7 +63,9 @@ ] # test for png file -PNG_TEST = re.compile("WRITTEN TO FILE(.*).png") +PNG_IS_WRITTEN_TO_FILE = re.compile( + "WRITTEN TO FILE" +) # getting the file name is buggy. VWRITE_REPLACEMENT = """ Cannot use *VWRITE directly as a command in MAPDL @@ -1361,8 +1363,8 @@ def nplot(self, nnum="", vtk=None, **kwargs): if isinstance(nnum, bool): nnum = int(nnum) - self._enable_interactive_plotting() - return super().nplot(nnum, **kwargs) + with self._enable_interactive_plotting(): + return super().nplot(nnum, **kwargs) def eplot(self, show_node_numbering=False, vtk=None, **kwargs): """Plots the currently selected elements. @@ -1491,8 +1493,8 @@ def eplot(self, show_node_numbering=False, vtk=None, **kwargs): ) # otherwise, use MAPDL plotter - self._enable_interactive_plotting() - return self.run("EPLOT", **kwargs) + with self._enable_interactive_plotting(): + return self.run("EPLOT", **kwargs) def vplot( self, @@ -1592,10 +1594,10 @@ def vplot( self.cmsel("S", cm_name, "AREA", mute=True) return out else: - self._enable_interactive_plotting() - return super().vplot( - nv1=nv1, nv2=nv2, ninc=ninc, degen=degen, scale=scale, **kwargs - ) + with self._enable_interactive_plotting(): + return super().vplot( + nv1=nv1, nv2=nv2, ninc=ninc, degen=degen, scale=scale, **kwargs + ) def aplot( self, @@ -1782,13 +1784,13 @@ def aplot( return general_plotter(meshes, [], labels, **kwargs) - self._enable_interactive_plotting() - return super().aplot( - na1=na1, na2=na2, ninc=ninc, degen=degen, scale=scale, **kwargs - ) + with self._enable_interactive_plotting(): + return super().aplot( + na1=na1, na2=na2, ninc=ninc, degen=degen, scale=scale, **kwargs + ) @supress_logging - def _enable_interactive_plotting(self, pixel_res=1600): + def _enable_interactive_plotting(self, pixel_res: int = 1600): """Enables interactive plotting. Requires matplotlib Parameters @@ -1799,16 +1801,32 @@ def _enable_interactive_plotting(self, pixel_res=1600): Increasing the resolution produces a "sharper" image but takes longer to render. """ - if not self._has_matplotlib: - raise ImportError( - "Install matplotlib to display plots from MAPDL ," - "from Python. Otherwise, plot with vtk with:\n" - "``vtk=True``" - ) + return self.WithInterativePlotting(self, pixel_res) + + class WithInterativePlotting: + """Allows to redirect plots to MAPDL plots.""" + + def __init__(self, parent: "_MapdlCore", pixel_res: int) -> None: + self._parent = weakref.ref(parent) + self._pixel_res = pixel_res + + def __enter__(self) -> None: + self._parent()._log.debug("Entering in 'WithInterativePlotting' mode") + + if not self._parent()._has_matplotlib: # pragma: no cover + raise ImportError( + "Install matplotlib to display plots from MAPDL ," + "from Python. Otherwise, plot with vtk with:\n" + "``vtk=True``" + ) - if not self._png_mode: - self.show("PNG", mute=True) - self.gfile(pixel_res, mute=True) + if not self._parent()._png_mode: + self._parent().show("PNG", mute=True) + self._parent().gfile(self._pixel_res, mute=True) + + def __exit__(self, *args) -> None: + self._parent()._log.debug("Exiting in 'WithInterativePlotting' mode") + self._parent().show("close", mute=True) @property def _has_matplotlib(self): @@ -1957,8 +1975,8 @@ def lplot( return general_plotter(meshes, [], labels, **kwargs) else: - self._enable_interactive_plotting() - return super().lplot(nl1=nl1, nl2=nl2, ninc=ninc, **kwargs) + with self._enable_interactive_plotting(): + return super().lplot(nl1=nl1, nl2=nl2, ninc=ninc, **kwargs) def kplot( self, @@ -2033,8 +2051,8 @@ def kplot( return general_plotter([], points, labels, **kwargs) # otherwise, use the legacy plotter - self._enable_interactive_plotting() - return super().kplot(np1=np1, np2=np2, ninc=ninc, lab=lab, **kwargs) + with self._enable_interactive_plotting(): + return super().kplot(np1=np1, np2=np2, ninc=ninc, lab=lab, **kwargs) @property @requires_package("ansys.mapdl.reader", softerror=True) @@ -2551,7 +2569,7 @@ def jobname(self) -> str: def jobname(self, new_jobname: str): """Set the jobname""" self.finish(mute=True) - self.filname(new_jobname, mute=True) + self.filname(new_jobname) self._jobname = new_jobname def modal_analysis( @@ -3008,7 +3026,13 @@ def run(self, command, write_to_log=True, mute=None, **kwargs) -> str: short_cmd = parse_to_short_cmd(command) if short_cmd in PLOT_COMMANDS: - return self._display_plot(self._response) + self._log.debug("It is a plot command.") + plot_path = self._get_plot_name(text) + save_fig = kwargs.get("savefig", False) + if save_fig: + self._download_plot(plot_path, save_fig) + else: + return self._display_plot(plot_path) return self._response @@ -3258,9 +3282,6 @@ def load_table(self, name, array, var1="", var2="", var3="", csysid=""): else: self.slashdelete(filename) - def _display_plot(self, *args, **kwargs): # pragma: no cover - raise NotImplementedError("Implemented by child class") - def _run(self, *args, **kwargs): # pragma: no cover raise NotImplementedError("Implemented by child class") @@ -3488,34 +3509,87 @@ def _get_array( else: return array - def _display_plot(self, text): - """Display the last generated plot (*.png) from MAPDL""" - import scooby + def _get_plot_name(self, text: str) -> str: + """ "Obtain the plot filename. It also downloads it if in remote session.""" + self._log.debug(text) + png_found = PNG_IS_WRITTEN_TO_FILE.findall(text) - self._enable_interactive_plotting() - png_found = PNG_TEST.findall(text) if png_found: # flush graphics writer self.show("CLOSE", mute=True) - self.show("PNG", mute=True) - - import matplotlib.image as mpimg - import matplotlib.pyplot as plt + # self.show("PNG", mute=True) filename = self._screenshot_path() + self._log.debug(f"Screenshot at: {filename}") if os.path.isfile(filename): - img = mpimg.imread(filename) - plt.imshow(img) - plt.axis("off") - if self._show_matplotlib_figures: # pragma: no cover - plt.show() # consider in-line plotting - if scooby.in_ipython(): - from IPython.display import display - - display(plt.gcf()) + return filename else: # pragma: no cover self._log.error("Unable to find screenshot at %s", filename) + else: + self._log.error("Unable to find file in MAPDL command output.") + + def _display_plot(self, filename: str) -> None: + """Display the last generated plot (*.png) from MAPDL""" + import matplotlib.image as mpimg + import matplotlib.pyplot as plt + + def in_ipython(): + # from scooby.in_ipython + # to avoid dependency here. + try: + __IPYTHON__ + return True + except NameError: # pragma: no cover + return False + + self._log.debug("A screenshot file has been found.") + img = mpimg.imread(filename) + plt.imshow(img) + plt.axis("off") + + if self._show_matplotlib_figures: # pragma: no cover + self._log.debug("Using Matplotlib to plot") + plt.show() # consider in-line plotting + + if in_ipython(): + self._log.debug("Using ipython") + from IPython.display import display + + display(plt.gcf()) + + def _download_plot(self, filename: str, plot_name: str) -> None: + """Copy the temporary download plot to the working directory.""" + if isinstance(plot_name, str): + provided = True + path_ = pathlib.Path(plot_name) + plot_name = path_.name + plot_stem = path_.stem + plot_ext = path_.suffix + plot_path = str(path_.parent) + if not plot_path or plot_path == ".": + plot_path = os.getcwd() + + elif isinstance(plot_name, bool): + provided = False + plot_name = "plot.png" + plot_stem = "plot" + plot_ext = ".png" + plot_path = os.getcwd() + else: # pragma: no cover + raise ValueError("Only booleans and str are allowed.") + + id_ = 0 + plot_path_ = os.path.join(plot_path, plot_name) + while os.path.exists(plot_path_) and not provided: + id_ += 1 + plot_path_ = os.path.join(plot_path, f"{plot_stem}_{id_}{plot_ext}") + else: + copyfile(filename, plot_path_) + + self._log.debug( + f"Copy plot file from temp directory to working directory as: {plot_path}" + ) def _screenshot_path(self): """Return last filename based on the current jobname""" diff --git a/src/ansys/mapdl/core/mapdl_grpc.py b/src/ansys/mapdl/core/mapdl_grpc.py index aab19201ed..cd60fcf183 100644 --- a/src/ansys/mapdl/core/mapdl_grpc.py +++ b/src/ansys/mapdl/core/mapdl_grpc.py @@ -2377,7 +2377,7 @@ def _screenshot_path(self): all_filenames = self.list_files() filenames = [] for filename in all_filenames: - if ".png" == filename[-4:]: + if filename.endswith(".png"): filenames.append(filename) filenames.sort() filename = os.path.basename(filenames[-1]) diff --git a/tests/test_mapdl.py b/tests/test_mapdl.py index a617d0534c..23b40f5f1c 100644 --- a/tests/test_mapdl.py +++ b/tests/test_mapdl.py @@ -1982,3 +1982,50 @@ def num_(): def test_download_results_non_local(mapdl, cube_solve): assert mapdl.result is not None assert isinstance(mapdl.result, Result) + + +def test_download_file_with_vkt_false(mapdl, cube_solve, tmpdir): + # Testing basic behaviour + mapdl.eplot(vtk=False, savefig="myfile.png") + assert os.path.exists("myfile.png") + ti_m = os.path.getmtime("myfile.png") + + # Testing overwriting + mapdl.eplot(vtk=False, savefig="myfile.png") + assert not os.path.exists("myfile_1.png") + assert os.path.getmtime("myfile.png") != ti_m # file has been modified. + + os.remove("myfile.png") + + # Testing no extension + mapdl.eplot(vtk=False, savefig="myfile") + assert os.path.exists("myfile") + os.remove("myfile") + + # Testing update name when file exists. + mapdl.eplot(vtk=False, savefig=True) + assert os.path.exists("plot.png") + + mapdl.eplot(vtk=False, savefig=True) + assert os.path.exists("plot_1.png") + + os.remove("plot.png") + os.remove("plot_1.png") + + # Testing full path for downloading + plot_ = os.path.join(tmpdir, "myplot.png") + mapdl.eplot(vtk=False, savefig=plot_) + assert os.path.exists(plot_) + + plot_ = os.path.join(tmpdir, "myplot") + mapdl.eplot(vtk=False, savefig=plot_) + assert os.path.exists(plot_) + + +def test_plots_no_vtk(mapdl): + mapdl.kplot(vtk=False) + mapdl.lplot(vtk=False) + mapdl.aplot(vtk=False) + mapdl.vplot(vtk=False) + mapdl.nplot(vtk=False) + mapdl.eplot(vtk=False) From 0902bad528ad87a08a8b6021eda7ac92a8429f3c Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Tue, 27 Jun 2023 10:44:30 +0200 Subject: [PATCH 4/5] Fixing Pyvista for the next release (#2140) --- src/ansys/mapdl/core/__init__.py | 2 +- src/ansys/mapdl/core/mapdl_geometry.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/ansys/mapdl/core/__init__.py b/src/ansys/mapdl/core/__init__.py index 6375f74cd6..7c4f77a281 100644 --- a/src/ansys/mapdl/core/__init__.py +++ b/src/ansys/mapdl/core/__init__.py @@ -23,7 +23,7 @@ # Per contract with Sphinx-Gallery, this method must be available at top level try: - from pyvista.utilities.sphinx_gallery import _get_sg_image_scraper + import pyvista _HAS_PYVISTA = True except ModuleNotFoundError: # pragma: no cover diff --git a/src/ansys/mapdl/core/mapdl_geometry.py b/src/ansys/mapdl/core/mapdl_geometry.py index 56d1d0faaa..89130286d9 100644 --- a/src/ansys/mapdl/core/mapdl_geometry.py +++ b/src/ansys/mapdl/core/mapdl_geometry.py @@ -32,10 +32,7 @@ def merge_polydata(items): """Merge list of polydata or unstructured grids""" # lazy import here for faster module loading - try: - from pyvista._vtk import vtkAppendPolyData - except: - from vtk import vtkAppendPolyData + from vtkmodules.vtkFiltersCore import vtkAppendPolyData afilter = vtkAppendPolyData() for item in items: From f94ae0908884c8e7c45aa01bb77c54fb527a223b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 09:11:42 +0000 Subject: [PATCH 5/5] Bump scipy, pytest and sphinx-autodoc-typehints (#2146) * Bump scipy, pytest and sphinx-autodoc-typehints Bumps [scipy](https://github.com/scipy/scipy), [pytest](https://github.com/pytest-dev/pytest) and [sphinx-autodoc-typehints](https://github.com/tox-dev/sphinx-autodoc-typehints). These dependencies needed to be updated together. Updates `scipy` from 1.10.1 to 1.11.0 - [Release notes](https://github.com/scipy/scipy/releases) - [Commits](https://github.com/scipy/scipy/compare/v1.10.1...v1.11.0) Updates `pytest` from 7.3.2 to 7.4.0 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.3.2...7.4.0) * Trigger Build * disabling auto update typehints --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: German --- .github/workflows/dependabot.yml | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index 5edc1e6d81..d4c3986661 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: marcoroth/dependabot-bump-together-action@main with: - dependencies: ansys-api-mapdl, vtk, ansys-corba, ansys-dpf-core, ansys-mapdl-reader, ansys-platform-instancemanagement, ansys-sphinx-theme, pyansys-tools-report, platformdirs, autopep8, click, imageio-ffmpeg, imageio, importlib-metadata, jupyter_sphinx, jupyterlab, matplotlib, numpy, numpydoc, pandas, pexpect, plotly, protobuf, pyiges, pypandoc, pytest-cov, pytest-rerunfailures, pytest-sphinx, pytest, pythreejs, pyvista, scipy, setuptools, sphinx-autobuild, sphinx-autodoc-typehints, sphinx-copybutton, sphinx-gallery, sphinx-notfound-page, sphinxcontrib-websupport, sphinxemoji, tqdm, wheel + dependencies: ansys-api-mapdl, vtk, ansys-corba, ansys-dpf-core, ansys-mapdl-reader, ansys-platform-instancemanagement, ansys-sphinx-theme, pyansys-tools-report, platformdirs, autopep8, click, imageio-ffmpeg, imageio, importlib-metadata, jupyter_sphinx, jupyterlab, matplotlib, numpy, numpydoc, pandas, pexpect, plotly, protobuf, pyiges, pypandoc, pytest-cov, pytest-rerunfailures, pytest-sphinx, pytest, pythreejs, pyvista, scipy, setuptools, sphinx-autobuild, sphinx-copybutton, sphinx-gallery, sphinx-notfound-page, sphinxcontrib-websupport, sphinxemoji, tqdm, wheel #sphinx-autodoc-typehints package_managers: pip directory: / branch: main diff --git a/pyproject.toml b/pyproject.toml index 82c7b6c75b..2fdba4cbdc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,9 +58,9 @@ tests = [ "ansys-dpf-core==0.8.1", "autopep8==2.0.2", "matplotlib==3.7.1", - "scipy==1.10.1", + "scipy==1.11.0", "pandas==2.0.2", - "pytest==7.3.2", + "pytest==7.4.0", "pytest-cov==4.1.0", "pyvista==0.39.1", "pyansys-tools-report==0.5.0",