Skip to content

feat: Replace compose env vars with parameters #4284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Jul 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ef266bd
feat: Replace compose env vars with parameters
hpohekar Jul 15, 2025
b03deaf
chore: adding changelog file 4284.added.md [dependabot-skip]
pyansys-ci-bot Jul 15, 2025
5feec28
feat: Replace compose env vars with parameters
hpohekar Jul 15, 2025
8474181
feat: Replace compose env vars with parameters
hpohekar Jul 15, 2025
31c102c
Merge branch 'main' into feat/make_compose_env_vars_parameter
hpohekar Jul 21, 2025
dce7056
update docstrings
hpohekar Jul 21, 2025
01fec37
update signature
hpohekar Jul 21, 2025
749f483
update signature
hpohekar Jul 21, 2025
75c4e13
fix the signature
hpohekar Jul 21, 2025
a543185
chore: Add more tests
hpohekar Jul 21, 2025
97d8c5d
chore: update docs
hpohekar Jul 21, 2025
3ddf467
chore: update docs 1
hpohekar Jul 21, 2025
0aa2176
chore: update default value
hpohekar Jul 21, 2025
072e6dc
chore: update default value
hpohekar Jul 21, 2025
21dda3c
chore: update default value
hpohekar Jul 21, 2025
7d417fe
chore: bug fix
hpohekar Jul 22, 2025
6027c48
chore: bug fix 1
hpohekar Jul 22, 2025
afc1cb6
Revert "chore: update default value"
hpohekar Jul 22, 2025
15d9b55
Merge branch 'main' into feat/make_compose_env_vars_parameter
hpohekar Jul 22, 2025
7e17047
Reapply "chore: update default value"
hpohekar Jul 22, 2025
b03e4b7
Merge branch 'main' into feat/make_compose_env_vars_parameter
hpohekar Jul 22, 2025
0c8e02e
Merge branch 'main' into feat/make_compose_env_vars_parameter
hpohekar Jul 22, 2025
d3b6890
refactor: Check env in constructor only
hpohekar Jul 22, 2025
fbe5a63
refactor: Check env in constructor only
hpohekar Jul 22, 2025
a492709
use ComposeConfig
hpohekar Jul 22, 2025
bf57975
use ComposeConfig
hpohekar Jul 22, 2025
d10772b
use ComposeConfig
hpohekar Jul 22, 2025
9c8e243
Revert "use ComposeConfig"
hpohekar Jul 23, 2025
e6e1ba8
Revert "use ComposeConfig"
hpohekar Jul 23, 2025
fad8166
Use correct ConfigCompose
hpohekar Jul 23, 2025
28888a6
Update tests
hpohekar Jul 23, 2025
859169e
Update boolean values
hpohekar Jul 23, 2025
22b137a
Merge branch 'main' into feat/make_compose_env_vars_parameter
hpohekar Jul 23, 2025
e4bab37
Update condition check
hpohekar Jul 23, 2025
dcf14c1
Update condition 2
hpohekar Jul 23, 2025
cc1d907
fix: resolve None type error
hpohekar Jul 24, 2025
4a0fe86
fix: resolve None type error 1
hpohekar Jul 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/test-podman-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ env:
ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER) }}
PYFLUENT_TIMEOUT_FORCE_EXIT: 30
PYFLUENT_LAUNCH_CONTAINER: 1
PYFLUENT_USE_PODMAN_COMPOSE: 1
PYFLUENT_LOGGING: "DEBUG"
PYFLUENT_WATCHDOG_DEBUG: "OFF"
PYFLUENT_HIDE_LOG_SECRETS: 1
Expand Down
1 change: 1 addition & 0 deletions doc/changelog.d/4284.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Replace compose env vars with parameters
11 changes: 11 additions & 0 deletions doc/deprecated_pyfluent_apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,15 @@
"0.23.dev0",
"`surfaces`",
),
# Environment variables
(
"The `PYFLUENT_USE_DOCKER_COMPOSE` environment variable",
"0.34.0",
"``use_docker_compose`` argument of :py:func:`ansys.fluent.core.launcher.launcher.launch_fluent <ansys.fluent.core.launcher.launcher.launch_fluent>`",
),
(
"The `PYFLUENT_USE_PODMAN_COMPOSE` environment variable",
"0.34.0",
"``use_podman_compose`` argument of :py:func:`ansys.fluent.core.launcher.launcher.launch_fluent <ansys.fluent.core.launcher.launcher.launch_fluent>`",
),
]
4 changes: 0 additions & 4 deletions doc/source/contributing/environment_variables.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,3 @@ Following is a list of environment variables that can be set to control various
- Specifies the IP address of the Fluent server while launching Fluent in :func:`launch_fluent() <ansys.fluent.core.launcher.launcher.launch_fluent>`.
* - SERVER_INFO_DIR
- Specifies the directory where the server-info file is created while launching Fluent in :func:`launch_fluent() <ansys.fluent.core.launcher.launcher.launch_fluent>`.
* - PYFLUENT_USE_DOCKER_COMPOSE
- Specifies Docker as a compose source to launch Fluent in container mode.
* - PYFLUENT_USE_PODMAN_COMPOSE
- Specifies Podman as a compose source to launch Fluent in container mode.
6 changes: 2 additions & 4 deletions doc/source/user_guide/make_container_image.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,9 @@ Run Docker container using PyFluent

