<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 PyGeM
Tutorial 1
Step1:  We need to read a parameters file. If does not exist the FFDParameters() class creates a default prm file that you have to edit for your problem specifications.
Step2:  The following is the parameters file for the case at hand. In particular, if you look at the Box info section, there is 3-by-2-by-2 lattice around an emisphere. Then we rotate it by 10 deg around y and 15 deg around z axis. In Parameters weights we just change the weights of the control point with indices 2, 0, 0.
[Box info]
&#35; This section collects all the properties of the FFD bounding box.
&#35; n control points indicates the number of control points in each direction (x, y, z).
&#35; For example, to create a 2 x 3 x 2 grid, use the following
Step3:  This is the original geometry
Step4:  Finally we can actually perform the FFD with the freeform module. The inputs are the parameters and the points positions we extracted before.
Step5:  Now we can write the modified stl file with the new points coordinates.
Step6:  And here we have the final plot
<ASSISTANT_TASK:>
Python Code:
%matplotlib inline
import pygem as pg
Explanation: PyGeM
Tutorial 1: Free Form Deformation on a sphere in stl file format
In this tutorial we will show the typical workflow. In particular we are going to parse the parameters file for the FFD, read an stl file of a sphere, perform the FFD and write the results on a new stl file.
First of all we just import pygem package and we set matplotlib for the notebook.
End of explanation
params = pg.params.FFDParameters()
params.read_parameters(filename='../tests/test_datasets/parameters_test_ffd_sphere.prm')
Explanation: We need to read a parameters file. If does not exist the FFDParameters() class creates a default prm file that you have to edit for your problem specifications.
End of explanation
stl_handler = pg.stlhandler.StlHandler()
mesh_points = stl_handler.parse('../tests/test_datasets/test_sphere.stl')
Explanation: The following is the parameters file for the case at hand. In particular, if you look at the Box info section, there is 3-by-2-by-2 lattice around an emisphere. Then we rotate it by 10 deg around y and 15 deg around z axis. In Parameters weights we just change the weights of the control point with indices 2, 0, 0.
[Box info]
&#35; This section collects all the properties of the FFD bounding box.
&#35; n control points indicates the number of control points in each direction (x, y, z).
&#35; For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2
n control points x: 3
n control points y: 2
n control points z: 2  
&#35; box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).
&#35; It uses the local coordinate system.
&#35; 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: 45.0
box lenght y: 90.0
box lenght z: 90.0  
&#35; box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of
&#35; rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].
&#35; See section "Parameters weights" for more details.
&#35; For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.
box origin x: -20.0
box origin y: -55.0
box origin z: -45.0  
&#35; rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.
&#35; The rotation is done with respect to the box origin.
&#35; 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
rotation angle y: 10
rotation angle z: 15  
[Parameters weights]
&#35; This section describes the weights of the FFD control points.
&#35; We adopt the following convention:
&#35; For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.
&#35; 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   |  
&#35; parameter x collects the displacements along x, normalized with the box lenght x.
parameter x: &nbsp;2&nbsp;&nbsp;0&nbsp;&nbsp;0&nbsp;&nbsp;0.8
&#35; parameter y collects the displacements along y, normalized with the box lenght y.
parameter y: &nbsp;2&nbsp;&nbsp;0&nbsp;&nbsp;0&nbsp;&nbsp;-0.5
&#35; parameter z collects the displacements along z, normalized with the box lenght z.
parameter z: &nbsp;2&nbsp;&nbsp;0&nbsp;&nbsp;0&nbsp;&nbsp;-1.0
Now we have to load the stl file on which we will perform the FFD. Since it is an stl we use the StlHandler() class. The parse method extract the points coordinate without touching the topology of the geometry.
End of explanation
stl_handler.plot(plot_file='../tests/test_datasets/test_sphere.stl')
Explanation: This is the original geometry:
End of explanation
free_form = pg.freeform.FFD(params, mesh_points)
free_form.perform()
new_mesh_points = free_form.modified_mesh_points
Explanation: Finally we can actually perform the FFD with the freeform module. The inputs are the parameters and the points positions we extracted before.
End of explanation
stl_handler.write(new_mesh_points, 'test_sphere_mod.stl')
Explanation: Now we can write the modified stl file with the new points coordinates.
End of explanation
stl_handler.plot(plot_file='test_sphere_mod.stl')
Explanation: And here we have the final plot:
End of explanation
<END_TASK>