Free-Form Deformation Module
The deformation module relies on an abstract class Deform
theoretically compatible with any kind of geometric deformation but it was only subclassed for Free-Form Deformation (FFD) purposes so far. FFD is a technique designed to deform solid geometric models in a free-form manner. It was originally introduced by Sederberg in 1986 (doi). The present implementation is based on the description in Duvigneau 2006.
The main steps of FFD are:
1) create a lattice, i.e. the minimal parallelepiped box that embeds the geometry (a rectangle in 2D),
2) project the geometry points into the lattice referential such that its coordinates now vary between [0;1] in both x and y directions,
3) for a given deformation vector applied to each control points of the lattice, the displacements of all points forming the geometry are computed with the tensor product of the Bernstein basis polynomials,
4) the deformed profile is finally projected back into the original referential.
Note
This implementation is limited to 2D geometries only. More sophisticated libraries exist such as PyGeM or pyGeo but they come with heavier dependencies and more cumbersome installation procedures.
2D FFD
Free-Form Deformation is implemented in FFD_2D
, a straightforward class instantiated with two positional arguments:
file (str)
which indicates the filename of the baseline geometry to be deformed,ncontrol (int)
which indicates the number of design points on each side of the lattice.
And two optional arguments:
pad (tuple[int, int])
which can be used to make the lattice edges moveable,header (int)
which is used to specify the number of header lines infile
.
Warning
The input file is expected to have a specific formatting i.e. a 2 line header followed by coordinates given as tabulated entries (one point per row) with single space separators (see data/naca12.dat
for an example).
Once instantiated, the apply_ffd(Delta)
method can be used to perform the deformation corresponding to the array Delta
.
Note
During an FFD-based optimization, FFD_2D
is initialized with ncontrol = n_design // 2
.
Illustration
An FFD with 2 control points (i.e. 4 in total) and the deformation vector Delta = (0., 0., 1., 1.)
will yield the following profile:
Considering the figure notations, one notices that the deformation vector Delta
corresponds to the list of deformations to be applied to the lower control points followed by those applied to the upper control points. In this case, Delta
is: $$(D_{10}=0.,\, D_{20}=0.,\, D_{11}=1.,\, D_{21}=1.)$$ in lattice unit. The corner points are left unchanged so that the lattice corners remain fixed.
Quick Experiments
The auto_ffd.py
scripts is called with the ffd
command. It enables basic testing and visualization. It comes with a few options:
ffd --help
usage: ffd [-h] -f FILE [-c CONFIG] [-o OUTDIR] [-nc NCONTROL] [-np NPROFILE] [-d DELTA]
options:
-h, --help show this help message and exit
-f FILE, --file FILE baseline geometry: --file=/path/to/file.dat (default: None)
-c CONFIG, --config CONFIG
config: --config=/path/to/config.json (default: )
-o OUTDIR, --outdir OUTDIR
output directory (default: output)
-nc NCONTROL, --ncontrol NCONTROL
number of control points on each side of the lattice (default: 3)
-np NPROFILE, --nprofile NPROFILE
number of profiles to generate (default: 3)
-d DELTA, --delta DELTA
Delta: 'D10 D20 .. D2nc' (default: None)
For instance, the command below will perform 3 control points FFDs for 4 random deformations sampled with an LHS sampler:
# from aero-optim to naca_base
cd examples/NACA12/naca_base
ffd -f ../data/naca12.dat -np 4 -nc 3
2D FFD & POD
The coupling between POD and FFD was implemented in the FFD_POD_2D
class. Its objective is to build a dataset of n
FFD perturbed profiles sampled from LHS and to use it to perform POD-based data reduction from the FFD dimension d
to a smaller dimension d*
. Details on how to do so are given in (POD 1) and (POD 2).
The FFD_pod_2D
class is instantiated with 5 positional arguments:
file (str)
which indicates the filename of the baseline geometry to be deformed,pod_ncontrol (int)
which indicates the number of reduced design points,ffd_ncontrol (int)
which indicates the number of FFD control points design points,ffd_dataset_size (int)
which indicates the size of the FFD dataset used in the POD procedure,ffd_bound (tuple[Any])
which set the min/max displacement ranges of the FFD dataset used in the POD procedure.
To use this functionality, the ffd_type
of the "study"
entry must be set to "ffd_pod_2d"
. The number of FFD control points and boundaries are still given by the "n_design"
and "bound"
values of the "optim"
entry. In the "ffd"
entry, the POD reduced dimension is set via "pod_ncontrol"
and the size of the FFD dataset with "ffd_dataset_size"
.
Note
In the Optimizer
class, the n_design
attribute is automatically switched to "pod_ncontrol"
once the FFD_POD_2D
class is set.
Quick Experiments
An application of this feature is illustrated in the POD notebook.
Quick Example
Let us condiser a user who would want to perform an optimization based on 2D FFD of 10 control points in total (5 on each side) coupled with a POD of reduced dimension 5. The configuration file should be set as follows:
{
"study": {
"ffd_type: "ffd_pod_2d",
...
},
"optim": {
"n_design": 10,
"bound": [-0.2, 0.2],
...
}
"ffd": {
"pod_ncontrol": 5,
"ffd_dataset_size": 1000
}
}
In turns, the effective number of design variables would be n_design = 5
.
2D FFD with rotation
A variant of the FFD including an extra-rotation step around the center of gravity was implemented with the RotationWrapper
class. It directly inherits from Deform
and overrides apply_fd
in a way such that for any deformation array Delta
, the first components are used to perform a standard FFD deformation and the last value is used to rotate the deformed geometry. In the context of an optimization this means that n_design += 1
.
Note
A direct consequence of this approach is that the deformation array Delta
has size n_design + 1
. In the optimizer, this is automatically handled by adding 1 to n_design
when instantiating the FFDClass
in set_ffd_class
.
To use this functionality, the ffd_type
of the "study"
entry can be set to any deformation kind and the boundaries are still given by the "bound"
values of the "optim"
entry. In the "ffd"
entry, "rotate"
should be set to true
and the rotation range must be specified via "rot_bound"
as a list of two values. The values will be automatically appended to the bound
attribute of the Optimizer
object.
Quick Example
Let us condiser a user who would want to perform an optimization based on 2D FFD of 10 control points in total (5 on each side) and an extra rotation step inside a +/-2 degrees range. The configuration file should be set as follows:
{
"study": {
"ffd_type: "ffd_2d",
...
},
"optim": {
"n_design": 10,
"bound": [-0.2, 0.2],
...
}
"ffd": {
"rotation": true,
"rot_bound": [-2, 2]
}
}
In turns, the effective number of design variables would become n_design = 11
.