PyGeM

Tutorial 3: Free Form Deformation on a unv mesh on cylinder: continuity of the deformation

In this tutorial we show how to perform the Free Form Deformation on a hexaedral mesh of a cylinder. In particular, above what already seen in the previous tutorials, we give some information about how to set the desired continuity to the geometry we are morphing.

First of all we just import the required PyGeM classes and we read a parameters file.

In [1]:
from pygem import FFDParameters, FFD, UnvHandler

params = FFDParameters()
params.read_parameters(filename='../tests/test_datasets/parameters_test_ffd_pipe_unv_C0.prm')

The following is the parameters file for the case at hand. In particular, if you look at the Box info section, there is 2-by-2-by-3 lattice around a cylinder. Since we want to shift the middle section of the cylinder along the x direction we modify only the parameter x weights corresponding to the middle point in z direction (index==1). In the following we show only the important parts of the parameters file

In [2]:
%cat ../tests/test_datasets/parameters_test_ffd_pipe_unv_C0.prm
[Box info]
# This section collects all the properties of the FFD bounding box.

# n control points indicates the number of control points in each direction (x, y, z).
# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2
n control points x: 2
n control points y: 2
n control points z: 3

# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).
# It uses the local coordinate system.
# For example to create a 2 x 1.5 x 3 meters box use the following: lenght box: 2.0, 1.5, 3.0
box lenght x: 2.2
box lenght y: 2.2
box lenght z: 6.0

# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of
# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].
# See section "Parameters weights" for more details.
# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.
box origin x: -1.1
box origin y: -1.1
box origin z: 2.0

# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.
# The rotation is done with respect to the box origin.
# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.
rotation angle x: 0.0
rotation angle y: 0.0
rotation angle z: 0.0


[Parameters weights]
# This section describes the weights of the FFD control points.
# We adopt the following convention:
# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.
# If a weight is equal to zero you can discard the line since the default is zero.
#
# | x index | y index | z index | weight |
#  --------------------------------------
# |    0    |    0    |    0    |  1.0   |
# |    0    |    1    |    1    |  0.0   | --> you can erase this line without effects
# |    0    |    1    |    0    | -2.1   |
# |    0    |    0    |    1    |  3.4   |

# parameter x collects the displacements along x, normalized with the box lenght x.
parameter x: 0   0   0   0.0
             0   0   1   0.8
             0   0   2   0.0
             0   1   0   0.0
             0   1   1   0.8
             0   1   2   0.0
             1   0   0   0.0
             1   0   1   0.8
             1   0   2   0.0
             1   1   0   0.0
             1   1   1   0.8
             1   1   2   0.0

# parameter y collects the displacements along y, normalized with the box lenght y.
parameter y: 0   0   0   0.0
             0   0   1   0.0
             0   0   2   0.0
             0   1   0   0.0
             0   1   1   0.0
             0   1   2   0.0
             1   0   0   0.0
             1   0   1   0.0
             1   0   2   0.0
             1   1   0   0.0
             1   1   1   0.0
             1   1   2   0.0

# parameter z collects the displacements along z, normalized with the box lenght z.
parameter z: 0   0   0   0.0
             0   0   1   0.0
             0   0   2   0.0
             0   1   0   0.0
             0   1   1   0.0
             0   1   2   0.0
             1   0   0   0.0
             1   0   1   0.0
             1   0   2   0.0
             1   1   0   0.0
             1   1   1   0.0
             1   1   2   0.0

We now load the unv file!

In [3]:
unv_handler = UnvHandler()
mesh_points = unv_handler.parse('../tests/test_datasets/test_pipe.unv')

and visualize the undeformed mesh.

We now, as always, perform the FFD and write out the results in another unv file.

In [4]:
free_form = FFD(params, mesh_points)
free_form.perform()
new_mesh_points = free_form.modified_mesh_points

unv_handler.write(new_mesh_points, 'test_pipe_mod_C0.unv')

Let us see the result.

As you can easily see the deformation makes the mesh only C0 continuous.

This is not wrong a priori, but it can have some drawbacks.

First of all, if you start with a smooth geometry, probably you do not want to end up with a geometry with more edges.

