From 27dc8798a0d88e15c3534f6b6ab5240b4591c706 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:12:56 +0200 Subject: [PATCH 01/15] feat: using screenshot method for plotting --- src/ansys/mapdl/core/mapdl_core.py | 56 ++++++++++++------------------ 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/src/ansys/mapdl/core/mapdl_core.py b/src/ansys/mapdl/core/mapdl_core.py index 320e84abed9..cb2011238c8 100644 --- a/src/ansys/mapdl/core/mapdl_core.py +++ b/src/ansys/mapdl/core/mapdl_core.py @@ -2447,16 +2447,7 @@ def run( # special returns for certain geometry commands if short_cmd in PLOT_COMMANDS: self._log.debug("It is a plot command.") - plot_path = self._get_plot_name(text) - - if save_fig: - return self._download_plot(plot_path, save_fig) - elif self._has_matplotlib: - return self._display_plot(plot_path) - else: - self._log.debug( - "Since matplolib is not installed, images are not shown." - ) + self.screenshot(savefig=save_fig) return self._response @@ -3325,36 +3316,35 @@ def get_file_name(path): return target_dir if savefig is None or savefig is False: - self._display_plot(file_name) + return self._display_plot(file_name) - else: - if savefig is True: - # Copying to working directory - target_dir = get_file_name(os.getcwd()) - - elif isinstance(savefig, str): - if not os.path.dirname(savefig): - # File name given only - target_dir = os.path.join(os.getcwd(), savefig) + if savefig is True: + # Copying to working directory + target_dir = get_file_name(os.getcwd()) - elif os.path.isdir(savefig): - # Given directory path only, but not file name. - target_dir = get_file_name(savefig) + elif isinstance(savefig, str): + if not os.path.dirname(savefig): + # File name given only + target_dir = os.path.join(os.getcwd(), savefig) - elif os.path.exists(os.path.dirname(savefig)): - # Only directory is given. Checking if directory exists. - target_dir = savefig + elif os.path.isdir(savefig): + # Given directory path only, but not file name. + target_dir = get_file_name(savefig) - else: - raise FileNotFoundError("The filename or path is not valid.") + elif os.path.exists(os.path.dirname(savefig)): + # Only directory is given. Checking if directory exists. + target_dir = savefig else: - raise ValueError( - "Only strings or Booleans are valid inputs for the 'savefig' parameter." - ) + raise FileNotFoundError("The filename or path is not valid.") + + else: + raise ValueError( + "Only strings or Booleans are valid inputs for the 'savefig' parameter." + ) - copy(file_name, target_dir) - return os.path.basename(target_dir) + copy(file_name, target_dir) + return os.path.basename(target_dir) def _create_session(self): """Generate a session ID.""" From ff59545b6bdb91df09a229de1677b885f26ab727 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:16:10 +0200 Subject: [PATCH 02/15] feat: adding plvar to plot commands --- src/ansys/mapdl/core/mapdl_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ansys/mapdl/core/mapdl_core.py b/src/ansys/mapdl/core/mapdl_core.py index cb2011238c8..f735a257b42 100644 --- a/src/ansys/mapdl/core/mapdl_core.py +++ b/src/ansys/mapdl/core/mapdl_core.py @@ -192,6 +192,7 @@ "NPLO", "PLES", "PLNS", + "PLVA", "PSDG", "SECP", "SPGR", From 4f295a101a6893bf4e5ec580fa8a45aabfaf84b0 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Tue, 26 Aug 2025 14:18:51 +0000 Subject: [PATCH 03/15] chore: adding changelog file 4196.added.md [dependabot-skip] --- doc/changelog.d/4196.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/4196.added.md diff --git a/doc/changelog.d/4196.added.md b/doc/changelog.d/4196.added.md new file mode 100644 index 00000000000..5f7f767741a --- /dev/null +++ b/doc/changelog.d/4196.added.md @@ -0,0 +1 @@ +Adding plvar to plot commands From 68bd0458e7492c160544355b5916dc7d1f0cb989 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Tue, 26 Aug 2025 17:20:21 +0200 Subject: [PATCH 04/15] refactor: remove redundant reissuing of plot commands in _MapdlCore --- src/ansys/mapdl/core/mapdl_core.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ansys/mapdl/core/mapdl_core.py b/src/ansys/mapdl/core/mapdl_core.py index f735a257b42..eb969974859 100644 --- a/src/ansys/mapdl/core/mapdl_core.py +++ b/src/ansys/mapdl/core/mapdl_core.py @@ -2420,14 +2420,6 @@ def run( self._log.debug(f"Running (verbose: {verbose}, mute={mute}): '{command}'") text = self._run(command, verbose=verbose, mute=mute) - if ( - "Display device has not yet been specified with the /SHOW command" in text - and short_cmd in PLOT_COMMANDS - ): - # Reissuing the command to make sure we get output. - self.show(self.default_file_type_for_plots) - text = self._run(command, verbose=verbose, mute=mute) - self._after_run(command) if mute: From 6218a0ea3c6d9352f9876d0f79a1626631c7d3ed Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Tue, 26 Aug 2025 17:24:21 +0200 Subject: [PATCH 05/15] test: clean up screenshot file before test execution --- tests/test_mapdl.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_mapdl.py b/tests/test_mapdl.py index b5154fd3a5a..a1277c16c61 100644 --- a/tests/test_mapdl.py +++ b/tests/test_mapdl.py @@ -2689,6 +2689,9 @@ def test_screenshot(mapdl, make_block, tmpdir): assert mapdl.screenshot(False) is None assert "TIFF" == mapdl.file_type_for_plots + if os.path.exists("mapdl_screenshot_0.png"): + os.remove("mapdl_screenshot_0.png") + file_name = mapdl.screenshot(True) assert "mapdl_screenshot_0.png" == file_name assert "TIFF" == mapdl.file_type_for_plots From 374e754aedb9fb7467e6da8e951c8f48b4b29d20 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:23:52 +0200 Subject: [PATCH 06/15] refactor: simplify screenshot filename filtering in MapdlGrpc class --- src/ansys/mapdl/core/mapdl_grpc.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ansys/mapdl/core/mapdl_grpc.py b/src/ansys/mapdl/core/mapdl_grpc.py index 50c8ee0165c..555cf145f58 100644 --- a/src/ansys/mapdl/core/mapdl_grpc.py +++ b/src/ansys/mapdl/core/mapdl_grpc.py @@ -2600,11 +2600,9 @@ def _screenshot_path(self): if self._local: return super()._screenshot_path() - all_filenames = self.list_files() - filenames = [] - for filename in all_filenames: - if filename.endswith(".png"): - filenames.append(filename) + filenames = [ + filename for filename in self.list_files() if filename.endswith(".png") + ] filenames.sort() filename = os.path.basename(filenames[-1]) From e6a800902caa46df0cbe12fe9579f9de87133b20 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:27:10 +0200 Subject: [PATCH 07/15] refactor: improve screenshot handling and filename management in _MapdlCore --- src/ansys/mapdl/core/mapdl_core.py | 84 ++++++++++++------------------ 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/src/ansys/mapdl/core/mapdl_core.py b/src/ansys/mapdl/core/mapdl_core.py index eb969974859..7650802c2ff 100644 --- a/src/ansys/mapdl/core/mapdl_core.py +++ b/src/ansys/mapdl/core/mapdl_core.py @@ -28,7 +28,7 @@ import os import pathlib import re -from shutil import copy, copyfile, rmtree +from shutil import copyfile, rmtree # Subprocess is needed to start the backend. But # the input is controlled by the library. Excluding bandit check. @@ -2324,7 +2324,7 @@ def run( # Check kwargs verbose = kwargs.pop("verbose", False) - save_fig = kwargs.pop("savefig", False) + savefig = kwargs.pop("savefig", False) # Check if you want to avoid the current non-interactive context. avoid_non_interactive = kwargs.pop("avoid_non_interactive", False) @@ -2440,7 +2440,7 @@ def run( # special returns for certain geometry commands if short_cmd in PLOT_COMMANDS: self._log.debug("It is a plot command.") - self.screenshot(savefig=save_fig) + return self.screenshot(savefig=savefig, default_name="plot") return self._response @@ -2498,7 +2498,7 @@ def _get_plot_name(self, text: str) -> str: if os.path.isfile(filename): return filename else: # pragma: no cover - raise MapdlRuntimeError("Unable to find screenshot at %s", filename) + raise MapdlRuntimeError(f"Unable to find screenshot at {filename}") else: raise MapdlRuntimeError( "Unable to find plotted file in MAPDL command output. " @@ -2538,22 +2538,30 @@ def in_ipython(): display(plt.gcf()) - def _download_plot(self, filename: str, plot_name: str) -> None: + def _download_plot( + self, filename: str, plot_name: str, default_name: str = "plot" + ) -> 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() + if path_.is_dir() or path_.suffix == "": + plot_name = f"{default_name}.png" + plot_stem = default_name + plot_ext = ".png" + plot_path = str(path_) + else: + 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_name = f"{default_name}.png" + plot_stem = default_name plot_ext = ".png" plot_path = os.getcwd() else: # pragma: no cover @@ -2570,6 +2578,10 @@ def _download_plot(self, filename: str, plot_name: str) -> None: self._log.debug( f"Copy plot file from temp directory to working directory as: {plot_path}" ) + if provided: + return plot_path_ + else: + return os.path.basename(plot_path_) def _screenshot_path(self): """Return last filename based on the current jobname""" @@ -3271,7 +3283,9 @@ def list_files(self, refresh_cache: bool = True) -> List[str]: warn("No files listed") return files - def screenshot(self, savefig: Optional[str] = None): + def screenshot( + self, savefig: Optional[str] = None, default_name: str = "mapdl_screenshot" + ) -> str: """Take an MAPDL screenshot and show it in a popup window. Parameters @@ -3298,46 +3312,12 @@ def screenshot(self, savefig: Optional[str] = None): self.show(previous_device) # previous device file_name = self._get_plot_name(out_) - def get_file_name(path): - """Get a new filename so as not to overwrite an existing one.""" - target_dir = os.path.join(path, "mapdl_screenshot_0.png") - i = 0 - while os.path.exists(target_dir): - # Ensuring file is not overwritten. - i += 1 - target_dir = os.path.join(path, f"mapdl_screenshot_{i}.png") - return target_dir - - if savefig is None or savefig is False: + if savefig: + return self._download_plot(file_name, savefig, default_name=default_name) + elif self._has_matplotlib: return self._display_plot(file_name) - - if savefig is True: - # Copying to working directory - target_dir = get_file_name(os.getcwd()) - - elif isinstance(savefig, str): - if not os.path.dirname(savefig): - # File name given only - target_dir = os.path.join(os.getcwd(), savefig) - - elif os.path.isdir(savefig): - # Given directory path only, but not file name. - target_dir = get_file_name(savefig) - - elif os.path.exists(os.path.dirname(savefig)): - # Only directory is given. Checking if directory exists. - target_dir = savefig - - else: - raise FileNotFoundError("The filename or path is not valid.") - else: - raise ValueError( - "Only strings or Booleans are valid inputs for the 'savefig' parameter." - ) - - copy(file_name, target_dir) - return os.path.basename(target_dir) + self._log.debug("Since matplolib is not installed, images are not shown.") def _create_session(self): """Generate a session ID.""" From 852fa7a2acaf63ae29a6f788f68bda93d66711a3 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:27:26 +0200 Subject: [PATCH 08/15] refactor: update screenshot filename handling in test cases --- tests/test_mapdl.py | 9 +++------ tests/test_plotting.py | 11 +++++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_mapdl.py b/tests/test_mapdl.py index a1277c16c61..56ef1beb906 100644 --- a/tests/test_mapdl.py +++ b/tests/test_mapdl.py @@ -2689,11 +2689,8 @@ def test_screenshot(mapdl, make_block, tmpdir): assert mapdl.screenshot(False) is None assert "TIFF" == mapdl.file_type_for_plots - if os.path.exists("mapdl_screenshot_0.png"): - os.remove("mapdl_screenshot_0.png") - file_name = mapdl.screenshot(True) - assert "mapdl_screenshot_0.png" == file_name + assert "mapdl_screenshot.png" == file_name assert "TIFF" == mapdl.file_type_for_plots assert file_name in os.listdir(os.getcwd()) @@ -2702,12 +2699,12 @@ def test_screenshot(mapdl, make_block, tmpdir): assert "TIFF" == mapdl.file_type_for_plots assert file_name in os.listdir(os.getcwd()) - os.remove("mapdl_screenshot_0.png") + os.remove("mapdl_screenshot.png") os.remove(file_name) file_name = mapdl.screenshot(str(tmpdir)) assert "TIFF" == mapdl.file_type_for_plots - assert file_name in os.listdir(str(tmpdir)) + assert os.path.basename(file_name) in os.listdir(str(tmpdir)) dest = os.path.join(tmpdir, "myscreenshot.png") file_name = mapdl.screenshot(dest) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 27b6f9b419a..39f98169ffe 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -221,9 +221,12 @@ def test_kplot(cleared, mapdl, tmpdir, backend): filename = str(tmpdir.mkdir("tmpdir").join("tmp.png")) cpos = mapdl.kplot(graphics_backend=backend, savefig=filename) - assert cpos is None - if backend: - assert os.path.isfile(filename) + if backend == GraphicsBackend.MAPDL: + assert isinstance(cpos, str) + else: + assert cpos is None + + assert os.path.isfile(filename) @pytest.mark.parametrize( @@ -1054,7 +1057,7 @@ def test_file_type_for_plots(mapdl, cleared): [each for each in mapdl.list_files() if each.endswith(".png")] ) - assert n_files_ending_png_before + 1 == n_files_ending_png_after + assert n_files_ending_png_before + 2 == n_files_ending_png_after @pytest.mark.parametrize("entity", ["KP", "LINE", "AREA", "VOLU", "NODE", "ELEM"]) From be445991cc142b25c59c3e253a8d2ef783bf2a92 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Wed, 27 Aug 2025 17:21:08 +0200 Subject: [PATCH 09/15] test: update file handling in test_download_file_with_vkt_false --- tests/test_plotting.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 39f98169ffe..cd7aeec50ae 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -167,14 +167,8 @@ def test_download_file_with_vkt_false(mapdl, cube_solve, tmpdir): mapdl.eplot(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(savefig="myfile") - assert os.path.exists("myfile") - os.remove("myfile") - # Testing update name when file exists. mapdl.eplot(savefig=True) assert os.path.exists("plot.png") @@ -190,7 +184,7 @@ def test_download_file_with_vkt_false(mapdl, cube_solve, tmpdir): mapdl.eplot(savefig=plot_) assert os.path.exists(plot_) - plot_ = os.path.join(tmpdir, "myplot") + plot_ = os.path.join(tmpdir, "myplot.png") mapdl.eplot(savefig=plot_) assert os.path.exists(plot_) From 0dacb50b324b38f19e710f769070efb5ba30be0f Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:34:50 +0200 Subject: [PATCH 10/15] refactor: remove unused plot command 'PLVA' from PLOT_COMMANDS so I can do another PR about it --- src/ansys/mapdl/core/mapdl_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ansys/mapdl/core/mapdl_core.py b/src/ansys/mapdl/core/mapdl_core.py index 7650802c2ff..4ae193cbcfd 100644 --- a/src/ansys/mapdl/core/mapdl_core.py +++ b/src/ansys/mapdl/core/mapdl_core.py @@ -192,7 +192,6 @@ "NPLO", "PLES", "PLNS", - "PLVA", "PSDG", "SECP", "SPGR", From 7c18f00808d61915007da74c8629fcc414ebcb3c Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:36:02 +0200 Subject: [PATCH 11/15] feat: add plot command 'PLVA' to PLOT_COMMANDS --- src/ansys/mapdl/core/mapdl_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ansys/mapdl/core/mapdl_core.py b/src/ansys/mapdl/core/mapdl_core.py index 4ae193cbcfd..7650802c2ff 100644 --- a/src/ansys/mapdl/core/mapdl_core.py +++ b/src/ansys/mapdl/core/mapdl_core.py @@ -192,6 +192,7 @@ "NPLO", "PLES", "PLNS", + "PLVA", "PSDG", "SECP", "SPGR", From 5e00a25e185fa854f0540dc8874498b9e1203055 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Thu, 28 Aug 2025 12:56:32 +0200 Subject: [PATCH 12/15] feat: enhance PSD plotting functionality with MAPDL and matplotlib --- examples/00-mapdl-examples/psd-vm203.py | 36 ++++++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/examples/00-mapdl-examples/psd-vm203.py b/examples/00-mapdl-examples/psd-vm203.py index 1545b83ed6c..b6f31856f5d 100644 --- a/examples/00-mapdl-examples/psd-vm203.py +++ b/examples/00-mapdl-examples/psd-vm203.py @@ -256,17 +256,39 @@ Pmax = rpsduz.max() ############################################################################### -# plot the response psd +# You can plot the response psd using MAPDL methods: + +# Setting up the PSD plot with MAPDL +mapdl.axlab("X", "Frequency (Hz)") +mapdl.axlab("Y", "RPSD (M**2/Hz)") +mapdl.yrange(ymin="0", ymax="0.004") +mapdl.xrange(xmin="0", xmax="80") +mapdl.gropt(lab="LOGY", key="ON") + +mapdl.xvar(0) # Plot against time/frequency +mapdl.plvar(3) # Plot variable set 3 (RPSD) + +############################################################################### +# or use matplotlib: + freqs = mapdl.vget("FREQS", 1) # Remove the last two values as they are zero -plt.plot(freqs[:-2], rpsduz[:-2], label="RPSD UZ") +fig, ax = plt.subplots(figsize=(8, 4)) + +ax.plot(freqs, rpsduz, label="RPSD UZ") + +ax.set_yscale("log") +ax.set_xlabel("Frequency (Hz)") +ax.set_ylabel(r"RPSD $\left( \dfrac{M^2}{Hz}\right)$") +ax.set_xlim((0, 80)) +ax.set_ylim((0, 0.004)) +ax.grid() + +fig.legend() -plt.xlabel("Frequency Hz") -plt.ylabel(r"RPSD $\left( \dfrac{M^2}{Hz}\right)$") -plt.legend() -plt.tight_layout() -plt.show() +fig.tight_layout() +fig.show() ############################################################################### # Compare and print results to manual values From 027a55550485cf68215e75f6052e92a98e5eaf9f Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Thu, 28 Aug 2025 13:13:21 +0200 Subject: [PATCH 13/15] test: add test for plvar function to verify screenshot behavior --- tests/test_plotting.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index cd7aeec50ae..7acde5bb496 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -1338,3 +1338,10 @@ def test_deprecated_params(mapdl, make_block): mapdl.eplot(vtk=True) with pytest.warns(DeprecationWarning, match="'vtk' and 'use_vtk' are deprecated"): mapdl.eplot(vtk=False) + + +def test_plvar(mapdl, coupled_example, verify_image_cache): + mapdl.post26() + with patch("ansys.mapdl.core.Mapdl.screenshot") as mock_screenshot: + mapdl.plvar(4, 5) + mock_screenshot.assert_called_once() From 3bf454f741495be506edc34f9321ae82068abf4b Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:16:30 +0000 Subject: [PATCH 14/15] chore: adding changelog file 4204.added.md [dependabot-skip] --- doc/changelog.d/4204.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/4204.added.md diff --git a/doc/changelog.d/4204.added.md b/doc/changelog.d/4204.added.md new file mode 100644 index 00000000000..731d7653d0f --- /dev/null +++ b/doc/changelog.d/4204.added.md @@ -0,0 +1 @@ +Adding PLVAR to plot commands From 996c5407d63cdfb646aff2909e42af73b74e7508 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Thu, 28 Aug 2025 14:04:35 +0200 Subject: [PATCH 15/15] test: remove unused parameter from test_plvar function --- tests/test_plotting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 7acde5bb496..d2e2fd5050e 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -1340,7 +1340,7 @@ def test_deprecated_params(mapdl, make_block): mapdl.eplot(vtk=False) -def test_plvar(mapdl, coupled_example, verify_image_cache): +def test_plvar(mapdl, coupled_example): mapdl.post26() with patch("ansys.mapdl.core.Mapdl.screenshot") as mock_screenshot: mapdl.plvar(4, 5)