ansys-fluent-core 0.34.dev0__py3-none-any.whl → 0.35.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansys-fluent-core might be problematic. Click here for more details.

Files changed (108) hide show
  1. ansys/fluent/core/__init__.py +48 -84
  2. ansys/fluent/core/codegen/allapigen.py +2 -2
  3. ansys/fluent/core/codegen/builtin_settingsgen.py +41 -13
  4. ansys/fluent/core/codegen/datamodelgen.py +3 -1
  5. ansys/fluent/core/codegen/print_fluent_version.py +2 -2
  6. ansys/fluent/core/codegen/settingsgen.py +18 -6
  7. ansys/fluent/core/codegen/tuigen.py +6 -5
  8. ansys/fluent/core/data_model_cache.py +2 -2
  9. ansys/fluent/core/docker/docker_compose.py +11 -9
  10. ansys/fluent/core/docker/utils.py +35 -0
  11. ansys/fluent/core/examples/downloads.py +8 -11
  12. ansys/fluent/core/exceptions.py +13 -1
  13. ansys/fluent/core/field_data_interfaces.py +239 -38
  14. ansys/fluent/core/file_session.py +167 -61
  15. ansys/fluent/core/fluent_connection.py +41 -26
  16. ansys/fluent/core/generated/api_tree/api_objects.json +1 -1
  17. ansys/fluent/core/generated/datamodel_231/flicing.py +40 -40
  18. ansys/fluent/core/generated/datamodel_231/meshing.py +231 -231
  19. ansys/fluent/core/generated/datamodel_232/flicing.py +50 -50
  20. ansys/fluent/core/generated/datamodel_232/meshing.py +189 -189
  21. ansys/fluent/core/generated/datamodel_241/flicing.py +30 -30
  22. ansys/fluent/core/generated/datamodel_241/meshing.py +290 -290
  23. ansys/fluent/core/generated/datamodel_242/flicing.py +50 -50
  24. ansys/fluent/core/generated/datamodel_242/meshing.py +331 -331
  25. ansys/fluent/core/generated/datamodel_242/part_management.py +6 -6
  26. ansys/fluent/core/generated/datamodel_251/flicing.py +65 -65
  27. ansys/fluent/core/generated/datamodel_251/meshing.py +300 -300
  28. ansys/fluent/core/generated/datamodel_251/part_management.py +6 -6
  29. ansys/fluent/core/generated/datamodel_252/flicing.py +25 -25
  30. ansys/fluent/core/generated/datamodel_252/meshing.py +382 -382
  31. ansys/fluent/core/generated/datamodel_252/part_management.py +10 -10
  32. ansys/fluent/core/generated/datamodel_261/flicing.py +45 -45
  33. ansys/fluent/core/generated/datamodel_261/meshing.py +454 -435
  34. ansys/fluent/core/generated/datamodel_261/part_management.py +5 -5
  35. ansys/fluent/core/generated/datamodel_261/preferences.py +7 -0
  36. ansys/fluent/core/generated/fluent_version_252.py +1 -1
  37. ansys/fluent/core/generated/fluent_version_261.py +3 -3
  38. ansys/fluent/core/generated/meshing/tui_261.py +54 -3
  39. ansys/fluent/core/generated/solver/settings_231.py +1 -0
  40. ansys/fluent/core/generated/solver/settings_231.pyi +3025 -1
  41. ansys/fluent/core/generated/solver/settings_232.py +1 -0
  42. ansys/fluent/core/generated/solver/settings_232.pyi +3425 -1
  43. ansys/fluent/core/generated/solver/settings_241.py +1 -0
  44. ansys/fluent/core/generated/solver/settings_241.pyi +4423 -1
  45. ansys/fluent/core/generated/solver/settings_242.py +1 -0
  46. ansys/fluent/core/generated/solver/settings_242.pyi +5474 -1
  47. ansys/fluent/core/generated/solver/settings_251.py +11 -0
  48. ansys/fluent/core/generated/solver/settings_251.pyi +6006 -1
  49. ansys/fluent/core/generated/solver/settings_252.py +11 -1
  50. ansys/fluent/core/generated/solver/settings_252.pyi +6782 -2
  51. ansys/fluent/core/generated/solver/settings_261.py +5592 -2740
  52. ansys/fluent/core/generated/solver/settings_261.pyi +10335 -1994
  53. ansys/fluent/core/generated/solver/settings_builtin.py +560 -38
  54. ansys/fluent/core/generated/solver/settings_builtin.pyi +24 -18
  55. ansys/fluent/core/generated/solver/tui_261.py +409 -285
  56. ansys/fluent/core/launcher/container_launcher.py +25 -6
  57. ansys/fluent/core/launcher/error_handler.py +1 -1
  58. ansys/fluent/core/launcher/fluent_container.py +97 -45
  59. ansys/fluent/core/launcher/launch_options.py +5 -4
  60. ansys/fluent/core/launcher/launcher.py +18 -2
  61. ansys/fluent/core/launcher/launcher_utils.py +63 -15
  62. ansys/fluent/core/launcher/pim_launcher.py +17 -3
  63. ansys/fluent/core/launcher/process_launch_string.py +3 -2
  64. ansys/fluent/core/launcher/server_info.py +7 -3
  65. ansys/fluent/core/launcher/slurm_launcher.py +4 -2
  66. ansys/fluent/core/launcher/standalone_launcher.py +6 -3
  67. ansys/fluent/core/launcher/watchdog.py +6 -6
  68. ansys/fluent/core/launcher/watchdog_exec +1 -1
  69. ansys/fluent/core/logger.py +3 -1
  70. ansys/fluent/core/module_config.py +358 -0
  71. ansys/fluent/core/pyfluent_warnings.py +9 -3
  72. ansys/fluent/core/report.py +2 -2
  73. ansys/fluent/core/search.py +34 -13
  74. ansys/fluent/core/services/__init__.py +2 -2
  75. ansys/fluent/core/services/api_upgrade.py +3 -2
  76. ansys/fluent/core/services/app_utilities.py +39 -0
  77. ansys/fluent/core/services/datamodel_se.py +4 -2
  78. ansys/fluent/core/services/deprecated_field_data.py +4 -4
  79. ansys/fluent/core/services/field_data.py +185 -49
  80. ansys/fluent/core/services/health_check.py +3 -1
  81. ansys/fluent/core/services/interceptors.py +8 -6
  82. ansys/fluent/core/services/reduction.py +16 -5
  83. ansys/fluent/core/services/settings.py +1 -0
  84. ansys/fluent/core/session.py +47 -4
  85. ansys/fluent/core/session_pure_meshing.py +6 -6
  86. ansys/fluent/core/session_pure_meshing.pyi +1 -0
  87. ansys/fluent/core/session_shared.py +4 -4
  88. ansys/fluent/core/session_solver.py +41 -10
  89. ansys/fluent/core/session_solver.pyi +1 -0
  90. ansys/fluent/core/session_utilities.py +7 -0
  91. ansys/fluent/core/solver/error_message.py +2 -2
  92. ansys/fluent/core/solver/flobject.py +192 -123
  93. ansys/fluent/core/solver/function/reduction.py +37 -9
  94. ansys/fluent/core/solver/settings_builtin_bases.py +3 -3
  95. ansys/fluent/core/solver/settings_builtin_data.py +7 -17
  96. ansys/fluent/core/streaming_services/datamodel_event_streaming.py +3 -2
  97. ansys/fluent/core/streaming_services/datamodel_streaming.py +3 -1
  98. ansys/fluent/core/streaming_services/events_streaming.py +2 -18
  99. ansys/fluent/core/system_coupling.py +3 -1
  100. ansys/fluent/core/utils/__init__.py +0 -7
  101. ansys/fluent/core/utils/data_transfer.py +3 -3
  102. ansys/fluent/core/utils/file_transfer_service.py +24 -15
  103. ansys/fluent/core/utils/fluent_version.py +4 -6
  104. ansys/fluent/core/utils/networking.py +21 -11
  105. {ansys_fluent_core-0.34.dev0.dist-info → ansys_fluent_core-0.35.dev0.dist-info}/METADATA +10 -11
  106. {ansys_fluent_core-0.34.dev0.dist-info → ansys_fluent_core-0.35.dev0.dist-info}/RECORD +108 -106
  107. {ansys_fluent_core-0.34.dev0.dist-info → ansys_fluent_core-0.35.dev0.dist-info}/WHEEL +1 -1
  108. {ansys_fluent_core-0.34.dev0.dist-info/licenses → ansys_fluent_core-0.35.dev0.dist-info}/LICENSE +0 -0