import os
import ansys.fluent.core as pyfluent
os.environ["PYFLUENT_USE_DOCKER_COMPOSE"] = "1"
os.environ["ANSYSLMD_LICENSE_FILE"] = "<license file or server>"
custom_config = {'fluent_image': 'ansys_inc:latest', 'mount_source': f"{os.getcwd()}", 'auto_remove': False}
solver_session = pyfluent.launch_fluent(container_dict=custom_config)
solver_session = pyfluent.launch_fluent(container_dict=custom_config, user_docker_compose=True)


Run Podman container using the command line
Expand Down Expand Up @@ -137,7 +136,6 @@ Run Podman container using PyFluent
import os
import ansys.fluent.core as pyfluent
os.environ["ANSYSLMD_LICENSE_FILE"] = "<license file or server>"
os.environ["PYFLUENT_USE_PODMAN_COMPOSE"] = "1"
custom_config = {'fluent_image': 'ansys_inc:latest', 'mount_source': f"{os.getcwd()}", 'auto_remove': False}
solver_session = pyfluent.launch_fluent(container_dict=custom_config)
solver_session = pyfluent.launch_fluent(container_dict=custom_config, use_podman_compose=True)

18 changes: 9 additions & 9 deletions doc/source/user_guide/session/launching_ansys_fluent.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Launch in a container
---------------------

The :meth:`from_container() <ansys.fluent.core.session_utilities.SessionBase.from_container>` method launches Fluent inside a Docker container.
Pass ``use_docker_compose=True`` or ``use_podman_compose=True`` to use Docker Compose or Podman Compose, respectively.

Use this method when:

Expand All @@ -55,7 +56,6 @@ Use this method when:

import os
os.environ["PYFLUENT_LAUNCH_CONTAINER"] = "1"
os.environ["PYFLUENT_USE_DOCKER_COMPOSE"] = "1" # or os.environ["PYFLUENT_USE_PODMAN_COMPOSE"] = "1"

import ansys.fluent.core as pyfluent
from ansys.fluent.core.utils.networking import get_free_port
Expand All @@ -64,12 +64,12 @@ Use this method when:
port_2 = get_free_port()
container_dict = {"ports": {f"{port_1}": port_1, f"{port_2}": port_2}}

meshing = pyfluent.Meshing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252)
pure_meshing = pyfluent.PureMeshing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252)
solver = pyfluent.Solver.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252)
solver_aero = pyfluent.SolverAero.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252)
solver_icing = pyfluent.SolverIcing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252)
pre_post = pyfluent.PrePost.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252)
meshing = pyfluent.Meshing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_docker_compose=True)
pure_meshing = pyfluent.PureMeshing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_podman_compose=True)
solver = pyfluent.Solver.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_docker_compose=True)
solver_aero = pyfluent.SolverAero.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_podman_compose=True)
solver_icing = pyfluent.SolverIcing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_docker_compose=True)
pre_post = pyfluent.PrePost.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_podman_compose=True)


Connect to an existing session
Expand Down Expand Up @@ -433,6 +433,7 @@ Launching Fluent in container mode with Docker Compose or Podman Compose
------------------------------------------------------------------------

