Skip to content

Commit e9cfde6

Browse files
luisaFelixSallesPProfiziJennaPaikowsky
authored
Add animation tutorials to the main tutorials branch (#1915)
* 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]>
1 parent 991b95a commit e9cfde6

File tree

2 files changed

+366
-4
lines changed

2 files changed

+366
-4
lines changed
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
.. _ref_tutorials_animate_time:
2+
3+
======================
4+
Animate data over time
5+
======================
6+
7+
.. |Examples| replace:: :mod:`ansys.dpf.core.examples`
8+
.. |Animator| replace:: :class:`Animator<ansys.dpf.core.animator.Animator>`
9+
.. |Field| replace:: :class:`Field<ansys.dpf.core.field.Field>`
10+
.. |FieldsContainer| replace:: :class:`FieldsContainer<ansys.dpf.core.fields_container.FieldsContainer>`
11+
.. |MeshedRegion| replace:: :class:`MeshedRegion <ansys.dpf.core.meshed_region.MeshedRegion>`
12+
.. |TimeFreqSupport| replace:: :class:`TimeFreqSupport <ansys.dpf.core.time_freq_support.TimeFreqSupport>`
13+
.. |animate| replace:: :func:`FieldsContainer.animate() <ansys.dpf.core.fields_container.FieldsContainer.animate>`
14+
.. |Result| replace:: :class:`Result <ansys.dpf.core.results.Result>`
15+
.. |Operator| replace:: :class:`Operator<ansys.dpf.core.dpf_operator.Operator>`
16+
.. |Workflow| replace:: :class:`Workflow<ansys.dpf.core.workflow.Workflow>`
17+
.. |Elemental| replace:: :class:`elemental<ansys.dpf.core.common.locations>`
18+
.. |ElementalNodal| replace:: :class:`elemental_nodal<ansys.dpf.core.common.locations>`
19+
.. |Nodal| replace:: :class:`nodal<ansys.dpf.core.common.locations>`
20+
.. |Faces| replace:: :class:`faces<ansys.dpf.core.common.locations>`
21+
.. |Overall| replace:: :class:`overall<ansys.dpf.core.common.locations>`
22+
.. |open_movie| replace:: :class:`pyvista.Plotter.open_movie`
23+
24+
This tutorial demonstrates how to create 3D animations of data in time.
25+
26+
:jupyter-download-script:`Download tutorial as Python script<animate_time>` :jupyter-download-notebook:`Download tutorial as Jupyter notebook<animate_time>`
27+
28+
To animate data across time, you must store the data in a |FieldsContainer| with a ``time`` label.
29+
30+
31+
Get the result files
32+
--------------------
33+
34+
First, import a results file. For this tutorial, you can use the one available in the |Examples| module.
35+
For more information about how to import your own result file in DPF, see
36+
the :ref:`ref_tutorials_import_data` tutorial section.
37+
38+
.. jupyter-execute::
39+
40+
# Import the ``ansys.dpf.core`` module
41+
from ansys.dpf import core as dpf
42+
# Import the examples module
43+
from ansys.dpf.core import examples
44+
# Import the operators module
45+
from ansys.dpf.core import operators as ops
46+
47+
# Define the result file path
48+
result_file_path = examples.find_msup_transient()
49+
# Create the model
50+
model = dpf.Model(data_sources=result_file_path)
51+
52+
Define a time scoping
53+
---------------------
54+
55+
To animate across time, you must define the time steps you are interested in.
56+
This tutorial retrieves all the time steps available in |TimeFreqSupport|, but you can also filter them.
57+
For more information on how to define a scoping, see the ``Narrow down data`` tutorial in the
58+
:ref:`ref_tutorials_import_data` tutorials section.
59+
60+
.. jupyter-execute::
61+
62+
# Get a scoping of all time steps available
63+
time_steps = model.metadata.time_freq_support.time_frequencies
64+
65+
Extract the results
66+
-------------------
67+
68+
Extract the results to animate. In this tutorial, you extract the displacement and stress results.
69+
70+
.. note::
71+
72+
Only the |Elemental|, |Nodal|, or |Faces| locations are supported for animations.
73+
|Overall| and |ElementalNodal| locations are not currently supported.
74+
75+
76+
.. jupyter-execute::
77+
78+
# Get the displacement fields (already on nodes) at all time steps
79+
disp_fc = model.results.displacement(time_scoping=time_steps).eval()
80+
print(disp_fc)
81+
82+
.. jupyter-execute::
83+
84+
# Get the stress fields on nodes at all time steps
85+
# Request the stress on |Nodal| location as the default |ElementalNodal| location is not supported.
86+
stress_fc = model.results.stress.on_location(location=dpf.locations.nodal).on_time_scoping(time_scoping=time_steps).eval()
87+
print(stress_fc)
88+
89+
Animate the results
90+
-------------------
91+
92+
Animate the results with the |animate| method.
93+
You can animate them on a deformed mesh (animate the color map and the mesh)
94+
or on a static mesh (animate the color map only).
95+
96+
The default behavior of the |animate| method is to:
97+
98+
- Display the norm of the data components;
99+
- Display data at the top layer for shells;
100+
- Display the deformed mesh when animating displacements;
101+
- Display the static mesh for other types of results;
102+
- Use a constant and uniform scale factor of 1.0 when deforming the mesh.
103+
104+
You can animate any result on a deformed geometry by providing displacement results in the `deform_by` parameter.
105+
106+
The geometry can be deformed by a |Result| object, an |Operator| (It must evaluate to a |FieldsContainer|
107+
of same length as the one being animated), or a |FieldsContainer| (also of same length as the one being animated).
108+
109+
.. note::
110+
111+
The behavior of the |animate| method is defined by a |Workflow| it creates and feeds to an |Animator|.
112+
This |Workflow| loops over a |Field| of frame indices and for each frame generates a field of norm contours
113+
to render, as well as a displacement field to deform the mesh if `deform_by` is provided.
114+
For more information on plots on deformed meshes see: :ref:`ref_plotting_data_on_deformed_mesh`.
115+
116+
117+
Animate the displacement results
118+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
119+
120+
Use |animate| with the displacement results.
121+
122+
.. tab-set::
123+
124+
.. tab-item:: Deformed mesh
125+
126+
.. jupyter-execute::
127+
:hide-output:
128+
129+
# Animate the displacement results in a deformed geometry
130+
disp_fc.animate()
131+
132+
.. jupyter-execute::
133+
:hide-code:
134+
:hide-output:
135+
136+
disp_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_disp_1.gif")
137+
138+
.. image:: animate_disp_1.gif
139+
:scale: 50 %
140+
:align: center
141+
142+
.. tab-item:: Static mesh
143+
144+
.. jupyter-execute::
145+
:hide-output:
146+
147+
# Animate the displacement results on a static mesh using ``deform_by=False``
148+
disp_fc.animate(deform_by=False)
149+
150+
.. jupyter-execute::
151+
:hide-code:
152+
:hide-output:
153+
154+
disp_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_disp_2.gif",
155+
deform_by=False)
156+
157+
.. image:: animate_disp_2.gif
158+
:scale: 50 %
159+
:align: center
160+
161+
Animate the stress
162+
^^^^^^^^^^^^^^^^^^
163+
164+
Use |animate| with the stress results.
165+
166+
.. tab-set::
167+
168+
.. tab-item:: Deformed mesh
169+
170+
.. jupyter-execute::
171+
:hide-output:
172+
173+
# Animate the stress results on a deformed mesh
174+
# Use the ``deform_by`` argument and give the displacement results.
175+
stress_fc.animate(deform_by=disp_fc)
176+
177+
.. jupyter-execute::
178+
:hide-code:
179+
:hide-output:
180+
181+
stress_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_stress_1.gif",
182+
deform_by=disp_fc)
183+
184+
.. image:: animate_stress_1.gif
185+
:scale: 50 %
186+
:align: center
187+
188+
.. tab-item:: Static mesh
189+
190+
.. jupyter-execute::
191+
:hide-output:
192+
193+
# Animate the stress results in a static geometry
194+
stress_fc.animate()
195+
196+
.. jupyter-execute::
197+
:hide-code:
198+
:hide-output:
199+
200+
stress_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_stress_2.gif")
201+
202+
.. image:: animate_stress_2.gif
203+
:scale: 50 %
204+
:align: center
205+
206+
Change the scale factor
207+
-----------------------
208+
209+
You can change the scale factor using:
210+
211+
- A single number for a uniform constant scaling;
212+
- A list of numbers for a varying scaling (same length as the number of frames).
213+
214+
Uniform constant scaling
215+
^^^^^^^^^^^^^^^^^^^^^^^^
216+
.. jupyter-execute::
217+
:hide-output:
218+
219+
# Define a uniform scale factor
220+
uniform_scale_factor=10.
221+
# Animate the displacements
222+
disp_fc.animate(scale_factor=uniform_scale_factor)
223+
224+
.. jupyter-execute::
225+
:hide-code:
226+
:hide-output:
227+
228+
disp_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_disp_3.gif",
229+
scale_factor=uniform_scale_factor, text="Uniform scale factor")
230+
231+
.. image:: animate_disp_3.gif
232+
:scale: 45 %
233+
:align: center
234+
235+
Varying scaling
236+
^^^^^^^^^^^^^^^
237+
238+
.. jupyter-execute::
239+
:hide-output:
240+
241+
# Define a varying scale factor
242+
varying_scale_factor = [i for i in range(len(disp_fc))]
243+
# Animate the displacements
244+
disp_fc.animate(scale_factor=varying_scale_factor)
245+
246+
.. jupyter-execute::
247+
:hide-code:
248+
:hide-output:
249+
250+
disp_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_disp_4.gif",
251+
scale_factor=varying_scale_factor, text="Varying scale factor")
252+
253+
.. image:: animate_disp_4.gif
254+
:scale: 45 %
255+
:align: center
256+
257+
Save the animation
258+
------------------
259+
260+
You can save the animation using the ``save_as`` argument with a target file path with the desired format as the extension key.
261+
Accepted extensions are:
262+
- ``.gif``;
263+
- ``.avi``;
264+
- ``.mp4``
265+
266+
For more information see |open_movie|.
267+
268+
.. jupyter-execute::
269+
:hide-output:
270+
271+
# Animate the stress results and save it
272+
stress_fc.animate(deform_by=disp_fc, save_as="animate_stress.gif")
273+
274+
275+
Control the camera
276+
------------------
277+
278+
Control the camera with the ``cpos`` argument.
279+
280+
A camera position is a combination of:
281+
- A position;
282+
- A focal point (the target);
283+
- A upwards vector.
284+
285+
It results in a list of format:
286+
287+
.. code-block:: python
288+
289+
camera_position= [[pos_x, pos_y, pos_z], # position
290+
[fp_x, fp_y, fp_z], # focal point
291+
[up_x, up_y, up_z]] # upwards vector
292+
293+
The |animate| method accepts a single camera position or a list of camera positions for each frame.
294+
295+
.. note::
296+
A tip for defining a camera position is to do a first interactive plot of the data
297+
with argument ``return_cpos=True``, position the camera as desired in the view, and retrieve
298+
the output of the plotting command.
299+
300+
Fixed camera
301+
^^^^^^^^^^^^
302+
303+
.. jupyter-execute::
304+
:hide-output:
305+
306+
# Define the camera position
307+
cam_pos = [[0., 2.0, 0.6], [0.05, 0.005, 0.5], [0.0, 0.0, 1.0]]
308+
# Animate the stress with a custom fixed camera position
309+
stress_fc.animate(cpos=cam_pos)
310+
311+
.. jupyter-execute::
312+
:hide-code:
313+
:hide-output:
314+
315+
stress_fc.animate(save_as="source/user_guide/tutorials/animate/animate_disp_5.gif",
316+
cpos=cam_pos,
317+
off_screen=True)
318+
319+
.. image:: animate_disp_5.gif
320+
:scale: 50 %
321+
:align: center
322+
323+
Moving camera
324+
^^^^^^^^^^^^^
325+
326+
.. jupyter-execute::
327+
:hide-output:
328+
329+
import copy
330+
# Define the list of camera positions
331+
cpos_list = [cam_pos]
332+
# Incrementally increase the x coordinate of the camera by 0.1 for each frame
333+
for i in range(1, len(disp_fc)):
334+
new_pos = copy.deepcopy(cpos_list[i-1])
335+
new_pos[0][0] += 0.1
336+
cpos_list.append(new_pos)
337+
338+
# Animate the stress with a moving camera
339+
stress_fc.animate(cpos=cpos_list)
340+
341+
.. jupyter-execute::
342+
:hide-code:
343+
:hide-output:
344+
345+
stress_fc.animate(save_as="source/user_guide/tutorials/animate/animate_disp_6.gif",
346+
cpos=cpos_list,
347+
off_screen=True)
348+
349+
.. image:: animate_disp_6.gif
350+
:scale: 50 %
351+
:align: center
352+
353+
Additional options
354+
------------------
355+
356+
You can use additional PyVista arguments of |open_movie|), such as:
357+
358+
- Show or hide the coordinate system axis with ``show_axes=True`` or ``show_axes=False``;
359+
- Render off-screen for batch animation creation with ``off_screen=True``;
360+
- Change the frame-rate with ``framerate``;
361+
- Change the image quality with ``quality``.

doc/source/user_guide/tutorials/animate/index.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,24 @@
44
Animate
55
=======
66

7-
These tutorials demonstrate how to visualise the data in an animation.
7+
These tutorials demonstrate how to visualize the data as an animation.
88

99
.. grid:: 1 1 3 3
1010
:gutter: 2
1111
:padding: 2
1212
:margin: 2
1313

14-
.. grid-item-card:: Animate data
15-
:link: ref_tutorials
14+
.. grid-item-card:: Animate data over time
15+
:link: ref_tutorials_animate_time
1616
:link-type: ref
1717
:text-align: center
1818

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

2323
.. toctree::
2424
:maxdepth: 2
2525
:hidden:
2626

27+
animate_time.rst

0 commit comments

Comments
 (0)