diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2bc2a0b5ee9..fee3eadf280 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: hooks: - id: black args: [ - --target-version=py39 + --target-version=py310 ] - repo: https://github.com/pycqa/isort diff --git a/codegen/allapigen.py b/codegen/allapigen.py index a33df8a1766..74ef2cd4702 100644 --- a/codegen/allapigen.py +++ b/codegen/allapigen.py @@ -3,7 +3,8 @@ from time import time from ansys.fluent.core import CODEGEN_OUTDIR, FluentMode, FluentVersion, launch_fluent -from ansys.fluent.core.codegen import StaticInfoType, allapigen, print_fluent_version +from ansys.fluent.core.codegen import StaticInfoType, allapigen +from ansys.fluent.core.codegen.print_fluent_version import print_fluent_version from ansys.fluent.core.search import _generate_api_data from ansys.fluent.core.utils.fluent_version import get_version_for_file_name @@ -60,7 +61,7 @@ t1 = time() print(f"Time to fetch static info: {t1 - t0:.2f} seconds") CODEGEN_OUTDIR.mkdir(parents=True, exist_ok=True) - print_fluent_version.generate(version, solver.scheme_eval.scheme_eval) + print_fluent_version(solver._app_utilities) solver.exit() allapigen.generate(version, static_infos) diff --git a/src/ansys/fluent/core/codegen/print_fluent_version.py b/src/ansys/fluent/core/codegen/print_fluent_version.py index 100762ca926..3e6b5aac7ae 100644 --- a/src/ansys/fluent/core/codegen/print_fluent_version.py +++ b/src/ansys/fluent/core/codegen/print_fluent_version.py @@ -1,26 +1,21 @@ """Module to write Fluent version information.""" -from ansys.fluent.core import CODEGEN_OUTDIR, launch_fluent -from ansys.fluent.core.utils.fluent_version import get_version_for_file_name +from ansys.fluent.core import CODEGEN_OUTDIR, FluentVersion, launch_fluent -def print_fluent_version(version: str, scheme_eval): +def print_fluent_version(app_utilities): """Write Fluent version information to file.""" + version = FluentVersion(app_utilities.get_product_version()).number + build_info = app_utilities.get_build_info() version_file = (CODEGEN_OUTDIR / f"fluent_version_{version}.py").resolve() with open(version_file, "w", encoding="utf8") as f: f.write(f'FLUENT_VERSION = "{version}"\n') - f.write(f'FLUENT_BUILD_TIME = "{scheme_eval("(inquire-build-time)")}"\n') - f.write(f'FLUENT_BUILD_ID = "{scheme_eval("(inquire-build-id)")}"\n') - f.write(f'FLUENT_REVISION = "{scheme_eval("(inquire-src-vcs-id)")}"\n') - f.write(f'FLUENT_BRANCH = "{scheme_eval("(inquire-src-vcs-branch)")}"\n') - - -def generate(version: str, scheme_eval): - """Write Fluent version information.""" - print_fluent_version(version, scheme_eval) + f.write(f'FLUENT_BUILD_TIME = "{build_info["build_time"]}"\n') + f.write(f'FLUENT_BUILD_ID = "{build_info["build_id"]}"\n') + f.write(f'FLUENT_REVISION = "{build_info["vcs_revision"]}"\n') + f.write(f'FLUENT_BRANCH = "{build_info["vcs_branch"]}"\n') if __name__ == "__main__": solver = launch_fluent() - version = get_version_for_file_name(session=solver) - generate(version, solver.scheme_eval.scheme_eval) + print_fluent_version(solver._app_utilities) diff --git a/src/ansys/fluent/core/fluent_connection.py b/src/ansys/fluent/core/fluent_connection.py index 3f32b293acd..3759c2ec91c 100644 --- a/src/ansys/fluent/core/fluent_connection.py +++ b/src/ansys/fluent/core/fluent_connection.py @@ -21,6 +21,10 @@ import ansys.fluent.core as pyfluent from ansys.fluent.core.services import service_creator +from ansys.fluent.core.services.app_utilities import ( + AppUtilitiesOld, + AppUtilitiesService, +) from ansys.fluent.core.services.scheme_eval import SchemeEvalService from ansys.fluent.core.utils.execution import timeout_exec, timeout_loop from ansys.fluent.core.utils.file_transfer_service import RemoteFileTransferStrategy @@ -246,15 +250,24 @@ def __init__(self, create_grpc_service, error_state): self.scheme_eval = service_creator("scheme_eval").create( self._scheme_eval_service ) + if ( + pyfluent.FluentVersion(self.scheme_eval.version) + < pyfluent.FluentVersion.v252 + ): + self._app_utilities = AppUtilitiesOld(self.scheme_eval) + else: + self._app_utilities_service = create_grpc_service( + AppUtilitiesService, error_state + ) + self._app_utilities = service_creator("app_utilities").create( + self._app_utilities_service + ) @property def product_build_info(self) -> str: """Get Fluent build information.""" - build_time = self.scheme_eval.scheme_eval("(inquire-build-time)") - build_id = self.scheme_eval.scheme_eval("(inquire-build-id)") - rev = self.scheme_eval.scheme_eval("(inquire-src-vcs-id)") - branch = self.scheme_eval.scheme_eval("(inquire-src-vcs-branch)") - return f"Build Time: {build_time} Build Id: {build_id} Revision: {rev} Branch: {branch}" + build_info = self._app_utilities.get_build_info() + return f'Build Time: {build_info["build_time"]} Build Id: {build_info["build_id"]} Revision: {build_info["vcs_revision"]} Branch: {build_info["vcs_branch"]}' def get_cortex_connection_properties(self): """Get connection properties of Fluent.""" @@ -263,10 +276,12 @@ def get_cortex_connection_properties(self): try: logger.info(self.product_build_info) logger.debug("Obtaining Cortex connection properties...") - fluent_host_pid = self.scheme_eval.scheme_eval("(cx-client-id)") - cortex_host = self.scheme_eval.scheme_eval("(cx-cortex-host)") - cortex_pid = self.scheme_eval.scheme_eval("(cx-cortex-id)") - cortex_pwd = self.scheme_eval.scheme_eval("(cortex-pwd)") + cortex_info = self._app_utilities.get_controller_process_info() + solver_info = self._app_utilities.get_solver_process_info() + fluent_host_pid = solver_info["process_id"] + cortex_host = cortex_info["hostname"] + cortex_pid = cortex_info["process_id"] + cortex_pwd = cortex_info["working_directory"] logger.debug("Cortex connection properties successfully obtained.") except _InactiveRpcError: logger.warning( @@ -282,22 +297,11 @@ def get_cortex_connection_properties(self): def get_mode(self): """Get the mode of a running fluent session.""" - from ansys.fluent.core import FluentMode - - if self.scheme_eval.scheme_eval("(cx-solver-mode?)"): - mode_str = self.scheme_eval.scheme_eval('(getenv "PRJAPP_APP")') - if mode_str == "flaero_server": - return FluentMode.SOLVER_AERO - elif mode_str == "flicing": - return FluentMode.SOLVER_ICING - else: - return FluentMode.SOLVER - else: - return FluentMode.MESHING + return self._app_utilities.get_app_mode() def exit_server(self): """Exits the server.""" - self.scheme_eval.exec(("(exit-server)",)) + self._app_utilities.exit() def _pid_exists(pid): diff --git a/src/ansys/fluent/core/journaling.py b/src/ansys/fluent/core/journaling.py index 6d39bbb7529..c4f86918d2b 100644 --- a/src/ansys/fluent/core/journaling.py +++ b/src/ansys/fluent/core/journaling.py @@ -4,14 +4,14 @@ class Journal: """Control the writing of Fluent Python journals.""" - def __init__(self, scheme_eval): + def __init__(self, app_utilities): """__init__ method of Journal class.""" - self.scheme_eval = scheme_eval + self._app_utilities = app_utilities def start(self, file_name: str): """Start writing a Fluent Python journal at the specified file_name.""" - self.scheme_eval.exec([f'(api-start-python-journal "{file_name}")']) + self._app_utilities.start_python_journal(journal_name=file_name) def stop(self): """Stop writing the Fluent Python journal.""" - self.scheme_eval.exec(["(api-stop-python-journal)"]) + self._app_utilities.stop_python_journal() diff --git a/src/ansys/fluent/core/services/__init__.py b/src/ansys/fluent/core/services/__init__.py index 6cc7b15a406..3a3b9a62330 100644 --- a/src/ansys/fluent/core/services/__init__.py +++ b/src/ansys/fluent/core/services/__init__.py @@ -1,5 +1,6 @@ """Provides a module to create gRPC services.""" +from ansys.fluent.core.services.app_utilities import AppUtilities from ansys.fluent.core.services.batch_ops import BatchOpsService from ansys.fluent.core.services.datamodel_se import ( DatamodelService as DatamodelService_SE, @@ -22,6 +23,7 @@ from ansys.fluent.core.services.transcript import TranscriptService _service_cls_by_name = { + "app_utilities": AppUtilities, "health_check": HealthCheckService, "datamodel": DatamodelService_SE, "tui": DatamodelService_TUI, diff --git a/src/ansys/fluent/core/services/api_upgrade.py b/src/ansys/fluent/core/services/api_upgrade.py index b90c8647ec8..f8e957fde5a 100644 --- a/src/ansys/fluent/core/services/api_upgrade.py +++ b/src/ansys/fluent/core/services/api_upgrade.py @@ -3,7 +3,7 @@ import os from typing import TypeVar -from ansys.fluent.core.services.scheme_eval import SchemeEval +from ansys.fluent.core.services.app_utilities import AppUtilities from ansys.fluent.core.utils.fluent_version import FluentVersion _TApiUpgradeAdvisor = TypeVar("_TApiUpgradeAdvisor", bound="ApiUpgradeAdvisor") @@ -12,11 +12,17 @@ class ApiUpgradeAdvisor: """API upgrade advisor.""" - def __init__(self, scheme_eval: SchemeEval, version: str, mode: str) -> None: + def __init__( + self, + app_utilities: AppUtilities, + version: str, + mode: str, + ) -> None: """Initialize ApiUpgradeAdvisor.""" - self._scheme_eval = scheme_eval.scheme_eval + self._app_utilities = app_utilities self._version = version self._mode = mode + self._id = None def _can_advise(self) -> bool: return ( @@ -27,16 +33,12 @@ def _can_advise(self) -> bool: def __enter__(self) -> _TApiUpgradeAdvisor: if self._can_advise(): - self._scheme_eval("(define pyfluent-journal-str-port (open-output-string))") - self._scheme_eval("(api-echo-python-port pyfluent-journal-str-port)") + self._id = self._app_utilities.start_python_journal() return self def __exit__(self, exc_type, exc_value, exc_tb) -> None: if self._can_advise(): - self._scheme_eval("(api-unecho-python-port pyfluent-journal-str-port)") - journal_str = self._scheme_eval( - "(close-output-port pyfluent-journal-str-port)" - ).strip() + journal_str = (self._app_utilities.stop_python_journal(self._id)).strip() if ( journal_str.startswith("solver.") and not journal_str.startswith("solver.tui") diff --git a/src/ansys/fluent/core/services/app_utilities.py b/src/ansys/fluent/core/services/app_utilities.py new file mode 100644 index 00000000000..86ce0147e54 --- /dev/null +++ b/src/ansys/fluent/core/services/app_utilities.py @@ -0,0 +1,407 @@ +"""Wrappers over AppUtilities gRPC service of Fluent.""" + +from enum import Enum +from typing import List, Tuple + +import grpc + +from ansys.api.fluent.v0 import app_utilities_pb2 as AppUtilitiesProtoModule +from ansys.api.fluent.v0 import app_utilities_pb2_grpc as AppUtilitiesGrpcModule +from ansys.fluent.core.services.interceptors import ( + BatchInterceptor, + ErrorStateInterceptor, + GrpcErrorInterceptor, + TracingInterceptor, +) +from ansys.fluent.core.streaming_services.events_streaming import SolverEvent + + +class AppUtilitiesService: + """AppUtilities Service.""" + + def __init__( + self, channel: grpc.Channel, metadata: List[Tuple[str, str]], fluent_error_state + ): + """__init__ method of AppUtilities class.""" + intercept_channel = grpc.intercept_channel( + channel, + GrpcErrorInterceptor(), + ErrorStateInterceptor(fluent_error_state), + TracingInterceptor(), + BatchInterceptor(), + ) + self._stub = AppUtilitiesGrpcModule.AppUtilitiesStub(intercept_channel) + self._metadata = metadata + + def get_product_version( + self, request: AppUtilitiesProtoModule.GetProductVersionRequest + ) -> AppUtilitiesProtoModule.GetProductVersionResponse: + """Get product version RPC of AppUtilities service.""" + return self._stub.GetProductVersion(request, metadata=self._metadata) + + def get_build_info( + self, request: AppUtilitiesProtoModule.GetBuildInfoRequest + ) -> AppUtilitiesProtoModule.GetBuildInfoResponse: + """Get build info RPC of AppUtilities service.""" + return self._stub.GetBuildInfo(request, metadata=self._metadata) + + def get_controller_process_info( + self, request: AppUtilitiesProtoModule.GetControllerProcessInfoRequest + ) -> AppUtilitiesProtoModule.GetControllerProcessInfoResponse: + """Get controller process info RPC of AppUtilities service.""" + return self._stub.GetControllerProcessInfo(request, metadata=self._metadata) + + def get_solver_process_info( + self, request: AppUtilitiesProtoModule.GetSolverProcessInfoRequest + ) -> AppUtilitiesProtoModule.GetSolverProcessInfoResponse: + """Get solver process info RPC of AppUtilities service.""" + return self._stub.GetSolverProcessInfo(request, metadata=self._metadata) + + def get_app_mode( + self, request: AppUtilitiesProtoModule.GetAppModeRequest + ) -> AppUtilitiesProtoModule.GetAppModeResponse: + """Get app mode RPC of AppUtilities service.""" + return self._stub.GetAppMode(request, metadata=self._metadata) + + def start_python_journal( + self, request: AppUtilitiesProtoModule.StartPythonJournalRequest + ) -> AppUtilitiesProtoModule.StartPythonJournalResponse: + """Start python journal RPC of AppUtilities service.""" + return self._stub.StartPythonJournal(request, metadata=self._metadata) + + def stop_python_journal( + self, request: AppUtilitiesProtoModule.StopPythonJournalRequest + ) -> AppUtilitiesProtoModule.StopPythonJournalResponse: + """Stop python journal RPC of AppUtilities service.""" + return self._stub.StopPythonJournal(request, metadata=self._metadata) + + def is_beta_enabled( + self, request: AppUtilitiesProtoModule.IsBetaEnabledRequest + ) -> AppUtilitiesProtoModule.IsBetaEnabledResponse: + """Is beta enabled RPC of AppUtilities service.""" + return self._stub.IsBetaEnabled(request, metadata=self._metadata) + + def is_wildcard( + self, request: AppUtilitiesProtoModule.IsWildcardRequest + ) -> AppUtilitiesProtoModule.IsWildcardResponse: + """Is wildcard RPC of AppUtilities service.""" + return self._stub.IsWildcard(request, metadata=self._metadata) + + def is_solution_data_available( + self, request: AppUtilitiesProtoModule.IsSolutionDataAvailableRequest + ) -> AppUtilitiesProtoModule.IsSolutionDataAvailableResponse: + """Is solution data available RPC of AppUtilities service.""" + return self._stub.IsSolutionDataAvailable(request, metadata=self._metadata) + + def register_pause_on_solution_events( + self, request: AppUtilitiesProtoModule.RegisterPauseOnSolutionEventsRequest + ) -> AppUtilitiesProtoModule.RegisterPauseOnSolutionEventsResponse: + """Register on pause solution events RPC of AppUtilities service.""" + return self._stub.RegisterPauseOnSolutionEvents( + request, metadata=self._metadata + ) + + def resume_on_solution_event( + self, request: AppUtilitiesProtoModule.ResumeOnSolutionEventRequest + ) -> AppUtilitiesProtoModule.ResumeOnSolutionEventResponse: + """Resume on solution event RPC of AppUtilities service.""" + return self._stub.ResumeOnSolutionEvent(request, metadata=self._metadata) + + def unregister_pause_on_solution_events( + self, request: AppUtilitiesProtoModule.UnregisterPauseOnSolutionEventsRequest + ) -> AppUtilitiesProtoModule.UnregisterPauseOnSolutionEventsResponse: + """Unregister on pause solution events RPC of AppUtilities service.""" + return self._stub.UnregisterPauseOnSolutionEvents( + request, metadata=self._metadata + ) + + def exit( + self, request: AppUtilitiesProtoModule.ExitRequest + ) -> AppUtilitiesProtoModule.ExitResponse: + """Exit RPC of AppUtilities service.""" + return self._stub.Exit(request, metadata=self._metadata) + + +class AppUtilitiesOld: + """AppUtilitiesOld.""" + + def __init__(self, scheme_eval): + """__init__ method of AppUtilitiesOld class.""" + self.scheme_eval = scheme_eval + + def get_product_version(self) -> str: + """Get product version.""" + return self.scheme_eval.version + + def get_build_info(self) -> dict: + """Get build info.""" + build_time = self.scheme_eval.scheme_eval("(inquire-build-time)") + build_id = self.scheme_eval.scheme_eval("(inquire-build-id)") + vcs_revision = self.scheme_eval.scheme_eval("(inquire-src-vcs-id)") + vcs_branch = self.scheme_eval.scheme_eval("(inquire-src-vcs-branch)") + return { + "build_time": build_time, + "build_id": build_id, + "vcs_revision": vcs_revision, + "vcs_branch": vcs_branch, + } + + def get_controller_process_info(self) -> dict: + """Get controller process info.""" + cortex_host = self.scheme_eval.scheme_eval("(cx-cortex-host)") + cortex_pid = self.scheme_eval.scheme_eval("(cx-cortex-id)") + cortex_pwd = self.scheme_eval.scheme_eval("(cortex-pwd)") + return { + "hostname": cortex_host, + "process_id": cortex_pid, + "working_directory": cortex_pwd, + } + + def get_solver_process_info(self) -> dict: + """Get solver process info.""" + fluent_host = self.scheme_eval.scheme_eval("(cx-client-host)") + fluent_pid = self.scheme_eval.scheme_eval("(cx-client-id)") + fluent_pwd = self.scheme_eval.scheme_eval("(cx-send '(cx-client-pwd))") + return { + "hostname": fluent_host, + "process_id": fluent_pid, + "working_directory": fluent_pwd, + } + + def get_app_mode(self) -> Enum: + """Get app mode.""" + from ansys.fluent.core import FluentMode + + if self.scheme_eval.scheme_eval("(cx-solver-mode?)"): + mode_str = self.scheme_eval.scheme_eval('(getenv "PRJAPP_APP")') + if mode_str == "flaero_server": + return FluentMode.SOLVER_AERO + elif mode_str == "flicing": + return FluentMode.SOLVER_ICING + else: + return FluentMode.SOLVER + else: + return FluentMode.MESHING + + def start_python_journal(self, journal_name: str | None = None) -> int: + """Start python journal.""" + if journal_name: + self.scheme_eval.exec([f'(api-start-python-journal "{journal_name}")']) + else: + self.scheme_eval.scheme_eval( + "(define pyfluent-journal-str-port (open-output-string))" + ) + self.scheme_eval.scheme_eval( + "(api-echo-python-port pyfluent-journal-str-port)" + ) + return "1" + + def stop_python_journal(self, journal_id: str | None = None) -> str: + """Stop python journal.""" + if journal_id: + self.scheme_eval.scheme_eval( + "(api-unecho-python-port pyfluent-journal-str-port)" + ) + journal_str = self.scheme_eval.scheme_eval( + "(close-output-port pyfluent-journal-str-port)" + ) + return journal_str + else: + self.scheme_eval.exec(["(api-stop-python-journal)"]) + + def is_beta_enabled(self) -> bool: + """Is beta enabled.""" + return self.scheme_eval.scheme_eval("(is-beta-feature-available?)") + + def is_wildcard(self, input: str | None = None) -> bool: + """Is wildcard.""" + return self.scheme_eval.scheme_eval(f'(has-fnmatch-wild-card? "{input}")') + + def is_solution_data_available(self) -> bool: + """Is solution data available.""" + return self.scheme_eval.scheme_eval("(data-valid?)") + + def register_pause_on_solution_events(self, solution_event: SolverEvent) -> int: + """Register pause on solution events.""" + unique_id: int = self.scheme_eval.scheme_eval( + f""" + (let + ((ids + (let loop ((i 1)) + (define next-id (string->symbol (format #f "pyfluent-~d" i))) + (if (check-monitor-existence next-id) + (loop (1+ i)) + (list i next-id) + ) + ) + )) + (register-solution-monitor + (cadr ids) + (lambda (niter time) + (if (integer? niter) + (begin + (events/transmit 'auto-pause (cons (car ids) niter)) + (grpcserver/auto-pause (is-server-running?) (cadr ids)) + ) + ) + () + ) + {'#t' if solution_event == SolverEvent.TIMESTEP_ENDED else '#f'} + ) + (car ids) + ) + """ + ) + return unique_id + + def resume_on_solution_event(self, registration_id: int) -> None: + """Resume on solution event.""" + self.scheme_eval.scheme_eval( + f"(grpcserver/auto-resume (is-server-running?) 'pyfluent-{registration_id})" + ) + + def unregister_pause_on_solution_events(self, registration_id: int) -> None: + """Unregister pause on solution events.""" + self.scheme_eval.scheme_eval( + f"(cancel-solution-monitor 'pyfluent-{registration_id})" + ) + + def exit(self) -> None: + """Exit.""" + self.scheme_eval.exec(("(exit-server)",)) + + +class AppUtilities: + """AppUtilities.""" + + def __init__(self, service: AppUtilitiesService): + """__init__ method of AppUtilities class.""" + self.service = service + + def get_product_version(self) -> str: + """Get product version.""" + request = AppUtilitiesProtoModule.GetProductVersionRequest() + response = self.service.get_product_version(request) + return f"{response.major}.{response.minor}.{response.patch}" + + def get_build_info(self) -> dict: + """Get build info.""" + request = AppUtilitiesProtoModule.GetBuildInfoRequest() + response = self.service.get_build_info(request) + return { + "build_time": response.build_time, + "build_id": response.build_id, + "vcs_revision": response.vcs_revision, + "vcs_branch": response.vcs_branch, + } + + def get_controller_process_info(self) -> dict: + """Get controller process info.""" + request = AppUtilitiesProtoModule.GetControllerProcessInfoRequest() + response = self.service.get_controller_process_info(request) + return { + "hostname": response.hostname, + "process_id": response.process_id, + "working_directory": response.working_directory, + } + + def get_solver_process_info(self) -> dict: + """Get solver process info.""" + request = AppUtilitiesProtoModule.GetSolverProcessInfoRequest() + response = self.service.get_solver_process_info(request) + return { + "hostname": response.hostname, + "process_id": response.process_id, + "working_directory": response.working_directory, + } + + def get_app_mode(self) -> Enum: + """Get app mode. + + Raises + ------ + ValueError + If app mode is unknown. + """ + import ansys.fluent.core as pyfluent + + request = AppUtilitiesProtoModule.GetAppModeRequest() + response = self.service.get_app_mode(request) + match response.app_mode: + case AppUtilitiesProtoModule.APP_MODE_UNKNOWN: + raise ValueError("Unknown app mode.") + case AppUtilitiesProtoModule.APP_MODE_MESHING: + return pyfluent.FluentMode.MESHING + case AppUtilitiesProtoModule.APP_MODE_SOLVER: + return pyfluent.FluentMode.SOLVER + case AppUtilitiesProtoModule.APP_MODE_SOLVER_ICING: + return pyfluent.FluentMode.SOLVER_ICING + case AppUtilitiesProtoModule.APP_MODE_SOLVER_AERO: + return pyfluent.FluentMode.SOLVER_AERO + + def start_python_journal(self, journal_name: str | None = None) -> int: + """Start python journal.""" + request = AppUtilitiesProtoModule.StartPythonJournalRequest() + request.journal_name = journal_name + response = self.service.start_python_journal(request) + return response.journal_id + + def stop_python_journal(self, journal_id: str | None = None) -> str: + """Stop python journal.""" + request = AppUtilitiesProtoModule.StopPythonJournalRequest() + if journal_id: + request.journal_id = journal_id + response = self.service.stop_python_journal(request) + return response.journal_str + + def is_beta_enabled(self) -> bool: + """Is beta enabled.""" + request = AppUtilitiesProtoModule.IsBetaEnabledRequest() + response = self.service.is_beta_enabled(request) + return response.is_beta_enabled + + def is_wildcard(self, input: str | None = None) -> bool: + """Is wildcard.""" + request = AppUtilitiesProtoModule.IsWildcardRequest() + request.input = input + response = self.service.is_wildcard(request) + return response.is_wildcard + + def is_solution_data_available(self) -> bool: + """Is solution data available.""" + request = AppUtilitiesProtoModule.IsSolutionDataAvailableRequest() + response = self.service.is_solution_data_available(request) + return response.is_solution_data_available + + def register_pause_on_solution_events(self, solution_event: SolverEvent) -> int: + """Register pause on solution events.""" + request = AppUtilitiesProtoModule.RegisterPauseOnSolutionEventsRequest() + request.solution_event = AppUtilitiesProtoModule.SOLUTION_EVENT_UNKNOWN + match solution_event: + case SolverEvent.ITERATION_ENDED: + request.solution_event = ( + AppUtilitiesProtoModule.SOLUTION_EVENT_ITERATION + ) + case SolverEvent.TIMESTEP_ENDED: + request.solution_event = ( + AppUtilitiesProtoModule.SOLUTION_EVENT_TIME_STEP + ) + response = self.service.register_pause_on_solution_events(request) + return response.registration_id + + def resume_on_solution_event(self, registration_id: int) -> None: + """Resume on solution event.""" + request = AppUtilitiesProtoModule.ResumeOnSolutionEventRequest() + request.registration_id = registration_id + self.service.resume_on_solution_event(request) + + def unregister_pause_on_solution_events(self, registration_id: int) -> None: + """Unregister pause on solution events.""" + request = AppUtilitiesProtoModule.UnregisterPauseOnSolutionEventsRequest() + request.registration_id = registration_id + self.service.unregister_pause_on_solution_events(request) + + def exit(self) -> None: + """Exit.""" + request = AppUtilitiesProtoModule.ExitRequest() + self.service.exit(request) diff --git a/src/ansys/fluent/core/services/datamodel_tui.py b/src/ansys/fluent/core/services/datamodel_tui.py index 75f956b7d23..97a3eeb7f3d 100644 --- a/src/ansys/fluent/core/services/datamodel_tui.py +++ b/src/ansys/fluent/core/services/datamodel_tui.py @@ -125,10 +125,12 @@ def __init__( channel: grpc.Channel, metadata: list[tuple[str, str]], fluent_error_state, + app_utilities, scheme_eval, ) -> None: """__init__ method of DatamodelService class.""" self._impl = DatamodelServiceImpl(channel, metadata, fluent_error_state) + self._app_utilities = app_utilities self._scheme_eval = scheme_eval def get_attribute_value( @@ -233,7 +235,7 @@ def execute(self, *args, **kwargs) -> Any: Query result (any Python datatype) """ with ApiUpgradeAdvisor( - self._service._scheme_eval, + self._service._app_utilities, self._version, self._mode, ): diff --git a/src/ansys/fluent/core/services/settings.py b/src/ansys/fluent/core/services/settings.py index ea9f366f6c1..b4c5efb1e8d 100644 --- a/src/ansys/fluent/core/services/settings.py +++ b/src/ansys/fluent/core/services/settings.py @@ -136,9 +136,12 @@ def _get_request_instance_for_path(request_class, path: str) -> Any: class SettingsService: """Service for accessing and modifying Fluent settings.""" - def __init__(self, channel, metadata, scheme_eval, fluent_error_state) -> None: + def __init__( + self, channel, metadata, app_utilities, scheme_eval, fluent_error_state + ) -> None: """__init__ method of SettingsService class.""" self._service_impl = _SettingsServiceImpl(channel, metadata, fluent_error_state) + self._app_utilities = app_utilities self._scheme_eval = scheme_eval @_trace @@ -369,7 +372,7 @@ def has_wildcard(self, name: str) -> bool: """Checks whether a name has a wildcard pattern.""" return self._scheme_eval.is_defined( "has-fnmatch-wild-card?" - ) and self._scheme_eval.scheme_eval(f'(has-fnmatch-wild-card? "{name}")') + ) and self._app_utilities.is_wildcard(name) @_trace def is_interactive_mode(self) -> bool: diff --git a/src/ansys/fluent/core/session.py b/src/ansys/fluent/core/session.py index a35ed312c98..42e45907495 100644 --- a/src/ansys/fluent/core/session.py +++ b/src/ansys/fluent/core/session.py @@ -10,6 +10,7 @@ from ansys.fluent.core.fluent_connection import FluentConnection from ansys.fluent.core.journaling import Journal from ansys.fluent.core.services import service_creator +from ansys.fluent.core.services.app_utilities import AppUtilitiesOld from ansys.fluent.core.services.field_data import FieldDataService from ansys.fluent.core.services.scheme_eval import SchemeEval from ansys.fluent.core.streaming_services.datamodel_event_streaming import ( @@ -124,7 +125,6 @@ def _build_from_fluent_connection( self.scheme_eval = scheme_eval self.rp_vars = RPVars(self.scheme_eval.string_eval) self._preferences = None - self.journal = Journal(self.scheme_eval) self._transcript_service = service_creator("transcript").create( fluent_connection._channel, fluent_connection._metadata @@ -133,10 +133,20 @@ def _build_from_fluent_connection( if self._start_transcript: self.transcript.start() + if FluentVersion(self.scheme_eval.version) < FluentVersion.v252: + self._app_utilities = AppUtilitiesOld(self.scheme_eval) + else: + self._app_utilities = ( + self._fluent_connection._connection_interface._app_utilities + ) + + self.journal = Journal(self._app_utilities) + self._datamodel_service_tui = service_creator("tui").create( fluent_connection._channel, fluent_connection._metadata, self._error_state, + self._app_utilities, self.scheme_eval, ) @@ -175,13 +185,17 @@ class Fields: def __init__(self, _session): """Initialize Fields.""" + self._is_solution_data_valid = ( + _session._app_utilities.is_solution_data_available + ) self.field_info = service_creator("field_info").create( - _session._field_data_service, _IsDataValid(_session.scheme_eval) + _session._field_data_service, + self._is_solution_data_valid, ) self.field_data = service_creator("field_data").create( _session._field_data_service, self.field_info, - _IsDataValid(_session.scheme_eval), + self._is_solution_data_valid, _session.scheme_eval, ) self.field_data_streaming = FieldDataStreaming( @@ -190,7 +204,7 @@ def __init__(self, _session): self.field_data_old = service_creator("field_data_old").create( _session._field_data_service, self.field_info, - _IsDataValid(_session.scheme_eval), + self._is_solution_data_valid, _session.scheme_eval, ) @@ -199,6 +213,7 @@ def __init__(self, _session): self._settings_service = service_creator("settings").create( fluent_connection._channel, fluent_connection._metadata, + self._app_utilities, self.scheme_eval, self._error_state, ) diff --git a/src/ansys/fluent/core/session_pure_meshing.py b/src/ansys/fluent/core/session_pure_meshing.py index f3932df2a55..188f67b14c9 100644 --- a/src/ansys/fluent/core/session_pure_meshing.py +++ b/src/ansys/fluent/core/session_pure_meshing.py @@ -156,7 +156,7 @@ def topology_based(self): RuntimeError If beta features are not enabled in Fluent. """ - if not self.scheme_eval.scheme_eval("(is-beta-feature-available?)"): + if not self._app_utilities.is_beta_enabled(): raise RuntimeError("Topology-based Meshing is a beta feature in Fluent.") self._base_meshing.topology_based_meshing_workflow.initialize() return self._base_meshing.topology_based_meshing_workflow diff --git a/src/ansys/fluent/core/streaming_services/events_streaming.py b/src/ansys/fluent/core/streaming_services/events_streaming.py index 0c48c15511f..c0f11dd4879 100644 --- a/src/ansys/fluent/core/streaming_services/events_streaming.py +++ b/src/ansys/fluent/core/streaming_services/events_streaming.py @@ -538,8 +538,8 @@ def unregister_callback(self, callback_id: str): del callbacks_map[callback_id] sync_event_id = self._sync_event_ids.pop(callback_id, None) if sync_event_id: - self._session.scheme_eval.scheme_eval( - f"(cancel-solution-monitor 'pyfluent-{sync_event_id})" + self._session._app_utilities.unregister_pause_on_solution_events( + registration_id=sync_event_id ) def start(self, *args, **kwargs) -> None: @@ -556,34 +556,8 @@ def _register_solution_event_sync_callback( callback_id: str, callback: Callable, ) -> tuple[Literal[SolverEvent.SOLUTION_PAUSED], Callable]: - unique_id: int = self._session.scheme_eval.scheme_eval( - f""" - (let - ((ids - (let loop ((i 1)) - (define next-id (string->symbol (format #f "pyfluent-~d" i))) - (if (check-monitor-existence next-id) - (loop (1+ i)) - (list i next-id) - ) - ) - )) - (register-solution-monitor - (cadr ids) - (lambda (niter time) - (if (integer? niter) - (begin - (events/transmit 'auto-pause (cons (car ids) niter)) - (grpcserver/auto-pause (is-server-running?) (cadr ids)) - ) - ) - () - ) - {'#t' if event_type == SolverEvent.TIMESTEP_ENDED else '#f'} - ) - (car ids) - ) - """ + unique_id: int = self._session._app_utilities.register_pause_on_solution_events( + solution_event=event_type ) def on_pause(session, event_info: SolutionPausedEventInfo): @@ -604,8 +578,8 @@ def on_pause(session, event_info: SolutionPausedEventInfo): exc_info=True, ) finally: - session.scheme_eval.scheme_eval( - f"(grpcserver/auto-resume (is-server-running?) 'pyfluent-{unique_id})" + session._app_utilities.resume_on_solution_event( + registration_id=unique_id ) self._sync_event_ids[callback_id] = unique_id diff --git a/tests/test_session.py b/tests/test_session.py index 09aacc13bc1..35e6bf34195 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -306,7 +306,7 @@ def test_journal_creation(file_format, new_meshing_session): if session.connection_properties.inside_container: session.journal.start(file_name.name) else: - session.journal.start(file_name) + session.journal.start(str(file_name)) session = session.switch_to_solver() session.journal.stop() new_stat = file_name.stat() @@ -577,3 +577,18 @@ def test_general_exception_behaviour_in_session(new_solver_session): # This appears to be a surface mesh.\nSurface meshes cannot be read under the /file/read-case functionality. # with pytest.raises(RuntimeError): # solver.settings.file.read(file_type='case', file_name=mesh_file_2d) + + +@pytest.mark.fluent_version(">=23.2") +def test_app_utilities_new_and_old(mixing_elbow_settings_session): + solver = mixing_elbow_settings_session + + assert solver._app_utilities.get_app_mode() == pyfluent.FluentMode.SOLVER + + assert not solver._app_utilities.is_beta_enabled() + + assert not solver._app_utilities.is_wildcard("no") + + assert solver._app_utilities.is_wildcard("yes*") + + assert not solver._app_utilities.is_solution_data_available() diff --git a/tests/test_settings_api.py b/tests/test_settings_api.py index b511fdcf55c..92535218cca 100644 --- a/tests/test_settings_api.py +++ b/tests/test_settings_api.py @@ -495,6 +495,7 @@ def test_generated_code_special_cases(new_solver_session): assert _OutputFile in write_file_bases +@pytest.mark.skip("https://github.com/ansys/pyfluent/issues/3591") @pytest.mark.fluent_version(">=25.1") def test_child_alias_with_parent_path(mixing_elbow_settings_session): solver = mixing_elbow_settings_session