From 463621e8d226a739571464f1b501c92c930de49f Mon Sep 17 00:00:00 2001 From: Mainak Kundu Date: Mon, 16 Dec 2024 17:49:24 -0500 Subject: [PATCH 1/8] feat: Return launch and connection info from standalone launcher dry-run --- src/ansys/fluent/core/launcher/launcher.py | 5 ++-- src/ansys/fluent/core/launcher/server_info.py | 27 +++++++++++++++++-- .../fluent/core/launcher/slurm_launcher.py | 7 +++-- .../core/launcher/standalone_launcher.py | 14 ++++++---- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/ansys/fluent/core/launcher/launcher.py b/src/ansys/fluent/core/launcher/launcher.py index 7e1be2ab755..a6432024c45 100644 --- a/src/ansys/fluent/core/launcher/launcher.py +++ b/src/ansys/fluent/core/launcher/launcher.py @@ -168,8 +168,9 @@ def launch_fluent( See also :mod:`~ansys.fluent.core.launcher.fluent_container`. dry_run : bool, optional Defaults to False. If True, will not launch Fluent, and will instead print configuration information - that would be used as if Fluent was being launched. If dry running a container start, - ``launch_fluent()`` will return the configured ``container_dict``. + that would be used as if Fluent was being launched. If dry running a standalone start + ``launch_fluent()`` will return a tuple containing the launch string and the server info file name. + If dry running a container start, ``launch_fluent()`` will return the configured ``container_dict``. cleanup_on_exit : bool, optional Whether to shut down the connected Fluent session when PyFluent is exited, or the ``exit()`` method is called on the session instance, diff --git a/src/ansys/fluent/core/launcher/server_info.py b/src/ansys/fluent/core/launcher/server_info.py index 662d76e8d8c..4cbaf52e01d 100644 --- a/src/ansys/fluent/core/launcher/server_info.py +++ b/src/ansys/fluent/core/launcher/server_info.py @@ -10,7 +10,27 @@ from ansys.fluent.core.session import _parse_server_info_file -def _get_server_info_file_name(use_tmpdir=True): +def _get_server_info_file_name(use_tmpdir=True) -> tuple[str, str]: + """Get a tuple containing server and client-side file names with the server connection information. + When server and client are in a different machine, the environment variable SERVER_INFO_DIR + can be set to a shared directory between the two machines and the server-info file will be + created in that directory. The value of the environment variable SERVER_INFO_DIR can be + different for the server and client machines. The relative path of the server-side server-info + file is passed to Fluent launcher and PyFluent connects to the server using the absolute path + of the client-side server-info file. A typical use case of the environment variable + SERVER_INFO_DIR is as follows: + - Server machine environment variable: SERVER_INFO_DIR=/mnt/shared + - Client machine environment variable: SERVER_INFO_DIR=\\\\server\\shared + - Server-side server-info file: /mnt/shared/serverinfo-xyz.txt + - Client-side server-info file: \\\\server\\shared\\serverinfo-xyz.txt + - Fluent launcher command: fluent ... -sifile=serverinfo-xyz.txt ... + - From PyFluent: connect_to_fluent(server_info_file_name="\\\\server\\shared\\serverinfo-xyz.txt") + + When the environment variable SERVER_INFO_DIR is not set, the server-side and client-side + file paths for the server-info file are identical. The server-info file is created in the + temporary directory if ``use_tmpdir`` is True, otherwise it is created in the current working + directory. + """ server_info_dir = os.getenv("SERVER_INFO_DIR") dir_ = ( Path(server_info_dir) @@ -19,7 +39,10 @@ def _get_server_info_file_name(use_tmpdir=True): ) fd, file_name = tempfile.mkstemp(suffix=".txt", prefix="serverinfo-", dir=str(dir_)) os.close(fd) - return file_name + if server_info_dir: + return Path(file_name).name, file_name + else: + return file_name, file_name def _get_server_info( diff --git a/src/ansys/fluent/core/launcher/slurm_launcher.py b/src/ansys/fluent/core/launcher/slurm_launcher.py index 9323fc51040..7cec4268467 100644 --- a/src/ansys/fluent/core/launcher/slurm_launcher.py +++ b/src/ansys/fluent/core/launcher/slurm_launcher.py @@ -416,11 +416,14 @@ def __init__( ) def _prepare(self): - self._server_info_file_name = _get_server_info_file_name(use_tmpdir=False) + server_info_file_name_for_server, server_info_file_name_for_client = ( + _get_server_info_file_name(use_tmpdir=False) + ) + self._server_info_file_name = server_info_file_name_for_client self._argvals.update(self._argvals["scheduler_options"]) launch_cmd = _generate_launch_string( self._argvals, - self._server_info_file_name, + server_info_file_name_for_server, ) self._sifile_last_mtime = Path(self._server_info_file_name).stat().st_mtime diff --git a/src/ansys/fluent/core/launcher/standalone_launcher.py b/src/ansys/fluent/core/launcher/standalone_launcher.py index 4455b35d71d..f2a4fe2920f 100644 --- a/src/ansys/fluent/core/launcher/standalone_launcher.py +++ b/src/ansys/fluent/core/launcher/standalone_launcher.py @@ -128,8 +128,9 @@ def __init__( exited, or the ``exit()`` method is called on the session instance, or if the session instance becomes unreferenced. The default is ``True``. dry_run : bool, optional - Defaults to False. If True, will not launch Fluent, and will instead print configuration information - that would be used as if Fluent was being launched. + Defaults to False. If True, will not launch Fluent, and will print configuration information + that would be used as if Fluent was being launched. If True, the ``call()`` method will return + a tuple containing the launch string and the server info file name. start_transcript : bool, optional Whether to start streaming the Fluent transcript in the client. The default is ``True``. You can stop and start the streaming of the @@ -200,10 +201,13 @@ def __init__( if os.getenv("PYFLUENT_FLUENT_DEBUG") == "1": self.argvals["fluent_debug"] = True - self._server_info_file_name = _get_server_info_file_name() + server_info_file_name_for_server, server_info_file_name_for_client = ( + _get_server_info_file_name() + ) + self._server_info_file_name = server_info_file_name_for_client self._launch_string = _generate_launch_string( self.argvals, - self._server_info_file_name, + server_info_file_name_for_server, ) self._sifile_last_mtime = Path(self._server_info_file_name).stat().st_mtime @@ -232,7 +236,7 @@ def __init__( def __call__(self): if self.argvals["dry_run"]: print(f"Fluent launch string: {self._launch_string}") - return + return self._launch_string, self._server_info_file_name try: logger.debug(f"Launching Fluent with command: {self._launch_cmd}") From ce7083c6e1243459c428003cb61cc4f32db09adc Mon Sep 17 00:00:00 2001 From: Mainak Kundu Date: Mon, 16 Dec 2024 18:10:35 -0500 Subject: [PATCH 2/8] feat: use fluent_path verbatim --- src/ansys/fluent/core/launcher/launcher.py | 2 +- .../fluent/core/launcher/process_launch_string.py | 14 ++++++++------ .../fluent/core/launcher/standalone_launcher.py | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ansys/fluent/core/launcher/launcher.py b/src/ansys/fluent/core/launcher/launcher.py index a6432024c45..2b5f830635b 100644 --- a/src/ansys/fluent/core/launcher/launcher.py +++ b/src/ansys/fluent/core/launcher/launcher.py @@ -169,7 +169,7 @@ def launch_fluent( dry_run : bool, optional Defaults to False. If True, will not launch Fluent, and will instead print configuration information that would be used as if Fluent was being launched. If dry running a standalone start - ``launch_fluent()`` will return a tuple containing the launch string and the server info file name. + ``launch_fluent()`` will return a tuple containing Fluent launch string and the server info file name. If dry running a container start, ``launch_fluent()`` will return the configured ``container_dict``. cleanup_on_exit : bool, optional Whether to shut down the connected Fluent session when PyFluent is diff --git a/src/ansys/fluent/core/launcher/process_launch_string.py b/src/ansys/fluent/core/launcher/process_launch_string.py index 91dcc1744aa..2b1ed4cfad8 100644 --- a/src/ansys/fluent/core/launcher/process_launch_string.py +++ b/src/ansys/fluent/core/launcher/process_launch_string.py @@ -137,7 +137,14 @@ def get_exe_path(fluent_root: Path) -> Path: return fluent_root / "bin" / "fluent" # Look for Fluent exe path in the following order: - # 1. product_version parameter passed with launch_fluent + # 1. Custom Path provided by the user in launch_fluent + fluent_path = launch_argvals.get("fluent_path") + if fluent_path: + # Return the fluent_path string verbatim. The path may not even exist + # in the current machine if user wants to launch fluent externally (dry_run use case). + return fluent_path + + # 2. product_version parameter passed with launch_fluent product_version = launch_argvals.get("product_version") if product_version: return get_exe_path(get_fluent_root(FluentVersion(product_version))) @@ -147,10 +154,5 @@ def get_exe_path(fluent_root: Path) -> Path: if fluent_root: return get_exe_path(Path(fluent_root)) - # 2. Custom Path provided by the user in launch_fluent - fluent_path = launch_argvals.get("fluent_path") - if fluent_path: - return Path(fluent_path) - # 3. the latest ANSYS version from AWP_ROOT environment variables return get_exe_path(get_fluent_root(FluentVersion.get_latest_installed())) diff --git a/src/ansys/fluent/core/launcher/standalone_launcher.py b/src/ansys/fluent/core/launcher/standalone_launcher.py index f2a4fe2920f..bdcd9770fe8 100644 --- a/src/ansys/fluent/core/launcher/standalone_launcher.py +++ b/src/ansys/fluent/core/launcher/standalone_launcher.py @@ -130,7 +130,7 @@ def __init__( dry_run : bool, optional Defaults to False. If True, will not launch Fluent, and will print configuration information that would be used as if Fluent was being launched. If True, the ``call()`` method will return - a tuple containing the launch string and the server info file name. + a tuple containing Fluent launch string and the server info file name. start_transcript : bool, optional Whether to start streaming the Fluent transcript in the client. The default is ``True``. You can stop and start the streaming of the From 067bd5af730dcb23b8d0975c7c449e1ed3178c3d Mon Sep 17 00:00:00 2001 From: Mainak Kundu Date: Mon, 16 Dec 2024 18:22:14 -0500 Subject: [PATCH 3/8] feat: Add tests --- tests/test_launcher.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_launcher.py b/tests/test_launcher.py index 90be7b30019..410df52030a 100644 --- a/tests/test_launcher.py +++ b/tests/test_launcher.py @@ -1,6 +1,7 @@ import os from pathlib import Path import platform +import tempfile from tempfile import TemporaryDirectory import pytest @@ -498,3 +499,29 @@ def test_fluent_automatic_transcript(monkeypatch): with TemporaryDirectory(dir=pyfluent.EXAMPLES_PATH) as tmp_dir: with pyfluent.launch_fluent(container_dict=dict(working_dir=tmp_dir)): assert not list(Path(tmp_dir).glob("*.trn")) + + +def test_standalone_launcher_dry_run(): + fluent_path = r"\x\y\z\fluent.exe" + fluent_launch_string, server_info_file_name = pyfluent.launch_fluent( + fluent_path=fluent_path, dry_run=True + ) + assert str(Path(server_info_file_name).parent) == tempfile.gettempdir() + assert ( + fluent_launch_string + == f"{fluent_path} 3ddp -hidden -sifile={server_info_file_name} -nm" + ) + + +def test_standalone_launcher_dry_run_with_server_info_dir(monkeypatch): + with tempfile.TemporaryDirectory() as tmp_dir: + monkeypatch.setenv("SERVER_INFO_DIR", tmp_dir) + fluent_path = r"\x\y\z\fluent.exe" + fluent_launch_string, server_info_file_name = pyfluent.launch_fluent( + fluent_path=fluent_path, dry_run=True + ) + assert str(Path(server_info_file_name).parent) == tmp_dir + assert ( + fluent_launch_string + == f"{fluent_path} 3ddp -hidden -sifile={Path(server_info_file_name).name} -nm" + ) From c194d65730e449c6a7454ca76c4999bb2d554089 Mon Sep 17 00:00:00 2001 From: Mainak Kundu Date: Mon, 16 Dec 2024 18:31:30 -0500 Subject: [PATCH 4/8] feat: Return launch and connection info from standalone launcher dry-run --- src/ansys/fluent/core/launcher/server_info.py | 2 +- src/ansys/fluent/core/launcher/slurm_launcher.py | 4 ++-- src/ansys/fluent/core/launcher/standalone_launcher.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ansys/fluent/core/launcher/server_info.py b/src/ansys/fluent/core/launcher/server_info.py index 4cbaf52e01d..1308bd08af9 100644 --- a/src/ansys/fluent/core/launcher/server_info.py +++ b/src/ansys/fluent/core/launcher/server_info.py @@ -10,7 +10,7 @@ from ansys.fluent.core.session import _parse_server_info_file -def _get_server_info_file_name(use_tmpdir=True) -> tuple[str, str]: +def _get_server_info_file_names(use_tmpdir=True) -> tuple[str, str]: """Get a tuple containing server and client-side file names with the server connection information. When server and client are in a different machine, the environment variable SERVER_INFO_DIR can be set to a shared directory between the two machines and the server-info file will be diff --git a/src/ansys/fluent/core/launcher/slurm_launcher.py b/src/ansys/fluent/core/launcher/slurm_launcher.py index 7cec4268467..eb0397ba0f4 100644 --- a/src/ansys/fluent/core/launcher/slurm_launcher.py +++ b/src/ansys/fluent/core/launcher/slurm_launcher.py @@ -64,7 +64,7 @@ UIMode, _get_argvals_and_session, ) -from ansys.fluent.core.launcher.server_info import _get_server_info_file_name +from ansys.fluent.core.launcher.server_info import _get_server_info_file_names from ansys.fluent.core.session_meshing import Meshing from ansys.fluent.core.session_pure_meshing import PureMeshing from ansys.fluent.core.session_solver import Solver @@ -417,7 +417,7 @@ def __init__( def _prepare(self): server_info_file_name_for_server, server_info_file_name_for_client = ( - _get_server_info_file_name(use_tmpdir=False) + _get_server_info_file_names(use_tmpdir=False) ) self._server_info_file_name = server_info_file_name_for_client self._argvals.update(self._argvals["scheduler_options"]) diff --git a/src/ansys/fluent/core/launcher/standalone_launcher.py b/src/ansys/fluent/core/launcher/standalone_launcher.py index bdcd9770fe8..804d5732133 100644 --- a/src/ansys/fluent/core/launcher/standalone_launcher.py +++ b/src/ansys/fluent/core/launcher/standalone_launcher.py @@ -43,7 +43,7 @@ ) from ansys.fluent.core.launcher.server_info import ( _get_server_info, - _get_server_info_file_name, + _get_server_info_file_names, ) import ansys.fluent.core.launcher.watchdog as watchdog from ansys.fluent.core.utils.fluent_version import FluentVersion @@ -202,7 +202,7 @@ def __init__( self.argvals["fluent_debug"] = True server_info_file_name_for_server, server_info_file_name_for_client = ( - _get_server_info_file_name() + _get_server_info_file_names() ) self._server_info_file_name = server_info_file_name_for_client self._launch_string = _generate_launch_string( From c09771c8560d4bc9a653486b664ade70538d2358 Mon Sep 17 00:00:00 2001 From: Mainak Kundu Date: Mon, 16 Dec 2024 18:33:16 -0500 Subject: [PATCH 5/8] feat: doc --- src/ansys/fluent/core/launcher/server_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/fluent/core/launcher/server_info.py b/src/ansys/fluent/core/launcher/server_info.py index 1308bd08af9..44c3f5c3081 100644 --- a/src/ansys/fluent/core/launcher/server_info.py +++ b/src/ansys/fluent/core/launcher/server_info.py @@ -11,7 +11,7 @@ def _get_server_info_file_names(use_tmpdir=True) -> tuple[str, str]: - """Get a tuple containing server and client-side file names with the server connection information. + """Returns a tuple containing server and client-side file names with the server connection information. When server and client are in a different machine, the environment variable SERVER_INFO_DIR can be set to a shared directory between the two machines and the server-info file will be created in that directory. The value of the environment variable SERVER_INFO_DIR can be From 2f9c5c6c60ab6adf26b4d344cf1777c6455a2881 Mon Sep 17 00:00:00 2001 From: Mainak Kundu Date: Tue, 17 Dec 2024 13:37:53 -0500 Subject: [PATCH 6/8] fix: tests --- tests/test_launcher.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_launcher.py b/tests/test_launcher.py index 410df52030a..b8b8c1633c8 100644 --- a/tests/test_launcher.py +++ b/tests/test_launcher.py @@ -501,7 +501,8 @@ def test_fluent_automatic_transcript(monkeypatch): assert not list(Path(tmp_dir).glob("*.trn")) -def test_standalone_launcher_dry_run(): +def test_standalone_launcher_dry_run(monkeypatch): + monkeypatch.setenv("PYFLUENT_LAUNCH_CONTAINER", "0") fluent_path = r"\x\y\z\fluent.exe" fluent_launch_string, server_info_file_name = pyfluent.launch_fluent( fluent_path=fluent_path, dry_run=True @@ -514,6 +515,7 @@ def test_standalone_launcher_dry_run(): def test_standalone_launcher_dry_run_with_server_info_dir(monkeypatch): + monkeypatch.setenv("PYFLUENT_LAUNCH_CONTAINER", "0") with tempfile.TemporaryDirectory() as tmp_dir: monkeypatch.setenv("SERVER_INFO_DIR", tmp_dir) fluent_path = r"\x\y\z\fluent.exe" From ddfc49f7c7405a47f8d3c51d19ea2666b3031882 Mon Sep 17 00:00:00 2001 From: Mainak Kundu Date: Tue, 17 Dec 2024 15:36:13 -0500 Subject: [PATCH 7/8] fix: test --- src/ansys/fluent/core/launcher/pyfluent_enums.py | 9 ++++++--- src/ansys/fluent/core/launcher/standalone_launcher.py | 4 +--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ansys/fluent/core/launcher/pyfluent_enums.py b/src/ansys/fluent/core/launcher/pyfluent_enums.py index cfff8b63c2b..278b055061e 100644 --- a/src/ansys/fluent/core/launcher/pyfluent_enums.py +++ b/src/ansys/fluent/core/launcher/pyfluent_enums.py @@ -276,9 +276,7 @@ def _get_running_session_mode( return session_mode.get_fluent_value() -def _get_standalone_launch_fluent_version( - product_version: FluentVersion | str | float | int | None, -) -> FluentVersion | None: +def _get_standalone_launch_fluent_version(argvals) -> FluentVersion | None: """Determine the Fluent version during the execution of the ``launch_fluent()`` method in standalone mode. @@ -295,9 +293,14 @@ def _get_standalone_launch_fluent_version( # Look for Fluent version in the following order: # 1. product_version parameter passed with launch_fluent + product_version = argvals.get("product_version") if product_version: return FluentVersion(product_version) + # If fluent_path is provided, we cannot determine the Fluent version, so returning None. + if argvals.get("fluent_path"): + return None + # (DEV) if "PYFLUENT_FLUENT_ROOT" environment variable is defined, we cannot # determine the Fluent version, so returning None. if os.getenv("PYFLUENT_FLUENT_ROOT"): diff --git a/src/ansys/fluent/core/launcher/standalone_launcher.py b/src/ansys/fluent/core/launcher/standalone_launcher.py index 804d5732133..edf5bb062e6 100644 --- a/src/ansys/fluent/core/launcher/standalone_launcher.py +++ b/src/ansys/fluent/core/launcher/standalone_launcher.py @@ -189,9 +189,7 @@ def __init__( self.argvals["start_timeout"] = 60 if self.argvals["lightweight_mode"] is None: self.argvals["lightweight_mode"] = False - fluent_version = _get_standalone_launch_fluent_version( - self.argvals["product_version"] - ) + fluent_version = _get_standalone_launch_fluent_version(self.argvals) if fluent_version: _raise_non_gui_exception_in_windows(self.argvals["ui_mode"], fluent_version) From 8652b7134349e3d9c132b7c9fceb8403fb2bcf8f Mon Sep 17 00:00:00 2001 From: Mainak Kundu Date: Tue, 17 Dec 2024 16:29:25 -0500 Subject: [PATCH 8/8] fix: test --- tests/test_launcher.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_launcher.py b/tests/test_launcher.py index b8b8c1633c8..31e34c774cb 100644 --- a/tests/test_launcher.py +++ b/tests/test_launcher.py @@ -505,12 +505,12 @@ def test_standalone_launcher_dry_run(monkeypatch): monkeypatch.setenv("PYFLUENT_LAUNCH_CONTAINER", "0") fluent_path = r"\x\y\z\fluent.exe" fluent_launch_string, server_info_file_name = pyfluent.launch_fluent( - fluent_path=fluent_path, dry_run=True + fluent_path=fluent_path, dry_run=True, ui_mode="no_gui" ) assert str(Path(server_info_file_name).parent) == tempfile.gettempdir() assert ( fluent_launch_string - == f"{fluent_path} 3ddp -hidden -sifile={server_info_file_name} -nm" + == f"{fluent_path} 3ddp -gu -sifile={server_info_file_name} -nm" ) @@ -520,10 +520,10 @@ def test_standalone_launcher_dry_run_with_server_info_dir(monkeypatch): monkeypatch.setenv("SERVER_INFO_DIR", tmp_dir) fluent_path = r"\x\y\z\fluent.exe" fluent_launch_string, server_info_file_name = pyfluent.launch_fluent( - fluent_path=fluent_path, dry_run=True + fluent_path=fluent_path, dry_run=True, ui_mode="no_gui" ) assert str(Path(server_info_file_name).parent) == tmp_dir assert ( fluent_launch_string - == f"{fluent_path} 3ddp -hidden -sifile={Path(server_info_file_name).name} -nm" + == f"{fluent_path} 3ddp -gu -sifile={Path(server_info_file_name).name} -nm" )