ansys-fluent-core 0.31.dev1__py3-none-any.whl → 0.31.1__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 (59) hide show
  1. ansys/fluent/core/__init__.py +11 -3
  2. ansys/fluent/core/codegen/settingsgen.py +6 -0
  3. ansys/fluent/core/codegen/tuigen.py +1 -2
  4. ansys/fluent/core/docker/docker_compose.py +243 -0
  5. ansys/fluent/core/field_data_interfaces.py +6 -0
  6. ansys/fluent/core/file_session.py +158 -128
  7. ansys/fluent/core/filereader/data_file.py +11 -0
  8. ansys/fluent/core/filereader/pre_processor.py +22 -0
  9. ansys/fluent/core/fluent_connection.py +48 -20
  10. ansys/fluent/core/generated/api_tree/api_objects.json +1 -1
  11. ansys/fluent/core/generated/datamodel_231/flicing.py +35 -35
  12. ansys/fluent/core/generated/datamodel_231/meshing.py +189 -189
  13. ansys/fluent/core/generated/datamodel_232/flicing.py +35 -35
  14. ansys/fluent/core/generated/datamodel_232/meshing.py +237 -237
  15. ansys/fluent/core/generated/datamodel_241/flicing.py +45 -45
  16. ansys/fluent/core/generated/datamodel_241/meshing.py +295 -295
  17. ansys/fluent/core/generated/datamodel_242/flicing.py +60 -60
  18. ansys/fluent/core/generated/datamodel_242/meshing.py +285 -285
  19. ansys/fluent/core/generated/datamodel_242/part_management.py +6 -6
  20. ansys/fluent/core/generated/datamodel_251/flicing.py +55 -55
  21. ansys/fluent/core/generated/datamodel_251/meshing.py +370 -370
  22. ansys/fluent/core/generated/datamodel_251/part_management.py +6 -6
  23. ansys/fluent/core/generated/datamodel_252/flicing.py +55 -55
  24. ansys/fluent/core/generated/datamodel_252/meshing.py +790 -428
  25. ansys/fluent/core/generated/datamodel_252/part_management.py +10 -10
  26. ansys/fluent/core/generated/datamodel_252/preferences.py +1 -1
  27. ansys/fluent/core/generated/fluent_version_252.py +4 -4
  28. ansys/fluent/core/generated/meshing/tui_252.py +1133 -1178
  29. ansys/fluent/core/generated/solver/settings_252.py +2241 -1649
  30. ansys/fluent/core/generated/solver/settings_252.pyi +1785 -1430
  31. ansys/fluent/core/generated/solver/settings_builtin.pyi +104 -0
  32. ansys/fluent/core/generated/solver/tui_252.py +2174 -2005
  33. ansys/fluent/core/launcher/container_launcher.py +39 -8
  34. ansys/fluent/core/launcher/fluent_container.py +61 -22
  35. ansys/fluent/core/launcher/launcher.py +24 -13
  36. ansys/fluent/core/launcher/launcher_utils.py +8 -0
  37. ansys/fluent/core/launcher/process_launch_string.py +2 -6
  38. ansys/fluent/core/launcher/slurm_launcher.py +1 -0
  39. ansys/fluent/core/report.py +2 -0
  40. ansys/fluent/core/services/deprecated_field_data.py +74 -46
  41. ansys/fluent/core/services/field_data.py +104 -69
  42. ansys/fluent/core/services/reduction.py +55 -66
  43. ansys/fluent/core/services/solution_variables.py +9 -1
  44. ansys/fluent/core/session.py +15 -12
  45. ansys/fluent/core/session_meshing.py +3 -0
  46. ansys/fluent/core/session_solver.py +20 -43
  47. ansys/fluent/core/session_utilities.py +429 -0
  48. ansys/fluent/core/solver/flobject.py +28 -0
  49. ansys/fluent/core/utils/deprecate.py +46 -0
  50. ansys/fluent/core/utils/file_transfer_service.py +19 -3
  51. ansys/fluent/core/utils/fluent_version.py +42 -11
  52. ansys/fluent/core/variable_strategies/__init__.py +29 -0
  53. ansys/fluent/core/variable_strategies/expr.py +186 -0
  54. ansys/fluent/core/variable_strategies/field.py +186 -0
  55. ansys/fluent/core/variable_strategies/svar.py +61 -0
  56. {ansys_fluent_core-0.31.dev1.dist-info → ansys_fluent_core-0.31.1.dist-info}/METADATA +9 -6
  57. {ansys_fluent_core-0.31.dev1.dist-info → ansys_fluent_core-0.31.1.dist-info}/RECORD +59 -53
  58. {ansys_fluent_core-0.31.dev1.dist-info → ansys_fluent_core-0.31.1.dist-info}/WHEEL +1 -1
  59. {ansys_fluent_core-0.31.dev1.dist-info → ansys_fluent_core-0.31.1.dist-info/licenses}/LICENSE +0 -0