@@ -56,7 +56,7 @@ from ansys.fluent.core.launcher.launch_options import (
56
56
  UIMode,
57
57
  _get_argvals_and_session,
58
58
  )
59
- from ansys.fluent.core.launcher.launcher_utils import is_compose
59
+ from ansys.fluent.core.launcher.launcher_utils import ComposeConfig
60
60
  from ansys.fluent.core.launcher.process_launch_string import (
61
61
  _build_fluent_launch_args_string,
62
62
  )
@@ -108,6 +108,8 @@ class DockerLauncher:
108
108
  gpu: bool | None = None,
109
109
  start_watchdog: bool | None = None,
110
110
  file_transfer_service: Any | None = None,
111
+ use_docker_compose: bool | None = None,
112
+ use_podman_compose: bool | None = None,
111
113
  ):
112
114
  """
113
115
  Launch a Fluent session in container mode.
@@ -161,6 +163,10 @@ class DockerLauncher:
161
163
  GUI-less Fluent sessions started by PyFluent are properly closed when the current Python process ends.
162
164
  file_transfer_service : Any, optional
163
165
  Service for uploading/downloading files to/from the server.
166
+ use_docker_compose: bool
167
+ Whether to use Docker Compose to launch Fluent.
168
+ use_podman_compose: bool
169
+ Whether to use Podman Compose to launch Fluent.
164
170
 
165
171
  Returns
166
172
  -------
@@ -198,12 +204,15 @@ class DockerLauncher:
198
204
  self._args = _build_fluent_launch_args_string(**self.argvals).split()
199
205
  if FluentMode.is_meshing(self.argvals["mode"]):
200
206
  self._args.append(" -meshing")
207
+ self._compose_config = ComposeConfig(use_docker_compose, use_podman_compose)
201
208
 
202
209
  def __call__(self):
203
210
 
204
211
  if self.argvals["dry_run"]:
205
212
  config_dict, *_ = configure_container_dict(
206
- self._args, **self.argvals["container_dict"]
213
+ self._args,
214
+ compose_config=self._compose_config,
215
+ **self.argvals["container_dict"],
207
216
  )
208
217
  dict_str = dict_to_str(config_dict)
209
218
  print("\nDocker container run configuration:\n")
@@ -214,15 +223,21 @@ class DockerLauncher:
214
223
  logger.debug(f"Fluent container launcher args: {self._args}")
215
224
  logger.debug(f"Fluent container launcher argvals:\n{dict_to_str(self.argvals)}")
216
225
 
217
- if is_compose():
226
+ if self._compose_config.is_compose:
218
227
  port, config_dict, container = start_fluent_container(
219
- self._args, self.argvals["container_dict"]
228
+ self._args,
229
+ self.argvals["container_dict"],
230
+ self.argvals["start_timeout"],
231
+ compose_config=self._compose_config,
220
232
  )
221
233
 
222
234
  _, _, password = _get_server_info_from_container(config_dict=config_dict)
223
235
  else:
224
236
  port, password, container = start_fluent_container(
225
- self._args, self.argvals["container_dict"]
237
+ self._args,
238
+ self.argvals["container_dict"],
239
+ self.argvals["start_timeout"],
240
+ compose_config=self._compose_config,
226
241
  )
227
242
 
228
243
  fluent_connection = FluentConnection(
@@ -233,18 +248,22 @@ class DockerLauncher:
233
248
  slurm_job_id=self.argvals and self.argvals.get("slurm_job_id"),
234
249
  inside_container=True,
235
250
  container=container,
251
+ compose_config=self._compose_config,
236
252
  )
237
253
 
254
+ self.argvals["compose_config"] = self._compose_config
255
+
238
256
  session = self.new_session(
239
257
  fluent_connection=fluent_connection,
240
258
  scheme_eval=fluent_connection._connection_interface.scheme_eval,
241
259
  file_transfer_service=self.file_transfer_service,
242
260
  start_transcript=self.argvals["start_transcript"],
261
+ launcher_args=self.argvals,
243
262
  )
244
263
 
245
264
  session._container = container
246
265
 
247
- if not is_compose():
266
+ if not self._compose_config.is_compose:
248
267
  if (
249
268
  self.argvals["start_watchdog"] is None
250
269
  and self.argvals["cleanup_on_exit"]
@@ -70,7 +70,7 @@ class LaunchFluentError(Exception):
70
70
 
71
71
  def __init__(self, launch_string):
72
72
  """__init__ method of LaunchFluentError class."""
73
- details = "\n" + "Fluent Launch string: " + launch_string
73
+ details = "\n" + "Fluent Launch command: " + launch_string
74
74
  super().__init__(details)
75
75
 
76
76
 
@@ -62,7 +62,7 @@ config_dict =
62
62
  'detach': True,
63
63
  'environment': {'ANSYSLMD_LICENSE_FILE': '2048@licenseserver.com',
64
64
  'REMOTING_PORTS': '54000/portspan=2'},
65
- 'fluent_image': 'ghcr.io/ansys/pyfluent:v23.2.0',
65
+ 'fluent_image': '<image registry>:v23.2.0',
66
66
  'labels': {'test_name': 'none'},
67
67
  'ports': {'54000': 54000},
68
68
  'volumes': ['/home/user/.local/share/ansys_fluent_core/examples:/mnt/pyfluent'],
@@ -77,10 +77,16 @@ from pathlib import Path, PurePosixPath
77
77
  from pprint import pformat
78
78
  import tempfile
79
79
  from typing import Any, List
80
+ import warnings
80
81
 
81
82
  import ansys.fluent.core as pyfluent
82
83
  from ansys.fluent.core.docker.docker_compose import ComposeBasedLauncher
83
- from ansys.fluent.core.launcher.launcher_utils import is_compose
84
+ from ansys.fluent.core.docker.utils import get_ghcr_fluent_image_name
85
+ from ansys.fluent.core.launcher.error_handler import (
86
+ LaunchFluentError,
87
+ )
88
+ from ansys.fluent.core.launcher.launcher_utils import ComposeConfig
89
+ from ansys.fluent.core.pyfluent_warnings import PyFluentDeprecationWarning
84
90
  from ansys.fluent.core.session import _parse_server_info_file
85
91
  from ansys.fluent.core.utils.deprecate import all_deprecators
86
92
  from ansys.fluent.core.utils.execution import timeout_loop
@@ -125,7 +131,7 @@ def dict_to_str(dict: dict) -> str:
125
131
  This is useful for logging purposes, to avoid printing sensitive information such as license server details.
126
132
  """
