Skip to content

Commit

Permalink
Add animation tutorials to the main tutorials branch (#1915)
Browse files Browse the repository at this point in the history
* add animate_data.rst tutorial

* completes the animate_data.rst tutorial

* Apply suggestions from code review

Co-authored-by: Paul Profizi <[email protected]>

* codes for just one case and erase badges

* change tutorial name

* last updates

* Update animate_time.rst

* Fixing moving camera

* Apply suggestions from code review

Co-authored-by: Luisa Felix Salles <[email protected]>

* Update doc/source/user_guide/tutorials/animate/animate_time.rst

* Apply suggestions from code review

Co-authored-by: JennaPaikowsky <[email protected]>

* Apply suggestions from code review

Co-authored-by: Luisa Felix Salles <[email protected]>

* Apply suggestions from code review

* Update doc/source/user_guide/tutorials/animate/animate_time.rst

* updates

---------

Co-authored-by: Paul Profizi <[email protected]>
Co-authored-by: PProfizi <[email protected]>
Co-authored-by: JennaPaikowsky <[email protected]>
  • Loading branch information
4 people authored Dec 3, 2024
1 parent 991b95a commit e9cfde6
Show file tree
Hide file tree
Showing 2 changed files with 366 additions and 4 deletions.
361 changes: 361 additions & 0 deletions doc/source/user_guide/tutorials/animate/animate_time.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,361 @@
.. _ref_tutorials_animate_time:

======================
Animate data over time
======================

.. |Examples| replace:: :mod:`ansys.dpf.core.examples`
.. |Animator| replace:: :class:`Animator<ansys.dpf.core.animator.Animator>`
.. |Field| replace:: :class:`Field<ansys.dpf.core.field.Field>`
.. |FieldsContainer| replace:: :class:`FieldsContainer<ansys.dpf.core.fields_container.FieldsContainer>`
.. |MeshedRegion| replace:: :class:`MeshedRegion <ansys.dpf.core.meshed_region.MeshedRegion>`
.. |TimeFreqSupport| replace:: :class:`TimeFreqSupport <ansys.dpf.core.time_freq_support.TimeFreqSupport>`
.. |animate| replace:: :func:`FieldsContainer.animate() <ansys.dpf.core.fields_container.FieldsContainer.animate>`
.. |Result| replace:: :class:`Result <ansys.dpf.core.results.Result>`
.. |Operator| replace:: :class:`Operator<ansys.dpf.core.dpf_operator.Operator>`
.. |Workflow| replace:: :class:`Workflow<ansys.dpf.core.workflow.Workflow>`
.. |Elemental| replace:: :class:`elemental<ansys.dpf.core.common.locations>`
.. |ElementalNodal| replace:: :class:`elemental_nodal<ansys.dpf.core.common.locations>`
.. |Nodal| replace:: :class:`nodal<ansys.dpf.core.common.locations>`
.. |Faces| replace:: :class:`faces<ansys.dpf.core.common.locations>`
.. |Overall| replace:: :class:`overall<ansys.dpf.core.common.locations>`
.. |open_movie| replace:: :class:`pyvista.Plotter.open_movie`

This tutorial demonstrates how to create 3D animations of data in time.

:jupyter-download-script:`Download tutorial as Python script<animate_time>` :jupyter-download-notebook:`Download tutorial as Jupyter notebook<animate_time>`

To animate data across time, you must store the data in a |FieldsContainer| with a ``time`` label.


Get the result files
--------------------

First, import a results file. For this tutorial, you can use the one available in the |Examples| module.
For more information about how to import your own result file in DPF, see
the :ref:`ref_tutorials_import_data` tutorial section.

.. jupyter-execute::

# Import the ``ansys.dpf.core`` module
from ansys.dpf import 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 = examples.find_msup_transient()
# Create the model
model = dpf.Model(data_sources=result_file_path)

Define a time scoping
---------------------

To animate across time, you must define the time steps you are interested in.
This tutorial retrieves all the time steps available in |TimeFreqSupport|, but you can also filter them.
For more information on how to define a scoping, see the ``Narrow down data`` tutorial in the
:ref:`ref_tutorials_import_data` tutorials section.

.. jupyter-execute::

# Get a scoping of all time steps available
time_steps = model.metadata.time_freq_support.time_frequencies

Extract the results
-------------------

Extract the results to animate. In this tutorial, you extract the displacement and stress results.

.. note::

Only the |Elemental|, |Nodal|, or |Faces| locations are supported for animations.
|Overall| and |ElementalNodal| locations are not currently supported.


.. jupyter-execute::

# Get the displacement fields (already on nodes) at all time steps
disp_fc = model.results.displacement(time_scoping=time_steps).eval()
print(disp_fc)

.. jupyter-execute::

# Get the stress fields on nodes at all time steps
# Request the stress on |Nodal| location as the default |ElementalNodal| location is not supported.
stress_fc = model.results.stress.on_location(location=dpf.locations.nodal).on_time_scoping(time_scoping=time_steps).eval()
print(stress_fc)

Animate the results
-------------------

Animate the results with the |animate| method.
You can animate them on a deformed mesh (animate the color map and the mesh)
or on a static mesh (animate the color map only).

The default behavior of the |animate| method is to:

- Display the norm of the data components;
- Display data at the top layer for shells;
- Display the deformed mesh when animating displacements;
- Display the static mesh for other types of results;
- Use a constant and uniform scale factor of 1.0 when deforming the mesh.

You can animate any result on a deformed geometry by providing displacement results in the `deform_by` parameter.

The geometry can be deformed by a |Result| object, an |Operator| (It must evaluate to a |FieldsContainer|
of same length as the one being animated), or a |FieldsContainer| (also of same length as the one being animated).

.. note::

The behavior of the |animate| method is defined by a |Workflow| it creates and feeds to an |Animator|.
This |Workflow| loops over a |Field| of frame indices and for each frame generates a field of norm contours
to render, as well as a displacement field to deform the mesh if `deform_by` is provided.
For more information on plots on deformed meshes see: :ref:`ref_plotting_data_on_deformed_mesh`.


Animate the displacement results
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use |animate| with the displacement results.

.. tab-set::

.. tab-item:: Deformed mesh

.. jupyter-execute::
:hide-output:

# Animate the displacement results in a deformed geometry
disp_fc.animate()

.. jupyter-execute::
:hide-code:
:hide-output:

disp_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_disp_1.gif")

.. image:: animate_disp_1.gif
:scale: 50 %
:align: center

.. tab-item:: Static mesh

.. jupyter-execute::
:hide-output:

# Animate the displacement results on a static mesh using ``deform_by=False``
disp_fc.animate(deform_by=False)

.. jupyter-execute::
:hide-code:
:hide-output:

disp_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_disp_2.gif",
deform_by=False)

