From b556f1e5b419729dfdf7a9cbbddacdabf523c878 Mon Sep 17 00:00:00 2001 From: Mainak Kundu <94432368+mkundu1@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:15:40 -0500 Subject: [PATCH] docs: event streaming callback (#3546) * docs: event streaming callback * docs: event streaming callback * feat: optional last * docs: move cheatsheet build to nightly --- .github/workflows/ci.yml | 17 +------- .github/workflows/doc-build-dev-nightly.yml | 16 ++++++++ .github/workflows/doc-build-release.yml | 16 ++++++++ doc/source/conf.py | 13 +++---- .../contributing/environment_variables.rst | 6 ++- doc/source/user_guide/events.rst | 21 ++++++---- .../streaming_services/events_streaming.py | 39 +++++++++++++------ tests/test_events_manager.py | 13 ++++++- 8 files changed, 97 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 527477151ad..e45b1445b70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,22 +115,6 @@ jobs: restore-keys: | Python-${{ runner.os }}-${{ matrix.python-version }} - - name: Install Quarto - uses: quarto-dev/quarto-actions/setup@v2 - with: - tinytex: true - - - name: Check Quarto Version - shell: bash - run: | - quarto --version - - - name: "Install Poppler for PDF to PNG conversion" - shell: bash - run: | - sudo apt-get update - sudo apt-get install -y poppler-utils - - name: Install pyfluent run: make install @@ -197,6 +181,7 @@ jobs: make build-doc-source env: FLUENT_IMAGE_TAG: ${{ env.DOC_DEPLOYMENT_IMAGE_TAG }} + PYFLUENT_DOC_SKIP_CHEATSHEET: 1 - name: Zip HTML Documentation before upload run: | diff --git a/.github/workflows/doc-build-dev-nightly.yml b/.github/workflows/doc-build-dev-nightly.yml index d4ad51ec8c6..a5ae573e594 100644 --- a/.github/workflows/doc-build-dev-nightly.yml +++ b/.github/workflows/doc-build-dev-nightly.yml @@ -34,6 +34,22 @@ jobs: sudo apt update sudo apt-get install pandoc libegl1 make xvfb libfontconfig1 libxrender1 libxkbcommon-x11-0 -y + - name: Install Quarto + uses: quarto-dev/quarto-actions/setup@v2 + with: + tinytex: true + + - name: Check Quarto Version + shell: bash + run: | + quarto --version + + - name: "Install Poppler for PDF to PNG conversion" + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y poppler-utils + - name: Install pyfluent run: make install diff --git a/.github/workflows/doc-build-release.yml b/.github/workflows/doc-build-release.yml index b304b462253..62312889b8b 100644 --- a/.github/workflows/doc-build-release.yml +++ b/.github/workflows/doc-build-release.yml @@ -36,6 +36,22 @@ jobs: sudo apt update sudo apt-get install pandoc libegl1 make xvfb libfontconfig1 libxrender1 libxkbcommon-x11-0 -y + - name: Install Quarto + uses: quarto-dev/quarto-actions/setup@v2 + with: + tinytex: true + + - name: Check Quarto Version + shell: bash + run: | + quarto --version + + - name: "Install Poppler for PDF to PNG conversion" + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y poppler-utils + - name: Install pyfluent run: make install diff --git a/doc/source/conf.py b/doc/source/conf.py index 1ed05a26c3a..0b7fbbdd122 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -42,10 +42,7 @@ toggleprompt_offset_right = 35 -skip_examples = int(os.getenv("PYFLUENT_SKIP_EXAMPLES_DOC", 0)) -if skip_examples: - pass -else: +if os.getenv("PYFLUENT_DOC_SKIP_EXAMPLES") != "1": extensions.append("sphinx_gallery.gen_gallery") typehints_document_rtype = False @@ -197,13 +194,15 @@ def _stop_fluent_container(gallery_conf, fname): "navbar_end": ["version-switcher", "theme-switcher", "navbar-icon-links"], "navigation_depth": -1, "collapse_navigation": True, - "cheatsheet": { +} + +if os.getenv("PYFLUENT_DOC_SKIP_CHEATSHEET") != "1": + html_theme_options["cheatsheet"] = { "file": "cheatsheet/cheat_sheet.qmd", "pages": ["index", "getting_started/index", "user_guide/index"], "title": "PyFluent cheat sheet", "version": __version__, - }, -} + } # -- Options for HTMLHelp output --------------------------------------------- diff --git a/doc/source/contributing/environment_variables.rst b/doc/source/contributing/environment_variables.rst index 75b47d6edd2..280b2b1505d 100644 --- a/doc/source/contributing/environment_variables.rst +++ b/doc/source/contributing/environment_variables.rst @@ -29,6 +29,10 @@ Following is a list of environment variables that can be set to control various - Specifies the path inside the container where the host path is mounted while starting a Fluent container in :func:`launch_fluent() `. * - PYFLUENT_FLUENT_DEBUG - Starts Fluent in debug mode while launching Fluent in :func:`launch_fluent() `. + * - PYFLUENT_DOC_SKIP_CHEATSHEET: + - Skips the generation of cheatsheet. + * - PYFLUENT_DOC_SKIP_EXAMPLES + - Skips the generation of examples documentation. * - PYFLUENT_FLUENT_IP - Specifies the IP address of the Fluent server in :func:`connect_to_fluent() `. * - PYFLUENT_FLUENT_PORT @@ -47,8 +51,6 @@ Following is a list of environment variables that can be set to control various - Shows the Fluent GUI while launching Fluent in :func:`launch_fluent() `. * - PYFLUENT_SKIP_API_UPGRADE_ADVICE - Disables printing of TUI to settings API upgrade advice. - * - PYFLUENT_SKIP_EXAMPLES_DOC - - Skips the generation of examples documentation. * - PYFLUENT_TIMEOUT_FORCE_EXIT - Enables force exit while exiting a Fluent session and specifies the timeout in seconds. * - PYFLUENT_WATCHDOG_DEBUG diff --git a/doc/source/user_guide/events.rst b/doc/source/user_guide/events.rst index d38879b01c1..1bd18e53a0b 100644 --- a/doc/source/user_guide/events.rst +++ b/doc/source/user_guide/events.rst @@ -18,13 +18,19 @@ The following code triggers a callback at the end of every iteration. .. code-block:: python - >>> from ansys.fluent.core import SolverEvent + >>> from ansys.fluent.core import SolverEvent, IterationEndedEventInfo >>> - >>> def on_iteration_ended(session, event_info): + >>> def on_iteration_ended(session, event_info: IterationEndedEventInfo): >>> print("Iteration ended. Index = ", event_info.index) >>> >>> callback_id = solver.events.register_callback(SolverEvent.ITERATION_ENDED, on_iteration_ended) - >>> + +The general signature of the callback function is ``cb(session, event_info, )``, where ``session`` is the session instance +and ``event_info`` instance holds information about the event. The event information classes for each event are documented in the +API reference of the :obj:`~ansys.fluent.core.streaming_services.events_streaming` module. See the callback function +``on_case_loaded_with_args()`` in the below examples for an example of how to pass additional arguments to the callback +function. + Examples -------- @@ -32,6 +38,7 @@ Examples .. code-block:: python >>> from ansys.fluent.core import MeshingEvent, SolverEvent + >>> from ansys.fluent.core import CaseLoadedEventInfo, DataLoadedEventInfo, SolutionInitializedEventInfo, IterationEndedEventInfo >>> from ansys.fluent.core.utils.event_loop import execute_in_event_loop_threadsafe >>> from ansys.fluent.visualization.matplotlib import matplot_windows_manager >>> from ansys.fluent.visualization.pyvista import pyvista_windows_manager @@ -48,7 +55,7 @@ Examples >>> contour2.surfaces_list = ["symmetry"] >>> >>> @execute_in_event_loop_threadsafe - >>> def auto_refersh_call_back_iteration(session, event_info): + >>> def auto_refersh_call_back_iteration(session, event_info: IterationEndedEventInfo): >>> if event_info.index % 5 == 0: >>> pyvista_windows_manager.refresh_windows(session.id, ["contour-1", "contour-2"]) >>> matplot_windows_manager.refresh_windows("", ["residual"]) @@ -56,7 +63,7 @@ Examples >>> callback_itr_id = solver.events.register_callback(SolverEvent.ITERATION_ENDED, auto_refersh_call_back_iteration) >>> >>> @execute_in_event_loop_threadsafe - >>> def initialize_call_back(session, event_info): + >>> def initialize_call_back(session, event_info: SolutionInitializedEventInfo | DataLoadedEventInfo): >>> pyvista_windows_manager.refresh_windows(session.id, ["contour-1", "contour-2"]) >>> matplot_windows_manager.refresh_windows("", ["residual"]) >>> @@ -64,10 +71,10 @@ Examples >>> >>> callback_data_read_id = solver.events.register_callback(SolverEvent.DATA_LOADED, initialize_call_back) >>> - >>> def on_case_loaded(session, event_info): + >>> def on_case_loaded(session, event_info: CaseLoadedEventInfo): >>> print("Case loaded. Index = ", event_info.index) >>> - >>> def on_case_loaded_with_args(x, y, session, event_info): + >>> def on_case_loaded_with_args(session, event_info: CaseLoadedEventInfo, x, y): >>> print(f"Case loaded with {x}, {y}. Index = ", event_info.index) >>> >>> callback = meshing.events.register_callback(MeshingEvent.CASE_LOADED, on_case_loaded) diff --git a/src/ansys/fluent/core/streaming_services/events_streaming.py b/src/ansys/fluent/core/streaming_services/events_streaming.py index 1d73b02c6aa..0c48c15511f 100644 --- a/src/ansys/fluent/core/streaming_services/events_streaming.py +++ b/src/ansys/fluent/core/streaming_services/events_streaming.py @@ -16,6 +16,7 @@ from ansys.fluent.core.warnings import PyFluentDeprecationWarning __all__ = [ + "EventsManager", "Event", "SolverEvent", "MeshingEvent", @@ -128,6 +129,7 @@ def __getattr__(self, name): @dataclass class TimestepStartedEventInfo(EventInfoBase, event=SolverEvent.TIMESTEP_STARTED): """Information about the event triggered when a timestep is started. + Attributes ---------- index : int @@ -143,6 +145,7 @@ class TimestepStartedEventInfo(EventInfoBase, event=SolverEvent.TIMESTEP_STARTED @dataclass class TimestepEndedEventInfo(EventInfoBase, event=SolverEvent.TIMESTEP_ENDED): """Information about the event triggered when a timestep is ended. + Attributes ---------- index : int @@ -158,6 +161,7 @@ class TimestepEndedEventInfo(EventInfoBase, event=SolverEvent.TIMESTEP_ENDED): @dataclass class IterationEndedEventInfo(EventInfoBase, event=SolverEvent.ITERATION_ENDED): """Information about the event triggered when an iteration is ended. + Attributes ---------- index : int @@ -190,6 +194,7 @@ class CalculationsResumedEventInfo( @dataclass class AboutToLoadCaseEventInfo(EventInfoBase, event=SolverEvent.ABOUT_TO_LOAD_CASE): """Information about the event triggered just before a case file is loaded. + Attributes ---------- case_file_name : str @@ -202,6 +207,7 @@ class AboutToLoadCaseEventInfo(EventInfoBase, event=SolverEvent.ABOUT_TO_LOAD_CA @dataclass class CaseLoadedEventInfo(EventInfoBase, event=SolverEvent.CASE_LOADED): """Information about the event triggered after a case file is loaded. + Attributes ---------- case_file_name : str @@ -214,6 +220,7 @@ class CaseLoadedEventInfo(EventInfoBase, event=SolverEvent.CASE_LOADED): @dataclass class AboutToLoadDataEventInfo(EventInfoBase, event=SolverEvent.ABOUT_TO_LOAD_DATA): """Information about the event triggered just before a data file is loaded. + Attributes ---------- data_file_name : str @@ -226,6 +233,7 @@ class AboutToLoadDataEventInfo(EventInfoBase, event=SolverEvent.ABOUT_TO_LOAD_DA @dataclass class DataLoadedEventInfo(EventInfoBase, event=SolverEvent.DATA_LOADED): """Information about the event triggered after a data file is loaded. + Attributes ---------- data_file_name : str @@ -252,6 +260,7 @@ class ReportDefinitionUpdatedEventInfo( EventInfoBase, event=SolverEvent.REPORT_DEFINITION_UPDATED ): """Information about the event triggered when a report definition is updated. + Attributes ---------- report_name : str @@ -266,6 +275,7 @@ class ReportPlotSetUpdatedEventInfo( EventInfoBase, event=SolverEvent.REPORT_PLOT_SET_UPDATED ): """Information about the event triggered when a report plot set is updated. + Attributes ---------- plot_set_name : str @@ -288,6 +298,7 @@ class SettingsClearedEventInfo(EventInfoBase, event=SolverEvent.SETTINGS_CLEARED @dataclass class SolutionPausedEventInfo(EventInfoBase, event=SolverEvent.SOLUTION_PAUSED): """Information about the event triggered when solution is paused. + Attributes ---------- level : str @@ -303,6 +314,7 @@ class SolutionPausedEventInfo(EventInfoBase, event=SolverEvent.SOLUTION_PAUSED): @dataclass class ProgressUpdatedEventInfo(EventInfoBase, event=SolverEvent.PROGRESS_UPDATED): """Information about the event triggered when progress is updated. + Attributes ---------- message : str @@ -320,6 +332,7 @@ class SolverTimeEstimateUpdatedEventInfo( EventInfoBase, event=SolverEvent.SOLVER_TIME_ESTIMATE_UPDATED ): """Information about the event triggered when solver time estimate is updated. + Attributes ---------- hours : float @@ -338,6 +351,7 @@ class SolverTimeEstimateUpdatedEventInfo( @dataclass class FatalErrorEventInfo(EventInfoBase, event=SolverEvent.FATAL_ERROR): """Information about the event triggered when a fatal error occurs. + Attributes ---------- message : str @@ -428,23 +442,26 @@ def _process_streaming( @staticmethod def _make_callback_to_call(callback: Callable, args, kwargs): - old_style = "session_id" in inspect.signature(callback).parameters - if old_style: + params = inspect.signature(callback).parameters + if "session_id" in params: warnings.warn( "Update event callback function signatures" " substituting 'session' for 'session_id'.", PyFluentDeprecationWarning, ) - fn = partial(callback, *args, **kwargs) - return ( - ( - lambda session, event_info: fn( - session_id=session.id, event_info=event_info - ) + return lambda session, event_info: callback( + *args, session_id=session.id, event_info=event_info, **kwargs + ) + else: + positional_args = [ + p + for p in params + if p not in kwargs and p not in ("session", "event_info") + ] + kwargs.update(dict(zip(positional_args, args))) + return lambda session, event_info: callback( + session=session, event_info=event_info, **kwargs ) - if old_style - else fn - ) def register_callback( self, diff --git a/tests/test_events_manager.py b/tests/test_events_manager.py index 925ed39de3e..fea1b35b067 100644 --- a/tests/test_events_manager.py +++ b/tests/test_events_manager.py @@ -28,7 +28,12 @@ def on_case_loaded(session, event_info): on_case_loaded.loaded = False - def on_case_loaded_with_args(x, y, session, event_info): + def on_case_loaded_with_args_optional_first(x, y, session, event_info): + on_case_loaded_with_args_optional_first.state = dict(x=x, y=y) + + on_case_loaded_with_args_optional_first.state = None + + def on_case_loaded_with_args(session, event_info, x, y): on_case_loaded_with_args.state = dict(x=x, y=y) on_case_loaded_with_args.state = None @@ -43,6 +48,10 @@ def on_case_loaded_with_args(x, y, session, event_info): solver.events.register_callback(SolverEvent.CASE_LOADED, on_case_loaded) + solver.events.register_callback( + SolverEvent.CASE_LOADED, on_case_loaded_with_args_optional_first, 12, y=42 + ) + solver.events.register_callback( SolverEvent.CASE_LOADED, on_case_loaded_with_args, 12, y=42 ) @@ -54,6 +63,7 @@ def on_case_loaded_with_args(x, y, session, event_info): assert not on_case_loaded_old.loaded assert not on_case_loaded.loaded assert not on_case_loaded_old_with_args.state + assert not on_case_loaded_with_args_optional_first.state assert not on_case_loaded_with_args.state try: @@ -64,6 +74,7 @@ def on_case_loaded_with_args(x, y, session, event_info): assert on_case_loaded_old.loaded assert on_case_loaded.loaded assert on_case_loaded_old_with_args.state == dict(x=12, y=42) + assert on_case_loaded_with_args_optional_first.state == dict(x=12, y=42) assert on_case_loaded_with_args.state == dict(x=12, y=42)