From a699963399aefa51da10f1f90ea501b80c3197c6 Mon Sep 17 00:00:00 2001 From: Mainak Kundu <94432368+mkundu1@users.noreply.github.com> Date: Tue, 26 Mar 2024 08:11:53 -0400 Subject: [PATCH 1/7] fix: typing stub (#2614) * fix: typing stubs * fix: typing stubs --- src/ansys/fluent/core/session_meshing.pyi | 8 +++----- src/ansys/fluent/core/session_pure_meshing.py | 2 +- src/ansys/fluent/core/session_pure_meshing.pyi | 8 +++----- src/ansys/fluent/core/session_solver.pyi | 5 +---- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/ansys/fluent/core/session_meshing.pyi b/src/ansys/fluent/core/session_meshing.pyi index 19ea2ae8eed..4c72c117089 100644 --- a/src/ansys/fluent/core/session_meshing.pyi +++ b/src/ansys/fluent/core/session_meshing.pyi @@ -3,12 +3,10 @@ from ansys.fluent.core.datamodel_241.PMFileManagement import ( ) from ansys.fluent.core.datamodel_241.PartManagement import Root as partmanagement_root from ansys.fluent.core.datamodel_241.meshing import Root as meshing_root -from ansys.fluent.core.datamodel_241.meshing_utilities import ( - Root as meshing_utilities_root, -) from ansys.fluent.core.datamodel_241.preferences import Root as preferences_root from ansys.fluent.core.datamodel_241.workflow import Root as workflow_root -from ansys.fluent.core.solver.tui_241 import main_menu +from ansys.fluent.core.meshing.tui_241 import main_menu +from ansys.fluent.core.services.meshing_queries import MeshingQueries class Meshing: @property @@ -16,7 +14,7 @@ class Meshing: @property def meshing(self) -> meshing_root: ... @property - def meshing_utilities(self) -> meshing_utilities_root: ... + def meshing_queries(self) -> MeshingQueries: ... @property def workflow(self) -> workflow_root: ... @property diff --git a/src/ansys/fluent/core/session_pure_meshing.py b/src/ansys/fluent/core/session_pure_meshing.py index 9988e38017d..4969c496f31 100644 --- a/src/ansys/fluent/core/session_pure_meshing.py +++ b/src/ansys/fluent/core/session_pure_meshing.py @@ -81,7 +81,7 @@ def meshing(self): return self._base_meshing.meshing @property - def meshing_queries(self): + def meshing_queries(self) -> MeshingQueries: """Datamodel root of meshing_queries.""" if self.get_fluent_version() >= FluentVersion.v232: return MeshingQueries(self.meshing_queries_service) diff --git a/src/ansys/fluent/core/session_pure_meshing.pyi b/src/ansys/fluent/core/session_pure_meshing.pyi index 4a3c03e7b08..c9638a5e1bf 100644 --- a/src/ansys/fluent/core/session_pure_meshing.pyi +++ b/src/ansys/fluent/core/session_pure_meshing.pyi @@ -3,12 +3,10 @@ from ansys.fluent.core.datamodel_241.PMFileManagement import ( ) from ansys.fluent.core.datamodel_241.PartManagement import Root as partmanagement_root from ansys.fluent.core.datamodel_241.meshing import Root as meshing_root -from ansys.fluent.core.datamodel_241.meshing_utilities import ( - Root as meshing_utilities_root, -) from ansys.fluent.core.datamodel_241.preferences import Root as preferences_root from ansys.fluent.core.datamodel_241.workflow import Root as workflow_root -from ansys.fluent.core.solver.tui_241 import main_menu +from ansys.fluent.core.meshing.tui_241 import main_menu +from ansys.fluent.core.services.meshing_queries import MeshingQueries class PureMeshing: @property @@ -16,7 +14,7 @@ class PureMeshing: @property def meshing(self) -> meshing_root: ... @property - def meshing_utilities(self) -> meshing_utilities_root: ... + def meshing_utilities(self) -> MeshingQueries: ... @property def workflow(self) -> workflow_root: ... def watertight(self): ... diff --git a/src/ansys/fluent/core/session_solver.pyi b/src/ansys/fluent/core/session_solver.pyi index 014751ec4b4..ea4411d207a 100644 --- a/src/ansys/fluent/core/session_solver.pyi +++ b/src/ansys/fluent/core/session_solver.pyi @@ -1,5 +1,4 @@ from ansys.fluent.core.datamodel_241.preferences import Root as preferences_root -from ansys.fluent.core.datamodel_241.solverworkflow import Root as solverworkflow_root from ansys.fluent.core.datamodel_241.workflow import Root as workflow_root from ansys.fluent.core.solver.settings_241.current_parametric_study import ( current_parametric_study, @@ -7,8 +6,8 @@ from ansys.fluent.core.solver.settings_241.current_parametric_study import ( from ansys.fluent.core.solver.settings_241.file import file from ansys.fluent.core.solver.settings_241.mesh import mesh from ansys.fluent.core.solver.settings_241.parallel import parallel +from ansys.fluent.core.solver.settings_241.parameters import parameters from ansys.fluent.core.solver.settings_241.parametric_studies import parametric_studies -from ansys.fluent.core.solver.settings_241.report import report from ansys.fluent.core.solver.settings_241.results import results from ansys.fluent.core.solver.settings_241.server import server from ansys.fluent.core.solver.settings_241.setup import setup @@ -44,8 +43,6 @@ class Solver: @property def results(self) -> results: ... @property - def design(self) -> design: ... - @property def parametric_studies(self) -> parametric_studies: ... @property def current_parametric_study(self) -> current_parametric_study: ... From fa529fa5dc4862290c4261cb4b92c5d0c59cf2ea Mon Sep 17 00:00:00 2001 From: Harshal Pohekar <106588300+hpohekar@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:48:42 +0530 Subject: [PATCH 2/7] fix: Fix for Ansys Lab upload/download methods (#2597) * fix: Fix for Ansys Lab upload/download methods * user warning. * user warning method. * user warning method 1. * progressbar * progressbar fix * Integrate progressbar and improve error handling * remove test_uploader * progressbar2 usage * suggestion * restore alive_bar * restore alive_bar 1 * restore alive_bar 2 --- pyproject.toml | 1 + .../fluent/core/launcher/pim_launcher.py | 4 +- src/ansys/fluent/core/session.py | 24 ++++++++-- .../core/utils/file_transfer_service.py | 48 ++++++++++++------- tests/test_uploader.py | 38 --------------- 5 files changed, 54 insertions(+), 61 deletions(-) delete mode 100644 tests/test_uploader.py diff --git a/pyproject.toml b/pyproject.toml index e998ee50a38..e65c26b1f6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ importlib-metadata = {version = "^7.0.1", python = "<3.9"} ansys-api-fluent = "^0.3.22" ansys-platform-instancemanagement = "^1.0" ansys-units = "^0.3.2" +alive-progress = "^3.1.5" beartype = "^0.17" docker = "^6.1.3" grpcio = "^1.30.0" diff --git a/src/ansys/fluent/core/launcher/pim_launcher.py b/src/ansys/fluent/core/launcher/pim_launcher.py index 26051c8c0a0..50abe6a3346 100644 --- a/src/ansys/fluent/core/launcher/pim_launcher.py +++ b/src/ansys/fluent/core/launcher/pim_launcher.py @@ -200,9 +200,7 @@ def __init__( "'start_watchdog' argument for 'launch_fluent' is currently not supported " "when starting a remote Fluent PyPIM client." ) - self.file_transfer_service = ( - file_transfer_service if file_transfer_service else PimFileTransferService() - ) + self.file_transfer_service = file_transfer_service def __call__(self): logger.info( diff --git a/src/ansys/fluent/core/session.py b/src/ansys/fluent/core/session.py index 612cf96abee..737ab32177c 100644 --- a/src/ansys/fluent/core/session.py +++ b/src/ansys/fluent/core/session.py @@ -3,7 +3,7 @@ import importlib import json import logging -from typing import Any, Optional +from typing import Any, Optional, Union import warnings from ansys.fluent.core.fluent_connection import FluentConnection @@ -308,16 +308,31 @@ def force_exit(self) -> None: data.""" self._fluent_connection.force_exit() - def upload(self, file_name: str): + def _file_transfer_api_warning(self, method_name: str) -> str: + """User warning for upload/download methods.""" + return f"You have directly called the {method_name} method of the session. \ + Please be advised that for the current version of Fluent, many API methods \ + automatically handle file uploads and downloads internally. You may not \ + need to explicitly call {method_name} in most cases. \ + However, there are exceptions, particularly in PMFileManagement, where complex \ + file interactions require explicit use of {method_name} method \ + for relevant files." + + def upload( + self, file_name: Union[list[str], str], remote_file_name: Optional[str] = None + ): """Upload a file to the server. Parameters ---------- file_name : str Name of the local file to upload to the server. + remote_file_name : str, optional + remote file name, by default None """ + warnings.warn(self._file_transfer_api_warning("upload()"), UserWarning) if self._file_transfer_service: - return self._file_transfer_service.upload_file(file_name) + return self._file_transfer_service.upload(file_name, remote_file_name) def download(self, file_name: str, local_directory: Optional[str] = "."): """Download a file from the server. @@ -329,8 +344,9 @@ def download(self, file_name: str, local_directory: Optional[str] = "."): local_directory : str, optional Local destination directory. The default is the current working directory. """ + warnings.warn(self._file_transfer_api_warning("download()"), UserWarning) if self._file_transfer_service: - return self._file_transfer_service.download_file(file_name, local_directory) + return self._file_transfer_service.download(file_name, local_directory) def __enter__(self): return self diff --git a/src/ansys/fluent/core/utils/file_transfer_service.py b/src/ansys/fluent/core/utils/file_transfer_service.py index 66d10989ea6..904ca6f5437 100644 --- a/src/ansys/fluent/core/utils/file_transfer_service.py +++ b/src/ansys/fluent/core/utils/file_transfer_service.py @@ -3,6 +3,8 @@ import os from typing import Any, Callable, Optional, Union # noqa: F401 +from alive_progress import alive_bar + import ansys.platform.instancemanagement as pypim @@ -99,13 +101,17 @@ def upload_file(self, file_name: str, remote_file_name: Optional[str] = None): else: raise FileNotFoundError(f"{file_name} does not exist.") - def upload(self, file_name: Union[list[str], str]): + def upload( + self, file_name: Union[list[str], str], remote_file_name: Optional[str] = None + ): """Upload a file to the server. Parameters ---------- file_name : str File name + remote_file_name : str, optional + remote file name, by default None Raises ------ FileNotFoundError @@ -113,12 +119,18 @@ def upload(self, file_name: Union[list[str], str]): """ files = [file_name] if isinstance(file_name, str) else file_name if self.is_configured(): - for file in files: - if os.path.isfile(file): - if not self.file_service.file_exist(os.path.basename(file)): - self.upload_file(file_name=file) - elif not self.file_service.file_exist(os.path.basename(file)): - raise FileNotFoundError(f"{file} does not exist.") + with alive_bar(len(files), title="Uploading...") as bar: + for file in files: + if os.path.isfile(file): + if not self.file_service.file_exist(os.path.basename(file)): + self.upload_file( + file_name=file, remote_file_name=remote_file_name + ) + bar() + else: + print(f"\n{file} already uploaded.\n") + elif not self.file_service.file_exist(os.path.basename(file)): + raise FileNotFoundError(f"{file} does not exist.") def download_file(self, file_name: str, local_directory: Optional[str] = None): """Download a file from the server supported by `PyPIM`. @@ -146,8 +158,7 @@ def download_file(self, file_name: str, local_directory: Optional[str] = None): raise FileNotFoundError("Remote file does not exist.") def download( - self, - file_name: Union[list[str], str], + self, file_name: Union[list[str], str], local_directory: Optional[str] = "." ): """Download a file from the server. @@ -155,16 +166,21 @@ def download( ---------- file_name : str File name + local_directory : str, optional + local directory, by default None """ files = [file_name] if isinstance(file_name, str) else file_name if self.is_configured(): - for file in files: - if os.path.isfile(file): - print(f"\nFile already exists. File path:\n{file}\n") - else: - self.download_file( - file_name=os.path.basename(file), local_directory="." - ) + with alive_bar(len(files), title="Downloading...") as bar: + for file in files: + if os.path.isfile(file): + print(f"\nFile already exists. File path:\n{file}\n") + else: + self.download_file( + file_name=os.path.basename(file), + local_directory=local_directory, + ) + bar() def __call__(self, pim_instance: Optional[Any] = None): self.pim_instance = pim_instance diff --git a/tests/test_uploader.py b/tests/test_uploader.py deleted file mode 100644 index c6cb8f4d036..00000000000 --- a/tests/test_uploader.py +++ /dev/null @@ -1,38 +0,0 @@ -import pytest -from util.meshing_workflow import new_mesh_session # noqa: F401 -from util.solver_workflow import new_solver_session # noqa: F401 - -from ansys.fluent.core import examples -from ansys.fluent.core.utils.file_transfer_service import PyPIMConfigurationError - -import_file_name = examples.download_file( - "mixing_elbow.msh.h5", "pyfluent/mixing_elbow" -) - - -def test_meshing_session_upload(new_mesh_session): - session = new_mesh_session - with pytest.raises(PyPIMConfigurationError) as e_info: - session.upload(import_file_name) - session.exit() - - -def test_meshing_session_download(new_mesh_session): - session = new_mesh_session - with pytest.raises(PyPIMConfigurationError) as e_info: - session.download(import_file_name) - session.exit() - - -def test_solver_session_upload(new_solver_session): - session = new_solver_session - with pytest.raises(PyPIMConfigurationError) as e_info: - session.upload(import_file_name) - session.exit() - - -def test_solver_session_download(new_solver_session): - session = new_solver_session - with pytest.raises(PyPIMConfigurationError) as e_info: - session.download(import_file_name) - session.exit() From 98193023ca6a970433898b95064a35b3ee8d2b32 Mon Sep 17 00:00:00 2001 From: Mainak Kundu <94432368+mkundu1@users.noreply.github.com> Date: Tue, 26 Mar 2024 19:40:11 -0400 Subject: [PATCH 3/7] fix: processor_count (#2623) * fix: processor_count * fix: processor_count * fix: processor_count * fix: processor_count * fix: processor_count * fix: processor_count --- .../core/launcher/process_launch_string.py | 4 ++-- tests/test_launcher.py | 21 +++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/ansys/fluent/core/launcher/process_launch_string.py b/src/ansys/fluent/core/launcher/process_launch_string.py index 6211e7e26ad..a2149cb05b2 100644 --- a/src/ansys/fluent/core/launcher/process_launch_string.py +++ b/src/ansys/fluent/core/launcher/process_launch_string.py @@ -53,8 +53,8 @@ def _build_fluent_launch_args_string(**kwargs) -> str: json_key = json.dumps(argval) argval = fluent_map[json_key] launch_args_string += v["fluent_format"].replace("{}", str(argval)) - addArgs = kwargs.get("additional_arguments") - if addArgs and "-t" not in addArgs and "-cnf=" not in addArgs: + addArgs = kwargs["additional_arguments"] + if "-t" not in addArgs and "-cnf=" not in addArgs: parallel_options = build_parallel_options( load_machines(ncores=kwargs["processor_count"]) ) diff --git a/tests/test_launcher.py b/tests/test_launcher.py index 5820fa202b2..cccfacc3eff 100644 --- a/tests/test_launcher.py +++ b/tests/test_launcher.py @@ -398,15 +398,32 @@ def test_exposure_and_graphics_driver_arguments(): pyfluent.launch_fluent(graphics_driver="x11" if is_windows() else "dx11") for m in UIMode: assert ( - _build_fluent_launch_args_string(ui_mode=m).strip() == f"3ddp -{m.value[0]}" + _build_fluent_launch_args_string( + ui_mode=m, additional_arguments="", processor_count=None + ).strip() + == f"3ddp -{m.value[0]}" if m.value[0] else " 3ddp" ) for e in (FluentWindowsGraphicsDriver, FluentLinuxGraphicsDriver): for m in e: assert ( - _build_fluent_launch_args_string(graphics_driver=m).strip() + _build_fluent_launch_args_string( + graphics_driver=m, additional_arguments="", processor_count=None + ).strip() == f"3ddp -driver {m.value[0]}" if m.value[0] else " 3ddp" ) + + +def test_processor_count(): + def get_processor_count(solver): + return int(solver.rp_vars("parallel/nprocs_string").strip('"')) + + with pyfluent.launch_fluent(processor_count=2) as solver: + assert get_processor_count(solver) == 2 + # The following check is not yet supported for container launch + # https://github.com/ansys/pyfluent/issues/2624 + # with pyfluent.launch_fluent(additional_arguments="-t2") as solver: + # assert get_processor_count(solver) == 2 From c0792a05fa9e92ae1ec65177a4908a91b68598e1 Mon Sep 17 00:00:00 2001 From: Prithwish Mukherjee <109645853+prmukherj@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:44:06 +0530 Subject: [PATCH 4/7] fix: Update nightly test run and add reduction test. (#2595) --- tests/test_meshingmode/test_meshing_launch.py | 2 +- tests/test_pure_mesh_vs_mesh_workflow.py | 4 ++-- tests/test_reduction.py | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/tests/test_meshingmode/test_meshing_launch.py b/tests/test_meshingmode/test_meshing_launch.py index 11a8c2d778b..6ad8176b988 100644 --- a/tests/test_meshingmode/test_meshing_launch.py +++ b/tests/test_meshingmode/test_meshing_launch.py @@ -13,7 +13,7 @@ def test_launch_pure_meshing(load_mixing_elbow_pure_meshing): file_name = "launch_pure_meshing_journal.py" pure_meshing_session.journal.start(file_name) session_dir = dir(pure_meshing_session) - for attr in ("field_data", "field_info", "meshing", "workflow"): + for attr in ("fields", "meshing", "workflow"): assert attr in session_dir workflow = pure_meshing_session.workflow workflow.TaskObject["Import Geometry"].Execute() diff --git a/tests/test_pure_mesh_vs_mesh_workflow.py b/tests/test_pure_mesh_vs_mesh_workflow.py index 9477f625d42..40ad3fb0aaf 100644 --- a/tests/test_pure_mesh_vs_mesh_workflow.py +++ b/tests/test_pure_mesh_vs_mesh_workflow.py @@ -11,7 +11,7 @@ def test_pure_meshing_mode(load_mixing_elbow_pure_meshing): # n.b. 'field_data', 'field_info' need to # be eliminated from meshing sessions session_dir = dir(pure_meshing_session) - for attr in ("meshing", "workflow"): + for attr in ("fields", "meshing", "workflow"): assert attr in session_dir workflow = pure_meshing_session.workflow workflow_dir = dir(workflow) @@ -33,7 +33,7 @@ def test_meshing_mode(load_mixing_elbow_meshing): # n.b. 'field_data', 'field_info' need to # be eliminated from meshing sessions session_dir = dir(meshing_session) - for attr in ("meshing", "workflow"): + for attr in ("fields", "meshing", "workflow"): assert attr in session_dir assert meshing_session.workflow.InitializeWorkflow( WorkflowType="Watertight Geometry" diff --git a/tests/test_reduction.py b/tests/test_reduction.py index 0c67e6e58b5..5dab108ca81 100644 --- a/tests/test_reduction.py +++ b/tests/test_reduction.py @@ -362,3 +362,19 @@ def test_reduction_does_not_modify_case(load_static_mixer_case): locations=solver.setup.boundary_conditions.velocity_inlet, ) assert not solver.scheme_eval.scheme_eval("(case-modified?)") + + +@pytest.mark.fluent_version(">=24.2") +def test_fix_for_invalid_location_inputs(load_static_mixer_case): + solver = load_static_mixer_case + solver.solution.initialization.hybrid_initialize() + + assert solver.fields.reduction.area(locations=["inlet1"], ctxt=solver) + + with pytest.raises(RuntimeError): + assert solver.fields.reduction.area(locations=["inlet-1"], ctxt=solver) + + assert solver.fields.reduction.area(locations=["inlet1"]) + + with pytest.raises(RuntimeError): + assert solver.fields.reduction.area(locations=["inlet-1"]) From 02e32af1df00f600585370114ec2d3a40bbd9f43 Mon Sep 17 00:00:00 2001 From: Mainak Kundu <94432368+mkundu1@users.noreply.github.com> Date: Thu, 28 Mar 2024 08:35:49 -0400 Subject: [PATCH 5/7] build: Bump version 0.21.dev0 (#2626) * build: Bump version 0.21.dev0 * fix: skip some tests * Fix meshing workflow test. --------- Co-authored-by: Prithwish Mukherjee --- pyproject.toml | 2 +- src/ansys/fluent/core/_version.py | 2 +- tests/parametric/test_parametric_workflow.py | 31 ++++++++++++++++++++ tests/test_new_meshing_workflow.py | 4 --- tests/test_settings_api.py | 1 + 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e65c26b1f6b..2271c1cf28d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] # Check https://python-poetry.org/docs/pyproject/ for all available sections name = "ansys-fluent-core" -version = "0.20.dev12" +version = "0.21.dev0" description = "PyFluent provides Pythonic access to Ansys Fluent" license = "MIT" authors = ["ANSYS, Inc. "] diff --git a/src/ansys/fluent/core/_version.py b/src/ansys/fluent/core/_version.py index f4c1c4d1220..0fbcd63d179 100644 --- a/src/ansys/fluent/core/_version.py +++ b/src/ansys/fluent/core/_version.py @@ -6,7 +6,7 @@ """ # major, minor, patch -version_info = 0, 20, "dev12" +version_info = 0, 21, "dev0" # Nice string for the version __version__ = ".".join(map(str, version_info)) diff --git a/tests/parametric/test_parametric_workflow.py b/tests/parametric/test_parametric_workflow.py index 773b8aa1b42..17fb2896362 100644 --- a/tests/parametric/test_parametric_workflow.py +++ b/tests/parametric/test_parametric_workflow.py @@ -201,3 +201,34 @@ def test_parametric_workflow(): solver_session.file.parametric_project.archive(archive_name=write_archive_name) assert archive_name.exists() solver_session.exit() + + +@pytest.mark.fluent_version(">=24.2") +def test_parameters_list_function(load_static_mixer_settings_only): + solver = load_static_mixer_settings_only + solver.tui.define.parameters.enable_in_TUI("yes") + + velocity_inlet = solver.tui.define.boundary_conditions.set.velocity_inlet + velocity_inlet("inlet1", (), "vmag", "yes", "inlet1_vel", 1, "quit") + velocity_inlet("inlet1", (), "temperature", "yes", "inlet1_temp", 300, "quit") + velocity_inlet("inlet2", (), "vmag", "yes", "no", "inlet2_vel", 1, "quit") + velocity_inlet("inlet2", (), "temperature", "yes", "no", "inlet2_temp", 350, "quit") + + solver.solution.report_definitions.surface["outlet-temp-avg"] = {} + outlet_temp_avg = solver.solution.report_definitions.surface["outlet-temp-avg"] + outlet_temp_avg.report_type = "surface-areaavg" + outlet_temp_avg.field = "temperature" + outlet_temp_avg.surface_names = ["outlet"] + + solver.solution.report_definitions.surface["outlet-vel-avg"] = {} + outlet_vel_avg = solver.solution.report_definitions.surface["outlet-vel-avg"] + outlet_vel_avg.report_type = "surface-areaavg" + outlet_vel_avg.field = "velocity-magnitude" + outlet_vel_avg.surface_names = ["outlet"] + + create_output_param = solver.tui.define.parameters.output_parameters.create + create_output_param("report-definition", "outlet-temp-avg") + create_output_param("report-definition", "outlet-vel-avg") + + assert len(solver.parameters.input_parameters.list()) == 4 + assert len(solver.parameters.output_parameters.list()) == 2 diff --git a/tests/test_new_meshing_workflow.py b/tests/test_new_meshing_workflow.py index f6494dfd690..6965c013410 100644 --- a/tests/test_new_meshing_workflow.py +++ b/tests/test_new_meshing_workflow.py @@ -5,7 +5,6 @@ from ansys.fluent.core import examples from ansys.fluent.core.meshing.watertight import watertight_workflow -from ansys.fluent.core.utils.fluent_version import FluentVersion from tests.test_datamodel_service import disable_datamodel_cache # noqa: F401 @@ -550,11 +549,8 @@ def test_workflow_and_data_model_methods_new_meshing_workflow(new_mesh_session): "import_body_of_influence_geometry", "set_up_periodic_boundaries", "create_local_refinement_regions", - "load_cad_geometry", "run_custom_journal", ] - if meshing.get_fluent_version() < FluentVersion.v242: - _next_possible_tasks.remove("load_cad_geometry") assert ( watertight.task("import_geom_wtm").get_next_possible_tasks() == _next_possible_tasks diff --git a/tests/test_settings_api.py b/tests/test_settings_api.py index 90ad3c7b44b..bec8c0cc3d4 100644 --- a/tests/test_settings_api.py +++ b/tests/test_settings_api.py @@ -154,6 +154,7 @@ def test_api_upgrade(new_solver_session, capsys): ".file.read_case" in capsys.readouterr().out +@pytest.mark.skip @pytest.mark.fluent_version(">=24.2") def test_deprecated_settings(new_solver_session): solver = new_solver_session From 51fc833aec5bf38572be891a95c2a68b47ee0f8c Mon Sep 17 00:00:00 2001 From: Mainak Kundu <94432368+mkundu1@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:49:03 -0400 Subject: [PATCH 6/7] fix: dict set-state with alias (#2630) --- src/ansys/fluent/core/solver/flobject.py | 5 +- tests/test_settings_api.py | 67 ++++++++++++++++-------- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/ansys/fluent/core/solver/flobject.py b/src/ansys/fluent/core/solver/flobject.py index 7d8fb195a61..93779000e3f 100644 --- a/src/ansys/fluent/core/solver/flobject.py +++ b/src/ansys/fluent/core/solver/flobject.py @@ -661,10 +661,11 @@ def _unalias(cls, value): ) ret_alias = ret comps = alias.split("/") + aliased_cls = cls for i, comp in enumerate(comps): - cls = cls._child_classes[comp] + aliased_cls = aliased_cls._child_classes[comp] if i == len(comps) - 1: - ret_alias[comp] = cls._unalias(v) + ret_alias[comp] = aliased_cls._unalias(v) else: ret_alias[comp] = {} ret_alias = ret_alias[comp] diff --git a/tests/test_settings_api.py b/tests/test_settings_api.py index bec8c0cc3d4..8b3baa44db2 100644 --- a/tests/test_settings_api.py +++ b/tests/test_settings_api.py @@ -154,7 +154,6 @@ def test_api_upgrade(new_solver_session, capsys): ".file.read_case" in capsys.readouterr().out -@pytest.mark.skip @pytest.mark.fluent_version(">=24.2") def test_deprecated_settings(new_solver_session): solver = new_solver_session @@ -189,56 +188,76 @@ def test_deprecated_settings(new_solver_session): ) assert ( len( - solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t._child_aliases + solver.setup.boundary_conditions.wall[ + "wall-inlet" + ].thermal.temperature._child_aliases ) > 0 ) assert ( - solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t._child_aliases[ - "constant" - ] + solver.setup.boundary_conditions.wall[ + "wall-inlet" + ].thermal.temperature._child_aliases["constant"] == "value" ) with pytest.warns(DeprecatedSettingWarning): - solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t.constant = 400 + solver.setup.boundary_conditions.wall[ + "wall-inlet" + ].thermal.temperature.constant = 400 - assert solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t.value() == 400 + assert ( + solver.setup.boundary_conditions.wall["wall-inlet"].thermal.temperature.value() + == 400 + ) assert ( len( - solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t._child_aliases + solver.setup.boundary_conditions.wall[ + "wall-inlet" + ].thermal.temperature._child_aliases ) > 0 ) assert isinstance( - solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t._child_alias_objs[ - "constant" - ], + solver.setup.boundary_conditions.wall[ + "wall-inlet" + ].thermal.temperature._child_alias_objs["constant"], _Alias, ) - solver.setup.boundary_conditions.wall["wall-inlet"].thermal._setattr( - "_child_aliases", {"temp": "t"} - ) with pytest.warns(DeprecatedSettingWarning): - solver.setup.boundary_conditions.wall["wall-inlet"].thermal.temp.value = 410 + solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t.value = 410 - assert solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t.value() == 410 + assert ( + solver.setup.boundary_conditions.wall["wall-inlet"].thermal.temperature.value() + == 410 + ) solver.setup.boundary_conditions._setattr("_child_aliases", {"w": "wall"}) with pytest.warns(DeprecatedSettingWarning): - solver.setup.boundary_conditions.w["wall-inlet"].thermal.t.value = 420 + solver.setup.boundary_conditions.w["wall-inlet"].thermal.temperature.value = 420 - assert solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t.value() == 420 + assert ( + solver.setup.boundary_conditions.wall["wall-inlet"].thermal.temperature.value() + == 420 + ) solver.setup._setattr("_child_aliases", {"bc": "boundary_conditions"}) with pytest.warns(DeprecatedSettingWarning): - solver.setup.bc.wall["wall-inlet"].thermal.t.value = 430 + solver.setup.bc.wall["wall-inlet"].thermal.temperature.value = 430 - assert solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t.value() == 430 + assert ( + solver.setup.boundary_conditions.wall["wall-inlet"].thermal.temperature.value() + == 430 + ) with pytest.warns(DeprecatedSettingWarning): - solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t.constant = 400 + solver.setup.boundary_conditions.wall[ + "wall-inlet" + ].thermal.temperature.constant = 400 - assert solver.setup.boundary_conditions.wall["wall-inlet"].thermal.t.value() == 400 + assert ( + solver.setup.boundary_conditions.wall["wall-inlet"].thermal.temperature.value() + == 400 + ) solver.results._setattr("_child_aliases", {"gr": "graphics"}) with pytest.warns(DeprecatedSettingWarning): @@ -270,6 +289,10 @@ def test_deprecated_settings(new_solver_session): solver.setup.cell_zone_conditions.fluid["elbow-fluid"] = {"material": "air"} + solver.setup.boundary_conditions.wall["wall-inlet"] = { + "thermal": {"q_dot": {"value": 2000000000}, "wall_thickness": {"value": 0.002}} + } + @pytest.mark.fluent_version(">=24.2") def test_command_return_type(new_solver_session): From 80d492e07ad0413b58bf02661e013d81e8000827 Mon Sep 17 00:00:00 2001 From: Prithwish Mukherjee <109645853+prmukherj@users.noreply.github.com> Date: Thu, 28 Mar 2024 23:14:50 +0530 Subject: [PATCH 7/7] refactor: update enhanced meshing interface (#2606) * Return solver session only when switch_to_solver is successful. * Fix add_child_and_update * Add tests for 2D Meshing workflow. * Fix _add_child * Add documentation. * Update styling. * Revert changes in switching to solver. * Refactoring. * Refactor and update tests. * Fix test import. * Bug fixing. * Fix behaviour of defer_update * Update src/ansys/fluent/core/workflow.py Co-authored-by: Mainak Kundu <94432368+mkundu1@users.noreply.github.com> * Fix for defer_update. * Fix doc. * Update fluent version check. * Update doc/source/user_guide/meshing_workflow/meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update doc/source/user_guide/meshing_workflow/meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update doc/source/user_guide/meshing_workflow/meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update doc style. * Update doc/source/user_guide/meshing_workflow/meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update doc/source/user_guide/meshing_workflow/meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update doc style. * Update doc/source/user_guide/meshing_workflow/new_meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update doc/source/user_guide/meshing_workflow/new_meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update doc/source/user_guide/meshing_workflow/new_meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update src/ansys/fluent/core/meshing/meshing_workflow.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update src/ansys/fluent/core/meshing/meshing_workflow.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update src/ansys/fluent/core/meshing/meshing_workflow.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update src/ansys/fluent/core/meshing/meshing_workflow.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update src/ansys/fluent/core/session_base_meshing.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update src/ansys/fluent/core/workflow.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update docs. * Update meshing test. * Update const. call from Solver. --------- Co-authored-by: Mainak Kundu <94432368+mkundu1@users.noreply.github.com> Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> --- .../meshing_workflow/meshing_workflows.rst | 220 +++++++++++++++++- .../new_meshing_workflows.rst | 160 +++++++++++++ src/ansys/fluent/core/meshing/2d_meshing.py | 25 -- .../fluent/core/meshing/faulttolerant.py | 25 -- .../fluent/core/meshing/meshing_workflow.py | 200 +++++++++------- .../fluent/core/meshing/topology_based.py | 25 -- src/ansys/fluent/core/meshing/watertight.py | 32 --- src/ansys/fluent/core/session_base_meshing.py | 25 +- src/ansys/fluent/core/session_solver.py | 4 +- src/ansys/fluent/core/workflow.py | 38 ++- tests/test_meshing_workflow.py | 186 +++++++++++++-- tests/test_new_meshing_workflow.py | 159 ++++++++++++- 12 files changed, 850 insertions(+), 249 deletions(-) delete mode 100644 src/ansys/fluent/core/meshing/2d_meshing.py delete mode 100644 src/ansys/fluent/core/meshing/faulttolerant.py delete mode 100644 src/ansys/fluent/core/meshing/topology_based.py delete mode 100644 src/ansys/fluent/core/meshing/watertight.py diff --git a/doc/source/user_guide/meshing_workflow/meshing_workflows.rst b/doc/source/user_guide/meshing_workflow/meshing_workflows.rst index e9521789aab..e4b4348fd41 100644 --- a/doc/source/user_guide/meshing_workflow/meshing_workflows.rst +++ b/doc/source/user_guide/meshing_workflow/meshing_workflows.rst @@ -572,9 +572,223 @@ Switch to solution mode solver = meshing.switch_to_solver() -Sample use of CommandArguments ------------------------------- -This simple example shows you how to use the ``CommandArgument`` attributes and explicit + +2D meshing workflow +------------------- +Use the **2D** meshing workflow to mesh specific two-dimensional geometries. +The following example shows how to use the 2D Meshing workflow. + +Import geometry +~~~~~~~~~~~~~~~ + +.. code:: python + + import ansys.fluent.core as pyfluent + from ansys.fluent.core import examples + + import_file_name = examples.download_file('NACA0012.fmd', 'pyfluent/airfoils') + meshing = pyfluent.launch_fluent( + mode="meshing", precision='double', processor_count=2 + ) + meshing.workflow.InitializeWorkflow(WorkflowType="2D Meshing") + meshing.workflow.TaskObject["Load CAD Geometry"].Arguments.set_state( + { + r"FileName": import_file_name, + r"LengthUnit": r"mm", + r"Refaceting": { + r"Refacet": False, + }, + } + ) + meshing.workflow.TaskObject["Load CAD Geometry"].Execute() + +Set regions and boundaries +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + meshing.workflow.TaskObject["Update Regions"].Execute() + meshing.workflow.TaskObject["Update Boundaries"].Arguments.set_state( + { + r"SelectionType": r"zone", + } + ) + meshing.workflow.TaskObject["Update Boundaries"].Execute() + +Define global sizing +~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + meshing.workflow.TaskObject["Define Global Sizing"].Arguments.set_state( + { + r"CurvatureNormalAngle": 20, + r"MaxSize": 2000, + r"MinSize": 5, + r"SizeFunctions": r"Curvature", + } + ) + meshing.workflow.TaskObject["Define Global Sizing"].Execute() + +Add body of influence +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + meshing.workflow.TaskObject["Add Local Sizing"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BOIControlName": r"boi_1", + r"BOIExecution": r"Body Of Influence", + r"BOIFaceLabelList": [r"boi"], + r"BOISize": 50, + r"BOIZoneorLabel": r"label", + r"DrawSizeControl": True, + } + ) + meshing.workflow.TaskObject["Add Local Sizing"].AddChildAndUpdate(DeferUpdate=False) + +Set edge sizing +~~~~~~~~~~~~~~~ + +.. code:: python + + meshing.workflow.TaskObject["Add Local Sizing"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BOIControlName": r"edgesize_1", + r"BOIExecution": r"Edge Size", + r"BOISize": 5, + r"BOIZoneorLabel": r"label", + r"DrawSizeControl": True, + r"EdgeLabelList": [r"airfoil-te"], + } + ) + meshing.workflow.TaskObject["Add Local Sizing"].AddChildAndUpdate(DeferUpdate=False) + +Set curvature sizing +~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + meshing.workflow.TaskObject["Add Local Sizing"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BOIControlName": r"curvature_1", + r"BOICurvatureNormalAngle": 10, + r"BOIExecution": r"Curvature", + r"BOIMaxSize": 2, + r"BOIMinSize": 1.5, + r"BOIScopeTo": r"edges", + r"BOIZoneorLabel": r"label", + r"DrawSizeControl": True, + r"EdgeLabelList": [r"airfoil"], + } + ) + meshing.workflow.TaskObject["Add Local Sizing"].AddChildAndUpdate(DeferUpdate=False) + +Add boundary layer +~~~~~~~~~~~~~~~~~~ + +.. code:: python + + meshing.workflow.TaskObject["Add 2D Boundary Layers"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BLControlName": r"aspect-ratio_1", + r"NumberOfLayers": 4, + r"OffsetMethodType": r"aspect-ratio", + } + ) + meshing.workflow.TaskObject["Add 2D Boundary Layers"].AddChildAndUpdate( + DeferUpdate=False + ) + +Generate surface mesh +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state( + { + r"Surface2DPreferences": { + r"MergeEdgeZonesBasedOnLabels": r"no", + r"MergeFaceZonesBasedOnLabels": r"no", + r"ShowAdvancedOptions": True, + }, + } + ) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Execute() + + meshing.workflow.TaskObject["aspect-ratio_1"].Revert() + meshing.workflow.TaskObject["aspect-ratio_1"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BLControlName": r"uniform_1", + r"FirstLayerHeight": 2, + r"NumberOfLayers": 4, + r"OffsetMethodType": r"uniform", + } + ) + meshing.workflow.TaskObject["aspect-ratio_1"].Execute() + + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state(None) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state( + { + r"Surface2DPreferences": { + r"MergeEdgeZonesBasedOnLabels": r"no", + r"MergeFaceZonesBasedOnLabels": r"no", + r"ShowAdvancedOptions": True, + }, + } + ) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Execute() + + meshing.workflow.TaskObject["uniform_1"].Revert() + meshing.workflow.TaskObject["uniform_1"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BLControlName": r"smooth-transition_1", + r"FirstLayerHeight": 2, + r"NumberOfLayers": 7, + r"OffsetMethodType": r"smooth-transition", + } + ) + meshing.workflow.TaskObject["uniform_1"].Execute() + + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state(None) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state( + { + r"Surface2DPreferences": { + r"MergeEdgeZonesBasedOnLabels": r"no", + r"MergeFaceZonesBasedOnLabels": r"no", + r"ShowAdvancedOptions": True, + }, + } + ) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Execute() + +Export Fluent 2D mesh +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + meshing.workflow.TaskObject["Export Fluent 2D Mesh"].Arguments.set_state( + { + r"FileName": r"mesh1.msh.h5", + } + ) + meshing.workflow.TaskObject["Export Fluent 2D Mesh"].Execute() + +Switch to solver mode +~~~~~~~~~~~~~~~~~~~~~ + +Switching to solver mode is not allowed in 2D Meshing mode. + + +Sample use of ``CommandArguments`` +---------------------------------- +This simple example shows how to use the ``CommandArgument`` attributes and explicit attribute access methods in a watertight geometry meshing workflow. .. Note:: diff --git a/doc/source/user_guide/meshing_workflow/new_meshing_workflows.rst b/doc/source/user_guide/meshing_workflow/new_meshing_workflows.rst index 00b01bf7d33..6f68f306c4b 100644 --- a/doc/source/user_guide/meshing_workflow/new_meshing_workflows.rst +++ b/doc/source/user_guide/meshing_workflow/new_meshing_workflows.rst @@ -453,3 +453,163 @@ Switch to solution mode .. code:: python solver = meshing.switch_to_solver() + + +2D meshing workflow +------------------- +Use the **2D** meshing workflow to mesh specific two-dimensional geometries. +The following example shows you how to use the 2D meshing workflow. + +Import geometry +~~~~~~~~~~~~~~~ + +.. code:: python + + import ansys.fluent.core as pyfluent + from ansys.fluent.core import examples + + import_file_name = examples.download_file('NACA0012.fmd', 'pyfluent/airfoils') + meshing = pyfluent.launch_fluent( + mode="meshing", precision='double', processor_count=2 + ) + two_dim_mesh = new_mesh_session.two_dimensional_meshing() + + two_dim_mesh.load_cad_geometry_2d.file_name = import_file_name + two_dim_mesh.load_cad_geometry_2d.length_unit = "mm" + two_dim_mesh.load_cad_geometry_2d.refaceting.refacet = False + two_dim_mesh.load_cad_geometry_2d() + +Set regions and boundaries +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + two_dim_mesh.update_regions_2d() + two_dim_mesh.update_boundaries_2d.selection_type = "zone" + two_dim_mesh.update_boundaries_2d() + +Define global sizing +~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + two_dim_mesh.define_global_sizing_2d.curvature_normal_angle = 20 + two_dim_mesh.define_global_sizing_2d.max_size = 2000.0 + two_dim_mesh.define_global_sizing_2d.min_size = 5.0 + two_dim_mesh.define_global_sizing_2d.size_functions = "Curvature" + two_dim_mesh.define_global_sizing_2d() + +Adding BOI +~~~~~~~~~~ + +.. code:: python + + two_dim_mesh.add_local_sizing_2d.add_child = "yes" + two_dim_mesh.add_local_sizing_2d.boi_control_name = "boi_1" + two_dim_mesh.add_local_sizing_2d.boi_execution = "Body Of Influence" + two_dim_mesh.add_local_sizing_2d.boi_face_label_list = ["boi"] + two_dim_mesh.add_local_sizing_2d.boi_size = 50.0 + two_dim_mesh.add_local_sizing_2d.boi_zoneor_label = "label" + two_dim_mesh.add_local_sizing_2d.draw_size_control = True + two_dim_mesh.add_local_sizing_2d.add_child_and_update(defer_update=False) + +Set edge sizing +~~~~~~~~~~~~~~~ + +.. code:: python + + two_dim_mesh.add_local_sizing_2d.add_child = "yes" + two_dim_mesh.add_local_sizing_2d.boi_control_name = "edgesize_1" + two_dim_mesh.add_local_sizing_2d.boi_execution = "Edge Size" + two_dim_mesh.add_local_sizing_2d.boi_size = 5.0 + two_dim_mesh.add_local_sizing_2d.boi_zoneor_label = "label" + two_dim_mesh.add_local_sizing_2d.draw_size_control = True + two_dim_mesh.add_local_sizing_2d.edge_label_list = ["airfoil-te"] + two_dim_mesh.add_local_sizing_2d.add_child_and_update(defer_update=False) + +Set curvature sizing +~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + two_dim_mesh.add_local_sizing_2d.add_child = "yes" + two_dim_mesh.add_local_sizing_2d.boi_control_name = "curvature_1" + two_dim_mesh.add_local_sizing_2d.boi_curvature_normal_angle = 10 + two_dim_mesh.add_local_sizing_2d.boi_execution = "Curvature" + two_dim_mesh.add_local_sizing_2d.boi_max_size = 2 + two_dim_mesh.add_local_sizing_2d.boi_min_size = 1.5 + two_dim_mesh.add_local_sizing_2d.boi_scope_to = "edges" + two_dim_mesh.add_local_sizing_2d.boi_zoneor_label = "label" + two_dim_mesh.add_local_sizing_2d.draw_size_control = True + two_dim_mesh.add_local_sizing_2d.edge_label_list = ["airfoil"] + two_dim_mesh.add_local_sizing_2d.add_child_and_update(defer_update=False) + +Add boundary layer +~~~~~~~~~~~~~~~~~~ + +.. code:: python + + two_dim_mesh.add_2d_boundary_layers.add_child = "yes" + two_dim_mesh.add_2d_boundary_layers.bl_control_name = "aspect-ratio_1" + two_dim_mesh.add_2d_boundary_layers.number_of_layers = 4 + two_dim_mesh.add_2d_boundary_layers.offset_method_type = "aspect-ratio" + two_dim_mesh.add_2d_boundary_layers.add_child_and_update(defer_update=False) + +Generate surface mesh +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_edge_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_face_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.show_advanced_options = ( + True + ) + two_dim_mesh.generate_initial_surface_mesh() + + two_dim_mesh.task("aspect-ratio_1").revert() + two_dim_mesh.task("aspect-ratio_1").add_child = "yes" + two_dim_mesh.task("aspect-ratio_1").bl_control_name = "uniform_1" + two_dim_mesh.task("aspect-ratio_1").first_layer_height = 2 + two_dim_mesh.task("aspect-ratio_1").number_of_layers = 4 + two_dim_mesh.task("aspect-ratio_1").offset_method_type = "uniform" + two_dim_mesh.task("aspect-ratio_1")() + + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_edge_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_face_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.show_advanced_options = ( + True + ) + two_dim_mesh.generate_initial_surface_mesh() + + two_dim_mesh.task("uniform_1").revert() + two_dim_mesh.task("uniform_1").add_child = "yes" + two_dim_mesh.task("uniform_1").bl_control_name = "smooth-transition_1" + two_dim_mesh.task("uniform_1").first_layer_height = 2 + two_dim_mesh.task("uniform_1").number_of_layers = 7 + two_dim_mesh.task("uniform_1").offset_method_type = "smooth-transition" + two_dim_mesh.task("uniform_1")() + + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_edge_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_face_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.show_advanced_options = ( + True + ) + two_dim_mesh.generate_initial_surface_mesh() + +Switch to solution mode +~~~~~~~~~~~~~~~~~~~~~~~ + +Switching to solver is not allowed in 2D Meshing mode. diff --git a/src/ansys/fluent/core/meshing/2d_meshing.py b/src/ansys/fluent/core/meshing/2d_meshing.py deleted file mode 100644 index 58ee04a0545..00000000000 --- a/src/ansys/fluent/core/meshing/2d_meshing.py +++ /dev/null @@ -1,25 +0,0 @@ -"""2D Meshing workflow module.""" - -from typing import Union - -from ..session_meshing import Meshing -from ..session_pure_meshing import PureMeshing -from .meshing_workflow import TwoDimensionalMeshingWorkflow - - -def two_dim_workflow( - session: Union[Meshing, PureMeshing] -) -> TwoDimensionalMeshingWorkflow: - """Meshing workflow wrapper, initialized as 2D Meshing. - - Parameters - ---------- - session: Union[Meshing, PureMeshing] - Meshing session object. - - Returns - ------- - TwoDimensionalMeshingWorkflow - 2D meshing workflow wrapper. - """ - return session.two_dimensional_meshing() diff --git a/src/ansys/fluent/core/meshing/faulttolerant.py b/src/ansys/fluent/core/meshing/faulttolerant.py deleted file mode 100644 index cfbffdc0920..00000000000 --- a/src/ansys/fluent/core/meshing/faulttolerant.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Fault-tolerant workflow module.""" - -from typing import Union - -from ..session_meshing import Meshing -from ..session_pure_meshing import PureMeshing -from .meshing_workflow import FaultTolerantMeshingWorkflow - - -def fault_tolerant_workflow( - session: Union[Meshing, PureMeshing] -) -> FaultTolerantMeshingWorkflow: - """Meshing workflow wrapper, initialized as fault-tolerant. - - Parameters - ---------- - session: Union[Meshing, PureMeshing] - Meshing session object. - - Returns - ------- - FaultTolerantMeshingWorkflow - Fault-tolerant meshing workflow wrapper. - """ - return session.fault_tolerant() diff --git a/src/ansys/fluent/core/meshing/meshing_workflow.py b/src/ansys/fluent/core/meshing/meshing_workflow.py index 9e5a366fe4c..4238b9107c1 100644 --- a/src/ansys/fluent/core/meshing/meshing_workflow.py +++ b/src/ansys/fluent/core/meshing/meshing_workflow.py @@ -3,9 +3,11 @@ from __future__ import annotations +from enum import Enum from typing import Optional from ansys.fluent.core.services.datamodel_se import PyMenuGeneric +from ansys.fluent.core.utils.fluent_version import FluentVersion from ansys.fluent.core.workflow import ClassicWorkflow, Workflow @@ -16,6 +18,7 @@ def __init__( self, workflow: PyMenuGeneric, meshing: PyMenuGeneric, + fluent_version: FluentVersion, ) -> None: """Initialize ClassicMeshingWorkflow. @@ -25,8 +28,12 @@ def __init__( Underlying workflow object. meshing : PyMenuGeneric Meshing object. + fluent_version: FluentVersion + Version of Fluent in this session. """ - super().__init__(workflow=workflow, command_source=meshing) + super().__init__( + workflow=workflow, command_source=meshing, fluent_version=fluent_version + ) class MeshingWorkflow(Workflow): @@ -37,6 +44,9 @@ def __init__( self, workflow: PyMenuGeneric, meshing: PyMenuGeneric, + name: str, + identifier: str, + fluent_version: FluentVersion, ) -> None: """Initialize MeshingWorkflow. @@ -45,43 +55,66 @@ def __init__( workflow : PyMenuGeneric Underlying workflow object. meshing : PyMenuGeneric - The meshing object. - """ - super().__init__(workflow=workflow, command_source=meshing) - - -class WatertightMeshingWorkflow(MeshingWorkflow): - """Provides watertight meshing specialization of the workflow wrapper.""" - - def __init__(self, workflow: PyMenuGeneric, meshing: PyMenuGeneric) -> None: - """Initialize WatertightMeshingWorkflow. - - Parameters - ---------- - workflow : PyMenuGeneric - The underlying workflow object. - meshing : PyMenuGeneric - The meshing object. + Meshing object. + name: str + Workflow name to initialize it. + identifier: str + Workflow name to identify it from global settings. + fluent_version: FluentVersion + Version of Fluent in this session. """ - super().__init__(workflow=workflow, meshing=meshing) + super().__init__( + workflow=workflow, command_source=meshing, fluent_version=fluent_version + ) self._meshing = meshing + self._name = name + self._identifier = identifier def reinitialize(self) -> None: - """Initialize a watertight workflow.""" - self._new_workflow(name="Watertight Geometry") + """Initialize a workflow.""" + self._new_workflow(name=self._name) def __getattribute__(self, item: str): if ( item != "reinitialize" and not item.startswith("_") - and not self._meshing.GlobalSettings.EnableCleanCAD() + and not getattr(self._meshing.GlobalSettings, self._identifier)() ): raise RuntimeError( - "'Watertight' objects are inaccessible from other workflows." + f"'{self._name}' objects are inaccessible from other workflows." ) return super().__getattribute__(item) +class WatertightMeshingWorkflow(MeshingWorkflow): + """Provides watertight meshing specialization of the workflow wrapper.""" + + def __init__( + self, + workflow: PyMenuGeneric, + meshing: PyMenuGeneric, + fluent_version: FluentVersion, + ) -> None: + """Initialize WatertightMeshingWorkflow. + + Parameters + ---------- + workflow : PyMenuGeneric + Underlying workflow object. + meshing : PyMenuGeneric + Meshing object. + fluent_version: FluentVersion + Version of Fluent in this session. + """ + super().__init__( + workflow=workflow, + meshing=meshing, + name="Watertight Geometry", + identifier="EnableCleanCAD", + fluent_version=fluent_version, + ) + + class FaultTolerantMeshingWorkflow(MeshingWorkflow): """Provides fault-tolerant meshing specialization of the workflow wrapper.""" @@ -91,40 +124,33 @@ def __init__( meshing: PyMenuGeneric, part_management: PyMenuGeneric, pm_file_management: PyMenuGeneric, + fluent_version: FluentVersion, ) -> None: """Initialize FaultTolerantMeshingWorkflow. Parameters ---------- workflow : PyMenuGeneric - The underlying workflow object. + Underlying workflow object. meshing : PyMenuGeneric - The meshing object. + Meshing object. part_management : PyMenuGeneric - The part-management object. + Part management object. pm_file_management : PyMenuGeneric - The part-management file-management object. + File management object in the part management object. + fluent_version: FluentVersion + Version of Fluent in this session. """ - super().__init__(workflow=workflow, meshing=meshing) - self._meshing = meshing + super().__init__( + workflow=workflow, + meshing=meshing, + name="Fault-tolerant Meshing", + identifier="EnableComplexMeshing", + fluent_version=fluent_version, + ) self._part_management = part_management self._pm_file_management = pm_file_management - def reinitialize(self): - """Initialize a fault-tolerant workflow.""" - self._new_workflow("Fault-tolerant Meshing") - - def __getattribute__(self, item): - if ( - item != "reinitialize" - and not item.startswith("_") - and not self._meshing.GlobalSettings.EnableComplexMeshing() - ): - raise RuntimeError( - "'Fault-tolerant' objects are inaccessible from other workflows." - ) - return super().__getattribute__(item) - @property def part_management(self) -> Optional[PyMenuGeneric]: """Access part-management in fault-tolerant mode. @@ -143,7 +169,7 @@ def pm_file_management(self): Returns ------- Optional[PyMenuGeneric] - Part-management file-management object . + File management object in the part management object. """ return self._pm_file_management @@ -151,62 +177,66 @@ def pm_file_management(self): class TwoDimensionalMeshingWorkflow(MeshingWorkflow): """Provides 2D meshing specialization of the workflow wrapper.""" - def __init__(self, workflow: PyMenuGeneric, meshing: PyMenuGeneric) -> None: + def __init__( + self, + workflow: PyMenuGeneric, + meshing: PyMenuGeneric, + fluent_version: FluentVersion, + ) -> None: """Initialize TwoDimensionalMeshingWorkflow. Parameters ---------- workflow : PyMenuGeneric - The underlying workflow object. + Underlying workflow object. meshing : PyMenuGeneric - The meshing object. + Meshing object. + fluent_version: FluentVersion + Version of Fluent in this session. """ - super().__init__(workflow=workflow, meshing=meshing) - self._meshing = meshing - - def reinitialize(self) -> None: - """Initialize a 2D meshing workflow.""" - self._new_workflow(name="2D Meshing") - - def __getattribute__(self, item: str): - if ( - item != "reinitialize" - and not item.startswith("_") - and not self._meshing.GlobalSettings.EnablePrime2dMeshing() - ): - raise RuntimeError( - "'2D Meshing' objects are inaccessible from other workflows." - ) - return super().__getattribute__(item) + super().__init__( + workflow=workflow, + meshing=meshing, + name="2D Meshing", + identifier="EnablePrime2dMeshing", + fluent_version=fluent_version, + ) class TopologyBasedMeshingWorkflow(MeshingWorkflow): """Provides topology-based meshing specialization of the workflow wrapper.""" - def __init__(self, workflow: PyMenuGeneric, meshing: PyMenuGeneric) -> None: + def __init__( + self, + workflow: PyMenuGeneric, + meshing: PyMenuGeneric, + fluent_version: FluentVersion, + ) -> None: """Initialize TopologyBasedMeshingWorkflow. Parameters ---------- workflow : PyMenuGeneric - The underlying workflow object. + Underlying workflow object. meshing : PyMenuGeneric - The meshing object. + Meshing object. + fluent_version: FluentVersion + Version of Fluent in this session. """ - super().__init__(workflow=workflow, meshing=meshing) - self._meshing = meshing - - def reinitialize(self) -> None: - """Initialize a topology based meshing workflow.""" - self._new_workflow(name="Topology Based Meshing") - - def __getattribute__(self, item: str): - if ( - item != "reinitialize" - and not item.startswith("_") - and not self._meshing.GlobalSettings.EnablePrimeMeshing() - ): - raise RuntimeError( - "'Topology Based Meshing' objects are inaccessible from other workflows." - ) - return super().__getattribute__(item) + super().__init__( + workflow=workflow, + meshing=meshing, + name="Topology Based Meshing", + identifier="EnablePrimeMeshing", + fluent_version=fluent_version, + ) + + +class WorkflowMode(Enum): + """Provides an enum of supported Fluent meshing workflow modes.""" + + CLASSIC_MESHING_MODE = ClassicMeshingWorkflow + WATERTIGHT_MESHING_MODE = WatertightMeshingWorkflow + FAULT_TOLERANT_MESHING_MODE = FaultTolerantMeshingWorkflow + TWO_DIMENSIONAL_MESHING_MODE = TwoDimensionalMeshingWorkflow + TOPOLOGY_BASED_MESHING_MODE = TopologyBasedMeshingWorkflow diff --git a/src/ansys/fluent/core/meshing/topology_based.py b/src/ansys/fluent/core/meshing/topology_based.py deleted file mode 100644 index d8cd1b9e728..00000000000 --- a/src/ansys/fluent/core/meshing/topology_based.py +++ /dev/null @@ -1,25 +0,0 @@ -"""2D Meshing workflow module.""" - -from typing import Union - -from ..session_meshing import Meshing -from ..session_pure_meshing import PureMeshing -from .meshing_workflow import TopologyBasedMeshingWorkflow - - -def topology_based_workflow( - session: Union[Meshing, PureMeshing] -) -> TopologyBasedMeshingWorkflow: - """Meshing workflow wrapper, initialized as 2D Meshing. - - Parameters - ---------- - session: Union[Meshing, PureMeshing] - Meshing session object. - - Returns - ------- - TwoDimensionalMeshingWorkflow - 2D meshing workflow wrapper. - """ - return session.topology_based() diff --git a/src/ansys/fluent/core/meshing/watertight.py b/src/ansys/fluent/core/meshing/watertight.py deleted file mode 100644 index 4728d8faf2d..00000000000 --- a/src/ansys/fluent/core/meshing/watertight.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Watertight workflow module.""" - -from typing import Optional, Union - -from ..session_meshing import Meshing -from ..session_pure_meshing import PureMeshing -from .meshing_workflow import WatertightMeshingWorkflow - - -def watertight_workflow( - session: Union[Meshing, PureMeshing], geometry_file_name: Optional[str] = None -) -> WatertightMeshingWorkflow: - """Meshing workflow wrapper, initialized as watertight. - - Parameters - ---------- - session: Union[Meshing, PureMeshing] - Meshing session object. - geometry_file_name : Optional[str] - The path of a valid geometry file to import. Can be unset. - - Returns - ------- - WatertightMeshingWorkflow - Watertight meshing workflow wrapper. - """ - watertight = session.watertight() - if geometry_file_name: - import_geometry = watertight.import_geometry - import_geometry.file_name = geometry_file_name - import_geometry() - return watertight diff --git a/src/ansys/fluent/core/session_base_meshing.py b/src/ansys/fluent/core/session_base_meshing.py index 1806a5c6693..a1e7c42c44e 100644 --- a/src/ansys/fluent/core/session_base_meshing.py +++ b/src/ansys/fluent/core/session_base_meshing.py @@ -4,13 +4,7 @@ import logging from ansys.fluent.core.fluent_connection import FluentConnection -from ansys.fluent.core.meshing.meshing_workflow import ( - ClassicMeshingWorkflow, - FaultTolerantMeshingWorkflow, - TopologyBasedMeshingWorkflow, - TwoDimensionalMeshingWorkflow, - WatertightMeshingWorkflow, -) +from ansys.fluent.core.meshing.meshing_workflow import WorkflowMode from ansys.fluent.core.services.datamodel_se import PyMenuGeneric from ansys.fluent.core.services.datamodel_tui import TUIMenu from ansys.fluent.core.session_shared import _CODEGEN_MSG_DATAMODEL, _CODEGEN_MSG_TUI @@ -152,9 +146,10 @@ def _workflow_se(self): def workflow(self): """Datamodel root of workflow.""" if not self._old_workflow: - self._old_workflow = ClassicMeshingWorkflow( + self._old_workflow = WorkflowMode.CLASSIC_MESHING_MODE.value( self._workflow_se, self.meshing, + self.get_fluent_version(), ) return self._old_workflow @@ -162,9 +157,10 @@ def workflow(self): def watertight_workflow(self): """Datamodel root of workflow exposed in object-oriented manner.""" if not self._wt_workflow: - self._wt_workflow = WatertightMeshingWorkflow( + self._wt_workflow = WorkflowMode.WATERTIGHT_MESHING_MODE.value( self._workflow_se, self.meshing, + self.get_fluent_version(), ) return self._wt_workflow @@ -172,21 +168,23 @@ def watertight_workflow(self): def fault_tolerant_workflow(self): """Datamodel root of workflow exposed in object-oriented manner.""" if not self._ft_workflow: - self._ft_workflow = FaultTolerantMeshingWorkflow( + self._ft_workflow = WorkflowMode.FAULT_TOLERANT_MESHING_MODE.value( self._workflow_se, self.meshing, self.PartManagement, self.PMFileManagement, + self.get_fluent_version(), ) return self._ft_workflow @property def two_dimensional_meshing_workflow(self): - """Datamodel root of workflow exposed in object-oriented manner.""" + """Data model root of the workflow exposed in an object-oriented manner.""" if not self._2dm_workflow: - self._2dm_workflow = TwoDimensionalMeshingWorkflow( + self._2dm_workflow = WorkflowMode.TWO_DIMENSIONAL_MESHING_MODE.value( self._workflow_se, self.meshing, + self.get_fluent_version(), ) return self._2dm_workflow @@ -194,9 +192,10 @@ def two_dimensional_meshing_workflow(self): def topology_based_meshing_workflow(self): """Datamodel root of workflow exposed in object-oriented manner.""" if not self._tb_workflow: - self._tb_workflow = TopologyBasedMeshingWorkflow( + self._tb_workflow = WorkflowMode.TOPOLOGY_BASED_MESHING_MODE.value( self._workflow_se, self.meshing, + self.get_fluent_version(), ) return self._tb_workflow diff --git a/src/ansys/fluent/core/session_solver.py b/src/ansys/fluent/core/session_solver.py index 3b9181637ed..c0342359ed3 100644 --- a/src/ansys/fluent/core/session_solver.py +++ b/src/ansys/fluent/core/session_solver.py @@ -197,7 +197,9 @@ def _workflow_se(self): def workflow(self): """Datamodel root for workflow.""" if not self._workflow: - self._workflow = ClassicWorkflow(self._workflow_se, Solver) + self._workflow = ClassicWorkflow( + self._workflow_se, Solver, self.get_fluent_version() + ) return self._workflow @property diff --git a/src/ansys/fluent/core/workflow.py b/src/ansys/fluent/core/workflow.py index ab4e15b7eba..a4ef42a6ff9 100644 --- a/src/ansys/fluent/core/workflow.py +++ b/src/ansys/fluent/core/workflow.py @@ -16,6 +16,7 @@ PyMenuGeneric, PySingletonCommandArgumentsSubItem, ) +from ansys.fluent.core.utils.fluent_version import FluentVersion def camel_to_snake_case(camel_case_str: str) -> str: @@ -180,6 +181,7 @@ def __init__( _task_list=[], _task_objects={}, _dynamic_interface=command_source._dynamic_interface, + _fluent_version=command_source._fluent_version, ) ) @@ -979,21 +981,33 @@ def _add_child(self, state: Optional[dict] = None) -> None: state = state or {} if self._dynamic_interface: state.update({"add_child": "yes"}) - self.arguments.set_state(state) + self.arguments.update_dict(state) else: state.update({"AddChild": "yes"}) - self._task.Arguments.set_state(state) + self._task.Arguments.update_dict(state) - def add_child_and_update(self, state=None): + def add_child_and_update(self, state=None, defer_update=None): """Add a child to this CompoundTask and update. Parameters ---------- state : Optional[dict] Optional state. + defer_update : bool, default: False + Whether to defer the update. """ self._add_child(state) - self._task.AddChildAndUpdate() + if self._fluent_version >= FluentVersion.v241: + if defer_update is None: + defer_update = False + self._task.AddChildAndUpdate(DeferUpdate=defer_update) + else: + if defer_update is not None: + warnings.warn( + " The 'defer_update()' method is supported in Fluent 2024 R1 and later.", + UserWarning, + ) + self._task.AddChildAndUpdate() return self.last_child() def last_child(self) -> BaseTask: @@ -1060,7 +1074,12 @@ class Workflow: __call__() """ - def __init__(self, workflow: PyMenuGeneric, command_source: PyMenuGeneric) -> None: + def __init__( + self, + workflow: PyMenuGeneric, + command_source: PyMenuGeneric, + fluent_version: FluentVersion, + ) -> None: """Initialize WorkflowWrapper. Parameters @@ -1094,6 +1113,7 @@ def __init__(self, workflow: PyMenuGeneric, command_source: PyMenuGeneric) -> No "task_object", "workflow", } + self._fluent_version = fluent_version def task(self, name: str) -> BaseTask: """Get a TaskObject by name, in a ``BaseTask`` wrapper. The wrapper adds extra @@ -1394,7 +1414,12 @@ class ClassicWorkflow: __call__() """ - def __init__(self, workflow: PyMenuGeneric, command_source: PyMenuGeneric) -> None: + def __init__( + self, + workflow: PyMenuGeneric, + command_source: PyMenuGeneric, + fluent_version: FluentVersion, + ) -> None: """Initialize ClassicWorkflow. Parameters @@ -1410,6 +1435,7 @@ def __init__(self, workflow: PyMenuGeneric, command_source: PyMenuGeneric) -> No threading.RLock() ) # TODO: sort out issues with these un-used variables. self._dynamic_interface = False + self._fluent_version = fluent_version @property def TaskObject(self) -> TaskContainer: diff --git a/tests/test_meshing_workflow.py b/tests/test_meshing_workflow.py index b73cd711f34..cc1dfcba038 100644 --- a/tests/test_meshing_workflow.py +++ b/tests/test_meshing_workflow.py @@ -11,7 +11,6 @@ shared_watertight_workflow_session, ) -from ansys.fluent.core.meshing.faulttolerant import fault_tolerant_workflow from ansys.fluent.core.utils.fluent_version import FluentVersion @@ -364,29 +363,6 @@ def test_iterate_meshing_workflow_task_container(new_mesh_session): assert tasks[0].name() == "Import Geometry" -@pytest.mark.fluent_version("==23.2") -@pytest.mark.codegen_required -def test_fault_tolerant_workflow(exhaust_system_geometry, new_mesh_session): - fault_tolerant = fault_tolerant_workflow(session=new_mesh_session) - part_management = fault_tolerant.part_management - file_name = exhaust_system_geometry - part_management.LoadFmdFile(FilePath=file_name) - part_management.MoveCADComponentsToNewObject( - Paths=[r"/Bottom,1", r"/Left,1", r"/Others,1", r"/Right,1", r"/Top,1"] - ) - part_management.Node["Object"].Rename(NewName=r"Engine") - import_cad = fault_tolerant.task("Import CAD and Part Management") - import_cad.Arguments.setState( - { - r"CreateObjectPer": r"Custom", - r"FMDFileName": file_name, - r"FileLoaded": r"yes", - r"ObjectSetting": r"DefaultObjectSetting", - } - ) - import_cad() - - @pytest.mark.codegen_required def test_modified_workflow(new_mesh_session): meshing = new_mesh_session @@ -451,3 +427,165 @@ def test_new_workflow_structure(new_mesh_session): msg.value.args[0] == "'WatertightMeshingWorkflow' object has no attribute 'TaskObject'" ) + + +@pytest.mark.nightly +@pytest.mark.codegen_required +@pytest.mark.fluent_version(">=24.2") +def test_new_2d_meshing_workflow(new_mesh_session): + # Import geometry + # import_file_name = examples.download_file( + # "mixing_elbow.pmdb", "pyfluent/mixing_elbow" + # ) + import_file_name = r"C:\ANSYSDev\PyFluent_Dev_01\pyfluent\NACA0012.fmd" + meshing = new_mesh_session + meshing.workflow.InitializeWorkflow(WorkflowType="2D Meshing") + meshing.workflow.TaskObject["Load CAD Geometry"].Arguments.set_state( + { + r"FileName": import_file_name, + r"LengthUnit": r"mm", + r"Refaceting": { + r"Refacet": False, + }, + } + ) + meshing.workflow.TaskObject["Load CAD Geometry"].Execute() + + meshing.workflow.TaskObject["Update Regions"].Execute() + meshing.workflow.TaskObject["Update Boundaries"].Arguments.set_state( + { + r"SelectionType": r"zone", + } + ) + meshing.workflow.TaskObject["Update Boundaries"].Execute() + + meshing.workflow.TaskObject["Define Global Sizing"].Arguments.set_state( + { + r"CurvatureNormalAngle": 20, + r"MaxSize": 2000, + r"MinSize": 5, + r"SizeFunctions": r"Curvature", + } + ) + meshing.workflow.TaskObject["Define Global Sizing"].Execute() + + meshing.workflow.TaskObject["Add Local Sizing"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BOIControlName": r"boi_1", + r"BOIExecution": r"Body Of Influence", + r"BOIFaceLabelList": [r"boi"], + r"BOISize": 50, + r"BOIZoneorLabel": r"label", + r"DrawSizeControl": True, + } + ) + meshing.workflow.TaskObject["Add Local Sizing"].AddChildAndUpdate(DeferUpdate=False) + + meshing.workflow.TaskObject["Add Local Sizing"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BOIControlName": r"edgesize_1", + r"BOIExecution": r"Edge Size", + r"BOISize": 5, + r"BOIZoneorLabel": r"label", + r"DrawSizeControl": True, + r"EdgeLabelList": [r"airfoil-te"], + } + ) + meshing.workflow.TaskObject["Add Local Sizing"].AddChildAndUpdate(DeferUpdate=False) + + meshing.workflow.TaskObject["Add Local Sizing"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BOIControlName": r"curvature_1", + r"BOICurvatureNormalAngle": 10, + r"BOIExecution": r"Curvature", + r"BOIMaxSize": 2, + r"BOIMinSize": 1.5, + r"BOIScopeTo": r"edges", + r"BOIZoneorLabel": r"label", + r"DrawSizeControl": True, + r"EdgeLabelList": [r"airfoil"], + } + ) + meshing.workflow.TaskObject["Add Local Sizing"].AddChildAndUpdate(DeferUpdate=False) + + meshing.workflow.TaskObject["Add 2D Boundary Layers"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BLControlName": r"aspect-ratio_1", + r"NumberOfLayers": 4, + r"OffsetMethodType": r"aspect-ratio", + } + ) + meshing.workflow.TaskObject["Add 2D Boundary Layers"].AddChildAndUpdate( + DeferUpdate=False + ) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state( + { + r"Surface2DPreferences": { + r"MergeEdgeZonesBasedOnLabels": r"no", + r"MergeFaceZonesBasedOnLabels": r"no", + r"ShowAdvancedOptions": True, + }, + } + ) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Execute() + + meshing.workflow.TaskObject["aspect-ratio_1"].Revert() + meshing.workflow.TaskObject["aspect-ratio_1"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BLControlName": r"uniform_1", + r"FirstLayerHeight": 2, + r"NumberOfLayers": 4, + r"OffsetMethodType": r"uniform", + } + ) + meshing.workflow.TaskObject["aspect-ratio_1"].Execute() + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state(None) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state( + { + r"Surface2DPreferences": { + r"MergeEdgeZonesBasedOnLabels": r"no", + r"MergeFaceZonesBasedOnLabels": r"no", + r"ShowAdvancedOptions": True, + }, + } + ) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Execute() + meshing.workflow.TaskObject["uniform_1"].Revert() + meshing.workflow.TaskObject["uniform_1"].Arguments.set_state( + { + r"AddChild": r"yes", + r"BLControlName": r"smooth-transition_1", + r"FirstLayerHeight": 2, + r"NumberOfLayers": 7, + r"OffsetMethodType": r"smooth-transition", + } + ) + meshing.workflow.TaskObject["uniform_1"].Execute() + + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state(None) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Arguments.set_state( + { + r"Surface2DPreferences": { + r"MergeEdgeZonesBasedOnLabels": r"no", + r"MergeFaceZonesBasedOnLabels": r"no", + r"ShowAdvancedOptions": True, + }, + } + ) + meshing.workflow.TaskObject["Generate the Surface Mesh"].Execute() + + meshing.workflow.TaskObject["Export Fluent 2D Mesh"].Arguments.set_state( + { + r"FileName": r"C:\ANSYSDev\PyFluent_Dev_01\pyfluent\out\case1.msh.h5", + } + ) + meshing.workflow.TaskObject["Export Fluent 2D Mesh"].Execute() + + # Switch to solution mode + solver = meshing.switch_to_solver() + assert not solver diff --git a/tests/test_new_meshing_workflow.py b/tests/test_new_meshing_workflow.py index 6965c013410..5ee54f38fea 100644 --- a/tests/test_new_meshing_workflow.py +++ b/tests/test_new_meshing_workflow.py @@ -4,7 +4,6 @@ import pytest from ansys.fluent.core import examples -from ansys.fluent.core.meshing.watertight import watertight_workflow from tests.test_datamodel_service import disable_datamodel_cache # noqa: F401 @@ -473,6 +472,123 @@ def test_new_fault_tolerant_workflow(new_mesh_session): assert solver +@pytest.mark.nightly +@pytest.mark.codegen_required +@pytest.mark.fluent_version(">=24.2") +def test_new_2d_meshing_workflow(new_mesh_session): + # Import geometry + import_file_name = examples.download_file("NACA0012.fmd", "pyfluent/airfoils") + two_dim_mesh = new_mesh_session.two_dimensional_meshing() + + two_dim_mesh.load_cad_geometry_2d.file_name = import_file_name + two_dim_mesh.load_cad_geometry_2d.length_unit = "mm" + two_dim_mesh.load_cad_geometry_2d.refaceting.refacet = False + two_dim_mesh.load_cad_geometry_2d() + + # Set regions and boundaries + two_dim_mesh.update_regions_2d() + two_dim_mesh.update_boundaries_2d.selection_type = "zone" + two_dim_mesh.update_boundaries_2d() + + # Define global sizing + two_dim_mesh.define_global_sizing_2d.curvature_normal_angle = 20 + two_dim_mesh.define_global_sizing_2d.max_size = 2000.0 + two_dim_mesh.define_global_sizing_2d.min_size = 5.0 + two_dim_mesh.define_global_sizing_2d.size_functions = "Curvature" + two_dim_mesh.define_global_sizing_2d() + + # Add local sizing + two_dim_mesh.add_local_sizing_2d.add_child = "yes" + two_dim_mesh.add_local_sizing_2d.boi_control_name = "boi_1" + two_dim_mesh.add_local_sizing_2d.boi_execution = "Body Of Influence" + two_dim_mesh.add_local_sizing_2d.boi_face_label_list = ["boi"] + two_dim_mesh.add_local_sizing_2d.boi_size = 50.0 + two_dim_mesh.add_local_sizing_2d.boi_zoneor_label = "label" + two_dim_mesh.add_local_sizing_2d.draw_size_control = True + two_dim_mesh.add_local_sizing_2d.add_child_and_update(defer_update=False) + + two_dim_mesh.add_local_sizing_2d.add_child = "yes" + two_dim_mesh.add_local_sizing_2d.boi_control_name = "edgesize_1" + two_dim_mesh.add_local_sizing_2d.boi_execution = "Edge Size" + two_dim_mesh.add_local_sizing_2d.boi_size = 5.0 + two_dim_mesh.add_local_sizing_2d.boi_zoneor_label = "label" + two_dim_mesh.add_local_sizing_2d.draw_size_control = True + two_dim_mesh.add_local_sizing_2d.edge_label_list = ["airfoil-te"] + two_dim_mesh.add_local_sizing_2d.add_child_and_update(defer_update=False) + + two_dim_mesh.add_local_sizing_2d.add_child = "yes" + two_dim_mesh.add_local_sizing_2d.boi_control_name = "curvature_1" + two_dim_mesh.add_local_sizing_2d.boi_curvature_normal_angle = 10 + two_dim_mesh.add_local_sizing_2d.boi_execution = "Curvature" + two_dim_mesh.add_local_sizing_2d.boi_max_size = 2 + two_dim_mesh.add_local_sizing_2d.boi_min_size = 1.5 + two_dim_mesh.add_local_sizing_2d.boi_scope_to = "edges" + two_dim_mesh.add_local_sizing_2d.boi_zoneor_label = "label" + two_dim_mesh.add_local_sizing_2d.draw_size_control = True + two_dim_mesh.add_local_sizing_2d.edge_label_list = ["airfoil"] + two_dim_mesh.add_local_sizing_2d.add_child_and_update(defer_update=False) + + # Add boundary layer + two_dim_mesh.add_2d_boundary_layers.add_child = "yes" + two_dim_mesh.add_2d_boundary_layers.bl_control_name = "aspect-ratio_1" + two_dim_mesh.add_2d_boundary_layers.number_of_layers = 4 + two_dim_mesh.add_2d_boundary_layers.offset_method_type = "aspect-ratio" + two_dim_mesh.add_2d_boundary_layers.add_child_and_update(defer_update=False) + + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_edge_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_face_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.show_advanced_options = ( + True + ) + two_dim_mesh.generate_initial_surface_mesh() + + two_dim_mesh.task("aspect-ratio_1").revert() + two_dim_mesh.task("aspect-ratio_1").add_child = "yes" + two_dim_mesh.task("aspect-ratio_1").bl_control_name = "uniform_1" + two_dim_mesh.task("aspect-ratio_1").first_layer_height = 2 + two_dim_mesh.task("aspect-ratio_1").number_of_layers = 4 + two_dim_mesh.task("aspect-ratio_1").offset_method_type = "uniform" + two_dim_mesh.task("aspect-ratio_1")() + + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_edge_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_face_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.show_advanced_options = ( + True + ) + two_dim_mesh.generate_initial_surface_mesh() + + two_dim_mesh.task("uniform_1").revert() + two_dim_mesh.task("uniform_1").add_child = "yes" + two_dim_mesh.task("uniform_1").bl_control_name = "smooth-transition_1" + two_dim_mesh.task("uniform_1").first_layer_height = 2 + two_dim_mesh.task("uniform_1").number_of_layers = 7 + two_dim_mesh.task("uniform_1").offset_method_type = "smooth-transition" + two_dim_mesh.task("uniform_1")() + + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_edge_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.merge_face_zones_based_on_labels = ( + "no" + ) + two_dim_mesh.generate_initial_surface_mesh.surface2_d_preferences.show_advanced_options = ( + True + ) + two_dim_mesh.generate_initial_surface_mesh() + + # Switch to solution mode + solver = new_mesh_session.switch_to_solver() + assert solver + + @pytest.mark.codegen_required @pytest.mark.fluent_version(">=23.2") def test_updating_state_in_new_meshing_workflow(new_mesh_session): @@ -565,9 +681,9 @@ def test_workflow_and_data_model_methods_new_meshing_workflow(new_mesh_session): @pytest.mark.fluent_version(">=23.2") @pytest.mark.codegen_required def test_watertight_workflow(mixing_elbow_geometry, new_mesh_session): - watertight = watertight_workflow( - geometry_file_name=mixing_elbow_geometry, session=new_mesh_session - ) + watertight = new_mesh_session.watertight() + watertight.import_geometry.file_name = mixing_elbow_geometry + watertight.import_geometry() add_local_sizing = watertight.add_local_sizing assert not add_local_sizing.ordered_children() add_local_sizing._add_child(state={"boi_face_label_list": ["cold-inlet"]}) @@ -583,9 +699,9 @@ def test_watertight_workflow(mixing_elbow_geometry, new_mesh_session): @pytest.mark.fluent_version(">=23.2") @pytest.mark.codegen_required def test_watertight_workflow_children(mixing_elbow_geometry, new_mesh_session): - watertight = watertight_workflow( - geometry_file_name=mixing_elbow_geometry, session=new_mesh_session - ) + watertight = new_mesh_session.watertight() + watertight.import_geometry.file_name = mixing_elbow_geometry + watertight.import_geometry() add_local_sizing = watertight.add_local_sizing assert not add_local_sizing.ordered_children() add_local_sizing._add_child(state={"boi_face_label_list": ["cold-inlet"]}) @@ -619,9 +735,9 @@ def test_watertight_workflow_children(mixing_elbow_geometry, new_mesh_session): @pytest.mark.fluent_version(">=23.2") @pytest.mark.codegen_required def test_watertight_workflow_dynamic_interface(mixing_elbow_geometry, new_mesh_session): - watertight = watertight_workflow( - geometry_file_name=mixing_elbow_geometry, session=new_mesh_session - ) + watertight = new_mesh_session.watertight() + watertight.import_geometry.file_name = mixing_elbow_geometry + watertight.import_geometry() create_volume_mesh = watertight.create_volume_mesh assert create_volume_mesh is not None watertight.delete_tasks(list_of_tasks=["create_volume_mesh"]) @@ -651,6 +767,29 @@ def test_watertight_workflow_dynamic_interface(mixing_elbow_geometry, new_mesh_s watertight.create_volume_mesh +@pytest.mark.fluent_version("==23.2") +@pytest.mark.codegen_required +def test_fault_tolerant_workflow(exhaust_system_geometry, new_mesh_session): + fault_tolerant = new_mesh_session.fault_tolerant() + part_management = fault_tolerant.part_management + file_name = exhaust_system_geometry + part_management.LoadFmdFile(FilePath=file_name) + part_management.MoveCADComponentsToNewObject( + Paths=[r"/Bottom,1", r"/Left,1", r"/Others,1", r"/Right,1", r"/Top,1"] + ) + part_management.Node["Object"].Rename(NewName=r"Engine") + import_cad = fault_tolerant.task("Import CAD and Part Management") + import_cad.Arguments.setState( + { + r"CreateObjectPer": r"Custom", + r"FMDFileName": file_name, + r"FileLoaded": r"yes", + r"ObjectSetting": r"DefaultObjectSetting", + } + ) + import_cad() + + @pytest.mark.fluent_version(">=23.2") @pytest.mark.codegen_required def test_extended_wrapper(new_mesh_session, mixing_elbow_geometry):