diff --git a/doc/source/links_and_refs.rst b/doc/source/links_and_refs.rst index f6590b5549e..5cf85c2d2f0 100644 --- a/doc/source/links_and_refs.rst +++ b/doc/source/links_and_refs.rst @@ -32,6 +32,7 @@ .. Other libraries repos .. _pyvista_github : https://github.com/pyvista/pyvista +.. _matplotlib_github : https://github.com/matplotlib/matplotlib .. External links .. _sphinx: https://www.sphinx-doc.org/en/master/ diff --git a/doc/source/user_guide/index.rst b/doc/source/user_guide/index.rst index 0cc8f5f6921..4cc30c3dad0 100644 --- a/doc/source/user_guide/index.rst +++ b/doc/source/user_guide/index.rst @@ -62,7 +62,7 @@ Troubleshooting tutorials/post_processing_basics/index.rst tutorials/import_data/index.rst tutorials/mesh/index.rst - tutorials/transform_data/index.rst + tutorials/operators_and_workflows/index.rst tutorials/export_data/index.rst tutorials/plot/index.rst tutorials/animate/index.rst diff --git a/doc/source/user_guide/tutorials/index.rst b/doc/source/user_guide/tutorials/index.rst index 7e9490d4cfe..a24466eba5a 100644 --- a/doc/source/user_guide/tutorials/index.rst +++ b/doc/source/user_guide/tutorials/index.rst @@ -50,8 +50,8 @@ of our package background so you can understand how to work with it. transform simulation data into output data that can be used to visualize and analyze simulation results. -:fa:`book-open-reader` Features usage -************************************* +:fa:`book-open-reader` Common topics +************************************ .. grid:: 1 1 3 3 :gutter: 2 @@ -72,12 +72,12 @@ of our package background so you can understand how to work with it. Learn how to explore a mesh in DPF. - .. grid-item-card:: Transform data with PyDPF-Core - :link: ref_tutorials_transform_data + .. grid-item-card:: Manipulate data with operators and workflows + :link: ref_tutorials_operators_and_workflows :link-type: ref :text-align: center - Learn how to transform and operate on data to obtain the desired input. + Learn how to use operators to process your data and build workflows. .. grid-item-card:: Export data from DPF :link: ref_tutorials_export_data @@ -100,6 +100,21 @@ of our package background so you can understand how to work with it. Explore the different approaches to visualise the data in an animation. + .. grid-item-card:: Mathematical operations + :link: ref_tutorials_mathematics + :link-type: ref + :text-align: center + + Learn how to do mathematical operations using PyDPF-Core and data structures + + .. grid-item-card:: Manipulating physics data + :link: ref_tutorials_manipulate_physics_data + :link-type: ref + :text-align: center + + Learn how to manipulate the physics data associate to a + data storage structure. (Unit, homogeneity ...) + .. grid-item-card:: Enriching DPF capabilities :link: ref_tutorials_enriching :link-type: ref @@ -126,19 +141,4 @@ of our package background so you can understand how to work with it. :link-type: ref :text-align: center - Understand how to access the Entry and Premium licensing capabilities - - .. grid-item-card:: Mathematical operations - :link: ref_tutorials_mathematics - :link-type: ref - :text-align: center - - Learn how to do mathematical operations using PyDPF-Core API and data structures - - .. grid-item-card:: Manipulating physics data - :link: ref_tutorials_manipulate_physics_data - :link-type: ref - :text-align: center - - Learn how to manipulate the physics data associate to a - data storage structure. (Unit, homogeneity ...) \ No newline at end of file + Understand how to access the Entry and Premium licensing capabilities \ No newline at end of file diff --git a/doc/source/user_guide/tutorials/mesh/create_a_mesh_from_scratch.rst b/doc/source/user_guide/tutorials/mesh/create_a_mesh_from_scratch.rst index b4cbba70e9a..902bd72be8e 100644 --- a/doc/source/user_guide/tutorials/mesh/create_a_mesh_from_scratch.rst +++ b/doc/source/user_guide/tutorials/mesh/create_a_mesh_from_scratch.rst @@ -59,7 +59,7 @@ Here, we create a function that will find this connectivity. def search_sequence_numpy(arr, node): """Find the node location in an array of nodes and return its index.""" - indexes = np.isclose(arr, seq) + indexes = np.isclose(arr, node) match = np.all(indexes, axis=1).nonzero() return int(match[0][0]) diff --git a/doc/source/user_guide/tutorials/operators_and_workflows/index.rst b/doc/source/user_guide/tutorials/operators_and_workflows/index.rst new file mode 100644 index 00000000000..900657acab9 --- /dev/null +++ b/doc/source/user_guide/tutorials/operators_and_workflows/index.rst @@ -0,0 +1,41 @@ +.. _ref_tutorials_operators_and_workflows: + +========================================= +Process data with operators and workflows +========================================= + +An operator is the main object used to create, transform, and stream data in DPF. + +They can perform different modifications of the data: direct mathematical operations, +averaging on the mesh, changes in the model locations.... They can also be chained together +to create workflows for more complex operations and customizable results. + +The tutorials in this section present how to create and use these operators and workflows in PyDPF-Core. + +For more information on how to program with PyDPF-Core check the +:ref:`ref_tutorials_language_and_usage` tutorial. + + +.. grid:: 1 1 3 3 + :gutter: 2 + :padding: 2 + :margin: 2 + + .. grid-item-card:: Use operators + :link: ref_tutorials + :link-type: ref + :text-align: center + + This tutorial + + .. grid-item-card:: Create workflows + :link: ref_tutorials + :link-type: ref + :text-align: center + + This tutorial + +.. toctree:: + :maxdepth: 2 + :hidden: + diff --git a/doc/source/user_guide/tutorials/plot/index.rst b/doc/source/user_guide/tutorials/plot/index.rst index 32f11495e50..0b14860821a 100644 --- a/doc/source/user_guide/tutorials/plot/index.rst +++ b/doc/source/user_guide/tutorials/plot/index.rst @@ -4,41 +4,46 @@ Plot ==== -These tutorials demonstrate some different approaches to visualise the data in plots. +These tutorials demonstrate different ways one can visualize the data in plots using PyDPF-Core. .. grid:: 1 1 3 3 :gutter: 2 :padding: 2 :margin: 2 - .. grid-item-card:: Plotting meshes - :link: ref_tutorials + .. grid-item-card:: Plot a mesh + :link: ref_tutorials_plot_mesh :link-type: ref :text-align: center - This tutorial + This tutorial shows several ways to plot meshes. - .. grid-item-card:: Plotting data on the mesh - :link: ref_tutorials + .. grid-item-card:: Add deformation + :link: ref_tutorials_plot_deformed_mesh :link-type: ref :text-align: center - This tutorial + This tutorial shows how to add deformation to plots. - .. grid-item-card:: Plotting data on specific placements - :link: ref_tutorials + .. grid-item-card:: Plot contours + :link: ref_tutorials_plot_contour :link-type: ref :text-align: center - This tutorial + This tutorial shows how to plot contours. - .. grid-item-card:: Plotting a graph - :link: ref_tutorials + .. grid-item-card:: Plot a graph + :link: ref_tutorials_plot_graph :link-type: ref :text-align: center - This tutorial + This tutorial shows how to plot graphs using matplotlib. .. toctree:: :maxdepth: 2 :hidden: + + plot_mesh.rst + plot_deformed_mesh.rst + plot_contour.rst + plot_a_graph.rst \ No newline at end of file diff --git a/doc/source/user_guide/tutorials/plot/plot_a_graph.rst b/doc/source/user_guide/tutorials/plot/plot_a_graph.rst new file mode 100644 index 00000000000..4c77a25632b --- /dev/null +++ b/doc/source/user_guide/tutorials/plot/plot_a_graph.rst @@ -0,0 +1,265 @@ +.. _ref_tutorials_plot_graph: + +============================= +Plot a graph using matplotlib +============================= + +.. |Examples| replace:: :mod:`Examples` +.. |Field| replace:: :class:`Field` +.. |FieldsContainer| replace:: :class:`FieldsContainer` +.. |MeshedRegion| replace:: :class:`MeshedRegion ` +.. |DpfPlotter| replace:: :class:`DpfPlotter` +.. |TimeFreqSupport| replace:: :class:`TimeFreqSupport ` + +.. |Line| replace:: :class:`Line ` +.. |on_coordinates| replace:: :class:`on_coordinates ` +.. |Line.path| replace:: :py:attr:`Line.path ` +.. |min_max_fc| replace:: :class:`min_max_fc ` + +This tutorial explains how to plot a graph with data from DPF using `matplotlib `_. + +The current |DpfPlotter| module does not allow to plot graphs. Instead, you need to import the +`matplotlib `_ library to plot graphs with PyDPF-Core. + +:jupyter-download-script:`Download tutorial as Python script` +:jupyter-download-notebook:`Download tutorial as Jupyter notebook` + +There is a large range of graphs you can plot. Here, we showcase: + +- :ref:`A graph of a result along a path ` +- :ref:`A graph of transient data ` + +.. _ref_graph_result_space: + +Result along a path +------------------- + +In this tutorial, we plot the norm of the displacement along a custom path represented by a |Line|. +For more information about how to create a custom geometric object, +see the :ref:`ref_tutorials_plot_on_custom_geometry` tutorial. + +We first need to get the data of interest, then create a custom |Line| geometry for the path. +We then map the result on the path, and finally create a 2D graph. + +Extract the data +^^^^^^^^^^^^^^^^ + +First, extract the data from a result file or create some from scratch. +For this tutorial we use a case available in the |Examples| module. +For more information on how to import your own result file in DPF, +or on how to create data from user input in PyDPF-Core,see +the :ref:`ref_tutorials_import_data` tutorials section. + +.. jupyter-execute:: + + # Import the ``ansys.dpf.core`` module + import ansys.dpf.core as dpf + # Import the examples module + from ansys.dpf.core import examples + # Import the operators module + from ansys.dpf.core import operators as ops + # Import the geometry module + from ansys.dpf.core import geometry as geo + + # Import the ``matplotlib.pyplot`` module + import matplotlib.pyplot as plt + + # Download and get the path to an example result file + result_file_path_1 = examples.find_static_rst() + + # Create a model from the result file + model_1 = dpf.Model(data_sources=result_file_path_1) + +We then extract the result of interest for the graph. +In this tutorial, we want the norm of the displacement field at the last step. + +.. jupyter-execute:: + + # Get the nodal displacement field at the last simulation step (default) + disp_results_1 = model_1.results.displacement.eval() + + # Get the norm of the displacement field + norm_disp = ops.math.norm_fc(fields_container=disp_results_1).eval() + +Define the path +^^^^^^^^^^^^^^^ + +Create a path as a |Line| passing through the diagonal of the mesh. + +.. jupyter-execute:: + + # Create a discretized line for the path + line_1 = geo.Line(coordinates=[[0.0, 0.06, 0.0], [0.03, 0.03, 0.03]], n_points=50) + # Plot the line on the original mesh + line_1.plot(mesh=model_1.metadata.meshed_region) + +Map the data on the path +^^^^^^^^^^^^^^^^^^^^^^^^ + +Map the displacement norm field to the |Line| using the |on_coordinates| mapping operator. + +This operator interpolates field values at given node coordinates, using element shape functions. + +It takes as input a |FieldsContainer| of data, a 3D vector |Field| of coordinates to interpolate at, +and an optional |MeshedRegion| to use for element shape functions if the first |Field| in the data +provided does not have an associated meshed support. + +.. jupyter-execute:: + + # Interpolate the displacement norm field at the nodes of the custom path + disp_norm_on_path_fc: dpf.FieldsContainer = ops.mapping.on_coordinates( + fields_container=norm_disp, + coordinates=line_1.mesh.nodes.coordinates_field, + ).eval() + # Extract the only field in the collection obtained + disp_norm_on_path: dpf.Field = disp_norm_on_path_fc[0] + print(disp_norm_on_path) + +Plot the graph +^^^^^^^^^^^^^^ + +Plot a graph of the norm of the displacement field along the path using the +`matplotlib `_ library. + +To get the parametric coordinates of the nodes along the line and use them as X-axis, +you can use the |Line.path| property. +It gives the 1D array of parametric coordinates of the nodes of the line along the line. + +The values in the displacement norm field are in the same order as the parametric +coordinates because the mapping operator orders output data the same as the input coordinates. + +.. jupyter-execute:: + + # Get the field of parametric coordinates along the path for the X-axis + line_coordinates = line_1.path + + # Define the curve to plot + plt.plot(line_coordinates, disp_norm_on_path.data) + + # Add titles to the axes and the graph + plt.xlabel("Position on path") + plt.ylabel("Displacement norm") + plt.title("Displacement norm along the path") + + # Display the graph + plt.show() + +.. _ref_graph_result_time: + +Transient data +-------------- + +In this tutorial, we plot the minimum and maximum displacement norm over time for a transient analysis. +For more information about using PyDPF-Core with a transient analysis, +see the :ref:`static_transient_examples` examples. + +We first need to create data for the Y-axis, +and then format the time information of the model for the X-axis, +to finally create a 2D graph using both. + +Prepare data +^^^^^^^^^^^^ + +First, extract the data from a transient result file or create some from scratch. +For this tutorial we use a transient case available in the |Examples| module. +For more information on how to import your own result file in DPF, +or on how to create data from user input in PyDPF-Core, see +the :ref:`ref_tutorials_import_data` tutorials section. + +.. jupyter-execute:: + + # Import the ``ansys.dpf.core`` module + import ansys.dpf.core as dpf + # Import the examples module + from ansys.dpf.core import examples + # Import the operators module + from ansys.dpf.core import operators as ops + + # Import the ``matplotlib.pyplot`` module + import matplotlib.pyplot as plt + + # Download and get the path to an example transient result file + result_file_path_2 = examples.download_transient_result() + + # Create a model from the result file + model_2 = dpf.Model(data_sources=result_file_path_2) + + # Check the model is transient with its ``TimeFreqSupport`` + print(model_2.metadata.time_freq_support) + +We then extract the result of interest for the graph. +In this tutorial, we want the maximum and minimum displacement norm over the field at each time step. + +First extract the displacement field for every time step. + +.. jupyter-execute:: + + # Get the displacement at all time steps + disp_results_2: dpf.FieldsContainer = model_2.results.displacement.on_all_time_freqs.eval() + +Next, get the minimum and maximum of the norm of the displacement at each time step using the |min_max_fc| operator. + +.. jupyter-execute:: + + # Instantiate the min_max operator and give the output of the norm operator as input + min_max_op = ops.min_max.min_max_fc(fields_container=ops.math.norm_fc(disp_results_2)) + + # Get the field of maximum values at each time-step + max_disp: dpf.Field = min_max_op.outputs.field_max() + print(max_disp) + + # Get the field of minimum values at each time-step + min_disp: dpf.Field = min_max_op.outputs.field_min() + print(min_disp) + +The operator already outputs fields where data points are associated to time-steps. + +Prepare time values +^^^^^^^^^^^^^^^^^^^ + +The time or frequency information associated to DPF objects is stored in |TimeFreqSupport| objects. + +You can use the |TimeFreqSupport| of a |Field| with location ``time_freq`` to retrieve the time or +frequency values associated to the entities mentioned in its scoping. + +Here the fields are on all time-steps, so we can simply get the list of all time values without filtering. + +.. jupyter-execute:: + + # Get the field of time values + time_steps_1: dpf.Field = disp_results_2.time_freq_support.time_frequencies + + # Print the time values + print(time_steps_1) + +The time values associated to time-steps are given in a |Field|. +To use it in the graph you need to extract the data of the |Field| as an array. + +.. jupyter-execute:: + + # Get the time values + time_data = time_steps_1.data + print(time_data) + + +Plot the graph +^^^^^^^^^^^^^^ + +Plot a graph of the minimum and maximum displacement over time using the +`matplotlib `_ library. + +Use the ``unit`` property of the fields to properly label the axes. + +.. jupyter-execute:: + + # Define the plot figure + plt.plot(time_data, max_disp.data, "r", label="Max") + plt.plot(time_data, min_disp.data, "b", label="Min") + + # Add axis labels and legend + plt.xlabel(f"Time ({time_steps_1.unit})") + plt.ylabel(f"Displacement ({max_disp.unit})") + plt.legend() + + # Display the graph + plt.show() diff --git a/doc/source/user_guide/tutorials/plot/plot_contour.rst b/doc/source/user_guide/tutorials/plot/plot_contour.rst new file mode 100644 index 00000000000..7c1e74ef905 --- /dev/null +++ b/doc/source/user_guide/tutorials/plot/plot_contour.rst @@ -0,0 +1,290 @@ +.. _ref_tutorials_plot_contour: + +============= +Plot contours +============= + +.. |Field.plot| replace:: :py:meth:`Field.plot() ` +.. |MeshedRegion.plot| replace:: :py:meth:`MeshedRegion.plot() ` +.. |add_mesh| replace:: :py:meth:`add_mesh() ` +.. |add_field| replace:: :py:meth:`add_field() ` +.. |show_figure| replace:: :py:meth:`show_figure() ` +.. |to_nodal_fc| replace:: :py:class:`to_nodal_fc ` +.. |select_component| replace:: :func:`select_component() ` +.. |stress_op| replace:: :py:class:`stress ` +.. |Field.meshed_region| replace:: :py:attr:`Field.meshed_region ` +.. |FieldsContainer.plot| replace:: :py:meth:`FieldsContainer.plot() ` +.. |split_fields| replace:: :py:class:`split_fields ` +.. |split_mesh| replace:: :py:class:`split_mesh ` + +.. |Field| replace:: :py:class:`Field ` +.. |FieldsContainer| replace:: :py:class:`FieldsContainer ` +.. |Model| replace:: :py:class:`Model ` +.. |Examples| replace:: :py:mod:`Examples ` +.. |MeshedRegion| replace:: :py:class:`MeshedRegion ` +.. |MeshesContainer| replace:: :py:class:`MeshesContainer ` +.. |DpfPlotter| replace:: :py:class:`DpfPlotter ` + +This tutorial shows different commands for plotting data contours on meshes. + +PyDPF-Core has a variety of plotting methods for generating 3D plots with Python. +These methods use VTK and leverage the `PyVista `_ library. + +:jupyter-download-script:`Download tutorial as Python script` +:jupyter-download-notebook:`Download tutorial as Jupyter notebook` + +Load data to plot +----------------- + +Load a result file in a model +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For this tutorial, we use mesh information and data from a case available in the |Examples| module. +For more information on how to import your own result file in DPF, see +the :ref:`ref_tutorials_import_data` tutorials section. + +.. jupyter-execute:: + + # Import the ``ansys.dpf.core`` module + import ansys.dpf.core as dpf + # Import the examples module + from ansys.dpf.core import examples + # Import the operators module + from ansys.dpf.core import operators as ops + + # Define the result file path + result_file_path_1 = examples.download_piston_rod() + + # Create a model from the result file + model_1 = dpf.Model(data_sources=result_file_path_1) + +Extract data for the contour +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Extract data for the contour. For more information about extracting results from a result file, +see the :ref:`ref_tutorials_import_data` tutorials section. + +.. note:: + + Only the *'elemental'* or *'nodal'* locations are supported for plotting. + +Here, we choose to plot the XX component of the stress tensor. + +.. jupyter-execute:: + + # Get the stress operator for component XX + stress_XX_op = ops.result.stress_X(data_sources=model_1) + + # The default behavior of the operator is to return data as *'ElementalNodal'* + print(stress_XX_op.eval()) + +We must request the stress in a *'nodal'* location as the default *'ElementalNodal'* location for the stress results +is not supported for plotting. + +There are different ways to change the location. Here, we define the new location using the input of the |stress_op| +operator. Another option would be using an averaging operator on the output of the stress operator, +like the |to_nodal_fc| operator + +.. jupyter-execute:: + + # Define the desired location as an input of the stress operator + stress_XX_op.inputs.requested_location(dpf.locations.nodal) + + # Get the output + stress_XX_fc = stress_XX_op.eval() + +The output if a collection of fields, a |FieldsContainer|. + +Extract a mesh +^^^^^^^^^^^^^^ + +Here we simply get the |MeshedRegion| object of the model, but any other |MeshedRegion| works. + +.. jupyter-execute:: + + # Extract the mesh + meshed_region_1 = model_1.metadata.meshed_region + +Plot a contour of a single field +-------------------------------- + +To plot a single |Field|, you can use: + +- the |Field.plot| method +- the |MeshedRegion.plot| method with the field as argument +- the |DpfPlotter| class and its |add_field| method + +.. hint:: + + Using the |DpfPlotter| class is more performant than using the |Field.plot| method + +.. tab-set:: + + .. tab-item:: Field.plot() + + First, get a |Field| from the stress results |FieldsContainer|. Then, use the |Field.plot| method [1]_. + If the |Field| does not have an associated mesh support (see |Field.meshed_region|), + you must use the ``meshed_region`` argument and provide a mesh. + + .. jupyter-execute:: + + # Get a single field + stress_XX = stress_XX_fc[0] + + # Plot the contour on the mesh + stress_XX.plot(meshed_region=meshed_region_1) + + .. tab-item:: MeshedRegion.plot() + + Use the |MeshedRegion.plot| method [1]_. + You must use the *'field_or_fields_container'* argument and + give the |Field| or the |FieldsContainer| containing the stress results data. + + .. jupyter-execute:: + + # Plot the mesh with the stress field contour + meshed_region_1.plot(field_or_fields_container=stress_XX) + + .. tab-item:: DpfPlotter + + First create an instance of |DpfPlotter| [2]_. Then, add the |Field| to the scene using the |add_field| method. + If the |Field| does not have an associated mesh support (see |Field.meshed_region|), + you must use the *'meshed_region'* argument and provide a mesh. + + To render and show the figure based on the current state of the plotter object, use the |show_figure| method. + + .. jupyter-execute:: + + # Create a DpfPlotter instance + plotter_1 = dpf.plotter.DpfPlotter() + + # Add the field to the scene, here with an explicitly associated mesh + plotter_1.add_field(field=stress_XX, meshed_region=meshed_region_1) + + # Display the scene + plotter_1.show_figure() + + You can also first use the |add_mesh| method to add the mesh to the scene + and then use |add_field| without the ``meshed_region`` argument. + + +Plot a contour of multiple fields +--------------------------------- + +Prepare a collection of fields +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. warning:: + + The fields should not have conflicting data, meaning you cannot build a contour for two fields + with two different sets of data for the same mesh entities (intersecting scopings). + + This means the following methods are for example not available for a collection made of the same field + varying across time, or a collection of fields for different shell layers of the same elements. + +Here we split the field for XX stress based on material to get a collection of fields with non-conflicting associated mesh entities. + +We use the |split_fields| operator to split the field based on the result of the |split_mesh| operator. +The |split_mesh| operator returns a |MeshesContainer| with meshes labeled according to the criterion for the split. +In our case, the split criterion is the material ID. + +.. jupyter-execute:: + + # Split the field based on material property + fields = ( + ops.mesh.split_fields( + field_or_fields_container=stress_XX_fc, + meshes=ops.mesh.split_mesh(mesh=meshed_region_1, property="mat"), + ) + ).eval() + + # Show the result + print(fields) + +For ``MAPDL`` results the split on material is equivalent to a split on ``bodies``, hence the two equivalent labels. + +Plot the contour +^^^^^^^^^^^^^^^^ + +To plot a contour for multiple |Field| objects, you can use: + +- the |FieldsContainer.plot| method if the fields are in a collection +- the |MeshedRegion.plot| method with the field collection as argument +- the |DpfPlotter| class and several calls to its |add_field| method + +.. hint:: + + Using the |DpfPlotter| class is more performant than using the |Field.plot| method + +.. tab-set:: + + .. tab-item:: FieldsContainer.plot() + + Use the |FieldsContainer.plot| method [1]_. + + .. jupyter-execute:: + + # Plot the contour for all fields in the collection + fields.plot() + + The ``label_space`` argument provides further field filtering capabilities. + + .. jupyter-execute:: + + # Plot the contour for ``mat`` 1 only + fields.plot(label_space={"mat":1}) + + .. tab-item:: MeshedRegion.plot() + + Use the |MeshedRegion.plot| method [1]_. + You must use the *'field_or_fields_container'* argument and + give the |Field| or the |FieldsContainer| containing the stress results data. + + .. jupyter-execute:: + + # Plot the mesh with the stress field contours + meshed_region_1.plot(field_or_fields_container=fields) + + .. tab-item:: DpfPlotter + + First create an instance of |DpfPlotter| [2]_. + Then, add each |Field| to the scene using the |add_field| method. + If the |Field| does not have an associated mesh support (see |Field.meshed_region|), + you must use the *'meshed_region'* argument and provide a mesh. + + To render and show the figure based on the current state of the plotter object, use the |show_figure| method. + + .. jupyter-execute:: + + # Create a DpfPlotter instance + plotter_1 = dpf.plotter.DpfPlotter() + + # Add each field to the scene + plotter_1.add_field(field=fields[0]) + plotter_1.add_field(field=fields[1]) + + # Display the scene + plotter_1.show_figure() + +.. rubric:: Footnotes + +.. [1] The |DpfPlotter| displays the mesh with edges, lighting and axis widget enabled by default. + You can pass additional PyVista arguments to all plotting methods to change the default behavior + (see options for `pyvista.plot() `_), such as: + + .. jupyter-execute:: + + model_1.plot(title="Mesh", + text="this is a mesh", # Adds the given text at the bottom of the plot + off_screen=True, + screenshot="mesh_plot_1.png", # Save a screenshot to file with the given name + window_size=[450,350]) + # Notes: + # - To save a screenshot to file, use "screenshot=figure_name.png" ( as well as "notebook=False" if on a Jupyter notebook). + # - The "off_screen" keyword only works when "notebook=False". If "off_screen=True" the plot is not displayed when running the code. + +.. [2] The |DpfPlotter| is currently based on PyVista. + That means that PyVista must be installed. + The DPF plotter also passes additional parameters to the PyVista plotter + (arguments supported by the version of PyVista installed). + More information about available additional arguments is available at `pyvista.plot() `_. diff --git a/doc/source/user_guide/tutorials/plot/plot_deformed_mesh.rst b/doc/source/user_guide/tutorials/plot/plot_deformed_mesh.rst new file mode 100644 index 00000000000..012c593d6fe --- /dev/null +++ b/doc/source/user_guide/tutorials/plot/plot_deformed_mesh.rst @@ -0,0 +1,235 @@ +.. _ref_tutorials_plot_deformed_mesh: + +========================== +Plot with mesh deformation +========================== + +.. |Model.plot| replace:: :py:meth:`Model.plot() ` +.. |MeshedRegion.plot| replace:: :py:meth:`MeshedRegion.plot() ` +.. |MeshesContainer.plot| replace:: :py:meth:`MeshesContainer.plot() ` +.. |add_mesh| replace:: :py:meth:`add_mesh()` +.. |add_field| replace:: :py:meth:`add_field()` +.. |show_figure| replace:: :py:meth:`show_figure()` +.. |split_mesh| replace:: :py:class:`split_mesh ` +.. |disp_op| replace:: :py:class:`displacement operator ` + +.. |Model| replace:: :py:class:`Model ` +.. |Examples| replace:: :py:mod:`Examples` +.. |MeshedRegion| replace:: :py:class:`MeshedRegion ` +.. |MeshesContainer| replace:: :py:class:`MeshesContainer ` +.. |DpfPlotter| replace:: :py:class:`DpfPlotter` +.. |Result| replace:: :py:class:`Result ` +.. |Operator| replace:: :py:class:`Operator` +.. |Field| replace:: :py:class:`Field` +.. |FieldsContainer| replace:: :py:class:`FieldsContainer` + +This tutorial shows different commands for plotting a deformed mesh without data. + +A mesh is represented in DPF by a |MeshedRegion| object. +You can store multiple |MeshedRegion| in a DPF collection called |MeshesContainer|. + +You can obtain a |MeshedRegion| by creating your own from scratch or by getting it from a result file. +For more information, see the :ref:`ref_tutorials_create_a_mesh_from_scratch` and +:ref:`ref_tutorials_get_mesh_from_result_file` tutorials. + +PyDPF-Core has a variety of plotting methods for generating 3D plots with Python. +These methods use VTK and leverage the `PyVista `_ library. + +:jupyter-download-script:`Download tutorial as Python script` +:jupyter-download-notebook:`Download tutorial as Jupyter notebook` + +Load data to plot +----------------- + +For this tutorial, we use mesh information from a case available in the |Examples| module. +For more information see the :ref:`ref_tutorials_get_mesh_from_result_file` tutorial. + +.. jupyter-execute:: + + # Import the ``ansys.dpf.core`` module + import ansys.dpf.core as dpf + # Import the examples module + from ansys.dpf.core import examples + # Import the operators module + from ansys.dpf.core import operators as ops + + # Download and get the path to an example result file + result_file_path_1 = examples.download_piston_rod() + + # Create a model from the result file + model_1 = dpf.Model(data_sources=result_file_path_1) + +Get the deformation field +------------------------- + +To deform the mesh, we need a nodal 3D vector field specifying the translation of each node in the mesh. + +The following DPF objects are able to return or represent such a field +and are accepted inputs for the deformation parameter of plot methods: + +- A |Field| +- A |FieldsContainer| +- A |Result| +- An |Operator| + +Here, we use the |disp_op| which outputs a nodal 3D vector field of distances. + +One can get the operator from the |Model| with the source of data already connected. +For more information about extracting results from a result file, +see the :ref:`ref_tutorials_import_data` tutorials section. + +.. jupyter-execute:: + + # Get the displacement operator for this model + disp_op = model_1.results.displacement() + +You can apply a scale factor to the deformation for every method in this tutorial +by passing in the ``scale_factor`` argument. + +.. jupyter-execute:: + + # Define the scale factor + scl_fct = 2.0 + +.. _ref_plot_deformed_mesh_with_model: + +Plot a deformed model +--------------------- + +You can directly plot the overall mesh loaded by the model with |Model.plot| [1]_. +To plot it with deformation, use the *'deform_by'* argument and provide the displacement operator. + +.. jupyter-execute:: + + # Plot the deformed mesh + model_1.plot(deform_by=disp_op, scale_factor=scl_fct) + +You can apply a scale factor to the deformation for every method in this tutorial. + +.. jupyter-execute:: + + # Define the scale factor + scl_fct = 2.0 + +.. _ref_plot_deformed_mesh_with_meshed_region: + +Plot a single mesh +------------------ + +Get the mesh +^^^^^^^^^^^^ + +Here we simply get the |MeshedRegion| object of the model, but any other |MeshedRegion| works. + +.. jupyter-execute:: + + # Extract the mesh + meshed_region_1 = model_1.metadata.meshed_region + +Plot the mesh +^^^^^^^^^^^^^ + +To plot the deformed |MeshedRegion| you can use: + +- The |MeshedRegion.plot| method; +- The |DpfPlotter| object. + +.. tab-set:: + + .. tab-item:: MeshedRegion.plot() method + + Use the |MeshedRegion.plot| method [1]_ of the |MeshedRegion| object we defined. + Add deformation by providing our displacement operator to the *'deform_by'* argument. + + .. jupyter-execute:: + + # Plot the deformed mesh + meshed_region_1.plot(deform_by=disp_op, scale_factor=scl_fct) + + .. tab-item:: DpfPlotter object + + To plot the mesh with this approach, first create an instance of |DpfPlotter| [2]_. + Then, add the |MeshedRegion| to the scene using the |add_mesh| method. + Add deformation by providing our displacement operator to the *'deform_by'* argument. + + To render and show the figure based on the current state of the plotter object, use the |show_figure| method. + + .. jupyter-execute:: + + # Create a DpfPlotter instance + plotter_1 = dpf.plotter.DpfPlotter() + + # Add the mesh to the scene with deformation + plotter_1.add_mesh(meshed_region=meshed_region_1, + deform_by=disp_op, + scale_factor=scl_fct) + + # Display the scene + plotter_1.show_figure() + +You can also plot data contours on a deformed mesh. For more information, see :ref:`ref_tutorials_plot_contour` + +.. _ref_plot_deformed_mesh_with_meshes_container: + +Plot several meshes +------------------- + +Build a collection of meshes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are different ways to obtain a |MeshesContainer|. +You can for example split a |MeshedRegion| using operators. + +Here, we use the |split_mesh| operator to split the mesh based on the material of each element. +This operator returns a |MeshesContainer| with meshes labeled according to the criterion for the split. +In our case, each mesh has a *'mat'* label. +For more information about how to get a split mesh, see the :ref:`ref_tutorials_split_mesh` +and :ref:`ref_tutorials_extract_mesh_in_split_parts` tutorials. + +.. jupyter-execute:: + + # Split the mesh based on material property + meshes = ops.mesh.split_mesh(mesh=meshed_region_1, property="mat").eval() + + # Show the result + print(meshes) + +Plot the meshes +^^^^^^^^^^^^^^^ + +Use the |MeshesContainer.plot| method [1]_ of the |MeshesContainer| object we defined. +Provide the displacement operator to the *'deform_by'* argument to add mesh deformation. + +This method plots all the |MeshedRegion| objects stored in the |MeshesContainer| +and colors them based on the property used to split the mesh. + +.. jupyter-execute:: + + # Plot the deformed mesh + meshes.plot(deform_by=disp_op, scale_factor=scl_fct) + +You can also plot data on a collection of deformed meshes. +For more information, see :ref:`_ref_tutorials_plot_contour` + +.. rubric:: Footnotes + +.. [1] The |DpfPlotter| displays the mesh with edges, lighting and axis widget enabled by default. + You can pass additional PyVista arguments to all plotting methods to change the default behavior + (see options for `pyvista.plot() `_), such as: + + .. jupyter-execute:: + + model_1.plot(title="Mesh", + text="this is a mesh", # Adds the given text at the bottom of the plot + off_screen=True, + screenshot="mesh_plot_1.png", # Save a screenshot to file with the given name + window_size=[450,350]) + # Notes: + # - To save a screenshot to file, use "screenshot=figure_name.png" ( as well as "notebook=False" if on a Jupyter notebook). + # - The "off_screen" keyword only works when "notebook=False". If "off_screen=True" the plot is not displayed when running the code. + +.. [2] The |DpfPlotter| is currently based on PyVista. + That means that PyVista must be installed. + The DPF plotter also passes additional parameters to the PyVista plotter + (arguments supported by the version of PyVista installed). + More information about available additional arguments is available at `pyvista.plot() `_. diff --git a/doc/source/user_guide/tutorials/plot/plot_mesh.rst b/doc/source/user_guide/tutorials/plot/plot_mesh.rst new file mode 100644 index 00000000000..e00e33881c9 --- /dev/null +++ b/doc/source/user_guide/tutorials/plot/plot_mesh.rst @@ -0,0 +1,178 @@ +.. _ref_tutorials_plot_mesh: + +=========== +Plot a mesh +=========== + +.. |Model.plot| replace:: :py:meth:`Model.plot() ` +.. |MeshedRegion.plot| replace:: :py:meth:`MeshedRegion.plot() ` +.. |MeshesContainer.plot| replace:: :py:meth:`MeshesContainer.plot() ` +.. |add_mesh| replace:: :py:meth:`add_mesh() ` +.. |show_figure| replace:: :py:meth:`show_figure() ` +.. |split_mesh| replace:: :py:class:`split_mesh ` + +.. |Model| replace:: :py:class:`Model ` +.. |Examples| replace:: :py:mod:`Examples` +.. |MeshedRegion| replace:: :py:class:`MeshedRegion ` +.. |MeshesContainer| replace:: :py:class:`MeshesContainer ` +.. |DpfPlotter| replace:: :py:class:`DpfPlotter` + +This tutorial shows different commands for plotting a mesh without data. + +A mesh is represented in DPF by a |MeshedRegion| object. +You can store multiple |MeshedRegion| in a DPF collection called |MeshesContainer|. + +You can obtain a |MeshedRegion| by creating your own from scratch or by getting it from a result file. +For more information, see the :ref:`ref_tutorials_create_a_mesh_from_scratch` and +:ref:`ref_tutorials_get_mesh_from_result_file` tutorials. + +PyDPF-Core has a variety of plotting methods for generating 3D plots with Python. +These methods use VTK and leverage the `PyVista `_ library. + +:jupyter-download-script:`Download tutorial as Python script` +:jupyter-download-notebook:`Download tutorial as Jupyter notebook` + +Load data to plot +----------------- + +For this tutorial, we use mesh information from a case available in the |Examples| module. +For more information see the :ref:`ref_tutorials_get_mesh_from_result_file` tutorial. + +.. jupyter-execute:: + + # Import the ``ansys.dpf.core`` module + import ansys.dpf.core as dpf + # Import the examples module + from ansys.dpf.core import examples + # Import the operators module + from ansys.dpf.core import operators as ops + + # Download and get the path to an example result file + result_file_path_1 = examples.download_piston_rod() + + # Create a model from the result file + model_1 = dpf.Model(data_sources=result_file_path_1) + +Plot a model +------------ + +You can directly plot the overall mesh loaded by the model with |Model.plot| [1]_. + +.. jupyter-execute:: + + # Plot the mesh + model_1.plot() + +Plot a single mesh +------------------ + +Get the mesh +^^^^^^^^^^^^ + +Here we simply get the |MeshedRegion| object of the model, but any other |MeshedRegion| works. + +.. jupyter-execute:: + + # Extract the mesh + meshed_region_1 = model_1.metadata.meshed_region + +Plot the mesh +^^^^^^^^^^^^^ + +To plot the |MeshedRegion| you can use: + +- The |MeshedRegion.plot| method; +- The |DpfPlotter| object. + +.. tab-set:: + + .. tab-item:: MeshedRegion.plot() method + + Use the |MeshedRegion.plot| method [1]_ of the |MeshedRegion| object we defined. + + .. jupyter-execute:: + + # Plot the mesh object + meshed_region_1.plot() + + .. tab-item:: DpfPlotter object + + To plot the mesh with this approach, first create an instance of |DpfPlotter| [2]_. + Then, add the |MeshedRegion| to the scene using the |add_mesh| method. + + To render and show the figure based on the current state of the plotter object, use the |show_figure| method. + + .. jupyter-execute:: + + # Create a DpfPlotter instance + plotter_1 = dpf.plotter.DpfPlotter() + + # Add the mesh to the scene + plotter_1.add_mesh(meshed_region=meshed_region_1) + + # Display the scene + plotter_1.show_figure() + +You can also plot data contours on a mesh. For more information, see :ref:`ref_tutorials_plot_contour` + +Plot several meshes +------------------- + +Build a collection of meshes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are different ways to obtain a |MeshesContainer|. +You can for example split a |MeshedRegion| using operators. + +Here, we use the |split_mesh| operator to split the mesh based on the material of each element. +This operator returns a |MeshesContainer| with meshes labeled according to the criterion for the split. +In our case, each mesh has a *'mat'* label. +For more information about how to get a split mesh, see the :ref:`ref_tutorials_split_mesh` +and :ref:`ref_tutorials_extract_mesh_in_split_parts` tutorials. + +.. jupyter-execute:: + + # Split the mesh based on material property + meshes = ops.mesh.split_mesh(mesh=meshed_region_1, property="mat").eval() + + # Show the result + print(meshes) + +Plot the meshes +^^^^^^^^^^^^^^^ + +Use the |MeshesContainer.plot| method [1]_ of the |MeshesContainer| object we defined. + +This method plots all the |MeshedRegion| objects stored in the |MeshesContainer| +and colors them based on the property used to split the mesh. + +.. jupyter-execute:: + + # Plot the collection of meshes + meshes.plot() + +You can also plot data on a collection of meshes. +For more information, see :ref:`ref_tutorials_plot_contour` + +.. rubric:: Footnotes + +.. [1] The |DpfPlotter| displays the mesh with edges, lighting and axis widget enabled by default. + You can pass additional PyVista arguments to all plotting methods to change the default behavior + (see options for `pyvista.plot() `_), such as: + + .. jupyter-execute:: + + model_1.plot(title="Mesh", + text="this is a mesh", # Adds the given text at the bottom of the plot + off_screen=True, + screenshot="mesh_plot_1.png", # Save a screenshot to file with the given name + window_size=[450,350]) + # Notes: + # - To save a screenshot to file, use "screenshot=figure_name.png" ( as well as "notebook=False" if on a Jupyter notebook). + # - The "off_screen" keyword only works when "notebook=False". If "off_screen=True" the plot is not displayed when running the code. + +.. [2] The |DpfPlotter| is currently based on PyVista. + That means that PyVista must be installed. + The DPF plotter also passes additional parameters to the PyVista plotter + (arguments supported by the version of PyVista installed). + More information about available additional arguments is available at `pyvista.plot() `_. diff --git a/doc/source/user_guide/tutorials/transform_data/index.rst b/doc/source/user_guide/tutorials/transform_data/index.rst deleted file mode 100644 index 1af0c017fc6..00000000000 --- a/doc/source/user_guide/tutorials/transform_data/index.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. _ref_tutorials_transform_data: - -============== -Transform data -============== - -An operator is the main object that is used to create, transform, and stream data in DPF. - -They can perform different modifications with the data: direct mathematical operations, -averaging in the mesh, changes in the model locations .... They can also be chained together -to create more complex operations and customizable results. - -The tutorials in this section aims to explain how to transform and operate on data to obtain -the desired input by using the DPF operators with PyDPF-Core. - -For more information on how to program with PyDPF-Core check the -:ref:`ref_tutorials_language_and_usage` tutorial. - - -.. grid:: 1 1 3 3 - :gutter: 2 - :padding: 2 - :margin: 2 - - .. grid-item-card:: Using operators - :link: ref_tutorials - :link-type: ref - :text-align: center - - This tutorial - - .. grid-item-card:: Create a workflow - :link: ref_tutorials - :link-type: ref - :text-align: center - - This tutorial - - .. grid-item-card:: Mathematical operations with PyDPF-Core data storage structures - :link: ref_tutorials - :link-type: ref - :text-align: center - - This tutorial - -.. toctree:: - :maxdepth: 2 - :hidden: -