Use PyFluent with Docker Compose or Podman Compose to run Fluent in a consistent, reproducible containerized environment.
Pass ``use_docker_compose=True`` or ``use_podman_compose=True`` to use Docker Compose or Podman Compose, respectively.

1. **Docker Compose**

Expand All @@ -458,7 +459,6 @@ Set environment variables to select the container engine:

>>> import os
>>> os.environ["PYFLUENT_LAUNCH_CONTAINER"] = "1"
>>> os.environ["PYFLUENT_USE_DOCKER_COMPOSE"] = "1" # or os.environ["PYFLUENT_USE_PODMAN_COMPOSE"] = "1"


Then launch:
Expand All @@ -467,7 +467,7 @@ Then launch:

>>> import ansys.fluent.core as pyfluent
>>> from ansys.fluent.core import examples
>>> solver_session = pyfluent.launch_fluent()
>>> solver_session = pyfluent.launch_fluent(use_docker_compose=True)
>>> case_file_name = examples.download_file("mixing_elbow.cas.h5", "pyfluent/mixing_elbow")
>>> solver_session.file.read(file_name=case_file_name, file_type="case")
>>> solver_session.exit()
Expand Down
12 changes: 5 additions & 7 deletions src/ansys/fluent/core/docker/docker_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
class ComposeBasedLauncher:
"""Launch Fluent through docker or Podman compose."""

def __init__(self, *, container_dict):
def __init__(self, compose_config, container_dict):
self._compose_config = compose_config
self._compose_name = f"pyfluent_compose_{uuid.uuid4().hex}"
self._container_dict = container_dict
image_tag = os.getenv("FLUENT_IMAGE_TAG")
Expand All @@ -45,9 +46,6 @@ def __init__(self, *, container_dict):

self._compose_file = self._get_compose_file(container_dict)

def _is_podman_selected(self):
return os.getenv("PYFLUENT_USE_PODMAN_COMPOSE") == "1"

def _get_compose_file(self, container_dict):
"""Generates compose file for the Docker Compose setup.

Expand Down Expand Up @@ -132,14 +130,14 @@ def _set_compose_cmds(self):
"""

# Determine the compose command
if os.getenv("PYFLUENT_USE_PODMAN_COMPOSE") == "1":
if self._compose_config.use_podman_compose:
self._compose_cmds = (
["sudo", "podman", "compose"]
if hasattr(self, "_container_source")
and "sudo" in self._container_source
else ["podman", "compose"]
)
elif os.getenv("PYFLUENT_USE_DOCKER_COMPOSE") == "1":
elif self._compose_config.use_docker_compose:
self._compose_cmds = ["docker", "compose"]
else:
raise RuntimeError("Neither Docker nor Podman is specified.")
Expand All @@ -151,7 +149,7 @@ def check_image_exists(self) -> bool:
try:
cmd = self._container_source + ["images", "-q", self._image_name]
# Podman users do not always configure rootless mode in /etc/subuids and /etc/subgids
if self._is_podman_selected():
if self._compose_config.use_podman_compose:
sudo_cmd = ["sudo"] + cmd
output_1 = subprocess.check_output(cmd)
output_2 = subprocess.check_output(sudo_cmd)
Expand Down
20 changes: 15 additions & 5 deletions src/ansys/fluent/core/fluent_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import grpc

