-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug fix for non-aligned FFD blocks for rotType=0 and generalization of xFraction #67
Conversation
… to the main sys ref
I don't really know what's going on with this PR but I'll do my best to review it! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you comment on why you think the derivative tests are failing for rot cases not equal to 0?
pygeo/DVGeometry.py
Outdated
D_ = numpy.copy(D) | ||
bp_rot = numpy.copy(base_pt) # here base_pt has been rotated | ||
D = geo_utils.rotVbyW(D_ , ax_dir, -ang) | ||
base_pt = geo_utils.rotVbyW(bp_rot, ax_dir, -ang) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am 99% sure it's fine to do this but only if the rotation is truly implemented as a linear operator as it should be
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good point. This came directly from the Mads/Sandy fix so I did not 100% thought it through, I will check the rotVbyW
function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the rotation function ... do you think we should enforce some normalization of the rotation axis?
pygeo/DVGeometry.py
Outdated
x_max = numpy.max(pts_vec[:, 0]) | ||
y_min = numpy.min(pts_vec[:, 1]) | ||
y_max = numpy.max(pts_vec[:, 1]) | ||
z_min = numpy.min(pts_vec[:, 2]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is a possibility that this can lead to a reference axis that lies outside the volume
for example: if you have a typical ffd with the k direction as the ref axis and you rotate it in the yaw axis (so the wing is swept but the inboard edge isn't aligned with flow direction, then you can end up specifying curve ends that lie outside the FFD volume
though to be fair if you really really cambered our existing approach you could end up with a ref axis that lies outside the ffd volume
not sure if this matters or not but figured I'd bring it up
a bigger problem is that I don't think this is backward compatible with the existing default behavior. Right now the y and z frac depend on the xfrac automatically, with the new setup they're fixed at 0.5. I am surprised that this isn't failing reg tests (do we not use the xfrac option anyplace? or possibly our reg tests use rectangular volumes without pretwist or precamber)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The example you bring up is interesting... I am actually wondering if our current code could incur in such a problem.
I assume that for more complex FFD blocks you might want to "manually" feed the Ref Axis curve rather than relying on this approach.
However, at least with the current fix, the FFD is rotated back in the "correct" alignment before you define the nodes, similarly to what is done for the scaling fix, so this "yawed" FFD box you bring up should not be an issue. The correct set-up depends on the user, once more.
Anyway, this is not a good excuse for a sloppy fix, and we should take care of any corner case that comes up to mind. Let me sleep on this and get back to you!
About the backwards incompatibility, I am not sure it is actually such a huge problem here.
In the current code the y/z coordinates are given straight up by:
refaxisNodes[place+i,1] = numpy.mean(self.FFD.coef[sectionArr[i+skip,:,:],1])
refaxisNodes[place+i,2] = numpy.mean(self.FFD.coef[sectionArr[i+skip,:,:],2])
So it's a mean over the entire section points rather than the average of min/max. I agree it's a bit brute force, and I am open to improve it.
For a parallelepiped FFD the behaviour is identical, but I see how the behaviour for a cambered airfoil might be a bit different.
Maybe I am missing some major red flag here. Again, let me sleep on this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also worried about the changing the default behavior here.
Can we changing it so if only xFraction is given it works the same as before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I am envisioning something like getting rid of the defaults Fraction=0.5
and instead having an approach similar to before extended to y
and z
too.
For example:
if xFraction:
xMin = numpy.min(self.FFD.coef[sectionArr[i+skip,:,:],0])
xMax = numpy.max(self.FFD.coef[sectionArr[i+skip,:,:],0])
refaxisNodes[place+i,0] = xFraction*(xMax - xMin) + xMin
else:
refaxisNodes[place+i,0] = numpy.mean(self.FFD.coef[sectionArr[i+skip,:,:],0])
[same for y and z]
This way the default behavior will be exactly identical
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcomangano I think you are right and I was wrong about the way this piece of code was working
pygeo/DVGeometry.py
Outdated
x_max = numpy.max(pts_vec[:, 0]) | ||
y_min = numpy.min(pts_vec[:, 1]) | ||
y_max = numpy.max(pts_vec[:, 1]) | ||
z_min = numpy.min(pts_vec[:, 2]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also worried about the changing the default behavior here.
Can we changing it so if only xFraction is given it works the same as before?
Thanks a lot for the thorough review @joanibal ! I getting back at you with a set of commits to address all the comments by Wed |
I think I have addressed most of your comments! |
Just to give you an idea of the weird derivatives I am getting when switching to different rot types on my WT case:
Weird rotType==7 derivs:
Rotations are really off, but also the chord is inconsistent. For the first case, I would not bother too much because my configuration is pretty unconventional and requires |
Sorry you may have mentioned this somewhere, but just to be clear. Are the derivatives incorrect in the current version of pyGeo or only in this PR? |
@joanibal that is a very good question, I actually just mentioned it on slack. working sensitivities with rotType=0 (consistent with Mads' work)
weird rotType=7 sensitivities:
that's why probably I am overthinking it in the current PR |
Also worth noting, I made another test (test 24 without I guess understanding and fixing that might go beyond this PR, but we should at least take the opportunity to add a warning to prevent people to use these odd derivatives, as for example I did here. Still not satisfied about that because the scaling fix I added actually "could" be used even with other |
hmm, well that's not good. @marcomangano could you open an issue about the derivatives on github? |
Will do, just need to isolate a bit the problem. This would prevent using the new features included in this PR with problematic |
@marcomangano and @joanibal I'm not sure I totally understand the derivative situation and I don't have time right now to really dig into it. Go ahead and open and issue for the broken derivatives. Are the derivatives broken for all other rot types or just 7? Why would we allow people to use rot0angles with xyz rotations anyway? Also, on e.g. line 447 and in many other places you use |
Good point, I will check these Considering optimizations, note that
About the broken derivatives, I am doing more tests but the biggest mismatches in my cases come from DVs that use |
FYI, I am not able to investigate more on the sensitivities issue and open a proper issue until Monday - but I am not forgetting about it! |
I have been debugging a bit the issue with sensitivities for Regarding this PR, although the original fix by Mads extended to all Also, note I changed the check on the input rotation. For completeness, I verified that This PR is ready to be merged for me, let me know if you have other concerns! Thx for your thorough review |
IMO if this is documented in the docstrings this is fine.
Good stuff! |
I opened an issue to keep track of the issue with the sensitivities. |
@bbrelje can you take a final look and merge this if all looks good? |
Purpose
This PR is a combination of a bug-fix and new feature addition that builds on top of a previous, non-merged patch implemented by @Aagaard87 for his WT optimization paper.
Let's look at the bug-fix first. For WT parametrization, the
refAxis
object of kindrotType=0
is the most suited to handle (child) FFDs which are not aligned with the main system of reference.In the current code, the local section twist is implemented correctly, but the single-axis scaling parameters are not used in this part of the code.
This PR adds the necessary attributes and includes a pre/post-scaling rotation to ensure that the scaling axes definition is consistent with those of the local FFD section (see a more detailed example in the Testing section below).
This feature is in principle propagated to other
rotType
as well, but a warning is added as the sensitivities I am obtaining withrotType!=0
are slightly off. This will require more investigation and might be case-dependent, but the current code capabilities are preserved and, anyway, the parametrization cases this bugfix is addressing rely onrotType=0
a-priori.Following this, another minor but IMHO useful addition is included. Now the user can define
yFraction
andzFraction
alongsidexFraction
. This means that we can chose the parameterized location of therefAxis
node in the FFD section along both the chordwise and "thickness" direction. The motivation for this is briefly illustrated in the Testing section.Note that the
Fraction
option in the direction of the FFD-box spanwise axis is not effective, as the reference axis nodes must lie along the local FFD section.The fix for non-aligned FFDs is also extended to this feature for consistency.
The files affected by this PR are not related to the other major PRs currently open, so there should not be any conflicts.
I tried to add relevant comments where useful, but I am open to add more details based on the reviewers' feedback.
P.S. The explanation of this PR ended up being longer than expected, might probably add a related example in the upcoming MACH tutorial extension.
Type of change
Testing
The current code does not propagate
scale_x
,scale_y
, andscale_z
attributes to the FFD sections forrotType=0
. Newly addedtest_24_rot0_nonaligned
ensures that these scaling attributes are now effective.Moreover, this test ensures that
rotType=0
reference axis can handle (given appropriate input parameters) FFD blocks that are not aligned with the main system of reference, e.g. the blades of a 3-bladed wind turbine rotor. The newly added input parametersrot0ang
androt0axis
are used to provide the user control on this.The test can be used as reference to explain how
pyGeo
now handles these scaling+rotation operations. We start from an initial "vertical" FFD box which, using the combination ofrotType=0
,rot0ang=-90
, androt0axis=[1,0,0]
foraddRefAxis()
, is first rotated to have its "spanwise" axis along they
axis. Then, the script scales the 2nd section along thez
axis for a "thickness" increase and the 4th section along thex
axis for "chord" increase, it adds a +/- 30 deg twist respectively, and finally rotates the deformed FFD back in the initial position. The twist is added to ensure that the operation order is maintained, and the scaling preserves the orthogonality of the FFD in the section plane.See the initial (blue) and final (red) set of FFD points for
test_24_rot0_nonaligned
in the picture below:This is a particular case as the FFD box is already aligned with the main axis and we "flip" the
y
andz
axes, but the same criteria can be applied to a general rotation. In my case specifically, this feature is instrumental to apply the same deformations to all 3 blades of a WT rotor.Note that for this test I turned off the
.ref
file training via FD approach because of the relatively higher error with respect to the actualpyGeo
outputs.I suggest to plan a testing infrastructure refactoring to address this and other issues, including an extension of the testing covergage.
Ultimately, the
test_23_xyzfraction
ensures that the definition of the now-generalized location of the reference axis node in the FFD section is consistent with the user input. This is a new feature, and the current master code would not recognize the additional input arguments.This feature is again useful in the case of WT analysis and optimization, where the blade chord is not aligned to the
x
axis.Checklist
flake8
andblack
to make sure the code adheres to PEP-8 and is consistently formatted