Child FFD deltas not calculated with the same point set as parent.
Previously known issue written here for documentation.
The Problem
When working with a child FFD embed in a parent FFD, a sub set of the points embedded in the parent will also be embed in the child. When calculating the additional movement of embed points due to the modification of the child FFD, the child FFD will first update the points embed inside of itself according to how the control points of the child were moved when the parent deformed. However, updating the points according to how the control points of the child were moved will not produce a point set identical to those embed in the parent! So when the child calculates deltas (additional position modifications) and they are added to the modifications caused by the motion of the parent FFD, the deltas will not have the intended geometric effect. For example in cases where the parent FFD does shape modifications and the child rotates a subset of these points (think morphing LE/TE), the rotation will skew the shape modifications. An example of this is shown in the picture below.

When it's an Issue
- when combining parent shape variables with child shape deformation that isn't uniform for each point (child shape variables and rotation will cause issues, but translation will not).
- Especially apparent when using large child FFD modifications
Work Around
- use a linear spline surface for the Child FFD (works well for rotations, but not for child shape variables)
long term fix
- change the data structure for child FFDs so that it references the same point set as the parent (same object in memory) but only works with a sub set of these points (the ones inside the child FFD). This way the points that the child FFD uses to calculate its additional modifications will always be the same as the points warped by the parent deformation.
@joanibal can you post the global design variable function you used in the child?
While writing the tutorial I wanted to understand why global variable funcs that use += operations work properly. When self.update() is called, the FFD coefficients are set back to their original (baseline) locations and then the global DVs are computed in order of their addition, then sectionlocal and local variables. So you don't accumulate changes across iterations via += operations.
On the other hand, child FFDs do some weird stuff that I don't totally understand, but I don't think it's doing the same reset. So if you used += or *= operations in your child global dv funcs, it could lead to some weird behavior. It may or may not be relevant to your debugging here
if not self.isChild:
self.FFD.coef = self.origFFDCoef.copy()
self._setInitialValues()
else:
# Update all coef
self.FFD._updateVolumeCoef()
# Evaluate starting pointset
Xstart = self.FFD.getAttachedPoints(ptSetName)
if self.complex:
# Now we have to propagate the complex part through Xstart
tempCoef = self.FFD.coef.copy().astype('D')
Xstart = Xstart.astype('D')
imag_part = numpy.imag(tempCoef)
imag_j = 1j
dPtdCoef = self.FFD.embededVolumes[ptSetName].dPtdCoef
if dPtdCoef is not None:
for ii in range(3):
Xstart[:, ii] += imag_j*dPtdCoef.dot(imag_part[:, ii])
Here are the functions I used with child FFDs
def twist_flap(val, geo):
geo.rot_z['flap_axis'].coef[:] = val[:] - 29.5
def translate_x_flap(val, geo):
C = geo.extractCoef('flap_axis')
C[:, 0] += val[:] + 0.15421028
geo.restoreCoef(C, 'flap_axis')
def translate_y_flap(val, geo):
C = geo.extractCoef('flap_axis')
C[:, 1] += val[:] - 0.0816069
geo.restoreCoef(C, 'flap_axis')
def twist_slat(val, geo):
geo.rot_z['slat_axis'].coef[:] = val[:] + 29.0
def translate_x_slat(val, geo):
C = geo.extractCoef('slat_axis')
C[:, 0] += val[:] - .0911552
geo.restoreCoef(C, 'slat_axis')
def translate_y_slat(val, geo):
C = geo.extractCoef('slat_axis')
C[:, 1] += val[:] - 0.0735696
geo.restoreCoef(C, 'slat_axis')
Yeah I can't say for certain that the weird behavior is caused by the per-iteration reset behavior of the coefficients referenced by C = geo.extractCoef('slat_axis') but it's possible.