@@ -43,8 +43,12 @@ from ansys.fluent.core.field_data_interfaces import (
43
43
  _ReturnFieldData,
44
44
  )
45
45
  from ansys.fluent.core.filereader.case_file import CaseFile
46
- from ansys.fluent.core.filereader.data_file import DataFile
47
- from ansys.fluent.core.utils.deprecate import deprecate_argument, deprecate_arguments
46
+ from ansys.fluent.core.filereader.data_file import (
47
+ DataFile,
48
+ _to_scalar_field_name,
49
+ _to_vector_field_name,
50
+ )
51
+ from ansys.fluent.core.utils.deprecate import all_deprecators
48
52
 
49
53
 
50
54
  class InvalidMultiPhaseFieldName(ValueError):
@@ -113,7 +117,7 @@ class TransactionFieldData:
113
117
  )
114
118
  ]
115
119
  return self._returned_data._scalar_data(
116
- kwargs.get("field_name"),
120
+ _to_scalar_field_name(kwargs.get("field_name")),
117
121
  kwargs.get("surfaces"),
118
122
  self.get_surface_ids(kwargs.get("surfaces")),
119
123
  scalar_field_data,
@@ -137,7 +141,7 @@ class TransactionFieldData:
137
141
  ) -> Dict[int | str, np.array]:
138
142
  vector_field_data = self.data[(("type", "vector-field"),)]