import ansys.fluent.core as pyfluent
from ansys.fluent.core.launcher.launcher_utils import is_compose
from ansys.fluent.core.launcher.launcher_utils import ComposeConfig
from ansys.fluent.core.services import service_creator
from ansys.fluent.core.services.app_utilities import (
AppUtilitiesOld,
Expand Down Expand Up @@ -381,6 +381,7 @@ def __init__(
slurm_job_id: str | None = None,
inside_container: bool | None = None,
container: ContainerT | None = None,
compose_config: ComposeConfig | None = None,
):
"""Initialize a Session.

Expand Down Expand Up @@ -420,12 +421,15 @@ def __init__(
container: ContainerT, optional
The container instance if the Fluent session is running inside
a container.
compose_config: ComposeConfig, optional
Configuration for Docker Compose or Podman Compose.

Raises
------
PortNotProvided
If port is not provided.
"""
self._compose_config = compose_config if compose_config else ComposeConfig()
self._error_state = ErrorState()
self._data_valid = False
self._channel_str = None
Expand Down Expand Up @@ -472,7 +476,7 @@ def __init__(
and cortex_host is not None
):
logger.info("Checking if Fluent is running inside a container.")
if not is_compose():
if not self._compose_config.is_compose:
inside_container = get_container(cortex_host)
logger.debug(f"get_container({cortex_host}): {inside_container}")
if inside_container is False:
Expand Down Expand Up @@ -549,7 +553,10 @@ def force_exit(self):
>>> session = pyfluent.launch_fluent()
>>> session.force_exit()
"""
if self.connection_properties.inside_container or is_compose():
if (
self.connection_properties.inside_container
or self._compose_config.is_compose
):
self._force_exit_container()
elif self._remote_instance is not None:
logger.error("Cannot execute cleanup script, Fluent running remotely.")
Expand Down Expand Up @@ -601,7 +608,7 @@ def force_exit(self):
def _force_exit_container(self):
"""Immediately terminates the Fluent client running inside a container, losing
unsaved progress and data."""
if is_compose() and self._container:
if self._compose_config.is_compose and self._container:
self._container.stop()
else:
container = self.connection_properties.inside_container
Expand Down Expand Up @@ -679,7 +686,10 @@ def wait_process_finished(self, wait: float | int | bool = 60):
else:
raise WaitTypeError()

if self.connection_properties.inside_container and not is_compose():
if (
self.connection_properties.inside_container
and not self._compose_config.is_compose
):
_response = timeout_loop(
get_container,
wait,
Expand Down
23 changes: 19 additions & 4 deletions src/ansys/fluent/core/launcher/container_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
UIMode,
_get_argvals_and_session,
)
from ansys.fluent.core.launcher.launcher_utils import is_compose
from ansys.fluent.core.launcher.launcher_utils import ComposeConfig
from ansys.fluent.core.launcher.process_launch_string import (
_build_fluent_launch_args_string,
)
Expand Down Expand Up @@ -108,6 +108,8 @@ def __init__(
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,
):
"""
Launch a Fluent session in container mode.
Expand Down Expand Up @@ -161,6 +163,10 @@ def __init__(
GUI-less Fluent sessions started by PyFluent are properly closed when the current Python process ends.
file_transfer_service : Any, optional
Service for uploading/downloading files to/from the server.
use_docker_compose: bool
Whether to use Docker Compose to launch Fluent.
use_podman_compose: bool
Whether to use Podman Compose to launch Fluent.

Returns
-------
Expand Down Expand Up @@ -198,12 +204,15 @@ def __init__(
self._args = _build_fluent_launch_args_string(**self.argvals).split()
if FluentMode.is_meshing(self.argvals["mode"]):
self._args.append(" -meshing")
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, **self.argvals["container_dict"]
self._args,
compose_config=self._compose_config,
**self.argvals["container_dict"],
)
dict_str = dict_to_str(config_dict)
print("\nDocker container run configuration:\n")
Expand All @@ -214,11 +223,12 @@ def __call__(self):
logger.debug(f"Fluent container launcher args: {self._args}")
logger.debug(f"Fluent container launcher argvals:\n{dict_to_str(self.argvals)}")

if is_compose():
if self._compose_config.is_compose:
port, config_dict, container = start_fluent_container(
self._args,
self.argvals["container_dict"],
self.argvals["start_timeout"],
compose_config=self._compose_config,
)

_, _, password = _get_server_info_from_container(config_dict=config_dict)
Expand All @@ -227,6 +237,7 @@ def __call__(self):
self._args,
self.argvals["container_dict"],
self.argvals["start_timeout"],
compose_config=self._compose_config,
)

fluent_connection = FluentConnection(
Expand All @@ -237,18 +248,22 @@ def __call__(self):
slurm_job_id=self.argvals and self.argvals.get("slurm_job_id"),
inside_container=True,
container=container,
compose_config=self._compose_config,
)

self.argvals["compose_config"] = self._compose_config

session = self.new_session(
fluent_connection=fluent_connection,
scheme_eval=fluent_connection._connection_interface.scheme_eval,
file_transfer_service=self.file_transfer_service,
start_transcript=self.argvals["start_transcript"],
launcher_args=self.argvals,
)

session._container = container

if not is_compose():
if not self._compose_config.is_compose:
if (
self.argvals["start_watchdog"] is None
and self.argvals["cleanup_on_exit"]
Expand Down
Loading