diff --git a/README.md b/README.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pyproject.toml b/pyproject.toml index 5693a0b832cc..61c2ee8033fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ dependencies = [ "pandas>=1.1.0,<3.0.0", "pyansys-tools-report>=0.8.1", "pyyaml>=6.0", + "typing-extensions>=4.12" ] dynamic = ["version"] @@ -233,3 +234,8 @@ skips = [ "B604", "B607", ] + +[tool.basedpyright] +reportUnknownMemberType = false +reportExplicitAny = false +reportPrivateUsage = false \ No newline at end of file diff --git a/src/ansys/fluent/core/_types.py b/src/ansys/fluent/core/_types.py index 639a5018146b..070e7b552779 100644 --- a/src/ansys/fluent/core/_types.py +++ b/src/ansys/fluent/core/_types.py @@ -31,5 +31,5 @@ import os from typing import TypeAlias -PathType: TypeAlias = "os.PathLike[str] | os.PathLike[bytes] | str | bytes" +PathType: TypeAlias = "os.PathLike[str] | str" """Type alias for file system paths.""" diff --git a/src/ansys/fluent/core/launcher/container_launcher.py b/src/ansys/fluent/core/launcher/container_launcher.py index 864a8bb84e3b..8d30247fa842 100644 --- a/src/ansys/fluent/core/launcher/container_launcher.py +++ b/src/ansys/fluent/core/launcher/container_launcher.py @@ -35,11 +35,12 @@ >>> container_solver_session = container_solver_launcher() """ -import inspect import logging import os import time -from typing import Any +from typing import Any, TypedDict + +from typing_extensions import Unpack from ansys.fluent.core.fluent_connection import FluentConnection from ansys.fluent.core.launcher.fluent_container import ( @@ -64,6 +65,37 @@ from ansys.fluent.core.session import _parse_server_info_file from ansys.fluent.core.utils.fluent_version import FluentVersion + +class ContainerArgsWithoutDryRun( + TypedDict, total=False +): # pylint: disable=missing-class-docstring + ui_mode: UIMode | str | None + graphics_driver: ( + FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str | None + ) + product_version: FluentVersion | str | float | int | None + dimension: Dimension | int | None + precision: Precision | str | None + processor_count: int | None + start_timeout: int + additional_arguments: str + container_dict: dict[str, Any] | None + cleanup_on_exit: bool + start_transcript: bool + py: bool | None + gpu: bool | None + start_watchdog: bool | None + file_transfer_service: Any | None + use_docker_compose: bool | None + use_podman_compose: bool | None + + +class ContainerArgs( + ContainerArgsWithoutDryRun, total=False +): # pylint: disable=missing-class-docstring + dry_run: bool + + _THIS_DIR = os.path.dirname(__file__) _OPTIONS_FILE = os.path.join(_THIS_DIR, "fluent_launcher_options.json") logger = logging.getLogger("pyfluent.launcher") @@ -89,27 +121,8 @@ class DockerLauncher: def __init__( self, - mode: FluentMode | str | None = None, - ui_mode: UIMode | str | None = None, - graphics_driver: ( - FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str | None - ) = None, - product_version: FluentVersion | str | float | int | None = None, - dimension: Dimension | int | None = None, - precision: Precision | str | None = None, - processor_count: int | None = None, - start_timeout: int = 60, - additional_arguments: str = "", - container_dict: dict | None = None, - dry_run: bool = False, - cleanup_on_exit: bool = True, - start_transcript: bool = True, - py: bool | None = None, - gpu: bool | None = None, - start_watchdog: bool | None = None, - file_transfer_service: Any | None = None, - use_docker_compose: bool | None = None, - use_podman_compose: bool | None = None, + mode: FluentMode | str, + **kwargs: Unpack[ContainerArgs], ): """ Launch a Fluent session in container mode. @@ -184,20 +197,17 @@ def __init__( In job scheduler environments (e.g., SLURM, LSF, PBS), resources and compute nodes are allocated, and core counts are queried from these environments before being passed to Fluent. """ - locals_ = locals().copy() - argvals = { - arg: locals_.get(arg) - for arg in inspect.getargvalues(inspect.currentframe()).args - } - self.argvals, self.new_session = _get_argvals_and_session(argvals) - if self.argvals["start_timeout"] is None: + self.argvals, self.new_session = _get_argvals_and_session( + {**kwargs, mode: mode} + ) + if self.argvals.get("start_timeout") is None: self.argvals["start_timeout"] = 60 - self.file_transfer_service = file_transfer_service + self.file_transfer_service = kwargs.get("file_transfer_service") if self.argvals["mode"] == FluentMode.SOLVER_ICING: self.argvals["fluent_icing"] = True - if self.argvals["container_dict"] is None: + if self.argvals.get("container_dict") is None: self.argvals["container_dict"] = {} - if self.argvals["product_version"]: + if "product_version" in self.argvals: self.argvals["container_dict"][ "image_tag" ] = f"v{FluentVersion(self.argvals['product_version']).value}" @@ -205,10 +215,12 @@ def __init__( self._args = _build_fluent_launch_args_string(**self.argvals).split() if FluentMode.is_meshing(self.argvals["mode"]): self._args.append(" -meshing") + + use_docker_compose = kwargs.get("use_docker_compose") + use_podman_compose = kwargs.get("use_podman_compose") self._compose_config = ComposeConfig(use_docker_compose, use_podman_compose) def __call__(self): - if self.argvals["dry_run"]: config_dict, *_ = configure_container_dict( self._args, diff --git a/src/ansys/fluent/core/launcher/launcher.py b/src/ansys/fluent/core/launcher/launcher.py index ecb8d8555194..0c5b71cc78c9 100644 --- a/src/ansys/fluent/core/launcher/launcher.py +++ b/src/ansys/fluent/core/launcher/launcher.py @@ -29,7 +29,9 @@ import inspect import logging import os -from typing import Any, Dict +from typing import Any, Literal, TypedDict, overload + +from typing_extensions import Required, Unpack import ansys.fluent.core as pyfluent from ansys.fluent.core._types import PathType @@ -58,6 +60,7 @@ 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 +from ansys.fluent.core.session_solver_aero import SolverAero from ansys.fluent.core.session_solver_icing import SolverIcing from ansys.fluent.core.utils.deprecate import deprecate_arguments from ansys.fluent.core.utils.fluent_version import FluentVersion @@ -67,7 +70,7 @@ logger = logging.getLogger("pyfluent.launcher") -def create_launcher(fluent_launch_mode: LaunchMode = None, **kwargs): +def create_launcher(fluent_launch_mode: LaunchMode, **kwargs): """Use the factory function to create a launcher for supported launch modes. Parameters @@ -83,7 +86,7 @@ def create_launcher(fluent_launch_mode: LaunchMode = None, **kwargs): Session launcher. Raises ------ - DisallowedValuesError + ValueError If an unknown Fluent launch mode is passed. """ if fluent_launch_mode == LaunchMode.STANDALONE: @@ -94,6 +97,7 @@ def create_launcher(fluent_launch_mode: LaunchMode = None, **kwargs): return PIMLauncher(**kwargs) elif fluent_launch_mode == LaunchMode.SLURM: return SlurmLauncher(**kwargs) + raise ValueError(f"launch mode invalid: {fluent_launch_mode!r}") def _show_gui_to_ui_mode(old_arg_val, **kwds): @@ -126,6 +130,110 @@ def _version_to_dimension(old_arg_val): return None +class LaunchFluentArgs( + TypedDict, total=False +): # pylint: disable=missing-class-docstring + product_version: FluentVersion | str | float | int | None + dimension: Dimension | int + precision: Precision | str + processor_count: int | None + journal_file_names: None | str | list[str] + start_timeout: int + additional_arguments: str + env: dict[str, Any] | None + start_container: bool | None + container_dict: dict[str, Any] | None + cleanup_on_exit: bool + start_transcript: bool + ui_mode: UIMode | str | None + graphics_driver: ( + FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str | None + ) + case_file_name: str | None + case_data_file_name: str | None + lightweight_mode: bool | None + py: bool | None + gpu: bool | list[int] | None + cwd: str | None + fluent_path: str | None + topy: str | list | None + start_watchdog: bool | None + file_transfer_service: Any | None + use_docker_compose: bool + use_podman_compose: bool + + +class SlurmSchedulerOptions( + TypedDict, total=False +): # pylint: disable=missing-class-docstring + scheduler: Required[Literal["slurm"]] + scheduler_headnode: str + scheduler_queue: str + scheduler_account: str + + +@overload +def launch_fluent( + *, + dry_run: Literal[False] = False, + mode: Literal[FluentMode.MESHING, "meshing"], + **kwargs: Unpack[LaunchFluentArgs], +) -> Meshing: ... + + +@overload +def launch_fluent( + *, + dry_run: Literal[False] = False, + mode: Literal[FluentMode.PURE_MESHING, "pure_meshing"], + **kwargs: Unpack[LaunchFluentArgs], +) -> PureMeshing: ... + + +@overload +def launch_fluent( + *, + dry_run: Literal[False] = False, + mode: Literal[FluentMode.SOLVER, "solver"] = FluentMode.SOLVER, + **kwargs: Unpack[LaunchFluentArgs], +) -> Solver: ... + + +@overload +def launch_fluent( + *, + dry_run: Literal[False] = False, + mode: Literal[FluentMode.SOLVER_ICING, "solver_icing"], + **kwargs: Unpack[LaunchFluentArgs], +) -> SolverIcing: ... + + +@overload +def launch_fluent( + *, + dry_run: Literal[False] = False, + mode: Literal[FluentMode.SOLVER_AERO, "solver_aero"] = ..., + **kwargs: Unpack[LaunchFluentArgs], +) -> SolverAero: ... + + +@overload +def launch_fluent( + *, + dry_run: Literal[False] = False, + scheduler_options: SlurmSchedulerOptions, + mode: FluentMode | str = FluentMode.SOLVER, + **kwargs: Unpack[LaunchFluentArgs], +) -> SlurmFuture: ... + + +@overload +def launch_fluent( + *, + dry_run: Literal[True], + **kwargs: Unpack[LaunchFluentArgs], +) -> dict[str, Any]: ... + def _custom_converter_gui(kwargs): old_val = kwargs.pop("show_gui", None) kwargs["ui_mode"] = _show_gui_to_ui_mode(old_val, **kwargs) @@ -151,14 +259,15 @@ def _custom_converter_dimension(kwargs): converter=_custom_converter_dimension, ) def launch_fluent( + *, product_version: FluentVersion | str | float | int | None = None, - dimension: Dimension | int | None = None, - precision: Precision | str | None = None, + dimension: Dimension | int = Dimension.THREE, + precision: Precision | str = Precision.DOUBLE, processor_count: int | None = None, journal_file_names: None | str | list[str] = None, - start_timeout: int = None, + start_timeout: int | None = None, additional_arguments: str = "", - env: Dict[str, Any] | None = None, + env: dict[str, Any] | None = None, start_container: bool | None = None, container_dict: dict | None = None, dry_run: bool = False, @@ -171,18 +280,26 @@ def launch_fluent( case_file_name: "PathType | None" = None, case_data_file_name: "PathType | None" = None, lightweight_mode: bool | None = None, - mode: FluentMode | str | None = None, + mode: FluentMode | str = FluentMode.SOLVER, py: bool | None = None, gpu: bool | list[int] | None = None, cwd: "PathType | None" = None, fluent_path: "PathType | None" = None, topy: str | list | None = None, start_watchdog: bool | None = None, - scheduler_options: dict | None = None, + scheduler_options: SlurmSchedulerOptions | None = None, file_transfer_service: Any | None = None, - use_docker_compose: bool | None = None, - use_podman_compose: bool | None = None, -) -> Meshing | PureMeshing | Solver | SolverIcing | SlurmFuture | dict: + use_docker_compose: bool = False, + use_podman_compose: bool = False, +) -> ( + Meshing + | PureMeshing + | Solver + | SolverIcing + | SolverAero + | SlurmFuture + | dict[Any, Any] +): """Launch Fluent locally in server mode or connect to a running Fluent server instance. @@ -203,8 +320,7 @@ def launch_fluent( in which case ``Dimension.THREE`` is used. Options are either the values of the ``Dimension`` enum (``Dimension.TWO`` or ``Dimension.THREE``) or any of ``2`` and ``3``. precision : Precision or str, optional - Floating point precision. The default is ``None``, in which case ``Precision.DOUBLE`` - is used. Options are either the values of the ``Precision`` enum (``Precision.SINGLE`` + Floating point precision. Options are either the values of the ``Precision`` enum (``Precision.SINGLE`` or ``Precision.DOUBLE``) or any of ``"double"`` and ``"single"``. processor_count : int, optional Number of processors. The default is ``None``, in which case ``1`` @@ -269,7 +385,7 @@ def launch_fluent( This parameter is used only when ``case_file_name`` is provided. The default is ``False``. mode : FluentMode or str or None, optional Launch mode of Fluent to point to a specific session type. Can be a - ``FluentMode`` enum member or a string. The default value is ``None``. + ``FluentMode`` enum member or a string. The default value is ``SOLVER``. Valid string options include ``"meshing"``, ``"pure-meshing"``, and ``"solver"``. py : bool, optional @@ -371,6 +487,7 @@ def _mode_to_launcher_type(fluent_launch_mode: LaunchMode): def connect_to_fluent( + *, ip: str | None = None, port: int | None = None, cleanup_on_exit: bool = False, @@ -379,7 +496,7 @@ def connect_to_fluent( password: str | None = None, start_watchdog: bool | None = None, file_transfer_service: Any | None = None, -) -> Meshing | PureMeshing | Solver | SolverIcing: +) -> Meshing | PureMeshing | Solver | SolverIcing | SolverAero: """Connect to an existing Fluent server instance. Parameters diff --git a/src/ansys/fluent/core/launcher/pim_launcher.py b/src/ansys/fluent/core/launcher/pim_launcher.py index a519d18ba2a9..01cb20e67f3d 100644 --- a/src/ansys/fluent/core/launcher/pim_launcher.py +++ b/src/ansys/fluent/core/launcher/pim_launcher.py @@ -35,12 +35,13 @@ >>> pim_solver_session = pim_solver_launcher() """ -import inspect import logging import os import tempfile import time -from typing import Any, Dict +from typing import Any, Dict, TypedDict + +from typing_extensions import Unpack from ansys.fluent.core.fluent_connection import FluentConnection, _get_max_c_int_limit from ansys.fluent.core.launcher.launch_options import ( @@ -61,6 +62,37 @@ from ansys.fluent.core.utils.fluent_version import FluentVersion import ansys.platform.instancemanagement as pypim + +class PIMArgsWithoutDryRun( + TypedDict, total=False +): # pylint: disable=missing-class-docstring + ui_mode: UIMode | str | None + graphics_driver: ( + FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str | None + ) + product_version: FluentVersion | str | float | int | None + dimension: Dimension | int | None + precision: Precision | str | None + processor_count: int | None + start_timeout: int + additional_arguments: str + cleanup_on_exit: bool + start_transcript: bool + gpu: bool | None + start_watchdog: bool | None + file_transfer_service: Any | None + + +class PIMArgs( + PIMArgsWithoutDryRun, total=False +): # pylint: disable=missing-class-docstring + dry_run: bool + + +class PIMArgsWithMode(PIMArgs, total=False): # pylint: disable=missing-class-docstring + mode: FluentMode | str | None + + _THIS_DIR = os.path.dirname(__file__) _OPTIONS_FILE = os.path.join(_THIS_DIR, "fluent_launcher_options.json") logger = logging.getLogger("pyfluent.launcher") @@ -71,23 +103,7 @@ class PIMLauncher: def __init__( self, - mode: FluentMode | str | None = None, - ui_mode: UIMode | str | None = None, - graphics_driver: ( - FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str | None - ) = None, - product_version: FluentVersion | str | float | int | None = None, - dimension: Dimension | int | None = None, - precision: Precision | str | None = None, - processor_count: int | None = None, - start_timeout: int = 60, - additional_arguments: str = "", - cleanup_on_exit: bool = True, - dry_run: bool | None = None, - start_transcript: bool = True, - gpu: bool | None = None, - start_watchdog: bool | None = None, - file_transfer_service: Any | None = None, + **kwargs: Unpack[PIMArgsWithMode], ): """ Launch a Fluent session in `PIM `_ mode. @@ -152,6 +168,9 @@ def __init__( In job scheduler environments (e.g., SLURM, LSF, PBS), resources and compute nodes are allocated, and core counts are queried from these environments before being passed to Fluent. """ + additional_arguments = kwargs.get("additional_arguments", "") + start_watchdog = kwargs.get("start_watchdog") + file_transfer_service = kwargs.get("file_transfer_service") if additional_arguments: logger.warning( @@ -164,14 +183,9 @@ def __init__( "'start_watchdog' argument for 'launch_fluent()' method is not supported " "when starting a remote Fluent PyPIM client." ) - locals_ = locals().copy() - argvals = { - arg: locals_.get(arg) - for arg in inspect.getargvalues(inspect.currentframe()).args - } - self.argvals, self.new_session = _get_argvals_and_session(argvals) + self.argvals, self.new_session = _get_argvals_and_session(kwargs) self.file_transfer_service = file_transfer_service - if self.argvals["start_timeout"] is None: + if self.argvals.get("start_timeout") is None: self.argvals["start_timeout"] = 60 def __call__(self): diff --git a/src/ansys/fluent/core/launcher/slurm_launcher.py b/src/ansys/fluent/core/launcher/slurm_launcher.py index 08a7683a8cf6..b7c46b1ca1f3 100644 --- a/src/ansys/fluent/core/launcher/slurm_launcher.py +++ b/src/ansys/fluent/core/launcher/slurm_launcher.py @@ -68,7 +68,9 @@ import shutil import subprocess import time -from typing import Any, Callable, Dict +from typing import Any, Callable, Dict, Generic + +from typing_extensions import TypeVar from ansys.fluent.core._types import PathType from ansys.fluent.core.exceptions import InvalidArgument @@ -162,7 +164,14 @@ def cancel(job_id: int) -> None: subprocess.run(["scancel", f"{job_id}"]) -class SlurmFuture: +SessionT = TypeVar( + "SessionT", + bound="Meshing | PureMeshing | Solver | SolverIcing", + default="Meshing | PureMeshing | Solver | SolverIcing", +) + + +class SlurmFuture(Generic[SessionT]): """Encapsulates asynchronous launch of Fluent within a Slurm environment. The interface is similar to Python's @@ -222,9 +231,7 @@ def done(self) -> bool: finished running, otherwise ``False``.""" return self._get_state() in ["", "CANCELLED", "COMPLETED"] - def result( - self, timeout: int = None - ) -> Meshing | PureMeshing | Solver | SolverIcing: + def result(self, timeout: int | None = None) -> SessionT: """Return the session instance corresponding to the Fluent launch. If Fluent hasn't yet launched, then this method will wait up to timeout seconds. If Fluent hasn't launched in timeout seconds, then a TimeoutError will be raised. If @@ -247,7 +254,7 @@ def result( """ return self._future.result(timeout) - def exception(self, timeout: int = None) -> Exception: + def exception(self, timeout: int | None = None) -> Exception: """Return the exception raised by the Fluent launch. If Fluent hasn't yet launched, then this method will wait up to timeout seconds. If Fluent hasn't launched in timeout seconds, then a TimeoutError will be raised. If timeout is diff --git a/src/ansys/fluent/core/launcher/standalone_launcher.py b/src/ansys/fluent/core/launcher/standalone_launcher.py index 1c52ab562569..7d0b2e5981bc 100644 --- a/src/ansys/fluent/core/launcher/standalone_launcher.py +++ b/src/ansys/fluent/core/launcher/standalone_launcher.py @@ -35,12 +35,13 @@ >>> standalone_solver_session = standalone_solver_launcher() """ -import inspect import logging import os from pathlib import Path import subprocess -from typing import Any, Dict +from typing import Any, TypedDict + +from typing_extensions import Unpack from ansys.fluent.core._types import PathType from ansys.fluent.core.launcher.error_handler import ( @@ -57,6 +58,7 @@ _get_argvals_and_session, _get_standalone_launch_fluent_version, ) +from ansys.fluent.core.launcher.launcher import LaunchFluentArgs from ansys.fluent.core.launcher.launcher_utils import ( _await_fluent_launch, _build_journal_argument, @@ -70,8 +72,45 @@ _get_server_info_file_names, ) import ansys.fluent.core.launcher.watchdog as watchdog +from ansys.fluent.core.session import BaseSession from ansys.fluent.core.utils.fluent_version import FluentVersion + +class StandaloneArgsWithoutDryRun( + TypedDict, total=False +): # pylint: disable=missing-class-docstring + product_version: FluentVersion | str | float | int | None + dimension: Dimension | int + precision: Precision | str + processor_count: int | None + journal_file_names: None | str | list[str] + start_timeout: int + additional_arguments: str + env: dict[str, Any] | None + cleanup_on_exit: bool + start_transcript: bool + ui_mode: UIMode | str | None + graphics_driver: ( + FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str | None + ) + case_file_name: str | None + case_data_file_name: str | None + lightweight_mode: bool | None + py: bool | None + gpu: bool | list[int] | None + cwd: str | None + fluent_path: str | None + topy: str | list[Any] | None + start_watchdog: bool | None + file_transfer_service: Any | None + + +class StandaloneArgs( + StandaloneArgsWithoutDryRun, total=False +): # pylint: disable=missing-class-docstring + dry_run: bool | None + + logger = logging.getLogger("pyfluent.launcher") @@ -80,32 +119,10 @@ class StandaloneLauncher: def __init__( self, - mode: FluentMode | str | None = None, - ui_mode: UIMode | str | None = None, - graphics_driver: ( - FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str - ) = None, - product_version: FluentVersion | str | float | int | None = None, - dimension: Dimension | int | None = None, - precision: Precision | str | None = None, - processor_count: int | None = None, - journal_file_names: None | str | list[str] = None, - start_timeout: int = 60, - additional_arguments: str = "", - env: Dict[str, Any] | None = None, - cleanup_on_exit: bool = True, + *, + mode: FluentMode, dry_run: bool = False, - start_transcript: bool = True, - case_file_name: "PathType | None" = None, - case_data_file_name: "PathType | None" = None, - lightweight_mode: bool | None = None, - py: bool | None = None, - gpu: bool | None = None, - cwd: "PathType | None" = None, - fluent_path: "PathType | None" = None, - topy: str | list | None = None, - start_watchdog: bool | None = None, - file_transfer_service: Any | None = None, + **kwargs: Unpack[LaunchFluentArgs], ): """ Launch a Fluent session in standalone mode. @@ -188,19 +205,14 @@ def __init__( """ import ansys.fluent.core as pyfluent - locals_ = locals().copy() - argvals = { - arg: locals_.get(arg) - for arg in inspect.getargvalues(inspect.currentframe()).args - } - self.argvals, self.new_session = _get_argvals_and_session(argvals) - self.file_transfer_service = file_transfer_service + self.argvals, self.new_session = _get_argvals_and_session(kwargs) + self.file_transfer_service = kwargs.get("file_transfer_service") if pyfluent.config.show_fluent_gui: - ui_mode = UIMode.GUI - self.argvals["ui_mode"] = UIMode(ui_mode) - if self.argvals["start_timeout"] is None: + kwargs["ui_mode"] = UIMode.GUI + self.argvals["ui_mode"] = UIMode(kwargs.get("ui_mode")) + if self.argvals.get("start_timeout") is None: self.argvals["start_timeout"] = 60 - if self.argvals["lightweight_mode"] is None: + if self.argvals.get("lightweight_mode") is None: self.argvals["lightweight_mode"] = False fluent_version = _get_standalone_launch_fluent_version(self.argvals) if fluent_version: @@ -209,7 +221,7 @@ def __init__( if ( fluent_version and fluent_version >= FluentVersion.v251 - and self.argvals["py"] is None + and self.argvals.get("py") is None ): self.argvals["py"] = True @@ -227,12 +239,12 @@ def __init__( self._sifile_last_mtime = Path(self._server_info_file_name).stat().st_mtime self._kwargs = _get_subprocess_kwargs_for_fluent( - self.argvals["env"], self.argvals + self.argvals.get("env", {}), self.argvals ) - if self.argvals["cwd"]: - self._kwargs.update(cwd=self.argvals["cwd"]) + if "cwd" in self.argvals: + self._kwargs.update(cwd=self.argvals.get("cwd")) self._launch_string += _build_journal_argument( - self.argvals["topy"], self.argvals["journal_file_names"] + self.argvals.get("topy", []), self.argvals.get("journal_file_names") ) if is_windows(): @@ -245,14 +257,14 @@ def __init__( # Using 'start.exe' is better; otherwise Fluent is more susceptible to bad termination attempts. self._launch_cmd = 'start "" ' + self._launch_string else: - if self.argvals["ui_mode"] not in [UIMode.GUI, UIMode.HIDDEN_GUI]: + if self.argvals.get("ui_mode") not in [UIMode.GUI, UIMode.HIDDEN_GUI]: # Using nohup to hide Fluent output from the current terminal self._launch_cmd = "nohup " + self._launch_string + " &" else: self._launch_cmd = self._launch_string - def __call__(self): - if self.argvals["dry_run"]: + def __call__(self) -> tuple[str, str] | BaseSession: + if self.argvals.get("dry_run"): print(f"Fluent launch string: {self._launch_string}") return self._launch_string, self._server_info_file_name try: @@ -263,7 +275,7 @@ def __call__(self): try: _await_fluent_launch( self._server_info_file_name, - self.argvals["start_timeout"], + self.argvals.get("start_timeout", 60), self._sifile_last_mtime, ) except TimeoutError as ex: @@ -277,7 +289,7 @@ def __call__(self): process = subprocess.Popen(launch_cmd, **self._kwargs) _await_fluent_launch( self._server_info_file_name, - self.argvals["start_timeout"], + self.argvals.get("start_timeout", 60), self._sifile_last_mtime, ) else: @@ -286,36 +298,36 @@ def __call__(self): session = self.new_session._create_from_server_info_file( server_info_file_name=self._server_info_file_name, file_transfer_service=self.file_transfer_service, - cleanup_on_exit=self.argvals["cleanup_on_exit"], - start_transcript=self.argvals["start_transcript"], + cleanup_on_exit=self.argvals.get("cleanup_on_exit"), + start_transcript=self.argvals.get("start_transcript"), launcher_args=self.argvals, inside_container=False, ) session._process = process start_watchdog = _confirm_watchdog_start( - self.argvals["start_watchdog"], - self.argvals["cleanup_on_exit"], + self.argvals.get("start_watchdog"), + self.argvals.get("cleanup_on_exit"), session._fluent_connection, ) if start_watchdog: logger.info("Launching Watchdog for local Fluent client...") ip, port, password = _get_server_info(self._server_info_file_name) watchdog.launch(os.getpid(), port, password, ip) - if self.argvals["case_file_name"]: - if FluentMode.is_meshing(self.argvals["mode"]): - session.tui.file.read_case(self.argvals["case_file_name"]) - elif self.argvals["lightweight_mode"]: - session.read_case_lightweight(self.argvals["case_file_name"]) + if self.argvals.get("case_file_name"): + if FluentMode.is_meshing(self.argvals.get("mode")): + session.tui.file.read_case(self.argvals.get("case_file_name")) + elif self.argvals.get("lightweight_mode"): + session.read_case_lightweight(self.argvals.get("case_file_name")) else: session.file.read( file_type="case", - file_name=self.argvals["case_file_name"], + file_name=self.argvals.get("case_file_name"), ) - if self.argvals["case_data_file_name"]: - if not FluentMode.is_meshing(self.argvals["mode"]): + if self.argvals.get("case_data_file_name"): + if not FluentMode.is_meshing(self.argvals.get("mode")): session.file.read( file_type="case-data", - file_name=self.argvals["case_data_file_name"], + file_name=self.argvals.get("case_data_file_name"), ) else: raise RuntimeError( diff --git a/src/ansys/fluent/core/session_meshing.py b/src/ansys/fluent/core/session_meshing.py index 6287689b6b28..cf481abef422 100644 --- a/src/ansys/fluent/core/session_meshing.py +++ b/src/ansys/fluent/core/session_meshing.py @@ -22,7 +22,7 @@ """Module containing class encapsulating Fluent connection.""" -from typing import Any, Dict +from typing import TYPE_CHECKING, Any, Dict from ansys.fluent.core.fluent_connection import FluentConnection from ansys.fluent.core.services import SchemeEval @@ -94,59 +94,20 @@ def switch_to_solver(self) -> Any: ) return solver_session - def __getattribute__(self, item: str): - try: - _connection = super(Meshing, self).__getattribute__("_fluent_connection") - except AttributeError: - _connection = False - if _connection is None and item not in [ - "is_active", - "_fluent_connection", - "_fluent_connection_backup", - "wait_process_finished", - ]: - raise AttributeError( - f"'{__class__.__name__}' object has no attribute '{item}'" - ) - - return super(Meshing, self).__getattribute__(item) - - @property - def tui(self): - """Meshing TUI root.""" - return super(Meshing, self).tui - - @property - def meshing(self): - """Meshing datamodel root.""" - return super(Meshing, self).meshing - - @property - def meshing_utilities(self): - """Meshing utilities datamodel root.""" - return super(Meshing, self).meshing_utilities - - @property - def workflow(self): - """Workflow datamodel root.""" - return super(Meshing, self).workflow - - @property - def meshing_workflow(self): - """Full API to meshing and meshing_workflow.""" - return super(Meshing, self).meshing_workflow - - @property - def PartManagement(self): - """Part management datamodel root.""" - return super(Meshing, self).PartManagement - - @property - def PMFileManagement(self): - """Part management file management datamodel root.""" - return super(Meshing, self).PMFileManagement - - @property - def preferences(self): - """Preferences datamodel root.""" - return super(Meshing, self).preferences + if not TYPE_CHECKING: + def __getattribute__(self, item: str): + try: + _connection = super().__getattribute__("_fluent_connection") + except AttributeError: + _connection = False + if _connection is None and item not in [ + "is_active", + "_fluent_connection", + "_fluent_connection_backup", + "wait_process_finished", + ]: + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + + return super().__getattribute__(item) diff --git a/src/ansys/fluent/core/session_meshing.pyi b/src/ansys/fluent/core/session_meshing.pyi deleted file mode 100644 index 048bdf335c5d..000000000000 --- a/src/ansys/fluent/core/session_meshing.pyi +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates. -# SPDX-License-Identifier: MIT -# -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from ansys.fluent.core.generated.datamodel_252.meshing import Root as meshing_root -from ansys.fluent.core.generated.datamodel_252.meshing_utilities import ( - Root as meshing_utilities_root, -) -from ansys.fluent.core.generated.datamodel_252.part_management import ( - Root as partmanagement_root, -) -from ansys.fluent.core.generated.datamodel_252.pm_file_management import ( - Root as pmfilemanagement_root, -) -from ansys.fluent.core.generated.datamodel_252.preferences import ( - Root as preferences_root, -) -from ansys.fluent.core.generated.datamodel_252.workflow import Root as workflow_root -from ansys.fluent.core.generated.meshing.tui_252 import main_menu - -class Meshing: - @property - def tui(self) -> main_menu: ... - @property - def meshing(self) -> meshing_root: ... - @property - def meshing_utilities(self) -> meshing_utilities_root: ... - @property - def workflow(self) -> workflow_root: ... - @property - def PartManagement(self) -> partmanagement_root: ... - @property - def PMFileManagement(self) -> pmfilemanagement_root: ... - @property - def preferences(self) -> preferences_root: ... diff --git a/src/ansys/fluent/core/session_pure_meshing.py b/src/ansys/fluent/core/session_pure_meshing.py index 5ad58a9c2f6f..b7cf282a90c0 100644 --- a/src/ansys/fluent/core/session_pure_meshing.py +++ b/src/ansys/fluent/core/session_pure_meshing.py @@ -24,7 +24,7 @@ import functools import os -from typing import Any, Dict +from typing import TYPE_CHECKING, Any, Dict, cast import ansys.fluent.core as pyfluent from ansys.fluent.core._types import PathType @@ -39,6 +39,24 @@ from ansys.fluent.core.utils.data_transfer import transfer_case from ansys.fluent.core.utils.fluent_version import FluentVersion +if TYPE_CHECKING: + from ansys.fluent.core.generated.datamodel_252.meshing import Root as meshing_root + from ansys.fluent.core.generated.datamodel_252.meshing_utilities import ( + Root as meshing_utilities_root, + ) + from ansys.fluent.core.generated.datamodel_252.part_management import ( + Root as partmanagement_root, + ) + from ansys.fluent.core.generated.datamodel_252.pm_file_management import ( + Root as pmfilemanagement_root, + ) + from ansys.fluent.core.generated.datamodel_252.preferences import ( + Root as preferences_root, + ) + from ansys.fluent.core.generated.datamodel_252.workflow import Root as workflow_root + from ansys.fluent.core.generated.datamodel_252.meshing_workflow import Root as meshing_workflow_root + from ansys.fluent.core.generated.meshing.tui_252 import main_menu + class PureMeshing(BaseSession): """Encapsulates a Fluent meshing session. @@ -128,31 +146,32 @@ def __init__( self._fluent_connection.register_finalizer_cb(stream.stop) @property - def tui(self): + def tui(self) -> "main_menu": """Instance of ``main_menu`` on which Fluent's SolverTUI methods can be executed.""" - return self._base_meshing.tui + return cast("main_menu", self._base_meshing.tui) @property - def meshing(self): + def meshing(self) -> "meshing_root": """Datamodel root of meshing.""" - return self._base_meshing.meshing + return cast("meshing_root", self._base_meshing.meshing) @property - def meshing_utilities(self): + def meshing_utilities(self) -> "meshing_utilities_root | None": """Datamodel root of meshing_utilities.""" if self.get_fluent_version() >= FluentVersion.v242: - return self._base_meshing.meshing_utilities + return cast("meshing_utilities_root", self._base_meshing.meshing_utilities) + return None @property - def workflow(self): + def workflow(self) -> "workflow_root": """Datamodel root of workflow.""" - return self._base_meshing.workflow + return cast("workflow_root", self._base_meshing.workflow) @property def meshing_workflow(self): """Full API to meshing and meshing_workflow.""" - return self._base_meshing.meshing_workflow + return cast("meshing_workflow_root", self._base_meshing.meshing_workflow) def watertight(self): """Get a new watertight workflow.""" @@ -192,19 +211,19 @@ def topology_based(self): return self._base_meshing.topology_based_meshing_workflow() @property - def PartManagement(self): + def PartManagement(self) -> "partmanagement_root": """Datamodel root of PartManagement.""" - return self._base_meshing.PartManagement + return cast("partmanagement_root", self._base_meshing.PartManagement) @property - def PMFileManagement(self): + def PMFileManagement(self) -> "pmfilemanagement_root": """Datamodel root of PMFileManagement.""" - return self._base_meshing.PMFileManagement + return cast("pmfilemanagement_root", self._base_meshing.PMFileManagement) @property - def preferences(self): + def preferences(self) -> "preferences_root": """Datamodel root of preferences.""" - return self._base_meshing.preferences + return cast("preferences_root", self._base_meshing.preferences) def transfer_mesh_to_solvers( self, diff --git a/src/ansys/fluent/core/session_pure_meshing.pyi b/src/ansys/fluent/core/session_pure_meshing.pyi deleted file mode 100644 index 33e94fa92a4e..000000000000 --- a/src/ansys/fluent/core/session_pure_meshing.pyi +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates. -# SPDX-License-Identifier: MIT -# -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from ansys.fluent.core.generated.datamodel_252.meshing import Root as meshing_root -from ansys.fluent.core.generated.datamodel_252.meshing_utilities import ( - Root as meshing_utilities_root, -) -from ansys.fluent.core.generated.datamodel_252.part_management import ( - Root as partmanagement_root, -) -from ansys.fluent.core.generated.datamodel_252.pm_file_management import ( - Root as pmfilemanagement_root, -) -from ansys.fluent.core.generated.datamodel_252.preferences import ( - Root as preferences_root, -) -from ansys.fluent.core.generated.datamodel_252.workflow import Root as workflow_root -from ansys.fluent.core.generated.datamodel_261.meshing_workflow import ( - Root as meshing_workflow_root, -) -from ansys.fluent.core.generated.meshing.tui_252 import main_menu - -class PureMeshing: - @property - def tui(self) -> main_menu: ... - @property - def meshing(self) -> meshing_root: ... - @property - def meshing_utilities(self) -> meshing_utilities_root: ... - @property - def workflow(self) -> workflow_root: ... - @property - def meshing_workflow(self) -> meshing_workflow_root: ... - def watertight(self): ... - def fault_tolerant(self): ... - def two_dimensional_meshing(self): ... - def topology_based(self): ... - def load_workflow(self, file_path: str): ... - def create_workflow(self): ... - @property - def PartManagement(self) -> partmanagement_root: ... - @property - def PMFileManagement(self) -> pmfilemanagement_root: ... - @property - def preferences(self) -> preferences_root: ... - def transfer_mesh_to_solvers( - self, - solvers, - file_type: str = ..., - file_name_stem: str = ..., - num_files_to_try: int = ..., - clean_up_mesh_file: bool = ..., - overwrite_previous: bool = ..., - ): ... - def enable_beta_features(self): ... diff --git a/src/ansys/fluent/core/session_solver.py b/src/ansys/fluent/core/session_solver.py index 97869abd06f8..0f4a17aa8903 100644 --- a/src/ansys/fluent/core/session_solver.py +++ b/src/ansys/fluent/core/session_solver.py @@ -24,7 +24,7 @@ import logging import threading -from typing import Any, Dict +from typing import TYPE_CHECKING, Any, Dict, cast import warnings import weakref @@ -60,6 +60,14 @@ ) from ansys.fluent.core.workflow import ClassicWorkflow +if TYPE_CHECKING: + from ansys.fluent.core.generated.datamodel_252.preferences import ( + Root as preferences_root, + ) + import ansys.fluent.core.generated.solver.settings_252 as settings_root + from ansys.fluent.core.generated.solver.tui_252 import main_menu + + tui_logger = logging.getLogger("pyfluent.tui") datamodel_logger = logging.getLogger("pyfluent.datamodel") @@ -181,7 +189,7 @@ def _solution_variable_data(self) -> SolutionVariableData: ) @property - def settings(self): + def settings(self) -> "settings_root.root": """Settings root handle.""" if self._settings is None: #: Root settings object. @@ -192,7 +200,7 @@ def settings(self): file_transfer_service=self._file_transfer_service, scheme_eval=self.scheme.eval, ) - return self._settings + return cast("settings_root.root", self._settings) @property def svar_data(self): @@ -246,16 +254,16 @@ def _version(self): return self._fluent_version @property - def tui(self): + def tui(self) -> "main_menu": """Instance of ``main_menu`` on which Fluent's SolverTUI methods can be executed.""" if self._tui is None: self._tui = _make_tui_module(self, "solver") - return self._tui + return cast("main_menu", self._tui) @property - def workflow(self): + def workflow(self) -> ClassicWorkflow: """Datamodel root for workflow.""" if not self._workflow: self._workflow = ClassicWorkflow( @@ -277,18 +285,18 @@ def _interrupt(cls, command): command._root.solution.run_calculation.interrupt() @property - def system_coupling(self): + def system_coupling(self) -> SystemCoupling: """System coupling object.""" if self._system_coupling is None: self._system_coupling = SystemCoupling(self) return self._system_coupling @property - def preferences(self): + def preferences(self) -> "preferences_root": """Datamodel root of preferences.""" if self._preferences is None: self._preferences = _make_datamodel_module(self, "preferences") - return self._preferences + return cast("preferences_root", self._preferences) def _start_bg_session_and_sync(self, launcher_args): """Start a background session and sync it with the current session.""" diff --git a/src/ansys/fluent/core/session_solver.pyi b/src/ansys/fluent/core/session_solver.pyi deleted file mode 100644 index a834a1fd1611..000000000000 --- a/src/ansys/fluent/core/session_solver.pyi +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates. -# SPDX-License-Identifier: MIT -# -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from ansys.fluent.core.generated.datamodel_252.preferences import ( - Root as preferences_root, -) -from ansys.fluent.core.generated.datamodel_252.workflow import Root as workflow_root -import ansys.fluent.core.generated.solver.settings_252 as settings_root -from ansys.fluent.core.generated.solver.tui_252 import main_menu -from ansys.fluent.core.system_coupling import SystemCoupling - -class Solver: - @property - def version(self): ... - @property - def tui(self) -> main_menu: ... - @property - def workflow(self) -> workflow_root: ... - @property - def system_coupling(self) -> SystemCoupling: ... - @property - def preferences(self) -> preferences_root: ... - def read_case_lightweight(self, file_name: str): ... - def read_case(self, file_name: str): ... - def write_case(self, file_name: str): ... - @property - def settings(self) -> settings_root.root: ... - def enable_beta_features(self): ... diff --git a/src/ansys/fluent/core/session_utilities.py b/src/ansys/fluent/core/session_utilities.py index a97b1efbb94c..69042cfe8601 100644 --- a/src/ansys/fluent/core/session_utilities.py +++ b/src/ansys/fluent/core/session_utilities.py @@ -22,22 +22,35 @@ """Session utilities.""" -from typing import Any, Dict +from typing import TYPE_CHECKING, Any, Literal, overload -import ansys.fluent.core as pyfluent +from typing_extensions import Unpack + +from ansys.fluent.core import ( + session_meshing, + session_pure_meshing, + session_solver, + session_solver_aero, + session_solver_icing, +) from ansys.fluent.core._types import PathType -from ansys.fluent.core.launcher.container_launcher import DockerLauncher +from ansys.fluent.core.launcher.container_launcher import ( + ContainerArgsWithoutDryRun, + DockerLauncher, +) from ansys.fluent.core.launcher.launch_options import ( - Dimension, - FluentLinuxGraphicsDriver, FluentMode, - FluentWindowsGraphicsDriver, - Precision, - UIMode, ) -from ansys.fluent.core.launcher.pim_launcher import PIMLauncher -from ansys.fluent.core.launcher.standalone_launcher import StandaloneLauncher -from ansys.fluent.core.utils.fluent_version import FluentVersion +from ansys.fluent.core.launcher.launcher import LaunchFluentArgs, connect_to_fluent +from ansys.fluent.core.launcher.pim_launcher import ( + PIMArgsWithoutDryRun, + PIMLauncher, +) +from ansys.fluent.core.launcher.standalone_launcher import ( + StandaloneArgsWithoutDryRun, + StandaloneLauncher, +) +from ansys.fluent.core.session import BaseSession class SessionBase: @@ -48,7 +61,7 @@ class SessionBase: or `from_pim` functions to create a session. """ - _session_mode = { + _session_mode: dict[str, FluentMode] = { "Meshing": FluentMode.MESHING, "PureMeshing": FluentMode.PURE_MESHING, "PrePost": FluentMode.PRE_POST, @@ -57,35 +70,31 @@ class SessionBase: "SolverIcing": FluentMode.SOLVER_ICING, } + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[StandaloneArgsWithoutDryRun], + ) -> BaseSession: ... + + @overload @classmethod def from_install( cls, - ui_mode: UIMode | str | None = None, - graphics_driver: ( - FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str - ) = None, - product_version: FluentVersion | str | float | int | None = None, - dimension: Dimension | int | None = None, - precision: Precision | str | None = None, - processor_count: int | None = None, - journal_file_names: None | str | list[str] = None, - start_timeout: int = 60, - additional_arguments: str = "", - env: Dict[str, Any] = {}, # noqa: B006 - cleanup_on_exit: bool = True, + *, + dry_run: Literal[True], + **kwargs: Unpack[StandaloneArgsWithoutDryRun], + ) -> tuple[str, str]: ... + + @classmethod + def from_install( # pylint: disable=missing-param-doc + cls, + *, dry_run: bool = False, - start_transcript: bool = True, - case_file_name: "PathType | None" = None, - case_data_file_name: "PathType | None" = None, - lightweight_mode: bool | None = None, - py: bool | None = None, - gpu: bool | None = None, - cwd: "PathType | None" = None, - fluent_path: "PathType | None" = None, - topy: str | list | None = None, - start_watchdog: bool | None = None, - file_transfer_service: Any | None = None, - ): + **kwargs: Unpack[StandaloneArgsWithoutDryRun], + ) -> BaseSession | tuple[str, str]: """ Launch a Fluent session in standalone mode. @@ -163,36 +172,36 @@ def from_install( In job scheduler environments (e.g., SLURM, LSF, PBS), resources and compute nodes are allocated, and core counts are queried from these environments before being passed to Fluent. """ - mode = cls._session_mode[cls.__name__] - argvals = locals().copy() - argvals.pop("cls", None) # Remove the class reference from the arguments - launcher = StandaloneLauncher(**argvals) + launcher = StandaloneLauncher( + **kwargs, dry_run=dry_run, mode=cls._session_mode[cls.__name__] + ) return launcher() + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> BaseSession: ... + + @overload @classmethod def from_container( cls, - ui_mode: UIMode | str | None = None, - graphics_driver: ( - FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str | None - ) = None, - product_version: FluentVersion | str | float | int | None = None, - dimension: Dimension | int | None = None, - precision: Precision | str | None = None, - processor_count: int | None = None, - start_timeout: int = 60, - additional_arguments: str = "", - container_dict: dict | None = None, + *, + dry_run: Literal[True], + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> dict[str, Any]: ... + + @classmethod + def from_container( # pylint: disable=missing-param-doc + cls, + *, dry_run: bool = False, - cleanup_on_exit: bool = True, - start_transcript: bool = True, - py: bool | None = None, - gpu: bool | None = None, - start_watchdog: bool | None = None, - file_transfer_service: Any | None = None, - use_docker_compose: bool | None = None, - use_podman_compose: bool | None = None, - ): + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> BaseSession | dict[str, Any]: """ Launch a Fluent session in container mode. @@ -264,32 +273,36 @@ def from_container( In job scheduler environments (e.g., SLURM, LSF, PBS), resources and compute nodes are allocated, and core counts are queried from these environments before being passed to Fluent. """ - mode = cls._session_mode[cls.__name__] - argvals = locals().copy() - argvals.pop("cls", None) - launcher = DockerLauncher(**argvals) + launcher = DockerLauncher( + **kwargs, dry_run=dry_run, mode=cls._session_mode[cls.__name__] + ) return launcher() + @overload @classmethod def from_pim( cls, - ui_mode: UIMode | str | None = None, - graphics_driver: ( - FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str | None - ) = None, - product_version: FluentVersion | str | float | int | None = None, - dimension: Dimension | int | None = None, - precision: Precision | str | None = None, - processor_count: int | None = None, - start_timeout: int = 60, - additional_arguments: str = "", - cleanup_on_exit: bool = True, - dry_run: bool | None = None, - start_transcript: bool = True, - gpu: bool | None = None, - start_watchdog: bool | None = None, - file_transfer_service: Any | None = None, - ): + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> BaseSession: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> dict[str, Any]: ... + + @classmethod + def from_pim( # pylint: disable=missing-param-doc + cls, + *, + dry_run: bool = False, + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> BaseSession | dict[str, Any]: """ Launch a Fluent session in `PIM `_ mode. @@ -352,10 +365,10 @@ def from_pim( In job scheduler environments (e.g., SLURM, LSF, PBS), resources and compute nodes are allocated, and core counts are queried from these environments before being passed to Fluent. """ - mode = cls._session_mode[cls.__name__] - argvals = locals().copy() - argvals.pop("cls", None) - launcher = PIMLauncher(**argvals) + kwargs_with_mode = dict(kwargs) + kwargs_with_mode["mode"] = cls._session_mode[cls.__name__] + kwargs_with_mode["dry_run"] = dry_run + launcher = PIMLauncher(**kwargs_with_mode) return launcher() @classmethod @@ -392,7 +405,7 @@ def from_connection( TypeError If the session type does not match the expected session type. """ - session = pyfluent.connect_to_fluent( + session = connect_to_fluent( ip=ip, port=port, server_info_file_name=server_info_file_name, @@ -413,34 +426,358 @@ def from_connection( class Meshing(SessionBase): """Encapsulates a Fluent server for meshing session connection.""" - pass + if TYPE_CHECKING: + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[LaunchFluentArgs], + ) -> session_meshing.Meshing: ... + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[LaunchFluentArgs], + ) -> tuple[str, str]: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> session_meshing.Meshing: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> dict[str, Any]: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> session_meshing.Meshing: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> dict[str, Any]: ... class PureMeshing(SessionBase): """Encapsulates a Fluent server for pure meshing session connection.""" - pass + if TYPE_CHECKING: + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[LaunchFluentArgs], + ) -> session_pure_meshing.PureMeshing: ... + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[LaunchFluentArgs], + ) -> tuple[str, str]: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> session_pure_meshing.PureMeshing: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> dict[str, Any]: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> session_pure_meshing.PureMeshing: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> dict[str, Any]: ... class PrePost(SessionBase): """Encapsulates a Fluent server for pre-post session connection.""" - pass + if TYPE_CHECKING: + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[LaunchFluentArgs], + ) -> session_solver.Solver: ... + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[LaunchFluentArgs], + ) -> tuple[str, str]: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> session_solver.Solver: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> dict[str, Any]: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> session_solver.Solver: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> dict[str, Any]: ... class Solver(SessionBase): """Encapsulates a Fluent server for solver session connection.""" - pass + if TYPE_CHECKING: + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[LaunchFluentArgs], + ) -> session_solver.Solver: ... + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[LaunchFluentArgs], + ) -> tuple[str, str]: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> session_solver.Solver: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> dict[str, Any]: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> session_solver.Solver: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> dict[str, Any]: ... class SolverAero(SessionBase): """Encapsulates a Fluent server for solver aero session connection.""" - pass + if TYPE_CHECKING: + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[LaunchFluentArgs], + ) -> session_solver_aero.SolverAero: ... + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[LaunchFluentArgs], + ) -> tuple[str, str]: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> session_solver_aero.SolverAero: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> dict[str, Any]: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> session_solver_aero.SolverAero: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> dict[str, Any]: ... class SolverIcing(SessionBase): """Encapsulates a Fluent server for solver icing session connection.""" - pass + if TYPE_CHECKING: + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[LaunchFluentArgs], + ) -> session_solver_icing.SolverIcing: ... + + @overload + @classmethod + def from_install( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[LaunchFluentArgs], + ) -> tuple[str, str]: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> session_solver_icing.SolverIcing: ... + + @overload + @classmethod + def from_container( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[ContainerArgsWithoutDryRun], + ) -> dict[str, Any]: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[False] = False, + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> session_solver_icing.SolverIcing: ... + + @overload + @classmethod + def from_pim( + cls, + *, + dry_run: Literal[True], + **kwargs: Unpack[PIMArgsWithoutDryRun], + ) -> dict[str, Any]: ...