For large deformations this can cause inaccurancy errors when you try to perform analysis on the deformed mesh. In fact, it can happen that we have very stretched cells that the solver can not treat properly. Of course it depends mainly on the solver choosen, but, in general, it is not very clever to have "spiky" cells.

Moreover, if you are interested in some quantities, such as grandients and normal vectors, it can be difficult to compute them close to the "artificial edges".

Thus, we can rely on the properties of the Bernstein polynomials (on which FFD is built upon) to overcome this potential problem. In fact, if we add 2 new control points in the z direction we can move only the middle one, leaving the first two and the last two still. The new parameter file becomes:

In [5]:
%cat ../tests/test_datasets/parameters_test_ffd_pipe_unv_C1.prm
[Box info]
# This section collects all the properties of the FFD bounding box.

# n control points indicates the number of control points in each direction (x, y, z).
# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2
n control points x: 2
n control points y: 2
n control points z: 5

# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).
# It uses the local coordinate system.
# For example to create a 2 x 1.5 x 3 meters box use the following: lenght box: 2.0, 1.5, 3.0
box lenght x: 2.2
box lenght y: 2.2
box lenght z: 6.0

# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of
# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].
# See section "Parameters weights" for more details.
# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.
box origin x: -1.1
box origin y: -1.1
box origin z: 2.0

# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.
# The rotation is done with respect to the box origin.
# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.
rotation angle x: 0.0
rotation angle y: 0.0
rotation angle z: 0.0


[Parameters weights]
# This section describes the weights of the FFD control points.
# We adopt the following convention:
# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.
# If a weight is equal to zero you can discard the line since the default is zero.
#
# | x index | y index | z index | weight |
#  --------------------------------------
# |    0    |    0    |    0    |  1.0   |
# |    0    |    1    |    1    |  0.0   | --> you can erase this line without effects
# |    0    |    1    |    0    | -2.1   |
# |    0    |    0    |    1    |  3.4   |

# parameter x collects the displacements along x, normalized with the box lenght x.
parameter x: 0   0   0   0.0
             0   0   1   0.0
             0   0   2   0.8
             0   0   3   0.0
             0   0   4   0.0
             0   1   0   0.0
             0   1   1   0.0
             0   1   2   0.8
             0   1   3   0.0
             0   1   4   0.0
             1   0   0   0.0
             1   0   1   0.0
             1   0   2   0.8
             1   0   3   0.0
             1   0   4   0.0
             1   1   0   0.0
             1   1   1   0.0
             1   1   2   0.8
             1   1   3   0.0
             1   1   4   0.0

# parameter y collects the displacements along y, normalized with the box lenght y.
parameter y: 0   0   0   0.0
             0   0   1   0.0
             0   0   2   0.0
             0   0   3   0.0
             0   0   4   0.0
             0   1   0   0.0
             0   1   1   0.0
             0   1   2   0.0
             0   1   3   0.0
             0   1   4   0.0
             1   0   0   0.0
             1   0   1   0.0
             1   0   2   0.0
             1   0   3   0.0
             1   0   4   0.0
             1   1   0   0.0
             1   1   1   0.0
             1   1   2   0.0
             1   1   3   0.0
             1   1   4   0.0

# parameter z collects the displacements along z, normalized with the box lenght z.
parameter z: 0   0   0   0.0
             0   0   1   0.0
             0   0   2   0.0
             0   0   3   0.0
             0   0   4   0.0
             0   1   0   0.0
             0   1   1   0.0
             0   1   2   0.0
             0   1   3   0.0
             0   1   4   0.0
             1   0   0   0.0
             1   0   1   0.0
             1   0   2   0.0
             1   0   3   0.0
             1   0   4   0.0
             1   1   0   0.0
             1   1   1   0.0
             1   1   2   0.0
             1   1   3   0.0
             1   1   4   0.0

We again load the new parameter file, perform the FFD and write out the results in another unv file.

In [6]:
params = FFDParameters()
params.read_parameters(filename='../tests/test_datasets/parameters_test_ffd_pipe_unv_C1.prm')

free_form = FFD(params, mesh_points)
free_form.perform()
new_mesh_points = free_form.modified_mesh_points

unv_handler.write(new_mesh_points, 'test_pipe_mod_C1.unv')

And here it is the C1 mesh on the cylinder.

You can add some other points to increase again the continuity of the mesh!