127
133
 
128
- if "environment" in dict and os.getenv("PYFLUENT_HIDE_LOG_SECRETS") == "1":
134
+ if "environment" in dict and pyfluent.config.hide_log_secrets:
129
135
  modified_dict = dict.copy()
130
136
  modified_dict.pop("environment")
131
137
  return pformat(modified_dict)
@@ -155,7 +161,7 @@ def configure_container_dict(
155
161
  args: List[str],
156
162
  mount_source: str | Path | None = None,
157
163
  mount_target: str | Path | None = None,
158
- timeout: int = 60,
164
+ timeout: int | None = None,
159
165
  port: int | None = None,
160
166
  license_server: str | None = None,
161
167
  container_server_info_file: str | Path | None = None,
@@ -164,6 +170,7 @@ def configure_container_dict(
164
170
  image_name: str | None = None,
165
171
  image_tag: str | None = None,
166
172
  file_transfer_service: Any | None = None,
173
+ compose_config: ComposeConfig | None = None,
167
174
  **container_dict,
168
175
  ) -> (dict, int, int, Path, bool):
169
176
  """Parses the parameters listed below, and sets up the container configuration file.
@@ -180,7 +187,7 @@ def configure_container_dict(
180
187
  Path inside the container where ``mount_source`` will be mounted. This will be the working directory path
181
188
  visible to the Fluent process running inside the container.
182
189
  timeout : int, optional
183
- Time limit for the Fluent container to start, in seconds. By default, 30 seconds.
190
+ Time limit for the Fluent container to start, in seconds.
184
191
  port : int, optional
185
192
  Port for Fluent container to use.
186
193
  license_server : str, optional
@@ -198,6 +205,8 @@ def configure_container_dict(
198
205
  Ignored if ``fluent_image`` has been specified.
199
206
  file_transfer_service : optional
200
207
  Supports file upload and download.
208
+ compose_config : ComposeConfig, optional
209
+ Configuration for Docker Compose, if using Docker Compose to launch the container.
201
210
  **container_dict
202
211
  Additional keyword arguments can be specified, they will be treated as Docker container run options
203
212
  to be passed directly to the Docker run execution. See examples below and `Docker run`_ documentation.
@@ -231,24 +240,28 @@ def configure_container_dict(
231
240
  See also :func:`start_fluent_container`.
232
241
  """
233
242
 
243
+ compose_config = compose_config if compose_config else ComposeConfig()
244
+
245
+ if timeout is not None:
246
+ warnings.warn(
247
+ "configure_container_dict(timeout) is deprecated, use launch_fluent(start_timeout) instead.",
248
+ PyFluentDeprecationWarning,
249
+ )
250
+
234
251
  logger.debug(f"container_dict before processing:\n{dict_to_str(container_dict)}")
235
252
 
236
253
  # Starting with 'mount_source' because it is not tied to the 'working_dir'.
237
254
  # The intended 'mount_source' logic is as follows, if it is not directly specified:
238
255
  # 1. If 'file_transfer_service' is provided, use its 'mount_source'.
239
- # 2. Try to use the environment variable 'PYFLUENT_CONTAINER_MOUNT_SOURCE', if it is set.
240
- # 3. Use the value from 'pyfluent.CONTAINER_MOUNT_SOURCE', if it is set.
241
- # 4. If 'volumes' is specified in 'container_dict', try to infer the value from it.
242
- # 5. Finally, use the current working directory, which is always available.
256
+ # 2. Use the value from 'pyfluent.config.container_mount_source', if it is set.
257
+ # 3. If 'volumes' is specified in 'container_dict', try to infer the value from it.
258
+ # 4. Finally, use the current working directory, which is always available.
243
259
 
244
260
  if not mount_source:
245
261
  if file_transfer_service:
246
262
  mount_source = file_transfer_service.mount_source
247
263
  else:
248
- mount_source = os.getenv(
249
- "PYFLUENT_CONTAINER_MOUNT_SOURCE",
250
- pyfluent.CONTAINER_MOUNT_SOURCE,
251
- )
264
+ mount_source = pyfluent.config.container_mount_source
252
265
 
253
266
  if "volumes" in container_dict:
254
267
  if len(container_dict["volumes"]) != 1:
@@ -274,15 +287,14 @@ def configure_container_dict(
274
287
 
275
288
  # The intended 'mount_target' logic is as follows, if it is not directly specified:
276
289
  # 1. If 'working_dir' is specified in 'container_dict', use it as 'mount_target'.
277
- # 2. Use the environment variable 'PYFLUENT_CONTAINER_MOUNT_TARGET', if it is set.
278
- # 3. Try to infer the value from the 'volumes' keyword in 'container_dict', if available.
279
- # 4. Finally, use the value from 'pyfluent.CONTAINER_MOUNT_TARGET', which is always set.
290
+ # 2. Try to infer the value from the 'volumes' keyword in 'container_dict', if available.
291
+ # 3. Finally, use the value from 'pyfluent.config.container_mount_target', which is always set.
280
292
 
281
293
  if not mount_target:
282
294
  if "working_dir" in container_dict:
283
295
  mount_target = container_dict["working_dir"]
284
296
  else:
285
- mount_target = os.getenv("PYFLUENT_CONTAINER_MOUNT_TARGET")
297
+ mount_target = pyfluent.config.container_mount_target
286
298
 
287
299
  if "working_dir" in container_dict and mount_target:
288
300
  # working_dir will be set later to the final value of mount_target
@@ -293,7 +305,7 @@ def configure_container_dict(
293
305
 
294
306
  if not mount_target:
295
307
  logger.debug("No container 'mount_target' specified, using default value.")
296
- mount_target = pyfluent.CONTAINER_MOUNT_TARGET
308
+ mount_target = pyfluent.config.container_mount_target
297
309
 
298
310
  if "volumes" not in container_dict:
299
311
  container_dict.update(volumes=[f"{mount_source}:{mount_target}"])
@@ -314,8 +326,8 @@ def configure_container_dict(
314
326
  if not port_mapping and "ports" in container_dict:
315
327
  # take the specified 'port', OR the first port value from the specified 'ports', for Fluent to use
316
328
  port_mapping = container_dict["ports"]
317
- if not port_mapping and pyfluent.LAUNCH_FLUENT_PORT:
318
- port = pyfluent.LAUNCH_FLUENT_PORT
329
+ if not port_mapping and pyfluent.config.launch_fluent_port:
330
+ port = pyfluent.config.launch_fluent_port
319
331
  port_mapping = {port: port}
320
332
  if not port_mapping:
321
333
  port = get_free_port()
@@ -338,11 +350,12 @@ def configure_container_dict(
338
350
  environment={
339
351
  "ANSYSLMD_LICENSE_FILE": license_server,
340
352
  "REMOTING_PORTS": f"{container_grpc_port}/portspan=2",
353
+ "FLUENT_ALLOW_REMOTE_GRPC_CONNECTION": "1",
341
354
  }
342
355
  )
343
356
 
344
357
  if "labels" not in container_dict:
345
- test_name = os.getenv("PYFLUENT_TEST_NAME", "none")
358
+ test_name = pyfluent.config.test_name
346
359
  container_dict.update(
347
360
  labels={"test_name": test_name},
348
361
  )
@@ -387,13 +400,14 @@ def configure_container_dict(
387
400
 
388
401
  if not fluent_image:
389
402
  if not image_tag:
390
- image_tag = os.getenv(
391
- "FLUENT_IMAGE_TAG", f"v{pyfluent.FLUENT_RELEASE_VERSION}"
403
+ image_tag = pyfluent.config.fluent_image_tag
404
+ if not image_name and image_tag:
405
+ image_name = (
406
+ pyfluent.config.fluent_image_name
407
+ or get_ghcr_fluent_image_name(image_tag)
392
408
  )
393
- if not image_name:
394
- image_name = os.getenv("FLUENT_IMAGE_NAME", "ghcr.io/ansys/pyfluent")
395
409
  if not image_tag or not image_name:
396
- fluent_image = os.getenv("FLUENT_CONTAINER_IMAGE", None)
410
+ fluent_image = pyfluent.config.fluent_container_name
397
411
  elif image_tag and image_name:
398
412
  if image_tag.startswith("sha"):
399
413
  fluent_image = f"{image_name}@{image_tag}"
@@ -404,24 +418,19 @@ def configure_container_dict(
404
418
 
405
419
  container_dict["fluent_image"] = fluent_image
406
420
 
407
- if not pyfluent.FLUENT_AUTOMATIC_TRANSCRIPT:
421
+ if not pyfluent.config.fluent_automatic_transcript:
408
422
  if "environment" not in container_dict:
409
423
  container_dict["environment"] = {}
410
424
  container_dict["environment"]["FLUENT_NO_AUTOMATIC_TRANSCRIPT"] = "1"
411
425
 
412
- if os.getenv("REMOTING_NEW_DM_API") == "1":
413
- if "environment" not in container_dict:
414
- container_dict["environment"] = {}
415
- container_dict["environment"]["REMOTING_NEW_DM_API"] = "1"
416
-
417
- if pyfluent.LAUNCH_FLUENT_IP or os.getenv("REMOTING_SERVER_ADDRESS"):
426
+ if pyfluent.config.launch_fluent_ip or pyfluent.config.remoting_server_address:
418
427
  if "environment" not in container_dict:
419
428
  container_dict["environment"] = {}
420
429
  container_dict["environment"]["REMOTING_SERVER_ADDRESS"] = (
421
- pyfluent.LAUNCH_FLUENT_IP or os.getenv("REMOTING_SERVER_ADDRESS")
430
+ pyfluent.config.launch_fluent_ip or pyfluent.config.remoting_server_address
422
431
  )
423
432
 
424
- if pyfluent.LAUNCH_FLUENT_SKIP_PASSWORD_CHECK:
433
+ if pyfluent.config.launch_fluent_skip_password_check:
425
434
  if "environment" not in container_dict:
426
435
  container_dict["environment"] = {}
427
436
  container_dict["environment"]["FLUENT_LAUNCHED_FROM_PYFLUENT"] = "1"
@@ -438,13 +447,13 @@ def configure_container_dict(
438
447
 
439
448
  host_server_info_file = Path(mount_source) / container_server_info_file.name
440
449
 
441
- if is_compose():
450
+ if compose_config.is_compose:
442
451
  container_dict["host_server_info_file"] = host_server_info_file
443
452
  container_dict["mount_source"] = mount_source
444
453
  container_dict["mount_target"] = mount_target
445
454
 
446
455
  logger.debug(
447
- f"Fluent container timeout: {timeout}, container_grpc_port: {container_grpc_port}, "
456
+ f"Fluent container container_grpc_port: {container_grpc_port}, "
448
457
  f"host_server_info_file: '{host_server_info_file}', "
449
458
  f"remove_server_info_file: {remove_server_info_file}"
450
459
  )
@@ -460,7 +469,10 @@ def configure_container_dict(
460
469
 
461
470
 
462
471
  def start_fluent_container(
463
- args: List[str], container_dict: dict | None = None
472
+ args: List[str],
473
+ container_dict: dict | None = None,
474
+ start_timeout: int = 60,
475
+ compose_config: ComposeConfig | None = None,
464
476
  ) -> tuple[int, str, Any]:
465
477
  """Start a Fluent container.
466
478
 
@@ -470,6 +482,11 @@ def start_fluent_container(
470
482
  List of Fluent launch arguments.
471
483
  container_dict : dict, optional
472
484
  Dictionary with Docker container configuration.
485
+ start_timeout : int, optional
486
+ Timeout in seconds for the container to start. If not specified, it defaults to 60
487
+ seconds.
488
+ compose_config : ComposeConfig, optional
489
+ Configuration for Docker Compose, if using Docker Compose to launch the container.
473
490
 
474
491
  Returns
475
492
  -------
@@ -491,10 +508,16 @@ def start_fluent_container(
491
508
  :func:`~ansys.fluent.core.launcher.launcher.launch_fluent()`.
492
509
  """
493
510
 
511
+ compose_config = compose_config if compose_config else ComposeConfig()
512
+
494
513
  if container_dict is None:
495
514
  container_dict = {}
496
515
 
497
- container_vars = configure_container_dict(args, **container_dict)
516
+ container_vars = configure_container_dict(
517
+ args,
518
+ compose_config=compose_config,
519
+ **container_dict,
520
+ )
498
521
 
499
522
  (
500
523
  config_dict,
@@ -503,12 +526,23 @@ def start_fluent_container(
503
526
  host_server_info_file,
504
527
  remove_server_info_file,
505
528
  ) = container_vars
529
+ launch_string = " ".join(config_dict["command"])
530
+
531
+ if timeout:
532
+ logger.warning(
533
+ "launch_fluent(start_timeout) overridden by configure_container_dict(timeout) value."
534
+ )
535
+ start_timeout = timeout
536
+ del timeout
506
537
 
507
538
  try:
508
- if is_compose():
539
+ if compose_config.is_compose:
509
540
  config_dict["fluent_port"] = port
510
541
 
511
- compose_container = ComposeBasedLauncher(container_dict=config_dict)
542
+ compose_container = ComposeBasedLauncher(
543
+ compose_config=compose_config,
544
+ container_dict=config_dict,
545
+ )
512
546
 
513
547
  if not compose_container.check_image_exists():
514
548
  logger.debug(
@@ -536,18 +570,36 @@ def start_fluent_container(
536
570
  config_dict.pop("fluent_image"), **config_dict
537
571
  )
538
572
 
573
+ logger.debug(
574
+ f"Waiting for Fluent container for up to {start_timeout} seconds..."
575
+ )
576
+
539
577
  success = timeout_loop(
540
- lambda: host_server_info_file.stat().st_mtime > last_mtime, timeout
578
+ lambda: host_server_info_file.stat().st_mtime > last_mtime,
579
+ start_timeout,
541
580
  )
542
581
 
543
582
  if not success:
544
- raise TimeoutError(
545
- "Fluent container launch has timed out, stop container manually."
546
- )
583
+ try:
584
+ container.stop()
585
+ except Exception as stop_ex:
586
+ logger.error(f"Failed to stop container: {stop_ex}")
587
+ raise TimeoutError(
588
+ f"Fluent container launch has timed out after {start_timeout} seconds. "
589
+ f"Additionally, stopping the container failed: {stop_ex}"
590
+ ) from stop_ex
591
+ else:
592
+ raise TimeoutError(
593
+ f"Fluent container launch has timed out after {start_timeout} seconds."
594
+ " The container was stopped."
595
+ )
547
596
  else:
548
597
  _, _, password = _parse_server_info_file(str(host_server_info_file))
549
598
 
550
599
  return port, password, container
600
+ except Exception as ex:
601
+ logger.error(f"Exception caught - {type(ex).__name__}: {ex}")
602
+ raise LaunchFluentError(launch_string) from ex
551
603
  finally:
552
604
  if remove_server_info_file and host_server_info_file.exists():
553
605
  host_server_info_file.unlink()
@@ -23,7 +23,6 @@
23
23
  """Provides a module for enums used in the PyFluent."""
24
24
 
25
25
  from enum import Enum
26
- import os
27
26
  import warnings
28
27
 
29
28
  from ansys.fluent.core.exceptions import DisallowedValuesError
@@ -267,11 +266,12 @@ def _get_fluent_launch_mode(start_container, container_dict, scheduler_options):
267
266
  fluent_launch_mode: LaunchMode
268
267
  Fluent launch mode.
269
268
  """
269
+ from ansys.fluent.core import config
270
+
270
271
  if pypim.is_configured():
271
272
  fluent_launch_mode = LaunchMode.PIM
272
273
  elif start_container is True or (
273
- start_container is None
274
- and (container_dict or os.getenv("PYFLUENT_LAUNCH_CONTAINER") == "1")
274
+ start_container is None and (container_dict or config.launch_fluent_container)
275
275
  ):
276
276
  fluent_launch_mode = LaunchMode.CONTAINER
277
277
  # Currently, only Slurm scheduler is supported and within SlurmLauncher we check the value of the scheduler
@@ -344,6 +344,7 @@ def _get_standalone_launch_fluent_version(argvals) -> FluentVersion | None:
344
344
  FluentVersion, optional
345
345
  Fluent version or ``None``
346
346
  """
347
+ from ansys.fluent.core import config
347
348
 
348
349
  # Look for Fluent version in the following order:
349
350
  # 1. product_version parameter passed with launch_fluent
@@ -357,7 +358,7 @@ def _get_standalone_launch_fluent_version(argvals) -> FluentVersion | None:
357
358
 
358
359
  # (DEV) if "PYFLUENT_FLUENT_ROOT" environment variable is defined, we cannot
359
360
  # determine the Fluent version, so returning None.
360
- if os.getenv("PYFLUENT_FLUENT_ROOT"):
361
+ if config.fluent_root:
361
362
  return None
362
363
 
363
364
  # 2. the latest ANSYS version from AWP_ROOT environment variables
@@ -103,7 +103,7 @@ def _show_gui_to_ui_mode(old_arg_val, **kwds):
103
103
  return UIMode.NO_GUI
104
104
  elif container_dict:
105
105
  return UIMode.NO_GUI
106
- elif os.getenv("PYFLUENT_LAUNCH_CONTAINER") == "1":
106
+ elif pyfluent.config.launch_fluent_container:
107
107
  return UIMode.NO_GUI
108
108
  else:
109
109
  return UIMode.GUI
@@ -174,6 +174,8 @@ def launch_fluent(
174
174
  start_watchdog: bool | None = None,
175
175
  scheduler_options: dict | None = None,
176
176
  file_transfer_service: Any | None = None,
177
+ use_docker_compose: bool | None = None,
178
+ use_podman_compose: bool | None = None,
177
179
  ) -> Meshing | PureMeshing | Solver | SolverIcing | SlurmFuture | dict:
178
180
  """Launch Fluent locally in server mode or connect to a running Fluent server
179
181
  instance.
@@ -299,6 +301,10 @@ def launch_fluent(
299
301
  specified in a similar manner to Fluent's scheduler options.
300
302
  file_transfer_service : optional
301
303
  File transfer service. Uploads/downloads files to/from the server.
304
+ use_docker_compose: bool
305
+ Whether to use Docker Compose to launch Fluent.
306
+ use_podman_compose: bool
307
+ Whether to use Podman Compose to launch Fluent.
302
308
 
303
309
  Returns
304
310
  -------
@@ -312,6 +318,8 @@ def launch_fluent(
312
318
  ------
313
319
  UnexpectedKeywordArgument
314
320
  If an unexpected keyword argument is provided.
321
+ ValueError
322
+ If both ``use_docker_compose`` and ``use_podman_compose`` are set to ``True``.
315
323
 
316
324
  Notes
317
325
  -----
@@ -322,6 +330,14 @@ def launch_fluent(
322
330
  if env is None:
323
331
  env = {}
324
332
 
333
+ if use_docker_compose and use_podman_compose:
334
+ raise ValueError(
335
+ "Cannot use both 'use_docker_compose' and 'use_podman_compose' at the same time."
336
+ )
337
+
338
+ if start_timeout is None:
339
+ start_timeout = pyfluent.config.launch_fluent_timeout
340
+
325
341
  def _mode_to_launcher_type(fluent_launch_mode: LaunchMode):
326
342
  launcher_mode_type = {
327
343
  LaunchMode.CONTAINER: DockerLauncher,
@@ -346,7 +362,7 @@ def launch_fluent(
346
362
  )
347
363
  common_args = launch_fluent_args.intersection(launcher_type_args)
348
364
  launcher_argvals = {arg: val for arg, val in argvals.items() if arg in common_args}
349
- if pyfluent.START_WATCHDOG is False:
365
+ if pyfluent.config.start_watchdog is False:
350
366
  launcher_argvals["start_watchdog"] = False
351
367
  launcher = launcher_type(**launcher_argvals)
352
368
  return launcher()
@@ -30,19 +30,61 @@ import socket
30
30
  import subprocess
31
31
  import time
32
32
  from typing import Any, Dict
33
+ import warnings
33
34
 
34
35
  from ansys.fluent.core.exceptions import InvalidArgument
36
+ from ansys.fluent.core.pyfluent_warnings import PyFluentDeprecationWarning
35
37
  from ansys.fluent.core.utils.networking import find_remoting_ip
36
38
 
37
39
  logger = logging.getLogger("pyfluent.launcher")
38
40
 
39
41
 
40
- def is_compose() -> bool:
41
- """Check if the Fluent launch is through compose."""
42
- return (
43
- os.getenv("PYFLUENT_USE_DOCKER_COMPOSE") == "1"
44
- or os.getenv("PYFLUENT_USE_PODMAN_COMPOSE") == "1"
45
- )
42
+ class ComposeConfig:
43
+ """Configuration for Docker or Podman Compose usage in PyFluent."""
44
+
45
+ def __init__(
46
+ self,
47
+ use_docker_compose: bool | None = None,
48
+ use_podman_compose: bool | None = None,
49
+ ):
50
+ from ansys.fluent.core import config
51
+
52
+ self._env_docker = config.use_docker_compose
53
+ self._env_podman = config.use_podman_compose
54
+
55
+ self._use_docker = use_docker_compose
56
+ self._use_podman = use_podman_compose
57
+
58
+ if use_docker_compose is None and self._env_docker:
59
+ self._warn_env_deprecated()
60
+ if use_podman_compose is None and self._env_podman:
61
+ self._warn_env_deprecated()
62
+
63
+ def _warn_env_deprecated(self):
64
+ warnings.warn(
65
+ (
66
+ "The environment variables 'PYFLUENT_USE_DOCKER_COMPOSE' and "
67
+ "'PYFLUENT_USE_PODMAN_COMPOSE' are deprecated. "
68
+ "Use the 'use_docker_compose' and 'use_podman_compose' parameters instead."
69
+ ),
70
+ category=PyFluentDeprecationWarning,
71
+ stacklevel=3,
72
+ )
73
+
74
+ @property
75
+ def use_docker_compose(self) -> bool:
76
+ """Check if Docker Compose is configured to be used."""
77
+ return self._use_docker if self._use_docker is not None else self._env_docker
78
+
79
+ @property
80
+ def use_podman_compose(self) -> bool:
81
+ """Check if Podman Compose is configured to be used."""
82
+ return self._use_podman if self._use_podman is not None else self._env_podman
83
+
84
+ @property
85
+ def is_compose(self) -> bool:
86
+ """Check if either Docker Compose or Podman Compose is configured to be used."""
87
+ return self.use_docker_compose or self.use_podman_compose
46
88
 
47
89
 
48
90
  def is_windows():
@@ -60,7 +102,8 @@ def _get_subprocess_kwargs_for_fluent(env: Dict[str, Any], argvals) -> Dict[str,
60
102
  kwargs.update(stdout=subprocess.PIPE)
61
103
  else:
62
104
  kwargs.update(
63
- stdout=pyfluent.LAUNCH_FLUENT_STDOUT, stderr=pyfluent.LAUNCH_FLUENT_STDERR
105
+ stdout=pyfluent.config.launch_fluent_stdout,
106
+ stderr=pyfluent.config.launch_fluent_stderr,
64
107
  )
65
108
  if is_windows():
66
109
  kwargs.update(shell=True, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
@@ -70,26 +113,31 @@ def _get_subprocess_kwargs_for_fluent(env: Dict[str, Any], argvals) -> Dict[str,
70
113
  fluent_env.update({k: str(v) for k, v in env.items()})
71
114
  fluent_env["REMOTING_THROW_LAST_TUI_ERROR"] = "1"
72
115
  fluent_env["REMOTING_THROW_LAST_SETTINGS_ERROR"] = "1"
73
- if pyfluent.CLEAR_FLUENT_PARA_ENVS:
116
+ if pyfluent.config.clear_fluent_para_envs:
74
117
  fluent_env.pop("PARA_NPROCS", None)
75
118
  fluent_env.pop("PARA_MESH_NPROCS", None)
76
119
 
77
- if pyfluent.LAUNCH_FLUENT_IP:
78
- fluent_env["REMOTING_SERVER_ADDRESS"] = pyfluent.LAUNCH_FLUENT_IP
120
+ if pyfluent.config.launch_fluent_ip:
121
+ fluent_env["REMOTING_SERVER_ADDRESS"] = pyfluent.config.launch_fluent_ip
79
122
 
80
- if pyfluent.LAUNCH_FLUENT_PORT:
81
- fluent_env["REMOTING_PORTS"] = f"{pyfluent.LAUNCH_FLUENT_PORT}/portspan=2"
123
+ if pyfluent.config.launch_fluent_port:
124
+ fluent_env["REMOTING_PORTS"] = (
125
+ f"{pyfluent.config.launch_fluent_port}/portspan=2"
126
+ )
82
127
 
83
- if pyfluent.LAUNCH_FLUENT_SKIP_PASSWORD_CHECK:
128
+ if pyfluent.config.launch_fluent_skip_password_check:
84
129
  fluent_env["FLUENT_LAUNCHED_FROM_PYFLUENT"] = "1"
85
130
 
86
131
  if not is_slurm:
87
- if pyfluent.INFER_REMOTING_IP and "REMOTING_SERVER_ADDRESS" not in fluent_env:
132
+ if (
133
+ pyfluent.config.infer_remoting_ip
134
+ and "REMOTING_SERVER_ADDRESS" not in fluent_env
135
+ ):
88
136
  remoting_ip = find_remoting_ip()
89
137
  if remoting_ip:
90
138
  fluent_env["REMOTING_SERVER_ADDRESS"] = remoting_ip
91
139
 
92
- if not pyfluent.FLUENT_AUTOMATIC_TRANSCRIPT:
140
+ if not pyfluent.config.fluent_automatic_transcript:
93
141
  fluent_env["FLUENT_NO_AUTOMATIC_TRANSCRIPT"] = "1"
94
142
 
95
143
  kwargs.update(env=fluent_env)