WISDEM blade optimization example: my output plot not like in the example

I have run the Blade Optimization Example found in https://wisdem.readthedocs.io/en/master/examples/blade/tutorial.html for the three cases baseline, aerodynamic optimization, and structural optimization.

My problem is that my output plots are not like the ones shown in the example.

For all the runs I use the input .yaml files provided in my WISDEM folder “examples\03_blade”.

The baseline calculation succeds, the Simple Aerodynamic Optimization fails with the max_iter optimization parameter set to 2 (as expected, I have understood from the example text), but also with max_iter set to 10 or 20, and the Simple Structural Optimization succeds with max_iter set to 10.

Then, for the successful structural optimization, I generated the plots to compare the optimized variables to the baseline ones with the command

and, for the unsuccessful aerodynamic optimization, with the command

Now, for the structural optimization, my plot of unit mass vs. blade non-dimensional span is exactly like Fig. 4 in the example, but the plot of spar cap thickness vs. blade non-dim span is not like Fig. 3, and shows no difference between the baseline case and the optimized case (see attachment sc_opt.png).
Since I haven’t changed anything in the .yaml files except max_iter value, I would like to know why this plot does not match that reported in the example.

Similarly, for the failed aerodynamic optimization, my plot of twist vs non-dimensional blade span, unlike Fig. 2 in the example, shows no difference between the baseline case and the optimized case (see attachment twist_opt.png). However, the plot of axial induction vs. blade non-dim span does show a difference between the two cases, but is not the same as in Fig. 1 (see attachment induction.png).
In this case I wonder whether I should expect my plots to be like figures 1 and 2 only in case of succesfull optimization; I have not understood that from the example text.

Thank you for your time and attention.
induction.png
twist_opt.png
sc_opt.png

I obtain the same plots as you when I walk through the example. This is partly due to (1) the baseline design and code shifting a bit since those example plots in the documentation were generated, and (2) the compare_design script has slowly crept out of date. Thanks to your question, I have fixed some of the plotting issues in the “develop” branch. If you do “git checkout develop” and then “git pull”, that might help.

Thank you for your fast reply.
I have never used git before, and would like to be sure of what those two commands do before issuying them, so, please, clarify to me the following points.

“git checkout develop” should bring me into my local “develop” branch of WISDEM (I guess that I have worked in the master branch so far…).
Then, “git pull” should merge all the changes present in the remote “develop” branch to my local “develop” branch leaving untouched my master branch files. Is this correct? i want to be sure of this because I have modified some of the WISDEM codes, and do not want them to be accidentally overwritten/rolled-back.

I am using WISDEM via an anaconda environment, and have git available in this environment, so I will issue those commands from there.
Now, once the “develop” branches’ merge is done, do I simply re-issue

from my wisdem environment to run the example on the updated files?
Do I need to issue “git checkout master” to come back to and work in my master branch?

I am on a Windows 10 machine.

Thank you for your patience.

Yes, your description of the git commands is accurate. Git will never overwrite your changes without giving you ample warnings, so not to worry. If you want to be doubly cautious, you can always create a new conda environment that installs a separate WISDEM checkout that points to the develop branch.

I have re-run the simple structural optimization example with the following updated files:

and I had to add as well

to my wsidem\postprocessing folder.
The optimization is succesfull (with max_iter=20), and I get the spar cap thickness vs blade non-dim span plot showing difference between the baseline and the optimized case (see sc_opt.png attachment).
Thank you for your prompt action on this.

I also re-run the simple aerodynamic optimization case with the updated files and

instead of

The optimization failed again (as per message on the command line at the end of the run), but now I get also the twist vs non-dimensional blade span plot showing differences between the baseline case and the optimized case (see attachment twist_opt.png).

Finally, I run the aero-structural optimization case as well with

and the updated files, but it fails.

May I have some clues on how to succeed in the aerodynamic and aero-structural optimization cases, which are, actually, the ones I am mostly interested in now?

Thank you again for your support
twist_opt.png
sc_opt.png

You are treading into the area where optimization becomes an art as much of a science. I would suggest adjusting finite difference step sizes, central/forward methods, experimenting with different optimizers, experimenting with different objectives (e.g. blade mass instead of LCOE), slowly adding constraints one-by-one to prior solutions, etc. Usually launching into the full optimization leads to poorly posed problems for the optimizers.

thank you for your suggestions.
I will try them and report on the results.

The aerodynamic optimization case succeded with just the following changes in analysis_options_aero.yaml:
max_iter = 20
tol = 0.1
In attachment are the plots of axial induction vs blade span and twist vs blade span. The former is different to the one from the failed case, but the latter is the same as the one from the failed case (see previous posts).
I also tried tol=0.01, but the optimization failed as for the default value 0.001.

Now, to better understand what I am doing, I would like to ask someone to confirm my understanding of the meaning of those two parameters:

  1. max_iter: according to the documentaion this is the maximum number of major iterations before termination. And I understand this definition as: max_iter is the maximum number of times the calculation to find the optimum will be attempted before stopping the optimization run and yielding a result; this result can be successful optimization or failed optimization
  2. tol: this is tolerance for termination, and I have found also the definition convergence accuracy in the scipy documentation. I understand this as: if the difference between consecutive optimal values of the objective function, found iteration after iteration, is within the tol value, the optimization is considered succesfull and the run is stopped

Thank you.
induction.png
twist_opt.png

Your understanding of the options is correct. Those two parameters are linked in that a lower tolerance will usually require more iterations to converge. A vale of tol = 0.1 is a little high for doing a detailed design study, but fine if you just want a short demo optimization.

