diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32ed8075ee8..14be93defa6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -124,8 +124,8 @@ jobs: restore-keys: | Python-${{ runner.os }}-${{ matrix.python-version }} - - name: Install pyfluent with post requirements - run: make install-post + - name: Install pyfluent + run: make install - name: Login to GitHub Container Registry uses: docker/login-action@v1 @@ -208,8 +208,8 @@ jobs: - name: Add version information run: make version-info - - name: Install pyfluent with post requirements - run: make install-post + - name: Install pyfluent with post + run: make install - name: Login to GitHub Container Registry uses: docker/login-action@v1 @@ -231,7 +231,7 @@ jobs: - name: Install again after codegen run: | rm -rf dist - make install-post + make install - name: Unit Testing run: make unittest diff --git a/.github/workflows/nightly-doc-build.yml b/.github/workflows/nightly-doc-build.yml index c195f007052..1c00932be50 100644 --- a/.github/workflows/nightly-doc-build.yml +++ b/.github/workflows/nightly-doc-build.yml @@ -30,8 +30,8 @@ jobs: sudo apt update sudo apt install pandoc libegl1 - - name: Install pyfluent with post module - run: make install-post + - name: Install pyfluent + run: make install - name: Login to GitHub Container Registry uses: docker/login-action@v1 diff --git a/Makefile b/Makefile index 1a9ee352900..e96dbb1ab29 100644 --- a/Makefile +++ b/Makefile @@ -12,12 +12,6 @@ version-info: @bash -c "date -u +'Build date: %B %d, %Y %H:%M UTC ShaID: ' | xargs -I date sed -i 's/_VERSION_INFO = .*/_VERSION_INFO = \"date\"/g' src/ansys/fluent/core/__init__.py" @bash -c "git --no-pager log -n 1 --format='%h' | xargs -I hash sed -i 's//hash/g' src/ansys/fluent/core/__init__.py" -install-post: - @pip install -r requirements_build.txt - @python setup.py sdist - @python setup.py bdist_wheel - @find dist -name "*.whl" -exec pip install {}[post] \; - install-pyvistaqt-requirements: @sudo apt update @sudo apt install libegl1 -y diff --git a/doc/source/_static/DP_table.png b/doc/source/_static/DP_table.png deleted file mode 100644 index f6e9c0b13e9..00000000000 Binary files a/doc/source/_static/DP_table.png and /dev/null differ diff --git a/doc/source/api/index.rst b/doc/source/api/index.rst index be1c46bd448..aa9d515142c 100644 --- a/doc/source/api/index.rst +++ b/doc/source/api/index.rst @@ -14,6 +14,4 @@ Here we should discuss the different API modules at a high level. :maxdepth: 4 :hidden: - core/index - parametric - post/index \ No newline at end of file + core/index \ No newline at end of file diff --git a/doc/source/api/parametric.rst b/doc/source/api/parametric.rst deleted file mode 100644 index 8487c63e87f..00000000000 --- a/doc/source/api/parametric.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _ref_addons_parametric: - -Parameters -========== - -.. currentmodule:: ansys.fluent.parametric - -.. autosummary:: - :toctree: _autosummary - - DesignPoint - ParametricStudy \ No newline at end of file diff --git a/doc/source/api/post/index.rst b/doc/source/api/post/index.rst deleted file mode 100644 index aef0378c981..00000000000 --- a/doc/source/api/post/index.rst +++ /dev/null @@ -1,119 +0,0 @@ -.. _ref_postprocessing: - -Postprocessing -============== - -Post processing Fluent results can be done with either Fluent in-built post -processing capabilities or with the PyVista/MatplotLib integration. - -Fluent ------- - -Here visualization objects are constructed within Fluent. Graphics can be -written to a file using standard Fluent commands. - -.. code:: python - - session.solver.tui.display.objects.contour['contour-1'] = {'boundary_values': True, 'color_map': {'color': 'field-velocity', 'font_automatic': True, 'font_name': 'Helvetica', 'font_size': 0.032, 'format': '%0.2e', 'length': 0.54, 'log_scale': False, 'position': 1, 'show_all': True, 'size': 100, 'user_skip': 9, 'visible': True, 'width': 6.0}, 'coloring': {'smooth': False}, 'contour_lines': False, 'display_state_name': 'None', 'draw_mesh': False, 'field': 'pressure', 'filled': True, 'mesh_object': '', 'node_values': True, 'range_option': {'auto_range_on': {'global_range': True}}, 'surfaces_list': [2, 5]} - session.solver.tui.display.objects.contour['contour-1']() - session.solver.tui.display.objects.contour['contour-1'].field.set_state('velocity-magnitude') - session.solver.tui.display.objects.contour['contour-1'].field() - session.solver.tui.display.objects.contour['contour-1'].color_map.size.set_state(80.0) - session.solver.tui.display.objects.contour['contour-1'].color_map.size() - session.solver.tui.display.objects.contour['contour-1'].rename('my-contour') - del session.solver.tui.display.objects.contour['my-contour'] - -PyVista Example (Graphics) --------------------------- - -Here the field data is extracted from the Fluent session into the Python -environment and PyVista is used to visualze the extracted data. - -.. code:: python - - # import module - from ansys.fluent.post.pyvista import Graphics - - # get the graphics objects for the session - - graphics_session1 = Graphics(session) - mesh1 = graphics_session1.Meshes["mesh-1"] - contour1 = graphics_session1.Contours["contour-1"] - contour2 = graphics_session1.Contours["contour-2"] - surface1 = graphics_session1.Surfaces["surface-1"] - - # set graphics objects properties - - # mesh - mesh1.show_edges = True - mesh1.surfaces_list = ['symmetry'] - - # contour - contour1.field = "velocity-magnitude" - contour1.surfaces_list = ['symmetry'] - - contour2.field = "temperature" - contour2.surfaces_list = ['symmetry', 'wall'] - - # copy - graphics_session1.Contours["contour-3"] = contour2() - - # update - contour3 = graphics_session1.Contours["contour-3"] - contour3.update(contour1()) - - # delete - del graphics_session1.Contours["contour-3"] - - # loop - for name, _ in graphics_session1.Contours.items(): - print(name) - - # iso surface - surface1.surface.iso_surface.field= "velocity-magnitude" - surface1.surface.iso_surface.rendering= "contour" - - # display - contour1.display() - mesh1.display() - surface1.display() - - # To display in specific window e.g. window-2 - contour1.display("window-2") - -MatplotLib Example (XYPlots) ----------------------------- - -Here the plot data is extracted from the Fluent session into the Python -environment and data is plotted in MatplotLib. - -.. code:: python - - # import module - from ansys.fluent.post.matplotlib import Plots - - # get the plots object for the session - plots_session1 = Plots(session) - - #get xyplot object - plot1=plots_session1.XYPlots["plot-1"] - - #set properties - plot1.surfaces_list = ["symmetry"] - plot1.y_axis_function = "temperature" - - #Draw plot - plot1.plot("window-1") - - session.exit() - -.. currentmodule:: ansys.fluent.post - -.. autosummary:: - :toctree: _autosummary - -.. toctree:: - :maxdepth: 2 - :hidden: - - pyvista_objects diff --git a/doc/source/api/post/pyvista_objects.rst b/doc/source/api/post/pyvista_objects.rst deleted file mode 100644 index 6c9961fd6b3..00000000000 --- a/doc/source/api/post/pyvista_objects.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _ref_post_pyvista_graphics: - -Graphics -======== -.. currentmodule:: ansys.fluent.post.pyvista.pyvista_objects - -.. autosummary:: - :toctree: _autosummary - - Graphics - Mesh - Surface - Contour \ No newline at end of file diff --git a/doc/source/index.rst b/doc/source/index.rst index 4579283ddde..623149fdee7 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -55,14 +55,6 @@ The primary package, ``ansys-fluent``, provides features such as: for more information. - Scripting using Fluent's TUI commands. See the :ref:`ref_solver_tui` module for more information about the available commands. -- Scripting of a parametric study using Fluent. -- Script post processing using Fluent's in-built post processing capabilities. - See the :ref:`ref_postprocessing` module for more information. -- Plotting of Fluent geometry and meshes using `PyVista - `_ from within a Python script or an - interactive `Jupyter notebook `_. -- Access to Fluent surface based field data as Python objects via `NumPy - `_ arrays - and more... Beta Features diff --git a/doc/source/users_guide/index.rst b/doc/source/users_guide/index.rst index 5a3fb6ad7f9..b27c27acdf2 100644 --- a/doc/source/users_guide/index.rst +++ b/doc/source/users_guide/index.rst @@ -23,8 +23,6 @@ constituent modules and components. materials boundary_conditions solution - postprocessing - parametric_workflows PyFluent Basic Overview diff --git a/doc/source/users_guide/parametric_workflows.rst b/doc/source/users_guide/parametric_workflows.rst deleted file mode 100644 index e2c5cf53370..00000000000 --- a/doc/source/users_guide/parametric_workflows.rst +++ /dev/null @@ -1,242 +0,0 @@ -Defining Parametric Workflows -============================= -PyFluent supports parametric workflows in Fluent. - -Parametric Study ----------------- -Here is a simple example: - -Creating Input Parameters -~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can create input parameters: -inlet1_vel, inlet1_temp, inlet2_vel and inlet2_temp - -.. code:: python - - from pathlib import Path - import ansys.fluent.core as pyfluent - from ansys.fluent.core import examples - from ansys.fluent.parametric import ParametricProject, ParametricStudy - - session = pyfluent.launch_fluent(precision="double", processor_count=2) - import_filename = examples.download_file( - "Static_Mixer_main.cas.h5", "pyfluent/static_mixer" - ) - session.solver.root.file.read(file_type="case", file_name=import_filename) - session.solver.root.solution.run_calculation.iterate(number_of_iterations=100) - session.solver.tui.define.parameters.enable_in_TUI("yes") - session.solver.tui.define.boundary_conditions.set.velocity_inlet( - "inlet1", (), "vmag", "yes", "inlet1_vel", 1, "quit" - ) - session.solver.tui.define.boundary_conditions.set.velocity_inlet( - "inlet1", (), "temperature", "yes", "inlet1_temp", 300, "quit" - ) - session.solver.tui.define.boundary_conditions.set.velocity_inlet( - "inlet2", (), "vmag", "yes", "no", "inlet2_vel", 1, "quit" - ) - session.solver.tui.define.boundary_conditions.set.velocity_inlet( - "inlet2", (), "temperature", "yes", "no", "inlet2_temp", 350, "quit" - ) - -Creating Output Parameters -~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can create output parameters: -outlet-temp-avg and outlet-vel-avg - -.. code:: python - - session.solver.root.solution.report_definitions.surface["outlet-temp-avg"] = {} - session.solver.root.solution.report_definitions.surface[ - "outlet-temp-avg" - ].report_type = "surface-areaavg" - session.solver.root.solution.report_definitions.surface[ - "outlet-temp-avg" - ].field = "temperature" - session.solver.root.solution.report_definitions.surface[ - "outlet-temp-avg" - ].surface_names = ["outlet"] - session.solver.root.solution.report_definitions.surface["outlet-vel-avg"] = {} - session.solver.root.solution.report_definitions.surface[ - "outlet-vel-avg" - ].report_type = "surface-areaavg" - session.solver.root.solution.report_definitions.surface[ - "outlet-vel-avg" - ].field = "velocity-magnitude" - session.solver.root.solution.report_definitions.surface[ - "outlet-vel-avg" - ].surface_names = ["outlet"] - session.solver.tui.define.parameters.enable_in_TUI("yes") - session.solver.tui.define.parameters.output_parameters.create( - "report-definition", "outlet-temp-avg" - ) - session.solver.tui.define.parameters.output_parameters.create( - "report-definition", "outlet-vel-avg" - ) - -Instantiating a parametric study -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can instantiate a parametric study: - -.. code:: python - - study_1 = ParametricStudy(session.solver.root.parametric_studies).initialize() - -Accessing and Modifying Input Parameters of the Base Design Point -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can access and modify input parameters of the base design point: - -.. code:: python - - input_parameters_update = study_1.design_points["Base DP"].input_parameters - input_parameters_update["inlet1_vel"] = 0.5 - study_1.design_points["Base DP"].input_parameters = input_parameters_update - -Updating the Current Design Point -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can update the current design point: - -.. code:: python - - study_1.update_current_design_point() - -Adding New Design Points -~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how to add new design points: - -.. code:: python - - design_point_1 = study_1.add_design_point() - design_point_1_input_parameters = study_1.design_points["DP1"].input_parameters - design_point_1_input_parameters["inlet1_temp"] = 500 - design_point_1_input_parameters["inlet1_vel"] = 1 - design_point_1_input_parameters["inlet2_vel"] = 1 - study_1.design_points["DP1"].input_parameters = design_point_1_input_parameters - -Duplicating Design Points -~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can duplicate a design point: - -.. code:: python - - design_point_2 = study_1.duplicate_design_point(design_point_1) - -Updating All Design Points -~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can update all design points in your study: - -.. code:: python - - study_1.update_all_design_points() - -Exporting the Design Point Table -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can export the design point table as a comma separated value (CSV) table: - -.. code:: python - - design_point_table = str( - Path(pyfluent.EXAMPLES_PATH) / "design_point_table_study_1.csv" - ) - study_1.export_design_table(design_point_table) - -Deleting Design Points -~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can remove design points: - -.. code:: python - - study_1.delete_design_points([design_point_1]) - -Duplicating Design Points -~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can copy design points: - -.. code:: python - - study_2 = study_1.duplicate() - -Renaming Studies -~~~~~~~~~~~~~~~~ -The following example demonstrates how you can change the name of your study: - -.. code:: python - - study_2.rename("New Study") - -Deleting Studies -~~~~~~~~~~~~~~~~ -The following example demonstrates how you can remove old parametric studies: - -.. code:: python - - study_1.delete() - -Saving Your Study and Closing Fluent -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can save your work and close the Fluent instance: - -.. code:: python - - project_filepath = str(Path(pyfluent.EXAMPLES_PATH) / "static_mixer_study.flprj") - session.solver.tui.file.parametric_project.save_as(project_filepath) - session.exit() - -Resuming Your Work -~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can re-start Fluent and read in a previously saved project: - -.. code:: python - - session = pyfluent.launch_fluent(precision="double", processor_count=2) - project_filepath_read = str(Path(pyfluent.EXAMPLES_PATH) / "static_mixer_study.flprj") - proj = ParametricProject( - session.solver.root.file.parametric_project, - session.solver.root.parametric_studies, - project_filepath_read, - ) - -Saving Your Work -~~~~~~~~~~~~~~~~ -The following example demonstrates how you can save your current project: - -.. code:: python - - proj.save() - -Saving Your Work With a Different Name -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can save your current project to a different file name: - -.. code:: python - - project_filepath_save_as = str( - Path(pyfluent.EXAMPLES_PATH) / "static_mixer_study_save_as.flprj" - ) - proj.save_as(project_filepath=project_filepath_save_as) - -Exporting Your Work -~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can export the current project: - -.. code:: python - - project_filepath_export = str( - Path(pyfluent.EXAMPLES_PATH) / "static_mixer_study_export.flprj" - ) - proj.export(project_filepath=project_filepath_export) - -Archiving Projects -~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can archive your current project: - -.. code:: python - - proj.archive() - -Closing Fluent -~~~~~~~~~~~~~~ -The following example demonstrates how you can end your Fluent session: - -.. code:: python - - session.exit() \ No newline at end of file diff --git a/doc/source/users_guide/postprocessing.rst b/doc/source/users_guide/postprocessing.rst deleted file mode 100644 index 18a4309adee..00000000000 --- a/doc/source/users_guide/postprocessing.rst +++ /dev/null @@ -1,107 +0,0 @@ -Analyzing Your Results -====================== -PyFluent postprocessing supports graphics and plotting. - -Rendering Graphics Objects --------------------------- -The post package library is used for rendering graphics objects. -The following graphics operations are supported. - -Displaying Mesh Objects -~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can display the mesh object: - -.. code:: python - - import ansys.fluent.core as pyfluent - from ansys.fluent.core import examples - from ansys.fluent.post import set_config - from ansys.fluent.post.matplotlib import Plots - from ansys.fluent.post.pyvista import Graphics - - set_config(blocking=True, set_view_on_display="isometric") - - import_case = examples.download_file( - filename="exhaust_system.cas.h5", directory="pyfluent/exhaust_system" - ) - - import_data = examples.download_file( - filename="exhaust_system.dat.h5", directory="pyfluent/exhaust_system" - ) - - session = pyfluent.launch_fluent(precision="double", processor_count=2) - - session.solver.tui.file.read_case(case_file_name=import_case) - session.solver.tui.file.read_data(case_file_name=import_data) - - graphics = Graphics(session=session) - mesh1 = graphics.Meshes["mesh-1"] - mesh1.show_edges = True - mesh1.surfaces_list = [ - "in1", - "in2", - "in3", - "out1", - "solid_up:1", - "solid_up:1:830", - "solid_up:1:830-shadow", - ] - mesh1.display("window-1") - -Displaying Iso-Surfaces -~~~~~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can display the iso-surface: - -.. code:: python - - surf_outlet_plane = graphics.Surfaces["outlet-plane"] - surf_outlet_plane.surface.type = "iso-surface" - iso_surf1 = surf_outlet_plane.surface.iso_surface - iso_surf1.field = "y-coordinate" - iso_surf1.iso_value = -0.125017 - surf_outlet_plane.display("window-2") - -Displaying Contours -~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can display the contour object: - -.. code:: python - - temperature_contour_manifold = graphics.Contours["contour-temperature-manifold"] - temperature_contour_manifold.field = "temperature" - temperature_contour_manifold.surfaces_list = [ - "in1", - "in2", - "in3", - "out1", - "solid_up:1", - "solid_up:1:830", - ] - temperature_contour_manifold.display("window-3") - -Displaying Vectors -~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can display the vector object: - -.. code:: python - - velocity_vector = graphics.Vectors["velocity-vector"] - velocity_vector.surfaces_list = ["outlet-plane"] - velocity_vector.scale = 1 - velocity_vector.display("window-4") - -Plotting Your Data ------------------- -The following plotting operations are supported. - -Displaying XY Plots -~~~~~~~~~~~~~~~~~~~ -The following example demonstrates how you can display the xy plot: - -.. code:: python - - plots_session_1 = Plots(session) - plot_1 = plots_session_1.XYPlots["plot-1"] - plot_1.surfaces_list = ["outlet"] - plot_1.y_axis_function = "temperature" - plot_1.plot("window-5") \ No newline at end of file diff --git a/examples/00-fluent/exhaust_system.py b/examples/00-fluent/exhaust_system.py index 0210feba370..ea2380b5345 100644 --- a/examples/00-fluent/exhaust_system.py +++ b/examples/00-fluent/exhaust_system.py @@ -42,10 +42,11 @@ import ansys.fluent.core as pyfluent from ansys.fluent.core import examples -from ansys.fluent.post import set_config -from ansys.fluent.post.pyvista import Graphics -set_config(blocking=True, set_view_on_display="isometric") +# from ansys.fluent.post import set_config +# from ansys.fluent.post.pyvista import Graphics + +# set_config(blocking=True, set_view_on_display="isometric") import_filename = examples.download_file( "exhaust_system.fmd", "pyfluent/exhaust_system" @@ -658,22 +659,22 @@ ############################################################################### # Mesh display using PyVista -graphics_session = Graphics(session) -mesh_1 = graphics_session.Meshes["mesh-1"] -mesh_1.show_edges = True -mesh_1.surfaces_list = [ - "inlet-1", - "inlet-2", - "inlet-3", - "outlet-1", - "flow-pipe", - "main.1", - "object1.1", - "object2.1", - "outpipe3.1", -] - -mesh_1.display() +# graphics_session = Graphics(session) +# mesh_1 = graphics_session.Meshes["mesh-1"] +# mesh_1.show_edges = True +# mesh_1.surfaces_list = [ +# "inlet-1", +# "inlet-2", +# "inlet-3", +# "outlet-1", +# "flow-pipe", +# "main.1", +# "object1.1", +# "object2.1", +# "outpipe3.1", +# ] + +# mesh_1.display() ############################################################################### # Save case, data. # session.solver.tui.file.write_case_data("exhaust_system.cas.h5") diff --git a/examples/01-parametric/README.txt b/examples/01-parametric/README.txt deleted file mode 100644 index aff28dd7f1e..00000000000 --- a/examples/01-parametric/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -Parametric Examples -=================== -These examples demonstrate how to use the the parametric package to perform -parametric studies with Fluent. \ No newline at end of file diff --git a/examples/01-parametric/parametric_static_mixer_1.py b/examples/01-parametric/parametric_static_mixer_1.py deleted file mode 100755 index 43c718424ab..00000000000 --- a/examples/01-parametric/parametric_static_mixer_1.py +++ /dev/null @@ -1,228 +0,0 @@ -""".. _ref_parametric_static_mixer_1: - -Parametric Study Workflow ------------------------------- - -This parametric study workflow example performs these steps - -- Reads a case file and data file -- Creates input and output parameters -- Instantiates a design point study -- Accesses and modifies the input parameters of - the base design point (DP) -- Updates design points -- Creates, updates, and deletes more DPs -- Creates, renames, duplicates and deletes parametric studies -""" -# sphinx_gallery_thumbnail_path = '_static/DP_table.png' - -############################################################################ -from pathlib import Path - -import ansys.fluent.core as pyfluent -from ansys.fluent.core import examples -from ansys.fluent.parametric import ParametricProject, ParametricStudy -from ansys.fluent.post import set_config - -set_config(blocking=True, set_view_on_display="isometric") - -############################################################################ -# Launch Fluent in 3D and double precision - -session = pyfluent.launch_fluent(precision="double", processor_count=2) - -############################################################################ -# Read the hopper/mixer case - -import_filename = examples.download_file( - "Static_Mixer_main.cas.h5", "pyfluent/static_mixer" -) - -session.solver.tui.file.read_case(case_file_name=import_filename) - -############################################################################ -# Set number of iterations to 100 - -session.solver.tui.solve.set.number_of_iterations("100") - -############################################################################ -# Create input parameters after enabling parameter creation in the TUI: -# Parameter values: -# Inlet1: velocity (inlet1_vel) 0.5 m/s and temperature (inlet1_temp) at 300 K -# Inlet2: velocity (inlet2_vel) 0.5 m/s and temperature (inlet2_temp) at 350 K - -session.solver.tui.define.parameters.enable_in_TUI("yes") - -session.solver.tui.define.boundary_conditions.set.velocity_inlet( - "inlet1", (), "vmag", "yes", "inlet1_vel", 1, "quit" -) -session.solver.tui.define.boundary_conditions.set.velocity_inlet( - "inlet1", (), "temperature", "yes", "inlet1_temp", 300, "quit" -) - -session.solver.tui.define.boundary_conditions.set.velocity_inlet( - "inlet2", (), "vmag", "yes", "no", "inlet2_vel", 1, "quit" -) -session.solver.tui.define.boundary_conditions.set.velocity_inlet( - "inlet2", (), "temperature", "yes", "no", "inlet2_temp", 350, "quit" -) - -########################################################################### -# Create output parameters using report definitions - -session.solver.root.solution.report_definitions.surface["outlet-temp-avg"] = {} -session.solver.root.solution.report_definitions.surface[ - "outlet-temp-avg" -].report_type = "surface-areaavg" -session.solver.root.solution.report_definitions.surface[ - "outlet-temp-avg" -].field = "temperature" -session.solver.root.solution.report_definitions.surface[ - "outlet-temp-avg" -].surface_names = ["outlet"] - -session.solver.root.solution.report_definitions.surface["outlet-vel-avg"] = {} -session.solver.root.solution.report_definitions.surface[ - "outlet-vel-avg" -].report_type = "surface-areaavg" -session.solver.root.solution.report_definitions.surface[ - "outlet-vel-avg" -].field = "velocity-magnitude" -session.solver.root.solution.report_definitions.surface[ - "outlet-vel-avg" -].surface_names = ["outlet"] - -session.solver.tui.define.parameters.enable_in_TUI("yes") -session.solver.tui.define.parameters.output_parameters.create( - "report-definition", "outlet-temp-avg" -) -session.solver.tui.define.parameters.output_parameters.create( - "report-definition", "outlet-vel-avg" -) - -########################################################################### -# Enable convergence condition check - -session.solver.tui.solve.monitors.residual.criterion_type("0") - -########################################################################### -# Write case with all the settings in place -case_path = str(Path(pyfluent.EXAMPLES_PATH) / "Static_Mixer_Parameters.cas.h5") -session.solver.tui.file.write_case(case_path) - -########################################################################### -# Instantiate a parametric study from a Fluent session - -study_1 = ParametricStudy(session.solver.root.parametric_studies).initialize() - -########################################################################### -# Access and modify input parameters of base DP - -input_parameters_update = study_1.design_points["Base DP"].input_parameters -input_parameters_update["inlet1_vel"] = 0.5 -study_1.design_points["Base DP"].input_parameters = input_parameters_update - -########################################################################### -# Update current design point - -study_1.update_current_design_point() - -########################################################################### -# Add a new design point - -design_point_1 = study_1.add_design_point() -design_point_1_input_parameters = study_1.design_points["DP1"].input_parameters -design_point_1_input_parameters["inlet1_temp"] = 500 -design_point_1_input_parameters["inlet1_vel"] = 1 -design_point_1_input_parameters["inlet2_vel"] = 1 -study_1.design_points["DP1"].input_parameters = design_point_1_input_parameters - -########################################################################## -# Duplicate design points - -design_point_2 = study_1.duplicate_design_point(design_point_1) - -######################################################################### -# Update all design points for study 1 - -study_1.update_all_design_points() - -############################################################################### -# Export design point table as a CSV table - -design_point_table = str( - Path(pyfluent.EXAMPLES_PATH) / "design_point_table_study_1.csv" -) -study_1.export_design_table(design_point_table) - -########################################################################## -# Delete design points - -study_1.delete_design_points([design_point_1]) - -########################################################################## -# Create a new parametric study by duplicating the current one - -study_2 = study_1.duplicate() - -######################################################################### -# Rename the newly created parametric study - -study_2.rename("New Study") - -######################################################################### -# Delete the old parametric study - -study_1.delete() - -######################################################################### -# Save the parametric project and close Fluent - -project_filepath = str(Path(pyfluent.EXAMPLES_PATH) / "static_mixer_study.flprj") - -session.solver.tui.file.parametric_project.save_as(project_filepath) - -session.exit() - -######################################################################### -# Launch Fluent again and read the previously saved project - -session = pyfluent.launch_fluent(precision="double", processor_count=2) -project_filepath_read = str(Path(pyfluent.EXAMPLES_PATH) / "static_mixer_study.flprj") - -proj = ParametricProject( - session.solver.root.file.parametric_project, - session.solver.root.parametric_studies, - project_filepath_read, -) - -######################################################################### -# Save the current project - -proj.save() - -######################################################################### -# Save the current project to a different file name - -project_filepath_save_as = str( - Path(pyfluent.EXAMPLES_PATH) / "static_mixer_study_save_as.flprj" -) -proj.save_as(project_filepath=project_filepath_save_as) - -######################################################################### -# Export the current project - -project_filepath_export = str( - Path(pyfluent.EXAMPLES_PATH) / "static_mixer_study_export.flprj" -) -proj.export(project_filepath=project_filepath_export) - -######################################################################### -# Archive the current project - -proj.archive() - -######################################################################### -# Close Fluent - -session.exit() diff --git a/examples/02-postprocessing/README.txt b/examples/02-postprocessing/README.txt deleted file mode 100644 index ab302a748f0..00000000000 --- a/examples/02-postprocessing/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -Postprocessing Examples -======================== -These examples demonstrate how to use the PyVista package to postprocess Fluent -results. \ No newline at end of file diff --git a/examples/02-postprocessing/post_processing_exhaust_manifold.py b/examples/02-postprocessing/post_processing_exhaust_manifold.py deleted file mode 100644 index 4fe97fd29e5..00000000000 --- a/examples/02-postprocessing/post_processing_exhaust_manifold.py +++ /dev/null @@ -1,155 +0,0 @@ -""".. _ref_post_processing_exhaust_manifold: - -Postprocessing using PyVista and Matplotlib ---------------------------------------------- -This example demonstrates the postprocessing capabilities of PyFluent -(using PyVista and Matplotlib) using a 3D model -of an exhaust manifold with high temperature flows passing through. -The flow through the manifold is turbulent and -involves conjugate heat transfer. - -This example demonstrates postprocessing using pyvista - -- Create surfaces for the display of 3D data. -- Display filled contours of temperature on several surfaces. -- Display velocity vectors. -- Plot quantitative results using Matplotlib -""" -# sphinx_gallery_thumbnail_number = -3 - -############################################################################### -import ansys.fluent.core as pyfluent -from ansys.fluent.core import examples -from ansys.fluent.post import set_config -from ansys.fluent.post.matplotlib import Plots -from ansys.fluent.post.pyvista import Graphics - -set_config(blocking=True, set_view_on_display="isometric") - -############################################################################### -# First, download the case and data file and start Fluent as a service with -# Meshing mode, double precision, number of processors: 2 - -import_case = examples.download_file( - filename="exhaust_system.cas.h5", directory="pyfluent/exhaust_system" -) - -import_data = examples.download_file( - filename="exhaust_system.dat.h5", directory="pyfluent/exhaust_system" -) - -session = pyfluent.launch_fluent(precision="double", processor_count=2) - -session.solver.tui.file.read_case(case_file_name=import_case) -session.solver.tui.file.read_data(case_file_name=import_data) - -############################################################################### -# Get the graphics object for mesh display - -graphics = Graphics(session=session) - -############################################################################### -# Create a graphics object for mesh display - -mesh1 = graphics.Meshes["mesh-1"] - -############################################################################### -# Show edges and faces - -mesh1.show_edges = True -mesh1.show_faces = True - -############################################################################### -# Get the surfaces list - -mesh1.surfaces_list = [ - "in1", - "in2", - "in3", - "out1", - "solid_up:1", - "solid_up:1:830", - "solid_up:1:830-shadow", -] -mesh1.display("window-1") - -############################################################################### -# Disable edges and display again - -mesh1.show_edges = False -mesh1.display("window-2") - -############################################################################### -# Create iso-surface on the outlet plane - -surf_outlet_plane = graphics.Surfaces["outlet-plane"] -surf_outlet_plane.surface.type = "iso-surface" -iso_surf1 = surf_outlet_plane.surface.iso_surface -iso_surf1.field = "y-coordinate" -iso_surf1.iso_value = -0.125017 -surf_outlet_plane.display("window-3") - -############################################################################### -# Create iso-surface on the mid-plane (Issue # 276) - -surf_mid_plane_x = graphics.Surfaces["mid-plane-x"] -surf_mid_plane_x.surface.type = "iso-surface" -iso_surf2 = surf_mid_plane_x.surface.iso_surface -iso_surf2.field = "x-coordinate" -iso_surf2.iso_value = -0.174 -surf_mid_plane_x.display("window-4") - -############################################################################### -# Temperature contour on the mid-plane and the outlet - -temperature_contour = graphics.Contours["contour-temperature"] -temperature_contour.field = "temperature" -temperature_contour.surfaces_list = ["mid-plane-x", "outlet-plane"] -temperature_contour.display("window-4") - -############################################################################### -# Contour plot of temperature on the manifold - -temperature_contour_manifold = graphics.Contours["contour-temperature-manifold"] -temperature_contour_manifold.field = "temperature" -temperature_contour_manifold.surfaces_list = [ - "in1", - "in2", - "in3", - "out1", - "solid_up:1", - "solid_up:1:830", -] -temperature_contour_manifold.display("window-5") - -############################################################################### -# Vector on the mid-plane -# Currently using outlet-plane since mid-plane is affected by Issue # 276 - -velocity_vector = graphics.Vectors["velocity-vector"] -velocity_vector.surfaces_list = ["solid_up:1:830"] -velocity_vector.scale = 2 -velocity_vector.display("window-6") - -############################################################################### -# Commenting out due to issue #290 -# Start the Plot Object for the session -plots_session_1 = Plots(session) - -############################################################################### -# Create a default XY-Plot -plot_1 = plots_session_1.XYPlots["plot-1"] - -############################################################################### -# Set the surface on which the plot is plotted and the Y-axis function -plot_1.surfaces_list = ["outlet"] -plot_1.y_axis_function = "temperature" - -############################################################################### -# Plot the created XY-Plot -plot_1.plot("window-7") - -######################################################################### -# Close Fluent - -session.exit() diff --git a/examples/README.txt b/examples/README.txt index ac3e3cb11ff..ea255faebe6 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -13,6 +13,4 @@ Add examples here for PyFluent ``ansys-fluent``. :maxdepth: 1 :hidden: -.. 00-fluent/README.txt -.. 01-parametric/README.txt -.. 02-postprocessing/README.txt \ No newline at end of file +.. 00-fluent/README.txt \ No newline at end of file diff --git a/setup.py b/setup.py index 2d0f0324a29..2ca72b96bea 100644 --- a/setup.py +++ b/setup.py @@ -29,14 +29,6 @@ "appdirs>=1.4.0", ] -install_requires_post = [ - "vtk==9.1.0", - "pyvista==0.33.2", - "pyvistaqt==0.7.0", - "pyside6==6.2.3", - "matplotlib==3.5.1", -] - packages = [] for package in find_namespace_packages(where="src", include="ansys*"): if package.startswith("ansys.api"): @@ -67,7 +59,4 @@ url="https://github.com/pyansys/pyfluent", python_requires=">3.6", install_requires=install_requires, - extras_require={ - "post": install_requires_post, - }, ) diff --git a/src/ansys/fluent/parametric/__init__.py b/src/ansys/fluent/parametric/__init__.py deleted file mode 100644 index bfe52c8639c..00000000000 --- a/src/ansys/fluent/parametric/__init__.py +++ /dev/null @@ -1,734 +0,0 @@ -"""Classes for running a parametric study in Fluent. - -Example -------- ->>> from ansys.fluent.parametric import ParametricStudy - -Instantiate the study from a Fluent session which has already read a case - ->>> study1 = ParametricStudy(session.solver.root.parametric_studies).initialize() - -Access and modify the input parameters of base design point - ->>> ip = study1.design_points["Base DP"].input_parameters ->>> ip['vel_hot'] = 0.2 ->>> study1.design_points["Base DP"].input_parameters = ip - -Update the current design point - ->>> study1.update_current_design_point() - -Access the output parameters of base design point - ->>> study1.design_points["Base DP"].output_parameters - -Create, update more design points and delete them - ->>> dp1 = study1.add_design_point() ->>> dp2 = study1.duplicate_design_point(dp1) ->>> study1.update_all_design_points() ->>> study1.delete_design_points([dp1, dp2]) - -Create, rename, delete parametric studies - ->>> study2 = study1.duplicate() ->>> study2.rename("abc") ->>> study1.delete() - -Project workflow - ->>> from ansys.fluent.parametric import ParametricProject ->>> proj = ParametricProject(session.solver.root.file.parametric_project, session.solver.root.parametric_studies, "nozzle_para_named.flprj") # noqa: E501 ->>> proj.save() ->>> proj.save_as(project_filepath="nozzle_para_named1.flprj") ->>> proj.export(project_filepath="nozzle_para_named2.flprj") ->>> proj.archive() - -Using parametric session - ->>> from ansys.fluent.parametric import ParametricSession ->>> session1 = ParametricSession(case_filepath="elbow_params_2.cas.h5") ->>> session1.studies['elbow_params_2-Solve'].design_points['Base DP'].input_parameters # noqa: E501 ->>> study2 = session1.new_study() ->>> session2 = ParametricSession(project_filepath="nozzle_para_named.flprj") -""" - -from pathlib import Path -import tempfile -from typing import Any, Dict, List, Optional - -import ansys.fluent.core as pyfluent -from ansys.fluent.core import LOG - -BASE_DP_NAME = "Base DP" - - -class DesignPoint: - """Design point in a parametric study. - - Attributes - ---------- - name : str - Name of the design point. - input_parameters : Dict[str, float] - Input parameters values by name. - output_parameters : Dict[str, float] - Output parameters values by name. - write_data_enabled : bool - Whether to write data for the design point. - capture_simulation_report_data_enabled : bool - Whether to capture simulation report data for the design point. - """ - - def __init__(self, name: str, dp_settings: Any): - self.name = name - self._dp_settings = dp_settings - - @property - def input_parameters(self) -> Dict[str, float]: - """Input parameters values by name.""" - return self._dp_settings.input_parameters() - - @input_parameters.setter - def input_parameters(self, value: Dict[str, float]) -> None: - self._dp_settings.input_parameters = value - - @property - def output_parameters(self) -> Dict[str, float]: - """Output parameters values by name.""" - return self._dp_settings.output_parameters() - - @property - def write_data_enabled(self) -> bool: - """Whether to write data for the design point.""" - return self._dp_settings.write_data() - - @write_data_enabled.setter - def write_data_enabled(self, value: bool) -> None: - self._dp_settings.write_data = value - - @property - def capture_simulation_report_data_enabled(self) -> bool: - """Whether to capture simulation report data for the design point.""" - return self._dp_settings.capture_simulation_report_data() - - @capture_simulation_report_data_enabled.setter - def capture_simulation_report_data_enabled(self, value: bool) -> None: - self._dp_settings.capture_simulation_report_data = value - - -class ParametricStudy: - """Class to manage design points. - - Parametric study that manages design points to parametrize a - Fluent solver set-up. Provides ability to run Fluent for a series - of design points, and access/modify the input and output parameters. - - Attributes - ---------- - name : str - Name of the parametric study. - is_current : bool - Whether the parametric study is the current parametric study. - design_points : Dict[str, DesignPoint] - Design points under the parametric study by name. - current_design_point : DesignPoint - The current design point within the design points under the - parametric study. - project_filepath : Path - Filepath of the associated project. - - Methods - ------- - set_as_current() - Set the parametric study as the current parametric study. - get_all_studies() - Get all currently active studies. - initialize() - Initialize parametric study. - duplicate(copy_design_points) - Duplicate the parametric study. - rename(new_name) - Rename the parametric study. - delete() - Delete the parametric study. - use_base_data() - Use base data for the parametric study. - import_design_table(filepath) - Import the design table for the parametric study. - export_design_table(filepath) - Export the design table for the parametric study. - add_design_point(write_data, capture_simulation_report_data) - Add a new design point under the parametric study. - delete_design_points(design_points) - Delete a list of design points. - duplicate_design_point(design_point) - Duplicate the design point. - save_journals(separate_journals) - Save journals. - clear_generated_data(design_points) - Clear generated data for a list of design points. - load_current_design_point_case_data() - Load case-data of the current design point. - update_current_design_point() - Update the current design point. - update_all_design_points() - Update all design points. - update_selected_design_points(design_points) - Update a list of design points. - """ - - _all_studies: Dict[int, "ParametricStudy"] = {} - current_study_name = None - - def __init__( - self, - parametric_studies, - name: Optional[str] = None, - design_points: Dict[str, DesignPoint] = None, - ): - self._parametric_studies = parametric_studies - self.name = name - self.design_points = {} - if design_points is not None: - self.design_points = design_points - self.project_filepath = None - ParametricStudy._all_studies[id(self)] = self - - @classmethod - def get_all_studies(cls) -> Dict[str, "ParametricStudy"]: - """Get all currently active studies. - - Returns - ------- - Dict[str, "ParametricStudy"] - currently active studies - """ - return {v.name: v for _, v in cls._all_studies.items()} - - def initialize(self) -> "ParametricStudy": - """Initialize parametric study.""" - if self._parametric_studies.initialize.is_active(): - self.project_filepath = Path( - tempfile.mkdtemp( - prefix="project-", - suffix=".cffdb", - dir=str(Path.cwd()), # TODO: should be cwd of server - ) - ) - self.project_filepath.rmdir() - old_study_names = self._parametric_studies.get_object_names() - self._parametric_studies.initialize( - project_filename=self.project_filepath.stem - ) - new_study_names = self._parametric_studies.get_object_names() - self.name = set(new_study_names).difference(set(old_study_names)).pop() - base_design_point = DesignPoint( - BASE_DP_NAME, - self._parametric_studies[self.name].design_points[BASE_DP_NAME], - ) - self.design_points = {BASE_DP_NAME: base_design_point} - ParametricStudy.current_study_name = self.name - return self - else: - LOG.error("initialize is not available") - - def rename(self, new_name: str) -> None: - """Rename the parametric study. - - Parameters - ---------- - new_name : str - new name - """ - self._parametric_studies.rename(new_name, self.name) - self.name = new_name - self.design_points = { - k: DesignPoint(k, self._parametric_studies[self.name].design_points[k]) - for k, _ in self.design_points.items() - } - - @property - def is_current(self) -> bool: - """Whether the parametric study is the current parametric study.""" - return ParametricStudy.current_study_name == self.name - - def set_as_current(self) -> None: - """Set the parametric study as the current parametric study.""" - if not self.is_current: - self._parametric_studies.set_as_current(self.name) - ParametricStudy.current_study_name = self.name - - def duplicate(self, copy_design_points: bool = True) -> "ParametricStudy": - """Duplicate the current study. - - Parameters - ---------- - copy_design_points : bool - Whether to copy the design points from the current study. - - Returns - ------- - ParametricStudy - New parametric study instance. - """ - old_study_names = self._parametric_studies.get_object_names() - self._parametric_studies.duplicate(copy_design_points=copy_design_points) - new_study_names = self._parametric_studies.get_object_names() - clone_name = set(new_study_names).difference(set(old_study_names)).pop() - current_study = ParametricStudy.get_all_studies()[ - ParametricStudy.current_study_name - ] - if copy_design_points: - clone_design_points = { - k: DesignPoint(k, self._parametric_studies[clone_name].design_points[k]) - for k, _ in current_study.design_points.items() - } - else: - base_design_point = DesignPoint( - BASE_DP_NAME, - self._parametric_studies[clone_name].design_points[BASE_DP_NAME], - ) - clone_design_points = {BASE_DP_NAME: base_design_point} - clone = ParametricStudy( - self._parametric_studies, clone_name, clone_design_points - ) - ParametricStudy.current_study_name = clone.name - return clone - - def delete(self) -> None: - """Delete the parametric study.""" - if self.is_current: - LOG.error("Cannot delete the current study %s", self.name) - else: - del self._parametric_studies[self.name] - ParametricStudy._all_studies.pop(id(self)) - del self - - def use_base_data(self) -> None: - """Use base data for the parametric study.""" - self._parametric_studies.use_base_data() - - def import_design_table(self, filepath: str) -> None: - """Import the design table for the parametric study. - - Parameters - ---------- - filepath : str - Input filepath. - """ - self._parametric_studies.import_design_table(filepath=filepath) - - def export_design_table(self, filepath: str) -> None: - """Export the design table for the parametric study. - - Parameters - ---------- - filepath : str - Output filepath. - """ - self._parametric_studies.export_design_table(filepath=filepath) - - @property - def current_design_point(self) -> DesignPoint: - """Return the current design point. - - Current design point within the design points under the - parametric study. - """ - dp_name = self._parametric_studies[self.name].current_design_point() - return self.design_points[dp_name] - - def add_design_point( - self, - write_data: bool = False, - capture_simulation_report_data: bool = True, - ) -> DesignPoint: - """Add a new design point under the parametric study. - - Parameters - ---------- - write_data : bool, optional - Whether to write data for the design point, by default - False. - capture_simulation_report_data : bool, optional - Whether to capture simulation report data for the design - point, by default True. - - Returns - ------- - DesignPoint - The new design point. - """ - self.set_as_current() - dp_settings = self._parametric_studies[self.name].design_points - dps_before = dp_settings.get_object_names() - dp_settings.create( - write_data=write_data, - capture_simulation_report_data=capture_simulation_report_data, - ) - dps_after = dp_settings.get_object_names() - dp_name = set(dps_after).difference(set(dps_before)).pop() - design_point = DesignPoint( - dp_name, - self._parametric_studies[self.name].design_points[dp_name], - ) - self.design_points[dp_name] = design_point - return design_point - - def delete_design_points(self, design_points: List[DesignPoint]) -> None: - """Delete a list of design points. - - Parameters - ---------- - design_points : List[DesignPoint] - List of design points to delete. - """ - if self.current_design_point in design_points: - LOG.error( - "Cannot delete the current design point %s", - self.current_design_point.name, - ) - design_points.remove(self.current_design_point) - dp_settings = self._parametric_studies[self.name].design_points - dp_settings.delete_design_points( - design_points=[dp.name for dp in design_points] - ) - for design_point in design_points: - self.design_points.pop(design_point.name) - del design_point - - def duplicate_design_point(self, design_point: DesignPoint) -> DesignPoint: - """Duplicate the design point. - - Parameters - ---------- - design_point : DesignPoint - Design point to duplicate. - - Returns - ------- - DesignPoint - The new design point. - """ - dp_settings = self._parametric_studies[self.name].design_points - dps_before = dp_settings.get_object_names() - dp_settings.duplicate(design_point=design_point.name) - dps_after = dp_settings.get_object_names() - new_dp_name = set(dps_after).difference(set(dps_before)).pop() - new_dp = DesignPoint( - new_dp_name, - self._parametric_studies[self.name].design_points[new_dp_name], - ) - self.design_points[new_dp_name] = new_dp - return new_dp - - def save_journals(self, separate_journals: bool) -> None: - """Save journals. - - Parameters - ---------- - separate_journals : bool - Whether to save separate journal per design point. - """ - dp_settings = self._parametric_studies[self.name].design_points - dp_settings.save_journals(separate_journals=separate_journals) - - def clear_generated_data(self, design_points: List[DesignPoint]) -> None: - """Clear generated data for a list of design points. - - Parameters - ---------- - design_points : List[DesignPoint] - List of design points. - """ - dp_settings = self._parametric_studies[self.name].design_points - dp_settings.clear_generated_data( - design_points=[dp.name for dp in design_points] - ) - - def load_current_design_point_case_data(self) -> None: - """Load case-data of the current design point.""" - dp_settings = self._parametric_studies[self.name].design_points - dp_settings.load_case_data() - - def update_current_design_point(self) -> None: - """Update the current design point.""" - dp_settings = self._parametric_studies[self.name].design_points - dp_settings.update_current() - - def update_all_design_points(self) -> None: - """Update all design points.""" - dp_settings = self._parametric_studies[self.name].design_points - dp_settings.update_all() - - def update_selected_design_points(self, design_points: List[DesignPoint]) -> None: - """Update a list of design points. - - Parameters - ---------- - design_points : List[str] - List of design points to update. - """ - dp_settings = self._parametric_studies[self.name].design_points - dp_settings.update_selected(design_points=[dp.name for dp in design_points]) - - -class ParametricProject: - """Parametric project workflow. - - Attributes - ---------- - project_filepath : str - Filepath of the project. - - Methods - ------- - open(project_filepath, load_case) - Open a project. - save() - Save project. - save_as(project_filepath) - Save as project. - export(project_filepath, convert_to_managed) - Save project as a copy. - archive(archive_name) - Archive project. - """ - - def __init__( - self, - parametric_project, - parametric_studies, - project_filepath: str, - open_project: bool = True, - ): - self._parametric_project = parametric_project - self._parametric_studies = parametric_studies - self.project_filepath = project_filepath - if open_project: - self.open(project_filepath=project_filepath) - - def open( - self, project_filepath: str = "default.flprj", load_case: bool = True - ) -> None: - """Open a project. - - Parameters - ---------- - project_filepath : str, optional - Project filename, by default "default.flprj". - load_case : bool, optional - Specifies whether to load the current case, by default True. - """ - self._parametric_project.open( - project_filename=str(Path(project_filepath).resolve()), - load_case=load_case, - ) - self.project_filepath = project_filepath - for study_name in self._parametric_studies.get_object_names(): - study = ParametricStudy(self._parametric_studies, study_name) - dps_settings = self._parametric_studies[study_name].design_points - for dp_name in dps_settings.get_object_names(): - study.design_points[dp_name] = DesignPoint( - dp_name, dps_settings[dp_name] - ) - - def save(self) -> None: - """Save project.""" - self._parametric_project.save() - - def save_as(self, project_filepath: str) -> None: - """Save as project. - - Parameters - ---------- - project_filepath : str - Project filename. - """ - self._parametric_project.save_as(project_filename=project_filepath) - - def export(self, project_filepath: str, convert_to_managed: bool = False) -> None: - """Save project as a copy. - - Parameters - ---------- - project_filepath : str - Project filename. - convert_to_managed : bool - Specifies whether to convert to managed project. - """ - self._parametric_project.save_as_copy( - project_filename=project_filepath, - convert_to_managed=convert_to_managed, - ) - - def archive(self, archive_path: str = None) -> None: - """Archive project. - - Parameters - ---------- - archive_name : str, optional - Archive name. - """ - if not archive_path: - archive_path = str(Path(self.project_filepath).with_suffix(".flprz")) - self._parametric_project.archive(archive_name=archive_path) - - -class ParametricSessionLauncher: - """Launches fluent for parametric sessions. - - Methods - ------- - __call__(*args, **kwargs) - Launch a session. - """ - - def __init__(self, *args, **kwargs): - self._args = args - self._kwargs = kwargs - - def __call__(self): - return pyfluent.launch_fluent(*self._args, **self._kwargs) - - -class ParametricSession: - """ParametricSession class which encapsulates studies and project. - - Attributes - ---------- - studies : Dict[str, ParametricStudy] - Parametric studies by their name within the session. - project : ParametricProject - Parametric project if a project file is read. - - Methods - ------- - new_study() - Create new study. - delete_study(self, study_name) - Delete study. - rename_study(self, new_name, old_name) - Rename study. - start_transcript() - Start streaming of Fluent transcript. - stop_transcript() - Stop streaming of Fluent transcript. - """ - - def __init__( - self, - case_filepath: str = None, - project_filepath: str = None, - launcher: Any = ParametricSessionLauncher(), - start_transcript: bool = False, - ): - """Instantiate a ParametricSession. - - Parameters - ---------- - case_filepath : str, optional - Case file name, by default None. - project_filepath : str, optional - Project file name, by default None. - launcher : _type_, optional - Fluent launcher, by default ParametricSessionLauncher(). - start_transcript : bool, optional - Whether to start streaming of Fluent transcript, by default - False. - """ - self.studies = {} - self.project = None - self._session = launcher() - self.scheme_eval = self._session.scheme_eval.scheme_eval - self.scheme_eval( - "(set parametric-study-dependents-manager " "save-project-at-exit? #f)" - ) - if start_transcript: - self.start_transcript() - self._root = self._session.solver.root - if case_filepath is not None: - self._root.file.read(file_name=case_filepath, file_type="case") - study = ParametricStudy(self._root.parametric_studies).initialize() - self.studies[study.name] = study - self.project = ParametricProject( - parametric_project=self._root.file.parametric_project, - parametric_studies=self._root.parametric_studies, - project_filepath=str(study.project_filepath), - open_project=False, - ) - elif project_filepath is not None: - self.project = ParametricProject( - parametric_project=self._root.file.parametric_project, - parametric_studies=self._root.parametric_studies, - project_filepath=project_filepath, - ) - studies_settings = self._root.parametric_studies - for study_name in studies_settings.get_object_names(): - study = ParametricStudy(studies_settings, study_name) - dps_settings = studies_settings[study_name].design_points - for dp_name in dps_settings.get_object_names(): - study.design_points[dp_name] = DesignPoint( - dp_name, dps_settings[dp_name] - ) - self.studies[study_name] = study - ParametricStudy.current_study_name = self._root.current_parametric_study() - - def new_study(self) -> ParametricStudy: - """Create new study. - - Returns - ------- - ParametricStudy - New study. - """ - study = self.studies[ParametricStudy.current_study_name].duplicate() - self.studies[study.name] = study - return study - - def delete_study(self, study_name: str) -> None: - """Delete study. - - Parameters - ---------- - study_name : str - Study name. - """ - study = self.studies[study_name] - if study.is_current: - LOG.error("Cannot delete the current study %s", study_name) - else: - study.delete() - self.studies.pop(study_name) - - def rename_study(self, new_name: str, old_name: str) -> None: - """Rename study. - - Parameters - ---------- - new_name : str - New name. - old_name : str - Current name. - """ - study = self.studies.pop(old_name) - study.rename(new_name) - self.studies[new_name] = study - - def exit(self) -> None: - self._session.exit() - - def __enter__(self): - return self - - def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any): - self._session.exit() - - def start_transcript(self) -> None: - """Start streaming of Fluent transcript.""" - self._session.start_transcript() - - def stop_transcript(self) -> None: - """Stop streaming of Fluent transcript.""" - self._session.stop_transcript() diff --git a/src/ansys/fluent/post/__init__.py b/src/ansys/fluent/post/__init__.py deleted file mode 100644 index 21d6412ecef..00000000000 --- a/src/ansys/fluent/post/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Python post processing integrations for the Fluent solver.""" -import platform -import struct -import sys - -import pkg_resources - -required_libraries = { - "vtk": "9.1.0", - "pyvista": "0.33.2", - "pyvistaqt": "0.7.0", - "pyside6": "6.2.3", - "matplotlib": "3.5.1", -} - - -def _get_vtk_install_cmd(reinstall=False): - is64 = struct.calcsize("P") * 8 == 64 - if sys.version_info.minor == 10 and is64: - if platform.system().lower() == "linux": - return f" Please {'reinstall' if reinstall else 'install'} vtk with `pip install {'-I' if reinstall else ''} https://github.com/pyvista/pyvista-wheels/raw/main/vtk-9.1.0.dev0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl`" # noqa: E501 - - elif platform.system().lower() == "windows": - return f" Please {'reinstall' if reinstall else 'install'} vtk with `pip install {'-I' if reinstall else ''} https://github.com/pyvista/pyvista-wheels/raw/main/vtk-9.1.0.dev0-cp310-cp310-win_amd64.whl`" # noqa: E501 - else: - return ( - f" Please {'reinstall' if reinstall else 'install'} " - f"vtk with `pip install vtk=={required_libraries[lib]}`." - ) - - -def _update_vtk_version(): - is64 = struct.calcsize("P") * 8 == 64 - if sys.version_info.minor in (9, 10) and is64: - required_libraries.update({"vtk": "9.1.0.dev0"}) - - -_update_vtk_version() -installed = {pkg.key for pkg in pkg_resources.working_set} -installed_libraries = [ - lib for lib, version in required_libraries.items() if lib in installed -] -missing_libraries = required_libraries.keys() - installed -import_errors = [] -if missing_libraries: - import_errors.append( - (f"Required libraries {missing_libraries} " "are missing to use this feature.") - ) - for lib in missing_libraries: - import_errors.append( - ( - f" Please install {lib} with " - f"`pip install {lib}=={required_libraries[lib]}`." - if lib != "vtk" - else _get_vtk_install_cmd() - ) - ) -if installed_libraries: - versions_mismatched_message = False - for lib in installed_libraries: - required_version = required_libraries[lib] - installed_version = pkg_resources.get_distribution(lib).version - if pkg_resources.parse_version(installed_version) < pkg_resources.parse_version( - required_version - ): - if not versions_mismatched_message: - import_errors.append( - ( - f"Required libraries version is incompatible " - "to use this feature." - ) - ) - versions_mismatched_message = True - import_errors.append( - ( - f" Please re-install {lib} with " - f"`pip install -I {lib}=={required_libraries[lib]}`." - if lib != "vtk" - else _get_vtk_install_cmd(True) - ) - ) - -if import_errors: - raise ImportError("\n".join(import_errors)) -from ansys.fluent.post._config import get_config, set_config # noqa: F401 diff --git a/src/ansys/fluent/post/_config.py b/src/ansys/fluent/post/_config.py deleted file mode 100644 index f34544687fc..00000000000 --- a/src/ansys/fluent/post/_config.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Global configuration state for post.""" - -_global_config = {"blocking": False, "set_view_on_display": None} - - -def get_config() -> dict: - """Retrieve post configuration. - - Returns - ------- - config : dict - Keys are parameter names that can be passed to :func:`set_config`. - """ - return _global_config.copy() - - -def set_config(blocking: bool = False, set_view_on_display: str = None): - """Set post configuration. - - Parameters - ---------- - blocking : bool, default=False - If True, then graphics/plot display will block the current thread. - set_view_on_display : str, default=None - If specified, then graphics will always be displayed in the specified view. - Valid values are xy, xz, yx, yz, zx, zy and isometric. - """ - - _global_config["blocking"] = blocking - _global_config["set_view_on_display"] = set_view_on_display diff --git a/src/ansys/fluent/post/matplotlib/__init__.py b/src/ansys/fluent/post/matplotlib/__init__.py deleted file mode 100644 index 4bedcf55307..00000000000 --- a/src/ansys/fluent/post/matplotlib/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""A package that provides interfacing Fluent with Matplotlib.""" - -from ansys.fluent.post.matplotlib.matplot_objects import Plots # noqa: F401 -from ansys.fluent.post.matplotlib.matplot_windows_manager import ( # noqa: F401 - matplot_windows_manager, -) diff --git a/src/ansys/fluent/post/matplotlib/matplot_objects.py b/src/ansys/fluent/post/matplotlib/matplot_objects.py deleted file mode 100644 index 17e2df843fd..00000000000 --- a/src/ansys/fluent/post/matplotlib/matplot_objects.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Module providing post objects for Matplotlib.""" -import inspect -import sys -from typing import Optional - -from ansys.fluent.core.meta import PyLocalContainer -from ansys.fluent.post.matplotlib.matplot_windows_manager import matplot_windows_manager -from ansys.fluent.post.post_object_defns import XYPlotDefn - - -class Plots: - """Plot objects provider.""" - - _sessions_state = {} - - def __init__(self, session, local_surfaces_provider=None): - """Instantiate Plots, container of plot objects. - - Parameters - ---------- - session : - Session object. - local_surfaces_provider : object, optional - Object providing local surfaces. - """ - session_state = Plots._sessions_state.get(session.id if session else 1) - if not session_state: - session_state = self.__dict__ - Plots._sessions_state[session.id if session else 1] = session_state - self.session = session - self._init_module(self, sys.modules[__name__]) - else: - self.__dict__ = session_state - self._local_surfaces_provider = lambda: local_surfaces_provider or getattr( - self, "Surfaces", [] - ) - - def _init_module(self, obj, mod): - for name, cls in mod.__dict__.items(): - - if cls.__class__.__name__ in ( - "PyLocalNamedObjectMetaAbstract", - ) and not inspect.isabstract(cls): - setattr( - obj, - cls.PLURAL, - PyLocalContainer(self, cls), - ) - - -class XYPlot(XYPlotDefn): - """XY Plot.""" - - def plot(self, window_id: Optional[str] = None): - """Draw XYPlot. - - Parameters - ---------- - window_id : str, optional - Window id. If not specified unique id is used. - """ - self._pre_display() - matplot_windows_manager.plot(self, window_id) - self._post_display() diff --git a/src/ansys/fluent/post/matplotlib/matplot_windows_manager.py b/src/ansys/fluent/post/matplotlib/matplot_windows_manager.py deleted file mode 100644 index 02483064576..00000000000 --- a/src/ansys/fluent/post/matplotlib/matplot_windows_manager.py +++ /dev/null @@ -1,415 +0,0 @@ -"""Module for matplotlib windows management.""" -import itertools -import multiprocessing as mp -from typing import List, Optional, Union - -import numpy as np - -from ansys.api.fluent.v0.field_data_pb2 import PayloadTag -from ansys.fluent.core.session import Session -from ansys.fluent.core.utils.generic import AbstractSingletonMeta, in_notebook -from ansys.fluent.post import get_config -from ansys.fluent.post.matplotlib.plotter_defns import Plotter, ProcessPlotter -from ansys.fluent.post.post_object_defns import GraphicsDefn, PlotDefn -from ansys.fluent.post.post_windows_manager import PostWindow, PostWindowsManager - - -class _ProcessPlotterHandle: - """Class for process plotter handle.""" - - def __init__( - self, - window_id, - curves=[], - title="XY Plot", - xlabel="position", - ylabel="", - ): - self._closed = False - self.plot_pipe, plotter_pipe = mp.Pipe() - self.plotter = ProcessPlotter(window_id, curves, title, xlabel, ylabel) - self.plot_process = mp.Process( - target=self.plotter, args=(plotter_pipe,), daemon=True - ) - self.plot_process.start() - Session._monitor_thread.cbs.append(self.close) - - def plot(self, data): - self.plot_pipe.send(data) - - def set_properties(self, properties): - self.plot_pipe.send({"properties": properties}) - - def save_graphic(self, name: str): - self.plot_pipe.send({"save_graphic": name}) - - def is_closed(self): - if self._closed: - return True - try: - self.plot_pipe.send({}) - except (BrokenPipeError, AttributeError): - self._closed = True - return self._closed - - def close(self): - if self._closed: - return - self._closed = True - try: - self.plot_pipe.send(None) - except (BrokenPipeError, AttributeError): - pass - - -class MatplotWindow(PostWindow): - """Class for MatplotWindow.""" - - def __init__(self, id: str, post_object: Union[GraphicsDefn, PlotDefn]): - """Instantiate a MatplotWindow. - - Parameters - ---------- - id : str - Window id. - post_object : Union[GraphicsDefn, PlotDefn] - Object to plot. - """ - self.post_object: Union[GraphicsDefn, PlotDefn] = post_object - self.id: str = id - self.properties: dict = None - self.plotter: Union[_ProcessPlotterHandle, Plotter] = self._get_plotter() - self.animate: bool = False - self.close: bool = False - self.refresh: bool = False - - def plot(self): - """Draw plot.""" - if not self.post_object: - return - xy_data = self._get_xy_plot_data() - if in_notebook() or get_config()["blocking"]: - self.plotter.set_properties(self.properties) - else: - try: - self.plotter.set_properties(self.properties) - except BrokenPipeError: - self.plotter: Union[ - _ProcessPlotterHandle, Plotter - ] = self._get_plotter() - self.plotter.set_properties(self.properties) - self.plotter.plot(xy_data) - - # private methods - def _get_plotter(self): - return ( - Plotter(self.id) - if in_notebook() or get_config()["blocking"] - else _ProcessPlotterHandle(self.id) - ) - - def _get_xy_plot_data(self): - obj = self.post_object - field = obj.y_axis_function() - node_values = obj.node_values() - boundary_values = obj.boundary_values() - direction_vector = obj.direction_vector() - surfaces_list = obj.surfaces_list() - self.properties = { - "curves": surfaces_list, - "title": "XY Plot", - "xlabel": "position", - "ylabel": field, - } - field_info = obj._data_extractor.field_info() - field_data = obj._data_extractor.field_data() - surfaces_info = field_info.get_surfaces_info() - surface_ids = [ - id - for surf in map( - obj._data_extractor.remote_surface_name, obj.surfaces_list() - ) - for id in surfaces_info[surf]["surface_id"] - ] - - # get scalar field data - field_data.add_get_surfaces_request( - surface_ids, - provide_faces=False, - provide_vertices=True if node_values else False, - provide_faces_centroid=False if node_values else True, - ) - field_data.add_get_scalar_fields_request( - surface_ids, - field, - node_values, - boundary_values, - ) - - location_tag = ( - field_data._payloadTags[PayloadTag.NODE_LOCATION] - if node_values - else field_data._payloadTags[PayloadTag.ELEMENT_LOCATION] - ) - boundary_value_tag = ( - field_data._payloadTags[PayloadTag.BOUNDARY_VALUES] - if boundary_values - else 0 - ) - surface_tag = 0 - xyplot_payload_data = field_data.get_fields() - data_tag = location_tag | boundary_value_tag - xyplot_data = xyplot_payload_data[data_tag] - surface_data = xyplot_payload_data[surface_tag] - - # loop over all meshes - xy_plots_data = {} - surfaces_list_iter = iter(surfaces_list) - for surface_id, mesh_data in surface_data.items(): - mesh_data["vertices" if node_values else "centroid"].shape = ( - mesh_data["vertices" if node_values else "centroid"].size // 3, - 3, - ) - y_values = xyplot_data[surface_id][field] - x_values = np.matmul( - mesh_data["vertices" if node_values else "centroid"], - direction_vector, - ) - structured_data = np.empty( - x_values.size, - dtype={ - "names": ("xvalues", "yvalues"), - "formats": ("f8", "f8"), - }, - ) - structured_data["xvalues"] = x_values - structured_data["yvalues"] = y_values - sort = np.argsort(structured_data, order=["xvalues"]) - surface_name = next(surfaces_list_iter) - xy_plots_data[surface_name] = structured_data[sort] - return xy_plots_data - - -class MatplotWindowsManager(PostWindowsManager, metaclass=AbstractSingletonMeta): - """Class for matplot windows manager.""" - - def __init__(self): - """Instantiate a windows manager for matplotlib.""" - self._post_windows: Dict[str, MatplotWindow] = {} - - def open_window(self, window_id: Optional[str] = None) -> str: - """Open new window. - - Parameters - ---------- - window_id : str, optional - Id for new window. If not specified unique id is used. - - Returns - ------- - str - Window id. - """ - if not window_id: - window_id = self._get_unique_window_id() - self._open_window(window_id) - return window_id - - def set_object_for_window( - self, object: Union[PlotDefn, GraphicsDefn], window_id: str - ) -> None: - """Associate post object with running window instance. - - Parameters - ---------- - object : Union[GraphicsDefn, PlotDefn] - Post object to associate with window. - - window_id : str - Window id to associate. - - Raises - ------ - RuntimeError - If window does not support object. - """ - if not isinstance(object, PlotDefn): - raise RuntimeError("object not implemented.") - window = self._post_windows.get(window_id) - if window: - window.post_object = object - - def plot( - self, - object: Union[PlotDefn, GraphicsDefn], - window_id: Optional[str] = None, - ) -> None: - """Draw plot. - - Parameters - ---------- - object: Union[GraphicsDefn, PlotDefn] - Object to plot. - - window_id : str, optional - Window id for plot. If not specified unique id is used. - - Raises - ------ - RuntimeError - If window does not support object. - """ - if not isinstance(object, PlotDefn): - raise RuntimeError("object not implemented.") - if not window_id: - window_id = self._get_unique_window_id() - window = self._open_window(window_id) - window.post_object = object - window.plot() - - def save_graphic( - self, - window_id: str, - format: str, - ) -> None: - """Save graphics. - - Parameters - ---------- - window_id : str - Window id for which graphic should be saved. - format : str - Graphic format. Supported formats are eps, jpeg, jpg, - pdf, pgf, png, ps, raw, rgba, svg, svgz, tif and tiff. - - Raises - ------ - ValueError - If window does not support specified format. - """ - window = self._post_windows.get(window_id) - if window: - window.plotter.save_graphic(f"{window_id}.{format}") - - def refresh_windows( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> None: - """Refresh windows. - - Parameters - ---------- - session_id : str, optional - Session id to refresh. If specified, all windows which belong to - specified session will be refreshed. Otherwise windows for all - sessions will be refreshed. - - windows_id : List[str], optional - Windows id to refresh. If not specified, all windows will be - refreshed. - """ - windows_id = self._get_windows_id(session_id, windows_id) - for window_id in windows_id: - window = self._post_windows.get(window_id) - if window: - window.refresh = True - self.plot(window.post_object, window.id) - - def animate_windows( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> None: - """Animate windows. - - Parameters - ---------- - session_id : str, optional - Session id to animate. If specified, animation will be created - for windows which belong to specified session. Otherwise - animation will be created for all windows. - - windows_id : List[str], optional - Windows id to animate. If not specified, animation will be - created for all windows. - - Raises - ------ - NotImplementedError - If not implemented. - """ - raise NotImplementedError("animate_windows not implemented.") - - def close_windows( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> None: - """Close windows. - - Parameters - ---------- - session_id : str, optional - Session id to close. If specified, windows which belong to - specified session will be closed. Otherwise windows for all - sessions will be closed. - - windows_id : List[str], optional - Windows id to close. If not specified, all windows will be - closed. - """ - windows_id = self._get_windows_id(session_id, windows_id) - for window_id in windows_id: - window = self._post_windows.get(window_id) - if window: - window.plotter.close() - window.close = True - - # private methods - - def _open_window(self, window_id: str) -> Union[Plotter, _ProcessPlotterHandle]: - window = self._post_windows.get(window_id) - plotter = None - if ( - window - and not window.plotter.is_closed() - and (not (in_notebook() or get_config()["blocking"]) or window.refresh) - ): - window.refresh = False - else: - window = MatplotWindow(window_id, None) - self._post_windows[window_id] = window - if in_notebook(): - window.plotter() - return window - - def _get_windows_id( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> List[str]: - - return [ - window_id - for window_id in [ - window_id - for window_id, window in self._post_windows.items() - if not window.plotter.is_closed() - and ( - not session_id - or session_id == window.post_object._data_extractor.id() - ) - ] - if not windows_id or window_id in windows_id - ] - - def _get_unique_window_id(self) -> str: - itr_count = itertools.count() - while True: - window_id = f"window-{next(itr_count)}" - if window_id not in self._post_windows: - return window_id - - -matplot_windows_manager = MatplotWindowsManager() diff --git a/src/ansys/fluent/post/matplotlib/plotter_defns.py b/src/ansys/fluent/post/matplotlib/plotter_defns.py deleted file mode 100644 index 2aabe6cae8d..00000000000 --- a/src/ansys/fluent/post/matplotlib/plotter_defns.py +++ /dev/null @@ -1,217 +0,0 @@ -"""Module providing matplotlib plotter functionality.""" - -from typing import List, Optional - -import matplotlib.pyplot as plt -import numpy as np - - -class Plotter: - """Class for matplotlib plotter.""" - - def __init__( - self, - window_id: str, - curves: Optional[List[str]] = [], - title: Optional[str] = "XY Plot", - xlabel: Optional[str] = "position", - ylabel: Optional[str] = "", - remote_process: Optional[bool] = False, - ): - """Instantiate a matplotlib plotter. - - Parameters - ---------- - window_id : str - Window id. - curves : List[str], optional - List of curves name. - title : str, optional - Plot title. - xlabel : str, optional - X axis label. - ylabel : str, optional - Y axis label. - figure : str, optional - Matplot lib figure. - axis : str, optional - Subplot indices. - remote_process: bool, optional - Is remote process. - """ - self._curves = curves - self._title = title - self._xlabel = xlabel - self._ylabel = ylabel - self._window_id = window_id - self._min_y = None - self._max_y = None - self._min_x = None - self._max_x = None - self._data = {} - self._closed = False - self._visible = False - if not remote_process: - self.fig = plt.figure(num=self._window_id) - self.ax = self.fig.add_subplot(111) - - def plot(self, data: dict) -> None: - """Draw plot in window. - - Parameters - ---------- - data : dict - Data to plot. Data consists the list of x and y - values for each curve. - """ - if not data: - return - - for curve in data: - min_y_value = np.amin(data[curve]["yvalues"]) - max_y_value = np.amax(data[curve]["yvalues"]) - min_x_value = np.amin(data[curve]["xvalues"]) - max_x_value = np.amax(data[curve]["xvalues"]) - self._data[curve]["xvalues"] += data[curve]["xvalues"].tolist() - self._data[curve]["yvalues"] += data[curve]["yvalues"].tolist() - self._min_y = min(self._min_y, min_y_value) if self._min_y else min_y_value - self._max_y = max(self._max_y, max_y_value) if self._max_y else max_y_value - self._min_x = min(self._min_x, min_x_value) if self._min_x else min_x_value - self._max_x = max(self._max_x, max_x_value) if self._max_x else max_x_value - - curve_lines = self.ax.lines - for curve, curve_line in zip(self._curves, curve_lines): - curve_line.set_data( - self._data[curve]["xvalues"], self._data[curve]["yvalues"] - ) - x_range = max_x_value - min_x_value - y_range = max_y_value - min_y_value - self.ax.set_xlim(self._min_x, self._max_x) - self.ax.set_ylim(self._min_y - y_range * 0.2, self._max_y + y_range * 0.2) - if not self._visible: - self._visible = True - plt.show() - - def close(self): - """Close window.""" - plt.close(self.fig) - self._closed = True - - def is_closed(self): - """Check if window is closed.""" - return self._closed - - def save_graphic(self, file_name: str): - """Save graphics. - - Parameters - ---------- - file_name : str - File name to save graphic. - """ - plt.savefig(file_name) - - def set_properties(self, properties: dict): - """Set plot properties. - - Parameters - ---------- - properties : dict - Plot properties i.e. curves, title, xlabel and ylabel. - """ - self._curves = properties.get("curves", self._curves) - self._title = properties.get("title", self._title) - self._xlabel = properties.get("xlabel", self._xlabel) - self._ylabel = properties.get("ylabel", self._ylabel) - self._data = {} - self._min_y = None - self._max_y = None - self._min_x = None - self._max_x = None - self._reset() - - def __call__(self): - """Reset and show plot.""" - self._reset() - self._visible = True - plt.show() - - # private methods - def _reset(self): - plt.figure(self.fig.number) - self.ax.cla() - for curve_name in self._curves: - self._data[curve_name] = {} - self._data[curve_name]["xvalues"] = [] - self._data[curve_name]["yvalues"] = [] - self.ax.plot([], [], label=curve_name) - self.fig.canvas.set_window_title("PyFluent [" + self._window_id + "]") - plt.title(self._title) - plt.xlabel(self._xlabel) - plt.ylabel(self._ylabel) - plt.legend(loc="upper right") - - -class ProcessPlotter(Plotter): - """Class for matplotlib process plotter. - - Opens matplotlib window in a separate process. - """ - - def __init__( - self, - window_id, - curves_name=[], - title="XY Plot", - xlabel="position", - ylabel="", - ): - """Instantiate a matplotlib process plotter. - - Parameters - ---------- - window_id : str - Window id. - curves : List[str], optional - List of curves name. - title : str, optional - Plot title. - xlabel : str, optional - X axis label. - ylabel : str, optional - Y axis label. - """ - super().__init__(window_id, curves_name, title, xlabel, ylabel, True) - - def _call_back(self): - try: - while self.pipe.poll(): - data = self.pipe.recv() - if data is None: - self.close() - return False - elif data and isinstance(data, dict): - if "properties" in data: - properties = data["properties"] - self.set_properties(properties) - elif "save_graphic" in data: - name = data["save_graphic"] - self.save_graphic(name) - else: - self.plot(data) - self.fig.canvas.draw() - except BrokenPipeError: - self.close() - return True - - def __call__(self, pipe): - """Reset and show plot.""" - self.pipe = pipe - self.fig = plt.figure(num=self._window_id) - self.ax = self.fig.add_subplot(111) - self._reset() - timer = self.fig.canvas.new_timer(interval=10) - timer.add_callback(self._call_back) - timer.start() - self._visible = True - plt.show() diff --git a/src/ansys/fluent/post/post_object_defns.py b/src/ansys/fluent/post/post_object_defns.py deleted file mode 100644 index b6c49e773eb..00000000000 --- a/src/ansys/fluent/post/post_object_defns.py +++ /dev/null @@ -1,584 +0,0 @@ -"""Module providing post objects definition.""" -from abc import abstractmethod -from typing import List, NamedTuple, Optional - -from ansys.fluent.core.meta import ( - Attribute, - PyLocalNamedObjectMetaAbstract, - PyLocalObjectMeta, - PyLocalPropertyMeta, -) - - -class BasePostObjectDefn: - """Base class for post objects.""" - - def _pre_display(self): - local_surfaces_provider = self._get_top_most_parent()._local_surfaces_provider() - for surf_name in self.surfaces_list(): - if surf_name in list(local_surfaces_provider): - surf_obj = local_surfaces_provider[surf_name] - surf_api = surf_obj._data_extractor.surface_api - surf_api.create_surface_on_server() - - def _post_display(self): - local_surfaces_provider = self._get_top_most_parent()._local_surfaces_provider() - for surf_name in self.surfaces_list(): - if surf_name in list(local_surfaces_provider): - surf_obj = local_surfaces_provider[surf_name] - surf_api = surf_obj._data_extractor.surface_api - surf_api.delete_surface_on_server() - - -class GraphicsDefn(BasePostObjectDefn, metaclass=PyLocalNamedObjectMetaAbstract): - """Abstract base class for graphics objects.""" - - @abstractmethod - def display(self, plotter_id: Optional[str] = None): - """Display graphics. - - Parameters - ---------- - window_id : str, optional - Window id. If not specified unique id is used. - """ - pass - - -class PlotDefn(BasePostObjectDefn, metaclass=PyLocalNamedObjectMetaAbstract): - """Abstract base class for plot objects.""" - - @abstractmethod - def plot(self, plotter_id: Optional[str] = None): - """Draw plot. - - Parameters - ---------- - window_id : str, optional - Window id. If not specified unique id is used. - """ - pass - - -class Vector(NamedTuple): - """Class for vector definition.""" - - x: float - y: float - z: float - - -class XYPlotDefn(PlotDefn): - """XYPlot Definition.""" - - PLURAL = "XYPlots" - - class node_values(metaclass=PyLocalPropertyMeta): - """Show nodal data.""" - - value: bool = True - - class boundary_values(metaclass=PyLocalPropertyMeta): - """Show Boundary values.""" - - value: bool = True - - class direction_vector(metaclass=PyLocalPropertyMeta): - """Direction Vector.""" - - value: Vector = [1, 0, 0] - - class y_axis_function(metaclass=PyLocalPropertyMeta): - """Y Axis Function.""" - - value: str - - @Attribute - def allowed_values(self): - """Y axis function allowed values.""" - return [ - v["solver_name"] - for k, v in self._data_extractor.field_info().get_fields_info().items() - ] - - class x_axis_function(metaclass=PyLocalPropertyMeta): - """X Axis Function.""" - - value: str = "direction-vector" - - @Attribute - def allowed_values(self): - """X axis function allowed values.""" - return ["direction-vector", "curve-length"] - - class surfaces_list(metaclass=PyLocalPropertyMeta): - """List of surfaces for plotting.""" - - value: List[str] - - @Attribute - def allowed_values(self): - """Surface list allowed values.""" - return list( - self._data_extractor.field_info().get_surfaces_info().keys() - ) + list(self._get_top_most_parent()._local_surfaces_provider()) - - -class MeshDefn(GraphicsDefn): - """Mesh graphics.""" - - PLURAL = "Meshes" - - class surfaces_list(metaclass=PyLocalPropertyMeta): - """List of surfaces for mesh graphics.""" - - value: List[str] - - @Attribute - def allowed_values(self): - """Surface list allowed values.""" - return list( - (self._data_extractor.field_info().get_surfaces_info().keys()) - ) + list(self._get_top_most_parent()._local_surfaces_provider()) - - class show_edges(metaclass=PyLocalPropertyMeta): - """Show edges for mesh.""" - - value: bool = False - - -class SurfaceDefn(GraphicsDefn): - """Surface graphics.""" - - PLURAL = "Surfaces" - - class show_edges(metaclass=PyLocalPropertyMeta): - """Show edges for surface.""" - - value: bool = True - - class surface(metaclass=PyLocalObjectMeta): - """Specify surface type.""" - - def _availability(self, name): - if name == "plane_surface": - return self.type() == "plane-surface" - if name == "iso_surface": - return self.type() == "iso-surface" - return True - - class type(metaclass=PyLocalPropertyMeta): - """Surface type.""" - - value: str = "iso-surface" - - @Attribute - def allowed_values(self): - """Surface type allowed values.""" - return ["plane-surface", "iso-surface"] - - class plane_surface(metaclass=PyLocalObjectMeta): - """Plane surface data.""" - - def _availability(self, name): - if name == "xy_plane": - return self.creation_method() == "xy-plane" - if name == "yz_plane": - return self.creation_method() == "yz-plane" - if name == "zx_plane": - return self.creation_method() == "zx-plane" - return True - - class creation_method(metaclass=PyLocalPropertyMeta): - """Creation Method.""" - - value: str = "xy-plane" - - @Attribute - def allowed_values(self): - """Surface type allowed values.""" - return ["xy-plane", "yz-plane", "zx-plane"] - - class xy_plane(metaclass=PyLocalObjectMeta): - """XY Plane.""" - - class z(metaclass=PyLocalPropertyMeta): - """Z value.""" - - value: float = 0 - - @Attribute - def range(self): - """Z value range.""" - return self._data_extractor.field_info().get_range( - "z-coordinate", True - ) - - class yz_plane(metaclass=PyLocalObjectMeta): - """YZ Plane.""" - - class x(metaclass=PyLocalPropertyMeta): - """X value.""" - - value: float = 0 - - @Attribute - def range(self): - """X value range.""" - return self._data_extractor.field_info().get_range( - "x-coordinate", True - ) - - class zx_plane(metaclass=PyLocalObjectMeta): - """ZX Plane.""" - - class y(metaclass=PyLocalPropertyMeta): - """Y value.""" - - value: float = 0 - - @Attribute - def range(self): - """Y value range.""" - return self._data_extractor.field_info().get_range( - "y-coordinate", True - ) - - class iso_surface(metaclass=PyLocalObjectMeta): - """Iso surface data.""" - - class field(metaclass=PyLocalPropertyMeta): - """Iso surface field.""" - - value: str - - @Attribute - def allowed_values(self): - """Field allowed values.""" - field_info = self._data_extractor.field_info() - return [ - v["solver_name"] - for k, v in field_info.get_fields_info().items() - ] - - class rendering(metaclass=PyLocalPropertyMeta): - """Iso surface rendering.""" - - value: str = "mesh" - - @Attribute - def allowed_values(self): - """Surface rendering allowed values.""" - return ["mesh", "contour"] - - class iso_value(metaclass=PyLocalPropertyMeta): - """Iso surface field iso value.""" - - _value: float - - def _reset_on_change(self): - return [self._parent.field] - - @property - def value(self): - """Iso value property setter.""" - if getattr(self, "_value", None) is None: - range = self.range - self._value = range[0] if range else None - return self._value - - @value.setter - def value(self, value): - self._value = value - - @Attribute - def range(self): - """Iso value range.""" - field = self._parent.field() - if field: - return self._data_extractor.field_info().get_range(field, True) - - -class ContourDefn(GraphicsDefn): - """Contour graphics.""" - - PLURAL = "Contours" - - class field(metaclass=PyLocalPropertyMeta): - """Contour field.""" - - value: str - - @Attribute - def allowed_values(self): - """Field allowed values.""" - field_info = self._data_extractor.field_info() - return [v["solver_name"] for k, v in field_info.get_fields_info().items()] - - class surfaces_list(metaclass=PyLocalPropertyMeta): - """Contour surfaces.""" - - value: List[str] - - @Attribute - def allowed_values(self): - """Surfaces list allowed values.""" - return list( - self._data_extractor.field_info().get_surfaces_info().keys() - ) + list(self._get_top_most_parent()._local_surfaces_provider()) - - class filled(metaclass=PyLocalPropertyMeta): - """Show filled contour.""" - - value: bool = True - - class node_values(metaclass=PyLocalPropertyMeta): - """Show nodal data.""" - - _value: bool = True - - @property - def value(self): - """Node value property setter.""" - filled = self._get_parent_by_type(ContourDefn).filled() - auto_range_off = self._get_parent_by_type(ContourDefn).range.auto_range_off - if not filled or (auto_range_off and auto_range_off.clip_to_range()): - self._value = True - return self._value - - @value.setter - def value(self, value): - self._value = value - - class boundary_values(metaclass=PyLocalPropertyMeta): - """Show boundary values.""" - - value: bool = False - - class contour_lines(metaclass=PyLocalPropertyMeta): - """Show contour lines.""" - - value: bool = False - - class show_edges(metaclass=PyLocalPropertyMeta): - """Show edges.""" - - value: bool = False - - class range(metaclass=PyLocalObjectMeta): - """Specify range options.""" - - def _availability(self, name): - if name == "auto_range_on": - return self.option() == "auto-range-on" - if name == "auto_range_off": - return self.option() == "auto-range-off" - return True - - class option(metaclass=PyLocalPropertyMeta): - """Range option.""" - - value: str = "auto-range-on" - - @Attribute - def allowed_values(self): - """Range option allowed values.""" - return ["auto-range-on", "auto-range-off"] - - class auto_range_on(metaclass=PyLocalObjectMeta): - """Specify auto range on.""" - - class global_range(metaclass=PyLocalPropertyMeta): - """Show global range.""" - - value: bool = False - - class auto_range_off(metaclass=PyLocalObjectMeta): - """Specify auto range off.""" - - class clip_to_range(metaclass=PyLocalPropertyMeta): - """Clip contour within range.""" - - value: bool = False - - class minimum(metaclass=PyLocalPropertyMeta): - """Range minimum.""" - - _value: float - - def _reset_on_change(self): - return [ - self._get_parent_by_type(ContourDefn).field, - self._get_parent_by_type(ContourDefn).node_values, - ] - - @property - def value(self): - """Range minimum property setter.""" - if getattr(self, "_value", None) is None: - field = self._get_parent_by_type(ContourDefn).field() - if field: - field_info = self._data_extractor.field_info() - field_range = field_info.get_range( - field, - self._get_parent_by_type(ContourDefn).node_values(), - ) - self._value = field_range[0] - return self._value - - @value.setter - def value(self, value): - self._value = value - - class maximum(metaclass=PyLocalPropertyMeta): - """Range maximum.""" - - _value: float - - def _reset_on_change(self): - return [ - self._get_parent_by_type(ContourDefn).field, - self._get_parent_by_type(ContourDefn).node_values, - ] - - @property - def value(self): - """Range maximum property setter.""" - if getattr(self, "_value", None) is None: - field = self._get_parent_by_type(ContourDefn).field() - if field: - field_info = self._data_extractor.field_info() - field_range = field_info.get_range( - field, - self._get_parent_by_type(ContourDefn).node_values(), - ) - self._value = field_range[1] - - return self._value - - @value.setter - def value(self, value): - self._value = value - - -class VectorDefn(GraphicsDefn): - """Vector graphics.""" - - PLURAL = "Vectors" - - class vectors_of(metaclass=PyLocalPropertyMeta): - """Vector type.""" - - value: str = "velocity" - - @Attribute - def allowed_values(self): - """Vectors of allowed values.""" - return list( - self._data_extractor.field_info().get_vector_fields_info().keys() - ) - - class surfaces_list(metaclass=PyLocalPropertyMeta): - """List of surfaces for vector graphics.""" - - value: List[str] - - @Attribute - def allowed_values(self): - """Surface list allowed values.""" - return list( - self._data_extractor.field_info().get_surfaces_info().keys() - ) + list(self._get_top_most_parent()._local_surfaces_provider()) - - class scale(metaclass=PyLocalPropertyMeta): - """Vector scale.""" - - value: float = 1.0 - - class skip(metaclass=PyLocalPropertyMeta): - """Vector skip.""" - - value: int = 0 - - class show_edges(metaclass=PyLocalPropertyMeta): - """Show edges.""" - - value: bool = False - - class range(metaclass=PyLocalObjectMeta): - """Specify range options.""" - - def _availability(self, name): - if name == "auto_range_on": - return self.option() == "auto-range-on" - if name == "auto_range_off": - return self.option() == "auto-range-off" - return True - - class option(metaclass=PyLocalPropertyMeta): - """Range option.""" - - value: str = "auto-range-on" - - @Attribute - def allowed_values(self): - """Range option allowed values.""" - return ["auto-range-on", "auto-range-off"] - - class auto_range_on(metaclass=PyLocalObjectMeta): - """Specify auto range on.""" - - class global_range(metaclass=PyLocalPropertyMeta): - """Show global range.""" - - value: bool = False - - class auto_range_off(metaclass=PyLocalObjectMeta): - """Specify auto range off.""" - - class clip_to_range(metaclass=PyLocalPropertyMeta): - """Clip vector within range.""" - - value: bool = False - - class minimum(metaclass=PyLocalPropertyMeta): - """Range minimum.""" - - _value: float - - @property - def value(self): - """Range minimum property setter.""" - if getattr(self, "_value", None) is None: - field_info = self._data_extractor.field_info() - field_range = field_info.get_range( - "velocity-magnitude", - False, - ) - self._value = field_range[0] - return self._value - - @value.setter - def value(self, value): - self._value = value - - class maximum(metaclass=PyLocalPropertyMeta): - """Range maximum.""" - - _value: float - - @property - def value(self): - """Range maximum property setter.""" - if getattr(self, "_value", None) is None: - field_info = self._data_extractor.field_info() - field_range = field_info.get_range( - "velocity-magnitude", - False, - ) - self._value = field_range[1] - return self._value - - @value.setter - def value(self, value): - self._value = value diff --git a/src/ansys/fluent/post/post_windows_manager.py b/src/ansys/fluent/post/post_windows_manager.py deleted file mode 100644 index e51ca8d6e2a..00000000000 --- a/src/ansys/fluent/post/post_windows_manager.py +++ /dev/null @@ -1,171 +0,0 @@ -"""Module providing PostWindow and PostWindowManager abstract classes. - -PostWindowManager is container for PostWindow. -""" -from abc import ABCMeta, abstractmethod -from typing import List, Optional, Union - -from ansys.fluent.post.post_object_defns import GraphicsDefn, PlotDefn - - -class PostWindow: - """Abstract class for post window.""" - - @abstractmethod - def plot(self): - """Draw plot.""" - pass - - -class PostWindowsManager(metaclass=ABCMeta): - """Abstract class for post windows management.""" - - @abstractmethod - def open_window(self, window_id: Optional[str] = None) -> str: - """Open new window. - - Parameters - ---------- - window_id : str, optional - Id for new window. If not specified unique id is used. - - Returns - ------- - str - Window id. - """ - pass - - @abstractmethod - def set_object_for_window( - self, object: Union[GraphicsDefn, PlotDefn], window_id: str - ) -> None: - """Associate post object with running window instance. - - Parameters - ---------- - object : Union[GraphicsDefn, PlotDefn] - Post object to associate with window. - - window_id : str - Window id to associate. - - Raises - ------ - RuntimeError - If window does not support object. - """ - pass - - @abstractmethod - def plot( - self, - object: Union[GraphicsDefn, PlotDefn], - window_id: Optional[str] = None, - ) -> None: - """Draw plot. - - Parameters - ---------- - object: Union[GraphicsDefn, PlotDefn] - Object to plot. - - window_id : str, optional - Window id for plot. If not specified unique id is used. - - Raises - ------ - RuntimeError - If window does not support object. - """ - pass - - @abstractmethod - def save_graphic( - self, - window_id: str, - format: str, - ) -> None: - """Save graphics. - - Parameters - ---------- - window_id : str - Window id for which graphic should be saved. - format : str - Graphic format. - - Raises - ------ - ValueError - If window does not support specified format. - """ - pass - - @abstractmethod - def refresh_windows( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> None: - """Refresh windows. - - Parameters - ---------- - session_id : str, optional - Session id to refresh. If specified, all windows which belong to - specified session will be refreshed. Otherwise windows for all - sessions will be refreshed. - - windows_id : List[str], optional - Windows id to refresh. If not specified, all windows will be - refreshed. - """ - pass - - @abstractmethod - def animate_windows( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> None: - """Animate windows. - - Parameters - ---------- - session_id : str, optional - Session id to animate. If specified, animation will be created - for windows which belong to specified session. Otherwise - animation will be created for all windows. - - windows_id : List[str], optional - Windows id to animate. If not specified, animation will be - created for all windows. - - Raises - ------ - NotImplementedError - If not implemented. - """ - pass - - @abstractmethod - def close_windows( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> None: - """Close windows. - - Parameters - ---------- - session_id : str, optional - Session id to close. If specified, windows which belong to - specified session will be closed. Otherwise windows for all - sessions will be closed. - - windows_id : List[str], optional - Windows id to close. If not specified, all windows will be - closed. - """ - pass diff --git a/src/ansys/fluent/post/pyvista/__init__.py b/src/ansys/fluent/post/pyvista/__init__.py deleted file mode 100644 index 242f86c8374..00000000000 --- a/src/ansys/fluent/post/pyvista/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""A package that provides interfacing Fluent with PyVista.""" - -from ansys.fluent.post.pyvista.pyvista_objects import Graphics # noqa: F401 -from ansys.fluent.post.pyvista.pyvista_windows_manager import ( # noqa: F401 - pyvista_windows_manager, -) diff --git a/src/ansys/fluent/post/pyvista/pyvista_objects.py b/src/ansys/fluent/post/pyvista/pyvista_objects.py deleted file mode 100644 index 948e68c6db0..00000000000 --- a/src/ansys/fluent/post/pyvista/pyvista_objects.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Module providing post objects for PyVista.""" - -import inspect -import sys -from typing import Optional - -from ansys.fluent.core.meta import PyLocalContainer -from ansys.fluent.post.post_object_defns import ( - ContourDefn, - MeshDefn, - SurfaceDefn, - VectorDefn, -) -from ansys.fluent.post.pyvista.pyvista_windows_manager import pyvista_windows_manager - - -class Graphics: - """Graphics objects provider.""" - - _sessions_state = {} - - def __init__(self, session, local_surfaces_provider=None): - """Instantiate Graphics, container of graphics objects. - - Parameters - ---------- - session : - Session object. - local_surfaces_provider : object, optional - Object providing local surfaces. - """ - session_state = Graphics._sessions_state.get(session.id if session else 1) - if not session_state: - session_state = self.__dict__ - Graphics._sessions_state[session.id if session else 1] = session_state - self.session = session - self._init_module(self, sys.modules[__name__]) - else: - self.__dict__ = session_state - self._local_surfaces_provider = lambda: local_surfaces_provider or getattr( - self, "Surfaces", [] - ) - - def _init_module(self, obj, mod): - for name, cls in mod.__dict__.items(): - - if cls.__class__.__name__ in ( - "PyLocalNamedObjectMetaAbstract", - ) and not inspect.isabstract(cls): - setattr( - obj, - cls.PLURAL, - PyLocalContainer(self, cls), - ) - - -class Mesh(MeshDefn): - """Mesh graphics.""" - - def display(self, window_id: Optional[str] = None): - """Display mesh graphics. - - Parameters - ---------- - window_id : str, optional - Window id. If not specified unique id is used. - """ - self._pre_display() - pyvista_windows_manager.plot(self, window_id) - self._post_display() - - -class Surface(SurfaceDefn): - """Surface graphics.""" - - def display(self, window_id: Optional[str] = None): - """Display surface graphics. - - Parameters - ---------- - window_id : str, optional - Window id. If not specified unique id is used. - """ - pyvista_windows_manager.plot(self, window_id) - - -class Contour(ContourDefn): - """Contour graphics.""" - - def display(self, window_id: Optional[str] = None): - """Display contour graphics. - - Parameters - ---------- - window_id : str, optional - Window id. If not specified unique id is used. - """ - self._pre_display() - pyvista_windows_manager.plot(self, window_id) - self._post_display() - - -class Vector(VectorDefn): - """Vector graphics.""" - - def display(self, window_id: Optional[str] = None): - """Display vector graphics. - - Parameters - ---------- - window_id : str, optional - Window id. If not specified unique id is used. - """ - self._pre_display() - pyvista_windows_manager.plot(self, window_id) - self._post_display() diff --git a/src/ansys/fluent/post/pyvista/pyvista_windows_manager.py b/src/ansys/fluent/post/pyvista/pyvista_windows_manager.py deleted file mode 100644 index 88dd9855930..00000000000 --- a/src/ansys/fluent/post/pyvista/pyvista_windows_manager.py +++ /dev/null @@ -1,720 +0,0 @@ -"""Module for pyVista windows management.""" -import itertools -import threading -from typing import List, Optional, Union - -import numpy as np -import pyvista as pv -from pyvistaqt import BackgroundPlotter - -from ansys.api.fluent.v0.field_data_pb2 import PayloadTag -from ansys.fluent.core.session import Session -from ansys.fluent.core.utils.generic import AbstractSingletonMeta, in_notebook -from ansys.fluent.post import get_config -from ansys.fluent.post.post_object_defns import GraphicsDefn, PlotDefn -from ansys.fluent.post.post_windows_manager import PostWindow, PostWindowsManager - - -class PyVistaWindow(PostWindow): - """Class for PyVista window.""" - - def __init__(self, id: str, post_object: Union[GraphicsDefn, PlotDefn]): - """Instantiate a PyVistaWindow. - - Parameters - ---------- - id : str - Window id. - post_object : Union[GraphicsDefn, PlotDefn] - Object to draw. - """ - self.post_object: Union[GraphicsDefn, PlotDefn] = post_object - self.id: str = id - self.plotter: Union[BackgroundPlotter, pv.Plotter] = ( - pv.Plotter(title=f"PyFluent ({self.id})") - if in_notebook() or get_config()["blocking"] - else BackgroundPlotter(title=f"PyFluent ({self.id})") - ) - self.animate: bool = False - self.close: bool = False - self.refresh: bool = False - self.update: bool = False - self._visible: bool = False - self._init_properties() - - def plot(self): - """Plot graphics.""" - if not self.post_object: - return - obj = self.post_object - plotter = self.plotter - camera = plotter.camera.copy() - if in_notebook() and self.plotter.theme._jupyter_backend == "pythreejs": - plotter.remove_actor(plotter.renderer.actors.copy()) - else: - plotter.clear() - if obj.__class__.__name__ == "Mesh": - self._display_mesh(obj, plotter) - elif obj.__class__.__name__ == "Surface": - self._display_surface(obj, plotter) - elif obj.__class__.__name__ == "Contour": - self._display_contour(obj, plotter) - elif obj.__class__.__name__ == "Vector": - self._display_vector(obj, plotter) - if self.animate: - plotter.write_frame() - view = get_config()["set_view_on_display"] - view_fun = { - "xy": plotter.view_xy, - "xz": plotter.view_xz, - "yx": plotter.view_yx, - "yz": plotter.view_yz, - "zx": plotter.view_zx, - "zy": plotter.view_zy, - "isometric": plotter.view_isometric, - }.get(view) - if view_fun: - view_fun() - else: - plotter.camera = camera.copy() - if not self._visible: - plotter.show() - self._visible = True - - # private methods - - def _init_properties(self): - self.plotter.theme.cmap = "jet" - self.plotter.background_color = "white" - self.plotter.theme.font.color = "black" - - def _scalar_bar_default_properties(self) -> dict: - return dict( - title_font_size=20, - label_font_size=16, - shadow=True, - fmt="%.6e", - font_family="arial", - vertical=True, - position_x=0.06, - position_y=0.3, - ) - - def _display_vector(self, obj, plotter: Union[BackgroundPlotter, pv.Plotter]): - - if not obj.surfaces_list(): - raise RuntimeError("Vector definition is incomplete.") - - field_info = obj._data_extractor.field_info() - field_data = obj._data_extractor.field_data() - - # surface ids - surfaces_info = field_info.get_surfaces_info() - surface_ids = [ - id - for surf in map( - obj._data_extractor.remote_surface_name, obj.surfaces_list() - ) - for id in surfaces_info[surf]["surface_id"] - ] - - # field - field = "velocity-magnitude" - - # scalar bar properties - scalar_bar_args = self._scalar_bar_default_properties() - - field_data.add_get_surfaces_request(surface_ids) - field_data.add_get_vector_fields_request(surface_ids, obj.vectors_of()) - vector_field_tag = 0 - vector_field_data = field_data.get_fields()[vector_field_tag] - for surface_id, mesh_data in vector_field_data.items(): - mesh_data["vertices"].shape = mesh_data["vertices"].size // 3, 3 - mesh_data[obj.vectors_of()].shape = ( - mesh_data[obj.vectors_of()].size // 3, - 3, - ) - vector_scale = mesh_data["vector-scale"][0] - topology = "line" if mesh_data["faces"][0] == 2 else "face" - if topology == "line": - mesh = pv.PolyData( - mesh_data["vertices"], - lines=mesh_data["faces"], - ) - else: - mesh = pv.PolyData( - mesh_data["vertices"], - faces=mesh_data["faces"], - ) - mesh.cell_data["vectors"] = mesh_data[obj.vectors_of()] - velocity_magnitude = np.linalg.norm(mesh_data[obj.vectors_of()], axis=1) - if obj.range.option() == "auto-range-off": - auto_range_off = obj.range.auto_range_off - range = [auto_range_off.minimum(), auto_range_off.maximum()] - if auto_range_off.clip_to_range(): - velocity_magnitude = np.ma.masked_outside( - velocity_magnitude, - auto_range_off.minimum(), - auto_range_off.maximum(), - ).filled(fill_value=0) - else: - auto_range_on = obj.range.auto_range_on - if auto_range_on.global_range(): - range = field_info.get_range(field, False) - else: - range = field_info.get_range(field, False, surface_ids) - - if obj.skip(): - vmag = np.zeros(velocity_magnitude.size) - vmag[:: obj.skip() + 1] = velocity_magnitude[:: obj.skip() + 1] - velocity_magnitude = vmag - mesh.cell_data["Velocity Magnitude"] = velocity_magnitude - glyphs = mesh.glyph( - orient="vectors", - scale="Velocity Magnitude", - factor=vector_scale * obj.scale(), - geom=pv.Arrow(), - ) - plotter.add_mesh( - glyphs, - scalar_bar_args=scalar_bar_args, - clim=range, - ) - if obj.show_edges(): - plotter.add_mesh(mesh, show_edges=True, color="white") - - def _display_contour(self, obj, plotter: Union[BackgroundPlotter, pv.Plotter]): - if not obj.surfaces_list() or not obj.field(): - raise RuntimeError("Contour definition is incomplete.") - - # contour properties - field = obj.field() - range_option = obj.range.option() - filled = obj.filled() - contour_lines = obj.contour_lines() - node_values = obj.node_values() - boundary_values = obj.boundary_values() - - # scalar bar properties - scalar_bar_args = self._scalar_bar_default_properties() - - field_info = obj._data_extractor.field_info() - field_data = obj._data_extractor.field_data() - surfaces_info = field_info.get_surfaces_info() - surface_ids = [ - id - for surf in map( - obj._data_extractor.remote_surface_name, obj.surfaces_list() - ) - for id in surfaces_info[surf]["surface_id"] - ] - # get scalar field data - field_data.add_get_surfaces_request(surface_ids) - field_data.add_get_scalar_fields_request( - surface_ids, - field, - node_values, - boundary_values, - ) - - location_tag = ( - field_data._payloadTags[PayloadTag.NODE_LOCATION] - if node_values - else field_data._payloadTags[PayloadTag.ELEMENT_LOCATION] - ) - boundary_value_tag = ( - field_data._payloadTags[PayloadTag.BOUNDARY_VALUES] - if boundary_values - else 0 - ) - surface_tag = 0 - - scalar_field_payload_data = field_data.get_fields() - data_tag = location_tag | boundary_value_tag - scalar_field_data = scalar_field_payload_data[data_tag] - surface_data = scalar_field_payload_data[surface_tag] - - # loop over all meshes - for surface_id, mesh_data in surface_data.items(): - mesh_data["vertices"].shape = mesh_data["vertices"].size // 3, 3 - topology = "line" if mesh_data["faces"][0] == 2 else "face" - if topology == "line": - mesh = pv.PolyData( - mesh_data["vertices"], - lines=mesh_data["faces"], - ) - else: - mesh = pv.PolyData( - mesh_data["vertices"], - faces=mesh_data["faces"], - ) - if node_values: - mesh.point_data[field] = scalar_field_data[surface_id][field] - else: - mesh.cell_data[field] = scalar_field_data[surface_id][field] - if range_option == "auto-range-off": - auto_range_off = obj.range.auto_range_off - if auto_range_off.clip_to_range(): - if np.min(mesh[field]) < auto_range_off.maximum(): - maximum_below = mesh.clip_scalar( - scalars=field, - value=auto_range_off.maximum(), - ) - if np.max(maximum_below[field]) > auto_range_off.minimum(): - minimum_above = maximum_below.clip_scalar( - scalars=field, - invert=False, - value=auto_range_off.minimum(), - ) - if filled: - plotter.add_mesh( - minimum_above, - scalars=field, - show_edges=obj.show_edges(), - scalar_bar_args=scalar_bar_args, - ) - - if (not filled or contour_lines) and ( - np.min(minimum_above[field]) - != np.max(minimum_above[field]) - ): - plotter.add_mesh(minimum_above.contour(isosurfaces=20)) - else: - if filled: - plotter.add_mesh( - mesh, - clim=[ - auto_range_off.minimum(), - auto_range_off.maximum(), - ], - scalars=field, - show_edges=obj.show_edges(), - scalar_bar_args=scalar_bar_args, - ) - if (not filled or contour_lines) and ( - np.min(mesh[field]) != np.max(mesh[field]) - ): - plotter.add_mesh(mesh.contour(isosurfaces=20)) - else: - auto_range_on = obj.range.auto_range_on - if auto_range_on.global_range(): - if filled: - plotter.add_mesh( - mesh, - clim=field_info.get_range(field, False), - scalars=field, - show_edges=obj.show_edges(), - scalar_bar_args=scalar_bar_args, - ) - if (not filled or contour_lines) and ( - np.min(mesh[field]) != np.max(mesh[field]) - ): - plotter.add_mesh(mesh.contour(isosurfaces=20)) - - else: - if filled: - plotter.add_mesh( - mesh, - scalars=field, - show_edges=obj.show_edges(), - scalar_bar_args=scalar_bar_args, - ) - if (not filled or contour_lines) and ( - np.min(mesh[field]) != np.max(mesh[field]) - ): - plotter.add_mesh(mesh.contour(isosurfaces=20)) - - def _display_surface(self, obj, plotter: Union[BackgroundPlotter, pv.Plotter]): - surface_api = obj._data_extractor.surface_api - surface_api.create_surface_on_server() - dummy_object = "dummy_object" - post_session = obj._get_top_most_parent() - if ( - obj.surface.type() == "iso-surface" - and obj.surface.iso_surface.rendering() == "contour" - ): - contour = post_session.Contours[dummy_object] - contour.field = obj.surface.iso_surface.field() - contour.surfaces_list = [obj._name] - contour.show_edges = True - contour.range.auto_range_on.global_range = True - self._display_contour(contour, plotter) - del post_session.Contours[dummy_object] - else: - mesh = post_session.Meshes[dummy_object] - mesh.surfaces_list = [obj._name] - mesh.show_edges = True - self._display_mesh(mesh, plotter) - del post_session.Meshes[dummy_object] - surface_api.delete_surface_on_server() - - def _display_mesh(self, obj, plotter: Union[BackgroundPlotter, pv.Plotter]): - if not obj.surfaces_list(): - raise RuntimeError("Mesh definition is incomplete.") - field_info = obj._data_extractor.field_info() - field_data = obj._data_extractor.field_data() - surfaces_info = field_info.get_surfaces_info() - surface_ids = [ - id - for surf in map( - obj._data_extractor.remote_surface_name, obj.surfaces_list() - ) - for id in surfaces_info[surf]["surface_id"] - ] - - field_data.add_get_surfaces_request(surface_ids) - surface_tag = 0 - - surfaces_data = field_data.get_fields()[surface_tag] - for surface_id, mesh_data in surfaces_data.items(): - mesh_data["vertices"].shape = mesh_data["vertices"].size // 3, 3 - topology = "line" if mesh_data["faces"][0] == 2 else "face" - if topology == "line": - mesh = pv.PolyData( - mesh_data["vertices"], - lines=mesh_data["faces"], - ) - else: - mesh = pv.PolyData( - mesh_data["vertices"], - faces=mesh_data["faces"], - ) - plotter.add_mesh(mesh, show_edges=obj.show_edges(), color="lightgrey") - - def _get_refresh_for_plotter(self, window: "PyVistaWindow"): - def refresh(): - - with PyVistaWindowsManager._condition: - plotter = window.plotter - if window.close: - window.animate = False - plotter.close() - return - if not window.update: - return - window.update = False - try: - window.plot() - finally: - PyVistaWindowsManager._condition.notify() - - return refresh - - -class PyVistaWindowsManager(PostWindowsManager, metaclass=AbstractSingletonMeta): - """Class for PyVista windows manager.""" - - _condition = threading.Condition() - - def __init__(self): - """Instantiate WindowManager for PyVista.""" - self._post_windows: Dict[str:PyVistaWindow] = {} - self._plotter_thread: threading.Thread = None - self._post_object: Union[GraphicsDefn, PlotDefn] = None - self._window_id: str = None - self._exit_thread: bool = False - self._app = None - - def get_plotter(self, window_id: str) -> Union[BackgroundPlotter, pv.Plotter]: - """Get PyVista Plotter. - - Parameters - ---------- - window_id : str - Window Id for plotter. - - Returns - ------- - Union[BackgroundPlotter, pv.Plotter] - PyVista Plotter. - """ - with self._condition: - return self._post_windows[window_id].plotter - - def open_window(self, window_id: Optional[str] = None) -> str: - """Open new window. - - Parameters - ---------- - window_id : str, optional - Id for new window. If not specified unique id is used. - - Returns - ------- - str - Window id. - """ - with self._condition: - if not window_id: - window_id = self._get_unique_window_id() - if in_notebook() or get_config()["blocking"]: - self._open_window_notebook(window_id) - else: - self._open_and_plot_console(None, window_id) - return window_id - - def set_object_for_window( - self, object: Union[GraphicsDefn, PlotDefn], window_id: str - ) -> None: - """Associate post object with running window instance. - - Parameters - ---------- - object : Union[GraphicsDefn, PlotDefn] - Post object to associate with window. - - window_id : str - Window id to associate. - - Raises - ------ - RuntimeError - If window does not support object. - """ - if not isinstance(object, GraphicsDefn): - raise RuntimeError("Object type currently not supported.") - with self._condition: - window = self._post_windows.get(window_id) - if window: - window.post_object = object - - def plot( - self, object: Union[GraphicsDefn, PlotDefn], window_id: Optional[str] = None - ) -> None: - """Draw plot. - - Parameters - ---------- - object: Union[GraphicsDefn, PlotDefn] - Object to plot. - - window_id : str, optional - Window id for plot. If not specified unique id is used. - - Raises - ------ - RuntimeError - If window does not support object. - """ - if not isinstance(object, GraphicsDefn): - raise RuntimeError("Object type currently not supported.") - with self._condition: - if not window_id: - window_id = self._get_unique_window_id() - if in_notebook() or get_config()["blocking"]: - self._plot_notebook(object, window_id) - else: - self._open_and_plot_console(object, window_id) - - def save_graphic( - self, - window_id: str, - format: str, - ) -> None: - """Save graphics. - - Parameters - ---------- - window_id : str - Window id for which graphic should be saved. - format : str - Graphic format. Supported formats are svg, eps, ps, pdf and tex. - - Raises - ------ - ValueError - If window does not support specified format. - """ - with self._condition: - window = self._post_windows.get(window_id) - if window: - window.plotter.save_graphic(f"{window_id}.{format}") - - def refresh_windows( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> None: - """Refresh windows. - - Parameters - ---------- - session_id : str, optional - Session id to refresh. If specified, all windows which belong to - specified session will be refreshed. Otherwise windows for all - sessions will be refreshed. - - windows_id : List[str], optional - Windows id to refresh. If not specified, all windows will be - refreshed. - """ - with self._condition: - windows_id = self._get_windows_id(session_id, windows_id) - for window_id in windows_id: - window = self._post_windows.get(window_id) - if window: - window.refresh = True - self.plot(window.post_object, window.id) - - def animate_windows( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> None: - """Animate windows. - - Parameters - ---------- - session_id : str, optional - Session id to animate. If specified, animation will be created - for windows which belong to specified session. Otherwise - animation will be created for all windows. - - windows_id : List[str], optional - Windows id to animate. If not specified, animation will be - created for all windows. - - Raises - ------ - NotImplementedError - If not implemented. - """ - with self._condition: - windows_id = self._get_windows_id(session_id, windows_id) - for window_id in windows_id: - window = self._post_windows.get(window_id) - if window: - window.animate = True - window.plotter.open_gif(f"{window.id}.gif") - - def close_windows( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> None: - """Close windows. - - Parameters - ---------- - session_id : str, optional - Session id to close. If specified, windows which belong to - specified session will be closed. Otherwise windows for all - sessions will be closed. - - windows_id : List[str], optional - Windows id to close. If not specified, all windows will be - closed. - """ - with self._condition: - windows_id = self._get_windows_id(session_id, windows_id) - for window_id in windows_id: - window = self._post_windows.get(window_id) - if window: - if in_notebook() or get_config()["blocking"]: - window.plotter.close() - window.close = True - - # private methods - - def _display(self) -> None: - while True: - with self._condition: - if self._exit_thread: - break - if self._window_id: - window = self._post_windows.get(self._window_id) - plotter = window.plotter if window else None - animate = window.animate if window else False - if not plotter or plotter._closed: - window = PyVistaWindow(self._window_id, self._post_object) - plotter = window.plotter - self._app = plotter.app - plotter.add_callback( - window._get_refresh_for_plotter(window), - 100, - ) - window.post_object = self._post_object - window.animate = animate - window.update = True - self._post_windows[self._window_id] = window - self._post_object = None - self._window_id = None - self._app.processEvents() - with self._condition: - for window in self._post_windows.values(): - plotter = window.plotter - plotter.close() - plotter.app.quit() - self._post_windows.clear() - self._condition.notify() - - def _open_and_plot_console(self, obj: object, window_id: str) -> None: - if self._exit_thread: - return - with self._condition: - self._window_id = window_id - self._post_object = obj - - if not self._plotter_thread: - if Session._monitor_thread: - Session._monitor_thread.cbs.append(self._exit) - self._plotter_thread = threading.Thread(target=self._display, args=()) - self._plotter_thread.start() - - with self._condition: - self._condition.wait() - - def _open_window_notebook(self, window_id: str) -> pv.Plotter: - window = self._post_windows.get(window_id) - plotter = None - if window and not window.close and window.refresh: - window.refresh = False - else: - window = PyVistaWindow(window_id, None) - self._post_windows[window_id] = window - return window - - def _plot_notebook(self, obj: object, window_id: str) -> None: - window = self._open_window_notebook(window_id) - window.post_object = obj - plotter = window.plotter - window.plot() - - def _get_windows_id( - self, - session_id: Optional[str] = "", - windows_id: Optional[List[str]] = [], - ) -> List[str]: - with self._condition: - return [ - window_id - for window_id in [ - window_id - for window_id, window in self._post_windows.items() - if not window.plotter._closed - and ( - not session_id - or session_id == window.post_object._data_extractor.id() - ) - ] - if not windows_id or window_id in windows_id - ] - - def _exit(self) -> None: - if self._plotter_thread: - with self._condition: - self._exit_thread = True - self._condition.wait() - self._plotter_thread.join() - self._plotter_thread = None - - def _get_unique_window_id(self) -> str: - itr_count = itertools.count() - with self._condition: - while True: - window_id = f"window-{next(itr_count)}" - if window_id not in self._post_windows: - return window_id - - -pyvista_windows_manager = PyVistaWindowsManager() diff --git a/tests/session.dump b/tests/session.dump deleted file mode 100644 index 27eb2543e70..00000000000 Binary files a/tests/session.dump and /dev/null differ diff --git a/tests/test_parametric.py b/tests/test_parametric.py deleted file mode 100644 index f363ae5953e..00000000000 --- a/tests/test_parametric.py +++ /dev/null @@ -1,74 +0,0 @@ -from pathlib import Path - -import pytest -from pytest_mock import MockerFixture - -from ansys.fluent.core.solver.flobject import Command, NamedObject -from ansys.fluent.core.solver.settings import root -from ansys.fluent.parametric import ParametricProject - - -@pytest.fixture(autouse=True) -def mock_settings_service(mocker: MockerFixture) -> None: - Command.__call__ = mocker.Mock(return_value=None) - NamedObject.get_object_names = mocker.Mock(return_value=[]) - - -@pytest.fixture(name="parametric_project") -def fixture_parametric_project() -> ParametricProject: - return ParametricProject( - root.file.parametric_project(), root.parametric_studies(), "abc.flprj" - ) - - -class TestParamtericProject: - def test_open( - self, - mocker: MockerFixture, - parametric_project: ParametricProject, - ) -> None: - spy = mocker.spy(root.file.parametric_project.open, "__call__") - project_filepath = "abc.flprj" - parametric_project.open(project_filepath=project_filepath) - spy.assert_called_once_with( - project_filename=str(Path(project_filepath).resolve()), - load_case=True, - ) - - def test_save( - self, - mocker: MockerFixture, - parametric_project: ParametricProject, - ) -> None: - spy = mocker.spy(root.file.parametric_project.save, "__call__") - parametric_project.save() - spy.assert_called_once_with() - - def test_save_as( - self, - mocker: MockerFixture, - parametric_project: ParametricProject, - ) -> None: - spy = mocker.spy(root.file.parametric_project.save_as, "__call__") - parametric_project.save_as(project_filepath="abc.flprj") - spy.assert_called_once_with(project_filename="abc.flprj") - - def test_export( - self, - mocker: MockerFixture, - parametric_project: ParametricProject, - ) -> None: - spy = mocker.spy(root.file.parametric_project.save_as_copy, "__call__") - parametric_project.export(project_filepath="abc.flprj") - spy.assert_called_once_with( - project_filename="abc.flprj", convert_to_managed=False - ) - - def test_archive( - self, - mocker: MockerFixture, - parametric_project: ParametricProject, - ) -> None: - spy = mocker.spy(root.file.parametric_project.archive, "__call__") - parametric_project.archive() - spy.assert_called_once_with(archive_name="abc.flprz") diff --git a/tests/test_post.py b/tests/test_post.py deleted file mode 100644 index 70847218e00..00000000000 --- a/tests/test_post.py +++ /dev/null @@ -1,483 +0,0 @@ -from pathlib import Path -import pickle -from typing import Dict, List, Optional, Union - -import numpy as np -import pytest - -from ansys.fluent.core.services.field_data import SurfaceDataType -from ansys.fluent.post.matplotlib import Plots -from ansys.fluent.post.pyvista import Graphics - - -@pytest.fixture(autouse=True) -def patch_mock_data_extractor(mocker) -> None: - mocker.patch( - "ansys.fluent.core.meta.LocalObjectDataExtractor", - MockLocalObjectDataExtractor, - ) - - -class MockFieldData: - def __init__(self, solver_data, field_info): - self._session_data = solver_data - self._request_to_serve = {"surf": [], "scalar": [], "vector": []} - self._field_info = field_info - - def get_surface_data( - self, - surface_name: str, - data_type: Union[SurfaceDataType, int], - overset_mesh: Optional[bool] = False, - ) -> Dict: - surfaces_info = self._field_info().get_surfaces_info() - surface_ids = surfaces_info[surface_name]["surface_id"] - self._request_to_serve["surf"].append( - ( - surface_ids, - overset_mesh, - data_type == SurfaceDataType.Vertices, - data_type == SurfaceDataType.FacesConnectivity, - data_type == SurfaceDataType.FacesCentroid, - data_type == SurfaceDataType.FacesNormal, - ) - ) - enum_to_field_name = { - SurfaceDataType.FacesConnectivity: "faces", - SurfaceDataType.Vertices: "vertices", - SurfaceDataType.FacesCentroid: "centroid", - SurfaceDataType.FacesNormal: "face-normal", - } - - tag_id = 0 - if overset_mesh: - tag_id = self._payloadTags[FieldDataProtoModule.PayloadTag.OVERSET_MESH] - return { - surface_id: self._session_data["fields"][tag_id][surface_id][ - enum_to_field_name[data_type] - ] - for surface_id in surface_ids - } - - def add_get_surfaces_request( - self, - surface_ids: List[int], - overset_mesh: bool = False, - provide_vertices=True, - provide_faces=True, - provide_faces_centroid=False, - provide_faces_normal=False, - ) -> None: - self._request_to_serve["surf"].append( - ( - surface_ids, - overset_mesh, - provide_vertices, - provide_faces, - provide_faces_centroid, - provide_faces_normal, - ) - ) - - def add_get_scalar_fields_request( - self, - surface_ids: List[int], - field_name: str, - node_value: Optional[bool] = True, - boundary_value: Optional[bool] = False, - ) -> None: - self._request_to_serve["scalar"].append( - (surface_ids, field_name, node_value, boundary_value) - ) - - def add_get_vector_fields_request( - self, - surface_ids: List[int], - vector_field: Optional[str] = "velocity", - ) -> None: - self._request_to_serve["vector"].append((surface_ids, vector_field)) - - def get_fields(self) -> Dict[int, Dict]: - fields = {} - for request_type, requests in self._request_to_serve.items(): - for request in requests: - if request_type == "surf": - tag_id = 0 - if request_type == "scalar": - location_tag = 4 if request[2] else 2 - boundary_tag = 8 if request[3] else 0 - tag_id = location_tag | boundary_tag - if request_type == "vector": - tag_id = 0 - - field_requests = fields.get(tag_id) - if not field_requests: - field_requests = fields[tag_id] = {} - surf_ids = request[0] - for surf_id in surf_ids: - surface_requests = field_requests.get(surf_id) - if not surface_requests: - surface_requests = field_requests[surf_id] = {} - surface_requests.update( - self._session_data["fields"][tag_id][surf_id] - ) - return fields - - -class MockFieldInfo: - def __init__(self, solver_data): - self._session_data = solver_data - - def get_range( - self, field: str, node_value: bool = False, surface_ids: List[int] = [] - ) -> List[float]: - if not surface_ids: - surface_ids = [ - v["surface_id"][0] - for k, v in self._session_data["surfaces_info"].items() - ] - minimum, maximum = None, None - for surface_id in surface_ids: - range = self._session_data["range"][field][surface_id][ - "node_value" if node_value else "cell_value" - ] - minimum = min(range[0], minimum) if minimum else range[0] - maximum = max(range[1], maximum) if maximum else range[1] - return [minimum, maximum] - - def get_fields_info(self) -> dict: - return self._session_data["scalar_fields_info"] - - def get_vector_fields_info(self) -> dict: - return self._session_data["vector_fields_info"] - - def get_surfaces_info(self) -> dict: - return self._session_data["surfaces_info"] - - -class MockLocalObjectDataExtractor: - _session_data = None - _session_dump = "tests//session.dump" - - def __init__(self, obj=None): - if not MockLocalObjectDataExtractor._session_data: - with open( - str(Path(MockLocalObjectDataExtractor._session_dump).resolve()), - "rb", - ) as pickle_obj: - MockLocalObjectDataExtractor._session_data = pickle.load(pickle_obj) - self.field_info = lambda: MockFieldInfo( - MockLocalObjectDataExtractor._session_data - ) - self.field_data = lambda: MockFieldData( - MockLocalObjectDataExtractor._session_data, self.field_info - ) - self.id = lambda: 1 - - -def test_field_api(): - pyvista_graphics = Graphics(session=None) - contour1 = pyvista_graphics.Contours["contour-1"] - field_info = contour1._data_extractor.field_info() - field_data = contour1._data_extractor.field_data() - - surfaces_id = [ - v["surface_id"][0] for k, v in field_info.get_surfaces_info().items() - ] - - # Get vertices - vertices_data = field_data.get_surface_data("wall", SurfaceDataType.Vertices) - - # Get multiple fields - field_data.add_get_surfaces_request( - surfaces_id[:1], - provide_vertices=True, - provide_faces_centroid=True, - provide_faces=False, - ) - field_data.add_get_scalar_fields_request(surfaces_id[:1], "temperature", True) - field_data.add_get_scalar_fields_request(surfaces_id[:1], "temperature", False) - fields = field_data.get_fields() - - surface_tag = 0 - vertices = fields[surface_tag][surfaces_id[0]]["vertices"] - centroid = fields[surface_tag][surfaces_id[0]]["centroid"] - - node_location_tag = 4 - node_data = fields[node_location_tag][surfaces_id[0]]["temperature"] - element_location_tag = 2 - element_data = fields[element_location_tag][surfaces_id[0]]["temperature"] - - # Compare vertices obtained by different APIs - np.testing.assert_array_equal(vertices, vertices_data[next(iter(vertices_data))]) - assert len(vertices) == len(node_data) * 3 - assert len(centroid) == len(element_data) * 3 - - -def test_graphics_operations(): - pyvista_graphics1 = Graphics(session=None) - pyvista_graphics2 = Graphics(session=None) - contour1 = pyvista_graphics1.Contours["contour-1"] - contour2 = pyvista_graphics2.Contours["contour-2"] - - # create - assert pyvista_graphics1 is not pyvista_graphics2 - assert pyvista_graphics1.Contours is pyvista_graphics2.Contours - assert list(pyvista_graphics1.Contours) == ["contour-1", "contour-2"] - - contour2.field = "temperature" - contour2.surfaces_list = contour2.surfaces_list.allowed_values - - contour1.field = "pressure" - contour1.surfaces_list = contour2.surfaces_list.allowed_values[0] - - # copy - pyvista_graphics2.Contours["contour-3"] = contour1() - contour3 = pyvista_graphics2.Contours["contour-3"] - assert contour3() == contour1() - - # update - contour3.update(contour2()) - assert contour3() == contour2() - - # del - assert list(pyvista_graphics1.Contours) == [ - "contour-1", - "contour-2", - "contour-3", - ] - del pyvista_graphics1.Contours["contour-3"] - assert list(pyvista_graphics1.Contours) == ["contour-1", "contour-2"] - - -def test_contour_object(): - - pyvista_graphics = Graphics(session=None) - contour1 = pyvista_graphics.Contours["contour-1"] - field_info = contour1._data_extractor.field_info() - - # Surfaces allowed values should be all surfaces. - assert contour1.surfaces_list.allowed_values == list( - field_info.get_surfaces_info().keys() - ) - - # Invalid surface should raise exception. - with pytest.raises(ValueError) as value_error: - contour1.surfaces_list = "surface_does_not_exist" - - # Invalid surface should raise exception. - with pytest.raises(ValueError) as value_error: - contour1.surfaces_list = ["surface_does_not_exist"] - - # Should accept all valid surface. - contour1.surfaces_list = contour1.surfaces_list.allowed_values - - # Field allowed values should be all fields. - assert contour1.field.allowed_values == [ - v["solver_name"] for k, v in field_info.get_fields_info().items() - ] - - # Important. Because there is no type checking so following passes. - contour1.field = [contour1.field.allowed_values[0]] - - # Should accept all valid fields. - contour1.field = contour1.field.allowed_values[0] - - # Invalid field should raise exception. - with pytest.raises(ValueError) as value_error: - contour1.field = "field_does_not_exist" - - # Important. Because there is no type checking so following test passes. - contour1.node_values = "value should be boolean" - - # changing filled to False or setting clip_to_range should set node_value - # to True. - contour1.node_values = False - assert contour1.node_values() == False - contour1.filled = False - assert contour1.node_values() == True - # node value can not be set to False because Filled is False - contour1.node_values = False - assert contour1.node_values() == True - - contour1.filled = True - contour1.node_values = False - assert contour1.node_values() == False - contour1.range.option = "auto-range-off" - contour1.range.auto_range_off.clip_to_range = True - assert contour1.node_values() == True - - contour1.range.option = "auto-range-on" - assert contour1.range.auto_range_off is None - - contour1.range.option = "auto-range-off" - assert contour1.range.auto_range_on is None - - # Range should adjust to min/max of node field values. - contour1.node_values = True - contour1.field = "temperature" - surfaces_id = [ - v["surface_id"][0] - for k, v in field_info.get_surfaces_info().items() - if k in contour1.surfaces_list() - ] - - range = field_info.get_range(contour1.field(), contour1.node_values(), surfaces_id) - assert range[0] == pytest.approx(contour1.range.auto_range_off.minimum()) - assert range[1] == pytest.approx(contour1.range.auto_range_off.maximum()) - - # Range should adjust to min/max of cell field values. - contour1.node_values = False - range = field_info.get_range(contour1.field(), contour1.node_values(), surfaces_id) - assert range[0] == pytest.approx(contour1.range.auto_range_off.minimum()) - assert range[1] == pytest.approx(contour1.range.auto_range_off.maximum()) - - # Range should adjust to min/max of node field values - contour1.field = "pressure" - range = field_info.get_range(contour1.field(), contour1.node_values(), surfaces_id) - assert range[0] == pytest.approx(contour1.range.auto_range_off.minimum()) - assert range[1] == pytest.approx(contour1.range.auto_range_off.maximum()) - - -def test_vector_object(): - - pyvista_graphics = Graphics(session=None) - vector1 = pyvista_graphics.Vectors["contour-1"] - field_info = vector1._data_extractor.field_info() - - assert vector1.surfaces_list.allowed_values == list( - field_info.get_surfaces_info().keys() - ) - - with pytest.raises(ValueError) as value_error: - vector1.surfaces_list = "surface_does_not_exist" - - with pytest.raises(ValueError) as value_error: - vector1.surfaces_list = ["surface_does_not_exist"] - - vector1.surfaces_list = vector1.surfaces_list.allowed_values - - vector1.range.option = "auto-range-on" - assert vector1.range.auto_range_off is None - - vector1.range.option = "auto-range-off" - assert vector1.range.auto_range_on is None - - surfaces_id = [ - v["surface_id"][0] - for k, v in field_info.get_surfaces_info().items() - if k in vector1.surfaces_list() - ] - - range = field_info.get_range("velocity-magnitude", False) - assert range == pytest.approx( - [ - vector1.range.auto_range_off.minimum(), - vector1.range.auto_range_off.maximum(), - ] - ) - - -def test_surface_object(): - - pyvista_graphics = Graphics(session=None) - surf1 = pyvista_graphics.Surfaces["surf-1"] - field_info = surf1._data_extractor.field_info() - - surf1.surface.type = "iso-surface" - assert surf1.surface.plane_surface is None - surf1.surface.type = "plane-surface" - assert surf1.surface.iso_surface is None - - surf1.surface.plane_surface.creation_method = "xy-plane" - assert surf1.surface.plane_surface.yz_plane is None - assert surf1.surface.plane_surface.zx_plane is None - - surf1.surface.type = "iso-surface" - iso_surf = surf1.surface.iso_surface - - assert iso_surf.field.allowed_values == [ - v["solver_name"] for k, v in field_info.get_fields_info().items() - ] - - # Important. Because there is no type checking so following test passes. - iso_surf.field = [iso_surf.field.allowed_values[0]] - - # Incorrect field should throw exception - with pytest.raises(ValueError) as value_error: - iso_surf.field = "field_does_not_exist" - - # Iso surface value should automatically update upon change in field. - iso_surf.field = "temperature" - range = field_info.get_range(iso_surf.field(), True) - assert range[0] == pytest.approx(iso_surf.iso_value()) - - # Setting out of range should throw exception - with pytest.raises(ValueError) as value_error: - iso_surf.iso_value = range[1] + 0.001 - - with pytest.raises(ValueError) as value_error: - iso_surf.iso_value = range[0] - 0.001 - - # Iso surface value should automatically update upon change in field. - iso_surf.field = "pressure" - range = field_info.get_range(iso_surf.field(), True) - assert range[0] == pytest.approx(iso_surf.iso_value()) - - # New surface should be in allowed values for graphics. - cont1 = pyvista_graphics.Contours["surf-1"] - assert "surf-1" in cont1.surfaces_list.allowed_values - - # New surface is not available in allowed values for plots. - matplotlib_plots = Plots(session=None) - p1 = matplotlib_plots.XYPlots["p-1"] - assert "surf-1" not in p1.surfaces_list.allowed_values - - # With local surface provider it becomes available. - local_surfaces_provider = Graphics(session=None).Surfaces - matplotlib_plots = Plots( - session=None, local_surfaces_provider=local_surfaces_provider - ) - assert "surf-1" in p1.surfaces_list.allowed_values - - -def test_create_plot_objects(): - matplotlib_plots1 = Plots(session=None) - matplotlib_plots2 = Plots(session=None) - matplotlib_plots1.XYPlots["p-1"] - matplotlib_plots2.XYPlots["p-2"] - - assert matplotlib_plots1 is not matplotlib_plots2 - assert matplotlib_plots1.XYPlots is matplotlib_plots2.XYPlots - assert list(matplotlib_plots1.XYPlots) == ["p-1", "p-2"] - - -def test_xyplot_object(): - - matplotlib_plots = Plots(session=None) - p1 = matplotlib_plots.XYPlots["p-1"] - field_info = p1._data_extractor.field_info() - - assert p1.surfaces_list.allowed_values == list( - field_info.get_surfaces_info().keys() - ) - - with pytest.raises(ValueError) as value_error: - p1.surfaces_list = "surface_does_not_exist" - - with pytest.raises(ValueError) as value_error: - p1.surfaces_list = ["surface_does_not_exist"] - - p1.surfaces_list = p1.surfaces_list.allowed_values - - assert p1.y_axis_function.allowed_values == [ - v["solver_name"] for k, v in field_info.get_fields_info().items() - ] - - # Important. Because there is no type checking so following passes. - p1.y_axis_function = [p1.y_axis_function.allowed_values[0]] - - p1.y_axis_function = p1.y_axis_function.allowed_values[0] - - with pytest.raises(ValueError) as value_error: - p1.y_axis_function = "field_does_not_exist"