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 in file.

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_2Dclass 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.