.. image:: animate_disp_2.gif
:scale: 50 %
:align: center

Animate the stress
^^^^^^^^^^^^^^^^^^

Use |animate| with the stress results.

.. tab-set::

.. tab-item:: Deformed mesh

.. jupyter-execute::
:hide-output:

# Animate the stress results on a deformed mesh
# Use the ``deform_by`` argument and give the displacement results.
stress_fc.animate(deform_by=disp_fc)

.. jupyter-execute::
:hide-code:
:hide-output:

stress_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_stress_1.gif",
deform_by=disp_fc)

.. image:: animate_stress_1.gif
:scale: 50 %
:align: center

.. tab-item:: Static mesh

.. jupyter-execute::
:hide-output:

# Animate the stress results in a static geometry
stress_fc.animate()

.. jupyter-execute::
:hide-code:
:hide-output:

stress_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_stress_2.gif")

.. image:: animate_stress_2.gif
:scale: 50 %
:align: center

Change the scale factor
-----------------------

You can change the scale factor using:

- A single number for a uniform constant scaling;
- A list of numbers for a varying scaling (same length as the number of frames).

Uniform constant scaling
^^^^^^^^^^^^^^^^^^^^^^^^
.. jupyter-execute::
:hide-output:

# Define a uniform scale factor
uniform_scale_factor=10.
# Animate the displacements
disp_fc.animate(scale_factor=uniform_scale_factor)

.. jupyter-execute::
:hide-code:
:hide-output:

disp_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_disp_3.gif",
scale_factor=uniform_scale_factor, text="Uniform scale factor")