139
143
  return self._returned_data._vector_data(
140
- kwargs.get("field_name"),
144
+ _to_vector_field_name(kwargs.get("field_name")),
141
145
  kwargs.get("surfaces"),
142
146
  self.get_surface_ids(kwargs.get("surfaces")),
143
147
  vector_field_data,
@@ -151,10 +155,16 @@ class TransactionFieldData:
151
155
  zones = []
152
156
  del zones
153
157
  pathlines_data = self.data[
154
- (("type", "pathlines-field"), ("field", kwargs.get("field_name")))
158
+ (
159
+ ("type", "pathlines-field"),
160
+ (
161
+ "field",
162
+ _to_scalar_field_name(kwargs.get("field_name")),
163
+ ),
164
+ )
155
165
  ]
156
166
  return self._returned_data._pathlines_data(
157
- kwargs.get("field_name"),
167
+ _to_scalar_field_name(kwargs.get("field_name")),
158
168
  kwargs.get("surfaces"),
159
169
  self.get_surface_ids(kwargs.get("surfaces")),
160
170
  pathlines_data,
@@ -223,21 +233,23 @@ class Transaction(FieldTransaction):
223
233
  surfaces=surfaces,
224
234
  )
225
235
 
226
- @deprecate_argument(
227
- old_arg="surface_names",
228
- new_arg="surfaces",
229
- converter=lambda old_arg_val: old_arg_val,
230
- warning_cls=PyFluentDeprecationWarning,
231
- )
232
- @deprecate_argument(
233
- old_arg="surface_ids",
234
- new_arg="surfaces",
235
- converter=lambda old_arg_val: old_arg_val,
236
- warning_cls=PyFluentDeprecationWarning,
237
- )
238
- @deprecate_arguments(
239
- converter=_data_type_convertor,
240
- warning_cls=PyFluentDeprecationWarning,
236
+ @all_deprecators(
237
+ deprecate_arg_mappings=[
238
+ {
239
+ "old_arg": "surface_names",
240
+ "new_arg": "surfaces",
241
+ "converter": lambda old_arg_val: old_arg_val,
242
+ },
243
+ {
244
+ "old_arg": "surface_ids",
245
+ "new_arg": "surfaces",
246
+ "converter": lambda old_arg_val: old_arg_val,
247
+ },
248
+ ],
249
+ data_type_converter=_data_type_convertor,
250
+ deprecated_version="v0.25.0",
251
+ deprecated_reason="Old arguments 'surface_ids' and 'surface_names' are deprecated. Use 'surfaces' instead.",
252
+ warn_message="'add_surfaces_request' is deprecated, use 'add_requests' instead.",
241
253
  )
242
254
  def add_surfaces_request(
243
255
  self,
@@ -258,10 +270,6 @@ class Transaction(FieldTransaction):
258
270
  -------
259
271
  None
260
272
  """
261
- warnings.warn(
262
- "'add_surfaces_request' is deprecated, use 'add_requests' instead",
263
- PyFluentDeprecationWarning,
264
- )
265
273
  self._add_surfaces_request(data_types=data_types, surfaces=surfaces)
266
274
 
267
275
  def _add_surfaces_request(
@@ -285,17 +293,23 @@ class Transaction(FieldTransaction):
285
293
  )
286
294
  )
287
295
 
288
- @deprecate_argument(
289
- old_arg="surface_names",
290
- new_arg="surfaces",
291
- converter=lambda old_arg_val: old_arg_val,
292
- warning_cls=PyFluentDeprecationWarning,
293
- )
294
- @deprecate_argument(
295
- old_arg="surface_ids",
296
- new_arg="surfaces",
297
- converter=lambda old_arg_val: old_arg_val,
298
- warning_cls=PyFluentDeprecationWarning,
296
+ @all_deprecators(
297
+ deprecate_arg_mappings=[
298
+ {
299
+ "old_arg": "surface_names",
300
+ "new_arg": "surfaces",
301
+ "converter": lambda old_arg_val: old_arg_val,
302
+ },
303
+ {
304
+ "old_arg": "surface_ids",
305
+ "new_arg": "surfaces",
306
+ "converter": lambda old_arg_val: old_arg_val,
307
+ },
308
+ ],
309
+ data_type_converter=None,
310
+ deprecated_version="v0.25.0",
311
+ deprecated_reason="Old arguments 'surface_ids' and 'surface_names' are deprecated. Use 'surfaces' instead.",
312
+ warn_message="'add_scalar_fields_request' is deprecated, use 'add_requests' instead.",
299
313
  )
300
314
  def add_scalar_fields_request(
301
315
  self,
@@ -328,10 +342,6 @@ class Transaction(FieldTransaction):
328
342
  InvalidMultiPhaseFieldName
329
343
  If field name does not have prefix ``phase-`` for multi-phase cases.
330
344
  """
331
- warnings.warn(
332
- "'add_scalar_fields_request' is deprecated, use 'add_requests' instead",
333
- PyFluentDeprecationWarning,
334
- )
335
345
  self._add_scalar_fields_request(
336
346
  field_name=field_name,
337
347
  surfaces=surfaces,
@@ -360,17 +370,23 @@ class Transaction(FieldTransaction):
360
370
  Transaction._ScalarFieldTransaction(field_name, surface_ids)
361
371
  )
362
372
 
363
- @deprecate_argument(
364
- old_arg="surface_names",
365
- new_arg="surfaces",
366
- converter=lambda old_arg_val: old_arg_val,
367
- warning_cls=PyFluentDeprecationWarning,
368
- )
369
- @deprecate_argument(
370
- old_arg="surface_ids",
371
- new_arg="surfaces",
372
- converter=lambda old_arg_val: old_arg_val,
373
- warning_cls=PyFluentDeprecationWarning,
373
+ @all_deprecators(
374
+ deprecate_arg_mappings=[
375
+ {
376
+ "old_arg": "surface_names",
377
+ "new_arg": "surfaces",
378
+ "converter": lambda old_arg_val: old_arg_val,
379
+ },
380
+ {
381
+ "old_arg": "surface_ids",
382
+ "new_arg": "surfaces",
383
+ "converter": lambda old_arg_val: old_arg_val,
384
+ },
385
+ ],
386
+ data_type_converter=None,
387
+ deprecated_version="v0.25.0",
388
+ deprecated_reason="Old arguments 'surface_ids' and 'surface_names' are deprecated. Use 'surfaces' instead.",
389
+ warn_message="'add_vector_fields_request' is deprecated, use 'add_requests' instead.",
374
390
  )
375
391
  def add_vector_fields_request(
376
392
  self,
@@ -395,10 +411,6 @@ class Transaction(FieldTransaction):
395
411
  InvalidMultiPhaseFieldName
396
412
  If field name does not have prefix ``phase-`` for multi-phase cases.
397
413
  """
398
- warnings.warn(
399
- "'add_vector_fields_request' is deprecated, use 'add_requests' instead",
400
- PyFluentDeprecationWarning,
401
- )
402
414
  self._add_vector_fields_request(field_name=field_name, surfaces=surfaces)
403
415
 
404
416
  def _add_vector_fields_request(
@@ -420,17 +432,23 @@ class Transaction(FieldTransaction):
420
432
  Transaction._VectorFieldTransaction(field_name, surface_ids)
421
433
  )
422
434
 
423
- @deprecate_argument(
424
- old_arg="surface_names",
425
- new_arg="surfaces",
426
- converter=lambda old_arg_val: old_arg_val,
427
- warning_cls=PyFluentDeprecationWarning,
428
- )
429
- @deprecate_argument(
430
- old_arg="surface_ids",
431
- new_arg="surfaces",
432
- converter=lambda old_arg_val: old_arg_val,
433
- warning_cls=PyFluentDeprecationWarning,
435
+ @all_deprecators(
436
+ deprecate_arg_mappings=[
437
+ {
438
+ "old_arg": "surface_names",
439
+ "new_arg": "surfaces",
440
+ "converter": lambda old_arg_val: old_arg_val,
441
+ },
442
+ {
443
+ "old_arg": "surface_ids",
444
+ "new_arg": "surfaces",
445
+ "converter": lambda old_arg_val: old_arg_val,
446
+ },
447
+ ],
448
+ data_type_converter=None,
449
+ deprecated_version="v0.25.0",
450
+ deprecated_reason="Old arguments 'surface_ids' and 'surface_names' are deprecated. Use 'surfaces' instead.",
451
+ warn_message="Pathlines are not supported.",
434
452
  )
435
453
  def add_pathlines_fields_request(
436
454
  self,
@@ -604,23 +622,28 @@ class FileFieldData(FieldDataSource):
604
622
  surfaces=surfaces,
605
623
  )
606
624
 
607
- @deprecate_argument(
608
- old_arg="surface_name",
609
- new_arg="surfaces",
610
- converter=lambda old_arg_val: [old_arg_val],
611
- warning_cls=PyFluentDeprecationWarning,
612
- )
613
- @deprecate_argument(
614
- old_arg="surface_ids",
615
- new_arg="surfaces",
616
- converter=lambda old_arg_val: old_arg_val,
617
- warning_cls=PyFluentDeprecationWarning,
618
- )
619
- @deprecate_argument(
620
- old_arg="data_type",
621
- new_arg="data_types",
622
- converter=lambda old_arg_val: [old_arg_val] if old_arg_val else None,
623
- warning_cls=PyFluentDeprecationWarning,
625
+ @all_deprecators(
626
+ deprecate_arg_mappings=[
627
+ {
628
+ "old_arg": "surface_names",
629
+ "new_arg": "surfaces",
630
+ "converter": lambda old_arg_val: [old_arg_val],
631
+ },
632
+ {
633
+ "old_arg": "surface_ids",
634
+ "new_arg": "surfaces",
635
+ "converter": lambda old_arg_val: old_arg_val,
636
+ },
637
+ {
638
+ "old_arg": "data_type",
639
+ "new_arg": "data_types",
640
+ "converter": lambda old_arg_val: [old_arg_val] if old_arg_val else None,
641
+ },
642
+ ],
643
+ data_type_converter=None,
644
+ deprecated_version="v0.25.0",
645
+ deprecated_reason="Old arguments 'surface_ids', 'surface_names' and 'data_type' are deprecated. Use 'surfaces' and 'data_types' instead.",
646
+ warn_message="'get_surface_data' is deprecated, use 'get_field_data' instead.",
624
647
  )
625
648
  def get_surface_data(
626
649
  self,
@@ -646,10 +669,6 @@ class FileFieldData(FieldDataSource):
646
669
  If surface IDs are provided as input, a dictionary containing a map of surface IDs to face
647
670
  vertices, connectivity data, and normal or centroid data is returned.
648
671
  """
649
- warnings.warn(
650
- "'get_surface_data' is deprecated, use 'get_field_data' instead",
651
- PyFluentDeprecationWarning,
652
- )
653
672
  return self._get_surface_data(
654
673
  data_types=data_types,
655
674
  surfaces=surfaces,
@@ -697,17 +716,23 @@ class FileFieldData(FieldDataSource):
697
716
  i = end
698
717
  return faces_data
699
718
 
700
- @deprecate_argument(
701
- old_arg="surface_name",
702
- new_arg="surfaces",
703
- converter=lambda old_arg_val: [old_arg_val],
704
- warning_cls=PyFluentDeprecationWarning,
705
- )
706
- @deprecate_argument(
707
- old_arg="surface_ids",
708
- new_arg="surfaces",
709
- converter=lambda old_arg_val: old_arg_val,
710
- warning_cls=PyFluentDeprecationWarning,
719
+ @all_deprecators(
720
+ deprecate_arg_mappings=[
721
+ {
722
+ "old_arg": "surface_names",
723
+ "new_arg": "surfaces",
724
+ "converter": lambda old_arg_val: [old_arg_val],
725
+ },
726
+ {
727
+ "old_arg": "surface_ids",
728
+ "new_arg": "surfaces",
729
+ "converter": lambda old_arg_val: old_arg_val,
730
+ },
731
+ ],
732
+ data_type_converter=None,
733
+ deprecated_version="v0.25.0",
734
+ deprecated_reason="Old arguments 'surface_ids' and 'surface_names' are deprecated. Use 'surfaces' instead.",
735
+ warn_message="'get_scalar_field_data' is deprecated, use 'get_field_data' instead.",
711
736
  )
712
737
  def get_scalar_field_data(
713
738
  self,
@@ -743,10 +768,6 @@ class FileFieldData(FieldDataSource):
743
768
  InvalidMultiPhaseFieldName
744
769
  If field name does not have prefix ``phase-`` for multi-phase cases.
745
770
  """
746
- warnings.warn(
747
- "'get_scalar_field_data' is deprecated, use 'get_field_data' instead",
748
- PyFluentDeprecationWarning,
749
- )
750
771
  return self._get_scalar_field_data(
751
772
  field_name=field_name,
752
773
  surfaces=surfaces,
@@ -781,17 +802,23 @@ class FileFieldData(FieldDataSource):
781
802
  for count, surface in enumerate(surfaces)
782
803
  }
783
804
 
784
- @deprecate_argument(
785
- old_arg="surface_name",
786
- new_arg="surfaces",
787
- converter=lambda old_arg_val: [old_arg_val],
788
- warning_cls=PyFluentDeprecationWarning,
789
- )
790
- @deprecate_argument(
791
- old_arg="surface_ids",
792
- new_arg="surfaces",
793
- converter=lambda old_arg_val: old_arg_val,
794
- warning_cls=PyFluentDeprecationWarning,
805
+ @all_deprecators(
806
+ deprecate_arg_mappings=[
807
+ {
808
+ "old_arg": "surface_names",
809
+ "new_arg": "surfaces",
810
+ "converter": lambda old_arg_val: [old_arg_val],
811
+ },
812
+ {
813
+ "old_arg": "surface_ids",
814
+ "new_arg": "surfaces",
815
+ "converter": lambda old_arg_val: old_arg_val,
816
+ },
817
+ ],
818
+ data_type_converter=None,
819
+ deprecated_version="v0.25.0",
820
+ deprecated_reason="Old arguments 'surface_ids' and 'surface_names' are deprecated. Use 'surfaces' instead.",
821
+ warn_message="'get_vector_field_data' is deprecated, use 'get_field_data' instead.",
795
822
  )
796
823
  def get_vector_field_data(
797
824
  self,
@@ -821,10 +848,6 @@ class FileFieldData(FieldDataSource):
821
848
  InvalidMultiPhaseFieldName
822
849
  If field name does not have prefix ``phase-`` for multi-phase cases.
823
850
  """
824
- warnings.warn(
825
- "'get_vector_field_data' is deprecated, use 'get_field_data' instead",
826
- PyFluentDeprecationWarning,
827
- )
828
851
  return self._get_vector_field_data(
829
852
  field_name=field_name,
830
853
  surfaces=surfaces,
@@ -835,6 +858,7 @@ class FileFieldData(FieldDataSource):
835
858
  field_name: str,
836
859
  surfaces: List[int | str],
837
860
  ):
861
+ field_name = _to_vector_field_name(field_name)
838
862
  surface_ids = self.get_surface_ids(surfaces=surfaces)
839
863
  if (
840
864
  field_name.lower() != "velocity"
@@ -859,17 +883,23 @@ class FileFieldData(FieldDataSource):
859
883
  for count, surface in enumerate(surfaces)
860
884
  }
861
885
 
862
- @deprecate_argument(
863
- old_arg="surface_name",
864
- new_arg="surfaces",
865
- converter=lambda old_arg_val: [old_arg_val],
866
- warning_cls=PyFluentDeprecationWarning,
867
- )
868
- @deprecate_argument(
869
- old_arg="surface_ids",
870
- new_arg="surfaces",
871
- converter=lambda old_arg_val: old_arg_val,
872
- warning_cls=PyFluentDeprecationWarning,
886
+ @all_deprecators(
887
+ deprecate_arg_mappings=[
888
+ {
889
+ "old_arg": "surface_names",
890
+ "new_arg": "surfaces",
891
+ "converter": lambda old_arg_val: [old_arg_val],
892
+ },
893
+ {
894
+ "old_arg": "surface_ids",
895
+ "new_arg": "surfaces",
896
+ "converter": lambda old_arg_val: old_arg_val,
897
+ },
898
+ ],
899
+ data_type_converter=None,
900
+ deprecated_version="v0.25.0",
901
+ deprecated_reason="Old arguments 'surface_ids' and 'surface_names' are deprecated. Use 'surfaces' instead.",
902
+ warn_message="Pathlines are not supported.",
873
903
  )
874
904
  def get_pathlines_field_data(
875
905
  self,
@@ -52,6 +52,16 @@ except ModuleNotFoundError as exc:
52
52
  "Missing dependencies, use 'pip install ansys-fluent-core[reader]' to install them."
53
53
  ) from exc
54
54
 
55
+ from ansys.fluent.core.variable_strategies import (
56
+ FluentFieldDataNamingStrategy as vector_naming,
57
+ )
58
+ from ansys.fluent.core.variable_strategies import (
59
+ FluentSVarNamingStrategy as scalar_naming,
60
+ )
61
+
62
+ _to_scalar_field_name = scalar_naming().to_string if scalar_naming else lambda s: s
63
+ _to_vector_field_name = vector_naming().to_string if vector_naming else lambda s: s
64
+
55
65
 
56
66
  class DataFile:
57
67
  """Class to read a Fluent case file.
@@ -186,6 +196,7 @@ class DataFile:
186
196
  -------
187
197
  Numpy array containing scalar field data for a particular phase, field and surface.
188
198
  """
199
+ field_name = _to_scalar_field_name(field_name)
189
200
  if ":" in field_name:
190
201
  field_name = field_name.split(":")[1]
191
202
  min_id, max_id = self._case_file_handle.get_mesh().get_surface_locs(surface_id)
@@ -1,3 +1,25 @@
1
+ # Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
1
23
  """
2
24
  Pre-processor for XML content from Fluent project files.
3
25
  """
@@ -42,6 +42,7 @@ import weakref
42
42
  import grpc
43
43
 
44
44
  import ansys.fluent.core as pyfluent
45
+ from ansys.fluent.core.launcher.launcher_utils import is_compose
45
46
  from ansys.fluent.core.pyfluent_warnings import PyFluentDeprecationWarning
46
47
  from ansys.fluent.core.services import service_creator
47
48
  from ansys.fluent.core.services.app_utilities import (
@@ -150,7 +151,12 @@ def get_container(container_id_or_name: str) -> bool | ContainerT | None:
150
151
  container = docker_client.containers.get(container_id_or_name)
151
152
  except _docker().errors.NotFound: # NotFound is a child from DockerException
152
153
  return False
153
- except _docker().errors.DockerException as exc:
154
+ # DockerException is the most common exception we can get here.
155
+ # However, in some system setup, we get ReadTimeoutError from urllib3 library
156
+ # (https://github.com/ansys/pyfluent/issues/3425).
157
+ # As urllib3 is not a direct dependency of PyFluent, we don't want to import it here,
158
+ # hence we catch the generic Exception.
159
+ except Exception as exc:
154
160
  logger.info(f"{type(exc).__name__}: {exc}")
155
161
  return None
156
162
  return container
@@ -372,6 +378,7 @@ class FluentConnection:
372
378
  file_transfer_service: Any | None = None,
373
379
  slurm_job_id: str | None = None,
374
380
  inside_container: bool | None = None,
381
+ container: ContainerT | None = None,
375
382
  ):
376
383
  """Initialize a Session.
377
384
 
@@ -408,6 +415,9 @@ class FluentConnection:
408
415
  inside_container: bool, optional
409
416
  Whether the Fluent session that is being connected to
410
417
  is running inside a Docker container.
418
+ container: ContainerT, optional
419
+ The container instance if the Fluent session is running inside
420
+ a container.
411
421
 
412
422
  Raises
413
423
  ------
@@ -453,15 +463,16 @@ class FluentConnection:
453
463
  self._connection_interface.get_cortex_connection_properties()
454
464
  )
455
465
  self._cleanup_on_exit = cleanup_on_exit
456
-
466
+ self._container = container
457
467
  if (
458
468
  (inside_container is None or inside_container is True)
459
469
  and not remote_instance
460
470
  and cortex_host is not None
461
471
  ):
462
472
  logger.info("Checking if Fluent is running inside a container.")
463
- inside_container = get_container(cortex_host)
464
- logger.debug(f"get_container({cortex_host}): {inside_container}")
473
+ if not is_compose():
474
+ inside_container = get_container(cortex_host)
475
+ logger.debug(f"get_container({cortex_host}): {inside_container}")
465
476
  if inside_container is False:
466
477
  logger.info("Fluent is not running inside a container.")
467
478
  elif inside_container is None:
@@ -519,13 +530,18 @@ class FluentConnection:
519
530
  -----
520
531
  If the Fluent session is responsive, prefer using :func:`exit()` instead.
521
532
 
533
+ Raises
534
+ ------
535
+ subprocess.CalledProcessError
536
+ If the cleanup script fails to execute.
537
+
522
538
  Examples
523
539
  --------
524
540
  >>> import ansys.fluent.core as pyfluent
525
541
  >>> session = pyfluent.launch_fluent()
526
542
  >>> session.force_exit()
527
543
  """
528
- if self.connection_properties.inside_container:
544
+ if self.connection_properties.inside_container or is_compose():
529
545
  self._force_exit_container()
530
546
  elif self._remote_instance is not None:
531
547
  logger.error("Cannot execute cleanup script, Fluent running remotely.")
@@ -559,11 +575,15 @@ class FluentConnection:
559
575
  )
560
576
  cmd_list.append(cleanup_file_name)
561
577
  logger.debug(f"Cleanup command list = {cmd_list}")
562
- subprocess.Popen(
578
+ result = subprocess.run(
563
579
  cmd_list,
564
580
  stdout=subprocess.DEVNULL,
565
581
  stderr=subprocess.DEVNULL,
582
+ timeout=120,
583
+ check=True,
566
584
  )
585
+ if result.returncode != 0:
586
+ raise subprocess.CalledProcessError(result.returncode, cmd_list)
567
587
  elif self._slurm_job_id:
568
588
  logger.debug("Fluent running inside Slurm, closing Slurm session...")
569
589
  self._close_slurm()
@@ -573,21 +593,28 @@ class FluentConnection:
573
593
  def _force_exit_container(self):
574
594
  """Immediately terminates the Fluent client running inside a container, losing
575
595
  unsaved progress and data."""
576
- container = self.connection_properties.inside_container
577
- container_id = self.connection_properties.cortex_host
578
- pid = self.connection_properties.fluent_host_pid
579
- cleanup_file_name = f"cleanup-fluent-{container_id}-{pid}.sh"
580
- logger.debug(f"Executing Fluent container cleanup script: {cleanup_file_name}")
581
- if get_container(container_id):
582
- try:
583
- container.exec_run(["bash", cleanup_file_name], detach=True)
584
- except _docker().errors.APIError as e:
585
- logger.info(f"{type(e).__name__}: {e}")
596
+ if is_compose() and self._container:
597
+ self._container.stop()
598
+ else:
599
+ container = self.connection_properties.inside_container
600
+ container_id = self.connection_properties.cortex_host
601
+ pid = self.connection_properties.fluent_host_pid
602
+ cleanup_file_name = f"cleanup-fluent-{container_id}-{pid}.sh"
603
+ logger.debug(
604
+ f"Executing Fluent container cleanup script: {cleanup_file_name}"
605
+ )
606
+ if get_container(container_id):
607
+ try:
608
+ container.exec_run(["bash", cleanup_file_name], detach=True)
609
+ except _docker().errors.APIError as e:
610
+ logger.info(f"{type(e).__name__}: {e}")
611
+ logger.debug(
612
+ "Caught Docker APIError, Docker container probably not running anymore."
613
+ )
614
+ else:
586
615
  logger.debug(
587
- "Caught Docker APIError, Docker container probably not running anymore."
616
+ "Container not found, cancelling cleanup script execution."
588
617
  )
589
- else:
590
- logger.debug("Container not found, cancelling cleanup script execution.")
591
618
 
592
619
  def register_finalizer_cb(self, cb, at_start=False):
593
620
  """Register a callback to run with the finalizer."""
@@ -648,7 +675,8 @@ class FluentConnection:
648
675
  logger.info(f"Waiting {wait} seconds for Fluent processes to finish...")
649
676
  else:
650
677
  raise WaitTypeError()
651
- if self.connection_properties.inside_container:
678
+
679
+ if self.connection_properties.inside_container and not is_compose():
652
680
  _response = timeout_loop(
653
681
  get_container,
654
682
  wait,