diff --git a/doc/source/user_guide/tutorials/plot/index.rst b/doc/source/user_guide/tutorials/plot/index.rst index 3a41232392..ff8e7afe63 100644 --- a/doc/source/user_guide/tutorials/plot/index.rst +++ b/doc/source/user_guide/tutorials/plot/index.rst @@ -33,13 +33,20 @@ These tutorials demonstrate some different approaches to visualise the data in p This tutorial explains how to plot data on its supporting deformed mesh. - .. grid-item-card:: Plotting data on specific placements - :link: ref_plotting_data_on_specific_placements + .. grid-item-card:: Plotting data on a path + :link: ref_plotting_data_on_specific_path :link-type: ref :text-align: center - This tutorial shows how to plot data on specific placements of a - mesh (a specific path, a geometry elements ...) + This tutorial shows how to plot data on a specific path of a + mesh + + .. grid-item-card:: Plotting data on geometry elements + :link: ref_plotting_data_on_geometry_elements + :link-type: ref + :text-align: center + + This tutorial shows how to plot data on geometry elements .. grid-item-card:: Plotting a graph :link: ref_plotting_a_graph @@ -55,5 +62,6 @@ These tutorials demonstrate some different approaches to visualise the data in p plotting_meshes.rst plotting_data_on_the_mesh.rst plotting_data_on_deformed_mesh.rst - plotting_data_on_specific_placements.rst + plotting_data_on_specific_path.rst + plotting_data_on_geometry_elements.rst plotting_a_graph.rst \ No newline at end of file diff --git a/doc/source/user_guide/tutorials/plot/plotting_data_on_specific_placements.rst b/doc/source/user_guide/tutorials/plot/plotting_data_on_geometry_elements.rst similarity index 71% rename from doc/source/user_guide/tutorials/plot/plotting_data_on_specific_placements.rst rename to doc/source/user_guide/tutorials/plot/plotting_data_on_geometry_elements.rst index 56d14904ed..1b62919785 100644 --- a/doc/source/user_guide/tutorials/plot/plotting_data_on_specific_placements.rst +++ b/doc/source/user_guide/tutorials/plot/plotting_data_on_geometry_elements.rst @@ -1,8 +1,8 @@ -.. _ref_plotting_data_on_specific_placements: +.. _ref_plotting_data_on_geometry_elements: -==================================== -Plotting data on specific placements -==================================== +============================== +Plot data on geometry elements +============================== .. |DpfPlotter| replace:: :class:`DpfPlotter` .. |add_mesh| replace:: :func:`add_mesh()` @@ -15,198 +15,14 @@ Plotting data on specific placements .. |Plane| replace:: :class:`Plane ` .. |mapping| replace:: :class:`mapping ` -This tutorial shows how to plot data on specific placements of a mesh: - -- :ref:`plot_specific_path` -- :ref:`plot_geometry_elements` - -.. _plot_specific_path: - -Plot data on a specific path ----------------------------- - -This part shows how to get a result mapped over a specific path and how to plot it. - -Define the data -^^^^^^^^^^^^^^^ - -We will download a simple simulation result file available in our `Examples` package: - -.. code-block:: python - - # Import the ``ansys.dpf.core`` module, including examples files and the operators subpackage - from ansys.dpf import core as dpf - from ansys.dpf.core import examples - from ansys.dpf.core import operators as ops - # Define the result file - result_file = examples.find_static_rst() - -The results will be mapped over a defined path of coordinates. So, start by creating -a |Model| with the result file and extract the |MeshedRegion| from it: - -.. code-block:: python - - # Create the model - my_model = dpf.Model(data_sources=result_file) - my_meshed_region = my_model.metadata.meshed_region - -Define the path -^^^^^^^^^^^^^^^ - -The path coordinates have to be in the space domain of the mesh. You can verify the -range of coordinates values by checking the nodes coordinates. - -Get the nodes coordinates with the mesh operator -:class:`nodes_coordinates`: - -.. code-block:: python - - # Get the mesh nodes coordinates - nodes_coords = ops.mesh.node_coordinates(mesh=my_meshed_region).eval() - -Get the maximum values of the coordinates, so you know the space domain limits. - -.. code-block:: python - - # Get the maximum and minimum values of the mesh nodes coordinates - max_coords = ops.min_max.min_max(field=nodes_coords).eval(pin=1) - min_coords = ops.min_max.min_max(field=nodes_coords).eval(pin=0) - # Print the space domain limits - print("Max coordinates:", max_coords.data, '\n') - print("Min coordinates:",min_coords.data) - -.. rst-class:: sphx-glr-script-out - - .. jupyter-execute:: - :hide-code: - - from ansys.dpf import core as dpf - from ansys.dpf.core import examples - from ansys.dpf.core import operators as ops - result_file = examples.find_static_rst() - my_model = dpf.Model(data_sources=result_file) - my_meshed_region = my_model.metadata.meshed_region - nodes_coords = ops.mesh.node_coordinates(mesh=my_meshed_region).eval() - max_coords = ops.min_max.min_max(field=nodes_coords).eval(pin=1) - min_coords = ops.min_max.min_max(field=nodes_coords).eval(pin=0) - print("Max coordinates:", max_coords.data, '\n') - print("Min coordinates:",min_coords.data) - - -Create the path based on a set of coordinates. Here we choose the paths origin coordinates, -number of points in the path and the distance between each coordinate. - -.. code-block:: python - - # Initial coordinates - initial_coords = [0.024, 0.03, 0.003] - # Number of points in the path - n_points = 51 - # Distance between each coordinate - delta = 0.001 - - # Create the paths coordinates field - path_coords = dpf.fields_factory.create_3d_vector_field(n_points) - path_coords.scoping.ids = list(range(0, n_points)) - -Make a loop to define the paths coordinates field. Here we make a path that only moves along the y-axis. - -.. code-block:: python - - # For each iteration we add a new set of coordinates based on the predefined distance between each coordinate - for i in range(0, n_points): - initial_coords[1] += delta - path_coords.append(data=initial_coords, scopingid=0) - -Extract the result -^^^^^^^^^^^^^^^^^^ - -Extract the result from the model. Here we get the equivalent stress result - -.. code-block:: python - - # Get the stress result - my_stress = my_model.results.stress().eqv().eval() - -Map the result to the path -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Compute the mapped data using the |mapping| operator. The stress results are defined in a ``ElementalNodal`` location. -So, each entity has a coordinate in the mesh and a correspondent stress data. - -The |mapping| operator retrieves the results of the entities located in the given coordinates. -If the given coordinates don't match with any entity coordinate, the operator interpolates the results inside -elements with shape functions. - -.. code-block:: python - - # Map the path coordinates with the stress results - mapped_stress = ops.mapping.on_coordinates(fields_container=my_stress, - coordinates=path_coords, - create_support=True, - mesh=my_meshed_region - ).eval() - -Plot the result on the path -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Create the plotter and add fields and meshes. For more information about -plotting data on a mesh check the tutorial: :ref:`ref_plotting_data_on_the_mesh` - -First, define the |DpfPlotter| object [2]_, then add |MeshedRegion| -to it using the |add_mesh| method and add the field using the |add_field| method. - -To display the figure built by the plotter object use the |show_figure| method. - -.. code-block:: python - - # Declare the DpfPlotter object - my_plotter = dpf.plotter.DpfPlotter() - # Add the MeshedRegion to the DpfPlotter object - # We use custom style for the mesh so we can visualise the path (that is inside the mesh) - my_plotter.add_mesh(meshed_region=my_meshed_region,style="surface", show_edges=True, color="w", opacity=0.3) - # Add the Field to the DpfPlotter object - my_plotter.add_field(field=mapped_stress[0]) - # Display the plot - my_plotter.show_figure() - -.. rst-class:: sphx-glr-script-out - - .. jupyter-execute:: - :hide-code: - - initial_coords = [0.024, 0.03, 0.003] - n_points = 51 - delta = 0.001 - path_coords = dpf.fields_factory.create_3d_vector_field(n_points) - path_coords.scoping.ids = list(range(0, n_points)) - for i in range(0, n_points): - initial_coords[1] += delta - path_coords.append(data=initial_coords, scopingid=0) - my_stress = my_model.results.stress().eqv().eval() - mapped_stress = ops.mapping.on_coordinates(fields_container=my_stress, - coordinates=path_coords, - create_support=True, - mesh=my_meshed_region - ).eval() - my_plotter = dpf.plotter.DpfPlotter() - my_plotter.add_mesh(meshed_region=my_meshed_region,style="surface", show_edges=True, color="w", opacity=0.3) - my_plotter.add_field(field=mapped_stress[0]) - my_plotter.show_figure() - -.. _plot_geometry_elements: - -Plot data on geometry elements ------------------------------- - -This part shows how to get a result mapped over different geometric objects: +This tutorials shows how to get a result mapped over different geometric objects: - Points_ - Line_ - Plane_ Define the data -^^^^^^^^^^^^^^^ +--------------- We will download a simple simulation result file available in our `Examples` package: @@ -250,12 +66,14 @@ plot method [1]_: ] Points -^^^^^^ +------ Create points -~~~~~~~~~~~~~ +^^^^^^^^^^^^^ -Create |Points| by defining their coordinates. Once again they have to be in the space domain of the mesh. +Create |Points| by defining their coordinates. They have to be in the space +domain of the mesh. You can verify the range of coordinates values by checking +the nodes coordinates. Get the nodes coordinates with the mesh operator :class:`nodes_coordinates`: @@ -332,7 +150,7 @@ You can do it by hand or by calculating this combinations : ) Check the points on the mesh with a plot -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can plot the |Points| together with the mesh: @@ -364,7 +182,7 @@ You can plot the |Points| together with the mesh: my_points.plot(mesh=my_meshed_region, cpos=camera_position) Map displacement field to the points -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Compute the mapped data using the |mapping| operator. The displacement results are defined in a ``Nodal`` location. So, each node has a coordinate in the mesh and a correspondent displacement data. @@ -383,7 +201,7 @@ elements with shape functions. ).eval()[0] Plot displacement field on the points -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Create the plotter and add fields and meshes. For more information about plotting data on a mesh check the tutorial: :ref:`ref_plotting_data_on_the_mesh` @@ -421,10 +239,10 @@ To display the figure built by the plotter object use the |show_figure| method. my_plotter.show_figure(show_axes=True, cpos=camera_position) Line -^^^^ +---- Create the line -~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^ Create a |Line| passing through the mesh diagonal. To create a |Line| you need pass as arguments: the coordinates of the starting and ending points @@ -440,7 +258,7 @@ Check the `Create points`_ section to understand how we defined the points coord ) Check the line on the mesh with a plot -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can plot the |Line| together with the mesh: @@ -460,7 +278,7 @@ You can plot the |Line| together with the mesh: my_line.plot(mesh=my_meshed_region, cpos=camera_position) Map displacement field to the line -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Compute the mapped data using the |mapping| operator. The displacement results are defined in a ``Nodal`` location. So, each node has a coordinate in the mesh and a correspondent displacement data. @@ -479,7 +297,7 @@ elements with shape functions. ).eval()[0] Plot displacement field on the line -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Plot displacement field on the |Line| and display mesh in background. Create the plotter and add fields and meshes. For more information about @@ -518,10 +336,10 @@ To display the figure built by the plotter object use the |show_figure| method. my_plotter.show_figure(show_axes=True, cpos=camera_position) Plane -^^^^^ +----- Create the plane -~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^ Create a vertical |Plane| passing through the mesh mid point. To create a |Plane| you need pass as arguments: the coordinates of the center point, the vector of the normal direction to the plane, @@ -542,7 +360,7 @@ Check the `Create points`_ section to understand how we defined the mesh space c ) Check the plane on the mesh with a plot -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can plot the |Plane| together with the mesh: @@ -566,7 +384,7 @@ You can plot the |Plane| together with the mesh: my_plane.plot(mesh=my_meshed_region, cpos=camera_position) Map displacement field to the plane -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Compute the mapped data using the |mapping| operator. The displacement results are defined in a ``Nodal`` location. So, each node has a coordinate in the mesh and a correspondent displacement data. @@ -585,7 +403,7 @@ elements with shape functions. ).eval()[0] Plot displacement field on the plane -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Plot displacement field on the |Plane| and display mesh in background. Create the plotter and add fields and meshes. For more information about @@ -636,4 +454,4 @@ parameter (the argument must be supported by the installed PyVista version). The default |DpfPlotter| object settings display the mesh with edges and lighting enabled. Nevertheless, as we use the `PyVista `_ library to create the plot you can use additional PyVista arguments for the |DpfPlotter| -object and |add_field| method (available at: :func:`pyvista.plot`). \ No newline at end of file +object and |add_field| method (available at: :func:`pyvista.plot`). diff --git a/doc/source/user_guide/tutorials/plot/plotting_data_on_specific_path.rst b/doc/source/user_guide/tutorials/plot/plotting_data_on_specific_path.rst new file mode 100644 index 0000000000..99e5da6924 --- /dev/null +++ b/doc/source/user_guide/tutorials/plot/plotting_data_on_specific_path.rst @@ -0,0 +1,200 @@ +.. _ref_plotting_data_on_specific_path: + +================================ +Plotting data on a specific path +================================ + +.. |DpfPlotter| replace:: :class:`DpfPlotter` +.. |add_mesh| replace:: :func:`add_mesh()` +.. |add_field| replace:: :func:`add_field()` +.. |show_figure| replace:: :func:`show_figure()` +.. |MeshedRegion| replace:: :class:`MeshedRegion ` +.. |Model| replace:: :class:`Model ` +.. |Line| replace:: :class:`Line ` +.. |Points| replace:: :class:`Points ` +.. |Plane| replace:: :class:`Plane ` +.. |mapping| replace:: :class:`mapping ` + +This tutorial shows how to get a result mapped over a specific path and how to plot it. + +Define the data +--------------- + +We will download a simple simulation result file available in our `Examples` package: + +.. code-block:: python + + # Import the ``ansys.dpf.core`` module, including examples files and the operators subpackage + from ansys.dpf import core as dpf + from ansys.dpf.core import examples + from ansys.dpf.core import operators as ops + # Define the result file + result_file = examples.find_static_rst() + +The results will be mapped over a defined path of coordinates. So, start by creating +a |Model| with the result file and extract the |MeshedRegion| from it: + +.. code-block:: python + + # Create the model + my_model = dpf.Model(data_sources=result_file) + my_meshed_region = my_model.metadata.meshed_region + +Define the path +--------------- + +The path coordinates have to be in the space domain of the mesh. You can verify the +range of coordinates values by checking the nodes coordinates. + +Get the nodes coordinates with the mesh operator +:class:`nodes_coordinates`: + +.. code-block:: python + + # Get the mesh nodes coordinates + nodes_coords = ops.mesh.node_coordinates(mesh=my_meshed_region).eval() + +Get the maximum values of the coordinates, so you know the space domain limits. + +.. code-block:: python + + # Get the maximum and minimum values of the mesh nodes coordinates + max_coords = ops.min_max.min_max(field=nodes_coords).eval(pin=1) + min_coords = ops.min_max.min_max(field=nodes_coords).eval(pin=0) + # Print the space domain limits + print("Max coordinates:", max_coords.data, '\n') + print("Min coordinates:",min_coords.data) + +.. rst-class:: sphx-glr-script-out + + .. jupyter-execute:: + :hide-code: + + from ansys.dpf import core as dpf + from ansys.dpf.core import examples + from ansys.dpf.core import operators as ops + result_file = examples.find_static_rst() + my_model = dpf.Model(data_sources=result_file) + my_meshed_region = my_model.metadata.meshed_region + nodes_coords = ops.mesh.node_coordinates(mesh=my_meshed_region).eval() + max_coords = ops.min_max.min_max(field=nodes_coords).eval(pin=1) + min_coords = ops.min_max.min_max(field=nodes_coords).eval(pin=0) + print("Max coordinates:", max_coords.data, '\n') + print("Min coordinates:",min_coords.data) + + +Create the path based on a set of coordinates. Here we choose the paths origin coordinates, +number of points in the path and the distance between each coordinate. + +.. code-block:: python + + # Initial coordinates + initial_coords = [0.024, 0.03, 0.003] + # Number of points in the path + n_points = 51 + # Distance between each coordinate + delta = 0.001 + + # Create the paths coordinates field + path_coords = dpf.fields_factory.create_3d_vector_field(n_points) + path_coords.scoping.ids = list(range(0, n_points)) + +Make a loop to define the paths coordinates field. Here we make a path that only moves along the y-axis. + +.. code-block:: python + + # For each iteration we add a new set of coordinates based on the predefined distance between each coordinate + for i in range(0, n_points): + initial_coords[1] += delta + path_coords.append(data=initial_coords, scopingid=0) + +Extract the result +------------------ + +Extract the result from the model. Here we get the equivalent stress result + +.. code-block:: python + + # Get the stress result + my_stress = my_model.results.stress().eqv().eval() + +Map the result to the path +-------------------------- + +Compute the mapped data using the |mapping| operator. The stress results are defined in a ``ElementalNodal`` location. +So, each entity has a coordinate in the mesh and a correspondent stress data. + +The |mapping| operator retrieves the results of the entities located in the given coordinates. +If the given coordinates don't match with any entity coordinate, the operator interpolates the results inside +elements with shape functions. + +.. code-block:: python + + # Map the path coordinates with the stress results + mapped_stress = ops.mapping.on_coordinates(fields_container=my_stress, + coordinates=path_coords, + create_support=True, + mesh=my_meshed_region + ).eval() + +Plot the result on the path +--------------------------- + +Create the plotter and add fields and meshes. For more information about +plotting data on a mesh check the tutorial: :ref:`ref_plotting_data_on_the_mesh` + +First, define the |DpfPlotter| object [2]_, then add |MeshedRegion| +to it using the |add_mesh| method and add the field using the |add_field| method. + +To display the figure built by the plotter object use the |show_figure| method. + +.. code-block:: python + + # Declare the DpfPlotter object + my_plotter = dpf.plotter.DpfPlotter() + # Add the MeshedRegion to the DpfPlotter object + # We use custom style for the mesh so we can visualise the path (that is inside the mesh) + my_plotter.add_mesh(meshed_region=my_meshed_region,style="surface", show_edges=True, color="w", opacity=0.3) + # Add the Field to the DpfPlotter object + my_plotter.add_field(field=mapped_stress[0]) + # Display the plot + my_plotter.show_figure() + +.. rst-class:: sphx-glr-script-out + + .. jupyter-execute:: + :hide-code: + + initial_coords = [0.024, 0.03, 0.003] + n_points = 51 + delta = 0.001 + path_coords = dpf.fields_factory.create_3d_vector_field(n_points) + path_coords.scoping.ids = list(range(0, n_points)) + for i in range(0, n_points): + initial_coords[1] += delta + path_coords.append(data=initial_coords, scopingid=0) + my_stress = my_model.results.stress().eqv().eval() + mapped_stress = ops.mapping.on_coordinates(fields_container=my_stress, + coordinates=path_coords, + create_support=True, + mesh=my_meshed_region + ).eval() + my_plotter = dpf.plotter.DpfPlotter() + my_plotter.add_mesh(meshed_region=my_meshed_region,style="surface", show_edges=True, color="w", opacity=0.3) + my_plotter.add_field(field=mapped_stress[0]) + my_plotter.show_figure() + +.. rubric:: Footnotes + +.. [1] The default plotter settings display the mesh with edges, lighting and axis widget enabled. +Nevertheless, as we use the `PyVista `_ library to create +the plot you can use additional PyVista arguments (available at: :func:`pyvista.plot`). + +.. [2] Here we use the |DpfPlotter| object, that is currently a PyVista based object. +That means that PyVista must be installed, and that it supports kwargs as +parameter (the argument must be supported by the installed PyVista version). + +The default |DpfPlotter| object settings display the mesh with edges and lighting +enabled. Nevertheless, as we use the `PyVista `_ +library to create the plot you can use additional PyVista arguments for the |DpfPlotter| +object and |add_field| method (available at: :func:`pyvista.plot`). \ No newline at end of file