.. image:: animate_disp_3.gif
:scale: 45 %
:align: center

Varying scaling
^^^^^^^^^^^^^^^

.. jupyter-execute::
:hide-output:

# Define a varying scale factor
varying_scale_factor = [i for i in range(len(disp_fc))]
# Animate the displacements
disp_fc.animate(scale_factor=varying_scale_factor)

.. jupyter-execute::
:hide-code:
:hide-output:

disp_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_disp_4.gif",
scale_factor=varying_scale_factor, text="Varying scale factor")

.. image:: animate_disp_4.gif
:scale: 45 %
:align: center

Save the animation
------------------

You can save the animation using the ``save_as`` argument with a target file path with the desired format as the extension key.
Accepted extensions are:
- ``.gif``;
- ``.avi``;
- ``.mp4``

For more information see |open_movie|.

.. jupyter-execute::
:hide-output:

# Animate the stress results and save it
stress_fc.animate(deform_by=disp_fc, save_as="animate_stress.gif")


Control the camera
------------------

Control the camera with the ``cpos`` argument.

A camera position is a combination of:
- A position;
- A focal point (the target);
- A upwards vector.

It results in a list of format:

.. code-block:: python
camera_position= [[pos_x, pos_y, pos_z], # position
[fp_x, fp_y, fp_z], # focal point
[up_x, up_y, up_z]] # upwards vector
The |animate| method accepts a single camera position or a list of camera positions for each frame.

.. note::
A tip for defining a camera position is to do a first interactive plot of the data
with argument ``return_cpos=True``, position the camera as desired in the view, and retrieve
the output of the plotting command.

Fixed camera
^^^^^^^^^^^^

.. jupyter-execute::
:hide-output:

# Define the camera position
cam_pos = [[0., 2.0, 0.6], [0.05, 0.005, 0.5], [0.0, 0.0, 1.0]]
# Animate the stress with a custom fixed camera position
stress_fc.animate(cpos=cam_pos)

.. jupyter-execute::
:hide-code:
:hide-output:

stress_fc.animate(save_as="source/user_guide/tutorials/animate/animate_disp_5.gif",
cpos=cam_pos,
off_screen=True)

.. image:: animate_disp_5.gif
:scale: 50 %
:align: center

Moving camera
^^^^^^^^^^^^^

.. jupyter-execute::
:hide-output:

import copy
# Define the list of camera positions
cpos_list = [cam_pos]
# Incrementally increase the x coordinate of the camera by 0.1 for each frame
for i in range(1, len(disp_fc)):
new_pos = copy.deepcopy(cpos_list[i-1])
new_pos[0][0] += 0.1
cpos_list.append(new_pos)

# Animate the stress with a moving camera
stress_fc.animate(cpos=cpos_list)

.. jupyter-execute::
:hide-code:
:hide-output:

stress_fc.animate(save_as="source/user_guide/tutorials/animate/animate_disp_6.gif",
cpos=cpos_list,
off_screen=True)

.. image:: animate_disp_6.gif
:scale: 50 %
:align: center

Additional options
------------------

You can use additional PyVista arguments of |open_movie|), such as:

- Show or hide the coordinate system axis with ``show_axes=True`` or ``show_axes=False``;
- Render off-screen for batch animation creation with ``off_screen=True``;
- Change the frame-rate with ``framerate``;
- Change the image quality with ``quality``.
9 changes: 5 additions & 4 deletions doc/source/user_guide/tutorials/animate/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@
Animate
=======

These tutorials demonstrate how to visualise the data in an animation.
These tutorials demonstrate how to visualize the data as an animation.

.. grid:: 1 1 3 3
:gutter: 2
:padding: 2
:margin: 2

.. grid-item-card:: Animate data
:link: ref_tutorials
.. grid-item-card:: Animate data over time
:link: ref_tutorials_animate_time
:link-type: ref
:text-align: center

This tutorial
This tutorial shows how to animate your results data over time.
+++
:bdg-mapdl:`MAPDL` :bdg-lsdyna:`LS-DYNA` :bdg-fluent:`FLUENT` :bdg-cfx:`CFX`

.. toctree::
:maxdepth: 2
:hidden:

animate_time.rst

0 comments on commit e9cfde6

Please sign in to comment.