PyGeM

Tutorial 6: Deformation of the computational mesh through an RBF interpolator

In this tutorial we're going to show the procedure to propagate a given object deformation to the computational grid built around such a object. In this way, any deformation mapping we can apply to the object to morph is replicated to all discretized space around it, avoiding the expensive re-meshing phase. In this tutorial we just show the needed steps to achieve such parametric mesh exploiting the PyGeM package, presenting a very simple test-case, but for a practical real-world usecase we refer to

The numerical settings

The methodology that follows is very general and can be extended to many different scenario, since it basically requires only the coordinates of the nodes of the object geometry and of the (undeformed) initial mesh. For sake of simplicity, here we present the deformation of an OpenFOAM grid for simulating a 2D Navier-Stokes flow around a cylinder. We assume that this cilinder is the object to deform. Even if the entire procedure is employable also when the deformation mapping applied to the initial object is unknown (we see in few lines that the required input is just the displacement of the initial object after the deformation), here we apply the free-form deformation method to the undeformed cylinder in order to parametrize its geometry.

First of all, we import all the libraries which we're going to use:

Then we define the auxiliary function scatter3d which we're going to use often to plot several objects as lists of 3D points. You do not need to understand the exact details of this function since we are going to use it only to show the results:

1) Extraction of the mesh points

In few words, the procedure uses the the nodes coordinates of the deformed object in order to fit the RBF interpolator which will propagate such morphing to the computational grid. The first step is then the extraction of such points: we specify that, for avoiding that also the mesh boundaries are deformed, we need to force a zero displacement there. We can easily obtain that by passing these bondaries as the RBF control points.

As we mentioned before, in this tutorial we use the library Smithers to load the OpenFOAM mesh from the folder openfoam_mesh which serves as example. First of all, we use the method read() from the class OpenFoamHandler to load the data. This method returns a dictionary which contains all the informations available about the mesh, included the list of points (mesh['points']).

Moreover, the object returned by read() contains a list of points for each boundary, represented by a list of indexes which refers to mesh['points']. We can use these lists to obtain the coordinates of the points which compose the cylinder (which we call obstacle) and walls.

At this point we can plot the obstacle and the walls using the auxiliary function scatter3d:

As you can see our geometry is made of two faces, one at z=0.5 and the other at z=-0.5.

2) Object Deformation

Here we need to apply a generic deformation on the initial object (the cylinder here). In case this deformation is already computed, you can skip this section, having the forethought of storing the nodes coordinates of the undeformed and deformed object.

We use the FFD deformation from PyGeM (for a reference check this tutorial) to deform the original object (the upper and lower faces of a cylinder). We create the new FFD object and set its attributes in order to create a simple deformation

We then operate the deformation and plot the result, against the old version of the obstacle.

3) Propagation of the deformation to the computational mesh

We have now to deform the grid according to the object deformation. Once we have the coordinates of the undeformed and deformed object (and, we remark, also the boundaries that have to been fixed), we can just use them to fed an RBF interpolator.

We employ the RBF class from PyGeM. For a reference on the parameters available when using the class RBF from PyGeM, please check the documentation. We keep the default values for all the parameters except radius, for which we set radius=5. This parameter is a scaling coefficient which affects the shape of the radial basis function used for the interpolation. A practical note: long story short, RBF solves a linear system to fit the input data. However the matrix representing our system may result singular if we pass more times the same point(s). To avoid this issue, we just extract the unique points, as show in the next cell.

As visual proof, we plot the original and deformed control points we pass to RBF constructor!

At this point we can use the ingredients we prepared in the last sections to obtain the parametric mesh.

We can use the RBF.__call__() method to determine the new position of the points which compose the mesh. This is a resource-intensive computation and may slow down the device on which you're running this notebook.

And basically that's all! The array new_mesh_points contains the new coordinates of the mesh points, and give us the possibility to store it in a new file or exploit them for some computation.

The last thing we show here is the visualization of the deformed mesh. In order to plot the results we prefer a 2D scatter plot of the upper part of the mesh (z=0.5). Therefore we define the auxiliary function upper_layer which extracts the points at z=0.5 from the given list of points.

We can now plot the interpolated mesh, with the deformed and original obstacle.