Skip to content

Commit

Permalink
feat: Return launch and connection info from standalone launcher dry-…
Browse files Browse the repository at this point in the history
…run (#3576)

* feat: Return launch and connection info from standalone launcher dry-run

* feat: use fluent_path verbatim

* feat: Add tests

* feat: Return launch and connection info from standalone launcher dry-run

* feat: doc

* fix: tests

* fix: test

* fix: test
  • Loading branch information
mkundu1 authored Dec 18, 2024
1 parent a42a081 commit edd9adc
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 25 deletions.
5 changes: 3 additions & 2 deletions src/ansys/fluent/core/launcher/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 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
exited, or the ``exit()`` method is called on the session instance,
Expand Down
14 changes: 8 additions & 6 deletions src/ansys/fluent/core/launcher/process_launch_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand All @@ -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()))
9 changes: 6 additions & 3 deletions src/ansys/fluent/core/launcher/pyfluent_enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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"):
Expand Down
27 changes: 25 additions & 2 deletions src/ansys/fluent/core/launcher/server_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -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_names(use_tmpdir=True) -> tuple[str, str]:
"""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
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)
Expand All @@ -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(
Expand Down
9 changes: 6 additions & 3 deletions src/ansys/fluent/core/launcher/slurm_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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_names(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
Expand Down
20 changes: 11 additions & 9 deletions src/ansys/fluent/core/launcher/standalone_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 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
Expand Down Expand Up @@ -188,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)

Expand All @@ -200,10 +199,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_names()
)
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
Expand Down Expand Up @@ -232,7 +234,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}")

Expand Down
29 changes: 29 additions & 0 deletions tests/test_launcher.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
from pathlib import Path
import platform
import tempfile
from tempfile import TemporaryDirectory

import pytest
Expand Down Expand Up @@ -498,3 +499,31 @@ 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(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, ui_mode="no_gui"
)
assert str(Path(server_info_file_name).parent) == tempfile.gettempdir()
assert (
fluent_launch_string
== f"{fluent_path} 3ddp -gu -sifile={server_info_file_name} -nm"
)


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"
fluent_launch_string, server_info_file_name = pyfluent.launch_fluent(
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 -gu -sifile={Path(server_info_file_name).name} -nm"
)

0 comments on commit edd9adc

Please sign in to comment.