Thank you Garrett.

The optimization succeded also for tol = 0.05 and 0.02, and both values output the same identical plots and table values.
Below is the axial induction vs. blade span plot: run_0 is the baseline case; run_1 is tol=0.1; run_2 is tol=0.05; run_3 is tol=0.02.

The final AEP values are :

  • baseline: 24.128 [GWh]
  • optimized (tol=0.1): 23.967 [GWh]
  • optimized (tol=0.05 and 0.02): 23.975 [GWh]

so the optimized designs show a very slightly lower value of AEP, and this very slight decrease in AEP is also reported in the comment to the example.
AEP, however, is the figure of merit of the optimization (which, I assume, means the maximization of AEP), so the optimized designs shouldn’t feature an AEP higher than the AEP of the baseline design? In other words, how is the optimization succesfull, if the AEP is not higher than the baseline value?

I realize that I have questions after questions… :blush: :unamused:
Thank you for your patience
induction.png

There are likely constraints that are not fully satisfied in the baseline design that are satisfied in the final designs, just at a higher AEP value. Also, we decreasing the tolerance, try decreasing on a logarithmic scale (1e-2, then 1e-3, etc).

My aerodynamic optimization fails if I decrease the tolerance to 1e-3; the lowest tol I could succeed with is still 0.02.

Regarding the tol value, I’d like to have a further clarification: in this example, is the objective function automatically scaled to an order of magnitude ~1 and the solution algorithm applies the tol value on this scaled value, or there is no scaling?
In fact, according to my searches tol should be an absolute tolerance, and this should mean that the optimization is succesfull when

with i the iteration number.
Now, if my objective function is AEP or LCoE its value is ~10^1 GWh or $/MWh, so, if there is no scaling, do I really need to go to absolute tolerances of the order of 1e-3 (~1MW and ~0.1 cent $) to have a meaningful optimization result?

Thank you.

You have to increase the number of iterations (sometimes by an order of magnitude) as you lower the convergence tolerance. The tolerance is relative on the scaled quantity.

As suggested in a previous post I removed some constraints and tried the optimization with some more realistic tolerance values.
As a result the optimization succeded with the following settings:

  • constraints:
    -) removed: stall margin
    -) kept: pressure side and suction side spar-caps maximum strain; tip deflection
  • max iterations: 20
  • tolerance: 1e-3
  • twist, chord, spar-caps control points number (n_opt): 30

So, just removing the constraint on the stall margin made possible the optimization with the optimality tolerance at 1e-3 and 30 control points for the design variables.

However, even setting stall margin as the sole constraint, and keeping the other settings as above, makes the optimization fail. Tried to change the stall margin value, but still failure, so I tried the inverse setting for the design variable twist as follow:

design_variables:
    rotor_diameter:
        flag: True # False @ 3 Sept 2021 
        minimum: 190
        maximum: 240
    blade:
        aero_shape:
            twist:
                flag: False             # Flag to optimize the twist;
                inverse: True         # Flag to determine twist from the user-defined desired margin to stall (defined in constraints)
                n_opt: 30               # Number of control points along blade span; default=4
                max_decrease: 0.08722222222222221 # Maximum decrease for the twist in [rad] at the n_opt locations
                max_increase: 0.08722222222222221 # Maximum increase for the twist in [rad] at the n_opt locations
                index_start: 2         # Lock the first two DVs from blade root
                index_end: 4           # All DVs close to blade tip are active
 .
 .
 .
     constraints:
    blade:
        strains_spar_cap_ss:
            flag: False         # Flag to impose constraints on maximum strains (absolute value) in the spar cap on the blade suction side
            max:    3500.e-6   # Value of maximum strains [-]
            index_start: 1     # Do not enforce constraint at the first station from blade root of the n_opt from spar_cap_ss
            index_end: 3       # Do not enforce constraint at the last station at blade tip of the n_opt from spar_cap_ss
        strains_spar_cap_ps:
            flag: False         # Flag to impose constraints on maximum strains (absolute value) in the spar cap on the blade pressure side
            max:    3500.e-6   # Value of maximum strains [-]
            index_start: 1     # Do not enforce constraint at the first station from blade root of the n_opt from spar_cap_ps
            index_end: 3       # Do not enforce constraint at the last station at blade tip of the n_opt from spar_cap_ps
        tip_deflection:
            flag: False         # True
            margin: 1.4175
        stall:
            flag: True    # Constraint on minimum stall margin 
            margin: 0.087 # Value of minimum stall margin in [rad]             

and this time the optimization succeded, but, at the very beginning of the calculation I had this warning:

which doesn’t sound as I have set the calculation as I intended…
The documentation (https://wisdem.readthedocs.io/en/master/inputs/analysis_schema.html#constraints) on the inverse setting, says

which is why I set the twist flag to False, and inverse to True.
I think I am mistaking the inverse setting: how should I set it correctly and have the stall margin constraint enforced?

Thank you in advance

When the optimization says “failed”, it means it did not converge. You need to adjust tolerances, iterations, constraint values, design variable bounds, and the solver to coax the optimization to converge. There is no magic recipe I can give you that will work for every problem. That is the art of optimization.

You are not doing an inverse design problem, so leave inverse=False.

I succeded in running the optimization with:

  • tol=1e-3
  • n_opt=30 for the design variables chord, twist, and spar caps
  • max_iter=100
  • constraints: max strain on spar_cap_ss and spar_cap_ps; tip deflection; stall
    And inverse= False .

The reason of the failure was that I did not update the index_end setting for the design variables when I increased n_opt from 4 to 30.

Thank you Garret for your constant support.